OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
13 * distribution. | 13 * distribution. |
14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
17 * | 17 * |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | |
31 /** | 30 /** |
32 * @constructor | 31 * @unrestricted |
33 * @extends {WebInspector.Object} | |
34 * @suppressGlobalPropertiesCheck | |
35 */ | 32 */ |
36 WebInspector.ExtensionServer = function() | 33 WebInspector.ExtensionServer = class extends WebInspector.Object { |
37 { | 34 /** |
| 35 * @suppressGlobalPropertiesCheck |
| 36 */ |
| 37 constructor() { |
| 38 super(); |
38 this._clientObjects = {}; | 39 this._clientObjects = {}; |
39 this._handlers = {}; | 40 this._handlers = {}; |
40 this._subscribers = {}; | 41 this._subscribers = {}; |
41 this._subscriptionStartHandlers = {}; | 42 this._subscriptionStartHandlers = {}; |
42 this._subscriptionStopHandlers = {}; | 43 this._subscriptionStopHandlers = {}; |
43 this._extraHeaders = {}; | 44 this._extraHeaders = {}; |
44 this._requests = {}; | 45 this._requests = {}; |
45 this._lastRequestId = 0; | 46 this._lastRequestId = 0; |
46 this._registeredExtensions = {}; | 47 this._registeredExtensions = {}; |
47 this._status = new WebInspector.ExtensionStatus(); | 48 this._status = new WebInspector.ExtensionStatus(); |
(...skipping 26 matching lines...) Expand all Loading... |
74 this._registerHandler(commands.SetSidebarHeight, this._onSetSidebarHeight.bi
nd(this)); | 75 this._registerHandler(commands.SetSidebarHeight, this._onSetSidebarHeight.bi
nd(this)); |
75 this._registerHandler(commands.SetSidebarContent, this._onSetSidebarContent.
bind(this)); | 76 this._registerHandler(commands.SetSidebarContent, this._onSetSidebarContent.
bind(this)); |
76 this._registerHandler(commands.SetSidebarPage, this._onSetSidebarPage.bind(t
his)); | 77 this._registerHandler(commands.SetSidebarPage, this._onSetSidebarPage.bind(t
his)); |
77 this._registerHandler(commands.ShowPanel, this._onShowPanel.bind(this)); | 78 this._registerHandler(commands.ShowPanel, this._onShowPanel.bind(this)); |
78 this._registerHandler(commands.StopAuditCategoryRun, this._onStopAuditCatego
ryRun.bind(this)); | 79 this._registerHandler(commands.StopAuditCategoryRun, this._onStopAuditCatego
ryRun.bind(this)); |
79 this._registerHandler(commands.Subscribe, this._onSubscribe.bind(this)); | 80 this._registerHandler(commands.Subscribe, this._onSubscribe.bind(this)); |
80 this._registerHandler(commands.OpenResource, this._onOpenResource.bind(this)
); | 81 this._registerHandler(commands.OpenResource, this._onOpenResource.bind(this)
); |
81 this._registerHandler(commands.Unsubscribe, this._onUnsubscribe.bind(this)); | 82 this._registerHandler(commands.Unsubscribe, this._onUnsubscribe.bind(this)); |
82 this._registerHandler(commands.UpdateButton, this._onUpdateButton.bind(this)
); | 83 this._registerHandler(commands.UpdateButton, this._onUpdateButton.bind(this)
); |
83 this._registerHandler(commands.UpdateAuditProgress, this._onUpdateAuditProgr
ess.bind(this)); | 84 this._registerHandler(commands.UpdateAuditProgress, this._onUpdateAuditProgr
ess.bind(this)); |
84 window.addEventListener("message", this._onWindowMessage.bind(this), false);
// Only for main window. | 85 window.addEventListener('message', this._onWindowMessage.bind(this), false);
// Only for main window. |
85 | 86 |
86 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Event
s.AddExtensions, this._addExtensions, this); | 87 InspectorFrontendHost.events.addEventListener( |
87 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Event
s.SetInspectedTabId, this._setInspectedTabId, this); | 88 InspectorFrontendHostAPI.Events.AddExtensions, this._addExtensions, this
); |
| 89 InspectorFrontendHost.events.addEventListener( |
| 90 InspectorFrontendHostAPI.Events.SetInspectedTabId, this._setInspectedTab
Id, this); |
88 | 91 |
89 this._initExtensions(); | 92 this._initExtensions(); |
| 93 } |
| 94 |
| 95 initializeExtensions() { |
| 96 this._initializeCommandIssued = true; |
| 97 if (this._pendingExtensionInfos) { |
| 98 this._pendingExtensionInfos.forEach(this._addExtension, this); |
| 99 delete this._pendingExtensionInfos; |
| 100 } |
| 101 } |
| 102 |
| 103 /** |
| 104 * @return {boolean} |
| 105 */ |
| 106 hasExtensions() { |
| 107 return !!Object.keys(this._registeredExtensions).length; |
| 108 } |
| 109 |
| 110 /** |
| 111 * @param {string} panelId |
| 112 * @param {string} action |
| 113 * @param {string=} searchString |
| 114 */ |
| 115 notifySearchAction(panelId, action, searchString) { |
| 116 this._postNotification(WebInspector.extensionAPI.Events.PanelSearch + panelI
d, action, searchString); |
| 117 } |
| 118 |
| 119 /** |
| 120 * @param {string} identifier |
| 121 * @param {number=} frameIndex |
| 122 */ |
| 123 notifyViewShown(identifier, frameIndex) { |
| 124 this._postNotification(WebInspector.extensionAPI.Events.ViewShown + identifi
er, frameIndex); |
| 125 } |
| 126 |
| 127 /** |
| 128 * @param {string} identifier |
| 129 */ |
| 130 notifyViewHidden(identifier) { |
| 131 this._postNotification(WebInspector.extensionAPI.Events.ViewHidden + identif
ier); |
| 132 } |
| 133 |
| 134 /** |
| 135 * @param {string} identifier |
| 136 */ |
| 137 notifyButtonClicked(identifier) { |
| 138 this._postNotification(WebInspector.extensionAPI.Events.ButtonClicked + iden
tifier); |
| 139 } |
| 140 |
| 141 _inspectedURLChanged(event) { |
| 142 if (event.data !== WebInspector.targetManager.mainTarget()) |
| 143 return; |
| 144 this._requests = {}; |
| 145 var url = event.data.inspectedURL(); |
| 146 this._postNotification(WebInspector.extensionAPI.Events.InspectedURLChanged,
url); |
| 147 } |
| 148 |
| 149 /** |
| 150 * @param {string} categoryId |
| 151 * @param {!WebInspector.ExtensionAuditCategoryResults} auditResults |
| 152 */ |
| 153 startAuditRun(categoryId, auditResults) { |
| 154 this._clientObjects[auditResults.id()] = auditResults; |
| 155 this._postNotification('audit-started-' + categoryId, auditResults.id()); |
| 156 } |
| 157 |
| 158 /** |
| 159 * @param {!WebInspector.ExtensionAuditCategoryResults} auditResults |
| 160 */ |
| 161 stopAuditRun(auditResults) { |
| 162 delete this._clientObjects[auditResults.id()]; |
| 163 } |
| 164 |
| 165 /** |
| 166 * @param {string} traceProviderId |
| 167 */ |
| 168 startTraceRecording(traceProviderId) { |
| 169 this._postNotification('trace-recording-started-' + traceProviderId); |
| 170 } |
| 171 |
| 172 /** |
| 173 * @param {string} traceProviderId |
| 174 */ |
| 175 stopTraceRecording(traceProviderId) { |
| 176 this._postNotification('trace-recording-stopped-' + traceProviderId); |
| 177 } |
| 178 |
| 179 /** |
| 180 * @param {string} type |
| 181 * @return {boolean} |
| 182 */ |
| 183 hasSubscribers(type) { |
| 184 return !!this._subscribers[type]; |
| 185 } |
| 186 |
| 187 /** |
| 188 * @param {string} type |
| 189 * @param {...*} vararg |
| 190 */ |
| 191 _postNotification(type, vararg) { |
| 192 var subscribers = this._subscribers[type]; |
| 193 if (!subscribers) |
| 194 return; |
| 195 var message = {command: 'notify-' + type, arguments: Array.prototype.slice.c
all(arguments, 1)}; |
| 196 for (var i = 0; i < subscribers.length; ++i) |
| 197 subscribers[i].postMessage(message); |
| 198 } |
| 199 |
| 200 _onSubscribe(message, port) { |
| 201 var subscribers = this._subscribers[message.type]; |
| 202 if (subscribers) |
| 203 subscribers.push(port); |
| 204 else { |
| 205 this._subscribers[message.type] = [port]; |
| 206 if (this._subscriptionStartHandlers[message.type]) |
| 207 this._subscriptionStartHandlers[message.type](); |
| 208 } |
| 209 } |
| 210 |
| 211 _onUnsubscribe(message, port) { |
| 212 var subscribers = this._subscribers[message.type]; |
| 213 if (!subscribers) |
| 214 return; |
| 215 subscribers.remove(port); |
| 216 if (!subscribers.length) { |
| 217 delete this._subscribers[message.type]; |
| 218 if (this._subscriptionStopHandlers[message.type]) |
| 219 this._subscriptionStopHandlers[message.type](); |
| 220 } |
| 221 } |
| 222 |
| 223 _onAddRequestHeaders(message) { |
| 224 var id = message.extensionId; |
| 225 if (typeof id !== 'string') |
| 226 return this._status.E_BADARGTYPE('extensionId', typeof id, 'string'); |
| 227 var extensionHeaders = this._extraHeaders[id]; |
| 228 if (!extensionHeaders) { |
| 229 extensionHeaders = {}; |
| 230 this._extraHeaders[id] = extensionHeaders; |
| 231 } |
| 232 for (var name in message.headers) |
| 233 extensionHeaders[name] = message.headers[name]; |
| 234 var allHeaders = /** @type {!NetworkAgent.Headers} */ ({}); |
| 235 for (var extension in this._extraHeaders) { |
| 236 var headers = this._extraHeaders[extension]; |
| 237 for (name in headers) { |
| 238 if (typeof headers[name] === 'string') |
| 239 allHeaders[name] = headers[name]; |
| 240 } |
| 241 } |
| 242 |
| 243 WebInspector.multitargetNetworkManager.setExtraHTTPHeaders(allHeaders); |
| 244 } |
| 245 |
| 246 /** |
| 247 * @param {*} message |
| 248 * @suppressGlobalPropertiesCheck |
| 249 */ |
| 250 _onApplyStyleSheet(message) { |
| 251 if (!Runtime.experiments.isEnabled('applyCustomStylesheet')) |
| 252 return; |
| 253 var styleSheet = createElement('style'); |
| 254 styleSheet.textContent = message.styleSheet; |
| 255 document.head.appendChild(styleSheet); |
| 256 } |
| 257 |
| 258 _onCreatePanel(message, port) { |
| 259 var id = message.id; |
| 260 // The ids are generated on the client API side and must be unique, so the c
heck below |
| 261 // shouldn't be hit unless someone is bypassing the API. |
| 262 if (id in this._clientObjects || WebInspector.inspectorView.hasPanel(id)) |
| 263 return this._status.E_EXISTS(id); |
| 264 |
| 265 var page = this._expandResourcePath(port._extensionOrigin, message.page); |
| 266 var persistentId = port._extensionOrigin + message.title; |
| 267 persistentId = persistentId.replace(/\s/g, ''); |
| 268 var panelView = new WebInspector.ExtensionServerPanelView( |
| 269 persistentId, message.title, new WebInspector.ExtensionPanel(this, persi
stentId, id, page)); |
| 270 this._clientObjects[id] = panelView; |
| 271 WebInspector.inspectorView.addPanel(panelView); |
| 272 return this._status.OK(); |
| 273 } |
| 274 |
| 275 _onShowPanel(message) { |
| 276 var panelViewId = message.id; |
| 277 var panelView = this._clientObjects[message.id]; |
| 278 if (panelView && panelView instanceof WebInspector.ExtensionServerPanelView) |
| 279 panelViewId = panelView.viewId(); |
| 280 WebInspector.inspectorView.showPanel(panelViewId); |
| 281 } |
| 282 |
| 283 _onCreateToolbarButton(message, port) { |
| 284 var panelView = this._clientObjects[message.panel]; |
| 285 if (!panelView || !(panelView instanceof WebInspector.ExtensionServerPanelVi
ew)) |
| 286 return this._status.E_NOTFOUND(message.panel); |
| 287 var button = new WebInspector.ExtensionButton( |
| 288 this, message.id, this._expandResourcePath(port._extensionOrigin, messag
e.icon), message.tooltip, |
| 289 message.disabled); |
| 290 this._clientObjects[message.id] = button; |
| 291 |
| 292 panelView.widget().then(appendButton); |
| 293 |
| 294 /** |
| 295 * @param {!WebInspector.Widget} panel |
| 296 */ |
| 297 function appendButton(panel) { |
| 298 /** @type {!WebInspector.ExtensionPanel} panel*/ (panel).addToolbarItem(bu
tton.toolbarButton()); |
| 299 } |
| 300 |
| 301 return this._status.OK(); |
| 302 } |
| 303 |
| 304 _onUpdateButton(message, port) { |
| 305 var button = this._clientObjects[message.id]; |
| 306 if (!button || !(button instanceof WebInspector.ExtensionButton)) |
| 307 return this._status.E_NOTFOUND(message.id); |
| 308 button.update(this._expandResourcePath(port._extensionOrigin, message.icon),
message.tooltip, message.disabled); |
| 309 return this._status.OK(); |
| 310 } |
| 311 |
| 312 _onCreateSidebarPane(message) { |
| 313 if (message.panel !== 'elements' && message.panel !== 'sources') |
| 314 return this._status.E_NOTFOUND(message.panel); |
| 315 var id = message.id; |
| 316 var sidebar = new WebInspector.ExtensionSidebarPane(this, message.panel, mes
sage.title, id); |
| 317 this._sidebarPanes.push(sidebar); |
| 318 this._clientObjects[id] = sidebar; |
| 319 this.dispatchEventToListeners(WebInspector.ExtensionServer.Events.SidebarPan
eAdded, sidebar); |
| 320 |
| 321 return this._status.OK(); |
| 322 } |
| 323 |
| 324 /** |
| 325 * @return {!Array.<!WebInspector.ExtensionSidebarPane>} |
| 326 */ |
| 327 sidebarPanes() { |
| 328 return this._sidebarPanes; |
| 329 } |
| 330 |
| 331 _onSetSidebarHeight(message) { |
| 332 var sidebar = this._clientObjects[message.id]; |
| 333 if (!sidebar) |
| 334 return this._status.E_NOTFOUND(message.id); |
| 335 sidebar.setHeight(message.height); |
| 336 return this._status.OK(); |
| 337 } |
| 338 |
| 339 _onSetSidebarContent(message, port) { |
| 340 var sidebar = this._clientObjects[message.id]; |
| 341 if (!sidebar) |
| 342 return this._status.E_NOTFOUND(message.id); |
| 343 |
| 344 /** |
| 345 * @this {WebInspector.ExtensionServer} |
| 346 */ |
| 347 function callback(error) { |
| 348 var result = error ? this._status.E_FAILED(error) : this._status.OK(); |
| 349 this._dispatchCallback(message.requestId, port, result); |
| 350 } |
| 351 if (message.evaluateOnPage) |
| 352 return sidebar.setExpression( |
| 353 message.expression, message.rootTitle, message.evaluateOptions, port._
extensionOrigin, callback.bind(this)); |
| 354 sidebar.setObject(message.expression, message.rootTitle, callback.bind(this)
); |
| 355 } |
| 356 |
| 357 _onSetSidebarPage(message, port) { |
| 358 var sidebar = this._clientObjects[message.id]; |
| 359 if (!sidebar) |
| 360 return this._status.E_NOTFOUND(message.id); |
| 361 sidebar.setPage(this._expandResourcePath(port._extensionOrigin, message.page
)); |
| 362 } |
| 363 |
| 364 _onOpenResource(message) { |
| 365 var uiSourceCode = WebInspector.networkMapping.uiSourceCodeForURLForAnyTarge
t(message.url); |
| 366 if (uiSourceCode) { |
| 367 WebInspector.Revealer.reveal(uiSourceCode.uiLocation(message.lineNumber, 0
)); |
| 368 return this._status.OK(); |
| 369 } |
| 370 |
| 371 var resource = WebInspector.resourceForURL(message.url); |
| 372 if (resource) { |
| 373 WebInspector.Revealer.reveal(resource); |
| 374 return this._status.OK(); |
| 375 } |
| 376 |
| 377 var request = WebInspector.NetworkLog.requestForURL(message.url); |
| 378 if (request) { |
| 379 WebInspector.Revealer.reveal(request); |
| 380 return this._status.OK(); |
| 381 } |
| 382 |
| 383 return this._status.E_NOTFOUND(message.url); |
| 384 } |
| 385 |
| 386 _onSetOpenResourceHandler(message, port) { |
| 387 var name = this._registeredExtensions[port._extensionOrigin].name || ('Exten
sion ' + port._extensionOrigin); |
| 388 if (message.handlerPresent) |
| 389 WebInspector.openAnchorLocationRegistry.registerHandler(name, this._handle
OpenURL.bind(this, port)); |
| 390 else |
| 391 WebInspector.openAnchorLocationRegistry.unregisterHandler(name); |
| 392 } |
| 393 |
| 394 _handleOpenURL(port, details) { |
| 395 var url = /** @type {string} */ (details.url); |
| 396 var contentProvider = WebInspector.workspace.uiSourceCodeForURL(url) || WebI
nspector.resourceForURL(url); |
| 397 if (!contentProvider) |
| 398 return false; |
| 399 |
| 400 var lineNumber = details.lineNumber; |
| 401 if (typeof lineNumber === 'number') |
| 402 lineNumber += 1; |
| 403 port.postMessage({command: 'open-resource', resource: this._makeResource(con
tentProvider), lineNumber: lineNumber}); |
| 404 return true; |
| 405 } |
| 406 |
| 407 _onReload(message) { |
| 408 var options = /** @type {!ExtensionReloadOptions} */ (message.options || {})
; |
| 409 |
| 410 WebInspector.multitargetNetworkManager.setUserAgentOverride( |
| 411 typeof options.userAgent === 'string' ? options.userAgent : ''); |
| 412 var injectedScript; |
| 413 if (options.injectedScript) |
| 414 injectedScript = '(function(){' + options.injectedScript + '})()'; |
| 415 WebInspector.targetManager.reloadPage(!!options.ignoreCache, injectedScript)
; |
| 416 return this._status.OK(); |
| 417 } |
| 418 |
| 419 _onEvaluateOnInspectedPage(message, port) { |
| 420 /** |
| 421 * @param {?Protocol.Error} error |
| 422 * @param {?WebInspector.RemoteObject} remoteObject |
| 423 * @param {boolean=} wasThrown |
| 424 * @this {WebInspector.ExtensionServer} |
| 425 */ |
| 426 function callback(error, remoteObject, wasThrown) { |
| 427 var result; |
| 428 if (error || !remoteObject) |
| 429 result = this._status.E_PROTOCOLERROR(error.toString()); |
| 430 else if (wasThrown) |
| 431 result = {isException: true, value: remoteObject.description}; |
| 432 else |
| 433 result = {value: remoteObject.value}; |
| 434 |
| 435 this._dispatchCallback(message.requestId, port, result); |
| 436 } |
| 437 return this.evaluate( |
| 438 message.expression, true, true, message.evaluateOptions, port._extension
Origin, callback.bind(this)); |
| 439 } |
| 440 |
| 441 _onGetHAR() { |
| 442 var requests = WebInspector.NetworkLog.requests(); |
| 443 var harLog = (new WebInspector.HARLog(requests)).build(); |
| 444 for (var i = 0; i < harLog.entries.length; ++i) |
| 445 harLog.entries[i]._requestId = this._requestId(requests[i]); |
| 446 return harLog; |
| 447 } |
| 448 |
| 449 /** |
| 450 * @param {!WebInspector.ContentProvider} contentProvider |
| 451 */ |
| 452 _makeResource(contentProvider) { |
| 453 return {url: contentProvider.contentURL(), type: contentProvider.contentType
().name()}; |
| 454 } |
| 455 |
| 456 /** |
| 457 * @return {!Array<!WebInspector.ContentProvider>} |
| 458 */ |
| 459 _onGetPageResources() { |
| 460 /** @type {!Map<string, !WebInspector.ContentProvider>} */ |
| 461 var resources = new Map(); |
| 462 |
| 463 /** |
| 464 * @this {WebInspector.ExtensionServer} |
| 465 */ |
| 466 function pushResourceData(contentProvider) { |
| 467 if (!resources.has(contentProvider.contentURL())) |
| 468 resources.set(contentProvider.contentURL(), this._makeResource(contentPr
ovider)); |
| 469 } |
| 470 var uiSourceCodes = WebInspector.workspace.uiSourceCodesForProjectType(WebIn
spector.projectTypes.Network); |
| 471 uiSourceCodes = uiSourceCodes.concat( |
| 472 WebInspector.workspace.uiSourceCodesForProjectType(WebInspector.projectT
ypes.ContentScripts)); |
| 473 uiSourceCodes.forEach(pushResourceData.bind(this)); |
| 474 for (var target of WebInspector.targetManager.targets(WebInspector.Target.Ca
pability.DOM)) |
| 475 WebInspector.ResourceTreeModel.fromTarget(target).forAllResources(pushReso
urceData.bind(this)); |
| 476 return resources.valuesArray(); |
| 477 } |
| 478 |
| 479 /** |
| 480 * @param {!WebInspector.ContentProvider} contentProvider |
| 481 * @param {!Object} message |
| 482 * @param {!MessagePort} port |
| 483 */ |
| 484 _getResourceContent(contentProvider, message, port) { |
| 485 /** |
| 486 * @param {?string} content |
| 487 * @this {WebInspector.ExtensionServer} |
| 488 */ |
| 489 function onContentAvailable(content) { |
| 490 var contentEncoded = false; |
| 491 if (contentProvider instanceof WebInspector.Resource) |
| 492 contentEncoded = contentProvider.contentEncoded; |
| 493 if (contentProvider instanceof WebInspector.NetworkRequest) |
| 494 contentEncoded = contentProvider.contentEncoded; |
| 495 var response = {encoding: contentEncoded && content ? 'base64' : '', conte
nt: content}; |
| 496 this._dispatchCallback(message.requestId, port, response); |
| 497 } |
| 498 |
| 499 contentProvider.requestContent().then(onContentAvailable.bind(this)); |
| 500 } |
| 501 |
| 502 _onGetRequestContent(message, port) { |
| 503 var request = this._requestById(message.id); |
| 504 if (!request) |
| 505 return this._status.E_NOTFOUND(message.id); |
| 506 this._getResourceContent(request, message, port); |
| 507 } |
| 508 |
| 509 _onGetResourceContent(message, port) { |
| 510 var url = /** @type {string} */ (message.url); |
| 511 var contentProvider = WebInspector.workspace.uiSourceCodeForURL(url) || WebI
nspector.resourceForURL(url); |
| 512 if (!contentProvider) |
| 513 return this._status.E_NOTFOUND(url); |
| 514 this._getResourceContent(contentProvider, message, port); |
| 515 } |
| 516 |
| 517 _onSetResourceContent(message, port) { |
| 518 /** |
| 519 * @param {?Protocol.Error} error |
| 520 * @this {WebInspector.ExtensionServer} |
| 521 */ |
| 522 function callbackWrapper(error) { |
| 523 var response = error ? this._status.E_FAILED(error) : this._status.OK(); |
| 524 this._dispatchCallback(message.requestId, port, response); |
| 525 } |
| 526 |
| 527 var url = /** @type {string} */ (message.url); |
| 528 var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(url); |
| 529 if (!uiSourceCode || !uiSourceCode.contentType().isDocumentOrScriptOrStyleSh
eet()) { |
| 530 var resource = WebInspector.ResourceTreeModel.resourceForURL(url); |
| 531 if (!resource) |
| 532 return this._status.E_NOTFOUND(url); |
| 533 return this._status.E_NOTSUPPORTED('Resource is not editable'); |
| 534 } |
| 535 uiSourceCode.setWorkingCopy(message.content); |
| 536 if (message.commit) |
| 537 uiSourceCode.commitWorkingCopy(); |
| 538 callbackWrapper.call(this, null); |
| 539 } |
| 540 |
| 541 _requestId(request) { |
| 542 if (!request._extensionRequestId) { |
| 543 request._extensionRequestId = ++this._lastRequestId; |
| 544 this._requests[request._extensionRequestId] = request; |
| 545 } |
| 546 return request._extensionRequestId; |
| 547 } |
| 548 |
| 549 _requestById(id) { |
| 550 return this._requests[id]; |
| 551 } |
| 552 |
| 553 _onAddAuditCategory(message, port) { |
| 554 var category = new WebInspector.ExtensionAuditCategory( |
| 555 port._extensionOrigin, message.id, message.displayName, message.resultCo
unt); |
| 556 this._clientObjects[message.id] = category; |
| 557 this._auditCategories.push(category); |
| 558 this.dispatchEventToListeners(WebInspector.ExtensionServer.Events.AuditCateg
oryAdded, category); |
| 559 } |
| 560 |
| 561 /** |
| 562 * @param {!Object} message |
| 563 * @param {!MessagePort} port |
| 564 */ |
| 565 _onAddTraceProvider(message, port) { |
| 566 var provider = new WebInspector.ExtensionTraceProvider( |
| 567 port._extensionOrigin, message.id, message.categoryName, message.categor
yTooltip); |
| 568 this._clientObjects[message.id] = provider; |
| 569 this._traceProviders.push(provider); |
| 570 } |
| 571 |
| 572 /** |
| 573 * @return {!Array<!WebInspector.ExtensionTraceProvider>} |
| 574 */ |
| 575 traceProviders() { |
| 576 return this._traceProviders; |
| 577 } |
| 578 |
| 579 /** |
| 580 * @return {!Array.<!WebInspector.ExtensionAuditCategory>} |
| 581 */ |
| 582 auditCategories() { |
| 583 return this._auditCategories; |
| 584 } |
| 585 |
| 586 _onAddAuditResult(message) { |
| 587 var auditResult = /** {!WebInspector.ExtensionAuditCategoryResults} */ (this
._clientObjects[message.resultId]); |
| 588 if (!auditResult) |
| 589 return this._status.E_NOTFOUND(message.resultId); |
| 590 try { |
| 591 auditResult.addResult(message.displayName, message.description, message.se
verity, message.details); |
| 592 } catch (e) { |
| 593 return e; |
| 594 } |
| 595 return this._status.OK(); |
| 596 } |
| 597 |
| 598 _onUpdateAuditProgress(message) { |
| 599 var auditResult = /** {!WebInspector.ExtensionAuditCategoryResults} */ (this
._clientObjects[message.resultId]); |
| 600 if (!auditResult) |
| 601 return this._status.E_NOTFOUND(message.resultId); |
| 602 auditResult.updateProgress(Math.min(Math.max(0, message.progress), 1)); |
| 603 } |
| 604 |
| 605 _onStopAuditCategoryRun(message) { |
| 606 var auditRun = /** {!WebInspector.ExtensionAuditCategoryResults} */ (this._c
lientObjects[message.resultId]); |
| 607 if (!auditRun) |
| 608 return this._status.E_NOTFOUND(message.resultId); |
| 609 auditRun.done(); |
| 610 } |
| 611 |
| 612 _onForwardKeyboardEvent(message) { |
| 613 message.entries.forEach(handleEventEntry); |
| 614 |
| 615 /** |
| 616 * @param {*} entry |
| 617 * @suppressGlobalPropertiesCheck |
| 618 */ |
| 619 function handleEventEntry(entry) { |
| 620 if (!entry.ctrlKey && !entry.altKey && !entry.metaKey && !/^F\d+$/.test(en
try.key) && entry.key !== 'Escape') |
| 621 return; |
| 622 // Fool around closure compiler -- it has its own notion of both KeyboardE
vent constructor |
| 623 // and initKeyboardEvent methods and overriding these in externs.js does n
ot have effect. |
| 624 var event = new window.KeyboardEvent(entry.eventType, { |
| 625 key: entry.key, |
| 626 code: entry.code, |
| 627 keyCode: entry.keyCode, |
| 628 location: entry.location, |
| 629 ctrlKey: entry.ctrlKey, |
| 630 altKey: entry.altKey, |
| 631 shiftKey: entry.shiftKey, |
| 632 metaKey: entry.metaKey |
| 633 }); |
| 634 event.__keyCode = keyCodeForEntry(entry); |
| 635 document.dispatchEvent(event); |
| 636 } |
| 637 |
| 638 function keyCodeForEntry(entry) { |
| 639 var keyCode = entry.keyCode; |
| 640 if (!keyCode) { |
| 641 // This is required only for synthetic events (e.g. dispatched in tests)
. |
| 642 if (entry.key === 'Escape') |
| 643 keyCode = 27; |
| 644 } |
| 645 return keyCode || 0; |
| 646 } |
| 647 } |
| 648 |
| 649 _dispatchCallback(requestId, port, result) { |
| 650 if (requestId) |
| 651 port.postMessage({command: 'callback', requestId: requestId, result: resul
t}); |
| 652 } |
| 653 |
| 654 _initExtensions() { |
| 655 this._registerAutosubscriptionHandler( |
| 656 WebInspector.extensionAPI.Events.ResourceAdded, WebInspector.workspace, |
| 657 WebInspector.Workspace.Events.UISourceCodeAdded, this._notifyResourceAdd
ed); |
| 658 this._registerAutosubscriptionTargetManagerHandler( |
| 659 WebInspector.extensionAPI.Events.NetworkRequestFinished, WebInspector.Ne
tworkManager, |
| 660 WebInspector.NetworkManager.Events.RequestFinished, this._notifyRequestF
inished); |
| 661 |
| 662 /** |
| 663 * @this {WebInspector.ExtensionServer} |
| 664 */ |
| 665 function onElementsSubscriptionStarted() { |
| 666 WebInspector.context.addFlavorChangeListener(WebInspector.DOMNode, this._n
otifyElementsSelectionChanged, this); |
| 667 } |
| 668 |
| 669 /** |
| 670 * @this {WebInspector.ExtensionServer} |
| 671 */ |
| 672 function onElementsSubscriptionStopped() { |
| 673 WebInspector.context.removeFlavorChangeListener(WebInspector.DOMNode, this
._notifyElementsSelectionChanged, this); |
| 674 } |
| 675 |
| 676 this._registerSubscriptionHandler( |
| 677 WebInspector.extensionAPI.Events.PanelObjectSelected + 'elements', onEle
mentsSubscriptionStarted.bind(this), |
| 678 onElementsSubscriptionStopped.bind(this)); |
| 679 this._registerResourceContentCommittedHandler(this._notifyUISourceCodeConten
tCommitted); |
| 680 |
| 681 WebInspector.targetManager.addEventListener( |
| 682 WebInspector.TargetManager.Events.InspectedURLChanged, this._inspectedUR
LChanged, this); |
| 683 |
| 684 InspectorExtensionRegistry.getExtensionsAsync(); |
| 685 } |
| 686 |
| 687 _notifyResourceAdded(event) { |
| 688 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); |
| 689 this._postNotification(WebInspector.extensionAPI.Events.ResourceAdded, this.
_makeResource(uiSourceCode)); |
| 690 } |
| 691 |
| 692 _notifyUISourceCodeContentCommitted(event) { |
| 693 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.uiS
ourceCode); |
| 694 var content = /** @type {string} */ (event.data.content); |
| 695 this._postNotification( |
| 696 WebInspector.extensionAPI.Events.ResourceContentCommitted, this._makeRes
ource(uiSourceCode), content); |
| 697 } |
| 698 |
| 699 _notifyRequestFinished(event) { |
| 700 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data); |
| 701 this._postNotification( |
| 702 WebInspector.extensionAPI.Events.NetworkRequestFinished, this._requestId
(request), |
| 703 (new WebInspector.HAREntry(request)).build()); |
| 704 } |
| 705 |
| 706 _notifyElementsSelectionChanged() { |
| 707 this._postNotification(WebInspector.extensionAPI.Events.PanelObjectSelected
+ 'elements'); |
| 708 } |
| 709 |
| 710 /** |
| 711 * @param {!WebInspector.Event} event |
| 712 */ |
| 713 _addExtensions(event) { |
| 714 if (WebInspector.extensionServer._overridePlatformExtensionAPIForTest) |
| 715 window.buildPlatformExtensionAPI = WebInspector.extensionServer._overrideP
latformExtensionAPIForTest; |
| 716 |
| 717 var extensionInfos = /** @type {!Array.<!ExtensionDescriptor>} */ (event.dat
a); |
| 718 if (this._initializeCommandIssued) |
| 719 extensionInfos.forEach(this._addExtension, this); |
| 720 else |
| 721 this._pendingExtensionInfos = extensionInfos; |
| 722 } |
| 723 |
| 724 /** |
| 725 * @param {!WebInspector.Event} event |
| 726 */ |
| 727 _setInspectedTabId(event) { |
| 728 this._inspectedTabId = /** @type {string} */ (event.data); |
| 729 } |
| 730 |
| 731 /** |
| 732 * @param {!ExtensionDescriptor} extensionInfo |
| 733 * @suppressGlobalPropertiesCheck |
| 734 */ |
| 735 _addExtension(extensionInfo) { |
| 736 const urlOriginRegExp = new RegExp('([^:]+:\/\/[^/]*)\/'); // Can't use reg
exp literal here, MinJS chokes on it. |
| 737 var startPage = extensionInfo.startPage; |
| 738 var name = extensionInfo.name; |
| 739 |
| 740 try { |
| 741 var originMatch = urlOriginRegExp.exec(startPage); |
| 742 if (!originMatch) { |
| 743 console.error('Skipping extension with invalid URL: ' + startPage); |
| 744 return false; |
| 745 } |
| 746 var extensionOrigin = originMatch[1]; |
| 747 if (!this._registeredExtensions[extensionOrigin]) { |
| 748 // See ExtensionAPI.js for details. |
| 749 var injectedAPI = buildExtensionAPIInjectedScript( |
| 750 extensionInfo, this._inspectedTabId, WebInspector.themeSupport.theme
Name(), |
| 751 WebInspector.extensionServer['_extensionAPITestHook']); |
| 752 InspectorFrontendHost.setInjectedScriptForOrigin(extensionOrigin, inject
edAPI); |
| 753 this._registeredExtensions[extensionOrigin] = {name: name}; |
| 754 } |
| 755 var iframe = createElement('iframe'); |
| 756 iframe.src = startPage; |
| 757 iframe.style.display = 'none'; |
| 758 document.body.appendChild(iframe); // Only for main window. |
| 759 } catch (e) { |
| 760 console.error('Failed to initialize extension ' + startPage + ':' + e); |
| 761 return false; |
| 762 } |
| 763 return true; |
| 764 } |
| 765 |
| 766 _registerExtension(origin, port) { |
| 767 if (!this._registeredExtensions.hasOwnProperty(origin)) { |
| 768 if (origin !== window.location.origin) // Just ignore inspector frames. |
| 769 console.error('Ignoring unauthorized client request from ' + origin); |
| 770 return; |
| 771 } |
| 772 port._extensionOrigin = origin; |
| 773 port.addEventListener('message', this._onmessage.bind(this), false); |
| 774 port.start(); |
| 775 } |
| 776 |
| 777 _onWindowMessage(event) { |
| 778 if (event.data === 'registerExtension') |
| 779 this._registerExtension(event.origin, event.ports[0]); |
| 780 } |
| 781 |
| 782 _onmessage(event) { |
| 783 var message = event.data; |
| 784 var result; |
| 785 |
| 786 if (message.command in this._handlers) |
| 787 result = this._handlers[message.command](message, event.target); |
| 788 else |
| 789 result = this._status.E_NOTSUPPORTED(message.command); |
| 790 |
| 791 if (result && message.requestId) |
| 792 this._dispatchCallback(message.requestId, event.target, result); |
| 793 } |
| 794 |
| 795 _registerHandler(command, callback) { |
| 796 console.assert(command); |
| 797 this._handlers[command] = callback; |
| 798 } |
| 799 |
| 800 _registerSubscriptionHandler(eventTopic, onSubscribeFirst, onUnsubscribeLast)
{ |
| 801 this._subscriptionStartHandlers[eventTopic] = onSubscribeFirst; |
| 802 this._subscriptionStopHandlers[eventTopic] = onUnsubscribeLast; |
| 803 } |
| 804 |
| 805 /** |
| 806 * @param {string} eventTopic |
| 807 * @param {!Object} eventTarget |
| 808 * @param {string} frontendEventType |
| 809 * @param {function(!WebInspector.Event)} handler |
| 810 */ |
| 811 _registerAutosubscriptionHandler(eventTopic, eventTarget, frontendEventType, h
andler) { |
| 812 this._registerSubscriptionHandler( |
| 813 eventTopic, eventTarget.addEventListener.bind(eventTarget, frontendEvent
Type, handler, this), |
| 814 eventTarget.removeEventListener.bind(eventTarget, frontendEventType, han
dler, this)); |
| 815 } |
| 816 |
| 817 /** |
| 818 * @param {string} eventTopic |
| 819 * @param {!Function} modelClass |
| 820 * @param {string} frontendEventType |
| 821 * @param {function(!WebInspector.Event)} handler |
| 822 */ |
| 823 _registerAutosubscriptionTargetManagerHandler(eventTopic, modelClass, frontend
EventType, handler) { |
| 824 this._registerSubscriptionHandler( |
| 825 eventTopic, WebInspector.targetManager.addModelListener.bind( |
| 826 WebInspector.targetManager, modelClass, frontendEventTyp
e, handler, this), |
| 827 WebInspector.targetManager.removeModelListener.bind( |
| 828 WebInspector.targetManager, modelClass, frontendEventType, handler,
this)); |
| 829 } |
| 830 |
| 831 _registerResourceContentCommittedHandler(handler) { |
| 832 /** |
| 833 * @this {WebInspector.ExtensionServer} |
| 834 */ |
| 835 function addFirstEventListener() { |
| 836 WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.Work
ingCopyCommittedByUser, handler, this); |
| 837 WebInspector.workspace.setHasResourceContentTrackingExtensions(true); |
| 838 } |
| 839 |
| 840 /** |
| 841 * @this {WebInspector.ExtensionServer} |
| 842 */ |
| 843 function removeLastEventListener() { |
| 844 WebInspector.workspace.setHasResourceContentTrackingExtensions(false); |
| 845 WebInspector.workspace.removeEventListener( |
| 846 WebInspector.Workspace.Events.WorkingCopyCommittedByUser, handler, thi
s); |
| 847 } |
| 848 |
| 849 this._registerSubscriptionHandler( |
| 850 WebInspector.extensionAPI.Events.ResourceContentCommitted, addFirstEvent
Listener.bind(this), |
| 851 removeLastEventListener.bind(this)); |
| 852 } |
| 853 |
| 854 _expandResourcePath(extensionPath, resourcePath) { |
| 855 if (!resourcePath) |
| 856 return; |
| 857 return extensionPath + this._normalizePath(resourcePath); |
| 858 } |
| 859 |
| 860 _normalizePath(path) { |
| 861 var source = path.split('/'); |
| 862 var result = []; |
| 863 |
| 864 for (var i = 0; i < source.length; ++i) { |
| 865 if (source[i] === '.') |
| 866 continue; |
| 867 // Ignore empty path components resulting from //, as well as a leading an
d traling slashes. |
| 868 if (source[i] === '') |
| 869 continue; |
| 870 if (source[i] === '..') |
| 871 result.pop(); |
| 872 else |
| 873 result.push(source[i]); |
| 874 } |
| 875 return '/' + result.join('/'); |
| 876 } |
| 877 |
| 878 /** |
| 879 * @param {string} expression |
| 880 * @param {boolean} exposeCommandLineAPI |
| 881 * @param {boolean} returnByValue |
| 882 * @param {?Object} options |
| 883 * @param {string} securityOrigin |
| 884 * @param {function(?string, ?WebInspector.RemoteObject, boolean=)} callback |
| 885 * @return {!WebInspector.ExtensionStatus.Record|undefined} |
| 886 */ |
| 887 evaluate(expression, exposeCommandLineAPI, returnByValue, options, securityOri
gin, callback) { |
| 888 var contextId; |
| 889 |
| 890 /** |
| 891 * @param {string} url |
| 892 * @return {boolean} |
| 893 */ |
| 894 function resolveURLToFrame(url) { |
| 895 var found; |
| 896 function hasMatchingURL(frame) { |
| 897 found = (frame.url === url) ? frame : null; |
| 898 return found; |
| 899 } |
| 900 WebInspector.ResourceTreeModel.frames().some(hasMatchingURL); |
| 901 return found; |
| 902 } |
| 903 |
| 904 if (typeof options === 'object') { |
| 905 var frame; |
| 906 if (options.frameURL) { |
| 907 frame = resolveURLToFrame(options.frameURL); |
| 908 } else { |
| 909 var target = WebInspector.targetManager.mainTarget(); |
| 910 var resourceTreeModel = target && WebInspector.ResourceTreeModel.fromTar
get(target); |
| 911 frame = resourceTreeModel && resourceTreeModel.mainFrame; |
| 912 } |
| 913 if (!frame) { |
| 914 if (options.frameURL) |
| 915 console.warn('evaluate: there is no frame with URL ' + options.frameUR
L); |
| 916 else |
| 917 console.warn('evaluate: the main frame is not yet available'); |
| 918 return this._status.E_NOTFOUND(options.frameURL || '<top>'); |
| 919 } |
| 920 |
| 921 var contextSecurityOrigin; |
| 922 if (options.useContentScriptContext) |
| 923 contextSecurityOrigin = securityOrigin; |
| 924 else if (options.scriptExecutionContext) |
| 925 contextSecurityOrigin = options.scriptExecutionContext; |
| 926 |
| 927 var context; |
| 928 var executionContexts = frame.target().runtimeModel.executionContexts(); |
| 929 if (contextSecurityOrigin) { |
| 930 for (var i = 0; i < executionContexts.length; ++i) { |
| 931 var executionContext = executionContexts[i]; |
| 932 if (executionContext.frameId === frame.id && executionContext.origin =
== contextSecurityOrigin && |
| 933 !executionContext.isDefault) |
| 934 context = executionContext; |
| 935 } |
| 936 if (!context) { |
| 937 console.warn('The JavaScript context ' + contextSecurityOrigin + ' was
not found in the frame ' + frame.url); |
| 938 return this._status.E_NOTFOUND(contextSecurityOrigin); |
| 939 } |
| 940 } else { |
| 941 for (var i = 0; i < executionContexts.length; ++i) { |
| 942 var executionContext = executionContexts[i]; |
| 943 if (executionContext.frameId === frame.id && executionContext.isDefaul
t) |
| 944 context = executionContext; |
| 945 } |
| 946 if (!context) |
| 947 return this._status.E_FAILED(frame.url + ' has no execution context'); |
| 948 } |
| 949 |
| 950 contextId = context.id; |
| 951 } |
| 952 var target = target ? target : WebInspector.targetManager.mainTarget(); |
| 953 if (!target) |
| 954 return; |
| 955 |
| 956 target.runtimeAgent().evaluate( |
| 957 expression, 'extension', exposeCommandLineAPI, true, contextId, returnBy
Value, false, false, false, onEvalute); |
| 958 |
| 959 /** |
| 960 * @param {?Protocol.Error} error |
| 961 * @param {!RuntimeAgent.RemoteObject} result |
| 962 * @param {!RuntimeAgent.ExceptionDetails=} exceptionDetails |
| 963 */ |
| 964 function onEvalute(error, result, exceptionDetails) { |
| 965 if (error) { |
| 966 callback(error, null, !!exceptionDetails); |
| 967 return; |
| 968 } |
| 969 callback(error, target.runtimeModel.createRemoteObject(result), !!exceptio
nDetails); |
| 970 } |
| 971 } |
90 }; | 972 }; |
91 | 973 |
92 /** @enum {symbol} */ | 974 /** @enum {symbol} */ |
93 WebInspector.ExtensionServer.Events = { | 975 WebInspector.ExtensionServer.Events = { |
94 SidebarPaneAdded: Symbol("SidebarPaneAdded"), | 976 SidebarPaneAdded: Symbol('SidebarPaneAdded'), |
95 AuditCategoryAdded: Symbol("AuditCategoryAdded") | 977 AuditCategoryAdded: Symbol('AuditCategoryAdded') |
96 }; | 978 }; |
97 | 979 |
98 WebInspector.ExtensionServer.prototype = { | |
99 initializeExtensions: function() | |
100 { | |
101 this._initializeCommandIssued = true; | |
102 if (this._pendingExtensionInfos) { | |
103 this._pendingExtensionInfos.forEach(this._addExtension, this); | |
104 delete this._pendingExtensionInfos; | |
105 } | |
106 }, | |
107 | |
108 /** | |
109 * @return {boolean} | |
110 */ | |
111 hasExtensions: function() | |
112 { | |
113 return !!Object.keys(this._registeredExtensions).length; | |
114 }, | |
115 | |
116 /** | |
117 * @param {string} panelId | |
118 * @param {string} action | |
119 * @param {string=} searchString | |
120 */ | |
121 notifySearchAction: function(panelId, action, searchString) | |
122 { | |
123 this._postNotification(WebInspector.extensionAPI.Events.PanelSearch + pa
nelId, action, searchString); | |
124 }, | |
125 | |
126 /** | |
127 * @param {string} identifier | |
128 * @param {number=} frameIndex | |
129 */ | |
130 notifyViewShown: function(identifier, frameIndex) | |
131 { | |
132 this._postNotification(WebInspector.extensionAPI.Events.ViewShown + iden
tifier, frameIndex); | |
133 }, | |
134 | |
135 /** | |
136 * @param {string} identifier | |
137 */ | |
138 notifyViewHidden: function(identifier) | |
139 { | |
140 this._postNotification(WebInspector.extensionAPI.Events.ViewHidden + ide
ntifier); | |
141 }, | |
142 | |
143 /** | |
144 * @param {string} identifier | |
145 */ | |
146 notifyButtonClicked: function(identifier) | |
147 { | |
148 this._postNotification(WebInspector.extensionAPI.Events.ButtonClicked +
identifier); | |
149 }, | |
150 | |
151 _inspectedURLChanged: function(event) | |
152 { | |
153 if (event.data !== WebInspector.targetManager.mainTarget()) | |
154 return; | |
155 this._requests = {}; | |
156 var url = event.data.inspectedURL(); | |
157 this._postNotification(WebInspector.extensionAPI.Events.InspectedURLChan
ged, url); | |
158 }, | |
159 | |
160 /** | |
161 * @param {string} categoryId | |
162 * @param {!WebInspector.ExtensionAuditCategoryResults} auditResults | |
163 */ | |
164 startAuditRun: function(categoryId, auditResults) | |
165 { | |
166 this._clientObjects[auditResults.id()] = auditResults; | |
167 this._postNotification("audit-started-" + categoryId, auditResults.id())
; | |
168 }, | |
169 | |
170 /** | |
171 * @param {!WebInspector.ExtensionAuditCategoryResults} auditResults | |
172 */ | |
173 stopAuditRun: function(auditResults) | |
174 { | |
175 delete this._clientObjects[auditResults.id()]; | |
176 }, | |
177 | |
178 /** | |
179 * @param {string} traceProviderId | |
180 */ | |
181 startTraceRecording: function(traceProviderId) | |
182 { | |
183 this._postNotification("trace-recording-started-" + traceProviderId); | |
184 }, | |
185 | |
186 /** | |
187 * @param {string} traceProviderId | |
188 */ | |
189 stopTraceRecording: function(traceProviderId) | |
190 { | |
191 this._postNotification("trace-recording-stopped-" + traceProviderId); | |
192 }, | |
193 | |
194 /** | |
195 * @param {string} type | |
196 * @return {boolean} | |
197 */ | |
198 hasSubscribers: function(type) | |
199 { | |
200 return !!this._subscribers[type]; | |
201 }, | |
202 | |
203 /** | |
204 * @param {string} type | |
205 * @param {...*} vararg | |
206 */ | |
207 _postNotification: function(type, vararg) | |
208 { | |
209 var subscribers = this._subscribers[type]; | |
210 if (!subscribers) | |
211 return; | |
212 var message = { | |
213 command: "notify-" + type, | |
214 arguments: Array.prototype.slice.call(arguments, 1) | |
215 }; | |
216 for (var i = 0; i < subscribers.length; ++i) | |
217 subscribers[i].postMessage(message); | |
218 }, | |
219 | |
220 _onSubscribe: function(message, port) | |
221 { | |
222 var subscribers = this._subscribers[message.type]; | |
223 if (subscribers) | |
224 subscribers.push(port); | |
225 else { | |
226 this._subscribers[message.type] = [ port ]; | |
227 if (this._subscriptionStartHandlers[message.type]) | |
228 this._subscriptionStartHandlers[message.type](); | |
229 } | |
230 }, | |
231 | |
232 _onUnsubscribe: function(message, port) | |
233 { | |
234 var subscribers = this._subscribers[message.type]; | |
235 if (!subscribers) | |
236 return; | |
237 subscribers.remove(port); | |
238 if (!subscribers.length) { | |
239 delete this._subscribers[message.type]; | |
240 if (this._subscriptionStopHandlers[message.type]) | |
241 this._subscriptionStopHandlers[message.type](); | |
242 } | |
243 }, | |
244 | |
245 _onAddRequestHeaders: function(message) | |
246 { | |
247 var id = message.extensionId; | |
248 if (typeof id !== "string") | |
249 return this._status.E_BADARGTYPE("extensionId", typeof id, "string")
; | |
250 var extensionHeaders = this._extraHeaders[id]; | |
251 if (!extensionHeaders) { | |
252 extensionHeaders = {}; | |
253 this._extraHeaders[id] = extensionHeaders; | |
254 } | |
255 for (var name in message.headers) | |
256 extensionHeaders[name] = message.headers[name]; | |
257 var allHeaders = /** @type {!NetworkAgent.Headers} */ ({}); | |
258 for (var extension in this._extraHeaders) { | |
259 var headers = this._extraHeaders[extension]; | |
260 for (name in headers) { | |
261 if (typeof headers[name] === "string") | |
262 allHeaders[name] = headers[name]; | |
263 } | |
264 } | |
265 | |
266 WebInspector.multitargetNetworkManager.setExtraHTTPHeaders(allHeaders); | |
267 }, | |
268 | |
269 /** | |
270 * @param {*} message | |
271 * @suppressGlobalPropertiesCheck | |
272 */ | |
273 _onApplyStyleSheet: function(message) | |
274 { | |
275 if (!Runtime.experiments.isEnabled("applyCustomStylesheet")) | |
276 return; | |
277 var styleSheet = createElement("style"); | |
278 styleSheet.textContent = message.styleSheet; | |
279 document.head.appendChild(styleSheet); | |
280 }, | |
281 | |
282 _onCreatePanel: function(message, port) | |
283 { | |
284 var id = message.id; | |
285 // The ids are generated on the client API side and must be unique, so t
he check below | |
286 // shouldn't be hit unless someone is bypassing the API. | |
287 if (id in this._clientObjects || WebInspector.inspectorView.hasPanel(id)
) | |
288 return this._status.E_EXISTS(id); | |
289 | |
290 var page = this._expandResourcePath(port._extensionOrigin, message.page)
; | |
291 var persistentId = port._extensionOrigin + message.title; | |
292 persistentId = persistentId.replace(/\s/g, ""); | |
293 var panelView = new WebInspector.ExtensionServerPanelView(persistentId,
message.title, new WebInspector.ExtensionPanel(this, persistentId, id, page)); | |
294 this._clientObjects[id] = panelView; | |
295 WebInspector.inspectorView.addPanel(panelView); | |
296 return this._status.OK(); | |
297 }, | |
298 | |
299 _onShowPanel: function(message) | |
300 { | |
301 var panelViewId = message.id; | |
302 var panelView = this._clientObjects[message.id]; | |
303 if (panelView && panelView instanceof WebInspector.ExtensionServerPanelV
iew) | |
304 panelViewId = panelView.viewId(); | |
305 WebInspector.inspectorView.showPanel(panelViewId); | |
306 }, | |
307 | |
308 _onCreateToolbarButton: function(message, port) | |
309 { | |
310 var panelView = this._clientObjects[message.panel]; | |
311 if (!panelView || !(panelView instanceof WebInspector.ExtensionServerPan
elView)) | |
312 return this._status.E_NOTFOUND(message.panel); | |
313 var button = new WebInspector.ExtensionButton(this, message.id, this._ex
pandResourcePath(port._extensionOrigin, message.icon), message.tooltip, message.
disabled); | |
314 this._clientObjects[message.id] = button; | |
315 | |
316 panelView.widget().then(appendButton); | |
317 | |
318 /** | |
319 * @param {!WebInspector.Widget} panel | |
320 */ | |
321 function appendButton(panel) | |
322 { | |
323 /** @type {!WebInspector.ExtensionPanel} panel*/ (panel).addToolbarI
tem(button.toolbarButton()); | |
324 } | |
325 | |
326 return this._status.OK(); | |
327 }, | |
328 | |
329 _onUpdateButton: function(message, port) | |
330 { | |
331 var button = this._clientObjects[message.id]; | |
332 if (!button || !(button instanceof WebInspector.ExtensionButton)) | |
333 return this._status.E_NOTFOUND(message.id); | |
334 button.update(this._expandResourcePath(port._extensionOrigin, message.ic
on), message.tooltip, message.disabled); | |
335 return this._status.OK(); | |
336 }, | |
337 | |
338 _onCreateSidebarPane: function(message) | |
339 { | |
340 if (message.panel !== "elements" && message.panel !== "sources") | |
341 return this._status.E_NOTFOUND(message.panel); | |
342 var id = message.id; | |
343 var sidebar = new WebInspector.ExtensionSidebarPane(this, message.panel,
message.title, id); | |
344 this._sidebarPanes.push(sidebar); | |
345 this._clientObjects[id] = sidebar; | |
346 this.dispatchEventToListeners(WebInspector.ExtensionServer.Events.Sideba
rPaneAdded, sidebar); | |
347 | |
348 return this._status.OK(); | |
349 }, | |
350 | |
351 /** | |
352 * @return {!Array.<!WebInspector.ExtensionSidebarPane>} | |
353 */ | |
354 sidebarPanes: function() | |
355 { | |
356 return this._sidebarPanes; | |
357 }, | |
358 | |
359 _onSetSidebarHeight: function(message) | |
360 { | |
361 var sidebar = this._clientObjects[message.id]; | |
362 if (!sidebar) | |
363 return this._status.E_NOTFOUND(message.id); | |
364 sidebar.setHeight(message.height); | |
365 return this._status.OK(); | |
366 }, | |
367 | |
368 _onSetSidebarContent: function(message, port) | |
369 { | |
370 var sidebar = this._clientObjects[message.id]; | |
371 if (!sidebar) | |
372 return this._status.E_NOTFOUND(message.id); | |
373 | |
374 /** | |
375 * @this {WebInspector.ExtensionServer} | |
376 */ | |
377 function callback(error) | |
378 { | |
379 var result = error ? this._status.E_FAILED(error) : this._status.OK(
); | |
380 this._dispatchCallback(message.requestId, port, result); | |
381 } | |
382 if (message.evaluateOnPage) | |
383 return sidebar.setExpression(message.expression, message.rootTitle,
message.evaluateOptions, port._extensionOrigin, callback.bind(this)); | |
384 sidebar.setObject(message.expression, message.rootTitle, callback.bind(t
his)); | |
385 }, | |
386 | |
387 _onSetSidebarPage: function(message, port) | |
388 { | |
389 var sidebar = this._clientObjects[message.id]; | |
390 if (!sidebar) | |
391 return this._status.E_NOTFOUND(message.id); | |
392 sidebar.setPage(this._expandResourcePath(port._extensionOrigin, message.
page)); | |
393 }, | |
394 | |
395 _onOpenResource: function(message) | |
396 { | |
397 var uiSourceCode = WebInspector.networkMapping.uiSourceCodeForURLForAnyT
arget(message.url); | |
398 if (uiSourceCode) { | |
399 WebInspector.Revealer.reveal(uiSourceCode.uiLocation(message.lineNum
ber, 0)); | |
400 return this._status.OK(); | |
401 } | |
402 | |
403 var resource = WebInspector.resourceForURL(message.url); | |
404 if (resource) { | |
405 WebInspector.Revealer.reveal(resource); | |
406 return this._status.OK(); | |
407 } | |
408 | |
409 var request = WebInspector.NetworkLog.requestForURL(message.url); | |
410 if (request) { | |
411 WebInspector.Revealer.reveal(request); | |
412 return this._status.OK(); | |
413 } | |
414 | |
415 return this._status.E_NOTFOUND(message.url); | |
416 }, | |
417 | |
418 _onSetOpenResourceHandler: function(message, port) | |
419 { | |
420 var name = this._registeredExtensions[port._extensionOrigin].name || ("E
xtension " + port._extensionOrigin); | |
421 if (message.handlerPresent) | |
422 WebInspector.openAnchorLocationRegistry.registerHandler(name, this._
handleOpenURL.bind(this, port)); | |
423 else | |
424 WebInspector.openAnchorLocationRegistry.unregisterHandler(name); | |
425 }, | |
426 | |
427 _handleOpenURL: function(port, details) | |
428 { | |
429 var url = /** @type {string} */ (details.url); | |
430 var contentProvider = WebInspector.workspace.uiSourceCodeForURL(url) ||
WebInspector.resourceForURL(url); | |
431 if (!contentProvider) | |
432 return false; | |
433 | |
434 var lineNumber = details.lineNumber; | |
435 if (typeof lineNumber === "number") | |
436 lineNumber += 1; | |
437 port.postMessage({ | |
438 command: "open-resource", | |
439 resource: this._makeResource(contentProvider), | |
440 lineNumber: lineNumber | |
441 }); | |
442 return true; | |
443 }, | |
444 | |
445 _onReload: function(message) | |
446 { | |
447 var options = /** @type {!ExtensionReloadOptions} */ (message.options ||
{}); | |
448 | |
449 WebInspector.multitargetNetworkManager.setUserAgentOverride(typeof optio
ns.userAgent === "string" ? options.userAgent : ""); | |
450 var injectedScript; | |
451 if (options.injectedScript) | |
452 injectedScript = "(function(){" + options.injectedScript + "})()"; | |
453 WebInspector.targetManager.reloadPage(!!options.ignoreCache, injectedScr
ipt); | |
454 return this._status.OK(); | |
455 }, | |
456 | |
457 _onEvaluateOnInspectedPage: function(message, port) | |
458 { | |
459 /** | |
460 * @param {?Protocol.Error} error | |
461 * @param {?WebInspector.RemoteObject} remoteObject | |
462 * @param {boolean=} wasThrown | |
463 * @this {WebInspector.ExtensionServer} | |
464 */ | |
465 function callback(error, remoteObject, wasThrown) | |
466 { | |
467 var result; | |
468 if (error || !remoteObject) | |
469 result = this._status.E_PROTOCOLERROR(error.toString()); | |
470 else if (wasThrown) | |
471 result = { isException: true, value: remoteObject.description }; | |
472 else | |
473 result = { value: remoteObject.value }; | |
474 | |
475 this._dispatchCallback(message.requestId, port, result); | |
476 } | |
477 return this.evaluate(message.expression, true, true, message.evaluateOpt
ions, port._extensionOrigin, callback.bind(this)); | |
478 }, | |
479 | |
480 _onGetHAR: function() | |
481 { | |
482 var requests = WebInspector.NetworkLog.requests(); | |
483 var harLog = (new WebInspector.HARLog(requests)).build(); | |
484 for (var i = 0; i < harLog.entries.length; ++i) | |
485 harLog.entries[i]._requestId = this._requestId(requests[i]); | |
486 return harLog; | |
487 }, | |
488 | |
489 /** | |
490 * @param {!WebInspector.ContentProvider} contentProvider | |
491 */ | |
492 _makeResource: function(contentProvider) | |
493 { | |
494 return { | |
495 url: contentProvider.contentURL(), | |
496 type: contentProvider.contentType().name() | |
497 }; | |
498 }, | |
499 | |
500 /** | |
501 * @return {!Array<!WebInspector.ContentProvider>} | |
502 */ | |
503 _onGetPageResources: function() | |
504 { | |
505 /** @type {!Map<string, !WebInspector.ContentProvider>} */ | |
506 var resources = new Map(); | |
507 | |
508 /** | |
509 * @this {WebInspector.ExtensionServer} | |
510 */ | |
511 function pushResourceData(contentProvider) | |
512 { | |
513 if (!resources.has(contentProvider.contentURL())) | |
514 resources.set(contentProvider.contentURL(), this._makeResource(c
ontentProvider)); | |
515 } | |
516 var uiSourceCodes = WebInspector.workspace.uiSourceCodesForProjectType(W
ebInspector.projectTypes.Network); | |
517 uiSourceCodes = uiSourceCodes.concat(WebInspector.workspace.uiSourceCode
sForProjectType(WebInspector.projectTypes.ContentScripts)); | |
518 uiSourceCodes.forEach(pushResourceData.bind(this)); | |
519 for (var target of WebInspector.targetManager.targets(WebInspector.Targe
t.Capability.DOM)) | |
520 WebInspector.ResourceTreeModel.fromTarget(target).forAllResources(pu
shResourceData.bind(this)); | |
521 return resources.valuesArray(); | |
522 }, | |
523 | |
524 /** | |
525 * @param {!WebInspector.ContentProvider} contentProvider | |
526 * @param {!Object} message | |
527 * @param {!MessagePort} port | |
528 */ | |
529 _getResourceContent: function(contentProvider, message, port) | |
530 { | |
531 /** | |
532 * @param {?string} content | |
533 * @this {WebInspector.ExtensionServer} | |
534 */ | |
535 function onContentAvailable(content) | |
536 { | |
537 var contentEncoded = false; | |
538 if (contentProvider instanceof WebInspector.Resource) | |
539 contentEncoded = contentProvider.contentEncoded; | |
540 if (contentProvider instanceof WebInspector.NetworkRequest) | |
541 contentEncoded = contentProvider.contentEncoded; | |
542 var response = { | |
543 encoding: contentEncoded && content ? "base64" : "", | |
544 content: content | |
545 }; | |
546 this._dispatchCallback(message.requestId, port, response); | |
547 } | |
548 | |
549 contentProvider.requestContent().then(onContentAvailable.bind(this)); | |
550 }, | |
551 | |
552 _onGetRequestContent: function(message, port) | |
553 { | |
554 var request = this._requestById(message.id); | |
555 if (!request) | |
556 return this._status.E_NOTFOUND(message.id); | |
557 this._getResourceContent(request, message, port); | |
558 }, | |
559 | |
560 _onGetResourceContent: function(message, port) | |
561 { | |
562 var url = /** @type {string} */ (message.url); | |
563 var contentProvider = WebInspector.workspace.uiSourceCodeForURL(url) ||
WebInspector.resourceForURL(url); | |
564 if (!contentProvider) | |
565 return this._status.E_NOTFOUND(url); | |
566 this._getResourceContent(contentProvider, message, port); | |
567 }, | |
568 | |
569 _onSetResourceContent: function(message, port) | |
570 { | |
571 /** | |
572 * @param {?Protocol.Error} error | |
573 * @this {WebInspector.ExtensionServer} | |
574 */ | |
575 function callbackWrapper(error) | |
576 { | |
577 var response = error ? this._status.E_FAILED(error) : this._status.O
K(); | |
578 this._dispatchCallback(message.requestId, port, response); | |
579 } | |
580 | |
581 var url = /** @type {string} */ (message.url); | |
582 var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(url); | |
583 if (!uiSourceCode || !uiSourceCode.contentType().isDocumentOrScriptOrSty
leSheet()) { | |
584 var resource = WebInspector.ResourceTreeModel.resourceForURL(url); | |
585 if (!resource) | |
586 return this._status.E_NOTFOUND(url); | |
587 return this._status.E_NOTSUPPORTED("Resource is not editable"); | |
588 } | |
589 uiSourceCode.setWorkingCopy(message.content); | |
590 if (message.commit) | |
591 uiSourceCode.commitWorkingCopy(); | |
592 callbackWrapper.call(this, null); | |
593 }, | |
594 | |
595 _requestId: function(request) | |
596 { | |
597 if (!request._extensionRequestId) { | |
598 request._extensionRequestId = ++this._lastRequestId; | |
599 this._requests[request._extensionRequestId] = request; | |
600 } | |
601 return request._extensionRequestId; | |
602 }, | |
603 | |
604 _requestById: function(id) | |
605 { | |
606 return this._requests[id]; | |
607 }, | |
608 | |
609 _onAddAuditCategory: function(message, port) | |
610 { | |
611 var category = new WebInspector.ExtensionAuditCategory(port._extensionOr
igin, message.id, message.displayName, message.resultCount); | |
612 this._clientObjects[message.id] = category; | |
613 this._auditCategories.push(category); | |
614 this.dispatchEventToListeners(WebInspector.ExtensionServer.Events.AuditC
ategoryAdded, category); | |
615 }, | |
616 | |
617 /** | |
618 * @param {!Object} message | |
619 * @param {!MessagePort} port | |
620 */ | |
621 _onAddTraceProvider: function(message, port) | |
622 { | |
623 var provider = new WebInspector.ExtensionTraceProvider(port._extensionOr
igin, message.id, message.categoryName, message.categoryTooltip); | |
624 this._clientObjects[message.id] = provider; | |
625 this._traceProviders.push(provider); | |
626 }, | |
627 | |
628 /** | |
629 * @return {!Array<!WebInspector.ExtensionTraceProvider>} | |
630 */ | |
631 traceProviders: function() | |
632 { | |
633 return this._traceProviders; | |
634 }, | |
635 | |
636 /** | |
637 * @return {!Array.<!WebInspector.ExtensionAuditCategory>} | |
638 */ | |
639 auditCategories: function() | |
640 { | |
641 return this._auditCategories; | |
642 }, | |
643 | |
644 _onAddAuditResult: function(message) | |
645 { | |
646 var auditResult = /** {!WebInspector.ExtensionAuditCategoryResults} */ (
this._clientObjects[message.resultId]); | |
647 if (!auditResult) | |
648 return this._status.E_NOTFOUND(message.resultId); | |
649 try { | |
650 auditResult.addResult(message.displayName, message.description, mess
age.severity, message.details); | |
651 } catch (e) { | |
652 return e; | |
653 } | |
654 return this._status.OK(); | |
655 }, | |
656 | |
657 _onUpdateAuditProgress: function(message) | |
658 { | |
659 var auditResult = /** {!WebInspector.ExtensionAuditCategoryResults} */ (
this._clientObjects[message.resultId]); | |
660 if (!auditResult) | |
661 return this._status.E_NOTFOUND(message.resultId); | |
662 auditResult.updateProgress(Math.min(Math.max(0, message.progress), 1)); | |
663 }, | |
664 | |
665 _onStopAuditCategoryRun: function(message) | |
666 { | |
667 var auditRun = /** {!WebInspector.ExtensionAuditCategoryResults} */ (thi
s._clientObjects[message.resultId]); | |
668 if (!auditRun) | |
669 return this._status.E_NOTFOUND(message.resultId); | |
670 auditRun.done(); | |
671 }, | |
672 | |
673 _onForwardKeyboardEvent: function(message) | |
674 { | |
675 message.entries.forEach(handleEventEntry); | |
676 | |
677 /** | |
678 * @param {*} entry | |
679 * @suppressGlobalPropertiesCheck | |
680 */ | |
681 function handleEventEntry(entry) | |
682 { | |
683 if (!entry.ctrlKey && !entry.altKey && !entry.metaKey && !/^F\d+$/.t
est(entry.key) && entry.key !== "Escape") | |
684 return; | |
685 // Fool around closure compiler -- it has its own notion of both Key
boardEvent constructor | |
686 // and initKeyboardEvent methods and overriding these in externs.js
does not have effect. | |
687 var event = new window.KeyboardEvent(entry.eventType, { | |
688 key: entry.key, | |
689 code: entry.code, | |
690 keyCode: entry.keyCode, | |
691 location: entry.location, | |
692 ctrlKey: entry.ctrlKey, | |
693 altKey: entry.altKey, | |
694 shiftKey: entry.shiftKey, | |
695 metaKey: entry.metaKey | |
696 }); | |
697 event.__keyCode = keyCodeForEntry(entry); | |
698 document.dispatchEvent(event); | |
699 } | |
700 | |
701 function keyCodeForEntry(entry) | |
702 { | |
703 var keyCode = entry.keyCode; | |
704 if (!keyCode) { | |
705 // This is required only for synthetic events (e.g. dispatched i
n tests). | |
706 if (entry.key === "Escape") | |
707 keyCode = 27; | |
708 } | |
709 return keyCode || 0; | |
710 } | |
711 }, | |
712 | |
713 _dispatchCallback: function(requestId, port, result) | |
714 { | |
715 if (requestId) | |
716 port.postMessage({ command: "callback", requestId: requestId, result
: result }); | |
717 }, | |
718 | |
719 _initExtensions: function() | |
720 { | |
721 this._registerAutosubscriptionHandler(WebInspector.extensionAPI.Events.R
esourceAdded, | |
722 WebInspector.workspace, WebInspector.Workspace.Events.UISourceCodeAd
ded, this._notifyResourceAdded); | |
723 this._registerAutosubscriptionTargetManagerHandler(WebInspector.extensio
nAPI.Events.NetworkRequestFinished, | |
724 WebInspector.NetworkManager, WebInspector.NetworkManager.Events.Requ
estFinished, this._notifyRequestFinished); | |
725 | |
726 /** | |
727 * @this {WebInspector.ExtensionServer} | |
728 */ | |
729 function onElementsSubscriptionStarted() | |
730 { | |
731 WebInspector.context.addFlavorChangeListener(WebInspector.DOMNode, t
his._notifyElementsSelectionChanged, this); | |
732 } | |
733 | |
734 /** | |
735 * @this {WebInspector.ExtensionServer} | |
736 */ | |
737 function onElementsSubscriptionStopped() | |
738 { | |
739 WebInspector.context.removeFlavorChangeListener(WebInspector.DOMNode
, this._notifyElementsSelectionChanged, this); | |
740 } | |
741 | |
742 this._registerSubscriptionHandler(WebInspector.extensionAPI.Events.Panel
ObjectSelected + "elements", | |
743 onElementsSubscriptionStarted.bind(this), onElementsSubscriptionStop
ped.bind(this)); | |
744 this._registerResourceContentCommittedHandler(this._notifyUISourceCodeCo
ntentCommitted); | |
745 | |
746 WebInspector.targetManager.addEventListener(WebInspector.TargetManager.E
vents.InspectedURLChanged, | |
747 this._inspectedURLChanged, this); | |
748 | |
749 InspectorExtensionRegistry.getExtensionsAsync(); | |
750 }, | |
751 | |
752 _notifyResourceAdded: function(event) | |
753 { | |
754 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data
); | |
755 this._postNotification(WebInspector.extensionAPI.Events.ResourceAdded, t
his._makeResource(uiSourceCode)); | |
756 }, | |
757 | |
758 _notifyUISourceCodeContentCommitted: function(event) | |
759 { | |
760 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data
.uiSourceCode); | |
761 var content = /** @type {string} */ (event.data.content); | |
762 this._postNotification(WebInspector.extensionAPI.Events.ResourceContentC
ommitted, this._makeResource(uiSourceCode), content); | |
763 }, | |
764 | |
765 _notifyRequestFinished: function(event) | |
766 { | |
767 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data); | |
768 this._postNotification(WebInspector.extensionAPI.Events.NetworkRequestFi
nished, this._requestId(request), (new WebInspector.HAREntry(request)).build()); | |
769 }, | |
770 | |
771 _notifyElementsSelectionChanged: function() | |
772 { | |
773 this._postNotification(WebInspector.extensionAPI.Events.PanelObjectSelec
ted + "elements"); | |
774 }, | |
775 | |
776 /** | |
777 * @param {!WebInspector.Event} event | |
778 */ | |
779 _addExtensions: function(event) | |
780 { | |
781 if (WebInspector.extensionServer._overridePlatformExtensionAPIForTest) | |
782 window.buildPlatformExtensionAPI = WebInspector.extensionServer._ove
rridePlatformExtensionAPIForTest; | |
783 | |
784 var extensionInfos = /** @type {!Array.<!ExtensionDescriptor>} */ (event
.data); | |
785 if (this._initializeCommandIssued) | |
786 extensionInfos.forEach(this._addExtension, this); | |
787 else | |
788 this._pendingExtensionInfos = extensionInfos; | |
789 }, | |
790 | |
791 /** | |
792 * @param {!WebInspector.Event} event | |
793 */ | |
794 _setInspectedTabId: function(event) | |
795 { | |
796 this._inspectedTabId = /** @type {string} */ (event.data); | |
797 }, | |
798 | |
799 /** | |
800 * @param {!ExtensionDescriptor} extensionInfo | |
801 * @suppressGlobalPropertiesCheck | |
802 */ | |
803 _addExtension: function(extensionInfo) | |
804 { | |
805 const urlOriginRegExp = new RegExp("([^:]+:\/\/[^/]*)\/"); // Can't use
regexp literal here, MinJS chokes on it. | |
806 var startPage = extensionInfo.startPage; | |
807 var name = extensionInfo.name; | |
808 | |
809 try { | |
810 var originMatch = urlOriginRegExp.exec(startPage); | |
811 if (!originMatch) { | |
812 console.error("Skipping extension with invalid URL: " + startPag
e); | |
813 return false; | |
814 } | |
815 var extensionOrigin = originMatch[1]; | |
816 if (!this._registeredExtensions[extensionOrigin]) { | |
817 // See ExtensionAPI.js for details. | |
818 var injectedAPI = buildExtensionAPIInjectedScript(extensionInfo,
this._inspectedTabId, WebInspector.themeSupport.themeName(), WebInspector.exten
sionServer["_extensionAPITestHook"]); | |
819 InspectorFrontendHost.setInjectedScriptForOrigin(extensionOrigin
, injectedAPI); | |
820 this._registeredExtensions[extensionOrigin] = { name: name }; | |
821 } | |
822 var iframe = createElement("iframe"); | |
823 iframe.src = startPage; | |
824 iframe.style.display = "none"; | |
825 document.body.appendChild(iframe); // Only for main window. | |
826 } catch (e) { | |
827 console.error("Failed to initialize extension " + startPage + ":" +
e); | |
828 return false; | |
829 } | |
830 return true; | |
831 }, | |
832 | |
833 _registerExtension: function(origin, port) | |
834 { | |
835 if (!this._registeredExtensions.hasOwnProperty(origin)) { | |
836 if (origin !== window.location.origin) // Just ignore inspector fram
es. | |
837 console.error("Ignoring unauthorized client request from " + ori
gin); | |
838 return; | |
839 } | |
840 port._extensionOrigin = origin; | |
841 port.addEventListener("message", this._onmessage.bind(this), false); | |
842 port.start(); | |
843 }, | |
844 | |
845 _onWindowMessage: function(event) | |
846 { | |
847 if (event.data === "registerExtension") | |
848 this._registerExtension(event.origin, event.ports[0]); | |
849 }, | |
850 | |
851 _onmessage: function(event) | |
852 { | |
853 var message = event.data; | |
854 var result; | |
855 | |
856 if (message.command in this._handlers) | |
857 result = this._handlers[message.command](message, event.target); | |
858 else | |
859 result = this._status.E_NOTSUPPORTED(message.command); | |
860 | |
861 if (result && message.requestId) | |
862 this._dispatchCallback(message.requestId, event.target, result); | |
863 }, | |
864 | |
865 _registerHandler: function(command, callback) | |
866 { | |
867 console.assert(command); | |
868 this._handlers[command] = callback; | |
869 }, | |
870 | |
871 _registerSubscriptionHandler: function(eventTopic, onSubscribeFirst, onUnsub
scribeLast) | |
872 { | |
873 this._subscriptionStartHandlers[eventTopic] = onSubscribeFirst; | |
874 this._subscriptionStopHandlers[eventTopic] = onUnsubscribeLast; | |
875 }, | |
876 | |
877 /** | |
878 * @param {string} eventTopic | |
879 * @param {!Object} eventTarget | |
880 * @param {string} frontendEventType | |
881 * @param {function(!WebInspector.Event)} handler | |
882 */ | |
883 _registerAutosubscriptionHandler: function(eventTopic, eventTarget, frontend
EventType, handler) | |
884 { | |
885 this._registerSubscriptionHandler(eventTopic, | |
886 eventTarget.addEventListener.bind(eventTarget, frontendEventType, ha
ndler, this), | |
887 eventTarget.removeEventListener.bind(eventTarget, frontendEventType,
handler, this)); | |
888 }, | |
889 | |
890 /** | |
891 * @param {string} eventTopic | |
892 * @param {!Function} modelClass | |
893 * @param {string} frontendEventType | |
894 * @param {function(!WebInspector.Event)} handler | |
895 */ | |
896 _registerAutosubscriptionTargetManagerHandler: function(eventTopic, modelCla
ss, frontendEventType, handler) | |
897 { | |
898 this._registerSubscriptionHandler(eventTopic, | |
899 WebInspector.targetManager.addModelListener.bind(WebInspector.target
Manager, modelClass, frontendEventType, handler, this), | |
900 WebInspector.targetManager.removeModelListener.bind(WebInspector.tar
getManager, modelClass, frontendEventType, handler, this)); | |
901 }, | |
902 | |
903 _registerResourceContentCommittedHandler: function(handler) | |
904 { | |
905 /** | |
906 * @this {WebInspector.ExtensionServer} | |
907 */ | |
908 function addFirstEventListener() | |
909 { | |
910 WebInspector.workspace.addEventListener(WebInspector.Workspace.Event
s.WorkingCopyCommittedByUser, handler, this); | |
911 WebInspector.workspace.setHasResourceContentTrackingExtensions(true)
; | |
912 } | |
913 | |
914 /** | |
915 * @this {WebInspector.ExtensionServer} | |
916 */ | |
917 function removeLastEventListener() | |
918 { | |
919 WebInspector.workspace.setHasResourceContentTrackingExtensions(false
); | |
920 WebInspector.workspace.removeEventListener(WebInspector.Workspace.Ev
ents.WorkingCopyCommittedByUser, handler, this); | |
921 } | |
922 | |
923 this._registerSubscriptionHandler(WebInspector.extensionAPI.Events.Resou
rceContentCommitted, | |
924 addFirstEventListener.bind(this), | |
925 removeLastEventListener.bind(this)); | |
926 }, | |
927 | |
928 _expandResourcePath: function(extensionPath, resourcePath) | |
929 { | |
930 if (!resourcePath) | |
931 return; | |
932 return extensionPath + this._normalizePath(resourcePath); | |
933 }, | |
934 | |
935 _normalizePath: function(path) | |
936 { | |
937 var source = path.split("/"); | |
938 var result = []; | |
939 | |
940 for (var i = 0; i < source.length; ++i) { | |
941 if (source[i] === ".") | |
942 continue; | |
943 // Ignore empty path components resulting from //, as well as a lead
ing and traling slashes. | |
944 if (source[i] === "") | |
945 continue; | |
946 if (source[i] === "..") | |
947 result.pop(); | |
948 else | |
949 result.push(source[i]); | |
950 } | |
951 return "/" + result.join("/"); | |
952 }, | |
953 | |
954 /** | |
955 * @param {string} expression | |
956 * @param {boolean} exposeCommandLineAPI | |
957 * @param {boolean} returnByValue | |
958 * @param {?Object} options | |
959 * @param {string} securityOrigin | |
960 * @param {function(?string, ?WebInspector.RemoteObject, boolean=)} callback | |
961 * @return {!WebInspector.ExtensionStatus.Record|undefined} | |
962 */ | |
963 evaluate: function(expression, exposeCommandLineAPI, returnByValue, options,
securityOrigin, callback) | |
964 { | |
965 var contextId; | |
966 | |
967 /** | |
968 * @param {string} url | |
969 * @return {boolean} | |
970 */ | |
971 function resolveURLToFrame(url) | |
972 { | |
973 var found; | |
974 function hasMatchingURL(frame) | |
975 { | |
976 found = (frame.url === url) ? frame : null; | |
977 return found; | |
978 } | |
979 WebInspector.ResourceTreeModel.frames().some(hasMatchingURL); | |
980 return found; | |
981 } | |
982 | |
983 if (typeof options === "object") { | |
984 var frame; | |
985 if (options.frameURL) { | |
986 frame = resolveURLToFrame(options.frameURL); | |
987 } else { | |
988 var target = WebInspector.targetManager.mainTarget(); | |
989 var resourceTreeModel = target && WebInspector.ResourceTreeModel
.fromTarget(target); | |
990 frame = resourceTreeModel && resourceTreeModel.mainFrame; | |
991 } | |
992 if (!frame) { | |
993 if (options.frameURL) | |
994 console.warn("evaluate: there is no frame with URL " + optio
ns.frameURL); | |
995 else | |
996 console.warn("evaluate: the main frame is not yet available"
); | |
997 return this._status.E_NOTFOUND(options.frameURL || "<top>"); | |
998 } | |
999 | |
1000 var contextSecurityOrigin; | |
1001 if (options.useContentScriptContext) | |
1002 contextSecurityOrigin = securityOrigin; | |
1003 else if (options.scriptExecutionContext) | |
1004 contextSecurityOrigin = options.scriptExecutionContext; | |
1005 | |
1006 var context; | |
1007 var executionContexts = frame.target().runtimeModel.executionContext
s(); | |
1008 if (contextSecurityOrigin) { | |
1009 for (var i = 0; i < executionContexts.length; ++i) { | |
1010 var executionContext = executionContexts[i]; | |
1011 if (executionContext.frameId === frame.id && executionContex
t.origin === contextSecurityOrigin && !executionContext.isDefault) | |
1012 context = executionContext; | |
1013 | |
1014 } | |
1015 if (!context) { | |
1016 console.warn("The JavaScript context " + contextSecurityOrig
in + " was not found in the frame " + frame.url); | |
1017 return this._status.E_NOTFOUND(contextSecurityOrigin); | |
1018 } | |
1019 } else { | |
1020 for (var i = 0; i < executionContexts.length; ++i) { | |
1021 var executionContext = executionContexts[i]; | |
1022 if (executionContext.frameId === frame.id && executionContex
t.isDefault) | |
1023 context = executionContext; | |
1024 | |
1025 } | |
1026 if (!context) | |
1027 return this._status.E_FAILED(frame.url + " has no execution
context"); | |
1028 } | |
1029 | |
1030 contextId = context.id; | |
1031 } | |
1032 var target = target ? target : WebInspector.targetManager.mainTarget(); | |
1033 if (!target) | |
1034 return; | |
1035 | |
1036 target.runtimeAgent().evaluate(expression, "extension", exposeCommandLin
eAPI, true, contextId, returnByValue, false, false, false, onEvalute); | |
1037 | |
1038 /** | |
1039 * @param {?Protocol.Error} error | |
1040 * @param {!RuntimeAgent.RemoteObject} result | |
1041 * @param {!RuntimeAgent.ExceptionDetails=} exceptionDetails | |
1042 */ | |
1043 function onEvalute(error, result, exceptionDetails) | |
1044 { | |
1045 if (error) { | |
1046 callback(error, null, !!exceptionDetails); | |
1047 return; | |
1048 } | |
1049 callback(error, target.runtimeModel.createRemoteObject(result), !!ex
ceptionDetails); | |
1050 } | |
1051 }, | |
1052 | |
1053 __proto__: WebInspector.Object.prototype | |
1054 }; | |
1055 | |
1056 /** | 980 /** |
1057 * @constructor | 981 * @unrestricted |
1058 * @param {string} name | |
1059 * @param {string} title | |
1060 * @param {!WebInspector.Panel} panel | |
1061 * @extends {WebInspector.SimpleView} | |
1062 */ | 982 */ |
1063 WebInspector.ExtensionServerPanelView = function(name, title, panel) | 983 WebInspector.ExtensionServerPanelView = class extends WebInspector.SimpleView { |
1064 { | 984 /** |
1065 WebInspector.SimpleView.call(this, title); | 985 * @param {string} name |
| 986 * @param {string} title |
| 987 * @param {!WebInspector.Panel} panel |
| 988 */ |
| 989 constructor(name, title, panel) { |
| 990 super(title); |
1066 this._name = name; | 991 this._name = name; |
1067 this._panel = panel; | 992 this._panel = panel; |
| 993 } |
| 994 |
| 995 /** |
| 996 * @override |
| 997 * @return {string} |
| 998 */ |
| 999 viewId() { |
| 1000 return this._name; |
| 1001 } |
| 1002 |
| 1003 /** |
| 1004 * @override |
| 1005 * @return {!Promise.<!WebInspector.Widget>} |
| 1006 */ |
| 1007 widget() { |
| 1008 return /** @type {!Promise.<!WebInspector.Widget>} */ (Promise.resolve(this.
_panel)); |
| 1009 } |
1068 }; | 1010 }; |
1069 | 1011 |
1070 WebInspector.ExtensionServerPanelView.prototype = { | |
1071 /** | |
1072 * @override | |
1073 * @return {string} | |
1074 */ | |
1075 viewId: function() | |
1076 { | |
1077 return this._name; | |
1078 }, | |
1079 | |
1080 /** | |
1081 * @override | |
1082 * @return {!Promise.<!WebInspector.Widget>} | |
1083 */ | |
1084 widget: function() | |
1085 { | |
1086 return /** @type {!Promise.<!WebInspector.Widget>} */ (Promise.resolve(t
his._panel)); | |
1087 }, | |
1088 | |
1089 __proto__: WebInspector.SimpleView.prototype | |
1090 }; | |
1091 | |
1092 /** | 1012 /** |
1093 * @constructor | 1013 * @unrestricted |
1094 */ | 1014 */ |
1095 WebInspector.ExtensionStatus = function() | 1015 WebInspector.ExtensionStatus = class { |
1096 { | 1016 constructor() { |
1097 /** | 1017 /** |
1098 * @param {string} code | 1018 * @param {string} code |
1099 * @param {string} description | 1019 * @param {string} description |
1100 * @return {!WebInspector.ExtensionStatus.Record} | 1020 * @return {!WebInspector.ExtensionStatus.Record} |
1101 */ | 1021 */ |
1102 function makeStatus(code, description) | 1022 function makeStatus(code, description) { |
1103 { | 1023 var details = Array.prototype.slice.call(arguments, 2); |
1104 var details = Array.prototype.slice.call(arguments, 2); | 1024 var status = {code: code, description: description, details: details}; |
1105 var status = { code: code, description: description, details: details }; | 1025 if (code !== 'OK') { |
1106 if (code !== "OK") { | 1026 status.isError = true; |
1107 status.isError = true; | 1027 console.log('Extension server error: ' + String.vsprintf(description, de
tails)); |
1108 console.log("Extension server error: " + String.vsprintf(description
, details)); | 1028 } |
1109 } | 1029 return status; |
1110 return status; | 1030 } |
1111 } | 1031 |
1112 | 1032 this.OK = makeStatus.bind(null, 'OK', 'OK'); |
1113 this.OK = makeStatus.bind(null, "OK", "OK"); | 1033 this.E_EXISTS = makeStatus.bind(null, 'E_EXISTS', 'Object already exists: %s
'); |
1114 this.E_EXISTS = makeStatus.bind(null, "E_EXISTS", "Object already exists: %s
"); | 1034 this.E_BADARG = makeStatus.bind(null, 'E_BADARG', 'Invalid argument %s: %s')
; |
1115 this.E_BADARG = makeStatus.bind(null, "E_BADARG", "Invalid argument %s: %s")
; | 1035 this.E_BADARGTYPE = makeStatus.bind(null, 'E_BADARGTYPE', 'Invalid type for
argument %s: got %s, expected %s'); |
1116 this.E_BADARGTYPE = makeStatus.bind(null, "E_BADARGTYPE", "Invalid type for
argument %s: got %s, expected %s"); | 1036 this.E_NOTFOUND = makeStatus.bind(null, 'E_NOTFOUND', 'Object not found: %s'
); |
1117 this.E_NOTFOUND = makeStatus.bind(null, "E_NOTFOUND", "Object not found: %s"
); | 1037 this.E_NOTSUPPORTED = makeStatus.bind(null, 'E_NOTSUPPORTED', 'Object does n
ot support requested operation: %s'); |
1118 this.E_NOTSUPPORTED = makeStatus.bind(null, "E_NOTSUPPORTED", "Object does n
ot support requested operation: %s"); | 1038 this.E_PROTOCOLERROR = makeStatus.bind(null, 'E_PROTOCOLERROR', 'Inspector p
rotocol error: %s'); |
1119 this.E_PROTOCOLERROR = makeStatus.bind(null, "E_PROTOCOLERROR", "Inspector p
rotocol error: %s"); | 1039 this.E_FAILED = makeStatus.bind(null, 'E_FAILED', 'Operation failed: %s'); |
1120 this.E_FAILED = makeStatus.bind(null, "E_FAILED", "Operation failed: %s"); | 1040 } |
1121 }; | 1041 }; |
1122 | 1042 |
1123 /** | 1043 /** |
1124 * @typedef {{code: string, description: string, details: !Array.<*>}} | 1044 * @typedef {{code: string, description: string, details: !Array.<*>}} |
1125 */ | 1045 */ |
1126 WebInspector.ExtensionStatus.Record; | 1046 WebInspector.ExtensionStatus.Record; |
1127 | 1047 |
1128 WebInspector.extensionAPI = {}; | 1048 WebInspector.extensionAPI = {}; |
1129 defineCommonExtensionSymbols(WebInspector.extensionAPI); | 1049 defineCommonExtensionSymbols(WebInspector.extensionAPI); |
1130 | 1050 |
1131 /** @type {!WebInspector.ExtensionServer} */ | 1051 /** @type {!WebInspector.ExtensionServer} */ |
1132 WebInspector.extensionServer; | 1052 WebInspector.extensionServer; |
OLD | NEW |