blob: 94e3b89c071a6694be521d311b32c7474acad4a0 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
Paul Lewisaf066612020-07-28 15:32:4231// @ts-nocheck
32// TODO(crbug.com/1011811): Enable TypeScript compiler checks
33
Tim van der Lippea6110922020-01-09 15:38:3934export function defineCommonExtensionSymbols(apiPrivate) {
Tim van der Lippe1d6e57a2019-09-30 11:55:3435 if (!apiPrivate.panels) {
Blink Reformat4c46d092018-04-07 15:32:3736 apiPrivate.panels = {};
Tim van der Lippe1d6e57a2019-09-30 11:55:3437 }
Blink Reformat4c46d092018-04-07 15:32:3738 apiPrivate.panels.SearchAction = {
39 CancelSearch: 'cancelSearch',
40 PerformSearch: 'performSearch',
41 NextSearchResult: 'nextSearchResult',
42 PreviousSearchResult: 'previousSearchResult'
43 };
44
45 /** @enum {string} */
46 apiPrivate.Events = {
47 ButtonClicked: 'button-clicked-',
48 PanelObjectSelected: 'panel-objectSelected-',
49 NetworkRequestFinished: 'network-request-finished',
50 OpenResource: 'open-resource',
51 PanelSearch: 'panel-search-',
52 RecordingStarted: 'trace-recording-started-',
53 RecordingStopped: 'trace-recording-stopped-',
54 ResourceAdded: 'resource-added',
55 ResourceContentCommitted: 'resource-content-committed',
56 ViewShown: 'view-shown-',
57 ViewHidden: 'view-hidden-'
58 };
59
60 /** @enum {string} */
61 apiPrivate.Commands = {
62 AddRequestHeaders: 'addRequestHeaders',
63 AddTraceProvider: 'addTraceProvider',
64 ApplyStyleSheet: 'applyStyleSheet',
65 CompleteTraceSession: 'completeTraceSession',
66 CreatePanel: 'createPanel',
67 CreateSidebarPane: 'createSidebarPane',
68 CreateToolbarButton: 'createToolbarButton',
69 EvaluateOnInspectedPage: 'evaluateOnInspectedPage',
70 ForwardKeyboardEvent: '_forwardKeyboardEvent',
71 GetHAR: 'getHAR',
72 GetPageResources: 'getPageResources',
73 GetRequestContent: 'getRequestContent',
74 GetResourceContent: 'getResourceContent',
75 InspectedURLChanged: 'inspectedURLChanged',
76 OpenResource: 'openResource',
77 Reload: 'Reload',
78 Subscribe: 'subscribe',
79 SetOpenResourceHandler: 'setOpenResourceHandler',
80 SetResourceContent: 'setResourceContent',
81 SetSidebarContent: 'setSidebarContent',
82 SetSidebarHeight: 'setSidebarHeight',
83 SetSidebarPage: 'setSidebarPage',
84 ShowPanel: 'showPanel',
85 Unsubscribe: 'unsubscribe',
Philip Pfaffeedad8322020-07-20 10:24:2586 UpdateButton: 'updateButton',
87 RegisterLanguageExtensionPlugin: 'registerLanguageExtensionPlugin'
88 };
89
90 /** @enum {string} */
91 apiPrivate.LanguageExtensionPluginCommands = {
92 AddRawModule: 'addRawModule',
93 RemoveRawModule: 'removeRawModule',
94 SourceLocationToRawLocation: 'sourceLocationToRawLocation',
95 RawLocationToSourceLocation: 'rawLocationToSourceLocation',
Benedikt Meurer723d5432020-10-09 09:02:4296 GetScopeInfo: 'getScopeInfo',
Philip Pfaffeedad8322020-07-20 10:24:2597 ListVariablesInScope: 'listVariablesInScope',
Eric Leese5aaa2222020-10-01 11:49:3598 EvaluateVariable: 'evaluateVariable',
Philip Pfaffe480fa882020-10-22 09:38:3699 GetTypeInfo: 'getTypeInfo',
100 GetFormatter: 'getFormatter',
Eric Leese553115e2020-10-19 12:09:04101 GetFunctionInfo: 'getFunctionInfo',
102 GetInlinedFunctionRanges: 'getInlinedFunctionRanges',
103 GetInlinedCalleesRanges: 'getInlinedCalleesRanges'
Blink Reformat4c46d092018-04-07 15:32:37104 };
105}
106
107/**
108 * @param {!ExtensionDescriptor} extensionInfo
109 * @param {string} inspectedTabId
110 * @param {string} themeName
Joel Einbinder67f28fb2018-08-02 00:33:47111 * @param {!Array<number>} keysToForward
Blink Reformat4c46d092018-04-07 15:32:37112 * @param {number} injectedScriptId
113 * @param {function(!Object, !Object)} testHook
114 * @suppressGlobalPropertiesCheck
115 */
Tim van der Lippe226fc222019-10-10 12:17:12116self.injectedExtensionAPI = function(
117 extensionInfo, inspectedTabId, themeName, keysToForward, testHook, injectedScriptId) {
Joel Einbinder67f28fb2018-08-02 00:33:47118 const keysToForwardSet = new Set(keysToForward);
Blink Reformat4c46d092018-04-07 15:32:37119 const chrome = window.chrome || {};
120 const devtools_descriptor = Object.getOwnPropertyDescriptor(chrome, 'devtools');
Tim van der Lippe1d6e57a2019-09-30 11:55:34121 if (devtools_descriptor) {
Blink Reformat4c46d092018-04-07 15:32:37122 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34123 }
Blink Reformat4c46d092018-04-07 15:32:37124
125 const apiPrivate = {};
126
127 defineCommonExtensionSymbols(apiPrivate);
128
129 const commands = apiPrivate.Commands;
Philip Pfaffeedad8322020-07-20 10:24:25130 const languageExtensionPluginCommands = apiPrivate.LanguageExtensionPluginCommands;
Blink Reformat4c46d092018-04-07 15:32:37131 const events = apiPrivate.Events;
132 let userAction = false;
133
134 // Here and below, all constructors are private to API implementation.
135 // For a public type Foo, if internal fields are present, these are on
136 // a private FooImpl type, an instance of FooImpl is used in a closure
137 // by Foo consutrctor to re-bind publicly exported members to an instance
138 // of Foo.
139
140 /**
141 * @constructor
142 */
143 function EventSinkImpl(type, customDispatch) {
144 this._type = type;
145 this._listeners = [];
146 this._customDispatch = customDispatch;
147 }
148
149 EventSinkImpl.prototype = {
150 addListener: function(callback) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34151 if (typeof callback !== 'function') {
Blink Reformat4c46d092018-04-07 15:32:37152 throw 'addListener: callback is not a function';
Tim van der Lippe1d6e57a2019-09-30 11:55:34153 }
154 if (this._listeners.length === 0) {
Blink Reformat4c46d092018-04-07 15:32:37155 extensionServer.sendRequest({command: commands.Subscribe, type: this._type});
Tim van der Lippe1d6e57a2019-09-30 11:55:34156 }
Blink Reformat4c46d092018-04-07 15:32:37157 this._listeners.push(callback);
158 extensionServer.registerHandler('notify-' + this._type, this._dispatch.bind(this));
159 },
160
161 removeListener: function(callback) {
162 const listeners = this._listeners;
163
164 for (let i = 0; i < listeners.length; ++i) {
165 if (listeners[i] === callback) {
166 listeners.splice(i, 1);
167 break;
168 }
169 }
Tim van der Lippe1d6e57a2019-09-30 11:55:34170 if (this._listeners.length === 0) {
Blink Reformat4c46d092018-04-07 15:32:37171 extensionServer.sendRequest({command: commands.Unsubscribe, type: this._type});
Tim van der Lippe1d6e57a2019-09-30 11:55:34172 }
Blink Reformat4c46d092018-04-07 15:32:37173 },
174
175 /**
176 * @param {...} vararg
177 */
178 _fire: function(vararg) {
179 const listeners = this._listeners.slice();
Tim van der Lippe1d6e57a2019-09-30 11:55:34180 for (let i = 0; i < listeners.length; ++i) {
Blink Reformat4c46d092018-04-07 15:32:37181 listeners[i].apply(null, arguments);
Tim van der Lippe1d6e57a2019-09-30 11:55:34182 }
Blink Reformat4c46d092018-04-07 15:32:37183 },
184
185 _dispatch: function(request) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34186 if (this._customDispatch) {
Blink Reformat4c46d092018-04-07 15:32:37187 this._customDispatch.call(this, request);
Tim van der Lippe1d6e57a2019-09-30 11:55:34188 } else {
Blink Reformat4c46d092018-04-07 15:32:37189 this._fire.apply(this, request.arguments);
Tim van der Lippe1d6e57a2019-09-30 11:55:34190 }
Blink Reformat4c46d092018-04-07 15:32:37191 }
192 };
193
194 /**
195 * @constructor
196 */
197 function InspectorExtensionAPI() {
198 this.inspectedWindow = new InspectedWindow();
199 this.panels = new Panels();
200 this.network = new Network();
201 this.timeline = new Timeline();
Philip Pfaffeedad8322020-07-20 10:24:25202 this.languageServices = new LanguageServicesAPI();
Blink Reformat4c46d092018-04-07 15:32:37203 defineDeprecatedProperty(this, 'webInspector', 'resources', 'network');
204 }
205
206 /**
207 * @constructor
208 */
209 function Network() {
210 /**
211 * @this {EventSinkImpl}
212 */
213 function dispatchRequestEvent(message) {
214 const request = message.arguments[1];
215 request.__proto__ = new Request(message.arguments[0]);
216 this._fire(request);
217 }
218 this.onRequestFinished = new EventSink(events.NetworkRequestFinished, dispatchRequestEvent);
219 defineDeprecatedProperty(this, 'network', 'onFinished', 'onRequestFinished');
220 this.onNavigated = new EventSink(events.InspectedURLChanged);
221 }
222
223 Network.prototype = {
224 getHAR: function(callback) {
225 function callbackWrapper(result) {
226 const entries = (result && result.entries) || [];
227 for (let i = 0; i < entries.length; ++i) {
228 entries[i].__proto__ = new Request(entries[i]._requestId);
229 delete entries[i]._requestId;
230 }
231 callback(result);
232 }
233 extensionServer.sendRequest({command: commands.GetHAR}, callback && callbackWrapper);
234 },
235
236 addRequestHeaders: function(headers) {
237 extensionServer.sendRequest(
238 {command: commands.AddRequestHeaders, headers: headers, extensionId: window.location.hostname});
239 }
240 };
241
242 /**
243 * @constructor
244 */
245 function RequestImpl(id) {
246 this._id = id;
247 }
248
249 RequestImpl.prototype = {
250 getContent: function(callback) {
251 function callbackWrapper(response) {
252 callback(response.content, response.encoding);
253 }
254 extensionServer.sendRequest({command: commands.GetRequestContent, id: this._id}, callback && callbackWrapper);
255 }
256 };
257
258 /**
259 * @constructor
260 */
261 function Panels() {
262 const panels = {
263 elements: new ElementsPanel(),
264 sources: new SourcesPanel(),
265 };
266
267 function panelGetter(name) {
268 return panels[name];
269 }
Tim van der Lippe1d6e57a2019-09-30 11:55:34270 for (const panel in panels) {
Tim van der Lippeffa78622019-09-16 12:07:12271 Object.defineProperty(this, panel, {get: panelGetter.bind(null, panel), enumerable: true});
Tim van der Lippe1d6e57a2019-09-30 11:55:34272 }
Blink Reformat4c46d092018-04-07 15:32:37273 this.applyStyleSheet = function(styleSheet) {
274 extensionServer.sendRequest({command: commands.ApplyStyleSheet, styleSheet: styleSheet});
275 };
276 }
277
278 Panels.prototype = {
279 create: function(title, icon, page, callback) {
280 const id = 'extension-panel-' + extensionServer.nextObjectId();
281 const request = {command: commands.CreatePanel, id: id, title: title, icon: icon, page: page};
282 extensionServer.sendRequest(request, callback && callback.bind(this, new ExtensionPanel(id)));
283 },
284
285 setOpenResourceHandler: function(callback) {
286 const hadHandler = extensionServer.hasHandler(events.OpenResource);
287
288 function callbackWrapper(message) {
289 // Allow the panel to show itself when handling the event.
290 userAction = true;
291 try {
292 callback.call(null, new Resource(message.resource), message.lineNumber);
293 } finally {
294 userAction = false;
295 }
296 }
297
Tim van der Lippe1d6e57a2019-09-30 11:55:34298 if (!callback) {
Blink Reformat4c46d092018-04-07 15:32:37299 extensionServer.unregisterHandler(events.OpenResource);
Tim van der Lippe1d6e57a2019-09-30 11:55:34300 } else {
Blink Reformat4c46d092018-04-07 15:32:37301 extensionServer.registerHandler(events.OpenResource, callbackWrapper);
Tim van der Lippe1d6e57a2019-09-30 11:55:34302 }
Blink Reformat4c46d092018-04-07 15:32:37303
304 // Only send command if we either removed an existing handler or added handler and had none before.
Tim van der Lippe1d6e57a2019-09-30 11:55:34305 if (hadHandler === !callback) {
Blink Reformat4c46d092018-04-07 15:32:37306 extensionServer.sendRequest({command: commands.SetOpenResourceHandler, 'handlerPresent': !!callback});
Tim van der Lippe1d6e57a2019-09-30 11:55:34307 }
Blink Reformat4c46d092018-04-07 15:32:37308 },
309
310 openResource: function(url, lineNumber, callback) {
311 extensionServer.sendRequest({command: commands.OpenResource, 'url': url, 'lineNumber': lineNumber}, callback);
312 },
313
314 get SearchAction() {
315 return apiPrivate.panels.SearchAction;
316 }
317 };
318
319 /**
320 * @constructor
321 */
322 function ExtensionViewImpl(id) {
323 this._id = id;
324
325 /**
326 * @this {EventSinkImpl}
327 */
328 function dispatchShowEvent(message) {
329 const frameIndex = message.arguments[0];
Tim van der Lippe1d6e57a2019-09-30 11:55:34330 if (typeof frameIndex === 'number') {
Blink Reformat4c46d092018-04-07 15:32:37331 this._fire(window.parent.frames[frameIndex]);
Tim van der Lippe1d6e57a2019-09-30 11:55:34332 } else {
Blink Reformat4c46d092018-04-07 15:32:37333 this._fire();
Tim van der Lippe1d6e57a2019-09-30 11:55:34334 }
Blink Reformat4c46d092018-04-07 15:32:37335 }
336
337 if (id) {
338 this.onShown = new EventSink(events.ViewShown + id, dispatchShowEvent);
339 this.onHidden = new EventSink(events.ViewHidden + id);
340 }
341 }
342
343 /**
344 * @constructor
345 * @extends {ExtensionViewImpl}
346 * @param {string} hostPanelName
347 */
348 function PanelWithSidebarImpl(hostPanelName) {
349 ExtensionViewImpl.call(this, null);
350 this._hostPanelName = hostPanelName;
351 this.onSelectionChanged = new EventSink(events.PanelObjectSelected + hostPanelName);
352 }
353
354 PanelWithSidebarImpl.prototype = {
355 createSidebarPane: function(title, callback) {
356 const id = 'extension-sidebar-' + extensionServer.nextObjectId();
357 const request = {command: commands.CreateSidebarPane, panel: this._hostPanelName, id: id, title: title};
358 function callbackWrapper() {
359 callback(new ExtensionSidebarPane(id));
360 }
361 extensionServer.sendRequest(request, callback && callbackWrapper);
362 },
363
364 __proto__: ExtensionViewImpl.prototype
365 };
366
Philip Pfaffeedad8322020-07-20 10:24:25367 /**
368 * @constructor
369 */
370 function LanguageServicesAPIImpl() {
371 this._plugins = new Map();
372 }
373
374 LanguageServicesAPIImpl.prototype = {
375 /**
376 * @param {*} plugin The language plugin instance to register.
377 * @param {string} pluginName The plugin name
378 * @param {{language: string, symbol_types: !Array<string>}} supportedScriptTypes Script language and debug symbol types supported by this extension.
379 */
380 registerLanguageExtensionPlugin: function(plugin, pluginName, supportedScriptTypes) {
381 if (this._plugins.has(plugin)) {
382 throw new Error('Tried to register a plugin twice');
383 }
384 const channel = new MessageChannel();
385 const port = channel.port1;
386 this._plugins.set(plugin, port);
387 port.onmessage = ({data: {requestId, method, parameters}}) => {
388 dispatchMethodCall(method, parameters)
389 .then(result => port.postMessage({requestId, result}))
390 .catch(error => port.postMessage({requestId, error: {message: error.message}}));
391 };
392
393 /**
394 * @param {string} method
395 * @param {*} parameters
396 * @return {!Promise<*>}
397 */
398 function dispatchMethodCall(method, parameters) {
399 switch (method) {
400 case languageExtensionPluginCommands.AddRawModule:
401 return plugin.addRawModule(parameters.rawModuleId, parameters.symbolsURL, parameters.rawModule);
402 case languageExtensionPluginCommands.RemoveRawModule:
403 return plugin.removeRawModule(parameters.rawModuleId);
404 case languageExtensionPluginCommands.SourceLocationToRawLocation:
405 return plugin.sourceLocationToRawLocation(parameters.sourceLocation);
406 case languageExtensionPluginCommands.RawLocationToSourceLocation:
407 return plugin.rawLocationToSourceLocation(parameters.rawLocation);
Benedikt Meurer723d5432020-10-09 09:02:42408 case languageExtensionPluginCommands.GetScopeInfo:
409 return plugin.getScopeInfo(parameters.type);
Philip Pfaffeedad8322020-07-20 10:24:25410 case languageExtensionPluginCommands.ListVariablesInScope:
411 return plugin.listVariablesInScope(parameters.rawLocation);
412 case languageExtensionPluginCommands.EvaluateVariable:
413 return plugin.evaluateVariable(parameters.name, parameters.location);
Philip Pfaffe480fa882020-10-22 09:38:36414 case languageExtensionPluginCommands.GetTypeInfo:
415 return plugin.getTypeInfo(parameters.expression, parameters.context);
416 case languageExtensionPluginCommands.GetFormatter:
417 return plugin.getFormatter(parameters.expressionOrField, parameters.context);
Eric Leese5aaa2222020-10-01 11:49:35418 case languageExtensionPluginCommands.GetFunctionInfo:
419 return plugin.getFunctionInfo(parameters.rawLocation);
Eric Leese553115e2020-10-19 12:09:04420 case languageExtensionPluginCommands.GetInlinedFunctionRanges:
421 return plugin.getInlinedFunctionRanges(parameters.rawLocation);
422 case languageExtensionPluginCommands.GetInlinedCalleesRanges:
423 return plugin.getInlinedCalleesRanges(parameters.rawLocation);
Philip Pfaffeedad8322020-07-20 10:24:25424 }
425 throw new Error(`Unknown language plugin method ${method}`);
426 }
427
428 extensionServer.sendRequest(
429 {command: commands.RegisterLanguageExtensionPlugin, pluginName, port: channel.port2, supportedScriptTypes},
430 undefined, [channel.port2]);
431 }
432 };
433
Blink Reformat4c46d092018-04-07 15:32:37434 function declareInterfaceClass(implConstructor) {
435 return function() {
436 const impl = {__proto__: implConstructor.prototype};
437 implConstructor.apply(impl, arguments);
438 populateInterfaceClass(this, impl);
439 };
440 }
441
442 function defineDeprecatedProperty(object, className, oldName, newName) {
443 let warningGiven = false;
444 function getter() {
445 if (!warningGiven) {
446 console.warn(className + '.' + oldName + ' is deprecated. Use ' + className + '.' + newName + ' instead');
447 warningGiven = true;
448 }
449 return object[newName];
450 }
451 object.__defineGetter__(oldName, getter);
452 }
453
454 function extractCallbackArgument(args) {
455 const lastArgument = args[args.length - 1];
456 return typeof lastArgument === 'function' ? lastArgument : undefined;
457 }
458
Philip Pfaffeedad8322020-07-20 10:24:25459 const LanguageServicesAPI = declareInterfaceClass(LanguageServicesAPIImpl);
Blink Reformat4c46d092018-04-07 15:32:37460 const Button = declareInterfaceClass(ButtonImpl);
461 const EventSink = declareInterfaceClass(EventSinkImpl);
462 const ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
463 const ExtensionSidebarPane = declareInterfaceClass(ExtensionSidebarPaneImpl);
Tim van der Lippeffa78622019-09-16 12:07:12464 /**
465 * @constructor
466 * @param {string} hostPanelName
467 */
468 const PanelWithSidebarClass = declareInterfaceClass(PanelWithSidebarImpl);
Blink Reformat4c46d092018-04-07 15:32:37469 const Request = declareInterfaceClass(RequestImpl);
470 const Resource = declareInterfaceClass(ResourceImpl);
471 const TraceSession = declareInterfaceClass(TraceSessionImpl);
472
Tim van der Lippeffa78622019-09-16 12:07:12473 class ElementsPanel extends PanelWithSidebarClass {
474 constructor() {
475 super('elements');
476 }
Blink Reformat4c46d092018-04-07 15:32:37477 }
478
Tim van der Lippeffa78622019-09-16 12:07:12479 class SourcesPanel extends PanelWithSidebarClass {
480 constructor() {
481 super('sources');
482 }
Blink Reformat4c46d092018-04-07 15:32:37483 }
484
Blink Reformat4c46d092018-04-07 15:32:37485 /**
486 * @constructor
487 * @extends {ExtensionViewImpl}
488 */
489 function ExtensionPanelImpl(id) {
490 ExtensionViewImpl.call(this, id);
491 this.onSearch = new EventSink(events.PanelSearch + id);
492 }
493
494 ExtensionPanelImpl.prototype = {
495 /**
496 * @return {!Object}
497 */
498 createStatusBarButton: function(iconPath, tooltipText, disabled) {
499 const id = 'button-' + extensionServer.nextObjectId();
500 const request = {
501 command: commands.CreateToolbarButton,
502 panel: this._id,
503 id: id,
504 icon: iconPath,
505 tooltip: tooltipText,
506 disabled: !!disabled
507 };
508 extensionServer.sendRequest(request);
509 return new Button(id);
510 },
511
512 show: function() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34513 if (!userAction) {
Blink Reformat4c46d092018-04-07 15:32:37514 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34515 }
Blink Reformat4c46d092018-04-07 15:32:37516
517 const request = {command: commands.ShowPanel, id: this._id};
518 extensionServer.sendRequest(request);
519 },
520
521 __proto__: ExtensionViewImpl.prototype
522 };
523
524 /**
525 * @constructor
526 * @extends {ExtensionViewImpl}
527 */
528 function ExtensionSidebarPaneImpl(id) {
529 ExtensionViewImpl.call(this, id);
530 }
531
532 ExtensionSidebarPaneImpl.prototype = {
533 setHeight: function(height) {
534 extensionServer.sendRequest({command: commands.SetSidebarHeight, id: this._id, height: height});
535 },
536
537 setExpression: function(expression, rootTitle, evaluateOptions) {
538 const request = {
539 command: commands.SetSidebarContent,
540 id: this._id,
541 expression: expression,
542 rootTitle: rootTitle,
543 evaluateOnPage: true,
544 };
Tim van der Lippe1d6e57a2019-09-30 11:55:34545 if (typeof evaluateOptions === 'object') {
Blink Reformat4c46d092018-04-07 15:32:37546 request.evaluateOptions = evaluateOptions;
Tim van der Lippe1d6e57a2019-09-30 11:55:34547 }
Blink Reformat4c46d092018-04-07 15:32:37548 extensionServer.sendRequest(request, extractCallbackArgument(arguments));
549 },
550
551 setObject: function(jsonObject, rootTitle, callback) {
552 extensionServer.sendRequest(
553 {command: commands.SetSidebarContent, id: this._id, expression: jsonObject, rootTitle: rootTitle}, callback);
554 },
555
556 setPage: function(page) {
557 extensionServer.sendRequest({command: commands.SetSidebarPage, id: this._id, page: page});
558 },
559
560 __proto__: ExtensionViewImpl.prototype
561 };
562
563 /**
564 * @constructor
565 */
566 function ButtonImpl(id) {
567 this._id = id;
568 this.onClicked = new EventSink(events.ButtonClicked + id);
569 }
570
571 ButtonImpl.prototype = {
572 update: function(iconPath, tooltipText, disabled) {
573 const request =
574 {command: commands.UpdateButton, id: this._id, icon: iconPath, tooltip: tooltipText, disabled: !!disabled};
575 extensionServer.sendRequest(request);
576 }
577 };
578
579 /**
580 * @constructor
581 */
582 function Timeline() {
583 }
584
585 Timeline.prototype = {
586 /**
587 * @param {string} categoryName
588 * @param {string} categoryTooltip
589 * @return {!TraceProvider}
590 */
591 addTraceProvider: function(categoryName, categoryTooltip) {
592 const id = 'extension-trace-provider-' + extensionServer.nextObjectId();
593 extensionServer.sendRequest(
594 {command: commands.AddTraceProvider, id: id, categoryName: categoryName, categoryTooltip: categoryTooltip});
595 return new TraceProvider(id);
596 }
597 };
598
599 /**
600 * @constructor
601 * @param {string} id
602 */
603 function TraceSessionImpl(id) {
604 this._id = id;
605 }
606
607 TraceSessionImpl.prototype = {
608 /**
609 * @param {string=} url
610 * @param {number=} timeOffset
611 */
612 complete: function(url, timeOffset) {
613 const request =
614 {command: commands.CompleteTraceSession, id: this._id, url: url || '', timeOffset: timeOffset || 0};
615 extensionServer.sendRequest(request);
616 }
617 };
618
619 /**
620 * @constructor
621 * @param {string} id
622 */
623 function TraceProvider(id) {
624 /**
625 * @this {EventSinkImpl}
626 */
627 function dispatchRecordingStarted(message) {
628 const sessionId = message.arguments[0];
629 this._fire(new TraceSession(sessionId));
630 }
631
632 this.onRecordingStarted = new EventSink(events.RecordingStarted + id, dispatchRecordingStarted);
633 this.onRecordingStopped = new EventSink(events.RecordingStopped + id);
634 }
635
636 /**
637 * @constructor
638 */
639 function InspectedWindow() {
640 /**
641 * @this {EventSinkImpl}
642 */
643 function dispatchResourceEvent(message) {
644 this._fire(new Resource(message.arguments[0]));
645 }
646
647 /**
648 * @this {EventSinkImpl}
649 */
650 function dispatchResourceContentEvent(message) {
651 this._fire(new Resource(message.arguments[0]), message.arguments[1]);
652 }
653
654 this.onResourceAdded = new EventSink(events.ResourceAdded, dispatchResourceEvent);
655 this.onResourceContentCommitted = new EventSink(events.ResourceContentCommitted, dispatchResourceContentEvent);
656 }
657
658 InspectedWindow.prototype = {
659 reload: function(optionsOrUserAgent) {
660 let options = null;
661 if (typeof optionsOrUserAgent === 'object') {
662 options = optionsOrUserAgent;
663 } else if (typeof optionsOrUserAgent === 'string') {
664 options = {userAgent: optionsOrUserAgent};
665 console.warn(
666 'Passing userAgent as string parameter to inspectedWindow.reload() is deprecated. ' +
667 'Use inspectedWindow.reload({ userAgent: value}) instead.');
668 }
669 extensionServer.sendRequest({command: commands.Reload, options: options});
670 },
671
672 /**
673 * @return {?Object}
674 */
675 eval: function(expression, evaluateOptions) {
676 const callback = extractCallbackArgument(arguments);
677 function callbackWrapper(result) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34678 if (result.isError || result.isException) {
Blink Reformat4c46d092018-04-07 15:32:37679 callback(undefined, result);
Tim van der Lippe1d6e57a2019-09-30 11:55:34680 } else {
Blink Reformat4c46d092018-04-07 15:32:37681 callback(result.value);
Tim van der Lippe1d6e57a2019-09-30 11:55:34682 }
Blink Reformat4c46d092018-04-07 15:32:37683 }
684 const request = {command: commands.EvaluateOnInspectedPage, expression: expression};
Tim van der Lippe1d6e57a2019-09-30 11:55:34685 if (typeof evaluateOptions === 'object') {
Blink Reformat4c46d092018-04-07 15:32:37686 request.evaluateOptions = evaluateOptions;
Tim van der Lippe1d6e57a2019-09-30 11:55:34687 }
Blink Reformat4c46d092018-04-07 15:32:37688 extensionServer.sendRequest(request, callback && callbackWrapper);
689 return null;
690 },
691
692 getResources: function(callback) {
693 function wrapResource(resourceData) {
694 return new Resource(resourceData);
695 }
696 function callbackWrapper(resources) {
697 callback(resources.map(wrapResource));
698 }
699 extensionServer.sendRequest({command: commands.GetPageResources}, callback && callbackWrapper);
700 }
701 };
702
703 /**
704 * @constructor
705 */
706 function ResourceImpl(resourceData) {
707 this._url = resourceData.url;
708 this._type = resourceData.type;
709 }
710
711 ResourceImpl.prototype = {
712 get url() {
713 return this._url;
714 },
715
716 get type() {
717 return this._type;
718 },
719
720 getContent: function(callback) {
721 function callbackWrapper(response) {
722 callback(response.content, response.encoding);
723 }
724
725 extensionServer.sendRequest({command: commands.GetResourceContent, url: this._url}, callback && callbackWrapper);
726 },
727
728 setContent: function(content, commit, callback) {
729 extensionServer.sendRequest(
730 {command: commands.SetResourceContent, url: this._url, content: content, commit: commit}, callback);
731 }
732 };
733
734 function getTabId() {
735 return inspectedTabId;
736 }
737
738 let keyboardEventRequestQueue = [];
739 let forwardTimer = null;
740
Jan Schefflere7d7bb12019-10-24 09:18:52741 /**
742 * @suppressGlobalPropertiesCheck
743 */
Blink Reformat4c46d092018-04-07 15:32:37744 function forwardKeyboardEvent(event) {
Jan Schefflere7d7bb12019-10-24 09:18:52745 // Check if the event should be forwarded.
746 // This is a workaround for crbug.com/923338.
747 const focused = document.activeElement;
748 if (focused) {
749 const isInput = focused.nodeName === 'INPUT' || focused.nodeName === 'TEXTAREA';
750 if (isInput && !(event.ctrlKey || event.altKey || event.metaKey)) {
751 return;
752 }
753 }
754
Joel Einbinder67f28fb2018-08-02 00:33:47755 let modifiers = 0;
Tim van der Lippe1d6e57a2019-09-30 11:55:34756 if (event.shiftKey) {
Joel Einbinder67f28fb2018-08-02 00:33:47757 modifiers |= 1;
Tim van der Lippe1d6e57a2019-09-30 11:55:34758 }
759 if (event.ctrlKey) {
Joel Einbinder67f28fb2018-08-02 00:33:47760 modifiers |= 2;
Tim van der Lippe1d6e57a2019-09-30 11:55:34761 }
762 if (event.altKey) {
Joel Einbinder67f28fb2018-08-02 00:33:47763 modifiers |= 4;
Tim van der Lippe1d6e57a2019-09-30 11:55:34764 }
765 if (event.metaKey) {
Joel Einbinder67f28fb2018-08-02 00:33:47766 modifiers |= 8;
Tim van der Lippe1d6e57a2019-09-30 11:55:34767 }
Joel Einbinder67f28fb2018-08-02 00:33:47768 const num = (event.keyCode & 255) | (modifiers << 8);
Blink Reformat4c46d092018-04-07 15:32:37769 // We only care about global hotkeys, not about random text
Tim van der Lippe1d6e57a2019-09-30 11:55:34770 if (!keysToForwardSet.has(num)) {
Blink Reformat4c46d092018-04-07 15:32:37771 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34772 }
Joel Einbinder67f28fb2018-08-02 00:33:47773 event.preventDefault();
Blink Reformat4c46d092018-04-07 15:32:37774 const requestPayload = {
775 eventType: event.type,
776 ctrlKey: event.ctrlKey,
777 altKey: event.altKey,
778 metaKey: event.metaKey,
Joel Einbinder67f28fb2018-08-02 00:33:47779 shiftKey: event.shiftKey,
Blink Reformat4c46d092018-04-07 15:32:37780 keyIdentifier: event.keyIdentifier,
781 key: event.key,
782 code: event.code,
783 location: event.location,
784 keyCode: event.keyCode
785 };
786 keyboardEventRequestQueue.push(requestPayload);
Tim van der Lippe1d6e57a2019-09-30 11:55:34787 if (!forwardTimer) {
Blink Reformat4c46d092018-04-07 15:32:37788 forwardTimer = setTimeout(forwardEventQueue, 0);
Tim van der Lippe1d6e57a2019-09-30 11:55:34789 }
Blink Reformat4c46d092018-04-07 15:32:37790 }
791
792 function forwardEventQueue() {
793 forwardTimer = null;
794 const request = {command: commands.ForwardKeyboardEvent, entries: keyboardEventRequestQueue};
795 extensionServer.sendRequest(request);
796 keyboardEventRequestQueue = [];
797 }
798
799 document.addEventListener('keydown', forwardKeyboardEvent, false);
Blink Reformat4c46d092018-04-07 15:32:37800
801 /**
802 * @constructor
803 */
804 function ExtensionServerClient() {
805 this._callbacks = {};
806 this._handlers = {};
807 this._lastRequestId = 0;
808 this._lastObjectId = 0;
809
810 this.registerHandler('callback', this._onCallback.bind(this));
811
812 const channel = new MessageChannel();
813 this._port = channel.port1;
814 this._port.addEventListener('message', this._onMessage.bind(this), false);
815 this._port.start();
816
817 window.parent.postMessage('registerExtension', '*', [channel.port2]);
818 }
819
820 ExtensionServerClient.prototype = {
821 /**
822 * @param {!Object} message
823 * @param {function()=} callback
Philip Pfaffeedad8322020-07-20 10:24:25824 * @param {!Array<*>=} transfers
Blink Reformat4c46d092018-04-07 15:32:37825 */
Philip Pfaffeedad8322020-07-20 10:24:25826 sendRequest: function(message, callback, transfers) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34827 if (typeof callback === 'function') {
Blink Reformat4c46d092018-04-07 15:32:37828 message.requestId = this._registerCallback(callback);
Tim van der Lippe1d6e57a2019-09-30 11:55:34829 }
Philip Pfaffeedad8322020-07-20 10:24:25830 this._port.postMessage(message, transfers);
Blink Reformat4c46d092018-04-07 15:32:37831 },
832
833 /**
834 * @return {boolean}
835 */
836 hasHandler: function(command) {
837 return !!this._handlers[command];
838 },
839
840 registerHandler: function(command, handler) {
841 this._handlers[command] = handler;
842 },
843
844 unregisterHandler: function(command) {
845 delete this._handlers[command];
846 },
847
848 /**
849 * @return {string}
850 */
851 nextObjectId: function() {
852 return injectedScriptId.toString() + '_' + ++this._lastObjectId;
853 },
854
855 _registerCallback: function(callback) {
856 const id = ++this._lastRequestId;
857 this._callbacks[id] = callback;
858 return id;
859 },
860
861 _onCallback: function(request) {
862 if (request.requestId in this._callbacks) {
863 const callback = this._callbacks[request.requestId];
864 delete this._callbacks[request.requestId];
865 callback(request.result);
866 }
867 },
868
869 _onMessage: function(event) {
870 const request = event.data;
871 const handler = this._handlers[request.command];
Tim van der Lippe1d6e57a2019-09-30 11:55:34872 if (handler) {
Blink Reformat4c46d092018-04-07 15:32:37873 handler.call(this, request);
Tim van der Lippe1d6e57a2019-09-30 11:55:34874 }
Blink Reformat4c46d092018-04-07 15:32:37875 }
876 };
877
878 function populateInterfaceClass(interfaze, implementation) {
879 for (const member in implementation) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34880 if (member.charAt(0) === '_') {
Blink Reformat4c46d092018-04-07 15:32:37881 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34882 }
Blink Reformat4c46d092018-04-07 15:32:37883 let descriptor = null;
884 // Traverse prototype chain until we find the owner.
Tim van der Lippe1d6e57a2019-09-30 11:55:34885 for (let owner = implementation; owner && !descriptor; owner = owner.__proto__) {
Blink Reformat4c46d092018-04-07 15:32:37886 descriptor = Object.getOwnPropertyDescriptor(owner, member);
Tim van der Lippe1d6e57a2019-09-30 11:55:34887 }
888 if (!descriptor) {
Blink Reformat4c46d092018-04-07 15:32:37889 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34890 }
891 if (typeof descriptor.value === 'function') {
Blink Reformat4c46d092018-04-07 15:32:37892 interfaze[member] = descriptor.value.bind(implementation);
Tim van der Lippe1d6e57a2019-09-30 11:55:34893 } else if (typeof descriptor.get === 'function') {
Blink Reformat4c46d092018-04-07 15:32:37894 interfaze.__defineGetter__(member, descriptor.get.bind(implementation));
Tim van der Lippe1d6e57a2019-09-30 11:55:34895 } else {
Blink Reformat4c46d092018-04-07 15:32:37896 Object.defineProperty(interfaze, member, descriptor);
Tim van der Lippe1d6e57a2019-09-30 11:55:34897 }
Blink Reformat4c46d092018-04-07 15:32:37898 }
899 }
900
901 const extensionServer = new ExtensionServerClient();
902 const coreAPI = new InspectorExtensionAPI();
903
904 Object.defineProperty(chrome, 'devtools', {value: {}, enumerable: true});
905
906 // Only expose tabId on chrome.devtools.inspectedWindow, not webInspector.inspectedWindow.
907 chrome.devtools.inspectedWindow = {};
Tim van der Lippeffa78622019-09-16 12:07:12908 Object.defineProperty(chrome.devtools.inspectedWindow, 'tabId', {get: getTabId});
Blink Reformat4c46d092018-04-07 15:32:37909 chrome.devtools.inspectedWindow.__proto__ = coreAPI.inspectedWindow;
910 chrome.devtools.network = coreAPI.network;
911 chrome.devtools.panels = coreAPI.panels;
912 chrome.devtools.panels.themeName = themeName;
Kim-Anh Tran45ba0b32020-08-13 06:32:31913 chrome.devtools.languageServices = new LanguageServicesAPI();
Blink Reformat4c46d092018-04-07 15:32:37914
915 // default to expose experimental APIs for now.
916 if (extensionInfo.exposeExperimentalAPIs !== false) {
917 chrome.experimental = chrome.experimental || {};
918 chrome.experimental.devtools = chrome.experimental.devtools || {};
919
920 const properties = Object.getOwnPropertyNames(coreAPI);
921 for (let i = 0; i < properties.length; ++i) {
922 const descriptor = Object.getOwnPropertyDescriptor(coreAPI, properties[i]);
Tim van der Lippe1d6e57a2019-09-30 11:55:34923 if (descriptor) {
Blink Reformat4c46d092018-04-07 15:32:37924 Object.defineProperty(chrome.experimental.devtools, properties[i], descriptor);
Tim van der Lippe1d6e57a2019-09-30 11:55:34925 }
Blink Reformat4c46d092018-04-07 15:32:37926 }
927 chrome.experimental.devtools.inspectedWindow = chrome.devtools.inspectedWindow;
928 }
929
Tim van der Lippe1d6e57a2019-09-30 11:55:34930 if (extensionInfo.exposeWebInspectorNamespace) {
Blink Reformat4c46d092018-04-07 15:32:37931 window.webInspector = coreAPI;
Tim van der Lippe1d6e57a2019-09-30 11:55:34932 }
Blink Reformat4c46d092018-04-07 15:32:37933 testHook(extensionServer, coreAPI);
Tim van der Lippe226fc222019-10-10 12:17:12934};
Blink Reformat4c46d092018-04-07 15:32:37935
936/**
Tim van der Lipped71c22d2020-03-19 12:29:19937 * @param {!{startPage: string, name: string, exposeExperimentalAPIs: boolean}} extensionInfo
Blink Reformat4c46d092018-04-07 15:32:37938 * @param {string} inspectedTabId
939 * @param {string} themeName
Joel Einbinder67f28fb2018-08-02 00:33:47940 * @param {!Array<number>} keysToForward
Blink Reformat4c46d092018-04-07 15:32:37941 * @param {function(!Object, !Object)|undefined} testHook
942 * @return {string}
943 */
Tim van der Lippe29fab472019-08-15 14:46:48944self.buildExtensionAPIInjectedScript = function(extensionInfo, inspectedTabId, themeName, keysToForward, testHook) {
Philip Pfaffeedad8322020-07-20 10:24:25945 const argumentsJSON =
946 [extensionInfo, inspectedTabId || null, themeName, keysToForward].map(_ => JSON.stringify(_)).join(',');
Tim van der Lippe1d6e57a2019-09-30 11:55:34947 if (!testHook) {
Blink Reformat4c46d092018-04-07 15:32:37948 testHook = () => {};
Tim van der Lippe1d6e57a2019-09-30 11:55:34949 }
Blink Reformat4c46d092018-04-07 15:32:37950 return '(function(injectedScriptId){ ' + defineCommonExtensionSymbols.toString() + ';' +
Tim van der Lippe226fc222019-10-10 12:17:12951 '(' + self.injectedExtensionAPI.toString() + ')(' + argumentsJSON + ',' + testHook + ', injectedScriptId);' +
Blink Reformat4c46d092018-04-07 15:32:37952 '})';
Tim van der Lippe29fab472019-08-15 14:46:48953};