Web Inspector: Zoom never goes back to 100% size
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Base / Main.js
1 /*
2  * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.ContentViewCookieType = {
27     ApplicationCache: "application-cache",
28     CookieStorage: "cookie-storage",
29     Database: "database",
30     DatabaseTable: "database-table",
31     DOMStorage: "dom-storage",
32     Resource: "resource", // includes Frame too.
33     Timelines: "timelines"
34 };
35
36 WebInspector.DebuggableType = {
37     Web: "web",
38     JavaScript: "javascript"
39 };
40
41 WebInspector.SelectedSidebarPanelCookieKey = "selected-sidebar-panel";
42 WebInspector.TypeIdentifierCookieKey = "represented-object-type";
43
44 WebInspector.StateRestorationType = {
45     Load: "state-restoration-load",
46     Navigation: "state-restoration-navigation",
47     Delayed: "state-restoration-delayed",
48 };
49
50 WebInspector.loaded = function()
51 {
52     // Initialize WebSocket to communication.
53     this._initializeWebSocketIfNeeded();
54
55     this.debuggableType = InspectorFrontendHost.debuggableType() === "web" ? WebInspector.DebuggableType.Web : WebInspector.DebuggableType.JavaScript;
56     this.hasExtraDomains = false;
57
58     // Register observers for events from the InspectorBackend.
59     if (InspectorBackend.registerInspectorDispatcher)
60         InspectorBackend.registerInspectorDispatcher(new WebInspector.InspectorObserver);
61     if (InspectorBackend.registerPageDispatcher)
62         InspectorBackend.registerPageDispatcher(new WebInspector.PageObserver);
63     if (InspectorBackend.registerConsoleDispatcher)
64         InspectorBackend.registerConsoleDispatcher(new WebInspector.ConsoleObserver);
65     if (InspectorBackend.registerNetworkDispatcher)
66         InspectorBackend.registerNetworkDispatcher(new WebInspector.NetworkObserver);
67     if (InspectorBackend.registerDOMDispatcher)
68         InspectorBackend.registerDOMDispatcher(new WebInspector.DOMObserver);
69     if (InspectorBackend.registerDebuggerDispatcher)
70         InspectorBackend.registerDebuggerDispatcher(new WebInspector.DebuggerObserver);
71     if (InspectorBackend.registerHeapDispatcher)
72         InspectorBackend.registerHeapDispatcher(new WebInspector.HeapObserver);
73     if (InspectorBackend.registerDatabaseDispatcher)
74         InspectorBackend.registerDatabaseDispatcher(new WebInspector.DatabaseObserver);
75     if (InspectorBackend.registerDOMStorageDispatcher)
76         InspectorBackend.registerDOMStorageDispatcher(new WebInspector.DOMStorageObserver);
77     if (InspectorBackend.registerApplicationCacheDispatcher)
78         InspectorBackend.registerApplicationCacheDispatcher(new WebInspector.ApplicationCacheObserver);
79     if (InspectorBackend.registerTimelineDispatcher)
80         InspectorBackend.registerTimelineDispatcher(new WebInspector.TimelineObserver);
81     if (InspectorBackend.registerCSSDispatcher)
82         InspectorBackend.registerCSSDispatcher(new WebInspector.CSSObserver);
83     if (InspectorBackend.registerLayerTreeDispatcher)
84         InspectorBackend.registerLayerTreeDispatcher(new WebInspector.LayerTreeObserver);
85     if (InspectorBackend.registerRuntimeDispatcher)
86         InspectorBackend.registerRuntimeDispatcher(new WebInspector.RuntimeObserver);
87     if (InspectorBackend.registerReplayDispatcher)
88         InspectorBackend.registerReplayDispatcher(new WebInspector.ReplayObserver);
89
90     // Enable agents.
91     if (window.InspectorAgent)
92         InspectorAgent.enable();
93
94     // Perform one-time tasks.
95     WebInspector.CSSCompletions.requestCSSCompletions();
96
97     // Listen for the ProvisionalLoadStarted event before registering for events so our code gets called before any managers or sidebars.
98     // This lets us save a state cookie before any managers or sidebars do any resets that would affect state (namely TimelineManager).
99     WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ProvisionalLoadStarted, this._provisionalLoadStarted, this);
100
101     // Create the singleton managers next, before the user interface elements, so the user interface can register
102     // as event listeners on these managers.
103     this.branchManager = new WebInspector.BranchManager;
104     this.frameResourceManager = new WebInspector.FrameResourceManager;
105     this.storageManager = new WebInspector.StorageManager;
106     this.domTreeManager = new WebInspector.DOMTreeManager;
107     this.cssStyleManager = new WebInspector.CSSStyleManager;
108     this.logManager = new WebInspector.LogManager;
109     this.issueManager = new WebInspector.IssueManager;
110     this.analyzerManager = new WebInspector.AnalyzerManager;
111     this.runtimeManager = new WebInspector.RuntimeManager;
112     this.heapManager = new WebInspector.HeapManager;
113     this.applicationCacheManager = new WebInspector.ApplicationCacheManager;
114     this.timelineManager = new WebInspector.TimelineManager;
115     this.debuggerManager = new WebInspector.DebuggerManager;
116     this.sourceMapManager = new WebInspector.SourceMapManager;
117     this.layerTreeManager = new WebInspector.LayerTreeManager;
118     this.dashboardManager = new WebInspector.DashboardManager;
119     this.probeManager = new WebInspector.ProbeManager;
120     this.replayManager = new WebInspector.ReplayManager;
121
122     // Enable the Console Agent after creating the singleton managers.
123     if (window.ConsoleAgent)
124         ConsoleAgent.enable();
125
126     // Tell the backend we are initialized after all our initialization messages have been sent.
127     setTimeout(function() {
128         // COMPATIBILITY (iOS 8): Inspector.initialized did not exist yet.
129         if (window.InspectorAgent && InspectorAgent.initialized)
130             InspectorAgent.initialized();
131     }, 0);
132
133     // Register for events.
134     this.replayManager.addEventListener(WebInspector.ReplayManager.Event.CaptureStarted, this._captureDidStart, this);
135     this.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
136     this.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
137     this.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.InspectModeStateChanged, this._inspectModeStateChanged, this);
138     this.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.DOMNodeWasInspected, this._domNodeWasInspected, this);
139     this.storageManager.addEventListener(WebInspector.StorageManager.Event.DOMStorageObjectWasInspected, this._storageWasInspected, this);
140     this.storageManager.addEventListener(WebInspector.StorageManager.Event.DatabaseWasInspected, this._storageWasInspected, this);
141     this.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.MainFrameDidChange, this._mainFrameDidChange, this);
142     this.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.FrameWasAdded, this._frameWasAdded, this);
143
144     WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
145
146     document.addEventListener("DOMContentLoaded", this.contentLoaded.bind(this));
147
148     // Create settings.
149     this._showingSplitConsoleSetting = new WebInspector.Setting("showing-split-console", false);
150     this._splitConsoleHeightSetting = new WebInspector.Setting("split-console-height", 150);
151
152     this._openTabsSetting = new WebInspector.Setting("open-tab-types", ["elements", "network", "resources", "timeline", "debugger", "storage", "console"]);
153     this._selectedTabIndexSetting = new WebInspector.Setting("selected-tab-index", 0);
154
155     this.showShadowDOMSetting = new WebInspector.Setting("show-shadow-dom", false);
156     this.showReplayInterfaceSetting = new WebInspector.Setting("show-web-replay", false);
157
158     // COMPATIBILITY (iOS 8): Page.enableTypeProfiler did not exist.
159     this.showJavaScriptTypeInformationSetting = new WebInspector.Setting("show-javascript-type-information", false);
160     if (this.showJavaScriptTypeInformationSetting.value && window.RuntimeAgent && RuntimeAgent.enableTypeProfiler)
161         RuntimeAgent.enableTypeProfiler();
162
163     // COMPATIBILITY (iOS 8): Page.setShowPaintRects did not exist.
164     this.showPaintRectsSetting = new WebInspector.Setting("show-paint-rects", false);
165     if (this.showPaintRectsSetting.value && window.PageAgent && PageAgent.setShowPaintRects)
166         PageAgent.setShowPaintRects(true);
167
168     this.mouseCoords = {
169         x: 0,
170         y: 0
171     };
172
173     this._windowKeydownListeners = [];
174 };
175
176 WebInspector.contentLoaded = function()
177 {
178     // Register for global events.
179     document.addEventListener("beforecopy", this._beforecopy.bind(this));
180     document.addEventListener("copy", this._copy.bind(this));
181
182     document.addEventListener("click", this._mouseWasClicked.bind(this));
183     document.addEventListener("dragover", this._dragOver.bind(this));
184     document.addEventListener("focus", WebInspector._focusChanged.bind(this), true);
185
186     window.addEventListener("focus", this._windowFocused.bind(this));
187     window.addEventListener("blur", this._windowBlurred.bind(this));
188     window.addEventListener("resize", this._windowResized.bind(this));
189     window.addEventListener("keydown", this._windowKeyDown.bind(this));
190     window.addEventListener("keyup", this._windowKeyUp.bind(this));
191     window.addEventListener("mousemove", this._mouseMoved.bind(this), true);
192     window.addEventListener("pagehide", this._pageHidden.bind(this));
193
194     // Add platform style classes so the UI can be tweaked per-platform.
195     document.body.classList.add(WebInspector.Platform.name + "-platform");
196     if (WebInspector.Platform.isNightlyBuild)
197         document.body.classList.add("nightly-build");
198
199     if (WebInspector.Platform.name === "mac") {
200         document.body.classList.add(WebInspector.Platform.version.name);
201
202         if (WebInspector.Platform.version.release >= 11)
203             document.body.classList.add("latest-mac");
204         else
205             document.body.classList.add("legacy-mac");
206     }
207
208     document.body.classList.add(this.debuggableType);
209
210     // Create the user interface elements.
211     this.toolbar = new WebInspector.Toolbar(document.getElementById("toolbar"), null, true);
212     this.toolbar.displayMode = WebInspector.Toolbar.DisplayMode.IconOnly;
213     this.toolbar.sizeMode = WebInspector.Toolbar.SizeMode.Small;
214
215     this.tabBar = new WebInspector.TabBar(document.getElementById("tab-bar"));
216     this.tabBar.addEventListener(WebInspector.TabBar.Event.NewTabItemClicked, this._newTabItemClicked, this);
217     this.tabBar.addEventListener(WebInspector.TabBar.Event.OpenDefaultTab, this._openDefaultTab, this);
218
219     var contentElement = document.getElementById("content");
220     contentElement.setAttribute("role", "main");
221     contentElement.setAttribute("aria-label", WebInspector.UIString("Content"));
222
223     this.splitContentBrowser = new WebInspector.ContentBrowser(document.getElementById("split-content-browser"), this, true);
224     this.splitContentBrowser.navigationBar.element.addEventListener("mousedown", this._consoleResizerMouseDown.bind(this));
225
226     this.quickConsole = new WebInspector.QuickConsole(document.getElementById("quick-console"));
227     this.quickConsole.addEventListener(WebInspector.QuickConsole.Event.DidResize, this._quickConsoleDidResize, this);
228
229     this._consoleRepresentedObject = new WebInspector.LogObject;
230     this._consoleTreeElement = new WebInspector.LogTreeElement(this._consoleRepresentedObject);
231     this.consoleContentView = WebInspector.splitContentBrowser.contentViewForRepresentedObject(this._consoleRepresentedObject);
232     this.consoleLogViewController = this.consoleContentView.logViewController;
233     this.breakpointPopoverController = new WebInspector.BreakpointPopoverController;
234
235     // FIXME: The sidebars should be flipped in RTL languages.
236     this.navigationSidebar = new WebInspector.Sidebar(document.getElementById("navigation-sidebar"), WebInspector.Sidebar.Sides.Left);
237     this.navigationSidebar.addEventListener(WebInspector.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
238
239     this.detailsSidebar = new WebInspector.Sidebar(document.getElementById("details-sidebar"), WebInspector.Sidebar.Sides.Right, null, null, WebInspector.UIString("Details"), true);
240     this.detailsSidebar.addEventListener(WebInspector.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
241
242     this.searchKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "F", this._focusSearchField.bind(this));
243     this._findKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "F", this._find.bind(this));
244     this._saveKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "S", this._save.bind(this));
245     this._saveAsKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Shift | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "S", this._saveAs.bind(this));
246
247     this.navigationSidebarKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "0", this.toggleNavigationSidebar.bind(this));
248     this.detailsSidebarKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Option, "0", this.toggleDetailsSidebar.bind(this));
249
250     this._increaseZoomKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Plus, this._increaseZoom.bind(this));
251     this._decreaseZoomKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Minus, this._decreaseZoom.bind(this));
252
253     this.tabBrowser = new WebInspector.TabBrowser(document.getElementById("tab-browser"), this.tabBar, this.navigationSidebar, this.detailsSidebar);
254     this.tabBrowser.addEventListener(WebInspector.TabBrowser.Event.SelectedTabContentViewDidChange, this._tabBrowserSelectedTabContentViewDidChange, this);
255
256     this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemAdded, this._updateNewTabButtonState, this);
257     this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemRemoved, this._updateNewTabButtonState, this);
258
259     this._reloadPageKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "R", this._reloadPage.bind(this));
260     this._reloadPageIgnoringCacheKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "R", this._reloadPageIgnoringCache.bind(this));
261
262     this._consoleTabKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Option | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "C", this._showConsoleTab.bind(this));
263     this._quickConsoleKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control, WebInspector.KeyboardShortcut.Key.Apostrophe, this._focusConsolePrompt.bind(this));
264
265     this._inspectModeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "C", this._toggleInspectMode.bind(this));
266
267     this._undoKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Z", this._undoKeyboardShortcut.bind(this));
268     this._redoKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "Z", this._redoKeyboardShortcut.bind(this));
269     this._undoKeyboardShortcut.implicitlyPreventsDefault = this._redoKeyboardShortcut.implicitlyPreventsDefault = false;
270
271     this.toggleBreakpointsKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this.debuggerToggleBreakpoints.bind(this));
272     this.pauseOrResumeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this.debuggerPauseResumeToggle.bind(this));
273     this.stepOverKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F6, this.debuggerStepOver.bind(this));
274     this.stepIntoKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F7, this.debuggerStepInto.bind(this));
275     this.stepOutKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F8, this.debuggerStepOut.bind(this));
276
277     this.pauseOrResumeAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Backslash, this.debuggerPauseResumeToggle.bind(this));
278     this.stepOverAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.SingleQuote, this.debuggerStepOver.bind(this));
279     this.stepIntoAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this.debuggerStepInto.bind(this));
280     this.stepOutAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Shift | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this.debuggerStepOut.bind(this));
281
282     this._closeToolbarButton = new WebInspector.ControlToolbarItem("dock-close", WebInspector.UIString("Close"), "Images/Close.svg", 16, 14);
283     this._closeToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this.close, this);
284
285     this._undockToolbarButton = new WebInspector.ButtonToolbarItem("undock", WebInspector.UIString("Detach into separate window"), null, "Images/Undock.svg");
286     this._undockToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._undock, this);
287
288     this._dockRightToolbarButton = new WebInspector.ButtonToolbarItem("dock-right", WebInspector.UIString("Dock to right of window"), null, "Images/DockRight.svg");
289     this._dockRightToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._dockRight, this);
290
291     this._dockBottomToolbarButton = new WebInspector.ButtonToolbarItem("dock-bottom", WebInspector.UIString("Dock to bottom of window"), null, "Images/DockBottom.svg");
292     this._dockBottomToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._dockBottom, this);
293
294     var toolTip;
295     if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript)
296         toolTip = WebInspector.UIString("Restart (%s)").format(this._reloadPageKeyboardShortcut.displayName);
297     else
298         toolTip = WebInspector.UIString("Reload page (%s)\nReload ignoring cache (%s)").format(this._reloadPageKeyboardShortcut.displayName, this._reloadPageIgnoringCacheKeyboardShortcut.displayName);
299
300     this._reloadToolbarButton = new WebInspector.ButtonToolbarItem("reload", toolTip, null, "Images/ReloadToolbar.svg");
301     this._reloadToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._reloadPageClicked, this);
302
303     this._downloadToolbarButton = new WebInspector.ButtonToolbarItem("download", WebInspector.UIString("Download Web Archive"), null, "Images/DownloadArrow.svg");
304     this._downloadToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._downloadWebArchive, this);
305
306     this._updateReloadToolbarButton();
307     this._updateDownloadToolbarButton();
308
309     // The toolbar button for node inspection.
310     if (this.debuggableType === WebInspector.DebuggableType.Web) {
311         var toolTip = WebInspector.UIString("Enable point to inspect mode (%s)").format(WebInspector._inspectModeKeyboardShortcut.displayName);
312         var activatedToolTip = WebInspector.UIString("Disable point to inspect mode (%s)").format(WebInspector._inspectModeKeyboardShortcut.displayName);
313         this._inspectModeToolbarButton = new WebInspector.ActivateButtonToolbarItem("inspect", toolTip, activatedToolTip, null, "Images/Crosshair.svg");
314         this._inspectModeToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._toggleInspectMode, this);
315     }
316
317     this._dashboardContainer = new WebInspector.DashboardContainerView;
318     this._dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.default);
319
320     this._searchToolbarItem = new WebInspector.SearchBar("inspector-search", WebInspector.UIString("Search"), null, true);
321     this._searchToolbarItem.addEventListener(WebInspector.SearchBar.Event.TextChanged, this._searchTextDidChange, this);
322
323     this.toolbar.addToolbarItem(this._closeToolbarButton, WebInspector.Toolbar.Section.Control);
324
325     this.toolbar.addToolbarItem(this._undockToolbarButton, WebInspector.Toolbar.Section.Left);
326     this.toolbar.addToolbarItem(this._dockRightToolbarButton, WebInspector.Toolbar.Section.Left);
327     this.toolbar.addToolbarItem(this._dockBottomToolbarButton, WebInspector.Toolbar.Section.Left);
328
329     this.toolbar.addToolbarItem(this._reloadToolbarButton, WebInspector.Toolbar.Section.CenterLeft);
330     this.toolbar.addToolbarItem(this._downloadToolbarButton, WebInspector.Toolbar.Section.CenterLeft);
331
332     this.toolbar.addToolbarItem(this._dashboardContainer.toolbarItem, WebInspector.Toolbar.Section.Center);
333
334     if (this._inspectModeToolbarButton)
335         this.toolbar.addToolbarItem(this._inspectModeToolbarButton, WebInspector.Toolbar.Section.CenterRight);
336
337     this.toolbar.addToolbarItem(this._searchToolbarItem, WebInspector.Toolbar.Section.Right);
338
339     this.resourceDetailsSidebarPanel = new WebInspector.ResourceDetailsSidebarPanel;
340     this.domNodeDetailsSidebarPanel = new WebInspector.DOMNodeDetailsSidebarPanel;
341     this.cssStyleDetailsSidebarPanel = new WebInspector.CSSStyleDetailsSidebarPanel;
342     this.applicationCacheDetailsSidebarPanel = new WebInspector.ApplicationCacheDetailsSidebarPanel;
343     this.scopeChainDetailsSidebarPanel = new WebInspector.ScopeChainDetailsSidebarPanel;
344     this.probeDetailsSidebarPanel = new WebInspector.ProbeDetailsSidebarPanel;
345
346     if (window.LayerTreeAgent)
347         this.layerTreeDetailsSidebarPanel = new WebInspector.LayerTreeDetailsSidebarPanel;
348
349     this.modifierKeys = {altKey: false, metaKey: false, shiftKey: false};
350
351     this.toolbar.element.addEventListener("mousedown", this._toolbarMouseDown.bind(this));
352     document.getElementById("docked-resizer").addEventListener("mousedown", this._dockedResizerMouseDown.bind(this));
353
354     this._dockingAvailable = false;
355
356     this._updateDockNavigationItems();
357     this._updateToolbarHeight();
358
359     this._pendingOpenTabs = [];
360
361     let openTabTypes = this._openTabsSetting.value;
362
363     for (let i = 0; i < openTabTypes.length; ++i) {
364         let tabType = openTabTypes[i];
365         if (!this.isTabTypeAllowed(tabType)) {
366             this._pendingOpenTabs.push({tabType, index: i});
367             continue;
368         }
369
370         let tabContentView = this._tabContentViewForType(tabType);
371         if (!tabContentView)
372             continue;
373         this.tabBrowser.addTabForContentView(tabContentView, true);
374     }
375
376     this._restoreCookieForOpenTabs(WebInspector.StateRestorationType.Load);
377
378     this.tabBar.selectedTabBarItem = this._selectedTabIndexSetting.value;
379
380     if (!this.tabBar.selectedTabBarItem)
381         this.tabBar.selectedTabBarItem = 0;
382
383     if (!this.tabBar.hasNormalTab())
384         this.showNewTabTab();
385
386     // Listen to the events after restoring the saved tabs to avoid recursion.
387     this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemAdded, this._rememberOpenTabs, this);
388     this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemRemoved, this._rememberOpenTabs, this);
389     this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemsReordered, this._rememberOpenTabs, this);
390
391     // Signal that the frontend is now ready to receive messages.
392     InspectorFrontendAPI.loadCompleted();
393
394     // Tell the InspectorFrontendHost we loaded, which causes the window to display
395     // and pending InspectorFrontendAPI commands to be sent.
396     InspectorFrontendHost.loaded();
397
398     this._updateSplitConsoleHeight(this._splitConsoleHeightSetting.value);
399
400     if (this._showingSplitConsoleSetting.value)
401         this.showSplitConsole();
402
403     this._contentLoaded = true;
404
405     this.runBootstrapOperations();
406 };
407
408 WebInspector.isTabTypeAllowed = function(tabType)
409 {
410     switch (tabType) {
411     case WebInspector.ElementsTabContentView.Type:
412         return !!window.DOMAgent;
413     case WebInspector.NetworkTabContentView.Type:
414         return !!window.NetworkAgent && !!window.PageAgent;
415     case WebInspector.StorageTabContentView.Type:
416         return !!window.DOMStorageAgent || !!window.DatabaseAgent || !!window.IndexedDBAgent;
417     case WebInspector.TimelineTabContentView.Type:
418         return !!window.TimelineAgent;
419     }
420
421     return true;
422 };
423
424 WebInspector._tabContentViewForType = function(tabType)
425 {
426     switch (tabType) {
427     case WebInspector.ConsoleTabContentView.Type:
428         return new WebInspector.ConsoleTabContentView;
429     case WebInspector.DebuggerTabContentView.Type:
430         return new WebInspector.DebuggerTabContentView;
431     case WebInspector.ElementsTabContentView.Type:
432         return new WebInspector.ElementsTabContentView;
433     case WebInspector.NetworkTabContentView.Type:
434         return new WebInspector.NetworkTabContentView;
435     case WebInspector.NewTabContentView.Type:
436         return new WebInspector.NewTabContentView;
437     case WebInspector.ResourcesTabContentView.Type:
438         return new WebInspector.ResourcesTabContentView;
439     case WebInspector.SearchTabContentView.Type:
440         return new WebInspector.SearchTabContentView;
441     case WebInspector.StorageTabContentView.Type:
442         return new WebInspector.StorageTabContentView;
443     case WebInspector.TimelineTabContentView.Type:
444         return new WebInspector.TimelineTabContentView;
445     default:
446         console.error("Unknown tab type", tabType);
447     }
448
449     return null;
450 };
451
452 WebInspector._rememberOpenTabs = function()
453 {
454     var openTabs = [];
455
456     for (var tabBarItem of this.tabBar.tabBarItems) {
457         var tabContentView = tabBarItem.representedObject;
458         if (!(tabContentView instanceof WebInspector.TabContentView))
459             continue;
460         if (tabContentView instanceof WebInspector.SettingsTabContentView || tabContentView instanceof WebInspector.NewTabContentView)
461             continue;
462         console.assert(tabContentView.type, "Tab type can't be null, undefined, or empty string", tabContentView.type, tabContentView);
463         openTabs.push(tabContentView.type);
464     }
465
466     // Keep currently unsupported tabs in the setting at their previous index.
467     for (let {tabType, index} of this._pendingOpenTabs)
468         openTabs.insertAtIndex(tabType, index);
469
470     this._openTabsSetting.value = openTabs;
471 };
472
473 WebInspector._updateNewTabButtonState = function(event)
474 {
475     var newTabAllowed = this.isNewTabWithTypeAllowed(WebInspector.ConsoleTabContentView.Type) || this.isNewTabWithTypeAllowed(WebInspector.ElementsTabContentView.Type)
476         || this.isNewTabWithTypeAllowed(WebInspector.ResourcesTabContentView.Type) || this.isNewTabWithTypeAllowed(WebInspector.StorageTabContentView.Type)
477         || this.isNewTabWithTypeAllowed(WebInspector.TimelineTabContentView.Type) || this.isNewTabWithTypeAllowed(WebInspector.DebuggerTabContentView.Type)
478         || this.isNewTabWithTypeAllowed(WebInspector.NetworkTabContentView.Type);
479     this.tabBar.newTabItem.disabled = !newTabAllowed;
480 };
481
482 WebInspector._newTabItemClicked = function(event)
483 {
484     const shouldAnimate = true;
485     this.showNewTabTab(shouldAnimate);
486 };
487
488 WebInspector._openDefaultTab = function(event)
489 {
490     this.showNewTabTab();
491 };
492
493 WebInspector.showNewTabTab = function(shouldAnimate)
494 {
495     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.NewTabContentView);
496     if (!tabContentView)
497         tabContentView = new WebInspector.NewTabContentView;
498     this.tabBrowser.showTabForContentView(tabContentView, !shouldAnimate);
499 };
500
501 WebInspector.isNewTabWithTypeAllowed = function(tabType)
502 {
503     if (!this.isTabTypeAllowed(tabType))
504         return false;
505
506     // Only allow one tab per class for now.
507     for (var tabBarItem of this.tabBar.tabBarItems) {
508         var tabContentView = tabBarItem.representedObject;
509         if (!(tabContentView instanceof WebInspector.TabContentView))
510             continue;
511         if (tabContentView.type === tabType)
512             return false;
513     }
514
515     return true;
516 };
517
518 WebInspector.createNewTabWithType = function(tabType, options = {})
519 {
520     console.assert(this.isNewTabWithTypeAllowed(tabType));
521
522     let {referencedView, shouldReplaceTab, shouldShowNewTab} = options;
523     console.assert(!referencedView || referencedView instanceof WebInspector.TabContentView, referencedView);
524     console.assert(!shouldReplaceTab || referencedView, "Must provide a reference view to replace a tab.");
525
526     let tabContentView = this._tabContentViewForType(tabType);
527     const suppressAnimations = true;
528     let insertionIndex = referencedView ? this.tabBar.tabBarItems.indexOf(referencedView.tabBarItem) : undefined;
529     this.tabBrowser.addTabForContentView(tabContentView, suppressAnimations, insertionIndex);
530
531     if (shouldReplaceTab)
532         this.tabBrowser.closeTabForContentView(referencedView, suppressAnimations);
533
534     if (shouldShowNewTab)
535         this.tabBrowser.showTabForContentView(tabContentView);
536 };
537
538 WebInspector.activateExtraDomains = function(domains)
539 {
540     this.hasExtraDomains = true;
541
542     for (var domain of domains) {
543         var agent = InspectorBackend.activateDomain(domain);
544         if (agent.enable)
545             agent.enable();
546     }
547
548     this.notifications.dispatchEventToListeners(WebInspector.Notification.ExtraDomainsActivated, {"domains": domains});
549
550     WebInspector.CSSCompletions.requestCSSCompletions();
551
552     this._updateReloadToolbarButton();
553     this._updateDownloadToolbarButton();
554
555     let stillPendingOpenTabs = [];
556     for (let {tabType, index} of this._pendingOpenTabs) {
557         if (!this.isTabTypeAllowed(tabType)) {
558             stillPendingOpenTabs.push({tabType, index});
559             continue;
560         }
561
562         let tabContentView = this._tabContentViewForType(tabType);
563         if (!tabContentView)
564             continue;
565
566         this.tabBrowser.addTabForContentView(tabContentView, true, index);
567
568         tabContentView.restoreStateFromCookie(WebInspector.StateRestorationType.Load);
569     }
570
571     this._pendingOpenTabs = stillPendingOpenTabs;
572
573     this._updateNewTabButtonState();
574 };
575
576 WebInspector.contentBrowserTreeElementForRepresentedObject = function(contentBrowser, representedObject)
577 {
578     // The console does not have a sidebar, so return a tree element here so something is shown.
579     if (representedObject === this._consoleRepresentedObject)
580         return this._consoleTreeElement;
581     return null;
582 };
583
584 WebInspector.updateWindowTitle = function()
585 {
586     var mainFrame = this.frameResourceManager.mainFrame;
587     console.assert(mainFrame);
588
589     var urlComponents = mainFrame.mainResource.urlComponents;
590
591     var lastPathComponent;
592     try {
593         lastPathComponent = decodeURIComponent(urlComponents.lastPathComponent || "");
594     } catch (e) {
595         lastPathComponent = urlComponents.lastPathComponent;
596     }
597
598     // Build a title based on the URL components.
599     if (urlComponents.host && lastPathComponent)
600         var title = this.displayNameForHost(urlComponents.host) + " \u2014 " + lastPathComponent;
601     else if (urlComponents.host)
602         var title = this.displayNameForHost(urlComponents.host);
603     else if (lastPathComponent)
604         var title = lastPathComponent;
605     else
606         var title = mainFrame.url;
607
608     // The name "inspectedURLChanged" sounds like the whole URL is required, however this is only
609     // used for updating the window title and it can be any string.
610     InspectorFrontendHost.inspectedURLChanged(title);
611 };
612
613 WebInspector.updateDockingAvailability = function(available)
614 {
615     this._dockingAvailable = available;
616
617     this._updateDockNavigationItems();
618 };
619
620 WebInspector.updateDockedState = function(side)
621 {
622     if (this._dockSide === side)
623         return;
624
625     this._dockSide = side;
626
627     this.docked = side !== "undocked";
628
629     this._ignoreToolbarModeDidChangeEvents = true;
630
631     if (side === "bottom") {
632         document.body.classList.add("docked", "bottom");
633         document.body.classList.remove("window-inactive", "right");
634     } else if (side === "right") {
635         document.body.classList.add("docked", "right");
636         document.body.classList.remove("window-inactive", "bottom");
637     } else
638         document.body.classList.remove("docked", "right", "bottom");
639
640     this._ignoreToolbarModeDidChangeEvents = false;
641
642     this._updateDockNavigationItems();
643 };
644
645 WebInspector.handlePossibleLinkClick = function(event, frame, alwaysOpenExternally)
646 {
647     var anchorElement = event.target.enclosingNodeOrSelfWithNodeName("a");
648     if (!anchorElement || !anchorElement.href)
649         return false;
650
651     if (WebInspector.isBeingEdited(anchorElement)) {
652         // Don't follow the link when it is being edited.
653         return false;
654     }
655
656     // Prevent the link from navigating, since we don't do any navigation by following links normally.
657     event.preventDefault();
658     event.stopPropagation();
659
660     this.openURL(anchorElement.href, frame, false, anchorElement.lineNumber);
661
662     return true;
663 };
664
665 WebInspector.openURL = function(url, frame, alwaysOpenExternally, lineNumber)
666 {
667     console.assert(url);
668     if (!url)
669         return;
670
671     console.assert(typeof lineNumber === "undefined" || typeof lineNumber === "number", "lineNumber should be a number.");
672
673     // If alwaysOpenExternally is not defined, base it off the command/meta key for the current event.
674     if (alwaysOpenExternally === undefined || alwaysOpenExternally === null)
675         alwaysOpenExternally = window.event ? window.event.metaKey : false;
676
677     if (alwaysOpenExternally) {
678         InspectorFrontendHost.openInNewTab(url);
679         return;
680     }
681
682     var searchChildFrames = false;
683     if (!frame) {
684         frame = this.frameResourceManager.mainFrame;
685         searchChildFrames = true;
686     }
687
688     console.assert(frame);
689
690     // WebInspector.Frame.resourceForURL does not check the main resource, only sub-resources. So check both.
691     let simplifiedURL = removeURLFragment(url);
692     var resource = frame.url === simplifiedURL ? frame.mainResource : frame.resourceForURL(simplifiedURL, searchChildFrames);
693     if (resource) {
694         var position = new WebInspector.SourceCodePosition(lineNumber, 0);
695         this.showSourceCode(resource, position);
696         return;
697     }
698
699     InspectorFrontendHost.openInNewTab(url);
700 };
701
702 WebInspector.close = function()
703 {
704     if (this._isClosing)
705         return;
706
707     this._isClosing = true;
708
709     InspectorFrontendHost.closeWindow();
710 };
711
712 WebInspector.saveDataToFile = function(saveData, forceSaveAs)
713 {
714     console.assert(saveData);
715     if (!saveData)
716         return;
717
718     if (typeof saveData.customSaveHandler === "function") {
719         saveData.customSaveHandler(forceSaveAs);
720         return;
721     }
722
723     console.assert(saveData.url);
724     console.assert(typeof saveData.content === "string");
725     if (!saveData.url || typeof saveData.content !== "string")
726         return;
727
728     InspectorFrontendHost.save(saveData.url, saveData.content, false, forceSaveAs || saveData.forceSaveAs);
729 };
730
731 WebInspector.isConsoleFocused = function()
732 {
733     return this.quickConsole.prompt.focused;
734 };
735
736 WebInspector.isShowingSplitConsole = function()
737 {
738     return !this.splitContentBrowser.element.classList.contains("hidden");
739 };
740
741 WebInspector.doesCurrentTabSupportSplitContentBrowser = function()
742 {
743     var currentContentView = this.tabBrowser.selectedTabContentView;
744     return !currentContentView || currentContentView.supportsSplitContentBrowser;
745 };
746
747 WebInspector.toggleSplitConsole = function()
748 {
749     if (!this.doesCurrentTabSupportSplitContentBrowser()) {
750         this.showConsoleTab();
751         return;
752     }
753
754     if (this.isShowingSplitConsole())
755         this.hideSplitConsole();
756     else
757         this.showSplitConsole();
758 };
759
760 WebInspector.showSplitConsole = function()
761 {
762     if (!this.doesCurrentTabSupportSplitContentBrowser()) {
763         this.showConsoleTab();
764         return;
765     }
766
767     this.splitContentBrowser.element.classList.remove("hidden");
768
769     this._showingSplitConsoleSetting.value = true;
770
771     if (this.splitContentBrowser.currentContentView !== this.consoleContentView) {
772         // Be sure to close the view in the tab content browser before showing it in the
773         // split content browser. We can only show a content view in one browser at a time.
774         if (this.consoleContentView.parentContainer)
775             this.consoleContentView.parentContainer.closeContentView(this.consoleContentView);
776         this.splitContentBrowser.showContentView(this.consoleContentView);
777     } else {
778         // This causes the view to know it was shown and focus the prompt.
779         this.splitContentBrowser.shown();
780     }
781
782     this.quickConsole.consoleLogVisibilityChanged(true);
783 };
784
785 WebInspector.hideSplitConsole = function()
786 {
787     if (!this.isShowingSplitConsole())
788         return;
789
790     this.splitContentBrowser.element.classList.add("hidden");
791
792     this._showingSplitConsoleSetting.value = false;
793
794     // This causes the view to know it was hidden.
795     this.splitContentBrowser.hidden();
796
797     this.quickConsole.consoleLogVisibilityChanged(false);
798 };
799
800 WebInspector.showConsoleTab = function(requestedScope)
801 {
802     this.hideSplitConsole();
803
804     var scope = requestedScope || WebInspector.LogContentView.Scopes.All;
805
806     // If the requested scope is already selected and the console is showing, then switch back to All.
807     if (this.isShowingConsoleTab() && this.consoleContentView.scopeBar.item(scope).selected)
808         scope = WebInspector.LogContentView.Scopes.All;
809
810     if (requestedScope || !this.consoleContentView.scopeBar.selectedItems.length)
811         this.consoleContentView.scopeBar.item(scope).selected = true;
812
813     this.showRepresentedObject(this._consoleRepresentedObject);
814
815     console.assert(this.isShowingConsoleTab());
816 };
817
818 WebInspector.isShowingConsoleTab = function()
819 {
820     return this.tabBrowser.selectedTabContentView instanceof WebInspector.ConsoleTabContentView;
821 };
822
823 WebInspector.showElementsTab = function()
824 {
825     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.ElementsTabContentView);
826     if (!tabContentView)
827         tabContentView = new WebInspector.ElementsTabContentView;
828     this.tabBrowser.showTabForContentView(tabContentView);
829 };
830
831 WebInspector.showDebuggerTab = function(breakpointToSelect)
832 {
833     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.DebuggerTabContentView);
834     if (!tabContentView)
835         tabContentView = new WebInspector.DebuggerTabContentView;
836
837     if (breakpointToSelect instanceof WebInspector.Breakpoint)
838         tabContentView.revealAndSelectBreakpoint(breakpointToSelect);
839
840     this.tabBrowser.showTabForContentView(tabContentView);
841 };
842
843 WebInspector.isShowingDebuggerTab = function()
844 {
845     return this.tabBrowser.selectedTabContentView instanceof WebInspector.DebuggerTabContentView;
846 };
847
848 WebInspector.showResourcesTab = function()
849 {
850     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.ResourcesTabContentView);
851     if (!tabContentView)
852         tabContentView = new WebInspector.ResourcesTabContentView;
853     this.tabBrowser.showTabForContentView(tabContentView);
854 };
855
856 WebInspector.showStorageTab = function()
857 {
858     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.StorageTabContentView);
859     if (!tabContentView)
860         tabContentView = new WebInspector.StorageTabContentView;
861     this.tabBrowser.showTabForContentView(tabContentView);
862 };
863
864 WebInspector.showNetworkTab = function()
865 {
866     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.NetworkTabContentView);
867     if (!tabContentView)
868         tabContentView = new WebInspector.NetworkTabContentView;
869     this.tabBrowser.showTabForContentView(tabContentView);
870 };
871
872 WebInspector.showTimelineTab = function()
873 {
874     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.TimelineTabContentView);
875     if (!tabContentView)
876         tabContentView = new WebInspector.TimelineTabContentView;
877     this.tabBrowser.showTabForContentView(tabContentView);
878 };
879
880 WebInspector.UIString = function(string, vararg)
881 {
882     if (WebInspector.dontLocalizeUserInterface)
883         return string;
884
885     if (window.localizedStrings && string in window.localizedStrings)
886         return window.localizedStrings[string];
887
888     if (!this._missingLocalizedStrings)
889         this._missingLocalizedStrings = {};
890
891     if (!(string in this._missingLocalizedStrings)) {
892         console.error("Localized string \"" + string + "\" was not found.");
893         this._missingLocalizedStrings[string] = true;
894     }
895
896     return "LOCALIZED STRING NOT FOUND";
897 };
898
899 WebInspector.restoreFocusFromElement = function(element)
900 {
901     if (element && element.isSelfOrAncestor(this.currentFocusElement))
902         this.previousFocusElement.focus();
903 };
904
905 WebInspector.toggleNavigationSidebar = function(event)
906 {
907     if (!this.navigationSidebar.collapsed || !this.navigationSidebar.sidebarPanels.length) {
908         this.navigationSidebar.collapsed = true;
909         return;
910     }
911
912     if (!this.navigationSidebar.selectedSidebarPanel)
913         this.navigationSidebar.selectedSidebarPanel = this.navigationSidebar.sidebarPanels[0];
914     this.navigationSidebar.collapsed = false;
915 };
916
917 WebInspector.toggleDetailsSidebar = function(event)
918 {
919     if (!this.detailsSidebar.collapsed || !this.detailsSidebar.sidebarPanels.length) {
920         this.detailsSidebar.collapsed = true;
921         return;
922     }
923
924     if (!this.detailsSidebar.selectedSidebarPanel)
925         this.detailsSidebar.selectedSidebarPanel = this.detailsSidebar.sidebarPanels[0];
926     this.detailsSidebar.collapsed = false;
927 };
928
929 WebInspector.tabContentViewClassForRepresentedObject = function(representedObject)
930 {
931     if (representedObject instanceof WebInspector.DOMTree)
932         return WebInspector.ElementsTabContentView;
933
934     if (representedObject instanceof WebInspector.TimelineRecording)
935         return WebInspector.TimelineTabContentView;
936
937     // We only support one console tab right now. So this isn't an instanceof check.
938     if (representedObject === this._consoleRepresentedObject)
939         return WebInspector.ConsoleTabContentView;
940
941     if (WebInspector.debuggerManager.paused) {
942         if (representedObject instanceof WebInspector.Script)
943             return WebInspector.DebuggerTabContentView;
944
945         if (representedObject instanceof WebInspector.Resource && (representedObject.type === WebInspector.Resource.Type.Document || representedObject.type === WebInspector.Resource.Type.Script))
946             return WebInspector.DebuggerTabContentView;
947     }
948
949     if (representedObject instanceof WebInspector.Frame || representedObject instanceof WebInspector.Resource || representedObject instanceof WebInspector.Script)
950         return WebInspector.ResourcesTabContentView;
951
952     // FIXME: Move Content Flows to the Elements tab?
953     if (representedObject instanceof WebInspector.ContentFlow)
954         return WebInspector.ResourcesTabContentView;
955
956     // FIXME: Move these to a Storage tab.
957     if (representedObject instanceof WebInspector.DOMStorageObject || representedObject instanceof WebInspector.CookieStorageObject ||
958         representedObject instanceof WebInspector.DatabaseTableObject || representedObject instanceof WebInspector.DatabaseObject ||
959         representedObject instanceof WebInspector.ApplicationCacheFrame || representedObject instanceof WebInspector.IndexedDatabaseObjectStore ||
960         representedObject instanceof WebInspector.IndexedDatabaseObjectStoreIndex)
961         return WebInspector.ResourcesTabContentView;
962
963     return null;
964 };
965
966 WebInspector.tabContentViewForRepresentedObject = function(representedObject)
967 {
968     var tabContentView = this.tabBrowser.bestTabContentViewForRepresentedObject(representedObject);
969     if (tabContentView)
970         return tabContentView;
971
972     var tabContentViewClass = this.tabContentViewClassForRepresentedObject(representedObject);
973     if (!tabContentViewClass) {
974         console.error("Unknown representedObject, couldn't create TabContentView.", representedObject);
975         return null;
976     }
977
978     tabContentView = new tabContentViewClass;
979
980     this.tabBrowser.addTabForContentView(tabContentView);
981
982     return tabContentView;
983 };
984
985 WebInspector.showRepresentedObject = function(representedObject, cookie)
986 {
987     var tabContentView = this.tabContentViewForRepresentedObject(representedObject);
988     console.assert(tabContentView);
989     if (!tabContentView)
990         return;
991
992     this.tabBrowser.showTabForContentView(tabContentView);
993     tabContentView.showRepresentedObject(representedObject, cookie);
994 };
995
996 WebInspector.showMainFrameDOMTree = function(nodeToSelect)
997 {
998     console.assert(WebInspector.frameResourceManager.mainFrame);
999     if (!WebInspector.frameResourceManager.mainFrame)
1000         return;
1001     this.showRepresentedObject(WebInspector.frameResourceManager.mainFrame.domTree, {nodeToSelect});
1002 };
1003
1004 WebInspector.showContentFlowDOMTree = function(contentFlow, nodeToSelect)
1005 {
1006     this.showRepresentedObject(contentFlow, {nodeToSelect});
1007 };
1008
1009 WebInspector.showSourceCodeForFrame = function(frameIdentifier)
1010 {
1011     var frame = WebInspector.frameResourceManager.frameForIdentifier(frameIdentifier);
1012     if (!frame) {
1013         this._frameIdentifierToShowSourceCodeWhenAvailable = frameIdentifier;
1014         return;
1015     }
1016
1017     this._frameIdentifierToShowSourceCodeWhenAvailable = undefined;
1018
1019     this.showRepresentedObject(frame);
1020 };
1021
1022 WebInspector.showSourceCode = function(sourceCode, positionToReveal, textRangeToSelect, forceUnformatted)
1023 {
1024     console.assert(!positionToReveal || positionToReveal instanceof WebInspector.SourceCodePosition, positionToReveal);
1025     var representedObject = sourceCode;
1026
1027     if (representedObject instanceof WebInspector.Script) {
1028         // A script represented by a resource should always show the resource.
1029         representedObject = representedObject.resource || representedObject;
1030     }
1031
1032     var cookie = positionToReveal ? {lineNumber: positionToReveal.lineNumber, columnNumber: positionToReveal.columnNumber} : {};
1033     this.showRepresentedObject(representedObject, cookie);
1034 };
1035
1036 WebInspector.showSourceCodeLocation = function(sourceCodeLocation)
1037 {
1038     this.showSourceCode(sourceCodeLocation.displaySourceCode, sourceCodeLocation.displayPosition());
1039 };
1040
1041 WebInspector.showOriginalUnformattedSourceCodeLocation = function(sourceCodeLocation)
1042 {
1043     this.showSourceCode(sourceCodeLocation.sourceCode, sourceCodeLocation.position(), null, true);
1044 };
1045
1046 WebInspector.showOriginalOrFormattedSourceCodeLocation = function(sourceCodeLocation)
1047 {
1048     this.showSourceCode(sourceCodeLocation.sourceCode, sourceCodeLocation.formattedPosition());
1049 };
1050
1051 WebInspector.showOriginalOrFormattedSourceCodeTextRange = function(sourceCodeTextRange)
1052 {
1053     var textRangeToSelect = sourceCodeTextRange.formattedTextRange;
1054     this.showSourceCode(sourceCodeTextRange.sourceCode, textRangeToSelect.startPosition(), textRangeToSelect);
1055 };
1056
1057 WebInspector.showResourceRequest = function(resource)
1058 {
1059     this.showRepresentedObject(resource, {[WebInspector.ResourceClusterContentView.ContentViewIdentifierCookieKey]: WebInspector.ResourceClusterContentView.RequestIdentifier});
1060 };
1061
1062 WebInspector.debuggerToggleBreakpoints = function(event)
1063 {
1064     WebInspector.debuggerManager.breakpointsEnabled = !WebInspector.debuggerManager.breakpointsEnabled;
1065 };
1066
1067 WebInspector.debuggerPauseResumeToggle = function(event)
1068 {
1069     if (WebInspector.debuggerManager.paused)
1070         WebInspector.debuggerManager.resume();
1071     else
1072         WebInspector.debuggerManager.pause();
1073 };
1074
1075 WebInspector.debuggerStepOver = function(event)
1076 {
1077     WebInspector.debuggerManager.stepOver();
1078 };
1079
1080 WebInspector.debuggerStepInto = function(event)
1081 {
1082     WebInspector.debuggerManager.stepInto();
1083 };
1084
1085 WebInspector.debuggerStepOut = function(event)
1086 {
1087     WebInspector.debuggerManager.stepOut();
1088 };
1089
1090 WebInspector._searchTextDidChange = function(event)
1091 {
1092     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.SearchTabContentView);
1093     if (!tabContentView)
1094         tabContentView = new WebInspector.SearchTabContentView;
1095
1096     var searchQuery = this._searchToolbarItem.text;
1097     this._searchToolbarItem.text = "";
1098
1099     this.tabBrowser.showTabForContentView(tabContentView);
1100
1101     tabContentView.performSearch(searchQuery);
1102 };
1103
1104 WebInspector._focusSearchField = function(event)
1105 {
1106     if (this.tabBrowser.selectedTabContentView instanceof WebInspector.SearchTabContentView) {
1107         this.tabBrowser.selectedTabContentView.focusSearchField();
1108         return;
1109     }
1110
1111     this._searchToolbarItem.focus();
1112 };
1113
1114 WebInspector._focusChanged = function(event)
1115 {
1116     // Make a caret selection inside the focused element if there isn't a range selection and there isn't already
1117     // a caret selection inside. This is needed (at least) to remove caret from console when focus is moved.
1118     // The selection change should not apply to text fields and text areas either.
1119
1120     if (WebInspector.isEventTargetAnEditableField(event)) {
1121         // Still update the currentFocusElement if inside of a CodeMirror editor.
1122         var codeMirrorEditorElement = event.target.enclosingNodeOrSelfWithClass("CodeMirror");
1123         if (codeMirrorEditorElement && codeMirrorEditorElement !== this.currentFocusElement) {
1124             this.previousFocusElement = this.currentFocusElement;
1125             this.currentFocusElement = codeMirrorEditorElement;
1126         }
1127         return;
1128     }
1129
1130     var selection = window.getSelection();
1131     if (!selection.isCollapsed)
1132         return;
1133
1134     var element = event.target;
1135
1136     if (element !== this.currentFocusElement) {
1137         this.previousFocusElement = this.currentFocusElement;
1138         this.currentFocusElement = element;
1139     }
1140
1141     if (element.isInsertionCaretInside())
1142         return;
1143
1144     var selectionRange = element.ownerDocument.createRange();
1145     selectionRange.setStart(element, 0);
1146     selectionRange.setEnd(element, 0);
1147
1148     selection.removeAllRanges();
1149     selection.addRange(selectionRange);
1150 };
1151
1152 WebInspector._mouseWasClicked = function(event)
1153 {
1154     this.handlePossibleLinkClick(event);
1155 };
1156
1157 WebInspector._dragOver = function(event)
1158 {
1159     // Do nothing if another event listener handled the event already.
1160     if (event.defaultPrevented)
1161         return;
1162
1163     // Allow dropping into editable areas.
1164     if (WebInspector.isEventTargetAnEditableField(event))
1165         return;
1166
1167     // Prevent the drop from being accepted.
1168     event.dataTransfer.dropEffect = "none";
1169     event.preventDefault();
1170 };
1171
1172 WebInspector._captureDidStart = function(event)
1173 {
1174     this._dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.replay);
1175 };
1176
1177 WebInspector._debuggerDidPause = function(event)
1178 {
1179     // FIXME: <webkit.org/b/###> Web Inspector: Preference for Auto Showing Scope Chain sidebar on pause
1180     this.showDebuggerTab();
1181
1182     this._dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.debugger);
1183
1184     InspectorFrontendHost.bringToFront();
1185 };
1186
1187 WebInspector._debuggerDidResume = function(event)
1188 {
1189     this._dashboardContainer.closeDashboardViewForRepresentedObject(this.dashboardManager.dashboards.debugger);
1190 };
1191
1192 WebInspector._frameWasAdded = function(event)
1193 {
1194     if (!this._frameIdentifierToShowSourceCodeWhenAvailable)
1195         return;
1196
1197     var frame = event.data.frame;
1198     if (frame.id !== this._frameIdentifierToShowSourceCodeWhenAvailable)
1199         return;
1200
1201     function delayedWork()
1202     {
1203         this.showSourceCodeForFrame(frame.id);
1204     }
1205
1206     // Delay showing the frame since FrameWasAdded is called before MainFrameChanged.
1207     // Calling showSourceCodeForFrame before MainFrameChanged will show the frame then close it.
1208     setTimeout(delayedWork.bind(this));
1209 };
1210
1211 WebInspector._mainFrameDidChange = function(event)
1212 {
1213     this._updateDownloadToolbarButton();
1214
1215     this.updateWindowTitle();
1216 };
1217
1218 WebInspector._mainResourceDidChange = function(event)
1219 {
1220     if (!event.target.isMainFrame())
1221         return;
1222
1223     this._inProvisionalLoad = false;
1224
1225     // Run cookie restoration after we are sure all of the Tabs and NavigationSidebarPanels
1226     // have updated with respect to the main resource change.
1227     setTimeout(this._restoreCookieForOpenTabs.bind(this, WebInspector.StateRestorationType.Navigation));
1228
1229     this._updateDownloadToolbarButton();
1230
1231     this.updateWindowTitle();
1232 };
1233
1234 WebInspector._provisionalLoadStarted = function(event)
1235 {
1236     if (!event.target.isMainFrame())
1237         return;
1238
1239     this._saveCookieForOpenTabs();
1240
1241     this._inProvisionalLoad = true;
1242 };
1243
1244 WebInspector._restoreCookieForOpenTabs = function(restorationType)
1245 {
1246     for (var tabBarItem of this.tabBar.tabBarItems) {
1247         var tabContentView = tabBarItem.representedObject;
1248         if (!(tabContentView instanceof WebInspector.TabContentView))
1249             continue;
1250         tabContentView.restoreStateFromCookie(restorationType);
1251     }
1252 };
1253
1254 WebInspector._saveCookieForOpenTabs = function()
1255 {
1256     for (var tabBarItem of this.tabBar.tabBarItems) {
1257         var tabContentView = tabBarItem.representedObject;
1258         if (!(tabContentView instanceof WebInspector.TabContentView))
1259             continue;
1260         tabContentView.saveStateToCookie();
1261     }
1262 };
1263
1264 WebInspector._windowFocused = function(event)
1265 {
1266     if (event.target.document.nodeType !== Node.DOCUMENT_NODE)
1267         return;
1268
1269     // FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
1270     document.body.classList.remove(this.docked ? "window-docked-inactive" : "window-inactive");
1271 };
1272
1273 WebInspector._windowBlurred = function(event)
1274 {
1275     if (event.target.document.nodeType !== Node.DOCUMENT_NODE)
1276         return;
1277
1278     // FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
1279     document.body.classList.add(this.docked ? "window-docked-inactive" : "window-inactive");
1280 };
1281
1282 WebInspector._windowResized = function(event)
1283 {
1284     this.toolbar.updateLayout();
1285     this._tabBrowserSizeDidChange();
1286 };
1287
1288 WebInspector._updateModifierKeys = function(event)
1289 {
1290     var didChange = this.modifierKeys.altKey !== event.altKey || this.modifierKeys.metaKey !== event.metaKey || this.modifierKeys.shiftKey !== event.shiftKey;
1291
1292     this.modifierKeys = {altKey: event.altKey, metaKey: event.metaKey, shiftKey: event.shiftKey};
1293
1294     if (didChange)
1295         this.notifications.dispatchEventToListeners(WebInspector.Notification.GlobalModifierKeysDidChange, event);
1296 };
1297
1298 WebInspector._windowKeyDown = function(event)
1299 {
1300     this._updateModifierKeys(event);
1301 };
1302
1303 WebInspector._windowKeyUp = function(event)
1304 {
1305     this._updateModifierKeys(event);
1306 };
1307
1308 WebInspector._mouseMoved = function(event)
1309 {
1310     this._updateModifierKeys(event);
1311     this.mouseCoords = {
1312         x: event.pageX,
1313         y: event.pageY
1314     };
1315 };
1316
1317 WebInspector._pageHidden = function(event)
1318 {
1319     this._saveCookieForOpenTabs();
1320 };
1321
1322 WebInspector._undock = function(event)
1323 {
1324     InspectorFrontendHost.requestSetDockSide("undocked");
1325 };
1326
1327 WebInspector._dockBottom = function(event)
1328 {
1329     InspectorFrontendHost.requestSetDockSide("bottom");
1330 };
1331
1332 WebInspector._dockRight = function(event)
1333 {
1334     InspectorFrontendHost.requestSetDockSide("right");
1335 };
1336
1337 WebInspector._updateDockNavigationItems = function()
1338 {
1339     if (this._dockingAvailable || this.docked) {
1340         this._closeToolbarButton.hidden = !this.docked;
1341         this._undockToolbarButton.hidden = this._dockSide === "undocked";
1342         this._dockBottomToolbarButton.hidden = this._dockSide === "bottom";
1343         this._dockRightToolbarButton.hidden = this._dockSide === "right";
1344     } else {
1345         this._closeToolbarButton.hidden = true;
1346         this._undockToolbarButton.hidden = true;
1347         this._dockBottomToolbarButton.hidden = true;
1348         this._dockRightToolbarButton.hidden = true;
1349     }
1350 };
1351
1352 WebInspector._tabBrowserSizeDidChange = function()
1353 {
1354     this.tabBrowser.updateLayout();
1355     this.splitContentBrowser.updateLayout();
1356     this.quickConsole.updateLayout();
1357 };
1358
1359 WebInspector._quickConsoleDidResize = function(event)
1360 {
1361     this.tabBrowser.updateLayout();
1362 };
1363
1364 WebInspector._sidebarWidthDidChange = function(event)
1365 {
1366     this._tabBrowserSizeDidChange();
1367 };
1368
1369 WebInspector._updateToolbarHeight = function()
1370 {
1371     if (WebInspector.Platform.name === "mac" && WebInspector.Platform.version.release < 10)
1372         InspectorFrontendHost.setToolbarHeight(this.toolbar.element.offsetHeight);
1373 };
1374
1375 WebInspector._tabBrowserSelectedTabContentViewDidChange = function(event)
1376 {
1377     if (this.tabBar.selectedTabBarItem)
1378         this._selectedTabIndexSetting.value = this.tabBar.tabBarItems.indexOf(this.tabBar.selectedTabBarItem);
1379
1380     if (!this.doesCurrentTabSupportSplitContentBrowser())
1381         this.hideSplitConsole();
1382
1383     if (!this.isShowingSplitConsole())
1384         this.quickConsole.consoleLogVisibilityChanged(this.isShowingConsoleTab());
1385 };
1386
1387 WebInspector._initializeWebSocketIfNeeded = function()
1388 {
1389     if (!InspectorFrontendHost.initializeWebSocket)
1390         return;
1391
1392     var queryParams = parseLocationQueryParameters();
1393
1394     if ("ws" in queryParams)
1395         var url = "ws://" + queryParams.ws;
1396     else if ("page" in queryParams) {
1397         var page = queryParams.page;
1398         var host = "host" in queryParams ? queryParams.host : window.location.host;
1399         var url = "ws://" + host + "/devtools/page/" + page;
1400     }
1401
1402     if (!url)
1403         return;
1404
1405     InspectorFrontendHost.initializeWebSocket(url);
1406 };
1407
1408 WebInspector._updateSplitConsoleHeight = function(height)
1409 {
1410     const minimumHeight = 64;
1411     const maximumHeight = window.innerHeight * 0.55;
1412
1413     height = Math.max(minimumHeight, Math.min(height, maximumHeight));
1414
1415     this.splitContentBrowser.element.style.height = height + "px";
1416 };
1417
1418 WebInspector._consoleResizerMouseDown = function(event)
1419 {
1420     if (event.button !== 0 || event.ctrlKey)
1421         return;
1422
1423     // Only start dragging if the target is one of the elements that we expect.
1424     if (!event.target.classList.contains("navigation-bar") && !event.target.classList.contains("flexible-space"))
1425         return;
1426
1427     var resizerElement = event.target;
1428     var mouseOffset = resizerElement.offsetHeight - (event.pageY - resizerElement.totalOffsetTop);
1429
1430     function dockedResizerDrag(event)
1431     {
1432         if (event.button !== 0)
1433             return;
1434
1435         var height = window.innerHeight - event.pageY - mouseOffset;
1436
1437         this._splitConsoleHeightSetting.value = height;
1438
1439         this._updateSplitConsoleHeight(height);
1440
1441         this.quickConsole.dispatchEventToListeners(WebInspector.QuickConsole.Event.DidResize);
1442     }
1443
1444     function dockedResizerDragEnd(event)
1445     {
1446         if (event.button !== 0)
1447             return;
1448
1449         this.elementDragEnd(event);
1450     }
1451
1452     this.elementDragStart(resizerElement, dockedResizerDrag.bind(this), dockedResizerDragEnd.bind(this), event, "row-resize");
1453 };
1454
1455 WebInspector._toolbarMouseDown = function(event)
1456 {
1457     if (event.ctrlKey)
1458         return;
1459
1460     if (this._dockSide === "right")
1461         return;
1462
1463     if (this.docked)
1464         this._dockedResizerMouseDown(event);
1465     else
1466         this._moveWindowMouseDown(event);
1467 };
1468
1469 WebInspector._dockedResizerMouseDown = function(event)
1470 {
1471     if (event.button !== 0 || event.ctrlKey)
1472         return;
1473
1474     if (!this.docked)
1475         return;
1476
1477     // Only start dragging if the target is one of the elements that we expect.
1478     if (event.target.id !== "docked-resizer" && !event.target.classList.contains("toolbar") &&
1479         !event.target.classList.contains("flexible-space") && !event.target.classList.contains("item-section"))
1480         return;
1481
1482     var windowProperty = this._dockSide === "bottom" ? "innerHeight" : "innerWidth";
1483     var eventScreenProperty = this._dockSide === "bottom" ? "screenY" : "screenX";
1484     var eventClientProperty = this._dockSide === "bottom" ? "clientY" : "clientX";
1485
1486     var resizerElement = event.target;
1487     var firstClientPosition = event[eventClientProperty];
1488     var lastScreenPosition = event[eventScreenProperty];
1489
1490     function dockedResizerDrag(event)
1491     {
1492         if (event.button !== 0)
1493             return;
1494
1495         var position = event[eventScreenProperty];
1496         var delta = position - lastScreenPosition;
1497         var clientPosition = event[eventClientProperty];
1498
1499         lastScreenPosition = position;
1500
1501         // If delta is positive the docked Inspector size is decreasing, in which case the cursor client position
1502         // with respect to the target cannot be less than the first mouse down position within the target.
1503         if (delta > 0 && clientPosition < firstClientPosition)
1504             return;
1505
1506         // If delta is negative the docked Inspector size is increasing, in which case the cursor client position
1507         // with respect to the target cannot be greater than the first mouse down position within the target.
1508         if (delta < 0 && clientPosition > firstClientPosition)
1509             return;
1510
1511         var dimension = Math.max(0, window[windowProperty] - delta);
1512
1513         if (this._dockSide === "bottom")
1514             InspectorFrontendHost.setAttachedWindowHeight(dimension);
1515         else
1516             InspectorFrontendHost.setAttachedWindowWidth(dimension);
1517     }
1518
1519     function dockedResizerDragEnd(event)
1520     {
1521         if (event.button !== 0)
1522             return;
1523
1524         WebInspector.elementDragEnd(event);
1525     }
1526
1527     WebInspector.elementDragStart(resizerElement, dockedResizerDrag.bind(this), dockedResizerDragEnd.bind(this), event, this._dockSide === "bottom" ? "row-resize" : "col-resize");
1528 };
1529
1530 WebInspector._moveWindowMouseDown = function(event)
1531 {
1532     console.assert(!this.docked);
1533
1534     if (event.button !== 0 || event.ctrlKey)
1535         return;
1536
1537     // Only start dragging if the target is one of the elements that we expect.
1538     if (!event.target.classList.contains("toolbar") && !event.target.classList.contains("flexible-space") &&
1539         !event.target.classList.contains("item-section"))
1540         return;
1541
1542     if (WebInspector.Platform.name === "mac") {
1543         // New Mac releases can start a window drag.
1544         if (WebInspector.Platform.version.release >= 11) {
1545             InspectorFrontendHost.startWindowDrag();
1546             event.preventDefault();
1547             return;
1548         }
1549
1550         // Ignore dragging on the top of the toolbar on Mac if the system handles it.
1551         if (WebInspector.Platform.version.release === 10) {
1552             const windowDragHandledTitleBarHeight = 22;
1553             if (event.pageY < windowDragHandledTitleBarHeight) {
1554                 event.preventDefault();
1555                 return;
1556             }
1557         }
1558     }
1559
1560     var lastScreenX = event.screenX;
1561     var lastScreenY = event.screenY;
1562
1563     function toolbarDrag(event)
1564     {
1565         if (event.button !== 0)
1566             return;
1567
1568         var x = event.screenX - lastScreenX;
1569         var y = event.screenY - lastScreenY;
1570
1571         InspectorFrontendHost.moveWindowBy(x, y);
1572
1573         lastScreenX = event.screenX;
1574         lastScreenY = event.screenY;
1575     }
1576
1577     function toolbarDragEnd(event)
1578     {
1579         if (event.button !== 0)
1580             return;
1581
1582         WebInspector.elementDragEnd(event);
1583     }
1584
1585     WebInspector.elementDragStart(event.target, toolbarDrag, toolbarDragEnd, event, "default");
1586 };
1587
1588 WebInspector._storageWasInspected = function(event)
1589 {
1590     this.showStorageTab();
1591 };
1592
1593 WebInspector._domNodeWasInspected = function(event)
1594 {
1595     this.domTreeManager.highlightDOMNodeForTwoSeconds(event.data.node.id);
1596
1597     InspectorFrontendHost.bringToFront();
1598
1599     this.showElementsTab();
1600     this.showMainFrameDOMTree(event.data.node);
1601 };
1602
1603 WebInspector._inspectModeStateChanged = function(event)
1604 {
1605     this._inspectModeToolbarButton.activated = this.domTreeManager.inspectModeEnabled;
1606 };
1607
1608 WebInspector._toggleInspectMode = function(event)
1609 {
1610     this.domTreeManager.inspectModeEnabled = !this.domTreeManager.inspectModeEnabled;
1611 };
1612
1613 WebInspector._downloadWebArchive = function(event)
1614 {
1615     this.archiveMainFrame();
1616 };
1617
1618 WebInspector._reloadPage = function(event)
1619 {
1620     PageAgent.reload();
1621 };
1622
1623 WebInspector._reloadPageClicked = function(event)
1624 {
1625     // Ignore cache when the shift key is pressed.
1626     PageAgent.reload(window.event ? window.event.shiftKey : false);
1627 };
1628
1629 WebInspector._reloadPageIgnoringCache = function(event)
1630 {
1631     PageAgent.reload(true);
1632 };
1633
1634 WebInspector._updateReloadToolbarButton = function()
1635 {
1636     if (!window.PageAgent) {
1637         this._reloadToolbarButton.hidden = true;
1638         return;
1639     }
1640
1641     this._reloadToolbarButton.hidden = false;
1642 };
1643
1644 WebInspector._updateDownloadToolbarButton = function()
1645 {
1646     // COMPATIBILITY (iOS 7): Page.archive did not exist yet.
1647     if (!window.PageAgent || !PageAgent.archive || this.debuggableType !== WebInspector.DebuggableType.Web) {
1648         this._downloadToolbarButton.hidden = true;
1649         return;
1650     }
1651
1652     if (this._downloadingPage) {
1653         this._downloadToolbarButton.enabled = false;
1654         return;
1655     }
1656
1657     this._downloadToolbarButton.enabled = this.canArchiveMainFrame();
1658 };
1659
1660 WebInspector._toggleInspectMode = function(event)
1661 {
1662     this.domTreeManager.inspectModeEnabled = !this.domTreeManager.inspectModeEnabled;
1663 };
1664
1665 WebInspector._showConsoleTab = function(event)
1666 {
1667     this.showConsoleTab();
1668 };
1669
1670 WebInspector._focusConsolePrompt = function(event)
1671 {
1672     this.quickConsole.prompt.focus();
1673 };
1674
1675 WebInspector._focusedContentBrowser = function()
1676 {
1677     if (this.tabBrowser.element.isSelfOrAncestor(this.currentFocusElement) || document.activeElement === document.body) {
1678         var tabContentView = this.tabBrowser.selectedTabContentView;
1679         if (tabContentView instanceof WebInspector.ContentBrowserTabContentView)
1680             return tabContentView.contentBrowser;
1681         return null;
1682     }
1683
1684     if (this.splitContentBrowser.element.isSelfOrAncestor(this.currentFocusElement)
1685         || (WebInspector.isShowingSplitConsole() && this.quickConsole.element.isSelfOrAncestor(this.currentFocusElement)))
1686         return this.splitContentBrowser;
1687
1688     return null;
1689 };
1690
1691 WebInspector._focusedContentView = function()
1692 {
1693     if (this.tabBrowser.element.isSelfOrAncestor(this.currentFocusElement) || document.activeElement === document.body) {
1694         var tabContentView = this.tabBrowser.selectedTabContentView;
1695         if (tabContentView instanceof WebInspector.ContentBrowserTabContentView)
1696             return tabContentView.contentBrowser.currentContentView;
1697         return tabContentView;
1698     }
1699
1700     if (this.splitContentBrowser.element.isSelfOrAncestor(this.currentFocusElement)
1701         || (WebInspector.isShowingSplitConsole() && this.quickConsole.element.isSelfOrAncestor(this.currentFocusElement)))
1702         return this.splitContentBrowser.currentContentView;
1703
1704     return null;
1705 };
1706
1707 WebInspector._focusedOrVisibleContentBrowser = function()
1708 {
1709     let focusedContentBrowser = this._focusedContentBrowser();
1710     if (focusedContentBrowser)
1711         return focusedContentBrowser;
1712
1713     var tabContentView = this.tabBrowser.selectedTabContentView;
1714     if (tabContentView instanceof WebInspector.ContentBrowserTabContentView)
1715         return tabContentView.contentBrowser;
1716
1717     return null;
1718 };
1719
1720 WebInspector._focusedOrVisibleContentView = function()
1721 {
1722     let focusedContentView = this._focusedContentView();
1723     if (focusedContentView)
1724         return focusedContentView;
1725
1726     var tabContentView = this.tabBrowser.selectedTabContentView;
1727     if (tabContentView instanceof WebInspector.ContentBrowserTabContentView)
1728         return tabContentView.contentBrowser.currentContentView;
1729     return tabContentView;
1730 };
1731
1732 WebInspector._beforecopy = function(event)
1733 {
1734     var selection = window.getSelection();
1735
1736     // If there is no selection, see if the focused element or focused ContentView can handle the copy event.
1737     if (selection.isCollapsed && !WebInspector.isEventTargetAnEditableField(event)) {
1738         var focusedCopyHandler = this.currentFocusElement && this.currentFocusElement.copyHandler;
1739         if (focusedCopyHandler && typeof focusedCopyHandler.handleBeforeCopyEvent === "function") {
1740             focusedCopyHandler.handleBeforeCopyEvent(event);
1741             if (event.defaultPrevented)
1742                 return;
1743         }
1744
1745         var focusedContentView = this._focusedContentView();
1746         if (focusedContentView && typeof focusedContentView.handleCopyEvent === "function") {
1747             event.preventDefault();
1748             return;
1749         }
1750
1751         return;
1752     }
1753
1754     if (selection.isCollapsed)
1755         return;
1756
1757     // Say we can handle it (by preventing default) to remove word break characters.
1758     event.preventDefault();
1759 };
1760
1761 WebInspector._find = function(event)
1762 {
1763     var contentBrowser = this._focusedOrVisibleContentBrowser();
1764     if (!contentBrowser || typeof contentBrowser.handleFindEvent !== "function")
1765         return;
1766     
1767     contentBrowser.handleFindEvent(event);
1768 };
1769
1770 WebInspector._save = function(event)
1771 {
1772     var contentView = this._focusedOrVisibleContentView();
1773     if (!contentView || !contentView.supportsSave)
1774         return;
1775
1776     WebInspector.saveDataToFile(contentView.saveData);
1777 };
1778
1779 WebInspector._saveAs = function(event)
1780 {
1781     var contentView = this._focusedOrVisibleContentView();
1782     if (!contentView || !contentView.supportsSave)
1783         return;
1784
1785     WebInspector.saveDataToFile(contentView.saveData, true);
1786 };
1787
1788 WebInspector._copy = function(event)
1789 {
1790     var selection = window.getSelection();
1791
1792     // If there is no selection, pass the copy event on to the focused element or focused ContentView.
1793     if (selection.isCollapsed && !WebInspector.isEventTargetAnEditableField(event)) {
1794         var focusedCopyHandler = this.currentFocusElement && this.currentFocusElement.copyHandler;
1795         if (focusedCopyHandler && typeof focusedCopyHandler.handleCopyEvent === "function") {
1796             focusedCopyHandler.handleCopyEvent(event);
1797             if (event.defaultPrevented)
1798                 return;
1799         }
1800
1801         var focusedContentView = this._focusedContentView();
1802         if (focusedContentView && typeof focusedContentView.handleCopyEvent === "function") {
1803             focusedContentView.handleCopyEvent(event);
1804             return;
1805         }
1806
1807         return;
1808     }
1809
1810     if (selection.isCollapsed)
1811         return;
1812
1813     // Remove word break characters from the selection before putting it on the pasteboard.
1814     var selectionString = selection.toString().removeWordBreakCharacters();
1815     event.clipboardData.setData("text/plain", selectionString);
1816     event.preventDefault();
1817 };
1818
1819 WebInspector._increaseZoom = function(event) {
1820     let currentZoom = InspectorFrontendHost.zoomFactor();
1821     InspectorFrontendHost.setZoomFactor(currentZoom + 0.2);
1822     event.preventDefault();
1823 };
1824
1825 WebInspector._decreaseZoom = function(event) {
1826     let currentZoom = InspectorFrontendHost.zoomFactor();
1827     InspectorFrontendHost.setZoomFactor(currentZoom - 0.2);
1828     event.preventDefault();
1829 };
1830
1831 WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor, eventTarget)
1832 {
1833     if (WebInspector._elementDraggingEventListener || WebInspector._elementEndDraggingEventListener)
1834         WebInspector.elementDragEnd(event);
1835
1836     if (element) {
1837         // Install glass pane
1838         if (WebInspector._elementDraggingGlassPane)
1839             WebInspector._elementDraggingGlassPane.remove();
1840
1841         var glassPane = document.createElement("div");
1842         glassPane.style.cssText = "position:absolute;top:0;bottom:0;left:0;right:0;opacity:0;z-index:1";
1843         glassPane.id = "glass-pane-for-drag";
1844         element.ownerDocument.body.appendChild(glassPane);
1845         WebInspector._elementDraggingGlassPane = glassPane;
1846     }
1847
1848     WebInspector._elementDraggingEventListener = dividerDrag;
1849     WebInspector._elementEndDraggingEventListener = elementDragEnd;
1850
1851     var targetDocument = event.target.ownerDocument;
1852
1853     WebInspector._elementDraggingEventTarget = eventTarget || targetDocument;
1854     WebInspector._elementDraggingEventTarget.addEventListener("mousemove", dividerDrag, true);
1855     WebInspector._elementDraggingEventTarget.addEventListener("mouseup", elementDragEnd, true);
1856
1857     targetDocument.body.style.cursor = cursor;
1858
1859     event.preventDefault();
1860 };
1861
1862 WebInspector.elementDragEnd = function(event)
1863 {
1864     WebInspector._elementDraggingEventTarget.removeEventListener("mousemove", WebInspector._elementDraggingEventListener, true);
1865     WebInspector._elementDraggingEventTarget.removeEventListener("mouseup", WebInspector._elementEndDraggingEventListener, true);
1866
1867     event.target.ownerDocument.body.style.removeProperty("cursor");
1868
1869     if (WebInspector._elementDraggingGlassPane)
1870         WebInspector._elementDraggingGlassPane.remove();
1871
1872     delete WebInspector._elementDraggingGlassPane;
1873     delete WebInspector._elementDraggingEventTarget;
1874     delete WebInspector._elementDraggingEventListener;
1875     delete WebInspector._elementEndDraggingEventListener;
1876
1877     event.preventDefault();
1878 };
1879
1880 WebInspector.createMessageTextView = function(message, isError)
1881 {
1882     var messageElement = document.createElement("div");
1883     messageElement.className = "message-text-view";
1884     if (isError)
1885         messageElement.classList.add("error");
1886
1887     messageElement.textContent = message;
1888
1889     return messageElement;
1890 };
1891
1892 WebInspector.createGoToArrowButton = function()
1893 {
1894     function stopPropagation(event)
1895     {
1896         event.stopPropagation()
1897     }
1898
1899     var button = document.createElement("button");
1900     button.addEventListener("mousedown", stopPropagation, true);
1901     button.className = "go-to-arrow";
1902     button.tabIndex = -1;
1903     return button;
1904 };
1905
1906 WebInspector.createSourceCodeLocationLink = function(sourceCodeLocation, dontFloat, useGoToArrowButton)
1907 {
1908     console.assert(sourceCodeLocation);
1909     if (!sourceCodeLocation)
1910         return null;
1911
1912     var linkElement = document.createElement("a");
1913     linkElement.className = "go-to-link";
1914     WebInspector.linkifyElement(linkElement, sourceCodeLocation);
1915     sourceCodeLocation.populateLiveDisplayLocationTooltip(linkElement);
1916
1917     if (useGoToArrowButton)
1918         linkElement.appendChild(WebInspector.createGoToArrowButton());
1919     else
1920         sourceCodeLocation.populateLiveDisplayLocationString(linkElement, "textContent");
1921
1922     if (dontFloat)
1923         linkElement.classList.add("dont-float");
1924
1925     return linkElement;
1926 };
1927
1928 WebInspector.linkifyLocation = function(url, lineNumber, columnNumber, className)
1929 {
1930     var sourceCode = WebInspector.sourceCodeForURL(url);
1931
1932     if (!sourceCode) {
1933         var anchor = document.createElement("a");
1934         anchor.href  = url;
1935         anchor.lineNumber = lineNumber;
1936         if (className)
1937             anchor.className = className;
1938         anchor.append(WebInspector.displayNameForURL(url) + ":" + lineNumber);
1939         return anchor;
1940     }
1941
1942     var sourceCodeLocation = sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
1943     var linkElement = WebInspector.createSourceCodeLocationLink(sourceCodeLocation, true);
1944     if (className)
1945         linkElement.classList.add(className);
1946     return linkElement;
1947 };
1948
1949 WebInspector.linkifyElement = function(linkElement, sourceCodeLocation) {
1950     console.assert(sourceCodeLocation);
1951
1952     function showSourceCodeLocation(event)
1953     {
1954         event.stopPropagation();
1955         event.preventDefault();
1956
1957         if (event.metaKey)
1958             this.showOriginalUnformattedSourceCodeLocation(sourceCodeLocation);
1959         else
1960             this.showSourceCodeLocation(sourceCodeLocation);
1961     }
1962
1963     linkElement.addEventListener("click", showSourceCodeLocation.bind(this));
1964 };
1965
1966 WebInspector.sourceCodeForURL = function(url) {
1967     var sourceCode = WebInspector.frameResourceManager.resourceForURL(url);
1968     if (!sourceCode) {
1969         sourceCode = WebInspector.debuggerManager.scriptsForURL(url)[0];
1970         if (sourceCode)
1971             sourceCode = sourceCode.resource || sourceCode;
1972     }
1973     return sourceCode || null;
1974 };
1975
1976 WebInspector.linkifyURLAsNode = function(url, linkText, classes)
1977 {
1978     if (!linkText)
1979         linkText = url;
1980
1981     classes = (classes ? classes + " " : "");
1982
1983     var a = document.createElement("a");
1984     a.href = url;
1985     a.className = classes;
1986
1987     a.textContent = linkText;
1988     a.style.maxWidth = "100%";
1989
1990     return a;
1991 };
1992
1993 WebInspector.linkifyStringAsFragmentWithCustomLinkifier = function(string, linkifier)
1994 {
1995     var container = document.createDocumentFragment();
1996     var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/;
1997     var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
1998
1999     while (string) {
2000         var linkString = linkStringRegEx.exec(string);
2001         if (!linkString)
2002             break;
2003
2004         linkString = linkString[0];
2005         var linkIndex = string.indexOf(linkString);
2006         var nonLink = string.substring(0, linkIndex);
2007         container.append(nonLink);
2008
2009         var title = linkString;
2010         var realURL = (linkString.startsWith("www.") ? "http://" + linkString : linkString);
2011         var lineColumnMatch = lineColumnRegEx.exec(realURL);
2012         if (lineColumnMatch)
2013             realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length);
2014
2015         var lineNumber;
2016         if (lineColumnMatch)
2017             lineNumber = parseInt(lineColumnMatch[1]) - 1;
2018
2019         var linkNode = linkifier(title, realURL, lineNumber);
2020         container.appendChild(linkNode);
2021         string = string.substring(linkIndex + linkString.length, string.length);
2022     }
2023
2024     if (string)
2025         container.append(string);
2026
2027     return container;
2028 };
2029
2030 WebInspector.linkifyStringAsFragment = function(string)
2031 {
2032     function linkifier(title, url, lineNumber)
2033     {
2034         var urlNode = WebInspector.linkifyURLAsNode(url, title, undefined);
2035         if (lineNumber !== undefined)
2036             urlNode.lineNumber = lineNumber;
2037
2038         return urlNode;
2039     }
2040
2041     return WebInspector.linkifyStringAsFragmentWithCustomLinkifier(string, linkifier);
2042 };
2043
2044 WebInspector._undoKeyboardShortcut = function(event)
2045 {
2046     if (!this.isEditingAnyField() && !this.isEventTargetAnEditableField(event)) {
2047         this.undo();
2048         event.preventDefault();
2049     }
2050 };
2051
2052 WebInspector._redoKeyboardShortcut = function(event)
2053 {
2054     if (!this.isEditingAnyField() && !this.isEventTargetAnEditableField(event)) {
2055         this.redo();
2056         event.preventDefault();
2057     }
2058 };
2059
2060 WebInspector.undo = function()
2061 {
2062     DOMAgent.undo();
2063 };
2064
2065 WebInspector.redo = function()
2066 {
2067     DOMAgent.redo();
2068 };
2069
2070 WebInspector.highlightRangesWithStyleClass = function(element, resultRanges, styleClass, changes)
2071 {
2072     changes = changes || [];
2073     var highlightNodes = [];
2074     var lineText = element.textContent;
2075     var ownerDocument = element.ownerDocument;
2076     var textNodeSnapshot = ownerDocument.evaluate(".//text()", element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
2077
2078     var snapshotLength = textNodeSnapshot.snapshotLength;
2079     if (snapshotLength === 0)
2080         return highlightNodes;
2081
2082     var nodeRanges = [];
2083     var rangeEndOffset = 0;
2084     for (var i = 0; i < snapshotLength; ++i) {
2085         var range = {};
2086         range.offset = rangeEndOffset;
2087         range.length = textNodeSnapshot.snapshotItem(i).textContent.length;
2088         rangeEndOffset = range.offset + range.length;
2089         nodeRanges.push(range);
2090     }
2091
2092     var startIndex = 0;
2093     for (var i = 0; i < resultRanges.length; ++i) {
2094         var startOffset = resultRanges[i].offset;
2095         var endOffset = startOffset + resultRanges[i].length;
2096
2097         while (startIndex < snapshotLength && nodeRanges[startIndex].offset + nodeRanges[startIndex].length <= startOffset)
2098             startIndex++;
2099         var endIndex = startIndex;
2100         while (endIndex < snapshotLength && nodeRanges[endIndex].offset + nodeRanges[endIndex].length < endOffset)
2101             endIndex++;
2102         if (endIndex === snapshotLength)
2103             break;
2104
2105         var highlightNode = ownerDocument.createElement("span");
2106         highlightNode.className = styleClass;
2107         highlightNode.textContent = lineText.substring(startOffset, endOffset);
2108
2109         var lastTextNode = textNodeSnapshot.snapshotItem(endIndex);
2110         var lastText = lastTextNode.textContent;
2111         lastTextNode.textContent = lastText.substring(endOffset - nodeRanges[endIndex].offset);
2112         changes.push({ node: lastTextNode, type: "changed", oldText: lastText, newText: lastTextNode.textContent });
2113
2114         if (startIndex === endIndex) {
2115             lastTextNode.parentElement.insertBefore(highlightNode, lastTextNode);
2116             changes.push({ node: highlightNode, type: "added", nextSibling: lastTextNode, parent: lastTextNode.parentElement });
2117             highlightNodes.push(highlightNode);
2118
2119             var prefixNode = ownerDocument.createTextNode(lastText.substring(0, startOffset - nodeRanges[startIndex].offset));
2120             lastTextNode.parentElement.insertBefore(prefixNode, highlightNode);
2121             changes.push({ node: prefixNode, type: "added", nextSibling: highlightNode, parent: lastTextNode.parentElement });
2122         } else {
2123             var firstTextNode = textNodeSnapshot.snapshotItem(startIndex);
2124             var firstText = firstTextNode.textContent;
2125             var anchorElement = firstTextNode.nextSibling;
2126
2127             firstTextNode.parentElement.insertBefore(highlightNode, anchorElement);
2128             changes.push({ node: highlightNode, type: "added", nextSibling: anchorElement, parent: firstTextNode.parentElement });
2129             highlightNodes.push(highlightNode);
2130
2131             firstTextNode.textContent = firstText.substring(0, startOffset - nodeRanges[startIndex].offset);
2132             changes.push({ node: firstTextNode, type: "changed", oldText: firstText, newText: firstTextNode.textContent });
2133
2134             for (var j = startIndex + 1; j < endIndex; j++) {
2135                 var textNode = textNodeSnapshot.snapshotItem(j);
2136                 var text = textNode.textContent;
2137                 textNode.textContent = "";
2138                 changes.push({ node: textNode, type: "changed", oldText: text, newText: textNode.textContent });
2139             }
2140         }
2141         startIndex = endIndex;
2142         nodeRanges[startIndex].offset = endOffset;
2143         nodeRanges[startIndex].length = lastTextNode.textContent.length;
2144
2145     }
2146     return highlightNodes;
2147 };
2148
2149 WebInspector.revertDomChanges = function(domChanges)
2150 {
2151     for (var i = domChanges.length - 1; i >= 0; --i) {
2152         var entry = domChanges[i];
2153         switch (entry.type) {
2154         case "added":
2155             entry.node.remove();
2156             break;
2157         case "changed":
2158             entry.node.textContent = entry.oldText;
2159             break;
2160         }
2161     }
2162 };
2163
2164 WebInspector.archiveMainFrame = function()
2165 {
2166     this._downloadingPage = true;
2167     this._updateDownloadToolbarButton();
2168
2169     PageAgent.archive(function(error, data) {
2170         this._downloadingPage = false;
2171         this._updateDownloadToolbarButton();
2172
2173         if (error)
2174             return;
2175
2176         var mainFrame = WebInspector.frameResourceManager.mainFrame;
2177         var archiveName = mainFrame.mainResource.urlComponents.host || mainFrame.mainResource.displayName || "Archive";
2178         var url = "web-inspector:///" + encodeURI(archiveName) + ".webarchive";
2179
2180         InspectorFrontendHost.save(url, data, true, true);
2181     }.bind(this));
2182 };
2183
2184 WebInspector.canArchiveMainFrame = function()
2185 {
2186     // COMPATIBILITY (iOS 7): Page.archive did not exist yet.
2187     if (!PageAgent.archive || this.debuggableType !== WebInspector.DebuggableType.Web)
2188         return false;
2189
2190     if (!WebInspector.frameResourceManager.mainFrame || !WebInspector.frameResourceManager.mainFrame.mainResource)
2191         return;
2192
2193     return WebInspector.Resource.typeFromMIMEType(WebInspector.frameResourceManager.mainFrame.mainResource.mimeType) === WebInspector.Resource.Type.Document;
2194 };
2195
2196 WebInspector.addWindowKeydownListener = function(listener)
2197 {
2198     if (typeof listener.handleKeydownEvent !== "function")
2199         return;
2200
2201     this._windowKeydownListeners.push(listener);
2202
2203     this._updateWindowKeydownListener();
2204 };
2205
2206 WebInspector.removeWindowKeydownListener = function(listener)
2207 {
2208     this._windowKeydownListeners.remove(listener);
2209
2210     this._updateWindowKeydownListener();
2211 };
2212
2213 WebInspector._updateWindowKeydownListener = function()
2214 {
2215     if (this._windowKeydownListeners.length > 0)
2216         window.addEventListener("keydown", WebInspector._sharedWindowKeydownListener, true);
2217     else
2218         window.removeEventListener("keydown", WebInspector._sharedWindowKeydownListener, true);
2219 };
2220
2221 WebInspector._sharedWindowKeydownListener = function(event)
2222 {
2223     for (var i = WebInspector._windowKeydownListeners.length - 1; i >= 0; --i) {
2224         if (WebInspector._windowKeydownListeners[i].handleKeydownEvent(event)) {
2225             event.stopImmediatePropagation();
2226             event.preventDefault();
2227             break;
2228         }
2229     }
2230 };