11import * as path from 'path' ;
22import * as vscode from 'vscode' ;
3+ import TelemetryReporter from 'vscode-extension-telemetry' ;
34import WebSocket from 'ws' ;
45import QuickPickItem = vscode . QuickPickItem ;
56import * as utils from './utils' ;
67
7- let settings : vscode . WorkspaceConfiguration ;
8- let hostname : string ;
9- let port : number ;
8+ interface IPackageInfo {
9+ name : string ;
10+ version : string ;
11+ aiKey : string ;
12+ }
13+
1014const debuggerType : string = 'devtools-for-chrome' ;
15+ let telemetryReporter : TelemetryReporter ;
1116
1217export function activate ( context : vscode . ExtensionContext ) {
13- settings = vscode . workspace . getConfiguration ( 'vscode-devtools-for-chrome' ) ;
14- hostname = settings . get ( 'hostname' ) as string || 'localhost' ;
15- port = settings . get ( 'port' ) as number || 9222 ;
18+
19+ const packageInfo = getPackageInfo ( context ) ;
20+ telemetryReporter = packageInfo && new TelemetryReporter ( packageInfo . name , packageInfo . version , packageInfo . aiKey ) ;
21+ context . subscriptions . push ( telemetryReporter ) ;
1622
1723 context . subscriptions . push ( vscode . commands . registerCommand ( 'devtools-for-chrome.launch' , async ( ) => {
1824 launch ( context ) ;
1925 } ) ) ;
2026
2127 context . subscriptions . push ( vscode . commands . registerCommand ( 'devtools-for-chrome.attach' , async ( ) => {
22- attach ( context ) ;
28+ attach ( context , /* viaConfig= */ false ) ;
2329 } ) ) ;
2430
2531 vscode . debug . registerDebugConfigurationProvider ( debuggerType , {
@@ -35,7 +41,7 @@ export function activate(context: vscode.ExtensionContext) {
3541 resolveDebugConfiguration ( folder : vscode . WorkspaceFolder | undefined , config : vscode . DebugConfiguration , token ?: vscode . CancellationToken ) : vscode . ProviderResult < vscode . DebugConfiguration > {
3642 if ( config && config . type == debuggerType ) {
3743 if ( config . request && config . request . localeCompare ( 'attach' , 'en' , { sensitivity : 'base' } ) == 0 ) {
38- attach ( context ) ;
44+ attach ( context , /* viaConfig= */ true ) ;
3945 } else {
4046 let launchUri : string = '' ;
4147 if ( folder . uri . scheme == 'file' ) {
@@ -56,13 +62,19 @@ export function activate(context: vscode.ExtensionContext) {
5662}
5763
5864async function launch ( context : vscode . ExtensionContext , launchUrl ?: string , chromePathFromLaunchConfig ?: string ) {
59- const portFree = await utils . isPortFree ( hostname , port ) ;
65+ const viaConfig = ! ! ( launchUrl || chromePathFromLaunchConfig ) ;
66+ const telemetryProps = { viaConfig : `${ viaConfig } ` } ;
67+ telemetryReporter . sendTelemetryEvent ( 'launch' , telemetryProps ) ;
6068
69+ const { hostname, port } = getSettings ( ) ;
70+ const portFree = await utils . isPortFree ( hostname , port ) ;
6171 if ( portFree ) {
72+ const settings = vscode . workspace . getConfiguration ( 'vscode-devtools-for-chrome' ) ;
6273 const pathToChrome = settings . get ( 'chromePath' ) as string || chromePathFromLaunchConfig || utils . getPathToChrome ( ) ;
6374
6475 if ( ! pathToChrome || ! utils . existsSync ( pathToChrome ) ) {
6576 vscode . window . showErrorMessage ( 'Chrome was not found. Chrome must be installed for this extension to function. If you have Chrome installed at a custom location you can specify it in the \'chromePath\' setting.' ) ;
77+ telemetryReporter . sendTelemetryEvent ( 'launch/error/chrome_not_found' , telemetryProps ) ;
6678 return ;
6779 }
6880
@@ -73,16 +85,22 @@ async function launch(context: vscode.ExtensionContext, launchUrl?: string, chro
7385
7486 if ( ! target || ! target . webSocketDebuggerUrl || target . webSocketDebuggerUrl == '' ) {
7587 vscode . window . showErrorMessage ( `Could not find the launched Chrome tab: (${ launchUrl } ).` ) ;
76- attach ( context ) ;
88+ telemetryReporter . sendTelemetryEvent ( 'launch/error/tab_not_found' , telemetryProps ) ;
89+ attach ( context , viaConfig ) ;
7790 } else {
7891 DevToolsPanel . createOrShow ( context . extensionPath , target . webSocketDebuggerUrl ) ;
7992 }
8093}
8194
82- async function attach ( context : vscode . ExtensionContext ) {
83- const responseArray = await getListOfTargets ( ) ;
95+ async function attach ( context : vscode . ExtensionContext , viaConfig : boolean ) {
96+ const telemetryProps = { viaConfig : `${ viaConfig } ` } ;
97+ telemetryReporter . sendTelemetryEvent ( 'attach' , telemetryProps ) ;
8498
99+ const { hostname, port } = getSettings ( ) ;
100+ const responseArray = await getListOfTargets ( hostname , port ) ;
85101 if ( Array . isArray ( responseArray ) ) {
102+ telemetryReporter . sendTelemetryEvent ( 'attach/list' , telemetryProps , { targetCount : responseArray . length } ) ;
103+
86104 const items : QuickPickItem [ ] = [ ] ;
87105
88106 responseArray . forEach ( i => {
@@ -95,18 +113,46 @@ async function attach(context: vscode.ExtensionContext) {
95113 DevToolsPanel . createOrShow ( context . extensionPath , selection . detail as string ) ;
96114 }
97115 } ) ;
116+ } else {
117+ telemetryReporter . sendTelemetryEvent ( 'attach/error/no_json_array' , telemetryProps ) ;
98118 }
99119}
100120
101- async function getListOfTargets ( ) : Promise < Array < any > > {
121+ function getSettings ( ) : { hostname : string , port : number } {
122+ const settings = vscode . workspace . getConfiguration ( 'vscode-devtools-for-chrome' ) ;
123+ const hostname = settings . get ( 'hostname' ) as string || 'localhost' ;
124+ const port = settings . get ( 'port' ) as number || 9222 ;
125+
126+ return { hostname, port } ;
127+ }
128+
129+ function getPackageInfo ( context : vscode . ExtensionContext ) : IPackageInfo {
130+ const extensionPackage = require ( context . asAbsolutePath ( './package.json' ) ) ;
131+ if ( extensionPackage ) {
132+ return {
133+ name : extensionPackage . name ,
134+ version : extensionPackage . version ,
135+ aiKey : extensionPackage . aiKey
136+ } ;
137+ }
138+ return undefined ;
139+ }
140+
141+ async function getListOfTargets ( hostname : string , port : number ) : Promise < Array < any > > {
102142 const checkDiscoveryEndpoint = ( url : string ) => {
103143 return utils . getURL ( url , { headers : { Host : 'localhost' } } ) ;
104144 } ;
105145
106146 const jsonResponse = await checkDiscoveryEndpoint ( `http://${ hostname } :${ port } /json/list` )
107147 . catch ( ( ) => checkDiscoveryEndpoint ( `http://${ hostname } :${ port } /json` ) ) ;
108148
109- return JSON . parse ( jsonResponse ) ;
149+ let result : Array < string > ;
150+ try {
151+ result = JSON . parse ( jsonResponse ) ;
152+ } catch ( ex ) {
153+ result = undefined ;
154+ }
155+ return result ;
110156}
111157
112158class DevToolsPanel {
@@ -181,18 +227,24 @@ class DevToolsPanel {
181227 private _disposeSocket ( ) {
182228 if ( this . _socket ) {
183229 // Reset the socket since the devtools have been reloaded
184- this . _socket . onerror = undefined ;
230+ telemetryReporter . sendTelemetryEvent ( 'websocket/dispose' ) ;
185231 this . _socket . onopen = undefined ;
186- this . _socket . onclose = undefined ;
187232 this . _socket . onmessage = undefined ;
233+ this . _socket . onerror = undefined ;
234+ this . _socket . onclose = undefined ;
188235 this . _socket . close ( ) ;
189236 this . _socket = undefined ;
190237 }
191238 }
192239
193240 private _onMessageFromWebview ( message : string ) {
194241 if ( message === 'ready' ) {
242+ if ( this . _socket ) {
243+ telemetryReporter . sendTelemetryEvent ( 'websocket/reconnect' ) ;
244+ }
195245 this . _disposeSocket ( ) ;
246+ } else if ( message . substr ( 0 , 10 ) === 'telemetry:' ) {
247+ return this . _sendTelemetryMessage ( message . substr ( 10 ) ) ;
196248 }
197249
198250 if ( ! this . _socket ) {
@@ -212,22 +264,16 @@ class DevToolsPanel {
212264
213265 // Create the websocket
214266 this . _socket = new WebSocket ( url ) ;
215- this . _socket . onerror = this . _onError . bind ( this ) ;
216267 this . _socket . onopen = this . _onOpen . bind ( this ) ;
217268 this . _socket . onmessage = this . _onMessage . bind ( this ) ;
269+ this . _socket . onerror = this . _onError . bind ( this ) ;
218270 this . _socket . onclose = this . _onClose . bind ( this ) ;
219271 }
220272
221- private _onError ( ) {
222- if ( this . _isConnected ) {
223- // Tell the devtools that there was a connection error
224- this . _panel . webview . postMessage ( 'error' ) ;
225- }
226- }
227-
228273 private _onOpen ( ) {
229274 this . _isConnected = true ;
230275 // Tell the devtools that the real websocket was opened
276+ telemetryReporter . sendTelemetryEvent ( 'websocket/open' ) ;
231277 this . _panel . webview . postMessage ( 'open' ) ;
232278
233279 if ( this . _socket ) {
@@ -246,14 +292,28 @@ class DevToolsPanel {
246292 }
247293 }
248294
295+ private _onError ( ) {
296+ if ( this . _isConnected ) {
297+ // Tell the devtools that there was a connection error
298+ telemetryReporter . sendTelemetryEvent ( 'websocket/error' ) ;
299+ this . _panel . webview . postMessage ( 'error' ) ;
300+ }
301+ }
302+
249303 private _onClose ( ) {
250304 if ( this . _isConnected ) {
251305 // Tell the devtools that the real websocket was closed
306+ telemetryReporter . sendTelemetryEvent ( 'websocket/close' ) ;
252307 this . _panel . webview . postMessage ( 'close' ) ;
253308 }
254309 this . _isConnected = false ;
255310 }
256311
312+ private _sendTelemetryMessage ( message : string ) {
313+ const telemetry = JSON . parse ( message ) ;
314+ telemetryReporter . sendTelemetryEvent ( telemetry . name , telemetry . properties , telemetry . metrics ) ;
315+ }
316+
257317 private _update ( ) {
258318 this . _panel . webview . html = this . _getHtmlForWebview ( ) ;
259319 }
@@ -271,9 +331,10 @@ class DevToolsPanel {
271331 <head>
272332 <meta http-equiv="content-type" content="text/html; charset=utf-8">
273333 <style>
274- html, body {
334+ html, body, iframe {
275335 height: 100%;
276336 width: 100%;
337+ position: absolute;
277338 padding: 0;
278339 margin: 0;
279340 overflow: hidden;
0 commit comments