| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| /** |
| * @implements {InspectorFrontendHostAPI} |
| * @unrestricted |
| */ |
| class InspectorFrontendHostStub { |
| /** |
| * @suppressGlobalPropertiesCheck |
| */ |
| constructor() { |
| /** |
| * @param {!Event} event |
| * @this {InspectorFrontendHostAPI} |
| */ |
| function stopEventPropagation(event) { |
| // Let browser handle Ctrl+/Ctrl- shortcuts in hosted mode. |
| const zoomModifier = this.platform() === 'mac' ? event.metaKey : event.ctrlKey; |
| if (zoomModifier && (event.keyCode === 187 || event.keyCode === 189)) { |
| event.stopPropagation(); |
| } |
| } |
| document.addEventListener('keydown', stopEventPropagation.bind(this), true); |
| /** |
| * @type {!Map<string, !Array<string>>} |
| */ |
| this._urlsBeingSaved = new Map(); |
| |
| /** |
| * @type {!Common.EventTarget} |
| */ |
| this.events; |
| } |
| |
| /** |
| * @override |
| * @return {string} |
| */ |
| platform() { |
| let match = navigator.userAgent.match(/Windows NT/); |
| if (match) { |
| return 'windows'; |
| } |
| match = navigator.userAgent.match(/Mac OS X/); |
| if (match) { |
| return 'mac'; |
| } |
| return 'linux'; |
| } |
| |
| /** |
| * @override |
| */ |
| loadCompleted() { |
| } |
| |
| /** |
| * @override |
| */ |
| bringToFront() { |
| this._windowVisible = true; |
| } |
| |
| /** |
| * @override |
| */ |
| closeWindow() { |
| this._windowVisible = false; |
| } |
| |
| /** |
| * @override |
| * @param {boolean} isDocked |
| * @param {function()} callback |
| */ |
| setIsDocked(isDocked, callback) { |
| setTimeout(callback, 0); |
| } |
| |
| /** |
| * Requests inspected page to be placed atop of the inspector frontend with specified bounds. |
| * @override |
| * @param {{x: number, y: number, width: number, height: number}} bounds |
| */ |
| setInspectedPageBounds(bounds) { |
| } |
| |
| /** |
| * @override |
| */ |
| inspectElementCompleted() { |
| } |
| |
| /** |
| * @override |
| * @param {string} origin |
| * @param {string} script |
| */ |
| setInjectedScriptForOrigin(origin, script) { |
| } |
| |
| /** |
| * @override |
| * @param {string} url |
| * @suppressGlobalPropertiesCheck |
| */ |
| inspectedURLChanged(url) { |
| document.title = Common.UIString('DevTools - %s', url.replace(/^https?:\/\//, '')); |
| } |
| |
| /** |
| * @override |
| * @param {?(string|undefined)} text |
| * @suppressGlobalPropertiesCheck |
| */ |
| copyText(text) { |
| if (text === undefined || text === null) { |
| return; |
| } |
| if (navigator.clipboard) { |
| navigator.clipboard.writeText(text); |
| } else if (document.queryCommandSupported('copy')) { |
| const input = document.createElement('input'); |
| input.value = text; |
| document.body.appendChild(input); |
| input.select(); |
| document.execCommand('copy'); |
| document.body.removeChild(input); |
| } else { |
| Common.console.error('Clipboard is not enabled in hosted mode. Please inspect using chrome://inspect'); |
| } |
| } |
| |
| /** |
| * @override |
| * @param {string} url |
| */ |
| openInNewTab(url) { |
| window.open(url, '_blank'); |
| } |
| |
| /** |
| * @override |
| * @param {string} fileSystemPath |
| */ |
| showItemInFolder(fileSystemPath) { |
| Common.console.error('Show item in folder is not enabled in hosted mode. Please inspect using chrome://inspect'); |
| } |
| |
| /** |
| * @override |
| * @param {string} url |
| * @param {string} content |
| * @param {boolean} forceSaveAs |
| */ |
| save(url, content, forceSaveAs) { |
| let buffer = this._urlsBeingSaved.get(url); |
| if (!buffer) { |
| buffer = []; |
| this._urlsBeingSaved.set(url, buffer); |
| } |
| buffer.push(content); |
| this.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.SavedURL, {url, fileSystemPath: url}); |
| } |
| |
| /** |
| * @override |
| * @param {string} url |
| * @param {string} content |
| */ |
| append(url, content) { |
| const buffer = this._urlsBeingSaved.get(url); |
| buffer.push(content); |
| this.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.AppendedToURL, url); |
| } |
| |
| /** |
| * @override |
| * @param {string} url |
| */ |
| close(url) { |
| const buffer = this._urlsBeingSaved.get(url); |
| this._urlsBeingSaved.delete(url); |
| const fileName = url ? url.trimURL().removeURLFragment() : ''; |
| const link = createElement('a'); |
| link.download = fileName; |
| const blob = new Blob([buffer.join('')], {type: 'text/plain'}); |
| link.href = URL.createObjectURL(blob); |
| link.click(); |
| } |
| |
| /** |
| * @override |
| * @param {string} message |
| */ |
| sendMessageToBackend(message) { |
| } |
| |
| /** |
| * @override |
| * @param {string} actionName |
| * @param {number} actionCode |
| * @param {number} bucketSize |
| */ |
| recordEnumeratedHistogram(actionName, actionCode, bucketSize) { |
| } |
| |
| /** |
| * @override |
| * @param {string} histogramName |
| * @param {number} duration |
| */ |
| recordPerformanceHistogram(histogramName, duration) { |
| } |
| |
| /** |
| * @override |
| * @param {string} umaName |
| */ |
| recordUserMetricsAction(umaName) { |
| } |
| |
| /** |
| * @override |
| */ |
| requestFileSystems() { |
| this.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.FileSystemsLoaded, []); |
| } |
| |
| /** |
| * @override |
| * @param {string=} type |
| */ |
| addFileSystem(type) { |
| } |
| |
| /** |
| * @override |
| * @param {string} fileSystemPath |
| */ |
| removeFileSystem(fileSystemPath) { |
| } |
| |
| /** |
| * @override |
| * @param {string} fileSystemId |
| * @param {string} registeredName |
| * @return {?DOMFileSystem} |
| */ |
| isolatedFileSystem(fileSystemId, registeredName) { |
| return null; |
| } |
| |
| /** |
| * @override |
| * @param {string} url |
| * @param {string} headers |
| * @param {number} streamId |
| * @param {function(!InspectorFrontendHostAPI.LoadNetworkResourceResult)} callback |
| */ |
| loadNetworkResource(url, headers, streamId, callback) { |
| Root.Runtime.loadResourcePromise(url) |
| .then(function(text) { |
| Host.ResourceLoader.streamWrite(streamId, text); |
| callback({statusCode: 200}); |
| }) |
| .catch(function() { |
| callback({statusCode: 404}); |
| }); |
| } |
| |
| /** |
| * @override |
| * @param {function(!Object<string, string>)} callback |
| */ |
| getPreferences(callback) { |
| const prefs = {}; |
| for (const name in window.localStorage) { |
| prefs[name] = window.localStorage[name]; |
| } |
| callback(prefs); |
| } |
| |
| /** |
| * @override |
| * @param {string} name |
| * @param {string} value |
| */ |
| setPreference(name, value) { |
| window.localStorage[name] = value; |
| } |
| |
| /** |
| * @override |
| * @param {string} name |
| */ |
| removePreference(name) { |
| delete window.localStorage[name]; |
| } |
| |
| /** |
| * @override |
| */ |
| clearPreferences() { |
| window.localStorage.clear(); |
| } |
| |
| /** |
| * @override |
| * @param {!FileSystem} fileSystem |
| */ |
| upgradeDraggedFileSystemPermissions(fileSystem) { |
| } |
| |
| /** |
| * @override |
| * @param {number} requestId |
| * @param {string} fileSystemPath |
| * @param {string} excludedFolders |
| */ |
| indexPath(requestId, fileSystemPath, excludedFolders) { |
| } |
| |
| /** |
| * @override |
| * @param {number} requestId |
| */ |
| stopIndexing(requestId) { |
| } |
| |
| /** |
| * @override |
| * @param {number} requestId |
| * @param {string} fileSystemPath |
| * @param {string} query |
| */ |
| searchInPath(requestId, fileSystemPath, query) { |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| zoomFactor() { |
| return 1; |
| } |
| |
| /** |
| * @override |
| */ |
| zoomIn() { |
| } |
| |
| /** |
| * @override |
| */ |
| zoomOut() { |
| } |
| |
| /** |
| * @override |
| */ |
| resetZoom() { |
| } |
| |
| /** |
| * @override |
| * @param {string} shortcuts |
| */ |
| setWhitelistedShortcuts(shortcuts) { |
| } |
| |
| /** |
| * @override |
| * @param {boolean} active |
| */ |
| setEyeDropperActive(active) { |
| } |
| |
| /** |
| * @param {!Array<string>} certChain |
| * @override |
| */ |
| showCertificateViewer(certChain) { |
| } |
| |
| /** |
| * @override |
| * @param {function()} callback |
| */ |
| reattach(callback) { |
| } |
| |
| /** |
| * @override |
| */ |
| readyForTest() { |
| } |
| |
| /** |
| * @override |
| */ |
| connectionReady() { |
| } |
| |
| /** |
| * @override |
| * @param {boolean} value |
| */ |
| setOpenNewWindowForPopups(value) { |
| } |
| |
| /** |
| * @override |
| * @param {!Adb.Config} config |
| */ |
| setDevicesDiscoveryConfig(config) { |
| } |
| |
| /** |
| * @override |
| * @param {boolean} enabled |
| */ |
| setDevicesUpdatesEnabled(enabled) { |
| } |
| |
| /** |
| * @override |
| * @param {string} pageId |
| * @param {string} action |
| */ |
| performActionOnRemotePage(pageId, action) { |
| } |
| |
| /** |
| * @override |
| * @param {string} browserId |
| * @param {string} url |
| */ |
| openRemotePage(browserId, url) { |
| } |
| |
| /** |
| * @override |
| */ |
| openNodeFrontend() { |
| } |
| |
| /** |
| * @override |
| * @param {number} x |
| * @param {number} y |
| * @param {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>} items |
| * @param {!Document} document |
| */ |
| showContextMenuAtPoint(x, y, items, document) { |
| throw 'Soft context menu should be used'; |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| isHostedMode() { |
| return true; |
| } |
| |
| /** |
| * @override |
| * @param {function(!ExtensionDescriptor)} callback |
| */ |
| setAddExtensionCallback(callback) { |
| // Extensions are not supported in hosted mode. |
| } |
| } |
| |
| /** |
| * @type {!InspectorFrontendHostStub} |
| */ |
| let _InspectorFrontendHost = window.InspectorFrontendHost; |
| |
| /** |
| * @unrestricted |
| */ |
| class InspectorFrontendAPIImpl { |
| constructor() { |
| this._debugFrontend = |
| !!Root.Runtime.queryParam('debugFrontend') || (window['InspectorTest'] && window['InspectorTest']['debugTest']); |
| |
| const descriptors = Host.InspectorFrontendHostAPI.EventDescriptors; |
| for (let i = 0; i < descriptors.length; ++i) { |
| this[descriptors[i][1]] = this._dispatch.bind(this, descriptors[i][0], descriptors[i][2], descriptors[i][3]); |
| } |
| } |
| |
| /** |
| * @param {symbol} name |
| * @param {!Array.<string>} signature |
| * @param {boolean} runOnceLoaded |
| */ |
| _dispatch(name, signature, runOnceLoaded) { |
| const params = Array.prototype.slice.call(arguments, 3); |
| |
| if (this._debugFrontend) { |
| setImmediate(innerDispatch); |
| } else { |
| innerDispatch(); |
| } |
| |
| function innerDispatch() { |
| // Single argument methods get dispatched with the param. |
| if (signature.length < 2) { |
| try { |
| _InspectorFrontendHost.events.dispatchEventToListeners(name, params[0]); |
| } catch (e) { |
| console.error(e + ' ' + e.stack); |
| } |
| return; |
| } |
| const data = {}; |
| for (let i = 0; i < signature.length; ++i) { |
| data[signature[i]] = params[i]; |
| } |
| try { |
| _InspectorFrontendHost.events.dispatchEventToListeners(name, data); |
| } catch (e) { |
| console.error(e + ' ' + e.stack); |
| } |
| } |
| } |
| |
| /** |
| * @param {number} id |
| * @param {string} chunk |
| */ |
| streamWrite(id, chunk) { |
| Host.ResourceLoader.streamWrite(id, chunk); |
| } |
| } |
| |
| (function() { |
| |
| function initializeInspectorFrontendHost() { |
| let proto; |
| if (!_InspectorFrontendHost) { |
| // Instantiate stub for web-hosted mode if necessary. |
| window.InspectorFrontendHost = _InspectorFrontendHost = new InspectorFrontendHostStub(); |
| } else { |
| // Otherwise add stubs for missing methods that are declared in the interface. |
| proto = InspectorFrontendHostStub.prototype; |
| for (const name of Object.getOwnPropertyNames(proto)) { |
| const stub = proto[name]; |
| if (typeof stub !== 'function' || _InspectorFrontendHost[name]) { |
| continue; |
| } |
| |
| console.error( |
| 'Incompatible embedder: method Host.InspectorFrontendHost.' + name + ' is missing. Using stub instead.'); |
| _InspectorFrontendHost[name] = stub; |
| } |
| } |
| |
| // Attach the events object. |
| _InspectorFrontendHost.events = new Common.Object(); |
| } |
| |
| // FIXME: This file is included into both apps, since the devtools_app needs the InspectorFrontendHostAPI only, |
| // so the host instance should not initialized there. |
| initializeInspectorFrontendHost(); |
| window.InspectorFrontendAPI = new InspectorFrontendAPIImpl(); |
| })(); |
| |
| /** |
| * @param {!Object<string, string>=} prefs |
| * @return {boolean} |
| */ |
| export function isUnderTest(prefs) { |
| // Integration tests rely on test queryParam. |
| if (Root.Runtime.queryParam('test')) { |
| return true; |
| } |
| // Browser tests rely on prefs. |
| if (prefs) { |
| return prefs['isUnderTest'] === 'true'; |
| } |
| return Common.settings && Common.settings.createSetting('isUnderTest', false).get(); |
| } |
| |
| /* Legacy exported object */ |
| self.Host = self.Host || {}; |
| |
| /* Legacy exported object */ |
| Host = Host || {}; |
| |
| /** @type {!InspectorFrontendHostStub} */ |
| Host.InspectorFrontendHost = _InspectorFrontendHost; |
| |
| Host.isUnderTest = isUnderTest; |