Web Inspector: register context menu providers for lazily loaded panels.
[WebKit-https.git] / Source / WebCore / inspector / front-end / inspector.js
1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
3  * Copyright (C) 2007 Matt Lilek (pewtermoose@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         var parentElement = document.getElementById("main");
37         WebInspector.inspectorView.show(parentElement);
38         WebInspector.inspectorView.addEventListener(WebInspector.InspectorView.Events.PanelSelected, this._panelSelected, this);
39
40         var elements = new WebInspector.PanelDescriptor("elements", WebInspector.UIString("Elements"), "ElementsPanel", "ElementsPanel.js");
41         var resources = new WebInspector.PanelDescriptor("resources", WebInspector.UIString("Resources"), "ResourcesPanel", "ResourcesPanel.js");
42         var network = new WebInspector.NetworkPanelDescriptor();
43         var scripts = new WebInspector.ScriptsPanelDescriptor();
44         var timeline = new WebInspector.PanelDescriptor("timeline", WebInspector.UIString("Timeline"), "TimelinePanel", "TimelinePanel.js");
45         var profiles = new WebInspector.PanelDescriptor("profiles", WebInspector.UIString("Profiles"), "ProfilesPanel", "ProfilesPanel.js");
46         var audits = new WebInspector.PanelDescriptor("audits", WebInspector.UIString("Audits"), "AuditsPanel", "AuditsPanel.js");
47         var console = new WebInspector.PanelDescriptor("console", WebInspector.UIString("Console"), "ConsolePanel");
48         var allDescriptors = [elements, resources, network, scripts, timeline, profiles, audits, console];
49
50         var panelDescriptors = [];
51         if (WebInspector.WorkerManager.isWorkerFrontend()) {
52             panelDescriptors.push(scripts);
53             panelDescriptors.push(timeline);
54             panelDescriptors.push(profiles);
55             panelDescriptors.push(console);
56             return panelDescriptors;
57         }
58         var allDescriptors = [elements, resources, network, scripts, timeline, profiles, audits, console];
59         var hiddenPanels = InspectorFrontendHost.hiddenPanels();
60         for (var i = 0; i < allDescriptors.length; ++i) {
61             if (hiddenPanels.indexOf(allDescriptors[i].name) === -1)
62                 panelDescriptors.push(allDescriptors[i]);
63         }
64         return panelDescriptors;
65     },
66
67     _panelSelected: function()
68     {
69         this._toggleConsoleButton.disabled = WebInspector.inspectorView.currentPanel().name === "console";
70     },
71
72     _createGlobalStatusBarItems: function()
73     {
74         var bottomStatusBarContainer = document.getElementById("bottom-status-bar-container");
75         this._dockToggleButton = new WebInspector.StatusBarButton("", "dock-status-bar-item", 3);
76         this._dockToggleButton.makeLongClickEnabled(this._createDockOptions.bind(this));
77         this._dockToggleButton.addEventListener("click", this._toggleAttach.bind(this), false);
78         this._updateDockButtonState();
79
80         var mainStatusBar = document.getElementById("main-status-bar");
81         mainStatusBar.insertBefore(this._dockToggleButton.element, bottomStatusBarContainer);
82
83         this._toggleConsoleButton = new WebInspector.StatusBarButton(WebInspector.UIString("Show console."), "console-status-bar-item");
84         this._toggleConsoleButton.addEventListener("click", this._toggleConsoleButtonClicked.bind(this), false);
85         mainStatusBar.insertBefore(this._toggleConsoleButton.element, bottomStatusBarContainer);
86
87         if (!WebInspector.WorkerManager.isWorkerFrontend()) {
88             this._nodeSearchButton = new WebInspector.StatusBarButton(WebInspector.UIString("Select an element in the page to inspect it."), "node-search-status-bar-item");
89             this._nodeSearchButton.addEventListener("click", this._toggleSearchingForNode, this);
90             mainStatusBar.insertBefore(this._nodeSearchButton.element, bottomStatusBarContainer);
91         }
92
93         mainStatusBar.appendChild(this.settingsController.statusBarItem);
94     },
95
96     _createDockOptions: function()
97     {
98         var alternateDockToggleButton1 = new WebInspector.StatusBarButton("Dock to main window.", "dock-status-bar-item", 3);
99         var alternateDockToggleButton2 = new WebInspector.StatusBarButton("Undock into separate window.", "dock-status-bar-item", 3);
100
101         if (this.attached) {
102             alternateDockToggleButton1.state = WebInspector.settings.dockToRight.get() ? "bottom" : "right";
103             alternateDockToggleButton2.state = "undock";
104         } else {
105             alternateDockToggleButton1.state = WebInspector.settings.dockToRight.get() ? "bottom" : "right";
106             alternateDockToggleButton2.state = WebInspector.settings.dockToRight.get() ? "right" : "bottom";
107         }
108
109         alternateDockToggleButton1.addEventListener("click", onClick.bind(this), false);
110         alternateDockToggleButton2.addEventListener("click", onClick.bind(this), false);
111
112         function onClick(e)
113         {
114             var state = e.target.state;
115             if (state === "undock")
116                 this._toggleAttach();
117             else if (state === "right") {
118                 if (!this.attached)
119                     this._toggleAttach();
120                 WebInspector.settings.dockToRight.set(true);
121             } else if (state === "bottom") {
122                 if (!this.attached)
123                     this._toggleAttach();
124                 WebInspector.settings.dockToRight.set(false);
125             }
126         }
127
128         return [alternateDockToggleButton1, alternateDockToggleButton2];
129     },
130
131     _updateDockButtonState: function()
132     {
133         if (!this._dockToggleButton)
134             return;
135
136         if (this.attached) {
137             this._dockToggleButton.disabled = false;
138             this._dockToggleButton.state = "undock";
139             this._dockToggleButton.title = WebInspector.UIString("Undock into separate window.");
140         } else {
141             this._dockToggleButton.disabled = this._isDockingUnavailable;
142             this._dockToggleButton.state = WebInspector.settings.dockToRight.get() ? "right" : "bottom";
143             this._dockToggleButton.title = WebInspector.UIString("Dock to main window.");
144         }
145     },
146
147     _toggleAttach: function()
148     {
149         if (!this._attached) {
150             InspectorFrontendHost.requestAttachWindow();
151             WebInspector.userMetrics.WindowDocked.record();
152         } else {
153             InspectorFrontendHost.requestDetachWindow();
154             WebInspector.userMetrics.WindowUndocked.record();
155         }
156     },
157
158     _toggleConsoleButtonClicked: function()
159     {
160         if (this._toggleConsoleButton.disabled)
161             return;
162
163         this._toggleConsoleButton.toggled = !this._toggleConsoleButton.toggled;
164
165         var animationType = window.event && window.event.shiftKey ? WebInspector.Drawer.AnimationType.Slow : WebInspector.Drawer.AnimationType.Normal;
166         if (this._toggleConsoleButton.toggled) {
167             this._toggleConsoleButton.title = WebInspector.UIString("Hide console.");
168             this.drawer.show(this.consoleView, animationType);
169             this._consoleWasShown = true;
170         } else {
171             this._toggleConsoleButton.title = WebInspector.UIString("Show console.");
172             this.drawer.hide(animationType);
173             delete this._consoleWasShown;
174         }
175     },
176
177     /**
178      * @param {Element} statusBarElement
179      * @param {WebInspector.View} view
180      * @param {function()=} onclose
181      */
182     showViewInDrawer: function(statusBarElement, view, onclose)
183     {
184         this._toggleConsoleButton.title = WebInspector.UIString("Hide console.");
185         this._toggleConsoleButton.toggled = false;
186         this._closePreviousDrawerView();
187
188         var drawerStatusBarHeader = document.createElement("div");
189         drawerStatusBarHeader.className = "drawer-header status-bar-item";
190         drawerStatusBarHeader.appendChild(statusBarElement);
191         drawerStatusBarHeader.onclose = onclose;
192
193         var closeButton = drawerStatusBarHeader.createChild("span");
194         closeButton.textContent = WebInspector.UIString("\u00D7");
195         closeButton.addStyleClass("drawer-header-close-button");
196         closeButton.addEventListener("click", this.closeViewInDrawer.bind(this), false);
197
198         document.getElementById("panel-status-bar").firstElementChild.appendChild(drawerStatusBarHeader);
199         this._drawerStatusBarHeader = drawerStatusBarHeader;
200         this.drawer.show(view, WebInspector.Drawer.AnimationType.Immediately);
201     },
202
203     closeViewInDrawer: function()
204     {
205         if (this._drawerStatusBarHeader) {
206             this._closePreviousDrawerView();
207
208             // Once drawer is closed console should be shown if it was shown before current view replaced it in drawer. 
209             if (!this._consoleWasShown)
210                 this.drawer.hide(WebInspector.Drawer.AnimationType.Immediately);
211             else
212                 this._toggleConsoleButtonClicked();
213         }
214     },
215
216     _closePreviousDrawerView: function()
217     {
218         if (this._drawerStatusBarHeader) {
219             this._drawerStatusBarHeader.parentElement.removeChild(this._drawerStatusBarHeader);
220             if (this._drawerStatusBarHeader.onclose)
221                 this._drawerStatusBarHeader.onclose();
222             delete this._drawerStatusBarHeader;
223         }
224     },
225
226     get attached()
227     {
228         return this._attached;
229     },
230
231     set attached(x)
232     {
233         if (this._attached === x)
234             return;
235
236         this._attached = x;
237
238         if (x)
239             document.body.removeStyleClass("detached");
240         else
241             document.body.addStyleClass("detached");
242
243         this._setCompactMode(x && !WebInspector.settings.dockToRight.get());
244         this._updateDockButtonState();
245     },
246
247     isCompactMode: function()
248     {
249         return this.attached && !WebInspector.settings.dockToRight.get();
250     },
251
252     _setCompactMode: function(x)
253     {
254         var body = document.body;
255         if (x)
256             body.addStyleClass("compact");
257         else
258             body.removeStyleClass("compact");
259         WebInspector.windowResize();
260     },
261
262     _updateErrorAndWarningCounts: function()
263     {
264         var errorWarningElement = document.getElementById("error-warning-count");
265         if (!errorWarningElement)
266             return;
267
268         var errors = WebInspector.console.errors;
269         var warnings = WebInspector.console.warnings;
270         if (!errors && !warnings) {
271             errorWarningElement.addStyleClass("hidden");
272             return;
273         }
274
275         errorWarningElement.removeStyleClass("hidden");
276
277         errorWarningElement.removeChildren();
278
279         if (errors) {
280             var errorImageElement = document.createElement("img");
281             errorImageElement.id = "error-count-img";
282             errorWarningElement.appendChild(errorImageElement);
283             var errorElement = document.createElement("span");
284             errorElement.id = "error-count";
285             errorElement.textContent = errors;
286             errorWarningElement.appendChild(errorElement);
287         }
288
289         if (warnings) {
290             var warningsImageElement = document.createElement("img");
291             warningsImageElement.id = "warning-count-img";
292             errorWarningElement.appendChild(warningsImageElement);
293             var warningsElement = document.createElement("span");
294             warningsElement.id = "warning-count";
295             warningsElement.textContent = warnings;
296             errorWarningElement.appendChild(warningsElement);
297         }
298
299         if (errors) {
300             if (warnings) {
301                 if (errors == 1) {
302                     if (warnings == 1)
303                         errorWarningElement.title = WebInspector.UIString("%d error, %d warning", errors, warnings);
304                     else
305                         errorWarningElement.title = WebInspector.UIString("%d error, %d warnings", errors, warnings);
306                 } else if (warnings == 1)
307                     errorWarningElement.title = WebInspector.UIString("%d errors, %d warning", errors, warnings);
308                 else
309                     errorWarningElement.title = WebInspector.UIString("%d errors, %d warnings", errors, warnings);
310             } else if (errors == 1)
311                 errorWarningElement.title = WebInspector.UIString("%d error", errors);
312             else
313                 errorWarningElement.title = WebInspector.UIString("%d errors", errors);
314         } else if (warnings == 1)
315             errorWarningElement.title = WebInspector.UIString("%d warning", warnings);
316         else if (warnings)
317             errorWarningElement.title = WebInspector.UIString("%d warnings", warnings);
318         else
319             errorWarningElement.title = null;
320     },
321
322     get inspectedPageDomain()
323     {
324         var parsedURL = WebInspector.inspectedPageURL && WebInspector.inspectedPageURL.asParsedURL();
325         return parsedURL ? parsedURL.host : "";
326     },
327
328     _initializeCapability: function(name, callback, error, result)
329     {
330         Capabilities[name] = result;
331         if (callback)
332             callback();
333     },
334
335     _zoomIn: function()
336     {
337         ++this._zoomLevel;
338         this._requestZoom();
339     },
340
341     _zoomOut: function()
342     {
343         --this._zoomLevel;
344         this._requestZoom();
345     },
346
347     _resetZoom: function()
348     {
349         this._zoomLevel = 0;
350         this._requestZoom();
351     },
352
353     _requestZoom: function()
354     {
355         WebInspector.settings.zoomLevel.set(this._zoomLevel);
356         InspectorFrontendHost.setZoomFactor(Math.pow(1.2, this._zoomLevel));
357     },
358
359     _toggleSearchingForNode: function()
360     {
361         var enabled = !this._nodeSearchButton.toggled;
362         /**
363          * @param {?Protocol.Error} error
364          */
365         function callback(error)
366         {
367             if (!error)
368                 this._nodeSearchButton.toggled = enabled;
369         }
370         WebInspector.domAgent.setInspectModeEnabled(enabled, callback.bind(this));
371     },
372
373     _profilesLinkifier: function(title)
374     {
375         var profileStringMatches = WebInspector.ProfileURLRegExp.exec(title);
376         if (profileStringMatches) {
377             var profilesPanel = /** @ type {WebInspector.ProfilesPanel} */ WebInspector.panel("profiles");
378             title = WebInspector.ProfilesPanel._instance.displayTitleForProfileLink(profileStringMatches[2], profileStringMatches[1]);
379         }
380         return title;
381     },
382
383     _debuggerPaused: function()
384     {
385         // Create scripts panel upon demand.
386         WebInspector.panel("scripts");
387     }
388 }
389
390 WebInspector.Events = {
391     InspectorClosing: "InspectorClosing"
392 }
393
394 {(function parseQueryParameters()
395 {
396     WebInspector.queryParamsObject = {};
397     var queryParams = window.location.search;
398     if (!queryParams)
399         return;
400     var params = queryParams.substring(1).split("&");
401     for (var i = 0; i < params.length; ++i) {
402         var pair = params[i].split("=");
403         WebInspector.queryParamsObject[pair[0]] = pair[1];
404     }
405 })();}
406
407 WebInspector.loaded = function()
408 {
409     InspectorBackend.loadFromJSONIfNeeded();
410
411     if (WebInspector.WorkerManager.isDedicatedWorkerFrontend()) {
412         // Do not create socket for the worker front-end.
413         WebInspector.doLoadedDone();
414         return;
415     }
416
417     var ws;
418     if ("ws" in WebInspector.queryParamsObject)
419         ws = "ws://" + WebInspector.queryParamsObject.ws;
420     else if ("page" in WebInspector.queryParamsObject) {
421         var page = WebInspector.queryParamsObject.page;
422         var host = "host" in WebInspector.queryParamsObject ? WebInspector.queryParamsObject.host : window.location.host;
423         ws = "ws://" + host + "/devtools/page/" + page;
424     }
425
426     if (ws) {
427         WebInspector.socket = new WebSocket(ws);
428         WebInspector.socket.onmessage = function(message) { InspectorBackend.dispatch(message.data); }
429         WebInspector.socket.onerror = function(error) { console.error(error); }
430         WebInspector.socket.onopen = function() {
431             InspectorFrontendHost.sendMessageToBackend = WebInspector.socket.send.bind(WebInspector.socket);
432             WebInspector.doLoadedDone();
433         }
434         return;
435     }
436
437     WebInspector.doLoadedDone();
438
439     // In case of loading as a web page with no bindings / harness, kick off initialization manually.
440     if (InspectorFrontendHost.isStub) {
441         InspectorFrontendAPI.dispatchQueryParameters();
442         WebInspector._doLoadedDoneWithCapabilities();
443     }
444 }
445
446 WebInspector.doLoadedDone = function()
447 {
448     // Install styles and themes
449     WebInspector.installPortStyles();
450     if (WebInspector.socket)
451         document.body.addStyleClass("remote");
452
453     if (WebInspector.queryParamsObject.toolbarColor && WebInspector.queryParamsObject.textColor)
454         WebInspector.setToolbarColors(WebInspector.queryParamsObject.toolbarColor, WebInspector.queryParamsObject.textColor);
455
456     InspectorFrontendHost.loaded();
457     WebInspector.WorkerManager.loaded();
458
459     DebuggerAgent.causesRecompilation(WebInspector._initializeCapability.bind(WebInspector, "debuggerCausesRecompilation", null));
460     DebuggerAgent.supportsSeparateScriptCompilationAndExecution(WebInspector._initializeCapability.bind(WebInspector, "separateScriptCompilationAndExecutionEnabled", null));
461     ProfilerAgent.causesRecompilation(WebInspector._initializeCapability.bind(WebInspector, "profilerCausesRecompilation", null));
462     ProfilerAgent.isSampling(WebInspector._initializeCapability.bind(WebInspector, "samplingCPUProfiler", null));
463     ProfilerAgent.hasHeapProfiler(WebInspector._initializeCapability.bind(WebInspector, "heapProfilerPresent", null));
464     TimelineAgent.supportsFrameInstrumentation(WebInspector._initializeCapability.bind(WebInspector, "timelineSupportsFrameInstrumentation", null));
465     PageAgent.canOverrideDeviceMetrics(WebInspector._initializeCapability.bind(WebInspector, "canOverrideDeviceMetrics", null));
466     PageAgent.canOverrideGeolocation(WebInspector._initializeCapability.bind(WebInspector, "canOverrideGeolocation", null));
467     PageAgent.canOverrideDeviceOrientation(WebInspector._initializeCapability.bind(WebInspector, "canOverrideDeviceOrientation", WebInspector._doLoadedDoneWithCapabilities.bind(WebInspector)));
468 }
469
470 WebInspector._doLoadedDoneWithCapabilities = function()
471 {
472     WebInspector.shortcutsScreen = new WebInspector.ShortcutsScreen();
473     this._registerShortcuts();
474
475     // set order of some sections explicitly
476     WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
477     WebInspector.shortcutsScreen.section(WebInspector.UIString("Elements Panel"));
478
479     this.console = new WebInspector.ConsoleModel();
480     this.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._updateErrorAndWarningCounts, this);
481     this.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._updateErrorAndWarningCounts, this);
482     this.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._updateErrorAndWarningCounts, this);
483
484     WebInspector.CSSCompletions.requestCSSNameCompletions();
485
486     this.drawer = new WebInspector.Drawer();
487     this.consoleView = new WebInspector.ConsoleView(WebInspector.WorkerManager.isWorkerFrontend());
488
489     this.networkManager = new WebInspector.NetworkManager();
490     this.resourceTreeModel = new WebInspector.ResourceTreeModel(this.networkManager);
491     this.debuggerModel = new WebInspector.DebuggerModel();
492     this.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
493     this.networkLog = new WebInspector.NetworkLog();
494     this.domAgent = new WebInspector.DOMAgent();
495     this.javaScriptContextManager = new WebInspector.JavaScriptContextManager(this.resourceTreeModel, this.consoleView);
496
497     this.scriptSnippetModel = new WebInspector.ScriptSnippetModel();
498
499     InspectorBackend.registerInspectorDispatcher(this);
500
501     this.cssModel = new WebInspector.CSSStyleModel();
502     this.timelineManager = new WebInspector.TimelineManager();
503     this.userAgentSupport = new WebInspector.UserAgentSupport();
504
505     this.searchController = new WebInspector.SearchController();
506     this.advancedSearchController = new WebInspector.AdvancedSearchController();
507     this.settingsController = new WebInspector.SettingsController();
508
509     this.domBreakpointsSidebarPane = new WebInspector.DOMBreakpointsSidebarPane();
510
511     this._zoomLevel = WebInspector.settings.zoomLevel.get();
512     if (this._zoomLevel)
513         this._requestZoom();
514
515     var autoselectPanel = WebInspector.UIString("a panel chosen automatically");
516     var openAnchorLocationSetting = WebInspector.settings.createSetting("openLinkHandler", autoselectPanel);
517     this.openAnchorLocationRegistry = new WebInspector.HandlerRegistry(openAnchorLocationSetting);
518     this.openAnchorLocationRegistry.registerHandler(autoselectPanel, function() { return false; });
519
520     this.workspace = new WebInspector.Workspace();
521     this.breakpointManager = new WebInspector.BreakpointManager(WebInspector.settings.breakpoints, this.debuggerModel, this.workspace);
522
523     this._createGlobalStatusBarItems();
524
525     WebInspector._installDockToRight();
526
527     this.toolbar = new WebInspector.Toolbar();
528     WebInspector.startBatchUpdate();
529     var panelDescriptors = this._panelDescriptors();
530     for (var i = 0; i < panelDescriptors.length; ++i)
531         WebInspector.inspectorView.addPanel(panelDescriptors[i]);
532     WebInspector.endBatchUpdate();
533
534     this.addMainEventListeners(document);
535     WebInspector.registerLinkifierPlugin(this._profilesLinkifier.bind(this));
536
537     window.addEventListener("resize", this.windowResize.bind(this), true);
538
539     var errorWarningCount = document.getElementById("error-warning-count");
540     errorWarningCount.addEventListener("click", this.showConsole.bind(this), false);
541     this._updateErrorAndWarningCounts();
542
543     this.extensionServer.initExtensions();
544
545     this.console.enableAgent();
546
547     function showInitialPanel()
548     {
549         if (!WebInspector.inspectorView.currentPanel())
550             WebInspector.showPanel(WebInspector.settings.lastActivePanel.get());
551     }
552
553     InspectorAgent.enable(showInitialPanel);
554     this.databaseModel = new WebInspector.DatabaseModel();
555     this.domStorageModel = new WebInspector.DOMStorageModel();
556
557     if (!Capabilities.profilerCausesRecompilation || WebInspector.settings.profilerEnabled.get())
558         ProfilerAgent.enable();
559
560     if (WebInspector.settings.showPaintRects.get())
561         PageAgent.setShowPaintRects(true);
562
563     if (WebInspector.settings.javaScriptDisabled.get())
564         PageAgent.setScriptExecutionDisabled(true);
565
566     this.domAgent._emulateTouchEventsChanged();
567
568     WebInspector.WorkerManager.loadCompleted();
569     InspectorFrontendAPI.loadCompleted();
570 }
571
572 WebInspector._installDockToRight = function()
573 {
574     // Re-use Settings infrastructure for the dock-to-right settings UI
575     WebInspector.settings.dockToRight.set(WebInspector.queryParamsObject.dockSide === "right");
576
577     if (WebInspector.settings.dockToRight.get())
578         document.body.addStyleClass("dock-to-right");
579
580     if (WebInspector.attached)
581         WebInspector._setCompactMode(!WebInspector.settings.dockToRight.get());
582
583     WebInspector.settings.dockToRight.addChangeListener(listener.bind(this));
584
585     function listener(event)
586     {
587         var value = WebInspector.settings.dockToRight.get();
588         if (value) {
589             InspectorFrontendHost.requestSetDockSide("right");
590             document.body.addStyleClass("dock-to-right");
591         } else {
592             InspectorFrontendHost.requestSetDockSide("bottom");
593             document.body.removeStyleClass("dock-to-right");
594         }
595         if (WebInspector.attached)
596             WebInspector._setCompactMode(!value);
597         else
598             WebInspector._updateDockButtonState();
599     }
600 }
601
602 var windowLoaded = function()
603 {
604     var localizedStringsURL = InspectorFrontendHost.localizedStringsURL();
605     if (localizedStringsURL) {
606         var localizedStringsScriptElement = document.createElement("script");
607         localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false);
608         localizedStringsScriptElement.type = "text/javascript";
609         localizedStringsScriptElement.src = localizedStringsURL;
610         document.head.appendChild(localizedStringsScriptElement);
611     } else
612         WebInspector.loaded();
613
614     WebInspector.attached = WebInspector.queryParamsObject.docked === "true";
615
616     window.removeEventListener("DOMContentLoaded", windowLoaded, false);
617     delete windowLoaded;
618 };
619
620 window.addEventListener("DOMContentLoaded", windowLoaded, false);
621
622 // We'd like to enforce asynchronous interaction between the inspector controller and the frontend.
623 // It is needed to prevent re-entering the backend code.
624 // Also, native dispatches do not guarantee setTimeouts to be serialized, so we
625 // enforce serialization using 'messagesToDispatch' queue. It is also important that JSC debugger
626 // tests require that each command was dispatch within individual timeout callback, so we don't batch them.
627
628 var messagesToDispatch = [];
629
630 WebInspector.dispatchQueueIsEmpty = function() {
631     return messagesToDispatch.length == 0;
632 }
633
634 WebInspector.dispatch = function(message) {
635     messagesToDispatch.push(message);
636     setTimeout(function() {
637         InspectorBackend.dispatch(messagesToDispatch.shift());
638     }, 0);
639 }
640
641 WebInspector.dispatchMessageFromBackend = function(messageObject)
642 {
643     WebInspector.dispatch(messageObject);
644 }
645
646 WebInspector.windowResize = function(event)
647 {
648     if (WebInspector.inspectorView)
649         WebInspector.inspectorView.doResize();
650     if (WebInspector.drawer)
651         WebInspector.drawer.resize();
652     if (WebInspector.toolbar)
653         WebInspector.toolbar.resize();
654     if (WebInspector.settingsController)
655         WebInspector.settingsController.resize();
656 }
657
658 WebInspector.setDockingUnavailable = function(unavailable)
659 {
660     this._isDockingUnavailable = unavailable;
661     this._updateDockButtonState();
662 }
663
664 WebInspector.close = function(event)
665 {
666     if (this._isClosing)
667         return;
668     this._isClosing = true;
669     this.notifications.dispatchEventToListeners(WebInspector.Events.InspectorClosing);
670     InspectorFrontendHost.closeWindow();
671 }
672
673 WebInspector.documentClick = function(event)
674 {
675     var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
676     if (!anchor || anchor.target === "_blank")
677         return;
678
679     // Prevent the link from navigating, since we don't do any navigation by following links normally.
680     event.consume(true);
681
682     function followLink()
683     {
684         if (WebInspector.isBeingEdited(event.target) || WebInspector._showAnchorLocation(anchor))
685             return;
686
687         const profileMatch = WebInspector.ProfileURLRegExp.exec(anchor.href);
688         if (profileMatch) {
689             WebInspector.showProfileForURL(anchor.href);
690             return;
691         }
692
693         var parsedURL = anchor.href.asParsedURL();
694         if (parsedURL && parsedURL.scheme === "webkit-link-action") {
695             if (parsedURL.host === "show-panel") {
696                 var panel = parsedURL.path.substring(1);
697                 if (WebInspector.panel(panel))
698                     WebInspector.showPanel(panel);
699             }
700             return;
701         }
702
703         InspectorFrontendHost.openInNewTab(anchor.href);
704     }
705
706     if (WebInspector.followLinkTimeout)
707         clearTimeout(WebInspector.followLinkTimeout);
708
709     if (anchor.preventFollowOnDoubleClick) {
710         // Start a timeout if this is the first click, if the timeout is canceled
711         // before it fires, then a double clicked happened or another link was clicked.
712         if (event.detail === 1)
713             WebInspector.followLinkTimeout = setTimeout(followLink, 333);
714         return;
715     }
716
717     followLink();
718 }
719
720 WebInspector.openResource = function(resourceURL, inResourcesPanel)
721 {
722     var resource = WebInspector.resourceForURL(resourceURL);
723     if (inResourcesPanel && resource)
724         WebInspector.showPanel("resources").showResource(resource);
725     else
726         InspectorFrontendHost.openInNewTab(resourceURL);
727 }
728
729 WebInspector._registerShortcuts = function()
730 {
731     var shortcut = WebInspector.KeyboardShortcut;
732     var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("All Panels"));
733     var keys = [
734         shortcut.shortcutToString("]", shortcut.Modifiers.CtrlOrMeta),
735         shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta)
736     ];
737     section.addRelatedKeys(keys, WebInspector.UIString("Go to the panel to the left/right"));
738
739     var keys = [
740         shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta | shortcut.Modifiers.Alt),
741         shortcut.shortcutToString("]", shortcut.Modifiers.CtrlOrMeta | shortcut.Modifiers.Alt)
742     ];
743     section.addRelatedKeys(keys, WebInspector.UIString("Go back/forward in panel history"));
744
745     section.addKey(shortcut.shortcutToString(shortcut.Keys.Esc), WebInspector.UIString("Toggle console"));
746     section.addKey(shortcut.shortcutToString("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search"));
747
748     var advancedSearchShortcut = WebInspector.AdvancedSearchController.createShortcut();
749     section.addKey(advancedSearchShortcut.name, WebInspector.UIString("Search across all sources"));
750
751     var openResourceShortcut = WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
752     section.addKey(openResourceShortcut.name, WebInspector.UIString("Go to source"));
753
754     if (WebInspector.isMac()) {
755         keys = [
756             shortcut.shortcutToString("g", shortcut.Modifiers.Meta),
757             shortcut.shortcutToString("g", shortcut.Modifiers.Meta | shortcut.Modifiers.Shift)
758         ];
759         section.addRelatedKeys(keys, WebInspector.UIString("Find next/previous"));
760     }
761
762     var goToShortcut = WebInspector.GoToLineDialog.createShortcut();
763     section.addKey(goToShortcut.name, WebInspector.UIString("Go to line"));
764 }
765
766 WebInspector.documentKeyDown = function(event)
767 {
768     const helpKey = WebInspector.isMac() ? "U+003F" : "U+00BF"; // "?" for both platforms
769
770     if (event.keyIdentifier === "F1" ||
771         (event.keyIdentifier === helpKey && event.shiftKey && (!WebInspector.isBeingEdited(event.target) || event.metaKey))) {
772         this.settingsController.showSettingsScreen(WebInspector.SettingsScreen.Tabs.Shortcuts);
773         event.consume(true);
774         return;
775     }
776
777     if (WebInspector.currentFocusElement() && WebInspector.currentFocusElement().handleKeyEvent) {
778         WebInspector.currentFocusElement().handleKeyEvent(event);
779         if (event.handled) {
780             event.consume(true);
781             return;
782         }
783     }
784
785     if (WebInspector.inspectorView.currentPanel()) {
786         WebInspector.inspectorView.currentPanel().handleShortcut(event);
787         if (event.handled) {
788             event.consume(true);
789             return;
790         }
791     }
792
793     if (WebInspector.searchController.handleShortcut(event))
794         return;
795     if (WebInspector.advancedSearchController.handleShortcut(event))
796         return;
797
798     switch (event.keyIdentifier) {
799         case "U+004F": // O key
800             if (!event.shiftKey && !event.altKey && WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
801                 WebInspector.showPanel("scripts").showGoToSourceDialog();
802                 event.consume(true);
803             }
804             break;
805         case "U+0052": // R key
806             if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
807                 PageAgent.reload(event.shiftKey);
808                 event.consume(true);
809             }
810             break;
811         case "F5":
812             if (!WebInspector.isMac()) {
813                 PageAgent.reload(event.ctrlKey || event.shiftKey);
814                 event.consume(true);
815             }
816             break;
817     }
818
819     var isValidZoomShortcut = WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) &&
820         !event.altKey &&
821         !InspectorFrontendHost.isStub;
822     switch (event.keyCode) {
823         case 107: // +
824         case 187: // +
825             if (isValidZoomShortcut) {
826                 WebInspector._zoomIn();
827                 event.consume(true);
828             }
829             break;
830         case 109: // -
831         case 189: // -
832             if (isValidZoomShortcut) {
833                 WebInspector._zoomOut();
834                 event.consume(true);
835             }
836             break;
837         case 48: // 0
838             // Zoom reset shortcut does not allow "Shift" when handled by the browser.
839             if (isValidZoomShortcut && !event.shiftKey) {
840                 WebInspector._resetZoom();
841                 event.consume(true);
842             }
843             break;
844     }
845
846     // Cmd/Control + Shift + C should be a shortcut to clicking the Node Search Button.
847     // This shortcut matches Firebug.
848     if (event.keyIdentifier === "U+0043") { // C key
849         if (WebInspector.isMac())
850             var isNodeSearchKey = event.metaKey && !event.ctrlKey && !event.altKey && event.shiftKey;
851         else
852             var isNodeSearchKey = event.ctrlKey && !event.metaKey && !event.altKey && event.shiftKey;
853
854         if (isNodeSearchKey) {
855             this._toggleSearchingForNode();
856             event.consume(true);
857             return;
858         }
859         return;
860     }
861 }
862
863 WebInspector.postDocumentKeyDown = function(event)
864 {
865     if (event.handled)
866         return;
867
868     if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
869         // If drawer is open with some view other than console then close it.
870         if (!this._toggleConsoleButton.toggled && WebInspector.drawer.visible)
871             this.closeViewInDrawer();
872         else
873             this._toggleConsoleButtonClicked();
874     }
875 }
876
877 WebInspector.documentCanCopy = function(event)
878 {
879     if (WebInspector.inspectorView.currentPanel() && WebInspector.inspectorView.currentPanel().handleCopyEvent)
880         event.preventDefault();
881 }
882
883 WebInspector.documentCopy = function(event)
884 {
885     if (WebInspector.inspectorView.currentPanel() && WebInspector.inspectorView.currentPanel().handleCopyEvent)
886         WebInspector.inspectorView.currentPanel().handleCopyEvent(event);
887     WebInspector.documentCopyEventFired(event);
888 }
889
890 WebInspector.documentCopyEventFired = function(event)
891 {
892 }
893
894 WebInspector.contextMenuEventFired = function(event)
895 {
896     if (event.handled || event.target.hasStyleClass("popup-glasspane"))
897         event.preventDefault();
898 }
899
900 WebInspector.showConsole = function()
901 {
902     if (WebInspector._toggleConsoleButton && !WebInspector._toggleConsoleButton.toggled) {
903         if (WebInspector.drawer.visible)
904             this._closePreviousDrawerView();
905         WebInspector._toggleConsoleButtonClicked();
906     }
907 }
908
909 WebInspector.showPanel = function(panel)
910 {
911     return WebInspector.inspectorView.showPanel(panel);
912 }
913
914 WebInspector.panel = function(panel)
915 {
916     return WebInspector.inspectorView.panel(panel);
917 }
918
919 WebInspector.bringToFront = function()
920 {
921     InspectorFrontendHost.bringToFront();
922 }
923
924 /**
925  * @param {string=} messageLevel
926  * @param {boolean=} showConsole
927  */
928 WebInspector.log = function(message, messageLevel, showConsole)
929 {
930     // remember 'this' for setInterval() callback
931     var self = this;
932
933     // return indication if we can actually log a message
934     function isLogAvailable()
935     {
936         return WebInspector.ConsoleMessage && WebInspector.RemoteObject && self.console;
937     }
938
939     // flush the queue of pending messages
940     function flushQueue()
941     {
942         var queued = WebInspector.log.queued;
943         if (!queued)
944             return;
945
946         for (var i = 0; i < queued.length; ++i)
947             logMessage(queued[i]);
948
949         delete WebInspector.log.queued;
950     }
951
952     // flush the queue if it console is available
953     // - this function is run on an interval
954     function flushQueueIfAvailable()
955     {
956         if (!isLogAvailable())
957             return;
958
959         clearInterval(WebInspector.log.interval);
960         delete WebInspector.log.interval;
961
962         flushQueue();
963     }
964
965     // actually log the message
966     function logMessage(message)
967     {
968         // post the message
969         var msg = WebInspector.ConsoleMessage.create(
970             WebInspector.ConsoleMessage.MessageSource.Other,
971             messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug,
972             message);
973
974         self.console.addMessage(msg);
975         if (showConsole)
976             WebInspector.showConsole();
977     }
978
979     // if we can't log the message, queue it
980     if (!isLogAvailable()) {
981         if (!WebInspector.log.queued)
982             WebInspector.log.queued = [];
983
984         WebInspector.log.queued.push(message);
985
986         if (!WebInspector.log.interval)
987             WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000);
988
989         return;
990     }
991
992     // flush the pending queue if any
993     flushQueue();
994
995     // log the message
996     logMessage(message);
997 }
998
999 WebInspector.showErrorMessage = function(error)
1000 {
1001     WebInspector.log(error, WebInspector.ConsoleMessage.MessageLevel.Error, true);
1002 }
1003
1004 WebInspector.inspect = function(payload, hints)
1005 {
1006     var object = WebInspector.RemoteObject.fromPayload(payload);
1007     if (object.subtype === "node") {
1008         function callback(nodeId)
1009         {
1010             WebInspector._updateFocusedNode(nodeId);
1011             object.release();
1012         }
1013         object.pushNodeToFrontend(callback);
1014         return;
1015     }
1016
1017     if (hints.databaseId)
1018         WebInspector.showPanel("resources").selectDatabase(WebInspector.databaseModel.databaseForId(hints.databaseId));
1019     else if (hints.domStorageId)
1020         WebInspector.showPanel("resources").selectDOMStorage(WebInspector.domStorageModel.storageForId(hints.domStorageId));
1021
1022     object.release();
1023 }
1024
1025 WebInspector._updateFocusedNode = function(nodeId)
1026 {
1027     if (WebInspector._nodeSearchButton.toggled) {
1028         InspectorFrontendHost.bringToFront();
1029         WebInspector._nodeSearchButton.toggled = false;
1030     }
1031     WebInspector.showPanel("elements").revealAndSelectNode(nodeId);
1032 }
1033
1034 WebInspector._showAnchorLocation = function(anchor)
1035 {
1036     if (WebInspector.openAnchorLocationRegistry.dispatch({ url: anchor.href, lineNumber: anchor.lineNumber}))
1037         return true;
1038     var preferredPanel = this.panels[anchor.preferredPanel];
1039     if (preferredPanel && WebInspector._showAnchorLocationInPanel(anchor, preferredPanel))
1040         return true;
1041     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("scripts")))
1042         return true;
1043     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("resources")))
1044         return true;
1045     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("network")))
1046         return true;
1047     return false;
1048 }
1049
1050 WebInspector._showAnchorLocationInPanel = function(anchor, panel)
1051 {
1052     if (!panel || !panel.canShowAnchorLocation(anchor))
1053         return false;
1054
1055     // FIXME: support webkit-html-external-link links here.
1056     if (anchor.hasStyleClass("webkit-html-external-link")) {
1057         anchor.removeStyleClass("webkit-html-external-link");
1058         anchor.addStyleClass("webkit-html-resource-link");
1059     }
1060
1061     WebInspector.inspectorView.showPanelForAnchorNavigation(panel);
1062     panel.showAnchorLocation(anchor);
1063     return true;
1064 }
1065
1066 WebInspector.showProfileForURL = function(url)
1067 {
1068     WebInspector.showPanel("profiles").showProfileForURL(url);
1069 }
1070
1071 WebInspector.evaluateInConsole = function(expression, showResultOnly)
1072 {
1073     this.showConsole();
1074     this.consoleView.evaluateUsingTextPrompt(expression, showResultOnly);
1075 }
1076
1077 WebInspector.addMainEventListeners = function(doc)
1078 {
1079     doc.addEventListener("keydown", this.documentKeyDown.bind(this), true);
1080     doc.addEventListener("keydown", this.postDocumentKeyDown.bind(this), false);
1081     doc.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
1082     doc.addEventListener("copy", this.documentCopy.bind(this), true);
1083     doc.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true);
1084     doc.addEventListener("click", this.documentClick.bind(this), true);
1085 }
1086
1087 WebInspector.frontendReused = function()
1088 {
1089     this.resourceTreeModel.frontendReused();
1090 }
1091
1092 WebInspector.ProfileURLRegExp = /webkit-profile:\/\/(.+)\/(.+)#([0-9]+)/;