91ee54f03a4437af31dcdb97c96108fc3ea6e7ac
[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.PanelDescriptor("network", WebInspector.UIString("Network"), "NetworkPanel", "NetworkPanel.js");
43         var scripts = new WebInspector.PanelDescriptor("scripts", WebInspector.UIString("Sources"), "ScriptsPanel", "ScriptsPanel.js");
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() || "").split(',');
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     WebInspector.doLoadedDone();
437 }
438
439 WebInspector.doLoadedDone = function()
440 {
441     // Install styles and themes
442     WebInspector.installPortStyles();
443     if (WebInspector.socket)
444         document.body.addStyleClass("remote");
445
446     if (WebInspector.queryParamsObject.toolbarColor && WebInspector.queryParamsObject.textColor)
447         WebInspector.setToolbarColors(WebInspector.queryParamsObject.toolbarColor, WebInspector.queryParamsObject.textColor);
448
449     InspectorFrontendHost.loaded();
450     WebInspector.WorkerManager.loaded();
451
452     DebuggerAgent.causesRecompilation(WebInspector._initializeCapability.bind(WebInspector, "debuggerCausesRecompilation", null));
453     DebuggerAgent.supportsSeparateScriptCompilationAndExecution(WebInspector._initializeCapability.bind(WebInspector, "separateScriptCompilationAndExecutionEnabled", null));
454     ProfilerAgent.causesRecompilation(WebInspector._initializeCapability.bind(WebInspector, "profilerCausesRecompilation", null));
455     ProfilerAgent.isSampling(WebInspector._initializeCapability.bind(WebInspector, "samplingCPUProfiler", null));
456     ProfilerAgent.hasHeapProfiler(WebInspector._initializeCapability.bind(WebInspector, "heapProfilerPresent", null));
457     TimelineAgent.supportsFrameInstrumentation(WebInspector._initializeCapability.bind(WebInspector, "timelineSupportsFrameInstrumentation", null));
458     PageAgent.canOverrideDeviceMetrics(WebInspector._initializeCapability.bind(WebInspector, "canOverrideDeviceMetrics", null));
459     PageAgent.canOverrideGeolocation(WebInspector._initializeCapability.bind(WebInspector, "canOverrideGeolocation", null));
460     PageAgent.canOverrideDeviceOrientation(WebInspector._initializeCapability.bind(WebInspector, "canOverrideDeviceOrientation", WebInspector._doLoadedDoneWithCapabilities.bind(WebInspector)));
461     if ("debugLoad" in WebInspector.queryParamsObject)
462         WebInspector._doLoadedDoneWithCapabilities();
463 }
464
465 WebInspector._doLoadedDoneWithCapabilities = function()
466 {
467     WebInspector.shortcutsScreen = new WebInspector.ShortcutsScreen();
468     this._registerShortcuts();
469
470     // set order of some sections explicitly
471     WebInspector.shortcutsScreen.section(WebInspector.UIString("Console"));
472     WebInspector.shortcutsScreen.section(WebInspector.UIString("Elements Panel"));
473
474     this.console = new WebInspector.ConsoleModel();
475     this.console.addEventListener(WebInspector.ConsoleModel.Events.ConsoleCleared, this._updateErrorAndWarningCounts, this);
476     this.console.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, this._updateErrorAndWarningCounts, this);
477     this.console.addEventListener(WebInspector.ConsoleModel.Events.RepeatCountUpdated, this._updateErrorAndWarningCounts, this);
478
479     WebInspector.CSSCompletions.requestCSSNameCompletions();
480
481     this.drawer = new WebInspector.Drawer();
482     this.consoleView = new WebInspector.ConsoleView(WebInspector.WorkerManager.isWorkerFrontend());
483
484     this.networkManager = new WebInspector.NetworkManager();
485     this.resourceTreeModel = new WebInspector.ResourceTreeModel(this.networkManager);
486     this.debuggerModel = new WebInspector.DebuggerModel();
487     this.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
488     this.networkLog = new WebInspector.NetworkLog();
489     this.domAgent = new WebInspector.DOMAgent();
490     this.javaScriptContextManager = new WebInspector.JavaScriptContextManager(this.resourceTreeModel, this.consoleView);
491
492     this.scriptSnippetModel = new WebInspector.ScriptSnippetModel();
493
494     InspectorBackend.registerInspectorDispatcher(this);
495
496     this.cssModel = new WebInspector.CSSStyleModel();
497     this.timelineManager = new WebInspector.TimelineManager();
498     this.userAgentSupport = new WebInspector.UserAgentSupport();
499     InspectorBackend.registerDatabaseDispatcher(new WebInspector.DatabaseDispatcher());
500     InspectorBackend.registerDOMStorageDispatcher(new WebInspector.DOMStorageDispatcher());
501
502     this.searchController = new WebInspector.SearchController();
503     this.advancedSearchController = new WebInspector.AdvancedSearchController();
504     this.settingsController = new WebInspector.SettingsController();
505
506     this.domBreakpointsSidebarPane = new WebInspector.DOMBreakpointsSidebarPane();
507
508     this._zoomLevel = WebInspector.settings.zoomLevel.get();
509     if (this._zoomLevel)
510         this._requestZoom();
511
512     var autoselectPanel = WebInspector.UIString("a panel chosen automatically");
513     var openAnchorLocationSetting = WebInspector.settings.createSetting("openLinkHandler", autoselectPanel);
514     this.openAnchorLocationRegistry = new WebInspector.HandlerRegistry(openAnchorLocationSetting);
515     this.openAnchorLocationRegistry.registerHandler(autoselectPanel, function() { return false; });
516
517     this.workspace = new WebInspector.Workspace();
518     this.breakpointManager = new WebInspector.BreakpointManager(WebInspector.settings.breakpoints, this.debuggerModel, this.workspace);
519
520     this._createGlobalStatusBarItems();
521
522     WebInspector._installDockToRight();
523
524     this.toolbar = new WebInspector.Toolbar();
525     this.toolbar.setCoalescingUpdate(true);
526     var panelDescriptors = this._panelDescriptors();
527     for (var i = 0; i < panelDescriptors.length; ++i)
528         WebInspector.inspectorView.addPanel(panelDescriptors[i]);
529     this.toolbar.setCoalescingUpdate(false);
530
531     this.addMainEventListeners(document);
532     WebInspector.registerLinkifierPlugin(this._profilesLinkifier.bind(this));
533
534     window.addEventListener("resize", this.windowResize.bind(this), true);
535
536     var errorWarningCount = document.getElementById("error-warning-count");
537     errorWarningCount.addEventListener("click", this.showConsole.bind(this), false);
538     this._updateErrorAndWarningCounts();
539
540     this.extensionServer.initExtensions();
541
542     this.console.enableAgent();
543
544     function showInitialPanel()
545     {
546         if (!WebInspector.inspectorView.currentPanel())
547             WebInspector.showPanel(WebInspector.settings.lastActivePanel.get());
548     }
549
550     InspectorAgent.enable(showInitialPanel);
551     DatabaseAgent.enable();
552     DOMStorageAgent.enable();
553
554     if (!Capabilities.profilerCausesRecompilation || WebInspector.settings.profilerEnabled.get())
555         ProfilerAgent.enable();
556
557     if (WebInspector.settings.showPaintRects.get())
558         PageAgent.setShowPaintRects(true);
559
560     if (WebInspector.settings.javaScriptDisabled.get())
561         PageAgent.setScriptExecutionDisabled(true);
562
563     this.domAgent._emulateTouchEventsChanged();
564
565     WebInspector.WorkerManager.loadCompleted();
566     InspectorFrontendAPI.loadCompleted();
567 }
568
569 WebInspector._installDockToRight = function()
570 {
571     // Re-use Settings infrastructure for the dock-to-right settings UI
572     WebInspector.settings.dockToRight.set(WebInspector.queryParamsObject.dockSide === "right");
573
574     if (WebInspector.settings.dockToRight.get())
575         document.body.addStyleClass("dock-to-right");
576
577     if (WebInspector.attached)
578         WebInspector._setCompactMode(!WebInspector.settings.dockToRight.get());
579
580     WebInspector.settings.dockToRight.addChangeListener(listener.bind(this));
581
582     function listener(event)
583     {
584         var value = WebInspector.settings.dockToRight.get();
585         if (value) {
586             InspectorFrontendHost.requestSetDockSide("right");
587             document.body.addStyleClass("dock-to-right");
588         } else {
589             InspectorFrontendHost.requestSetDockSide("bottom");
590             document.body.removeStyleClass("dock-to-right");
591         }
592         if (WebInspector.attached)
593             WebInspector._setCompactMode(!value);
594         else
595             WebInspector._updateDockButtonState();
596     }
597 }
598
599 var windowLoaded = function()
600 {
601     var localizedStringsURL = InspectorFrontendHost.localizedStringsURL();
602     if (localizedStringsURL) {
603         var localizedStringsScriptElement = document.createElement("script");
604         localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false);
605         localizedStringsScriptElement.type = "text/javascript";
606         localizedStringsScriptElement.src = localizedStringsURL;
607         document.head.appendChild(localizedStringsScriptElement);
608     } else
609         WebInspector.loaded();
610
611     WebInspector.attached = WebInspector.queryParamsObject.docked === "true";
612
613     window.removeEventListener("DOMContentLoaded", windowLoaded, false);
614     delete windowLoaded;
615 };
616
617 window.addEventListener("DOMContentLoaded", windowLoaded, false);
618
619 // We'd like to enforce asynchronous interaction between the inspector controller and the frontend.
620 // It is needed to prevent re-entering the backend code.
621 // Also, native dispatches do not guarantee setTimeouts to be serialized, so we
622 // enforce serialization using 'messagesToDispatch' queue. It is also important that JSC debugger
623 // tests require that each command was dispatch within individual timeout callback, so we don't batch them.
624
625 var messagesToDispatch = [];
626
627 WebInspector.dispatchQueueIsEmpty = function() {
628     return messagesToDispatch.length == 0;
629 }
630
631 WebInspector.dispatch = function(message) {
632     messagesToDispatch.push(message);
633     setTimeout(function() {
634         InspectorBackend.dispatch(messagesToDispatch.shift());
635     }, 0);
636 }
637
638 WebInspector.dispatchMessageFromBackend = function(messageObject)
639 {
640     WebInspector.dispatch(messageObject);
641 }
642
643 WebInspector.windowResize = function(event)
644 {
645     if (WebInspector.inspectorView)
646         WebInspector.inspectorView.doResize();
647     if (WebInspector.drawer)
648         WebInspector.drawer.resize();
649     if (WebInspector.toolbar)
650         WebInspector.toolbar.resize();
651     if (WebInspector.settingsController)
652         WebInspector.settingsController.resize();
653 }
654
655 WebInspector.setDockingUnavailable = function(unavailable)
656 {
657     this._isDockingUnavailable = unavailable;
658     this._updateDockButtonState();
659 }
660
661 WebInspector.close = function(event)
662 {
663     if (this._isClosing)
664         return;
665     this._isClosing = true;
666     this.notifications.dispatchEventToListeners(WebInspector.Events.InspectorClosing);
667     InspectorFrontendHost.closeWindow();
668 }
669
670 WebInspector.documentClick = function(event)
671 {
672     var anchor = event.target.enclosingNodeOrSelfWithNodeName("a");
673     if (!anchor || anchor.target === "_blank")
674         return;
675
676     // Prevent the link from navigating, since we don't do any navigation by following links normally.
677     event.consume(true);
678
679     function followLink()
680     {
681         if (WebInspector.isBeingEdited(event.target) || WebInspector._showAnchorLocation(anchor))
682             return;
683
684         const profileMatch = WebInspector.ProfileURLRegExp.exec(anchor.href);
685         if (profileMatch) {
686             WebInspector.showProfileForURL(anchor.href);
687             return;
688         }
689
690         var parsedURL = anchor.href.asParsedURL();
691         if (parsedURL && parsedURL.scheme === "webkit-link-action") {
692             if (parsedURL.host === "show-panel") {
693                 var panel = parsedURL.path.substring(1);
694                 if (WebInspector.panel(panel))
695                     WebInspector.showPanel(panel);
696             }
697             return;
698         }
699
700         InspectorFrontendHost.openInNewTab(anchor.href);
701     }
702
703     if (WebInspector.followLinkTimeout)
704         clearTimeout(WebInspector.followLinkTimeout);
705
706     if (anchor.preventFollowOnDoubleClick) {
707         // Start a timeout if this is the first click, if the timeout is canceled
708         // before it fires, then a double clicked happened or another link was clicked.
709         if (event.detail === 1)
710             WebInspector.followLinkTimeout = setTimeout(followLink, 333);
711         return;
712     }
713
714     followLink();
715 }
716
717 WebInspector.openResource = function(resourceURL, inResourcesPanel)
718 {
719     var resource = WebInspector.resourceForURL(resourceURL);
720     if (inResourcesPanel && resource)
721         WebInspector.showPanel("resources").showResource(resource);
722     else
723         InspectorFrontendHost.openInNewTab(resourceURL);
724 }
725
726 WebInspector._registerShortcuts = function()
727 {
728     var shortcut = WebInspector.KeyboardShortcut;
729     var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("All Panels"));
730     var keys = [
731         shortcut.shortcutToString("]", shortcut.Modifiers.CtrlOrMeta),
732         shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta)
733     ];
734     section.addRelatedKeys(keys, WebInspector.UIString("Go to the panel to the left/right"));
735
736     var keys = [
737         shortcut.shortcutToString("[", shortcut.Modifiers.CtrlOrMeta | shortcut.Modifiers.Alt),
738         shortcut.shortcutToString("]", shortcut.Modifiers.CtrlOrMeta | shortcut.Modifiers.Alt)
739     ];
740     section.addRelatedKeys(keys, WebInspector.UIString("Go back/forward in panel history"));
741
742     section.addKey(shortcut.shortcutToString(shortcut.Keys.Esc), WebInspector.UIString("Toggle console"));
743     section.addKey(shortcut.shortcutToString("f", shortcut.Modifiers.CtrlOrMeta), WebInspector.UIString("Search"));
744
745     var advancedSearchShortcut = WebInspector.AdvancedSearchController.createShortcut();
746     section.addKey(advancedSearchShortcut.name, WebInspector.UIString("Search across all sources"));
747
748     var openResourceShortcut = WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta);
749     section.addKey(openResourceShortcut.name, WebInspector.UIString("Go to source"));
750
751     if (WebInspector.isMac()) {
752         keys = [
753             shortcut.shortcutToString("g", shortcut.Modifiers.Meta),
754             shortcut.shortcutToString("g", shortcut.Modifiers.Meta | shortcut.Modifiers.Shift)
755         ];
756         section.addRelatedKeys(keys, WebInspector.UIString("Find next/previous"));
757     }
758
759     var goToShortcut = WebInspector.GoToLineDialog.createShortcut();
760     section.addKey(goToShortcut.name, WebInspector.UIString("Go to line"));
761 }
762
763 WebInspector.documentKeyDown = function(event)
764 {
765     const helpKey = WebInspector.isMac() ? "U+003F" : "U+00BF"; // "?" for both platforms
766
767     if (event.keyIdentifier === "F1" ||
768         (event.keyIdentifier === helpKey && event.shiftKey && (!WebInspector.isBeingEdited(event.target) || event.metaKey))) {
769         this.settingsController.showSettingsScreen(WebInspector.SettingsScreen.Tabs.Shortcuts);
770         event.consume(true);
771         return;
772     }
773
774     if (WebInspector.currentFocusElement() && WebInspector.currentFocusElement().handleKeyEvent) {
775         WebInspector.currentFocusElement().handleKeyEvent(event);
776         if (event.handled) {
777             event.consume(true);
778             return;
779         }
780     }
781
782     if (WebInspector.inspectorView.currentPanel()) {
783         WebInspector.inspectorView.currentPanel().handleShortcut(event);
784         if (event.handled) {
785             event.consume(true);
786             return;
787         }
788     }
789
790     if (WebInspector.searchController.handleShortcut(event))
791         return;
792     if (WebInspector.advancedSearchController.handleShortcut(event))
793         return;
794
795     switch (event.keyIdentifier) {
796         case "U+004F": // O key
797             if (!event.shiftKey && !event.altKey && WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
798                 WebInspector.showPanel("scripts").showGoToSourceDialog();
799                 event.consume(true);
800             }
801             break;
802         case "U+0052": // R key
803             if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
804                 PageAgent.reload(event.shiftKey);
805                 event.consume(true);
806             }
807             break;
808         case "F5":
809             if (!WebInspector.isMac()) {
810                 PageAgent.reload(event.ctrlKey || event.shiftKey);
811                 event.consume(true);
812             }
813             break;
814     }
815
816     var isValidZoomShortcut = WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) &&
817         !event.altKey &&
818         !InspectorFrontendHost.isStub;
819     switch (event.keyCode) {
820         case 107: // +
821         case 187: // +
822             if (isValidZoomShortcut) {
823                 WebInspector._zoomIn();
824                 event.consume(true);
825             }
826             break;
827         case 109: // -
828         case 189: // -
829             if (isValidZoomShortcut) {
830                 WebInspector._zoomOut();
831                 event.consume(true);
832             }
833             break;
834         case 48: // 0
835             // Zoom reset shortcut does not allow "Shift" when handled by the browser.
836             if (isValidZoomShortcut && !event.shiftKey) {
837                 WebInspector._resetZoom();
838                 event.consume(true);
839             }
840             break;
841     }
842
843     // Cmd/Control + Shift + C should be a shortcut to clicking the Node Search Button.
844     // This shortcut matches Firebug.
845     if (event.keyIdentifier === "U+0043") { // C key
846         if (WebInspector.isMac())
847             var isNodeSearchKey = event.metaKey && !event.ctrlKey && !event.altKey && event.shiftKey;
848         else
849             var isNodeSearchKey = event.ctrlKey && !event.metaKey && !event.altKey && event.shiftKey;
850
851         if (isNodeSearchKey) {
852             this._toggleSearchingForNode();
853             event.consume(true);
854             return;
855         }
856         return;
857     }
858 }
859
860 WebInspector.postDocumentKeyDown = function(event)
861 {
862     if (event.handled)
863         return;
864
865     if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
866         // If drawer is open with some view other than console then close it.
867         if (!this._toggleConsoleButton.toggled && WebInspector.drawer.visible)
868             this.closeViewInDrawer();
869         else
870             this._toggleConsoleButtonClicked();
871     }
872 }
873
874 WebInspector.documentCanCopy = function(event)
875 {
876     if (WebInspector.inspectorView.currentPanel() && WebInspector.inspectorView.currentPanel().handleCopyEvent)
877         event.preventDefault();
878 }
879
880 WebInspector.documentCopy = function(event)
881 {
882     if (WebInspector.inspectorView.currentPanel() && WebInspector.inspectorView.currentPanel().handleCopyEvent)
883         WebInspector.inspectorView.currentPanel().handleCopyEvent(event);
884     WebInspector.documentCopyEventFired(event);
885 }
886
887 WebInspector.documentCopyEventFired = function(event)
888 {
889 }
890
891 WebInspector.contextMenuEventFired = function(event)
892 {
893     if (event.handled || event.target.hasStyleClass("popup-glasspane"))
894         event.preventDefault();
895 }
896
897 WebInspector.showConsole = function()
898 {
899     if (WebInspector._toggleConsoleButton && !WebInspector._toggleConsoleButton.toggled) {
900         if (WebInspector.drawer.visible)
901             this._closePreviousDrawerView();
902         WebInspector._toggleConsoleButtonClicked();
903     }
904 }
905
906 WebInspector.showPanel = function(panel)
907 {
908     return WebInspector.inspectorView.showPanel(panel);
909 }
910
911 WebInspector.panel = function(panel)
912 {
913     return WebInspector.inspectorView.panel(panel);
914 }
915
916 WebInspector.bringToFront = function()
917 {
918     InspectorFrontendHost.bringToFront();
919 }
920
921 /**
922  * @param {string=} messageLevel
923  * @param {boolean=} showConsole
924  */
925 WebInspector.log = function(message, messageLevel, showConsole)
926 {
927     // remember 'this' for setInterval() callback
928     var self = this;
929
930     // return indication if we can actually log a message
931     function isLogAvailable()
932     {
933         return WebInspector.ConsoleMessage && WebInspector.RemoteObject && self.console;
934     }
935
936     // flush the queue of pending messages
937     function flushQueue()
938     {
939         var queued = WebInspector.log.queued;
940         if (!queued)
941             return;
942
943         for (var i = 0; i < queued.length; ++i)
944             logMessage(queued[i]);
945
946         delete WebInspector.log.queued;
947     }
948
949     // flush the queue if it console is available
950     // - this function is run on an interval
951     function flushQueueIfAvailable()
952     {
953         if (!isLogAvailable())
954             return;
955
956         clearInterval(WebInspector.log.interval);
957         delete WebInspector.log.interval;
958
959         flushQueue();
960     }
961
962     // actually log the message
963     function logMessage(message)
964     {
965         // post the message
966         var msg = WebInspector.ConsoleMessage.create(
967             WebInspector.ConsoleMessage.MessageSource.Other,
968             messageLevel || WebInspector.ConsoleMessage.MessageLevel.Debug,
969             message);
970
971         self.console.addMessage(msg);
972         if (showConsole)
973             WebInspector.showConsole();
974     }
975
976     // if we can't log the message, queue it
977     if (!isLogAvailable()) {
978         if (!WebInspector.log.queued)
979             WebInspector.log.queued = [];
980
981         WebInspector.log.queued.push(message);
982
983         if (!WebInspector.log.interval)
984             WebInspector.log.interval = setInterval(flushQueueIfAvailable, 1000);
985
986         return;
987     }
988
989     // flush the pending queue if any
990     flushQueue();
991
992     // log the message
993     logMessage(message);
994 }
995
996 WebInspector.inspect = function(payload, hints)
997 {
998     var object = WebInspector.RemoteObject.fromPayload(payload);
999     if (object.subtype === "node") {
1000         function callback(nodeId)
1001         {
1002             WebInspector._updateFocusedNode(nodeId);
1003             object.release();
1004         }
1005         object.pushNodeToFrontend(callback);
1006         return;
1007     }
1008
1009     if (hints.databaseId)
1010         WebInspector.showPanel("resources").selectDatabase(hints.databaseId);
1011     else if (hints.domStorageId)
1012         WebInspector.showPanel("resources").selectDOMStorage(hints.domStorageId);
1013
1014     object.release();
1015 }
1016
1017 WebInspector._updateFocusedNode = function(nodeId)
1018 {
1019     if (WebInspector._nodeSearchButton.toggled) {
1020         InspectorFrontendHost.bringToFront();
1021         WebInspector._nodeSearchButton.toggled = false;
1022     }
1023     WebInspector.showPanel("elements").revealAndSelectNode(nodeId);
1024 }
1025
1026 WebInspector._showAnchorLocation = function(anchor)
1027 {
1028     if (WebInspector.openAnchorLocationRegistry.dispatch({ url: anchor.href, lineNumber: anchor.lineNumber}))
1029         return true;
1030     var preferredPanel = this.panels[anchor.preferredPanel];
1031     if (preferredPanel && WebInspector._showAnchorLocationInPanel(anchor, preferredPanel))
1032         return true;
1033     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("scripts")))
1034         return true;
1035     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("resources")))
1036         return true;
1037     if (WebInspector._showAnchorLocationInPanel(anchor, this.panel("network")))
1038         return true;
1039     return false;
1040 }
1041
1042 WebInspector._showAnchorLocationInPanel = function(anchor, panel)
1043 {
1044     if (!panel || !panel.canShowAnchorLocation(anchor))
1045         return false;
1046
1047     // FIXME: support webkit-html-external-link links here.
1048     if (anchor.hasStyleClass("webkit-html-external-link")) {
1049         anchor.removeStyleClass("webkit-html-external-link");
1050         anchor.addStyleClass("webkit-html-resource-link");
1051     }
1052
1053     this.showPanelForAnchorNavigation(panel);
1054     panel.showAnchorLocation(anchor);
1055     return true;
1056 }
1057
1058 WebInspector.showPanelForAnchorNavigation = function(panel)
1059 {
1060     WebInspector.searchController.disableSearchUntilExplicitAction();
1061     WebInspector.inspectorView.setCurrentPanel(panel);
1062 }
1063
1064 WebInspector.showProfileForURL = function(url)
1065 {
1066     WebInspector.showPanel("profiles").showProfileForURL(url);
1067 }
1068
1069 WebInspector.evaluateInConsole = function(expression, showResultOnly)
1070 {
1071     this.showConsole();
1072     this.consoleView.evaluateUsingTextPrompt(expression, showResultOnly);
1073 }
1074
1075 WebInspector.addMainEventListeners = function(doc)
1076 {
1077     doc.addEventListener("keydown", this.documentKeyDown.bind(this), true);
1078     doc.addEventListener("keydown", this.postDocumentKeyDown.bind(this), false);
1079     doc.addEventListener("beforecopy", this.documentCanCopy.bind(this), true);
1080     doc.addEventListener("copy", this.documentCopy.bind(this), true);
1081     doc.addEventListener("contextmenu", this.contextMenuEventFired.bind(this), true);
1082     doc.addEventListener("click", this.documentClick.bind(this), true);
1083 }
1084
1085 WebInspector.frontendReused = function()
1086 {
1087     this.resourceTreeModel.frontendReused();
1088 }
1089
1090 WebInspector.ProfileURLRegExp = /webkit-profile:\/\/(.+)\/(.+)#([0-9]+)/;