Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
      3  * Copyright (C) 2007 Matt Lilek (pewtermoose (at) gmail.com).
      4  * Copyright (C) 2009 Joseph Pecoraro
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  *
     10  * 1.  Redistributions of source code must retain the above copyright
     11  *     notice, this list of conditions and the following disclaimer.
     12  * 2.  Redistributions in binary form must reproduce the above copyright
     13  *     notice, this list of conditions and the following disclaimer in the
     14  *     documentation and/or other materials provided with the distribution.
     15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     16  *     its contributors may be used to endorse or promote products derived
     17  *     from this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 var WebInspector = {
     32     _panelDescriptors: function()
     33     {
     34         this.panels = {};
     35         WebInspector.inspectorView = new WebInspector.InspectorView();
     36         WebInspector.inspectorView.show(document.body);
     37 
     38         var elements = new WebInspector.ElementsPanelDescriptor();
     39         var network = new WebInspector.NetworkPanelDescriptor();
     40         var sources = new WebInspector.SourcesPanelDescriptor();
     41         var timeline = new WebInspector.TimelinePanelDescriptor();
     42         var profiles = new WebInspector.ProfilesPanelDescriptor();
     43         var resources = new WebInspector.PanelDescriptor("resources", WebInspector.UIString("Resources"), "ResourcesPanel", "ResourcesPanel.js");
     44         var audits = new WebInspector.PanelDescriptor("audits", WebInspector.UIString("Audits"), "AuditsPanel", "AuditsPanel.js");
     45         var console = new WebInspector.PanelDescriptor("console", WebInspector.UIString("Console"), "ConsolePanel");
     46 
     47         if (WebInspector.WorkerManager.isWorkerFrontend())
     48             return [sources, timeline, profiles, console];
     49 
     50         var panelDescriptors = [elements, network, sources, timeline, profiles, resources, audits, console];
     51         if (WebInspector.experimentsSettings.layersPanel.isEnabled()) {
     52             var layers = new WebInspector.LayersPanelDescriptor();
     53             panelDescriptors.push(layers);
     54         }
     55         return panelDescriptors;
     56     },
     57 
     58     _createGlobalStatusBarItems: function()
     59     {
     60         if (this.inspectElementModeController)
     61             this.inspectorView.appendToLeftToolbar(this.inspectElementModeController.toggleSearchButton.element);
     62 
     63         if (Capabilities.canScreencast) {
     64             this._toggleScreencastButton = new WebInspector.StatusBarButton(WebInspector.UIString("Toggle screencast."), "screencast-status-bar-item");
     65             this._toggleScreencastButton.addEventListener("click", this._toggleScreencastButtonClicked.bind(this), false);
     66             this.inspectorView.appendToLeftToolbar(this._toggleScreencastButton.element);
     67         }
     68 
     69         this.inspectorView.appendToRightToolbar(this.settingsController.statusBarItem);
     70         if (WebInspector.queryParamsObject["can_dock"])
     71             this.inspectorView.appendToRightToolbar(this.dockController.element);
     72 
     73         var closeButtonToolbarItem = document.createElementWithClass("div", "toolbar-close-button-item");
     74         var closeButtonElement = closeButtonToolbarItem.createChild("div", "close-button");
     75         closeButtonElement.addEventListener("click", WebInspector.close.bind(WebInspector), true);
     76         this.inspectorView.appendToRightToolbar(closeButtonToolbarItem);
     77     },
     78 
     79     /**
     80      * @return {boolean}
     81      */
     82     isInspectingDevice: function()
     83     {
     84         return !!WebInspector.queryParamsObject["remoteFrontend"];
     85     },
     86 
     87     _toggleScreencastButtonClicked: function()
     88     {
     89         this._toggleScreencastButton.toggled = !this._toggleScreencastButton.toggled;
     90         WebInspector.settings.screencastEnabled.set(this._toggleScreencastButton.toggled);
     91 
     92         if (this._toggleScreencastButton.toggled) {
     93             if (!this._screencastView) {
     94                 // Rebuild the UI upon first invocation.
     95                 this._screencastView = new WebInspector.ScreencastView();
     96                 this._screencastSplitView = new WebInspector.SplitView(true, WebInspector.settings.screencastSidebarWidth.name);
     97                 this._screencastSplitView.markAsRoot();
     98                 this._screencastSplitView.show(document.body);
     99 
    100                 this._screencastView.show(this._screencastSplitView.firstElement());
    101 
    102                 this.inspectorView.element.remove();
    103                 this.inspectorView.show(this._screencastSplitView.secondElement());
    104             }
    105             this._screencastSplitView.showBoth();
    106         } else {
    107             this._screencastSplitView.showOnlySecond();
    108         }
    109     },
    110 
    111 
    112     showConsole: function()
    113     {
    114         if (this.consoleView.isShowing() && !WebInspector.inspectorView.drawer().isHiding())
    115             return;
    116         this.inspectorView.showViewInDrawer("console");
    117     },
    118 
    119     _resetErrorAndWarningCounts: function()
    120     {
    121         WebInspector.inspectorView.setErrorAndWarningCounts(0, 0);
    122     },
    123 
    124     _updateErrorAndWarningCounts: function()
    125     {
    126         var errors = WebInspector.console.errors;
    127         var warnings = WebInspector.console.warnings;
    128         WebInspector.inspectorView.setErrorAndWarningCounts(errors, warnings);
    129     },
    130 
    131     get inspectedPageDomain()
    132     {
    133         var parsedURL = WebInspector.inspectedPageURL && WebInspector.inspectedPageURL.asParsedURL();
    134         return parsedURL ? parsedURL.host : "";
    135     },
    136 
    137     _initializeCapability: function(name, callback, error, result)
    138     {
    139         Capabilities[name] = result;
    140         if (callback)
    141             callback();
    142     },
    143 
    144     _zoomIn: function()
    145     {
    146         this._zoomLevel = Math.min(this._zoomLevel + 1, WebInspector.Zoom.Table.length - WebInspector.Zoom.DefaultOffset - 1);
    147         this._requestZoom();
    148     },
    149 
    150     _zoomOut: function()
    151     {
    152         this._zoomLevel = Math.max(this._zoomLevel - 1, -WebInspector.Zoom.DefaultOffset);
    153         this._requestZoom();
    154     },
    155 
    156     _resetZoom: function()
    157     {
    158         this._zoomLevel = 0;
    159         this._requestZoom();
    160     },
    161 
    162     /**
    163      * @return {number}
    164      * @this {WebInspector}
    165      */
    166     zoomFactor: function()
    167     {
    168         // For backwards compatibility, zoomLevel takes integers (with 0 being default zoom).
    169         var index = this._zoomLevel + WebInspector.Zoom.DefaultOffset;
    170         index = Math.min(WebInspector.Zoom.Table.length - 1, index);
    171         index = Math.max(0, index);
    172         return WebInspector.Zoom.Table[index];
    173     },
    174 
    175     _requestZoom: function()
    176     {
    177         WebInspector.settings.zoomLevel.set(this._zoomLevel);
    178         InspectorFrontendHost.setZoomFactor(this.zoomFactor());
    179     },
    180 
    181     _debuggerPaused: function()
    182     {
    183         this.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
    184         WebInspector.showPanel("sources");
    185     }
    186 }
    187 
    188 WebInspector.Events = {
    189     InspectorLoaded: "InspectorLoaded"
    190 }
    191 
    192 {(function parseQueryParameters()
    193 {
    194     WebInspector.queryParamsObject = {};
    195     var queryParams = window.location.search;
    196     if (!queryParams)
    197         return;
    198     var params = queryParams.substring(1).split("&");
    199     for (var i = 0; i < params.length; ++i) {
    200         var pair = params[i].split("=");
    201         WebInspector.queryParamsObject[pair[0]] = pair[1];
    202     }
    203 })();}
    204 
    205 WebInspector.suggestReload = function()
    206 {
    207     if (window.confirm(WebInspector.UIString("It is recommended to restart inspector after making these changes. Would you like to restart it?")))
    208         this.reload();
    209 }
    210 
    211 WebInspector.reload = function()
    212 {
    213     InspectorAgent.reset();
    214 
    215     var queryParams = window.location.search;
    216     var url = window.location.href;
    217     url = url.substring(0, url.length - queryParams.length);
    218     var queryParamsObject = {};
    219     for (var name in WebInspector.queryParamsObject)
    220         queryParamsObject[name] = WebInspector.queryParamsObject[name];
    221     if (this.dockController)
    222         queryParamsObject["dockSide"] = this.dockController.dockSide();
    223     var names = Object.keys(queryParamsObject);
    224     for (var i = 0; i < names.length; ++i)
    225         url += (i ? "&" : "?") + names[i] + "=" + queryParamsObject[names[i]];
    226     document.location = url;
    227 }
    228 
    229 WebInspector.loaded = function()
    230 {
    231     if (!InspectorFrontendHost.sendMessageToEmbedder) {
    232         var helpScreen = new WebInspector.HelpScreen(WebInspector.UIString("Incompatible Chrome version"));
    233         var p = helpScreen.contentElement.createChild("p", "help-section");
    234         p.textContent = WebInspector.UIString("Please upgrade to a newer Chrome version (you might need a Dev or Canary build).");
    235         helpScreen.showModal();
    236         return;
    237     }
    238 
    239     InspectorBackend.loadFromJSONIfNeeded("../protocol.json");
    240     WebInspector.dockController = new WebInspector.DockController();
    241 
    242     if (WebInspector.WorkerManager.isDedicatedWorkerFrontend()) {
    243         // Do not create socket for the worker front-end.
    244         WebInspector.doLoadedDone();
    245         return;
    246     }
    247 
    248     var ws;
    249     if ("ws" in WebInspector.queryParamsObject)
    250         ws = "ws://" + WebInspector.queryParamsObject.ws;
    251     else if ("page" in WebInspector.queryParamsObject) {
    252         var page = WebInspector.queryParamsObject.page;
    253         var host = "host" in WebInspector.queryParamsObject ? WebInspector.queryParamsObject.host : window.location.host;
    254         ws = "ws://" + host + "/devtools/page/" + page;
    255     }
    256 
    257     if (ws) {
    258         WebInspector.socket = new WebSocket(ws);
    259         WebInspector.socket.onmessage = function(message) { InspectorBackend.dispatch(message.data); }
    260         WebInspector.socket.onerror = function(error) { console.error(error); }
    261         WebInspector.socket.onopen = function() {
    262             InspectorFrontendHost.sendMessageToBackend = WebInspector.socket.send.bind(WebInspector.socket);
    263             WebInspector.doLoadedDone();
    264         }
    265         WebInspector.socket.onclose = function() {
    266             if (!WebInspector.socket._detachReason)
    267                 (new WebInspector.RemoteDebuggingTerminatedScreen("websocket_closed")).showModal();
    268         }
    269         return;
    270     }
    271 
    272     WebInspector.doLoadedDone();
    273 
    274     // In case of loading as a web page with no bindings / harness, kick off initialization manually.
    275     if (InspectorFrontendHost.isStub) {
    276         InspectorFrontendAPI.dispatchQueryParameters(WebInspector.queryParamsObject);
    277         WebInspector._doLoadedDoneWithCapabilities();
    278     }
    279 }
    280 
    281 WebInspector.doLoadedDone = function()
    282 {
    283     // Install styles and themes
    284     WebInspector.installPortStyles();
    285     if (WebInspector.socket)
    286         document.body.classList.add("remote");
    287     if (WebInspector.queryParamsObject["overlayContents"])
    288         document.body.classList.add("overlay-contents");
    289 
    290     if (WebInspector.queryParamsObject.toolbarColor && WebInspector.queryParamsObject.textColor)
    291         WebInspector.setToolbarColors(WebInspector.queryParamsObject.toolbarColor, WebInspector.queryParamsObject.textColor);
    292 
    293     WebInspector.WorkerManager.loaded();
    294 
    295     PageAgent.canScreencast(WebInspector._initializeCapability.bind(WebInspector, "canScreencast", null));
    296     WorkerAgent.canInspectWorkers(WebInspector._initializeCapability.bind(WebInspector, "canInspectWorkers", WebInspector._doLoadedDoneWithCapabilities.bind(WebInspector)));
    297 }
    298 
    299 WebInspector._doLoadedDoneWithCapabilities = function()
    300 {
    301     new WebInspector.VersionController().updateVersion();
    302 
    303     WebInspector.shortcutsScreen = new WebInspector.ShortcutsScreen();
    304     this._registerShortcuts();
    305 
    306     // set order of some sections explicitly
    307     WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
    308     WebInspector.shortcutsScreen.section(WebInspector.UIString("Elements Panel"));
    309 
    310     this.console = new WebInspector.ConsoleModel();
    311     this.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._resetErrorAndWarningCounts, this);
    312     this.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._updateErrorAndWarningCounts, this);
    313     this.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._updateErrorAndWarningCounts, this);
    314     this.networkManager = new WebInspector.NetworkManager();
    315     this.resourceTreeModel = new WebInspector.ResourceTreeModel(this.networkManager);
    316     this.debuggerModel = new WebInspector.DebuggerModel();
    317     this.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
    318     this.networkLog = new WebInspector.NetworkLog();
    319     this.domAgent = new WebInspector.DOMAgent();
    320     this.domAgent.addEventListener(WebInspector.DOMAgent.Events.InspectNodeRequested, this._inspectNodeRequested, this);
    321     this.runtimeModel = new WebInspector.RuntimeModel(this.resourceTreeModel);
    322 
    323     var panelDescriptors = this._panelDescriptors();
    324     this.advancedSearchController = new WebInspector.AdvancedSearchController();
    325     for (var i = 0; i < panelDescriptors.length; ++i)
    326         panelDescriptors[i].registerShortcuts();
    327 
    328     WebInspector.CSSMetadata.requestCSSShorthandData();
    329 
    330     this.consoleView = new WebInspector.ConsoleView(WebInspector.WorkerManager.isWorkerFrontend());
    331 
    332     InspectorBackend.registerInspectorDispatcher(this);
    333 
    334     this.isolatedFileSystemManager = new WebInspector.IsolatedFileSystemManager();
    335     this.isolatedFileSystemDispatcher = new WebInspector.IsolatedFileSystemDispatcher(this.isolatedFileSystemManager);
    336     this.workspace = new WebInspector.Workspace(this.isolatedFileSystemManager.mapping());
    337 
    338     this.cssModel = new WebInspector.CSSStyleModel(this.workspace);
    339     this.timelineManager = new WebInspector.TimelineManager();
    340     this.tracingAgent = new WebInspector.TracingAgent();
    341 
    342     if (!WebInspector.WorkerManager.isWorkerFrontend())
    343         this.inspectElementModeController = new WebInspector.InspectElementModeController();
    344 
    345     this.settingsController = new WebInspector.SettingsController();
    346 
    347     this.domBreakpointsSidebarPane = new WebInspector.DOMBreakpointsSidebarPane();
    348 
    349     this._zoomLevel = WebInspector.settings.zoomLevel.get();
    350     if (this._zoomLevel)
    351         this._requestZoom();
    352 
    353     var autoselectPanel = WebInspector.UIString("a panel chosen automatically");
    354     var openAnchorLocationSetting = WebInspector.settings.createSetting("openLinkHandler", autoselectPanel);
    355     this.openAnchorLocationRegistry = new WebInspector.HandlerRegistry(openAnchorLocationSetting);
    356     this.openAnchorLocationRegistry.registerHandler(autoselectPanel, function() { return false; });
    357 
    358     new WebInspector.WorkspaceController(this.workspace);
    359 
    360     this.fileSystemWorkspaceProvider = new WebInspector.FileSystemWorkspaceProvider(this.isolatedFileSystemManager, this.workspace);
    361 
    362     this.networkWorkspaceProvider = new WebInspector.SimpleWorkspaceProvider(this.workspace, WebInspector.projectTypes.Network);
    363     new WebInspector.NetworkUISourceCodeProvider(this.networkWorkspaceProvider, this.workspace);
    364 
    365     this.breakpointManager = new WebInspector.BreakpointManager(WebInspector.settings.breakpoints, this.debuggerModel, this.workspace);
    366 
    367     this.scriptSnippetModel = new WebInspector.ScriptSnippetModel(this.workspace);
    368 
    369     this.overridesSupport = new WebInspector.OverridesSupport();
    370     this.overridesSupport.applyInitialOverrides();
    371 
    372     new WebInspector.DebuggerScriptMapping(this.workspace, this.networkWorkspaceProvider);
    373     this.liveEditSupport = new WebInspector.LiveEditSupport(this.workspace);
    374     new WebInspector.CSSStyleSheetMapping(this.cssModel, this.workspace, this.networkWorkspaceProvider);
    375     new WebInspector.PresentationConsoleMessageHelper(this.workspace);
    376 
    377     this._createGlobalStatusBarItems();
    378 
    379     WebInspector.startBatchUpdate();
    380     for (var i = 0; i < panelDescriptors.length; ++i)
    381         WebInspector.inspectorView.addPanel(panelDescriptors[i]);
    382     WebInspector.endBatchUpdate();
    383 
    384     this.addMainEventListeners(document);
    385 
    386     window.addEventListener("resize", this.windowResize.bind(this), true);
    387 
    388     var errorWarningCount = document.getElementById("error-warning-count");
    389     errorWarningCount.addEventListener("click", this.showConsole.bind(this), false);
    390     this._updateErrorAndWarningCounts();
    391 
    392     this.extensionServer.initExtensions();
    393 
    394     this.console.enableAgent();
    395 
    396     InspectorAgent.enable(WebInspector.inspectorView.showInitialPanel.bind(WebInspector.inspectorView));
    397     this.databaseModel = new WebInspector.DatabaseModel();
    398     this.domStorageModel = new WebInspector.DOMStorageModel();
    399 
    400     this.cpuProfilerModel = new WebInspector.CPUProfilerModel();
    401     HeapProfilerAgent.enable();
    402 
    403     WebInspector.settings.showPaintRects = WebInspector.settings.createBackendSetting("showPaintRects", false, PageAgent.setShowPaintRects.bind(PageAgent));
    404     WebInspector.settings.showDebugBorders = WebInspector.settings.createBackendSetting("showDebugBorders", false, PageAgent.setShowDebugBorders.bind(PageAgent));
    405     WebInspector.settings.continuousPainting = WebInspector.settings.createBackendSetting("continuousPainting", false, PageAgent.setContinuousPaintingEnabled.bind(PageAgent));
    406     WebInspector.settings.showFPSCounter = WebInspector.settings.createBackendSetting("showFPSCounter", false, PageAgent.setShowFPSCounter.bind(PageAgent));
    407     WebInspector.settings.showScrollBottleneckRects = WebInspector.settings.createBackendSetting("showScrollBottleneckRects", false, PageAgent.setShowScrollBottleneckRects.bind(PageAgent));
    408 
    409     if (WebInspector.settings.showPaintRects.get() || WebInspector.settings.showDebugBorders.get() || WebInspector.settings.continuousPainting.get() ||
    410             WebInspector.settings.showFPSCounter.get() || WebInspector.settings.showScrollBottleneckRects.get()) {
    411         WebInspector.settings.showRenderingViewInDrawer.set(true);
    412     }
    413 
    414     WebInspector.settings.showMetricsRulers.addChangeListener(showRulersChanged);
    415     function showRulersChanged()
    416     {
    417         PageAgent.setShowViewportSizeOnResize(true, WebInspector.settings.showMetricsRulers.get());
    418     }
    419     showRulersChanged();
    420 
    421     WebInspector.WorkerManager.loadCompleted();
    422     InspectorFrontendAPI.loadCompleted();
    423 
    424     if (Capabilities.canScreencast && WebInspector.settings.screencastEnabled.get())
    425         this._toggleScreencastButtonClicked();
    426 
    427     WebInspector.notifications.dispatchEventToListeners(WebInspector.Events.InspectorLoaded);
    428 }
    429 
    430 var windowLoaded = function()
    431 {
    432     WebInspector.loaded();
    433     window.removeEventListener("DOMContentLoaded", windowLoaded, false);
    434     delete windowLoaded;
    435 };
    436 
    437 window.addEventListener("DOMContentLoaded", windowLoaded, false);
    438 
    439 // We'd like to enforce asynchronous interaction between the inspector controller and the frontend.
    440 // It is needed to prevent re-entering the backend code.
    441 // Also, native dispatches do not guarantee setTimeouts to be serialized, so we
    442 // enforce serialization using 'messagesToDispatch' queue. It is also important that JSC debugger
    443 // tests require that each command was dispatch within individual timeout callback, so we don't batch them.
    444 
    445 var messagesToDispatch = [];
    446 
    447 WebInspector.dispatchQueueIsEmpty = function() {
    448     return messagesToDispatch.length == 0;
    449 }
    450 
    451 WebInspector.dispatch = function(message) {
    452     messagesToDispatch.push(message);
    453     setTimeout(function() {
    454         InspectorBackend.dispatch(messagesToDispatch.shift());
    455     }, 0);
    456 }
    457 
    458 WebInspector.windowResize = function(event)
    459 {
    460     if (WebInspector.inspectorView)
    461         WebInspector.inspectorView.onResize();
    462     if (WebInspector.settingsController)
    463         WebInspector.settingsController.resize();
    464     if (WebInspector._screencastSplitView)
    465         WebInspector._screencastSplitView.doResize();
    466 }
    467 
    468 WebInspector.close = function(event)
    469 {
    470     InspectorFrontendHost.closeWindow();
    471 }
    472 
    473 WebInspector.documentClick = function(event)
    474 {
    475     var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
    476     if (!anchor || (anchor.target === "_blank"))
    477         return;
    478 
    479     // Prevent the link from navigating, since we don't do any navigation by following links normally.
    480     event.consume(true);
    481 
    482     function followLink()
    483     {
    484         if (WebInspector.isBeingEdited(event.target))
    485             return;
    486         if (WebInspector.openAnchorLocationRegistry.dispatch({ url: anchor.href, lineNumber: anchor.lineNumber}))
    487             return;
    488         if (WebInspector.showAnchorLocation(anchor))
    489             return;
    490 
    491         const profileMatch = WebInspector.ProfilesPanelDescriptor.ProfileURLRegExp.exec(anchor.href);
    492         if (profileMatch) {
    493             WebInspector.showPanel("profiles").showProfile(profileMatch[1], profileMatch[2]);
    494             return;
    495         }
    496 
    497         var parsedURL = anchor.href.asParsedURL();
    498         if (parsedURL && parsedURL.scheme === "webkit-link-action") {
    499             if (parsedURL.host === "show-panel") {
    500                 var panel = parsedURL.path.substring(1);
    501                 if (WebInspector.panel(panel))
    502                     WebInspector.showPanel(panel);
    503             }
    504             return;
    505         }
    506 
    507         InspectorFrontendHost.openInNewTab(anchor.href);
    508     }
    509 
    510     if (WebInspector.followLinkTimeout)
    511         clearTimeout(WebInspector.followLinkTimeout);
    512 
    513     if (anchor.preventFollowOnDoubleClick) {
    514         // Start a timeout if this is the first click, if the timeout is canceled
    515         // before it fires, then a double clicked happened or another link was clicked.
    516         if (event.detail === 1)
    517             WebInspector.followLinkTimeout = setTimeout(followLink, 333);
    518         return;
    519     }
    520 
    521     followLink();
    522 }
    523 
    524 WebInspector.openResource = function(resourceURL, inResourcesPanel)
    525 {
    526     var resource = WebInspector.resourceForURL(resourceURL);
    527     if (inResourcesPanel && resource)
    528         WebInspector.showPanel("resources").showResource(resource);
    529     else
    530         InspectorFrontendHost.openInNewTab(resourceURL);
    531 }
    532 
    533 WebInspector._registerShortcuts = function()
    534 {
    535     var shortcut = WebInspector.KeyboardShortcut;
    536     var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("All Panels"));
    537     var keys = [
    538         shortcut.makeDescriptor("[", shortcut.Modifiers.CtrlOrMeta),
    539         shortcut.makeDescriptor("]", shortcut.Modifiers.CtrlOrMeta)
    540     ];
    541     section.addRelatedKeys(keys, WebInspector.UIString("Go to the panel to the left/right"));
    542 
    543     keys = [
    544         shortcut.makeDescriptor("[", shortcut.Modifiers.CtrlOrMeta | shortcut.Modifiers.Alt),
    545         shortcut.makeDescriptor("]", shortcut.Modifiers.CtrlOrMeta | shortcut.Modifiers.Alt)
    546     ];
    547     section.addRelatedKeys(keys, WebInspector.UIString("Go back/forward in panel history"));
    548 
    549     var toggleConsoleLabel = WebInspector.UIString("Show console");
    550     section.addKey(shortcut.makeDescriptor(shortcut.Keys.Tilde, shortcut.Modifiers.Ctrl), toggleConsoleLabel);
    551     var doNotOpenDrawerOnEsc = WebInspector.experimentsSettings.doNotOpenDrawerOnEsc.isEnabled();
    552     var toggleDrawerLabel = doNotOpenDrawerOnEsc ? WebInspector.UIString("Hide drawer") : WebInspector.UIString("Toggle drawer");
    553     section.addKey(shortcut.makeDescriptor(shortcut.Keys.Esc), toggleDrawerLabel);
    554     section.addKey(shortcut.makeDescriptor("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search"));
    555 
    556     var advancedSearchShortcut = WebInspector.AdvancedSearchController.createShortcut();
    557     section.addKey(advancedSearchShortcut, WebInspector.UIString("Search across all sources"));
    558 
    559     var inspectElementModeShortcut = WebInspector.InspectElementModeController.createShortcut();
    560     section.addKey(inspectElementModeShortcut, WebInspector.UIString("Select node to inspect"));
    561 
    562     var openResourceShortcut = WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
    563     section.addKey(openResourceShortcut, WebInspector.UIString("Go to source"));
    564 
    565     if (WebInspector.isMac()) {
    566         keys = [
    567             shortcut.makeDescriptor("g", shortcut.Modifiers.Meta),
    568             shortcut.makeDescriptor("g", shortcut.Modifiers.Meta | shortcut.Modifiers.Shift)
    569         ];
    570         section.addRelatedKeys(keys, WebInspector.UIString("Find next/previous"));
    571     }
    572 
    573     var goToShortcut = WebInspector.GoToLineDialog.createShortcut();
    574     section.addKey(goToShortcut, WebInspector.UIString("Go to line"));
    575 
    576     keys = [
    577         shortcut.Keys.F1,
    578         shortcut.makeDescriptor("?")
    579     ];
    580     section.addAlternateKeys(keys, WebInspector.UIString("Show general settings"));
    581 }
    582 
    583 WebInspector.documentKeyDown = function(event)
    584 {
    585     if (WebInspector.currentFocusElement() && WebInspector.currentFocusElement().handleKeyEvent) {
    586         WebInspector.currentFocusElement().handleKeyEvent(event);
    587         if (event.handled) {
    588             event.consume(true);
    589             return;
    590         }
    591     }
    592 
    593     if (WebInspector.inspectorView.currentPanel()) {
    594         WebInspector.inspectorView.currentPanel().handleShortcut(event);
    595         if (event.handled) {
    596             event.consume(true);
    597             return;
    598         }
    599     }
    600 
    601     if (WebInspector.advancedSearchController.handleShortcut(event))
    602         return;
    603     if (WebInspector.inspectElementModeController && WebInspector.inspectElementModeController.handleShortcut(event))
    604         return;
    605 
    606     switch (event.keyIdentifier) {
    607         case "U+004F": // O key
    608         case "U+0050": // P key
    609             if (!event.shiftKey && !event.altKey && WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
    610                 WebInspector.showPanel("sources").showGoToSourceDialog();
    611                 event.consume(true);
    612             }
    613             break;
    614         case "U+0052": // R key
    615             if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
    616                 WebInspector.debuggerModel.skipAllPauses(true, true);
    617                 WebInspector.resourceTreeModel.reloadPage(event.shiftKey);
    618                 event.consume(true);
    619             }
    620             if (window.DEBUG && event.altKey) {
    621                 WebInspector.reload();
    622                 return;
    623             }
    624             break;
    625         case "F5":
    626             if (!WebInspector.isMac()) {
    627                 WebInspector.resourceTreeModel.reloadPage(event.ctrlKey || event.shiftKey);
    628                 event.consume(true);
    629             }
    630             break;
    631     }
    632 
    633     var isValidZoomShortcut = WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) &&
    634         !event.altKey &&
    635         !InspectorFrontendHost.isStub;
    636     switch (event.keyCode) {
    637         case 107: // +
    638         case 187: // +
    639             if (isValidZoomShortcut) {
    640                 WebInspector._zoomIn();
    641                 event.consume(true);
    642             }
    643             break;
    644         case 109: // -
    645         case 189: // -
    646             if (isValidZoomShortcut) {
    647                 WebInspector._zoomOut();
    648                 event.consume(true);
    649             }
    650             break;
    651         case 48: // 0
    652         case 96: // Numpad 0
    653             // Zoom reset shortcut does not allow "Shift" when handled by the browser.
    654             if (isValidZoomShortcut && !event.shiftKey) {
    655                 WebInspector._resetZoom();
    656                 event.consume(true);
    657             }
    658             break;
    659     }
    660 
    661 }
    662 
    663 WebInspector.postDocumentKeyDown = function(event)
    664 {
    665     const helpKey = WebInspector.isMac() ? "U+003F" : "U+00BF"; // "?" for both platforms
    666 
    667     if (event.keyIdentifier === "F1" ||
    668         (event.keyIdentifier === helpKey && event.shiftKey && (!WebInspector.isBeingEdited(event.target) || event.metaKey))) {
    669         this.settingsController.showSettingsScreen(WebInspector.SettingsScreen.Tabs.General);
    670         event.consume(true);
    671         return;
    672     }
    673 
    674     const Esc = "U+001B";
    675 
    676     if (event.handled)
    677         return;
    678 
    679     var doNotOpenDrawerOnEsc = WebInspector.experimentsSettings.doNotOpenDrawerOnEsc.isEnabled();
    680     if (event.keyIdentifier === Esc) {
    681         if (this.inspectorView.drawer().visible())
    682             this.inspectorView.drawer().hide();
    683         else if (!doNotOpenDrawerOnEsc)
    684             this.inspectorView.drawer().show();
    685     }
    686 
    687     if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Tilde.code && event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey)
    688         this.showConsole();
    689 }
    690 
    691 WebInspector.documentCanCopy = function(event)
    692 {
    693     if (WebInspector.inspectorView.currentPanel() && WebInspector.inspectorView.currentPanel().handleCopyEvent)
    694         event.preventDefault();
    695 }
    696 
    697 WebInspector.documentCopy = function(event)
    698 {
    699     if (WebInspector.inspectorView.currentPanel() && WebInspector.inspectorView.currentPanel().handleCopyEvent)
    700         WebInspector.inspectorView.currentPanel().handleCopyEvent(event);
    701 }
    702 
    703 WebInspector.contextMenuEventFired = function(event)
    704 {
    705     if (event.handled || event.target.classList.contains("popup-glasspane"))
    706         event.preventDefault();
    707 }
    708 
    709 WebInspector.showPanel = function(panel)
    710 {
    711     return WebInspector.inspectorView.showPanel(panel);
    712 }
    713 
    714 WebInspector.panel = function(panel)
    715 {
    716     return WebInspector.inspectorView.panel(panel);
    717 }
    718 
    719 WebInspector.bringToFront = function()
    720 {
    721     InspectorFrontendHost.bringToFront();
    722 }
    723 
    724 /**
    725  * @param {string=} messageLevel
    726  * @param {boolean=} showConsole
    727  */
    728 WebInspector.log = function(message, messageLevel, showConsole)
    729 {
    730     // remember 'this' for setInterval() callback
    731     var self = this;
    732 
    733     // return indication if we can actually log a message
    734     function isLogAvailable()
    735     {
    736         return WebInspector.ConsoleMessage && WebInspector.RemoteObject && self.console;
    737     }
    738 
    739     // flush the queue of pending messages
    740     function flushQueue()
    741     {
    742         var queued = WebInspector.log.queued;
    743         if (!queued)
    744             return;
    745 
    746         for (var i = 0; i < queued.length; ++i)
    747             logMessage(queued[i]);
    748 
    749         delete WebInspector.log.queued;
    750     }
    751 
    752     // flush the queue if it console is available
    753     // - this function is run on an interval
    754     function flushQueueIfAvailable()
    755     {
    756         if (!isLogAvailable())
    757             return;
    758 
    759         clearInterval(WebInspector.log.interval);
    760         delete WebInspector.log.interval;
    761 
    762         flushQueue();
    763     }
    764 
    765     // actually log the message
    766     function logMessage(message)
    767     {
    768         // post the message
    769         var msg = WebInspector.ConsoleMessage.create(
    770             WebInspector.ConsoleMessage.MessageSource.Other,
    771             messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug,
    772             message);
    773 
    774         self.console.addMessage(msg);
    775         if (showConsole)
    776             WebInspector.showConsole();
    777     }
    778 
    779     // if we can't log the message, queue it
    780     if (!isLogAvailable()) {
    781         if (!WebInspector.log.queued)
    782             WebInspector.log.queued = [];
    783 
    784         WebInspector.log.queued.push(message);
    785 
    786         if (!WebInspector.log.interval)
    787             WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000);
    788 
    789         return;
    790     }
    791 
    792     // flush the pending queue if any
    793     flushQueue();
    794 
    795     // log the message
    796     logMessage(message);
    797 }
    798 
    799 WebInspector.showErrorMessage = function(error)
    800 {
    801     WebInspector.log(error, WebInspector.ConsoleMessage.MessageLevel.Error, true);
    802 }
    803 
    804 // Inspector.inspect protocol event
    805 WebInspector.inspect = function(payload, hints)
    806 {
    807     var object = WebInspector.RemoteObject.fromPayload(payload);
    808     if (object.subtype === "node") {
    809         function callback(nodeId)
    810         {
    811             WebInspector._updateFocusedNode(nodeId);
    812             object.release();
    813         }
    814         object.pushNodeToFrontend(callback);
    815         WebInspector.showPanel("elements");
    816         return;
    817     }
    818 
    819     if (object.type === "function") {
    820         /**
    821          * @param {?Protocol.Error} error
    822          * @param {!DebuggerAgent.FunctionDetails} response
    823          */
    824         function didGetDetails(error, response)
    825         {
    826             object.release();
    827 
    828             if (error) {
    829                 console.error(error);
    830                 return;
    831             }
    832 
    833             var uiLocation = WebInspector.debuggerModel.rawLocationToUILocation(response.location);
    834             if (!uiLocation)
    835                 return;
    836 
    837             WebInspector.panel("sources").showUILocation(uiLocation, true);
    838         }
    839         DebuggerAgent.getFunctionDetails(object.objectId, didGetDetails.bind(this));
    840         return;
    841     }
    842 
    843     if (hints.databaseId)
    844         WebInspector.showPanel("resources").selectDatabase(WebInspector.databaseModel.databaseForId(hints.databaseId));
    845     else if (hints.domStorageId)
    846         WebInspector.showPanel("resources").selectDOMStorage(WebInspector.domStorageModel.storageForId(hints.domStorageId));
    847     else if (hints.copyToClipboard)
    848         InspectorFrontendHost.copyText(object.value);
    849     object.release();
    850 }
    851 
    852 // Inspector.detached protocol event
    853 WebInspector.detached = function(reason)
    854 {
    855     WebInspector.socket._detachReason = reason;
    856     (new WebInspector.RemoteDebuggingTerminatedScreen(reason)).showModal();
    857 }
    858 
    859 WebInspector.targetCrashed = function()
    860 {
    861     (new WebInspector.HelpScreenUntilReload(
    862         WebInspector.UIString("Inspected target crashed"),
    863         WebInspector.UIString("Inspected target has crashed. Once it reloads we will attach to it automatically."))).showModal();
    864 }
    865 
    866 WebInspector._inspectNodeRequested = function(event)
    867 {
    868     WebInspector._updateFocusedNode(event.data);
    869 }
    870 
    871 WebInspector._updateFocusedNode = function(nodeId)
    872 {
    873     if (WebInspector.inspectElementModeController && WebInspector.inspectElementModeController.enabled()) {
    874         InspectorFrontendHost.bringToFront();
    875         WebInspector.inspectElementModeController.disable();
    876     }
    877     WebInspector.showPanel("elements").revealAndSelectNode(nodeId);
    878 }
    879 
    880 WebInspector.showAnchorLocation = function(anchor)
    881 {
    882     var preferredPanel = this.panels[anchor.preferredPanel];
    883     if (preferredPanel && WebInspector._showAnchorLocationInPanel(anchor, preferredPanel))
    884         return true;
    885     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("sources")))
    886         return true;
    887     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("resources")))
    888         return true;
    889     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("network")))
    890         return true;
    891     return false;
    892 }
    893 
    894 WebInspector._showAnchorLocationInPanel = function(anchor, panel)
    895 {
    896     if (!panel)
    897         return false;
    898 
    899     var result = panel.showAnchorLocation(anchor);
    900     if (result) {
    901         // FIXME: support webkit-html-external-link links here.
    902         if (anchor.classList.contains("webkit-html-external-link")) {
    903             anchor.classList.remove("webkit-html-external-link");
    904             anchor.classList.add("webkit-html-resource-link");
    905         }
    906     }
    907     return result;
    908 }
    909 
    910 WebInspector.evaluateInConsole = function(expression, showResultOnly)
    911 {
    912     this.showConsole();
    913     this.consoleView.evaluateUsingTextPrompt(expression, showResultOnly);
    914 }
    915 
    916 WebInspector.addMainEventListeners = function(doc)
    917 {
    918     doc.addEventListener("keydown", this.documentKeyDown.bind(this), true);
    919     doc.addEventListener("keydown", this.postDocumentKeyDown.bind(this), false);
    920     doc.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
    921     doc.addEventListener("copy", this.documentCopy.bind(this), false);
    922     doc.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true);
    923     doc.addEventListener("click", this.documentClick.bind(this), true);
    924 }
    925 
    926 WebInspector.Zoom = {
    927     Table: [0.25, 0.33, 0.5, 0.66, 0.75, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5],
    928     DefaultOffset: 6
    929 }
    930 
    931 
    932 // Ex-DevTools.js content
    933 
    934 /**
    935  * @param {!ExtensionDescriptor} extensionInfo
    936  * @return {string}
    937  */
    938 function buildPlatformExtensionAPI(extensionInfo)
    939 {
    940     return "var extensionInfo = " + JSON.stringify(extensionInfo) + ";" +
    941        "var tabId = " + WebInspector._inspectedTabId + ";" +
    942        platformExtensionAPI.toString();
    943 }
    944 
    945 WebInspector.setInspectedTabId = function(tabId)
    946 {
    947     WebInspector._inspectedTabId = tabId;
    948 }
    949 
    950 /**
    951  * @return {string}
    952  */
    953 WebInspector.getSelectionBackgroundColor = function()
    954 {
    955     return InspectorFrontendHost.getSelectionBackgroundColor();
    956 }
    957 
    958 /**
    959  * @return {string}
    960  */
    961 WebInspector.getSelectionForegroundColor = function()
    962 {
    963     return InspectorFrontendHost.getSelectionForegroundColor();
    964 }
    965 
    966 window.DEBUG = true;
    967