| 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 |