2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @extends {WebInspector.Object}
34 * @suppressGlobalPropertiesCheck
36 WebInspector
.ExtensionServer = function()
38 this._clientObjects
= {};
40 this._subscribers
= {};
41 this._subscriptionStartHandlers
= {};
42 this._subscriptionStopHandlers
= {};
43 this._extraHeaders
= {};
45 this._lastRequestId
= 0;
46 this._registeredExtensions
= {};
47 this._status
= new WebInspector
.ExtensionStatus();
48 /** @type {!Array.<!WebInspector.ExtensionSidebarPane>} */
49 this._sidebarPanes
= [];
50 /** @type {!Array.<!WebInspector.ExtensionAuditCategory>} */
51 this._auditCategories
= [];
53 var commands
= WebInspector
.extensionAPI
.Commands
;
55 this._registerHandler(commands
.AddAuditCategory
, this._onAddAuditCategory
.bind(this));
56 this._registerHandler(commands
.AddAuditResult
, this._onAddAuditResult
.bind(this));
57 this._registerHandler(commands
.AddRequestHeaders
, this._onAddRequestHeaders
.bind(this));
58 this._registerHandler(commands
.ApplyStyleSheet
, this._onApplyStyleSheet
.bind(this));
59 this._registerHandler(commands
.CreatePanel
, this._onCreatePanel
.bind(this));
60 this._registerHandler(commands
.CreateSidebarPane
, this._onCreateSidebarPane
.bind(this));
61 this._registerHandler(commands
.CreateToolbarButton
, this._onCreateToolbarButton
.bind(this));
62 this._registerHandler(commands
.EvaluateOnInspectedPage
, this._onEvaluateOnInspectedPage
.bind(this));
63 this._registerHandler(commands
.ForwardKeyboardEvent
, this._onForwardKeyboardEvent
.bind(this));
64 this._registerHandler(commands
.GetHAR
, this._onGetHAR
.bind(this));
65 this._registerHandler(commands
.GetPageResources
, this._onGetPageResources
.bind(this));
66 this._registerHandler(commands
.GetRequestContent
, this._onGetRequestContent
.bind(this));
67 this._registerHandler(commands
.GetResourceContent
, this._onGetResourceContent
.bind(this));
68 this._registerHandler(commands
.Reload
, this._onReload
.bind(this));
69 this._registerHandler(commands
.SetOpenResourceHandler
, this._onSetOpenResourceHandler
.bind(this));
70 this._registerHandler(commands
.SetResourceContent
, this._onSetResourceContent
.bind(this));
71 this._registerHandler(commands
.SetSidebarHeight
, this._onSetSidebarHeight
.bind(this));
72 this._registerHandler(commands
.SetSidebarContent
, this._onSetSidebarContent
.bind(this));
73 this._registerHandler(commands
.SetSidebarPage
, this._onSetSidebarPage
.bind(this));
74 this._registerHandler(commands
.ShowPanel
, this._onShowPanel
.bind(this));
75 this._registerHandler(commands
.StopAuditCategoryRun
, this._onStopAuditCategoryRun
.bind(this));
76 this._registerHandler(commands
.Subscribe
, this._onSubscribe
.bind(this));
77 this._registerHandler(commands
.OpenResource
, this._onOpenResource
.bind(this));
78 this._registerHandler(commands
.Unsubscribe
, this._onUnsubscribe
.bind(this));
79 this._registerHandler(commands
.UpdateButton
, this._onUpdateButton
.bind(this));
80 this._registerHandler(commands
.UpdateAuditProgress
, this._onUpdateAuditProgress
.bind(this));
81 window
.addEventListener("message", this._onWindowMessage
.bind(this), false); // Only for main window.
83 InspectorFrontendHost
.events
.addEventListener(InspectorFrontendHostAPI
.Events
.AddExtensions
, this._addExtensions
, this);
84 InspectorFrontendHost
.events
.addEventListener(InspectorFrontendHostAPI
.Events
.SetInspectedTabId
, this._setInspectedTabId
, this);
86 this._initExtensions();
89 WebInspector
.ExtensionServer
.Events
= {
90 SidebarPaneAdded
: "SidebarPaneAdded",
91 AuditCategoryAdded
: "AuditCategoryAdded"
94 WebInspector
.ExtensionServer
.prototype = {
95 initializeExtensions: function()
97 this._initializeCommandIssued
= true;
98 if (this._pendingExtensionInfos
) {
99 this._pendingExtensionInfos
.forEach(this._addExtension
, this);
100 delete this._pendingExtensionInfos
;
107 hasExtensions: function()
109 return !!Object
.keys(this._registeredExtensions
).length
;
113 * @param {string} panelId
114 * @param {string} action
115 * @param {string=} searchString
117 notifySearchAction: function(panelId
, action
, searchString
)
119 this._postNotification(WebInspector
.extensionAPI
.Events
.PanelSearch
+ panelId
, action
, searchString
);
123 * @param {string} identifier
124 * @param {number=} frameIndex
126 notifyViewShown: function(identifier
, frameIndex
)
128 this._postNotification(WebInspector
.extensionAPI
.Events
.ViewShown
+ identifier
, frameIndex
);
132 * @param {string} identifier
134 notifyViewHidden: function(identifier
)
136 this._postNotification(WebInspector
.extensionAPI
.Events
.ViewHidden
+ identifier
);
140 * @param {string} identifier
142 notifyButtonClicked: function(identifier
)
144 this._postNotification(WebInspector
.extensionAPI
.Events
.ButtonClicked
+ identifier
);
147 _inspectedURLChanged: function(event
)
150 var url
= event
.data
;
151 this._postNotification(WebInspector
.extensionAPI
.Events
.InspectedURLChanged
, url
);
156 * @param {string} categoryId
157 * @param {!WebInspector.ExtensionAuditCategoryResults} auditResults
159 startAuditRun: function(categoryId
, auditResults
)
161 this._clientObjects
[auditResults
.id()] = auditResults
;
162 this._postNotification("audit-started-" + categoryId
, auditResults
.id());
166 * @param {!WebInspector.ExtensionAuditCategoryResults} auditResults
168 stopAuditRun: function(auditResults
)
170 delete this._clientObjects
[auditResults
.id()];
174 * @param {string} type
177 hasSubscribers: function(type
)
179 return !!this._subscribers
[type
];
183 * @param {string} type
184 * @param {...*} vararg
186 _postNotification: function(type
, vararg
)
188 var subscribers
= this._subscribers
[type
];
192 command
: "notify-" + type
,
193 arguments
: Array
.prototype.slice
.call(arguments
, 1)
195 for (var i
= 0; i
< subscribers
.length
; ++i
)
196 subscribers
[i
].postMessage(message
);
199 _onSubscribe: function(message
, port
)
201 var subscribers
= this._subscribers
[message
.type
];
203 subscribers
.push(port
);
205 this._subscribers
[message
.type
] = [ port
];
206 if (this._subscriptionStartHandlers
[message
.type
])
207 this._subscriptionStartHandlers
[message
.type
]();
211 _onUnsubscribe: function(message
, port
)
213 var subscribers
= this._subscribers
[message
.type
];
216 subscribers
.remove(port
);
217 if (!subscribers
.length
) {
218 delete this._subscribers
[message
.type
];
219 if (this._subscriptionStopHandlers
[message
.type
])
220 this._subscriptionStopHandlers
[message
.type
]();
224 _onAddRequestHeaders: function(message
)
226 var id
= message
.extensionId
;
227 if (typeof id
!== "string")
228 return this._status
.E_BADARGTYPE("extensionId", typeof id
, "string");
229 var extensionHeaders
= this._extraHeaders
[id
];
230 if (!extensionHeaders
) {
231 extensionHeaders
= {};
232 this._extraHeaders
[id
] = extensionHeaders
;
234 for (var name
in message
.headers
)
235 extensionHeaders
[name
] = message
.headers
[name
];
236 var allHeaders
= /** @type {!NetworkAgent.Headers} */ ({});
237 for (var extension
in this._extraHeaders
) {
238 var headers
= this._extraHeaders
[extension
];
239 for (name
in headers
) {
240 if (typeof headers
[name
] === "string")
241 allHeaders
[name
] = headers
[name
];
245 WebInspector
.multitargetNetworkManager
.setExtraHTTPHeaders(allHeaders
);
250 * @suppressGlobalPropertiesCheck
252 _onApplyStyleSheet: function(message
)
254 if (!Runtime
.experiments
.isEnabled("applyCustomStylesheet"))
256 var styleSheet
= createElement("style");
257 styleSheet
.textContent
= message
.styleSheet
;
258 document
.head
.appendChild(styleSheet
);
261 _onCreatePanel: function(message
, port
)
264 // The ids are generated on the client API side and must be unique, so the check below
265 // shouldn't be hit unless someone is bypassing the API.
266 if (id
in this._clientObjects
|| WebInspector
.inspectorView
.hasPanel(id
))
267 return this._status
.E_EXISTS(id
);
269 var page
= this._expandResourcePath(port
._extensionOrigin
, message
.page
);
270 var persistentId
= port
._extensionOrigin
+ message
.title
;
271 persistentId
= persistentId
.replace(/\s/g, "");
272 var panelDescriptor
= new WebInspector
.ExtensionServerPanelDescriptor(persistentId
, message
.title
, new WebInspector
.ExtensionPanel(this, persistentId
, id
, page
));
273 this._clientObjects
[id
] = panelDescriptor
;
274 WebInspector
.inspectorView
.addPanel(panelDescriptor
);
275 return this._status
.OK();
278 _onShowPanel: function(message
)
280 var panelName
= message
.id
;
281 var panelDescriptor
= this._clientObjects
[message
.id
];
282 if (panelDescriptor
&& panelDescriptor
instanceof WebInspector
.ExtensionServerPanelDescriptor
)
283 panelName
= panelDescriptor
.name();
284 WebInspector
.inspectorView
.showPanel(panelName
);
287 _onCreateToolbarButton: function(message
, port
)
289 var panelDescriptor
= this._clientObjects
[message
.panel
];
290 if (!panelDescriptor
|| !(panelDescriptor
instanceof WebInspector
.ExtensionServerPanelDescriptor
))
291 return this._status
.E_NOTFOUND(message
.panel
);
292 var button
= new WebInspector
.ExtensionButton(this, message
.id
, this._expandResourcePath(port
._extensionOrigin
, message
.icon
), message
.tooltip
, message
.disabled
);
293 this._clientObjects
[message
.id
] = button
;
295 panelDescriptor
.panel().then(appendButton
);
298 * @param {!WebInspector.Panel} panel
300 function appendButton(panel
)
302 /** @type {!WebInspector.ExtensionPanel} panel*/ (panel
).addToolbarItem(button
.toolbarButton());
305 return this._status
.OK();
308 _onUpdateButton: function(message
, port
)
310 var button
= this._clientObjects
[message
.id
];
311 if (!button
|| !(button
instanceof WebInspector
.ExtensionButton
))
312 return this._status
.E_NOTFOUND(message
.id
);
313 button
.update(this._expandResourcePath(port
._extensionOrigin
, message
.icon
), message
.tooltip
, message
.disabled
);
314 return this._status
.OK();
317 _onCreateSidebarPane: function(message
)
319 if (message
.panel
!== "elements" && message
.panel
!== "sources")
320 return this._status
.E_NOTFOUND(message
.panel
);
322 var sidebar
= new WebInspector
.ExtensionSidebarPane(this, message
.panel
, message
.title
, id
);
323 this._sidebarPanes
.push(sidebar
);
324 this._clientObjects
[id
] = sidebar
;
325 this.dispatchEventToListeners(WebInspector
.ExtensionServer
.Events
.SidebarPaneAdded
, sidebar
);
327 return this._status
.OK();
331 * @return {!Array.<!WebInspector.ExtensionSidebarPane>}
333 sidebarPanes: function()
335 return this._sidebarPanes
;
338 _onSetSidebarHeight: function(message
)
340 var sidebar
= this._clientObjects
[message
.id
];
342 return this._status
.E_NOTFOUND(message
.id
);
343 sidebar
.setHeight(message
.height
);
344 return this._status
.OK();
347 _onSetSidebarContent: function(message
, port
)
349 var sidebar
= this._clientObjects
[message
.id
];
351 return this._status
.E_NOTFOUND(message
.id
);
354 * @this {WebInspector.ExtensionServer}
356 function callback(error
)
358 var result
= error
? this._status
.E_FAILED(error
) : this._status
.OK();
359 this._dispatchCallback(message
.requestId
, port
, result
);
361 if (message
.evaluateOnPage
)
362 return sidebar
.setExpression(message
.expression
, message
.rootTitle
, message
.evaluateOptions
, port
._extensionOrigin
, callback
.bind(this));
363 sidebar
.setObject(message
.expression
, message
.rootTitle
, callback
.bind(this));
366 _onSetSidebarPage: function(message
, port
)
368 var sidebar
= this._clientObjects
[message
.id
];
370 return this._status
.E_NOTFOUND(message
.id
);
371 sidebar
.setPage(this._expandResourcePath(port
._extensionOrigin
, message
.page
));
374 _onOpenResource: function(message
)
376 var uiSourceCode
= WebInspector
.networkMapping
.uiSourceCodeForURLForAnyTarget(message
.url
);
378 WebInspector
.Revealer
.reveal(uiSourceCode
.uiLocation(message
.lineNumber
, 0));
379 return this._status
.OK();
382 var resource
= WebInspector
.resourceForURL(message
.url
);
384 WebInspector
.Revealer
.reveal(resource
, message
.lineNumber
);
385 return this._status
.OK();
388 var request
= WebInspector
.NetworkLog
.requestForURL(message
.url
);
390 WebInspector
.Revealer
.reveal(request
);
391 return this._status
.OK();
394 return this._status
.E_NOTFOUND(message
.url
);
397 _onSetOpenResourceHandler: function(message
, port
)
399 var name
= this._registeredExtensions
[port
._extensionOrigin
].name
|| ("Extension " + port
._extensionOrigin
);
400 if (message
.handlerPresent
)
401 WebInspector
.openAnchorLocationRegistry
.registerHandler(name
, this._handleOpenURL
.bind(this, port
));
403 WebInspector
.openAnchorLocationRegistry
.unregisterHandler(name
);
406 _handleOpenURL: function(port
, details
)
408 var url
= /** @type {string} */ (details
.url
);
409 var contentProvider
= WebInspector
.workspace
.uiSourceCodeForOriginURL(url
) || WebInspector
.resourceForURL(url
);
410 if (!contentProvider
)
413 var lineNumber
= details
.lineNumber
;
414 if (typeof lineNumber
=== "number")
417 command
: "open-resource",
418 resource
: this._makeResource(contentProvider
),
419 lineNumber
: lineNumber
424 _onReload: function(message
)
426 var options
= /** @type {!ExtensionReloadOptions} */ (message
.options
|| {});
428 WebInspector
.multitargetNetworkManager
.setUserAgentOverride(typeof options
.userAgent
=== "string" ? options
.userAgent
: "");
430 if (options
.injectedScript
)
431 injectedScript
= "(function(){" + options
.injectedScript
+ "})()";
432 // Reload main frame.
433 var target
= WebInspector
.targetManager
.mainTarget();
434 target
.resourceTreeModel
.reloadPage(!!options
.ignoreCache
, injectedScript
);
435 return this._status
.OK();
438 _onEvaluateOnInspectedPage: function(message
, port
)
441 * @param {?Protocol.Error} error
442 * @param {?WebInspector.RemoteObject} remoteObject
443 * @param {boolean=} wasThrown
444 * @this {WebInspector.ExtensionServer}
446 function callback(error
, remoteObject
, wasThrown
)
449 if (error
|| !remoteObject
)
450 result
= this._status
.E_PROTOCOLERROR(error
.toString());
452 result
= { isException
: true, value
: remoteObject
.description
};
454 result
= { value
: remoteObject
.value
};
456 this._dispatchCallback(message
.requestId
, port
, result
);
458 return this.evaluate(message
.expression
, true, true, message
.evaluateOptions
, port
._extensionOrigin
, callback
.bind(this));
461 _onGetHAR: function()
463 var requests
= WebInspector
.NetworkLog
.requests();
464 var harLog
= (new WebInspector
.HARLog(requests
)).build();
465 for (var i
= 0; i
< harLog
.entries
.length
; ++i
)
466 harLog
.entries
[i
]._requestId
= this._requestId(requests
[i
]);
471 * @param {!WebInspector.ContentProvider} contentProvider
473 _makeResource: function(contentProvider
)
476 url
: contentProvider
.contentURL(),
477 type
: contentProvider
.contentType().name()
482 * @return {!Array.<!WebInspector.ContentProvider>}
484 _onGetPageResources: function()
489 * @this {WebInspector.ExtensionServer}
491 function pushResourceData(contentProvider
)
493 if (!resources
[contentProvider
.contentURL()])
494 resources
[contentProvider
.contentURL()] = this._makeResource(contentProvider
);
496 var uiSourceCodes
= WebInspector
.workspace
.uiSourceCodesForProjectType(WebInspector
.projectTypes
.Network
);
497 uiSourceCodes
= uiSourceCodes
.concat(WebInspector
.workspace
.uiSourceCodesForProjectType(WebInspector
.projectTypes
.ContentScripts
));
498 uiSourceCodes
.forEach(pushResourceData
.bind(this));
499 for (var target
of WebInspector
.targetManager
.targets())
500 target
.resourceTreeModel
.forAllResources(pushResourceData
.bind(this));
501 return Object
.values(resources
);
505 * @param {!WebInspector.ContentProvider} contentProvider
506 * @param {!Object} message
507 * @param {!MessagePort} port
509 _getResourceContent: function(contentProvider
, message
, port
)
512 * @param {?string} content
513 * @this {WebInspector.ExtensionServer}
515 function onContentAvailable(content
)
517 var contentEncoded
= false;
518 if (contentProvider
instanceof WebInspector
.Resource
)
519 contentEncoded
= contentProvider
.contentEncoded
;
520 if (contentProvider
instanceof WebInspector
.NetworkRequest
)
521 contentEncoded
= contentProvider
.contentEncoded
;
523 encoding
: contentEncoded
&& content
? "base64" : "",
526 this._dispatchCallback(message
.requestId
, port
, response
);
529 contentProvider
.requestContent(onContentAvailable
.bind(this));
532 _onGetRequestContent: function(message
, port
)
534 var request
= this._requestById(message
.id
);
536 return this._status
.E_NOTFOUND(message
.id
);
537 this._getResourceContent(request
, message
, port
);
540 _onGetResourceContent: function(message
, port
)
542 var url
= /** @type {string} */ (message
.url
);
543 var contentProvider
= WebInspector
.workspace
.uiSourceCodeForOriginURL(url
) || WebInspector
.resourceForURL(url
);
544 if (!contentProvider
)
545 return this._status
.E_NOTFOUND(url
);
546 this._getResourceContent(contentProvider
, message
, port
);
549 _onSetResourceContent: function(message
, port
)
552 * @param {?Protocol.Error} error
553 * @this {WebInspector.ExtensionServer}
555 function callbackWrapper(error
)
557 var response
= error
? this._status
.E_FAILED(error
) : this._status
.OK();
558 this._dispatchCallback(message
.requestId
, port
, response
);
561 var url
= /** @type {string} */ (message
.url
);
562 var uiSourceCode
= WebInspector
.workspace
.uiSourceCodeForOriginURL(url
);
564 var resource
= WebInspector
.ResourceTreeModel
.resourceForURL(url
);
566 return this._status
.E_NOTFOUND(url
);
567 return this._status
.E_NOTSUPPORTED("Resource is not editable");
569 uiSourceCode
.setWorkingCopy(message
.content
);
571 uiSourceCode
.commitWorkingCopy();
572 callbackWrapper
.call(this, null);
575 _requestId: function(request
)
577 if (!request
._extensionRequestId
) {
578 request
._extensionRequestId
= ++this._lastRequestId
;
579 this._requests
[request
._extensionRequestId
] = request
;
581 return request
._extensionRequestId
;
584 _requestById: function(id
)
586 return this._requests
[id
];
589 _onAddAuditCategory: function(message
, port
)
591 var category
= new WebInspector
.ExtensionAuditCategory(port
._extensionOrigin
, message
.id
, message
.displayName
, message
.resultCount
);
592 this._clientObjects
[message
.id
] = category
;
593 this._auditCategories
.push(category
);
594 this.dispatchEventToListeners(WebInspector
.ExtensionServer
.Events
.AuditCategoryAdded
, category
);
598 * @return {!Array.<!WebInspector.ExtensionAuditCategory>}
600 auditCategories: function()
602 return this._auditCategories
;
605 _onAddAuditResult: function(message
)
607 var auditResult
= /** {!WebInspector.ExtensionAuditCategoryResults} */ (this._clientObjects
[message
.resultId
]);
609 return this._status
.E_NOTFOUND(message
.resultId
);
611 auditResult
.addResult(message
.displayName
, message
.description
, message
.severity
, message
.details
);
615 return this._status
.OK();
618 _onUpdateAuditProgress: function(message
)
620 var auditResult
= /** {!WebInspector.ExtensionAuditCategoryResults} */ (this._clientObjects
[message
.resultId
]);
622 return this._status
.E_NOTFOUND(message
.resultId
);
623 auditResult
.updateProgress(Math
.min(Math
.max(0, message
.progress
), 1));
626 _onStopAuditCategoryRun: function(message
)
628 var auditRun
= /** {!WebInspector.ExtensionAuditCategoryResults} */ (this._clientObjects
[message
.resultId
]);
630 return this._status
.E_NOTFOUND(message
.resultId
);
634 _onForwardKeyboardEvent: function(message
)
636 const Esc
= "U+001B";
637 message
.entries
.forEach(handleEventEntry
);
641 * @suppressGlobalPropertiesCheck
643 function handleEventEntry(entry
)
645 if (!entry
.ctrlKey
&& !entry
.altKey
&& !entry
.metaKey
&& !/^F\d+$/.test(entry
.keyIdentifier
) && entry
.keyIdentifier
!== Esc
)
647 // Fool around closure compiler -- it has its own notion of both KeyboardEvent constructor
648 // and initKeyboardEvent methods and overriding these in externs.js does not have effect.
649 var event
= new window
.KeyboardEvent(entry
.eventType
, {
650 keyIdentifier
: entry
.keyIdentifier
,
651 location
: entry
.location
,
652 ctrlKey
: entry
.ctrlKey
,
653 altKey
: entry
.altKey
,
654 shiftKey
: entry
.shiftKey
,
655 metaKey
: entry
.metaKey
657 event
.__keyCode
= keyCodeForEntry(entry
);
658 document
.dispatchEvent(event
);
661 function keyCodeForEntry(entry
)
663 var keyCode
= entry
.keyCode
;
665 // This is required only for synthetic events (e.g. dispatched in tests).
666 var match
= entry
.keyIdentifier
.match(/^U\+([\dA-Fa-f]+)$/);
668 keyCode
= parseInt(match
[1], 16);
674 _dispatchCallback: function(requestId
, port
, result
)
677 port
.postMessage({ command
: "callback", requestId
: requestId
, result
: result
});
680 _initExtensions: function()
682 this._registerAutosubscriptionHandler(WebInspector
.extensionAPI
.Events
.ResourceAdded
,
683 WebInspector
.workspace
, WebInspector
.Workspace
.Events
.UISourceCodeAdded
, this._notifyResourceAdded
);
684 this._registerAutosubscriptionTargetManagerHandler(WebInspector
.extensionAPI
.Events
.NetworkRequestFinished
,
685 WebInspector
.NetworkManager
, WebInspector
.NetworkManager
.EventTypes
.RequestFinished
, this._notifyRequestFinished
);
688 * @this {WebInspector.ExtensionServer}
690 function onElementsSubscriptionStarted()
692 WebInspector
.notifications
.addEventListener(WebInspector
.NotificationService
.Events
.SelectedNodeChanged
, this._notifyElementsSelectionChanged
, this);
696 * @this {WebInspector.ExtensionServer}
698 function onElementsSubscriptionStopped()
700 WebInspector
.notifications
.removeEventListener(WebInspector
.NotificationService
.Events
.SelectedNodeChanged
, this._notifyElementsSelectionChanged
, this);
703 this._registerSubscriptionHandler(WebInspector
.extensionAPI
.Events
.PanelObjectSelected
+ "elements",
704 onElementsSubscriptionStarted
.bind(this), onElementsSubscriptionStopped
.bind(this));
705 this._registerResourceContentCommittedHandler(this._notifyUISourceCodeContentCommitted
);
707 WebInspector
.targetManager
.addEventListener(WebInspector
.TargetManager
.Events
.InspectedURLChanged
,
708 this._inspectedURLChanged
, this);
710 InspectorExtensionRegistry
.getExtensionsAsync();
713 _notifyResourceAdded: function(event
)
715 var uiSourceCode
= /** @type {!WebInspector.UISourceCode} */ (event
.data
);
716 this._postNotification(WebInspector
.extensionAPI
.Events
.ResourceAdded
, this._makeResource(uiSourceCode
));
719 _notifyUISourceCodeContentCommitted: function(event
)
721 var uiSourceCode
= /** @type {!WebInspector.UISourceCode} */ (event
.data
.uiSourceCode
);
722 var content
= /** @type {string} */ (event
.data
.content
);
723 this._postNotification(WebInspector
.extensionAPI
.Events
.ResourceContentCommitted
, this._makeResource(uiSourceCode
), content
);
726 _notifyRequestFinished: function(event
)
728 var request
= /** @type {!WebInspector.NetworkRequest} */ (event
.data
);
729 this._postNotification(WebInspector
.extensionAPI
.Events
.NetworkRequestFinished
, this._requestId(request
), (new WebInspector
.HAREntry(request
)).build());
732 _notifyElementsSelectionChanged: function()
734 this._postNotification(WebInspector
.extensionAPI
.Events
.PanelObjectSelected
+ "elements");
738 * @param {!WebInspector.Event} event
740 _addExtensions: function(event
)
742 if (WebInspector
.extensionServer
._overridePlatformExtensionAPIForTest
)
743 window
.buildPlatformExtensionAPI
= WebInspector
.extensionServer
._overridePlatformExtensionAPIForTest
;
745 var extensionInfos
= /** @type {!Array.<!ExtensionDescriptor>} */ (event
.data
);
746 if (this._initializeCommandIssued
)
747 extensionInfos
.forEach(this._addExtension
, this);
749 this._pendingExtensionInfos
= extensionInfos
;
753 * @param {!WebInspector.Event} event
755 _setInspectedTabId: function(event
)
757 this._inspectedTabId
= /** @type {string} */ (event
.data
);
761 * @param {!ExtensionDescriptor} extensionInfo
762 * @suppressGlobalPropertiesCheck
764 _addExtension: function(extensionInfo
)
766 const urlOriginRegExp
= new RegExp("([^:]+:\/\/[^/]*)\/"); // Can't use regexp literal here, MinJS chokes on it.
767 var startPage
= extensionInfo
.startPage
;
768 var name
= extensionInfo
.name
;
771 var originMatch
= urlOriginRegExp
.exec(startPage
);
773 console
.error("Skipping extension with invalid URL: " + startPage
);
776 var extensionOrigin
= originMatch
[1];
777 if (!this._registeredExtensions
[extensionOrigin
]) {
778 // See ExtensionAPI.js for details.
779 InspectorFrontendHost
.setInjectedScriptForOrigin(extensionOrigin
, buildExtensionAPIInjectedScript(extensionInfo
, this._inspectedTabId
));
780 this._registeredExtensions
[extensionOrigin
] = { name
: name
};
782 var iframe
= createElement("iframe");
783 iframe
.src
= startPage
;
784 iframe
.style
.display
= "none";
785 document
.body
.appendChild(iframe
); // Only for main window.
787 console
.error("Failed to initialize extension " + startPage
+ ":" + e
);
793 _registerExtension: function(origin
, port
)
795 if (!this._registeredExtensions
.hasOwnProperty(origin
)) {
796 if (origin
!== window
.location
.origin
) // Just ignore inspector frames.
797 console
.error("Ignoring unauthorized client request from " + origin
);
800 port
._extensionOrigin
= origin
;
801 port
.addEventListener("message", this._onmessage
.bind(this), false);
805 _onWindowMessage: function(event
)
807 if (event
.data
=== "registerExtension")
808 this._registerExtension(event
.origin
, event
.ports
[0]);
811 _onmessage: function(event
)
813 var message
= event
.data
;
816 if (message
.command
in this._handlers
)
817 result
= this._handlers
[message
.command
](message
, event
.target
);
819 result
= this._status
.E_NOTSUPPORTED(message
.command
);
821 if (result
&& message
.requestId
)
822 this._dispatchCallback(message
.requestId
, event
.target
, result
);
825 _registerHandler: function(command
, callback
)
827 console
.assert(command
);
828 this._handlers
[command
] = callback
;
831 _registerSubscriptionHandler: function(eventTopic
, onSubscribeFirst
, onUnsubscribeLast
)
833 this._subscriptionStartHandlers
[eventTopic
] = onSubscribeFirst
;
834 this._subscriptionStopHandlers
[eventTopic
] = onUnsubscribeLast
;
838 * @param {string} eventTopic
839 * @param {!Object} eventTarget
840 * @param {string} frontendEventType
841 * @param {function(!WebInspector.Event)} handler
843 _registerAutosubscriptionHandler: function(eventTopic
, eventTarget
, frontendEventType
, handler
)
845 this._registerSubscriptionHandler(eventTopic
,
846 eventTarget
.addEventListener
.bind(eventTarget
, frontendEventType
, handler
, this),
847 eventTarget
.removeEventListener
.bind(eventTarget
, frontendEventType
, handler
, this));
851 * @param {string} eventTopic
852 * @param {!Function} modelClass
853 * @param {string} frontendEventType
854 * @param {function(!WebInspector.Event)} handler
856 _registerAutosubscriptionTargetManagerHandler: function(eventTopic
, modelClass
, frontendEventType
, handler
)
858 this._registerSubscriptionHandler(eventTopic
,
859 WebInspector
.targetManager
.addModelListener
.bind(WebInspector
.targetManager
, modelClass
, frontendEventType
, handler
, this),
860 WebInspector
.targetManager
.removeModelListener
.bind(WebInspector
.targetManager
, modelClass
, frontendEventType
, handler
, this));
863 _registerResourceContentCommittedHandler: function(handler
)
866 * @this {WebInspector.ExtensionServer}
868 function addFirstEventListener()
870 WebInspector
.workspace
.addEventListener(WebInspector
.Workspace
.Events
.UISourceCodeContentCommitted
, handler
, this);
871 WebInspector
.workspace
.setHasResourceContentTrackingExtensions(true);
875 * @this {WebInspector.ExtensionServer}
877 function removeLastEventListener()
879 WebInspector
.workspace
.setHasResourceContentTrackingExtensions(false);
880 WebInspector
.workspace
.removeEventListener(WebInspector
.Workspace
.Events
.UISourceCodeContentCommitted
, handler
, this);
883 this._registerSubscriptionHandler(WebInspector
.extensionAPI
.Events
.ResourceContentCommitted
,
884 addFirstEventListener
.bind(this),
885 removeLastEventListener
.bind(this));
888 _expandResourcePath: function(extensionPath
, resourcePath
)
892 return extensionPath
+ this._normalizePath(resourcePath
);
895 _normalizePath: function(path
)
897 var source
= path
.split("/");
900 for (var i
= 0; i
< source
.length
; ++i
) {
901 if (source
[i
] === ".")
903 // Ignore empty path components resulting from //, as well as a leading and traling slashes.
904 if (source
[i
] === "")
906 if (source
[i
] === "..")
909 result
.push(source
[i
]);
911 return "/" + result
.join("/");
915 * @param {string} expression
916 * @param {boolean} exposeCommandLineAPI
917 * @param {boolean} returnByValue
918 * @param {?Object} options
919 * @param {string} securityOrigin
920 * @param {function(?string, ?WebInspector.RemoteObject, boolean=)} callback
921 * @return {!WebInspector.ExtensionStatus.Record|undefined}
923 evaluate: function(expression
, exposeCommandLineAPI
, returnByValue
, options
, securityOrigin
, callback
)
928 * @param {string} url
931 function resolveURLToFrame(url
)
934 function hasMatchingURL(frame
)
936 found
= (frame
.url
=== url
) ? frame
: null;
939 WebInspector
.ResourceTreeModel
.frames().some(hasMatchingURL
);
943 if (typeof options
=== "object") {
944 var frame
= options
.frameURL
? resolveURLToFrame(options
.frameURL
) : WebInspector
.targetManager
.mainTarget().resourceTreeModel
.mainFrame
;
946 if (options
.frameURL
)
947 console
.warn("evaluate: there is no frame with URL " + options
.frameURL
);
949 console
.warn("evaluate: the main frame is not yet available");
950 return this._status
.E_NOTFOUND(options
.frameURL
|| "<top>");
953 var contextSecurityOrigin
;
954 if (options
.useContentScriptContext
)
955 contextSecurityOrigin
= securityOrigin
;
956 else if (options
.scriptExecutionContext
)
957 contextSecurityOrigin
= options
.scriptExecutionContext
;
960 var executionContexts
= frame
.target().runtimeModel
.executionContexts();
961 if (contextSecurityOrigin
) {
962 for (var i
= 0; i
< executionContexts
.length
; ++i
) {
963 var executionContext
= executionContexts
[i
];
964 if (executionContext
.frameId
=== frame
.id
&& executionContext
.origin
=== contextSecurityOrigin
&& !executionContext
.isMainWorldContext
)
965 context
= executionContext
;
969 console
.warn("The JavaScript context " + contextSecurityOrigin
+ " was not found in the frame " + frame
.url
)
970 return this._status
.E_NOTFOUND(contextSecurityOrigin
)
973 for (var i
= 0; i
< executionContexts
.length
; ++i
) {
974 var executionContext
= executionContexts
[i
];
975 if (executionContext
.frameId
=== frame
.id
&& executionContext
.isMainWorldContext
)
976 context
= executionContext
;
980 return this._status
.E_FAILED(frame
.url
+ " has no execution context");
983 contextId
= context
.id
;
985 var target
= target
? target
: WebInspector
.targetManager
.mainTarget();
989 target
.runtimeAgent().evaluate(expression
, "extension", exposeCommandLineAPI
, true, contextId
, returnByValue
, false, onEvalute
);
992 * @param {?Protocol.Error} error
993 * @param {!RuntimeAgent.RemoteObject} result
994 * @param {boolean=} wasThrown
996 function onEvalute(error
, result
, wasThrown
)
999 callback(error
, null, wasThrown
);
1002 callback(error
, target
.runtimeModel
.createRemoteObject(result
), wasThrown
);
1006 __proto__
: WebInspector
.Object
.prototype
1011 * @param {string} name
1012 * @param {string} title
1013 * @param {!WebInspector.Panel} panel
1014 * @implements {WebInspector.PanelDescriptor}
1016 WebInspector
.ExtensionServerPanelDescriptor = function(name
, title
, panel
)
1019 this._title
= title
;
1020 this._panel
= panel
;
1023 WebInspector
.ExtensionServerPanelDescriptor
.prototype = {
1044 * @return {!Promise.<!WebInspector.Panel>}
1048 return Promise
.resolve(this._panel
);
1055 WebInspector
.ExtensionStatus = function()
1058 * @param {string} code
1059 * @param {string} description
1060 * @return {!WebInspector.ExtensionStatus.Record}
1062 function makeStatus(code
, description
)
1064 var details
= Array
.prototype.slice
.call(arguments
, 2);
1065 var status
= { code
: code
, description
: description
, details
: details
};
1066 if (code
!== "OK") {
1067 status
.isError
= true;
1068 console
.log("Extension server error: " + String
.vsprintf(description
, details
));
1073 this.OK
= makeStatus
.bind(null, "OK", "OK");
1074 this.E_EXISTS
= makeStatus
.bind(null, "E_EXISTS", "Object already exists: %s");
1075 this.E_BADARG
= makeStatus
.bind(null, "E_BADARG", "Invalid argument %s: %s");
1076 this.E_BADARGTYPE
= makeStatus
.bind(null, "E_BADARGTYPE", "Invalid type for argument %s: got %s, expected %s");
1077 this.E_NOTFOUND
= makeStatus
.bind(null, "E_NOTFOUND", "Object not found: %s");
1078 this.E_NOTSUPPORTED
= makeStatus
.bind(null, "E_NOTSUPPORTED", "Object does not support requested operation: %s");
1079 this.E_PROTOCOLERROR
= makeStatus
.bind(null, "E_PROTOCOLERROR", "Inspector protocol error: %s");
1080 this.E_FAILED
= makeStatus
.bind(null, "E_FAILED", "Operation failed: %s");
1084 * @typedef {{code: string, description: string, details: !Array.<*>}}
1086 WebInspector
.ExtensionStatus
.Record
;
1088 WebInspector
.extensionAPI
= {};
1089 defineCommonExtensionSymbols(WebInspector
.extensionAPI
);