Skip to content

Commit

Permalink
feat: WebView2 debugging support (microsoft#264)
Browse files Browse the repository at this point in the history
* WebView2 debugging support

* Minimum changed needed to support `edge` debugger type, it is just an alias for `chrome`.
* This change is equivalent to the launch.json `{type: 'chrome', runTimeExecutable: 'path/to/msedge.exe'}`.
* Minimum change needed to support backwards compatibility with WebView2 launch
  debugging configurations (`useWebView`) from [vscode-edge-debug2][1].
* Adds `urlFilter` support for `chrome` `launch` requests.

Follow-ups to consider:
* Separate `chrome` and `edge` debugger type configurations with Edge strings.
* Validate `useWebView` configuration is configured with `runTimeExecutable`.
* Tests for `useWebView` configuration.
* Add browser `version` support (`stable`, `beta`, `dev`, `canary`) for Microsoft Edge (Chromium)
  to match backwards compatibility with [vscode-edge-debug2][1].
* Deprecate `{useWebView: 'advanced'}` in favor of `{useWebView: true, urlFilter: 'bing.com'}`
* Deprecate `useWebView` in favor of `webview` debugger type.

[1]: https://github.com/microsoft/vscode-edge-debug

* Only set filter for advanced web view debugging. This fixes an issue caught in tests where launching with a URL other than about:blank was broken because setting the launch URL happens after the target is attached, but it would never attach if the filter failed.

* Addressing review feedback: Changed target from `edge` to `msedge`, removed legacy fallback port of 2015, and added '(Chromium)' suffix to Edge identifier
  • Loading branch information
johnemau authored and connor4312 committed Jan 28, 2020
1 parent 7d48cb0 commit 811b2e7
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 4 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
"onCommand:extension.NAMESPACE(chrome-debug).addCustomBreakpoints",
"onCommand:extension.NAMESPACE(chrome-debug).removeAllCustomBreakpoints",
"onCommand:extension.NAMESPACE(chrome-debug).removeCustomBreakpoint",
"onDebugResolve:NAMESPACE(msedge)",
"onDebugResolve:NAMESPACE(extensionHost)"
],
"contributes": {
Expand Down
12 changes: 12 additions & 0 deletions src/build/generate-contributions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,12 @@ const chromeBaseConfigurationAttributes: ConfigurationAttributes<IChromeBaseConf
description: refString('chrome.url.description'),
default: 'http://localhost:8080',
},
useWebView: {
type: ['boolean', 'string'],
enum: [true, false, 'advanced'],
description: refString('edge.useWebView.description'),
default: false,
},
server: {
oneOf: [
{
Expand Down Expand Up @@ -707,6 +713,11 @@ const chromeAttachConfig: IDebugger<IChromeAttachConfiguration> = {
},
};

const edgeLaunchConfig: IDebugger<IChromeLaunchConfiguration> = {
...chromeLaunchConfig,
type: Contributions.EdgeDebugType,
};

function buildDebuggers() {
const debuggers = [
nodeAttachConfig,
Expand All @@ -715,6 +726,7 @@ function buildDebuggers() {
extensionHostConfig,
chromeLaunchConfig,
chromeAttachConfig,
edgeLaunchConfig,
];

// eslint-disable-next-line
Expand Down
3 changes: 3 additions & 0 deletions src/build/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const strings = {
'extensionHost.snippet.launch.description': 'Launch a VS Code extension in debug mode',
'extensionHost.snippet.launch.label': 'VS Code Extension Development',

'edge.useWebView.description':
"(Edge (Chromium) only) When 'true', the debugger will treat the runtime executable as a host application that contains a WebView allowing you to debug the WebView script content. When set to 'advanced', the debugger will wait to connect to a WebView matching the urlFilter.",

'chrome.address.description': 'TCP/IP address of debug port',
'chrome.baseUrl.description':
'Base URL to resolve paths baseUrl. baseURL is trimmed when mapping URLs to the files on disk. Defaults to the launch URL domain.',
Expand Down
1 change: 1 addition & 0 deletions src/common/contributionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const enum Contributions {
TerminalDebugType = 'NAMESPACE(node-terminal)',
NodeDebugType = 'NAMESPACE(node)',
ChromeDebugType = 'NAMESPACE(chrome)',
EdgeDebugType = 'NAMESPACE(msedge)',

BrowserBreakpointsView = 'jsBrowserBreakpoints',
}
Expand Down
12 changes: 11 additions & 1 deletion src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ export interface INodeLaunchConfiguration extends INodeBaseConfiguration, IConfi
export type PathMapping = Readonly<{ [key: string]: string }>;

export interface IChromeBaseConfiguration extends IBaseConfiguration {
type: Contributions.ChromeDebugType;
type: Contributions.ChromeDebugType | Contributions.EdgeDebugType;

/**
* Controls whether to skip the network cache for each request.
Expand Down Expand Up @@ -333,6 +333,11 @@ export interface IChromeBaseConfiguration extends IBaseConfiguration {
* Launch options to boot a server.
*/
server: INodeLaunchConfiguration | ITerminalLaunchConfiguration | null;

/**
* (Edge only) Enable web view debugging.
*/
useWebView: boolean | 'advanced';
}

/**
Expand Down Expand Up @@ -582,6 +587,8 @@ export const chromeAttachConfigDefaults: IChromeAttachConfiguration = {
sourceMapPathOverrides: defaultSourceMapPathOverrides('${webRoot}'),
webRoot: '${workspaceFolder}',
server: null,
// Edge only
useWebView: false,
};

export const chromeLaunchConfigDefaults: IChromeLaunchConfiguration = {
Expand Down Expand Up @@ -649,8 +656,11 @@ export function applyDefaults(config: AnyResolvingConfiguration): AnyLaunchConfi
case Contributions.NodeDebugType:
configWithDefaults = applyNodeDefaults(config);
break;
case Contributions.EdgeDebugType:
case Contributions.ChromeDebugType:
configWithDefaults = applyChromeDefaults(config);
// Reset type to ChromeDebugType incase EdgeDebugType was used.
configWithDefaults.type = Contributions.ChromeDebugType;
break;
case Contributions.ExtensionHostDebugType:
configWithDefaults = applyExtensionHostDefaults(config);
Expand Down
1 change: 1 addition & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export function activate(context: vscode.ExtensionContext) {
Contributions.ChromeDebugType,
sessionManager,
),
vscode.debug.registerDebugAdapterDescriptorFactory(Contributions.EdgeDebugType, sessionManager),
);
context.subscriptions.push(
vscode.debug.onDidTerminateDebugSession(s => sessionManager.terminate(s)),
Expand Down
21 changes: 19 additions & 2 deletions src/targets/browser/browserLauncher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { timeoutPromise } from '../../common/cancellation';
import { Contributions } from '../../common/contributionUtils';
import { EnvironmentVars } from '../../common/environmentVars';
import { EventEmitter, IDisposable } from '../../common/events';
import { absolutePathToFileUrl } from '../../common/urlUtils';
import { absolutePathToFileUrl, createTargetFilterForConfig } from '../../common/urlUtils';
import { AnyChromeConfiguration, IChromeLaunchConfiguration } from '../../configuration';
import Dap from '../../dap/api';
import {
Expand Down Expand Up @@ -144,6 +144,16 @@ export class BrowserLauncher implements ILauncher {
);
}

prepareWebViewLaunch(params: IChromeLaunchConfiguration) {
// Initialize WebView debugging environment variables
params.env = params.env || {};
if (params.userDataDir) {
params.env['WEBVIEW2_USER_DATA_FOLDER'] = params.userDataDir.toString();
}
params.env['WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS'] = `--remote-debugging-port=${params.port}`;
params.env['WEBVIEW2_WAIT_FOR_SCRIPT_DEBUGGER'] = 'true';
}

async prepareLaunch(
params: IChromeLaunchConfiguration,
{ dap, targetOrigin, cancellationToken, telemetryReporter }: ILaunchContext,
Expand Down Expand Up @@ -205,10 +215,13 @@ export class BrowserLauncher implements ILauncher {
this._onTargetListChangedEmitter.fire();
});

const filter =
params.useWebView === 'advanced' ? createTargetFilterForConfig(params) : undefined;

// Note: assuming first page is our main target breaks multiple debugging sessions
// sharing the browser instance. This can be fixed.
this._mainTarget = await timeoutPromise(
this._targetManager.waitForMainTarget(),
this._targetManager.waitForMainTarget(filter),
cancellationToken,
'Could not attach to main target',
);
Expand Down Expand Up @@ -247,6 +260,10 @@ export class BrowserLauncher implements ILauncher {
return { blockSessionTermination: false };
}

if (params.useWebView) {
this.prepareWebViewLaunch(params);
}

const targetOrError = await this.prepareLaunch(params, context, clientCapabilities);
if (typeof targetOrError === 'string') return { error: targetOrError };
await this.finishLaunch(targetOrError, params);
Expand Down
14 changes: 13 additions & 1 deletion src/targets/browser/browserTargets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ export class BrowserTargetManager implements IDisposable {
if (targetInfo.type !== 'page') {
return;
}
if (filter && !filter(targetInfo)) {

const advancedWebViewDebugging = this.launchParams.useWebView === 'advanced';
const failedFilter = filter && !filter(targetInfo);
if (!advancedWebViewDebugging && failedFilter) {
return;
}

Expand All @@ -130,6 +133,15 @@ export class BrowserTargetManager implements IDisposable {
return;
}

if (advancedWebViewDebugging && failedFilter) {
// web views created under script debugging are paused and need to be resumed.
// https://docs.microsoft.com/microsoft-edge/hosting/webview2/reference/webview2.idl#members
const cdp = this._connection.createSession(response.sessionId);
await cdp.Runtime.runIfWaitingForDebugger({});
this._connection.disposeSession(response.sessionId);
return;
}

callback(this._attachedToTarget(targetInfo, response.sessionId, true));
};

Expand Down

0 comments on commit 811b2e7

Please sign in to comment.