Chromium Code Reviews| Index: Source/devtools/front_end/extensions/ExtensionServer.js |
| diff --git a/Source/devtools/front_end/extensions/ExtensionServer.js b/Source/devtools/front_end/extensions/ExtensionServer.js |
| index 5d8cab22c3660a102f3c0efd86ddfb78d265cc8e..2a675e50a613144960313b1961dda3915ae01502 100644 |
| --- a/Source/devtools/front_end/extensions/ExtensionServer.js |
| +++ b/Source/devtools/front_end/extensions/ExtensionServer.js |
| @@ -31,12 +31,15 @@ |
| /** |
| * @constructor |
| * @extends {WebInspector.Object} |
| + * @param {!WebInspector.ExtensionServer.UIDelegate} delegate |
| * @suppressGlobalPropertiesCheck |
| */ |
| -WebInspector.ExtensionServer = function() |
| +WebInspector.ExtensionServer = function(delegate) |
| { |
| + this._delegate = delegate; |
| this._clientObjects = {}; |
| this._handlers = {}; |
| + this._callbacks = {}; |
| this._subscribers = {}; |
| this._subscriptionStartHandlers = {}; |
| this._subscriptionStopHandlers = {}; |
| @@ -59,15 +62,20 @@ WebInspector.ExtensionServer = function() |
| this._registerHandler(commands.CreatePanel, this._onCreatePanel.bind(this)); |
| this._registerHandler(commands.CreateSidebarPane, this._onCreateSidebarPane.bind(this)); |
| this._registerHandler(commands.CreateToolbarButton, this._onCreateToolbarButton.bind(this)); |
| + this._registerHandler(commands.DisplaySearchResults, this._onDisplaySearchResults.bind(this)); |
| this._registerHandler(commands.EvaluateOnInspectedPage, this._onEvaluateOnInspectedPage.bind(this)); |
| this._registerHandler(commands.ForwardKeyboardEvent, this._onForwardKeyboardEvent.bind(this)); |
| this._registerHandler(commands.GetHAR, this._onGetHAR.bind(this)); |
| this._registerHandler(commands.GetPageResources, this._onGetPageResources.bind(this)); |
| this._registerHandler(commands.GetRequestContent, this._onGetRequestContent.bind(this)); |
| this._registerHandler(commands.GetResourceContent, this._onGetResourceContent.bind(this)); |
| + this._registerHandler(commands.GetResourceLineMessages, this._onGetResourceLineMessages.bind(this)); |
| + this._registerHandler(commands.RegisterLanguageService, this._onRegisterLanguageService.bind(this)); |
| + this._registerHandler(commands.RegisterMimeRecognizer, this._onRegisterMimeRecognizer.bind(this)); |
| this._registerHandler(commands.Reload, this._onReload.bind(this)); |
| this._registerHandler(commands.SetOpenResourceHandler, this._onSetOpenResourceHandler.bind(this)); |
| this._registerHandler(commands.SetResourceContent, this._onSetResourceContent.bind(this)); |
| + this._registerHandler(commands.SetResourceLineMessages, this._onSetResourceLineMessages.bind(this)); |
| this._registerHandler(commands.SetSidebarHeight, this._onSetSidebarHeight.bind(this)); |
| this._registerHandler(commands.SetSidebarContent, this._onSetSidebarContent.bind(this)); |
| this._registerHandler(commands.SetSidebarPage, this._onSetSidebarPage.bind(this)); |
| @@ -78,6 +86,7 @@ WebInspector.ExtensionServer = function() |
| this._registerHandler(commands.Unsubscribe, this._onUnsubscribe.bind(this)); |
| this._registerHandler(commands.UpdateButton, this._onUpdateButton.bind(this)); |
| this._registerHandler(commands.UpdateAuditProgress, this._onUpdateAuditProgress.bind(this)); |
| + this._registerHandler("callback", this._onCallback.bind(this)); |
| window.addEventListener("message", this._onWindowMessage.bind(this), false); // Only for main window. |
| InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.AddExtensions, this._addExtensions, this); |
| @@ -179,6 +188,26 @@ WebInspector.ExtensionServer.prototype = { |
| return !!this._subscribers[type]; |
| }, |
| + _registerCallback: function(callback) { |
| + var cbId = Math.random()*Number.MAX_VALUE; |
| + if (this._callbacks[cbId]) { |
| + return this._registerCallback(callback); |
| + } |
| + this._callbacks[cbId] = callback; |
| + return cbId; |
| + }, |
| + |
| + /** |
| + * @param {!MessagePort} port |
| + * @param {*} message |
| + * @param {?Function=} callback |
| + */ |
| + _sendRequest: function(port, message, callback) { |
| + if (typeof callback === "function") |
| + message.requestId = this._registerCallback(callback); |
| + port.postMessage(message); |
| + }, |
| + |
| /** |
| * @param {string} type |
| * @param {...*} vararg |
| @@ -247,6 +276,197 @@ WebInspector.ExtensionServer.prototype = { |
| /** |
| * @param {*} message |
| + * @param {!MessagePort} port |
| + */ |
| + _onRegisterLanguageService: function(message, port) { |
| + if (!(message.mimes && message.name && message.capabilities)) { |
| + return; |
| + } |
| + var _this = this; |
| + |
| + if (message.simplemode) { |
| + this._delegate.addSimpleCodeMirrorMode(message.mimes, message.simplemode); |
| + } |
| + |
| + var service = new WebInspector.LanguageService.CallbackDelegate(message.name, {}); |
| + |
| + if (message.capabilities.indexOf(WebInspector.LanguageService.Capabilities.Transpile) !== -1) { |
| + service.appendActionHandler(WebInspector.LanguageService.Capabilities.Transpile, function(input, location, callback) { |
| + if (!callback) { |
| + callback = location; |
| + location = undefined; |
| + } |
| + _this._sendRequest(port, { |
| + command: WebInspector.LanguageService.Capabilities.Transpile+"-"+message.name, |
| + input: input, |
| + location: location |
| + }, function(result) { |
| + if ((!result) || typeof result !== "string") { |
| + return callback(input); |
| + } |
| + callback(result); |
| + }); |
| + }); |
| + } |
| + if (message.capabilities.indexOf(WebInspector.LanguageService.Capabilities.PopulateContextMenu) !== -1) { |
| + service.appendActionHandler(WebInspector.LanguageService.Capabilities.PopulateContextMenu, function(location, callback) { |
| + _this._sendRequest(port, { |
| + command: WebInspector.LanguageService.Capabilities.PopulateContextMenu+"-"+message.name, |
| + location: location |
| + }, function(items) { |
| + if (!items || !(items instanceof Array)) { |
| + return callback([]); |
| + } |
| + callback(items.map(function(item) { |
| + if (typeof item.text !== "string") return; |
| + if (typeof item.id !== "number") return; |
| + return { |
| + text: item.text, |
| + callback: function() { |
| + _this._sendRequest(port, {command: "notify-"+WebInspector.extensionAPI.Events.ContextMenuClicked, id: item.id}); |
| + } |
| + }; |
| + }).filter(i => {return i;})); |
| + }); |
| + }); |
| + } |
| + |
| + function handleCompletionsResponse(callback) { |
| + return function(completions) { |
| + if ((!completions) || !(completions instanceof Array)) { |
| + return callback([]); |
| + } |
| + callback(completions.map(function(c){ |
| + if (typeof c.text !== "string") return; |
| + if (!(typeof c.icon === "string" || typeof c.icon === "undefined")) return; |
| + if (typeof c.id !== "number") return; |
| + return { |
| + text: c.text, |
| + icon: c.icon, |
| + details: function() { |
| + return new Promise(function(resolve, reject) { |
| + _this._sendRequest(port, {command: "completionDetails-"+message.name, id: c.id}, function(resp) { |
| + if (!resp) reject(); |
| + if (typeof resp.detail !== "string") reject(); |
| + if (typeof resp.description !== "string") reject(); |
| + resolve(resp); |
| + }); |
| + }); |
| + } |
| + }; |
| + }).filter(i => {return i;})); |
| + } |
| + } |
| + |
| + if (message.capabilities.indexOf(WebInspector.LanguageService.Capabilities.Completions) !== -1) { |
| + service.appendActionHandler(WebInspector.LanguageService.Capabilities.Completions, function(location, prefix, callback) { |
| + _this._sendRequest(port, { |
| + command: WebInspector.LanguageService.Capabilities.Completions+"-"+message.name, |
| + location: location, |
| + prefix: prefix |
| + }, handleCompletionsResponse(callback)); |
| + }); |
| + } |
| + |
| + if (message.capabilities.indexOf(WebInspector.LanguageService.Capabilities.DebuggerCompletions) !== -1) { |
| + service.appendActionHandler(WebInspector.LanguageService.Capabilities.DebuggerCompletions, function(content, cursor, prefix, context, callback) { |
| + _this._sendRequest(port, { |
| + command: WebInspector.LanguageService.Capabilities.DebuggerCompletions+"-"+message.name, |
| + content: content, |
| + cursor: cursor, |
| + prefix: prefix, |
| + context: context |
| + }, handleCompletionsResponse(callback)); |
| + }); |
| + } |
| + |
| + WebInspector.languageService.register(message.mimes, service); |
| + }, |
| + |
| + _onRegisterMimeRecognizer: function(message) { |
| + if (typeof message.mime !== "string") { |
| + return this._status.E_BADARGTYPE("mime", typeof message.mime, "string"); |
| + } |
| + if (typeof message.match !== "string") { |
| + return this._status.E_BADARGTYPE("match", typeof message.match, "string"); |
| + } |
| + WebInspector.ResourceType.registerMimeRecognizer(message.match, message.mime, message.matchName); |
| + }, |
| + |
| + _onDisplaySearchResults: function(message) { |
| + //FIXME: Search result highlighting should allow ranges to be highlighted rather than regexes |
| + var results = message.results || []; |
| + |
| + var groupedResults = results.reduce(function(accum, item) { //group by source |
| + var elem; |
| + for (var index = 0; index < accum.length; index++) { |
| + var element = accum[index]; |
| + if (element.source === item.source) { |
| + elem = element; |
| + break; |
| + } |
| + } |
| + if (!elem) { |
| + elem = []; |
| + elem.source = item.source; |
| + accum.push(elem); |
| + } |
| + elem.push({ |
| + line: item.line, |
| + lineContent: item.lineContent || '', |
| + startColumn: item.startColumn || 0, |
| + endColumn: item.endColumn || 0 |
| + }); |
| + return accum; |
| + }, []); |
| + this._delegate.displaySearchResults(results.highlight || '', groupedResults); |
| + }, |
| + |
| + /** |
| + * @param {*} message |
| + * @param {!MessagePort} port |
| + */ |
| + _onGetResourceLineMessages: function(message, port) { |
| + var url = /** @type {string} */ (message.url); |
| + var uiSourceCode = WebInspector.networkMapping.uiSourceCodeFromURL(url); |
| + if (!uiSourceCode) |
| + return this._status.E_NOTFOUND(url); |
| + |
| + this._delegate.getResourceLineMessages(uiSourceCode).then(arr => { |
| + var result = arr.map(m => { |
| + return { |
| + text: m.messageText(), |
| + kind: m.level(), |
| + location: { |
| + startLine: m.start().line, |
| + startColumn: m.start().column, |
| + endLine: m.end().line, |
| + endColumn: m.end().column |
| + } |
| + }; |
| + }); |
| + this._dispatchCallback(message.requestId, port, result); |
| + }); |
| + }, |
| + |
| + /** |
| + * @param {*} message |
| + * @param {!MessagePort} port |
| + */ |
| + _onSetResourceLineMessages: function(message, port) { |
| + var url = /** @type {string} */ (message.url); |
| + var messages = /** @type {?Array.<{kind: string, text: string, location: {startLine: number, startColumn: number, endLine: number, endColumn: number}}>} */ (message.messages); |
| + if ((!messages) || typeof messages !== "object") |
| + return this._status.E_BADARGTYPE("messages", typeof messages, "Array<LineMessage>"); |
| + var uiSourceCode = WebInspector.networkMapping.uiSourceCodeFromURL(url); |
| + if (!uiSourceCode) |
| + return this._status.E_NOTFOUND(url); |
| + this._delegate.setResourceLineMessages(uiSourceCode, messages); |
| + this._dispatchCallback(message.requestId, port, this._status.OK()); |
| + }, |
| + |
| + /** |
| + * @param {*} message |
| * @suppressGlobalPropertiesCheck |
| */ |
| _onApplyStyleSheet: function(message) |
| @@ -367,7 +587,7 @@ WebInspector.ExtensionServer.prototype = { |
| _onOpenResource: function(message) |
| { |
| - var uiSourceCode = WebInspector.networkMapping.uiSourceCodeForURLForAnyTarget(message.url); |
| + var uiSourceCode = WebInspector.networkMapping.uiSourceCodeForURLForAnyTarget(message.url) || WebInspector.workspace.uiSourceCodeForFilePath(message.url); |
|
wes
2015/08/14 01:13:32
Upon review, I see that this line is an alias to t
pfeldman
2015/08/17 21:15:51
Your message has a target, so you should get resou
wes
2015/08/25 18:13:18
The open resource message only has a url and a lin
|
| if (uiSourceCode) { |
| WebInspector.Revealer.reveal(uiSourceCode.uiLocation(message.lineNumber, 0)); |
| return this._status.OK(); |
| @@ -489,6 +709,7 @@ WebInspector.ExtensionServer.prototype = { |
| } |
| var uiSourceCodes = WebInspector.workspace.uiSourceCodesForProjectType(WebInspector.projectTypes.Network); |
| uiSourceCodes = uiSourceCodes.concat(WebInspector.workspace.uiSourceCodesForProjectType(WebInspector.projectTypes.ContentScripts)); |
| + uiSourceCodes = uiSourceCodes.concat(WebInspector.workspace.uiSourceCodesForProjectType(WebInspector.projectTypes.FileSystem)); |
| uiSourceCodes.forEach(pushResourceData.bind(this)); |
| for (var target of WebInspector.targetManager.targets()) |
| target.resourceTreeModel.forAllResources(pushResourceData.bind(this)); |
| @@ -534,7 +755,7 @@ WebInspector.ExtensionServer.prototype = { |
| _onGetResourceContent: function(message, port) |
| { |
| var url = /** @type {string} */ (message.url); |
| - var contentProvider = WebInspector.workspace.uiSourceCodeForOriginURL(url) || WebInspector.resourceForURL(url); |
| + var contentProvider = WebInspector.networkMapping.uiSourceCodeFromURL(url); |
| if (!contentProvider) |
| return this._status.E_NOTFOUND(url); |
| this._getResourceContent(contentProvider, message, port); |
| @@ -671,6 +892,15 @@ WebInspector.ExtensionServer.prototype = { |
| port.postMessage({ command: "callback", requestId: requestId, result: result }); |
| }, |
| + _onCallback: function(request, target) |
| + { |
| + if (request.requestId in this._callbacks) { |
| + var callback = this._callbacks[request.requestId]; |
| + delete this._callbacks[request.requestId]; |
| + callback(request.result); |
| + } |
| + }, |
| + |
| _initExtensions: function() |
| { |
| this._registerAutosubscriptionHandler(WebInspector.extensionAPI.Events.ResourceAdded, |
| @@ -697,6 +927,7 @@ WebInspector.ExtensionServer.prototype = { |
| this._registerSubscriptionHandler(WebInspector.extensionAPI.Events.PanelObjectSelected + "elements", |
| onElementsSubscriptionStarted.bind(this), onElementsSubscriptionStopped.bind(this)); |
| this._registerResourceContentCommittedHandler(this._notifyUISourceCodeContentCommitted); |
| + this._registerResourceContentEditedHandler(this._notifyUISourceCodeContentEdited); |
| WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.InspectedURLChanged, |
| this._inspectedURLChanged, this); |
| @@ -717,6 +948,12 @@ WebInspector.ExtensionServer.prototype = { |
| this._postNotification(WebInspector.extensionAPI.Events.ResourceContentCommitted, this._makeResource(uiSourceCode), content); |
| }, |
| + _notifyUISourceCodeContentEdited: function(event) |
| + { |
| + var data = /** @type {{uiSourceCode: !WebInspector.UISourceCode, replacement: string, range: !WebInspector.TextRange}} */ (event.data); |
| + this._postNotification(WebInspector.extensionAPI.Events.ResourceContentEdited, this._makeResource(data.uiSourceCode), data.range, data.replacement); |
| + }, |
| + |
| _notifyRequestFinished: function(event) |
| { |
| var request = /** @type {!WebInspector.NetworkRequest} */ (event.data); |
| @@ -879,6 +1116,29 @@ WebInspector.ExtensionServer.prototype = { |
| removeLastEventListener.bind(this)); |
| }, |
| + _registerResourceContentEditedHandler: function(handler) |
| + { |
| + /** |
| + * @this {WebInspector.ExtensionServer} |
| + */ |
| + function addFirstEventListener() |
| + { |
| + WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeEdited, handler, this); |
| + } |
| + |
| + /** |
| + * @this {WebInspector.ExtensionServer} |
| + */ |
| + function removeLastEventListener() |
| + { |
| + WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeEdited, handler, this); |
| + } |
| + |
| + this._registerSubscriptionHandler(WebInspector.extensionAPI.Events.ResourceContentEdited, |
| + addFirstEventListener.bind(this), |
| + removeLastEventListener.bind(this)); |
| + }, |
| + |
| _expandResourcePath: function(extensionPath, resourcePath) |
| { |
| if (!resourcePath) |
| @@ -1081,3 +1341,44 @@ WebInspector.ExtensionStatus.Record; |
| WebInspector.extensionAPI = {}; |
| defineCommonExtensionSymbols(WebInspector.extensionAPI); |
| + |
| +/** |
| + * @typedef {Array<{line: number, lineContent: string}>} |
| + * @property {string} source |
| + */ |
| +var SearchResultArray; |
| + |
| +/** |
| + * @typedef {!Array<{messageText: function(): string, level: function(): string, start: function(): {line: number, column: number}, end: function(): {line: number, column: number}}>} |
| + */ |
| +var SourceFrameMessageGetter; |
| + |
| +/** |
| + * @interface |
| + */ |
| +WebInspector.ExtensionServer.UIDelegate = function() {} |
| +WebInspector.ExtensionServer.UIDelegate.prototype = { |
| + /** |
| + * @param {!WebInspector.UISourceCode} code |
| + * @param {!Array<{kind: string, text: string, location: {startLine: number, startColumn: number, endLine: number, endColumn: number}}>} messages |
| + */ |
| + setResourceLineMessages: function(code, messages) {}, |
| + |
| + /** |
| + * @param {!WebInspector.UISourceCode} code |
| + * @return {!Promise<!SourceFrameMessageGetter>} |
| + */ |
| + getResourceLineMessages: function(code) { return Promise.reject(); }, |
| + |
| + /** |
| + * @param {string} highlightText |
| + * @param {!Array<!SearchResultArray>} groupedResults |
| + */ |
| + displaySearchResults: function(highlightText, groupedResults) {}, |
| + |
| + /** |
| + * @param {!Array<string>} mimes |
| + * @param {?} mode |
| + */ |
| + addSimpleCodeMirrorMode: function(mimes, mode){} |
| +} |