454b8c7ec8a6709ee150e7a07b389ed212e4b035
[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.loaded = function()
45 {
46     // Initialize WebSocket to communication.
47     this._initializeWebSocketIfNeeded();
48
49     this.debuggableType = InspectorFrontendHost.debuggableType() === "web" ? WebInspector.DebuggableType.Web : WebInspector.DebuggableType.JavaScript;
50     this.hasExtraDomains = false;
51
52     // Register observers for events from the InspectorBackend.
53     if (InspectorBackend.registerInspectorDispatcher)
54         InspectorBackend.registerInspectorDispatcher(new WebInspector.InspectorObserver);
55     if (InspectorBackend.registerPageDispatcher)
56         InspectorBackend.registerPageDispatcher(new WebInspector.PageObserver);
57     if (InspectorBackend.registerConsoleDispatcher)
58         InspectorBackend.registerConsoleDispatcher(new WebInspector.ConsoleObserver);
59     if (InspectorBackend.registerNetworkDispatcher)
60         InspectorBackend.registerNetworkDispatcher(new WebInspector.NetworkObserver);
61     if (InspectorBackend.registerDOMDispatcher)
62         InspectorBackend.registerDOMDispatcher(new WebInspector.DOMObserver);
63     if (InspectorBackend.registerDebuggerDispatcher)
64         InspectorBackend.registerDebuggerDispatcher(new WebInspector.DebuggerObserver);
65     if (InspectorBackend.registerDatabaseDispatcher)
66         InspectorBackend.registerDatabaseDispatcher(new WebInspector.DatabaseObserver);
67     if (InspectorBackend.registerDOMStorageDispatcher)
68         InspectorBackend.registerDOMStorageDispatcher(new WebInspector.DOMStorageObserver);
69     if (InspectorBackend.registerApplicationCacheDispatcher)
70         InspectorBackend.registerApplicationCacheDispatcher(new WebInspector.ApplicationCacheObserver);
71     if (InspectorBackend.registerTimelineDispatcher)
72         InspectorBackend.registerTimelineDispatcher(new WebInspector.TimelineObserver);
73     if (InspectorBackend.registerCSSDispatcher)
74         InspectorBackend.registerCSSDispatcher(new WebInspector.CSSObserver);
75     if (InspectorBackend.registerLayerTreeDispatcher)
76         InspectorBackend.registerLayerTreeDispatcher(new WebInspector.LayerTreeObserver);
77     if (InspectorBackend.registerRuntimeDispatcher)
78         InspectorBackend.registerRuntimeDispatcher(new WebInspector.RuntimeObserver);
79     if (InspectorBackend.registerReplayDispatcher)
80         InspectorBackend.registerReplayDispatcher(new WebInspector.ReplayObserver);
81
82     // Enable agents.
83     if (window.InspectorAgent)
84         InspectorAgent.enable();
85
86     // Perform one-time tasks.
87     WebInspector.CSSCompletions.requestCSSNameCompletions();
88     this._generateDisclosureTriangleImages();
89
90     // Listen for the ProvisionalLoadStarted event before registering for events so our code gets called before any managers or sidebars.
91     // This lets us save a state cookie before any managers or sidebars do any resets that would affect state (namely TimelineManager).
92     WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ProvisionalLoadStarted, this._provisionalLoadStarted, this);
93
94     // Create the singleton managers next, before the user interface elements, so the user interface can register
95     // as event listeners on these managers.
96     this.branchManager = new WebInspector.BranchManager;
97     this.frameResourceManager = new WebInspector.FrameResourceManager;
98     this.storageManager = new WebInspector.StorageManager;
99     this.domTreeManager = new WebInspector.DOMTreeManager;
100     this.cssStyleManager = new WebInspector.CSSStyleManager;
101     this.logManager = new WebInspector.LogManager;
102     this.issueManager = new WebInspector.IssueManager;
103     this.analyzerManager = new WebInspector.AnalyzerManager;
104     this.runtimeManager = new WebInspector.RuntimeManager;
105     this.applicationCacheManager = new WebInspector.ApplicationCacheManager;
106     this.timelineManager = new WebInspector.TimelineManager;
107     this.debuggerManager = new WebInspector.DebuggerManager;
108     this.sourceMapManager = new WebInspector.SourceMapManager;
109     this.layerTreeManager = new WebInspector.LayerTreeManager;
110     this.dashboardManager = new WebInspector.DashboardManager;
111     this.probeManager = new WebInspector.ProbeManager;
112     this.replayManager = new WebInspector.ReplayManager;
113
114     // Enable the Console Agent after creating the singleton managers.
115     if (window.ConsoleAgent)
116         ConsoleAgent.enable();
117
118     // Tell the backend we are initialized after all our initialization messages have been sent.
119     setTimeout(function() {
120         if (window.InspectorAgent && InspectorAgent.initialized)
121             InspectorAgent.initialized();
122     }, 0);
123
124     // Register for events.
125     this.replayManager.addEventListener(WebInspector.ReplayManager.Event.CaptureStarted, this._captureDidStart, this);
126     this.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
127     this.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
128     this.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.InspectModeStateChanged, this._inspectModeStateChanged, this);
129     this.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.DOMNodeWasInspected, this._domNodeWasInspected, this);
130     this.storageManager.addEventListener(WebInspector.StorageManager.Event.DOMStorageObjectWasInspected, this._storageWasInspected, this);
131     this.storageManager.addEventListener(WebInspector.StorageManager.Event.DatabaseWasInspected, this._storageWasInspected, this);
132     this.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.MainFrameDidChange, this._mainFrameDidChange, this);
133     this.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.FrameWasAdded, this._frameWasAdded, this);
134
135     WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
136
137     document.addEventListener("DOMContentLoaded", this.contentLoaded.bind(this));
138
139     // Create settings.
140     this._toolbarDockedRightDisplayModeSetting = new WebInspector.Setting("toolbar-docked-right-display-mode", WebInspector.Toolbar.DisplayMode.IconAndLabelVertical);
141     this._toolbarDockedRightSizeModeSetting = new WebInspector.Setting("toolbar-docked-right-size-mode",WebInspector.Toolbar.SizeMode.Normal);
142
143     this._toolbarDockedBottomDisplayModeSetting = new WebInspector.Setting("toolbar-docked-display-mode", WebInspector.Toolbar.DisplayMode.IconAndLabelHorizontal);
144     this._toolbarDockedBottomSizeModeSetting = new WebInspector.Setting("toolbar-docked-size-mode",WebInspector.Toolbar.SizeMode.Small);
145
146     this._toolbarUndockedDisplayModeSetting = new WebInspector.Setting("toolbar-undocked-display-mode", WebInspector.Toolbar.DisplayMode.IconAndLabelVertical);
147     this._toolbarUndockedSizeModeSetting = new WebInspector.Setting("toolbar-undocked-size-mode",WebInspector.Toolbar.SizeMode.Normal);
148
149     this._showingSplitConsoleSetting = new WebInspector.Setting("showing-split-console", false);
150     this._splitConsoleHeightSetting = new WebInspector.Setting("split-console-height", 150);
151
152     this._dockButtonToggledSetting = new WebInspector.Setting("dock-button-toggled", false);
153
154     this._openTabsSetting = new WebInspector.Setting("open-tabs", ["elements", "resources", "timeline", "debugger", "console"]);
155     this._selectedTabIndexSetting = new WebInspector.Setting("selected-tab-index", 0);
156
157     this.showShadowDOMSetting = new WebInspector.Setting("show-shadow-dom", false);
158     this.showReplayInterfaceSetting = new WebInspector.Setting("show-web-replay", false);
159
160     this.showJavaScriptTypeInformationSetting = new WebInspector.Setting("show-javascript-type-information", false);
161     if (this.showJavaScriptTypeInformationSetting.value && window.RuntimeAgent && RuntimeAgent.enableTypeProfiler)
162         RuntimeAgent.enableTypeProfiler();
163
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     if (WebInspector.Platform.isLegacyMacOS)
199         document.body.classList.add("legacy");
200     if (WebInspector.Platform.version.name)
201         document.body.classList.add(WebInspector.Platform.version.name);
202
203     document.body.classList.add(this.debuggableType);
204
205     // Create the user interface elements.
206     this.toolbar = new WebInspector.Toolbar(document.getElementById("toolbar"));
207     this.toolbar.addEventListener(WebInspector.Toolbar.Event.DisplayModeDidChange, this._toolbarDisplayModeDidChange, this);
208     this.toolbar.addEventListener(WebInspector.Toolbar.Event.SizeModeDidChange, this._toolbarSizeModeDidChange, this);
209
210     this.tabBar = new WebInspector.TabBar(document.getElementById("tab-bar"));
211
212     var contentElement = document.getElementById("content");
213     contentElement.setAttribute("role", "main");
214     contentElement.setAttribute("aria-label", WebInspector.UIString("Content"));
215
216     this.splitContentBrowser = new WebInspector.ContentBrowser(document.getElementById("split-content-browser"), this, true);
217     this.splitContentBrowser.navigationBar.element.addEventListener("mousedown", this._consoleResizerMouseDown.bind(this));
218
219     this.quickConsole = new WebInspector.QuickConsole(document.getElementById("quick-console"));
220     this.quickConsole.addEventListener(WebInspector.QuickConsole.Event.DidResize, this._quickConsoleDidResize, this);
221
222     this._consoleRepresentedObject = new WebInspector.LogObject;
223     this._consoleTreeElement = new WebInspector.LogTreeElement(this._consoleRepresentedObject);
224     this.consoleContentView = WebInspector.splitContentBrowser.contentViewForRepresentedObject(this._consoleRepresentedObject);
225     this.consoleLogViewController = this.consoleContentView.logViewController;
226
227     // FIXME: The sidebars should be flipped in RTL languages.
228     this.navigationSidebar = new WebInspector.Sidebar(document.getElementById("navigation-sidebar"), WebInspector.Sidebar.Sides.Left);
229     this.navigationSidebar.addEventListener(WebInspector.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
230
231     this.detailsSidebar = new WebInspector.Sidebar(document.getElementById("details-sidebar"), WebInspector.Sidebar.Sides.Right, null, null, WebInspector.UIString("Details"));
232     this.detailsSidebar.addEventListener(WebInspector.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
233
234     this.navigationSidebarKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "0", this.toggleNavigationSidebar.bind(this));
235     this.detailsSidebarKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Option, "0", this.toggleDetailsSidebar.bind(this));
236
237     this.tabBrowser = new WebInspector.TabBrowser(document.getElementById("tab-browser"), this.tabBar, this.navigationSidebar, this.detailsSidebar);
238     this.tabBrowser.addEventListener(WebInspector.TabBrowser.Event.SelectedTabContentViewDidChange, this._tabBrowserSelectedTabContentViewDidChange, this);
239
240     this._reloadPageKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "R", this._reloadPage.bind(this));
241     this._reloadPageIgnoringCacheKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "R", this._reloadPageIgnoringCache.bind(this));
242
243     this._consoleKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Option | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "C", this._showConsoleTab.bind(this));
244
245     this._inspectModeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "C", this._toggleInspectMode.bind(this));
246
247     this._undoKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Z", this._undoKeyboardShortcut.bind(this));
248     this._redoKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "Z", this._redoKeyboardShortcut.bind(this));
249     this._undoKeyboardShortcut.implicitlyPreventsDefault = this._redoKeyboardShortcut.implicitlyPreventsDefault = false;
250
251     this.toggleBreakpointsKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this.debuggerToggleBreakpoints.bind(this));
252     this.pauseOrResumeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this.debuggerPauseResumeToggle.bind(this));
253     this.stepOverKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F6, this.debuggerStepOver.bind(this));
254     this.stepIntoKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F7, this.debuggerStepInto.bind(this));
255     this.stepOutKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F8, this.debuggerStepOut.bind(this));
256
257     this.pauseOrResumeAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Backslash, this.debuggerPauseResumeToggle.bind(this));
258     this.stepOverAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.SingleQuote, this.debuggerStepOver.bind(this));
259     this.stepIntoAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this.debuggerStepInto.bind(this));
260     this.stepOutAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Shift | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this.debuggerStepOut.bind(this));
261
262     this.undockButtonNavigationItem = new WebInspector.ToggleControlToolbarItem("undock", WebInspector.UIString("Detach into separate window"), "", platformImagePath("Undock.svg"), "", 16, 14);
263     this.undockButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._undock, this);
264
265     this.closeButtonNavigationItem = new WebInspector.ControlToolbarItem("dock-close", WebInspector.UIString("Close"), platformImagePath("Close.svg"), 16, 14);
266     this.closeButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this.close, this);
267
268     this.toolbar.addToolbarItem(this.closeButtonNavigationItem, WebInspector.Toolbar.Section.Control);
269     this.toolbar.addToolbarItem(this.undockButtonNavigationItem, WebInspector.Toolbar.Section.Control);
270
271     this.dashboardContainer = new WebInspector.DashboardContainerView;
272     this.dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.default);
273
274     this.toolbar.addToolbarItem(this.dashboardContainer.toolbarItem, WebInspector.Toolbar.Section.Center);
275
276     this.resourceDetailsSidebarPanel = new WebInspector.ResourceDetailsSidebarPanel;
277     this.domNodeDetailsSidebarPanel = new WebInspector.DOMNodeDetailsSidebarPanel;
278     this.cssStyleDetailsSidebarPanel = new WebInspector.CSSStyleDetailsSidebarPanel;
279     this.applicationCacheDetailsSidebarPanel = new WebInspector.ApplicationCacheDetailsSidebarPanel;
280     this.scopeChainDetailsSidebarPanel = new WebInspector.ScopeChainDetailsSidebarPanel;
281     this.probeDetailsSidebarPanel = new WebInspector.ProbeDetailsSidebarPanel;
282     this.renderingFrameDetailsSidebarPanel = new WebInspector.RenderingFrameDetailsSidebarPanel;
283
284     if (window.LayerTreeAgent)
285         this.layerTreeDetailsSidebarPanel = new WebInspector.LayerTreeDetailsSidebarPanel;
286
287     this.modifierKeys = {altKey: false, metaKey: false, shiftKey: false};
288
289     this.toolbar.element.addEventListener("mousedown", this._toolbarMouseDown.bind(this));
290     document.getElementById("docked-resizer").addEventListener("mousedown", this._dockedResizerMouseDown.bind(this));
291
292     this._updateDockNavigationItems();
293     this._updateToolbarHeight();
294
295     for (var tabType of this._openTabsSetting.value) {
296         var tabContentView = this._tabContentViewForType(tabType);
297         if (!tabContentView)
298             continue;
299         this.tabBrowser.addTabForContentView(tabContentView, true);
300     }
301
302     this.tabBar.selectedTabBarItem = this._selectedTabIndexSetting.value;
303
304     if (!this.tabBar.selectedTabBarItem)
305         this.tabBar.selectedTabBarItem = 0;
306
307     // Listen to the events after restoring the saved tabs to avoid recursion.
308     this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemAdded, this._rememberOpenTabs, this);
309     this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemRemoved, this._rememberOpenTabs, this);
310     this.tabBar.addEventListener(WebInspector.TabBar.Event.TabBarItemsReordered, this._rememberOpenTabs, this);
311
312     // Signal that the frontend is now ready to receive messages.
313     InspectorFrontendAPI.loadCompleted();
314
315     // Tell the InspectorFrontendHost we loaded, which causes the window to display
316     // and pending InspectorFrontendAPI commands to be sent.
317     InspectorFrontendHost.loaded();
318
319     this._updateSplitConsoleHeight(this._splitConsoleHeightSetting.value);
320
321     if (this._showingSplitConsoleSetting.value)
322         this.showSplitConsole();
323
324     this._contentLoaded = true;
325
326     this.runBootstrapOperations();
327 };
328
329 WebInspector._tabContentViewForType = function(tabType)
330 {
331     switch (tabType) {
332     case WebInspector.ElementsTabContentView.Type:
333         return new WebInspector.ElementsTabContentView;
334     case WebInspector.ResourcesTabContentView.Type:
335         return new WebInspector.ResourcesTabContentView;
336     case WebInspector.TimelineTabContentView.Type:
337         return new WebInspector.TimelineTabContentView;
338     case WebInspector.DebuggerTabContentView.Type:
339         return new WebInspector.DebuggerTabContentView;
340     case WebInspector.ConsoleTabContentView.Type:
341         return new WebInspector.ConsoleTabContentView;
342     default:
343         console.error("Unknown tab type", tabType);
344     }
345
346     return null;
347 };
348
349 WebInspector._rememberOpenTabs = function()
350 {
351     var openTabs = [];
352
353     for (var tabBarItem of this.tabBar.tabBarItems) {
354         var tabContentView = tabBarItem.representedObject;
355         if (tabContentView instanceof WebInspector.SettingsTabContentView)
356             continue;
357         console.assert(tabContentView.type, "Tab type can't be null, undefined, or empty string", tabContentView.type, tabContentView);
358         openTabs.push(tabContentView.type);
359     }
360
361     this._openTabsSetting.value = openTabs;
362 };
363
364 WebInspector.activateExtraDomains = function(domains)
365 {
366     this.hasExtraDomains = true;
367
368     for (var domain of domains) {
369         var agent = InspectorBackend.activateDomain(domain);
370         if (agent.enable)
371             agent.enable();
372     }
373
374     this.notifications.dispatchEventToListeners(WebInspector.Notification.ExtraDomainsActivated, {"domains": domains});
375
376     WebInspector.CSSCompletions.requestCSSNameCompletions();
377 };
378
379 WebInspector.contentBrowserTreeElementForRepresentedObject = function(contentBrowser, representedObject)
380 {
381     // The console does not have a sidebar, so return a tree element here so something is shown.
382     if (representedObject === this._consoleRepresentedObject)
383         return this._consoleTreeElement;
384     return null;
385 };
386
387 WebInspector.updateWindowTitle = function()
388 {
389     var mainFrame = this.frameResourceManager.mainFrame;
390     console.assert(mainFrame);
391
392     var urlComponents = mainFrame.mainResource.urlComponents;
393
394     var lastPathComponent;
395     try {
396         lastPathComponent = decodeURIComponent(urlComponents.lastPathComponent || "");
397     } catch (e) {
398         lastPathComponent = urlComponents.lastPathComponent;
399     }
400
401     // Build a title based on the URL components.
402     if (urlComponents.host && lastPathComponent)
403         var title = this.displayNameForHost(urlComponents.host) + " \u2014 " + lastPathComponent;
404     else if (urlComponents.host)
405         var title = this.displayNameForHost(urlComponents.host);
406     else if (lastPathComponent)
407         var title = lastPathComponent;
408     else
409         var title = mainFrame.url;
410
411     // The name "inspectedURLChanged" sounds like the whole URL is required, however this is only
412     // used for updating the window title and it can be any string.
413     InspectorFrontendHost.inspectedURLChanged(title);
414 };
415
416 WebInspector.updateDockedState = function(side)
417 {
418     if (this._dockSide === side)
419         return;
420
421     this._dockSide = side;
422
423     this.docked = side !== "undocked";
424
425     this._ignoreToolbarModeDidChangeEvents = true;
426
427     if (side === "bottom") {
428         document.body.classList.add("docked", "bottom");
429         document.body.classList.remove("window-inactive", "right");
430
431         this.toolbar.displayMode = this._toolbarDockedBottomDisplayModeSetting.value;
432         this.toolbar.sizeMode = this._toolbarDockedBottomSizeModeSetting.value;
433     } else if (side === "right") {
434         document.body.classList.add("docked", "right");
435         document.body.classList.remove("window-inactive", "bottom");
436
437         this.toolbar.displayMode = this._toolbarDockedRightDisplayModeSetting.value;
438         this.toolbar.sizeMode = this._toolbarDockedRightSizeModeSetting.value;
439     } else {
440         document.body.classList.remove("docked", "right", "bottom");
441
442         this.toolbar.displayMode = this._toolbarUndockedDisplayModeSetting.value;
443         this.toolbar.sizeMode = this._toolbarUndockedSizeModeSetting.value;
444     }
445
446     this._ignoreToolbarModeDidChangeEvents = false;
447
448     this._updateDockNavigationItems();
449     this._updateToolbarHeight();
450 };
451
452 WebInspector.handlePossibleLinkClick = function(event, frame, alwaysOpenExternally)
453 {
454     var anchorElement = event.target.enclosingNodeOrSelfWithNodeName("a");
455     if (!anchorElement || !anchorElement.href)
456         return false;
457
458     if (WebInspector.isBeingEdited(anchorElement)) {
459         // Don't follow the link when it is being edited.
460         return false;
461     }
462
463     // Prevent the link from navigating, since we don't do any navigation by following links normally.
464     event.preventDefault();
465     event.stopPropagation();
466
467     this.openURL(anchorElement.href, frame, false, anchorElement.lineNumber);
468
469     return true;
470 };
471
472 WebInspector.openURL = function(url, frame, alwaysOpenExternally, lineNumber)
473 {
474     console.assert(url);
475     if (!url)
476         return;
477
478     // If alwaysOpenExternally is not defined, base it off the command/meta key for the current event.
479     if (alwaysOpenExternally === undefined || alwaysOpenExternally === null)
480         alwaysOpenExternally = window.event ? window.event.metaKey : false;
481
482     if (alwaysOpenExternally) {
483         InspectorFrontendHost.openInNewTab(url);
484         return;
485     }
486
487     var searchChildFrames = false;
488     if (!frame) {
489         frame = this.frameResourceManager.mainFrame;
490         searchChildFrames = true;
491     }
492
493     console.assert(frame);
494
495     // WebInspector.Frame.resourceForURL does not check the main resource, only sub-resources. So check both.
496     var resource = frame.url === url ? frame.mainResource : frame.resourceForURL(url, searchChildFrames);
497     if (resource) {
498         var position = new WebInspector.SourceCodePosition(lineNumber, 0);
499         this.showSourceCode(resource, position);
500         return;
501     }
502
503     InspectorFrontendHost.openInNewTab(url);
504 };
505
506 WebInspector.close = function()
507 {
508     if (this._isClosing)
509         return;
510
511     this._isClosing = true;
512
513     InspectorFrontendHost.closeWindow();
514 };
515
516 WebInspector.isConsoleFocused = function()
517 {
518     return this.quickConsole.prompt.focused;
519 };
520
521 WebInspector.isShowingSplitConsole = function()
522 {
523     return !this.splitContentBrowser.element.classList.contains("hidden");
524 };
525
526 WebInspector.doesCurrentTabSupportSplitContentBrowser = function()
527 {
528     var currentContentView = this.tabBrowser.selectedTabContentView;
529     return !currentContentView || currentContentView.supportsSplitContentBrowser;
530 };
531
532 WebInspector.toggleSplitConsole = function()
533 {
534     if (!this.doesCurrentTabSupportSplitContentBrowser()) {
535         this.showConsoleTab();
536         return;
537     }
538
539     if (this.isShowingSplitConsole())
540         this.hideSplitConsole();
541     else
542         this.showSplitConsole();
543 };
544
545 WebInspector.showSplitConsole = function()
546 {
547     if (!this.doesCurrentTabSupportSplitContentBrowser()) {
548         this.showConsoleTab();
549         return;
550     }
551
552     this.splitContentBrowser.element.classList.remove("hidden");
553
554     this._showingSplitConsoleSetting.value = true;
555
556     if (this.splitContentBrowser.currentContentView !== this.consoleContentView) {
557         // Be sure to close the view in the tab content browser before showing it in the
558         // split content browser. We can only show a content view in one browser at a time.
559         if (this.consoleContentView.parentContainer)
560             this.consoleContentView.parentContainer.closeContentView(this.consoleContentView);
561         this.splitContentBrowser.showContentView(this.consoleContentView);
562     } else {
563         // This causes the view to know it was shown and focus the prompt.
564         this.splitContentBrowser.shown();
565     }
566
567     this.quickConsole.consoleLogVisibilityChanged(true);
568 };
569
570 WebInspector.hideSplitConsole = function()
571 {
572     if (!this.isShowingSplitConsole())
573         return;
574
575     this.splitContentBrowser.element.classList.add("hidden");
576
577     this._showingSplitConsoleSetting.value = false;
578
579     // This causes the view to know it was hidden.
580     this.splitContentBrowser.hidden();
581
582     this.quickConsole.consoleLogVisibilityChanged(false);
583 };
584
585 WebInspector.showConsoleTab = function(requestedScope)
586 {
587     this.hideSplitConsole();
588
589     var scope = requestedScope || WebInspector.LogContentView.Scopes.All;
590
591     // If the requested scope is already selected and the console is showing, then switch back to All.
592     if (this.isShowingConsoleTab() && this.consoleContentView.scopeBar.item(scope).selected)
593         scope = WebInspector.LogContentView.Scopes.All;
594
595     if (requestedScope || !this.consoleContentView.scopeBar.selectedItems.length)
596         this.consoleContentView.scopeBar.item(scope).selected = true;
597
598     this.showRepresentedObject(this._consoleRepresentedObject);
599
600     console.assert(this.isShowingConsoleTab());
601 };
602
603 WebInspector.isShowingConsoleTab = function()
604 {
605     return this.tabBrowser.selectedTabContentView instanceof WebInspector.ConsoleTabContentView;
606 };
607
608 WebInspector.showElementsTab = function()
609 {
610     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.ElementsTabContentView);
611     if (!tabContentView)
612         tabContentView = new WebInspector.ElementsTabContentView;
613     this.tabBrowser.showTabForContentView(tabContentView);
614 };
615
616 WebInspector.showDebuggerTab = function(breakpointToSelect)
617 {
618     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.DebuggerTabContentView);
619     if (!tabContentView)
620         tabContentView = new WebInspector.DebuggerTabContentView;
621
622     if (breakpointToSelect instanceof WebInspector.Breakpoint)
623         tabContentView.revealAndSelectBreakpoint(breakpointToSelect);
624
625     this.tabBrowser.showTabForContentView(tabContentView);
626 };
627
628 WebInspector.showResourcesTab = function()
629 {
630     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.ResourcesTabContentView);
631     if (!tabContentView)
632         tabContentView = new WebInspector.ResourcesTabContentView;
633     this.tabBrowser.showTabForContentView(tabContentView);
634 };
635
636 WebInspector.showTimelineTab = function()
637 {
638     var tabContentView = this.tabBrowser.bestTabContentViewForClass(WebInspector.TimelineTabContentView);
639     if (!tabContentView)
640         tabContentView = new WebInspector.TimelineTabContentView;
641     this.tabBrowser.showTabForContentView(tabContentView);
642 };
643
644 WebInspector.UIString = function(string, vararg)
645 {
646     if (WebInspector.dontLocalizeUserInterface)
647         return string;
648
649     if (window.localizedStrings && string in window.localizedStrings)
650         return window.localizedStrings[string];
651
652     if (!this._missingLocalizedStrings)
653         this._missingLocalizedStrings = {};
654
655     if (!(string in this._missingLocalizedStrings)) {
656         console.error("Localized string \"" + string + "\" was not found.");
657         this._missingLocalizedStrings[string] = true;
658     }
659
660     return "LOCALIZED STRING NOT FOUND";
661 };
662
663 WebInspector.restoreFocusFromElement = function(element)
664 {
665     if (element && element.isSelfOrAncestor(this.currentFocusElement))
666         this.previousFocusElement.focus();
667 };
668
669 WebInspector.toggleNavigationSidebar = function(event)
670 {
671     if (!this.navigationSidebar.collapsed || !this.navigationSidebar.sidebarPanels.length) {
672         this.navigationSidebar.collapsed = true;
673         return;
674     }
675
676     if (!this.navigationSidebar.selectedSidebarPanel)
677         this.navigationSidebar.selectedSidebarPanel = this.navigationSidebar.sidebarPanels[0];
678     this.navigationSidebar.collapsed = false;
679 };
680
681 WebInspector.toggleDetailsSidebar = function(event)
682 {
683     if (!this.detailsSidebar.collapsed || !this.detailsSidebar.sidebarPanels.length) {
684         this.detailsSidebar.collapsed = true;
685         return;
686     }
687
688     if (!this.detailsSidebar.selectedSidebarPanel)
689         this.detailsSidebar.selectedSidebarPanel = this.detailsSidebar.sidebarPanels[0];
690     this.detailsSidebar.collapsed = false;
691 };
692
693 WebInspector.tabContentViewClassForRepresentedObject = function(representedObject)
694 {
695     if (representedObject instanceof WebInspector.DOMTree)
696         return WebInspector.ElementsTabContentView;
697
698     if (representedObject instanceof WebInspector.TimelineRecording)
699         return WebInspector.TimelineTabContentView;
700
701     // We only support one console tab right now. So this isn't an instanceof check.
702     if (representedObject === this._consoleRepresentedObject)
703         return WebInspector.ConsoleTabContentView;
704
705     if (WebInspector.debuggerManager.paused) {
706         if (representedObject instanceof WebInspector.Script)
707             return WebInspector.DebuggerTabContentView;
708
709         if (representedObject instanceof WebInspector.Resource && (representedObject.type === WebInspector.Resource.Type.Document || representedObject.type === WebInspector.Resource.Type.Script))
710             return WebInspector.DebuggerTabContentView;
711     }
712
713     if (representedObject instanceof WebInspector.Frame || representedObject instanceof WebInspector.Resource || representedObject instanceof WebInspector.Script)
714         return WebInspector.ResourcesTabContentView;
715
716     // FIXME: Move Content Flows to the Elements tab?
717     if (representedObject instanceof WebInspector.ContentFlow)
718         return WebInspector.ResourcesTabContentView;
719
720     // FIXME: Move these to a Storage tab.
721     if (representedObject instanceof WebInspector.DOMStorageObject || representedObject instanceof WebInspector.CookieStorageObject ||
722         representedObject instanceof WebInspector.DatabaseTableObject || representedObject instanceof WebInspector.DatabaseObject ||
723         representedObject instanceof WebInspector.ApplicationCacheFrame || representedObject instanceof WebInspector.IndexedDatabaseObjectStore ||
724         representedObject instanceof WebInspector.IndexedDatabaseObjectStoreIndex)
725         return WebInspector.ResourcesTabContentView;
726
727     return null;
728 };
729
730 WebInspector.tabContentViewForRepresentedObject = function(representedObject)
731 {
732     var tabContentView = this.tabBrowser.bestTabContentViewForRepresentedObject(representedObject);
733     if (tabContentView)
734         return tabContentView;
735
736     var tabContentViewClass = this.tabContentViewClassForRepresentedObject(representedObject);
737     if (!tabContentViewClass) {
738         console.error("Unknown representedObject, couldn't create TabContentView.", representedObject);
739         return null;
740     }
741
742     tabContentView = new tabContentViewClass;
743
744     this.tabBrowser.addTabForContentView(tabContentView);
745
746     return tabContentView;
747 };
748
749 WebInspector.showRepresentedObject = function(representedObject, cookie, forceShowTab)
750 {
751     var tabContentView = this.tabContentViewForRepresentedObject(representedObject);
752     console.assert(tabContentView);
753     if (!tabContentView)
754         return;
755
756     if (window.event || forceShowTab)
757         this.tabBrowser.showTabForContentView(tabContentView);
758
759     tabContentView.showRepresentedObject(representedObject, cookie);
760 };
761
762 WebInspector.showMainFrameDOMTree = function(nodeToSelect, forceShowTab)
763 {
764     console.assert(WebInspector.frameResourceManager.mainFrame);
765     if (!WebInspector.frameResourceManager.mainFrame)
766         return;
767     this.showRepresentedObject(WebInspector.frameResourceManager.mainFrame.domTree, {nodeToSelect}, forceShowTab);
768 };
769
770 WebInspector.showContentFlowDOMTree = function(contentFlow, nodeToSelect, forceShowTab)
771 {
772     this.showRepresentedObject(contentFlow, {nodeToSelect}, forceShowTab);
773 };
774
775 WebInspector.showSourceCodeForFrame = function(frameIdentifier, forceShowTab)
776 {
777     var frame = WebInspector.frameResourceManager.frameForIdentifier(frameIdentifier);
778     if (!frame) {
779         this._frameIdentifierToShowSourceCodeWhenAvailable = frameIdentifier;
780         return;
781     }
782
783     this._frameIdentifierToShowSourceCodeWhenAvailable = undefined;
784
785     this.showRepresentedObject(frame, null, forceShowTab);
786 };
787
788 WebInspector.showSourceCode = function(sourceCode, positionToReveal, textRangeToSelect, forceUnformatted, forceShowTab)
789 {
790     console.assert(!positionToReveal || positionToReveal instanceof WebInspector.SourceCodePosition, positionToReveal);
791     var representedObject = sourceCode;
792
793     if (representedObject instanceof WebInspector.Script) {
794         // A script represented by a resource should always show the resource.
795         representedObject = representedObject.resource || representedObject;
796     }
797
798     var cookie = positionToReveal ? {lineNumber: positionToReveal.lineNumber, columnNumber: positionToReveal.columnNumber} : {};
799     this.showRepresentedObject(representedObject, cookie, forceShowTab);
800 };
801
802 WebInspector.showSourceCodeLocation = function(sourceCodeLocation, forceShowTab)
803 {
804     this.showSourceCode(sourceCodeLocation.displaySourceCode, sourceCodeLocation.displayPosition(), null, false, forceShowTab);
805 };
806
807 WebInspector.showOriginalUnformattedSourceCodeLocation = function(sourceCodeLocation, forceShowTab)
808 {
809     this.showSourceCode(sourceCodeLocation.sourceCode, sourceCodeLocation.position(), null, true);
810 };
811
812 WebInspector.showOriginalOrFormattedSourceCodeLocation = function(sourceCodeLocation, forceShowTab)
813 {
814     this.showSourceCode(sourceCodeLocation.sourceCode, sourceCodeLocation.formattedPosition(), null, false, forceShowTab);
815 };
816
817 WebInspector.showOriginalOrFormattedSourceCodeTextRange = function(sourceCodeTextRange, forceShowTab)
818 {
819     var textRangeToSelect = sourceCodeTextRange.formattedTextRange;
820     this.showSourceCode(sourceCodeTextRange.sourceCode, textRangeToSelect.startPosition(), textRangeToSelect, false, forceShowTab);
821 };
822
823 WebInspector.showResourceRequest = function(resource, forceShowTab)
824 {
825     this.showRepresentedObject(resource, {[WebInspector.ResourceClusterContentView.ContentViewIdentifierCookieKey]: WebInspector.ResourceClusterContentView.RequestIdentifier}, forceShowTab);
826 };
827
828 WebInspector.debuggerToggleBreakpoints = function(event)
829 {
830     WebInspector.debuggerManager.breakpointsEnabled = !WebInspector.debuggerManager.breakpointsEnabled;
831 };
832
833 WebInspector.debuggerPauseResumeToggle = function(event)
834 {
835     if (WebInspector.debuggerManager.paused)
836         WebInspector.debuggerManager.resume();
837     else
838         WebInspector.debuggerManager.pause();
839 };
840
841 WebInspector.debuggerStepOver = function(event)
842 {
843     WebInspector.debuggerManager.stepOver();
844 };
845
846 WebInspector.debuggerStepInto = function(event)
847 {
848     WebInspector.debuggerManager.stepInto();
849 };
850
851 WebInspector.debuggerStepOut = function(event)
852 {
853     WebInspector.debuggerManager.stepOut();
854 };
855
856 WebInspector._focusChanged = function(event)
857 {
858     // Make a caret selection inside the focused element if there isn't a range selection and there isn't already
859     // a caret selection inside. This is needed (at least) to remove caret from console when focus is moved.
860     // The selection change should not apply to text fields and text areas either.
861
862     if (WebInspector.isEventTargetAnEditableField(event))
863         return;
864
865     var selection = window.getSelection();
866     if (!selection.isCollapsed)
867         return;
868
869     var element = event.target;
870
871     if (element !== this.currentFocusElement) {
872         this.previousFocusElement = this.currentFocusElement;
873         this.currentFocusElement = element;
874     }
875
876     if (element.isInsertionCaretInside())
877         return;
878
879     var selectionRange = element.ownerDocument.createRange();
880     selectionRange.setStart(element, 0);
881     selectionRange.setEnd(element, 0);
882
883     selection.removeAllRanges();
884     selection.addRange(selectionRange);
885 };
886
887 WebInspector._mouseWasClicked = function(event)
888 {
889     this.handlePossibleLinkClick(event);
890 };
891
892 WebInspector._dragOver = function(event)
893 {
894     // Do nothing if another event listener handled the event already.
895     if (event.defaultPrevented)
896         return;
897
898     // Allow dropping into editable areas.
899     if (WebInspector.isEventTargetAnEditableField(event))
900         return;
901
902     // Prevent the drop from being accepted.
903     event.dataTransfer.dropEffect = "none";
904     event.preventDefault();
905 };
906
907 WebInspector._captureDidStart = function(event)
908 {
909     this.dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.replay);
910 };
911
912 WebInspector._debuggerDidPause = function(event)
913 {
914     this.showDebuggerTab();
915
916     this.dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.debugger);
917
918     InspectorFrontendHost.bringToFront();
919 };
920
921 WebInspector._debuggerDidResume = function(event)
922 {
923     this.dashboardContainer.closeDashboardViewForRepresentedObject(this.dashboardManager.dashboards.debugger);
924 };
925
926 WebInspector._frameWasAdded = function(event)
927 {
928     if (!this._frameIdentifierToShowSourceCodeWhenAvailable)
929         return;
930
931     var frame = event.data.frame;
932     if (frame.id !== this._frameIdentifierToShowSourceCodeWhenAvailable)
933         return;
934
935     this.showSourceCodeForFrame(frame.id);
936 };
937
938 WebInspector._mainFrameDidChange = function(event)
939 {
940     this.updateWindowTitle();
941 };
942
943 WebInspector._mainResourceDidChange = function(event)
944 {
945     if (!event.target.isMainFrame())
946         return;
947
948     this._inProvisionalLoad = false;
949
950     this.updateWindowTitle();
951 };
952
953 WebInspector._provisionalLoadStarted = function(event)
954 {
955     if (!event.target.isMainFrame())
956         return;
957
958     this._inProvisionalLoad = true;
959 };
960
961 WebInspector._windowFocused = function(event)
962 {
963     if (event.target.document.nodeType !== Node.DOCUMENT_NODE || this.docked)
964         return;
965
966     // FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
967     document.body.classList.remove("window-inactive");
968 };
969
970 WebInspector._windowBlurred = function(event)
971 {
972     if (event.target.document.nodeType !== Node.DOCUMENT_NODE || this.docked)
973         return;
974
975     // FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
976     document.body.classList.add("window-inactive");
977 };
978
979 WebInspector._windowResized = function(event)
980 {
981     this.toolbar.updateLayout();
982     this._tabBrowserSizeDidChange();
983 };
984
985 WebInspector._updateModifierKeys = function(event)
986 {
987     var didChange = this.modifierKeys.altKey !== event.altKey || this.modifierKeys.metaKey !== event.metaKey || this.modifierKeys.shiftKey !== event.shiftKey;
988
989     this.modifierKeys = {altKey: event.altKey, metaKey: event.metaKey, shiftKey: event.shiftKey};
990
991     if (didChange)
992         this.notifications.dispatchEventToListeners(WebInspector.Notification.GlobalModifierKeysDidChange, event);
993 };
994
995 WebInspector._windowKeyDown = function(event)
996 {
997     this._updateModifierKeys(event);
998
999     var opposite = !this._dockButtonToggledSetting.value;
1000     this.undockButtonNavigationItem.toggled = (event.altKey && !event.metaKey && !event.shiftKey) ? opposite : !opposite;
1001 };
1002
1003 WebInspector._windowKeyUp = function(event)
1004 {
1005     this._updateModifierKeys(event);
1006
1007     var opposite = !this._dockButtonToggledSetting.value;
1008     this.undockButtonNavigationItem.toggled = (event.altKey && !event.metaKey && !event.shiftKey) ? opposite : !opposite;
1009 };
1010
1011 WebInspector._mouseMoved = function(event)
1012 {
1013     this._updateModifierKeys(event);
1014     this.mouseCoords = {
1015         x: event.pageX,
1016         y: event.pageY
1017     };
1018 };
1019
1020 WebInspector._pageHidden = function(event)
1021 {
1022 };
1023
1024 WebInspector._undock = function(event)
1025 {
1026     this._dockButtonToggledSetting.value = this.undockButtonNavigationItem.toggled;
1027
1028     if (this.undockButtonNavigationItem.toggled)
1029         InspectorFrontendHost.requestSetDockSide(this._dockSide === "bottom" ? "right" : "bottom");
1030     else
1031         InspectorFrontendHost.requestSetDockSide("undocked");
1032 };
1033
1034 WebInspector._updateDockNavigationItems = function()
1035 {
1036     // The close and undock buttons are only available when docked.
1037     var docked = this.docked;
1038     this.closeButtonNavigationItem.hidden = !docked;
1039     this.undockButtonNavigationItem.hidden = !docked;
1040
1041     if (docked) {
1042         this.undockButtonNavigationItem.alternateImage = this._dockSide === "bottom" ? platformImagePath("DockRight.svg") : platformImagePath("DockBottom.svg");
1043         this.undockButtonNavigationItem.alternateToolTip = this._dockSide === "bottom" ? WebInspector.UIString("Dock to right of window") : WebInspector.UIString("Dock to bottom of window");
1044     }
1045
1046     this.undockButtonNavigationItem.toggled = this._dockButtonToggledSetting.value;
1047 };
1048
1049 WebInspector._tabBrowserSizeDidChange = function()
1050 {
1051     this.tabBrowser.updateLayout();
1052     this.splitContentBrowser.updateLayout();
1053     this.quickConsole.updateLayout();
1054 };
1055
1056 WebInspector._quickConsoleDidResize = function(event)
1057 {
1058     this.tabBrowser.updateLayout();
1059 };
1060
1061 WebInspector._sidebarWidthDidChange = function(event)
1062 {
1063     this._tabBrowserSizeDidChange();
1064 };
1065
1066 WebInspector._updateToolbarHeight = function()
1067 {
1068     if (WebInspector.Platform.isLegacyMacOS)
1069         InspectorFrontendHost.setToolbarHeight(this.toolbar.element.offsetHeight);
1070 };
1071
1072 WebInspector._toolbarDisplayModeDidChange = function(event)
1073 {
1074     if (this._ignoreToolbarModeDidChangeEvents)
1075         return;
1076
1077     if (this._dockSide === "bottom")
1078         this._toolbarDockedBottomDisplayModeSetting.value = this.toolbar.displayMode;
1079     else if (this._dockSide === "right")
1080         this._toolbarDockedRightDisplayModeSetting.value = this.toolbar.displayMode;
1081     else
1082         this._toolbarUndockedDisplayModeSetting.value = this.toolbar.displayMode;
1083
1084     this._updateToolbarHeight();
1085 };
1086
1087 WebInspector._toolbarSizeModeDidChange = function(event)
1088 {
1089     if (this._ignoreToolbarModeDidChangeEvents)
1090         return;
1091
1092     if (this._dockSide === "bottom")
1093         this._toolbarDockedBottomSizeModeSetting.value = this.toolbar.sizeMode;
1094     else if (this._dockSide === "right")
1095         this._toolbarDockedRightSizeModeSetting.value = this.toolbar.sizeMode;
1096     else
1097         this._toolbarUndockedSizeModeSetting.value = this.toolbar.sizeMode;
1098
1099     this._updateToolbarHeight();
1100 };
1101
1102 WebInspector._tabBrowserSelectedTabContentViewDidChange = function(event)
1103 {
1104     if (this.tabBar.selectedTabBarItem)
1105         this._selectedTabIndexSetting.value = this.tabBar.tabBarItems.indexOf(this.tabBar.selectedTabBarItem);
1106
1107     if (!this.doesCurrentTabSupportSplitContentBrowser())
1108         this.hideSplitConsole();
1109
1110     if (!this.isShowingSplitConsole())
1111         this.quickConsole.consoleLogVisibilityChanged(this.isShowingConsoleTab());
1112 };
1113
1114 WebInspector._initializeWebSocketIfNeeded = function()
1115 {
1116     if (!InspectorFrontendHost.initializeWebSocket)
1117         return;
1118
1119     var queryParams = parseLocationQueryParameters();
1120
1121     if ("ws" in queryParams)
1122         var url = "ws://" + queryParams.ws;
1123     else if ("page" in queryParams) {
1124         var page = queryParams.page;
1125         var host = "host" in queryParams ? queryParams.host : window.location.host;
1126         var url = "ws://" + host + "/devtools/page/" + page;
1127     }
1128
1129     if (!url)
1130         return;
1131
1132     InspectorFrontendHost.initializeWebSocket(url);
1133 };
1134
1135 WebInspector._updateSplitConsoleHeight = function(height)
1136 {
1137     const minimumHeight = 64;
1138     const maximumHeight = window.innerHeight * 0.55;
1139
1140     height = Math.max(minimumHeight, Math.min(height, maximumHeight));
1141
1142     this.splitContentBrowser.element.style.height = height + "px";
1143 };
1144
1145 WebInspector._consoleResizerMouseDown = function(event)
1146 {
1147     if (event.button !== 0 || event.ctrlKey)
1148         return;
1149
1150     // Only start dragging if the target is one of the elements that we expect.
1151     if (!event.target.classList.contains("navigation-bar") && !event.target.classList.contains("flexible-space"))
1152         return;
1153
1154     var resizerElement = event.target;
1155     var mouseOffset = resizerElement.offsetHeight - (event.pageY - resizerElement.totalOffsetTop);
1156
1157     function dockedResizerDrag(event)
1158     {
1159         if (event.button !== 0)
1160             return;
1161
1162         var height = window.innerHeight - event.pageY - mouseOffset;
1163
1164         this._splitConsoleHeightSetting.value = height;
1165
1166         this._updateSplitConsoleHeight(height);
1167
1168         this.quickConsole.dispatchEventToListeners(WebInspector.QuickConsole.Event.DidResize);
1169     }
1170
1171     function dockedResizerDragEnd(event)
1172     {
1173         if (event.button !== 0)
1174             return;
1175
1176         this.elementDragEnd(event);
1177     }
1178
1179     this.elementDragStart(resizerElement, dockedResizerDrag.bind(this), dockedResizerDragEnd.bind(this), event, "row-resize");
1180 };
1181
1182 WebInspector._toolbarMouseDown = function(event)
1183 {
1184     if (event.ctrlKey)
1185         return;
1186
1187     if (this._dockSide === "right")
1188         return;
1189
1190     if (this.docked)
1191         this._dockedResizerMouseDown(event);
1192     else
1193         this._moveWindowMouseDown(event);
1194 };
1195
1196 WebInspector._dockedResizerMouseDown = function(event)
1197 {
1198     if (event.button !== 0 || event.ctrlKey)
1199         return;
1200
1201     if (!this.docked)
1202         return;
1203
1204     // Only start dragging if the target is one of the elements that we expect.
1205     if (event.target.id !== "docked-resizer" && !event.target.classList.contains("toolbar") &&
1206         !event.target.classList.contains("flexible-space") && !event.target.classList.contains("item-section"))
1207         return;
1208
1209     var windowProperty = this._dockSide === "bottom" ? "innerHeight" : "innerWidth";
1210     var eventScreenProperty = this._dockSide === "bottom" ? "screenY" : "screenX";
1211     var eventClientProperty = this._dockSide === "bottom" ? "clientY" : "clientX";
1212
1213     var resizerElement = event.target;
1214     var firstClientPosition = event[eventClientProperty];
1215     var lastScreenPosition = event[eventScreenProperty];
1216
1217     function dockedResizerDrag(event)
1218     {
1219         if (event.button !== 0)
1220             return;
1221
1222         var position = event[eventScreenProperty];
1223         var delta = position - lastScreenPosition;
1224         var clientPosition = event[eventClientProperty];
1225
1226         lastScreenPosition = position;
1227
1228         // If delta is positive the docked Inspector size is decreasing, in which case the cursor client position
1229         // with respect to the target cannot be less than the first mouse down position within the target.
1230         if (delta > 0 && clientPosition < firstClientPosition)
1231             return;
1232
1233         // If delta is negative the docked Inspector size is increasing, in which case the cursor client position
1234         // with respect to the target cannot be greater than the first mouse down position within the target.
1235         if (delta < 0 && clientPosition > firstClientPosition)
1236             return;
1237
1238         var dimension = Math.max(0, window[windowProperty] - delta);
1239
1240         if (this._dockSide === "bottom")
1241             InspectorFrontendHost.setAttachedWindowHeight(dimension);
1242         else
1243             InspectorFrontendHost.setAttachedWindowWidth(dimension);
1244     }
1245
1246     function dockedResizerDragEnd(event)
1247     {
1248         if (event.button !== 0)
1249             return;
1250
1251         WebInspector.elementDragEnd(event);
1252     }
1253
1254     WebInspector.elementDragStart(resizerElement, dockedResizerDrag.bind(this), dockedResizerDragEnd.bind(this), event, this._dockSide === "bottom" ? "row-resize" : "col-resize");
1255 };
1256
1257 WebInspector._moveWindowMouseDown = function(event)
1258 {
1259     console.assert(!this.docked);
1260
1261     if (event.button !== 0 || event.ctrlKey)
1262         return;
1263
1264     // Only start dragging if the target is one of the elements that we expect.
1265     if (!event.target.classList.contains("toolbar") && !event.target.classList.contains("flexible-space") &&
1266         !event.target.classList.contains("item-section"))
1267         return;
1268
1269     // Ignore dragging on the top of the toolbar on Mac where the inspector content fills the entire window.
1270     if (WebInspector.Platform.name === "mac" && WebInspector.Platform.version.release >= 10) {
1271         const windowDragHandledTitleBarHeight = 22;
1272         if (event.pageY < windowDragHandledTitleBarHeight) {
1273             event.preventDefault();
1274             return;
1275         }
1276     }
1277
1278     var lastScreenX = event.screenX;
1279     var lastScreenY = event.screenY;
1280
1281     function toolbarDrag(event)
1282     {
1283         if (event.button !== 0)
1284             return;
1285
1286         var x = event.screenX - lastScreenX;
1287         var y = event.screenY - lastScreenY;
1288
1289         InspectorFrontendHost.moveWindowBy(x, y);
1290
1291         lastScreenX = event.screenX;
1292         lastScreenY = event.screenY;
1293     }
1294
1295     function toolbarDragEnd(event)
1296     {
1297         if (event.button !== 0)
1298             return;
1299
1300         WebInspector.elementDragEnd(event);
1301     }
1302
1303     WebInspector.elementDragStart(event.target, toolbarDrag, toolbarDragEnd, event, "default");
1304 };
1305
1306 WebInspector._storageWasInspected = function(event)
1307 {
1308     // FIXME: This should show a Storage tab when we have one.
1309     this.showResourcesTab();
1310 };
1311
1312 WebInspector._domNodeWasInspected = function(event)
1313 {
1314     WebInspector.domTreeManager.highlightDOMNodeForTwoSeconds(event.data.node.id);
1315
1316     InspectorFrontendHost.bringToFront();
1317
1318     this.showMainFrameDOMTree(event.data.node);
1319 };
1320
1321 WebInspector._inspectModeStateChanged = function(event)
1322 {
1323 //    this._inspectModeToolbarButton.activated = WebInspector.domTreeManager.inspectModeEnabled;
1324 };
1325
1326 WebInspector._toggleInspectMode = function(event)
1327 {
1328     WebInspector.domTreeManager.inspectModeEnabled = !WebInspector.domTreeManager.inspectModeEnabled;
1329 };
1330
1331 WebInspector._reloadPage = function(event)
1332 {
1333     PageAgent.reload();
1334 };
1335
1336 WebInspector._reloadPageIgnoringCache = function(event)
1337 {
1338     PageAgent.reload(true);
1339 };
1340
1341 WebInspector._toggleInspectMode = function(event)
1342 {
1343     this.domTreeManager.inspectModeEnabled = !this.domTreeManager.inspectModeEnabled;
1344 };
1345
1346 WebInspector._showConsoleTab = function(event)
1347 {
1348     this.showConsoleTab();
1349 };
1350
1351 WebInspector._focusedContentView = function()
1352 {
1353     if (this.tabBrowser.element.isSelfOrAncestor(this.currentFocusElement))
1354         return this.tabBrowser.selectedTabContentView;
1355     if (this.splitContentBrowser.element.isSelfOrAncestor(this.currentFocusElement))
1356         return  this.splitContentBrowser.currentContentView;
1357     return null;
1358 };
1359
1360 WebInspector._beforecopy = function(event)
1361 {
1362     var selection = window.getSelection();
1363
1364     // If there is no selection, see if the focused element or focused ContentView can handle the copy event.
1365     if (selection.isCollapsed && !WebInspector.isEventTargetAnEditableField(event)) {
1366         var focusedCopyHandler = this.currentFocusElement && this.currentFocusElement.copyHandler;
1367         if (focusedCopyHandler && typeof focusedCopyHandler.handleBeforeCopyEvent === "function") {
1368             focusedCopyHandler.handleBeforeCopyEvent(event);
1369             if (event.defaultPrevented)
1370                 return;
1371         }
1372
1373         var focusedContentView = this._focusedContentView();
1374         if (focusedContentView && typeof focusedContentView.handleCopyEvent === "function") {
1375             event.preventDefault();
1376             return;
1377         }
1378
1379         return;
1380     }
1381
1382     if (selection.isCollapsed)
1383         return;
1384
1385     // Say we can handle it (by preventing default) to remove word break characters.
1386     event.preventDefault();
1387 };
1388
1389 WebInspector._copy = function(event)
1390 {
1391     var selection = window.getSelection();
1392
1393     // If there is no selection, pass the copy event on to the focused element or focused ContentView.
1394     if (selection.isCollapsed && !WebInspector.isEventTargetAnEditableField(event)) {
1395         var focusedCopyHandler = this.currentFocusElement && this.currentFocusElement.copyHandler;
1396         if (focusedCopyHandler && typeof focusedCopyHandler.handleCopyEvent === "function") {
1397             focusedCopyHandler.handleCopyEvent(event);
1398             if (event.defaultPrevented)
1399                 return;
1400         }
1401
1402         var focusedContentView = this._focusedContentView();
1403         if (focusedContentView && typeof focusedContentView.handleCopyEvent === "function") {
1404             focusedContentView.handleCopyEvent(event);
1405             return;
1406         }
1407
1408         return;
1409     }
1410
1411     if (selection.isCollapsed)
1412         return;
1413
1414     // Remove word break characters from the selection before putting it on the pasteboard.
1415     var selectionString = selection.toString().removeWordBreakCharacters();
1416     event.clipboardData.setData("text/plain", selectionString);
1417     event.preventDefault();
1418 };
1419
1420 WebInspector._generateDisclosureTriangleImages = function()
1421 {
1422     var specifications = {};
1423     specifications["normal"] = {fillColor: [140, 140, 140, 1]};
1424     specifications["normal-active"] = {fillColor: [128, 128, 128, 1]};
1425
1426     generateColoredImagesForCSS("Images/DisclosureTriangleSmallOpen.svg", specifications, 13, 13, "disclosure-triangle-small-open-");
1427     generateColoredImagesForCSS("Images/DisclosureTriangleSmallClosed.svg", specifications, 13, 13, "disclosure-triangle-small-closed-");
1428
1429     specifications["selected"] = {fillColor: [255, 255, 255, 1]};
1430
1431     generateColoredImagesForCSS("Images/DisclosureTriangleTinyOpen.svg", specifications, 8, 8, "disclosure-triangle-tiny-open-");
1432     generateColoredImagesForCSS("Images/DisclosureTriangleTinyClosed.svg", specifications, 8, 8, "disclosure-triangle-tiny-closed-");
1433 };
1434
1435 WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor, eventTarget)
1436 {
1437     if (WebInspector._elementDraggingEventListener || WebInspector._elementEndDraggingEventListener)
1438         WebInspector.elementDragEnd(event);
1439
1440     if (element) {
1441         // Install glass pane
1442         if (WebInspector._elementDraggingGlassPane)
1443             WebInspector._elementDraggingGlassPane.parentElement.removeChild(WebInspector._elementDraggingGlassPane);
1444
1445         var glassPane = document.createElement("div");
1446         glassPane.style.cssText = "position:absolute;top:0;bottom:0;left:0;right:0;opacity:0;z-index:1";
1447         glassPane.id = "glass-pane-for-drag";
1448         element.ownerDocument.body.appendChild(glassPane);
1449         WebInspector._elementDraggingGlassPane = glassPane;
1450     }
1451
1452     WebInspector._elementDraggingEventListener = dividerDrag;
1453     WebInspector._elementEndDraggingEventListener = elementDragEnd;
1454
1455     var targetDocument = event.target.ownerDocument;
1456
1457     WebInspector._elementDraggingEventTarget = eventTarget || targetDocument;
1458     WebInspector._elementDraggingEventTarget.addEventListener("mousemove", dividerDrag, true);
1459     WebInspector._elementDraggingEventTarget.addEventListener("mouseup", elementDragEnd, true);
1460
1461     targetDocument.body.style.cursor = cursor;
1462
1463     event.preventDefault();
1464 };
1465
1466 WebInspector.elementDragEnd = function(event)
1467 {
1468     WebInspector._elementDraggingEventTarget.removeEventListener("mousemove", WebInspector._elementDraggingEventListener, true);
1469     WebInspector._elementDraggingEventTarget.removeEventListener("mouseup", WebInspector._elementEndDraggingEventListener, true);
1470
1471     event.target.ownerDocument.body.style.removeProperty("cursor");
1472
1473     if (WebInspector._elementDraggingGlassPane)
1474         WebInspector._elementDraggingGlassPane.parentElement.removeChild(WebInspector._elementDraggingGlassPane);
1475
1476     delete WebInspector._elementDraggingGlassPane;
1477     delete WebInspector._elementDraggingEventTarget;
1478     delete WebInspector._elementDraggingEventListener;
1479     delete WebInspector._elementEndDraggingEventListener;
1480
1481     event.preventDefault();
1482 };
1483
1484 WebInspector.createMessageTextView = function(message, isError)
1485 {
1486     var messageElement = document.createElement("div");
1487     messageElement.className = "message-text-view";
1488     if (isError)
1489         messageElement.classList.add("error");
1490
1491     messageElement.textContent = message;
1492
1493     return messageElement;
1494 };
1495
1496 WebInspector.createGoToArrowButton = function()
1497 {
1498     if (!WebInspector._generatedGoToArrowButtonImages) {
1499         WebInspector._generatedGoToArrowButtonImages = true;
1500
1501         var specifications = {};
1502         specifications["go-to-arrow-normal"] = {fillColor: [0, 0, 0, 0.5]};
1503         specifications["go-to-arrow-normal-active"] = {fillColor: [0, 0, 0, 0.7]};
1504         specifications["go-to-arrow-selected"] = {fillColor: [255, 255, 255, 0.8]};
1505         specifications["go-to-arrow-selected-active"] = {fillColor: [255, 255, 255, 1]};
1506
1507         generateColoredImagesForCSS("Images/GoToArrow.svg", specifications, 10, 10);
1508     }
1509
1510     function stopPropagation(event)
1511     {
1512         event.stopPropagation()
1513     }
1514
1515     var button = document.createElement("button");
1516     button.addEventListener("mousedown", stopPropagation, true);
1517     button.className = "go-to-arrow";
1518     button.tabIndex = -1;
1519     return button;
1520 };
1521
1522 WebInspector.createSourceCodeLocationLink = function(sourceCodeLocation, dontFloat, useGoToArrowButton)
1523 {
1524     console.assert(sourceCodeLocation);
1525     if (!sourceCodeLocation)
1526         return null;
1527
1528     function showSourceCodeLocation(event)
1529     {
1530         event.stopPropagation();
1531         event.preventDefault();
1532
1533         if (event.metaKey)
1534             this.showOriginalUnformattedSourceCodeLocation(sourceCodeLocation);
1535         else
1536             this.showSourceCodeLocation(sourceCodeLocation);
1537     }
1538
1539     var linkElement = document.createElement("a");
1540     linkElement.className = "go-to-link";
1541     linkElement.addEventListener("click", showSourceCodeLocation.bind(this));
1542     sourceCodeLocation.populateLiveDisplayLocationTooltip(linkElement);
1543
1544     if (useGoToArrowButton)
1545         linkElement.appendChild(WebInspector.createGoToArrowButton());
1546     else
1547         sourceCodeLocation.populateLiveDisplayLocationString(linkElement, "textContent");
1548
1549     if (dontFloat)
1550         linkElement.classList.add("dont-float");
1551
1552     return linkElement;
1553 };
1554
1555 WebInspector.linkifyLocation = function(url, lineNumber, columnNumber, className)
1556 {
1557     var sourceCode = WebInspector.frameResourceManager.resourceForURL(url);
1558     if (!sourceCode) {
1559         sourceCode = WebInspector.debuggerManager.scriptsForURL(url)[0];
1560         if (sourceCode)
1561             sourceCode = sourceCode.resource || sourceCode;
1562     }
1563
1564     if (!sourceCode) {
1565         var anchor = document.createElement("a");
1566         anchor.href  = url;
1567         anchor.lineNumber = lineNumber;
1568         if (className)
1569             anchor.className = className;
1570         anchor.appendChild(document.createTextNode(WebInspector.displayNameForURL(url) + ":" + lineNumber));
1571         return anchor;
1572     }
1573
1574     var sourceCodeLocation = sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
1575     var linkElement = WebInspector.createSourceCodeLocationLink(sourceCodeLocation, true);
1576     if (className)
1577         linkElement.classList.add(className);
1578     return linkElement;
1579 };
1580
1581 WebInspector.linkifyURLAsNode = function(url, linkText, classes, tooltipText)
1582 {
1583     if (!linkText)
1584         linkText = url;
1585
1586     classes = (classes ? classes + " " : "");
1587
1588     var a = document.createElement("a");
1589     a.href = url;
1590     a.className = classes;
1591
1592     if (tooltipText === undefined)
1593         a.title = url;
1594     else if (typeof tooltipText !== "string" || tooltipText.length)
1595         a.title = tooltipText;
1596
1597     a.textContent = linkText;
1598     a.style.maxWidth = "100%";
1599
1600     return a;
1601 };
1602
1603 WebInspector.linkifyStringAsFragmentWithCustomLinkifier = function(string, linkifier)
1604 {
1605     var container = document.createDocumentFragment();
1606     var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/;
1607     var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
1608
1609     while (string) {
1610         var linkString = linkStringRegEx.exec(string);
1611         if (!linkString)
1612             break;
1613
1614         linkString = linkString[0];
1615         var linkIndex = string.indexOf(linkString);
1616         var nonLink = string.substring(0, linkIndex);
1617         container.appendChild(document.createTextNode(nonLink));
1618
1619         var title = linkString;
1620         var realURL = (linkString.startsWith("www.") ? "http://" + linkString : linkString);
1621         var lineColumnMatch = lineColumnRegEx.exec(realURL);
1622         if (lineColumnMatch)
1623             realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length);
1624
1625         var linkNode = linkifier(title, realURL, lineColumnMatch ? lineColumnMatch[1] : undefined);
1626         container.appendChild(linkNode);
1627         string = string.substring(linkIndex + linkString.length, string.length);
1628     }
1629
1630     if (string)
1631         container.appendChild(document.createTextNode(string));
1632
1633     return container;
1634 };
1635
1636 WebInspector.linkifyStringAsFragment = function(string)
1637 {
1638     function linkifier(title, url, lineNumber)
1639     {
1640         var urlNode = WebInspector.linkifyURLAsNode(url, title, undefined);
1641         if (lineNumber !== undefined)
1642             urlNode.lineNumber = lineNumber;
1643
1644         return urlNode;
1645     }
1646
1647     return WebInspector.linkifyStringAsFragmentWithCustomLinkifier(string, linkifier);
1648 };
1649
1650 WebInspector._undoKeyboardShortcut = function(event)
1651 {
1652     if (!this.isEditingAnyField() && !this.isEventTargetAnEditableField(event)) {
1653         this.undo();
1654         event.preventDefault();
1655     }
1656 };
1657
1658 WebInspector._redoKeyboardShortcut = function(event)
1659 {
1660     if (!this.isEditingAnyField() && !this.isEventTargetAnEditableField(event)) {
1661         this.redo();
1662         event.preventDefault();
1663     }
1664 };
1665
1666 WebInspector.undo = function()
1667 {
1668     DOMAgent.undo();
1669 };
1670
1671 WebInspector.redo = function()
1672 {
1673     DOMAgent.redo();
1674 };
1675
1676 WebInspector.highlightRangesWithStyleClass = function(element, resultRanges, styleClass, changes)
1677 {
1678     changes = changes || [];
1679     var highlightNodes = [];
1680     var lineText = element.textContent;
1681     var ownerDocument = element.ownerDocument;
1682     var textNodeSnapshot = ownerDocument.evaluate(".//text()", element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1683
1684     var snapshotLength = textNodeSnapshot.snapshotLength;
1685     if (snapshotLength === 0)
1686         return highlightNodes;
1687
1688     var nodeRanges = [];
1689     var rangeEndOffset = 0;
1690     for (var i = 0; i < snapshotLength; ++i) {
1691         var range = {};
1692         range.offset = rangeEndOffset;
1693         range.length = textNodeSnapshot.snapshotItem(i).textContent.length;
1694         rangeEndOffset = range.offset + range.length;
1695         nodeRanges.push(range);
1696     }
1697
1698     var startIndex = 0;
1699     for (var i = 0; i < resultRanges.length; ++i) {
1700         var startOffset = resultRanges[i].offset;
1701         var endOffset = startOffset + resultRanges[i].length;
1702
1703         while (startIndex < snapshotLength && nodeRanges[startIndex].offset + nodeRanges[startIndex].length <= startOffset)
1704             startIndex++;
1705         var endIndex = startIndex;
1706         while (endIndex < snapshotLength && nodeRanges[endIndex].offset + nodeRanges[endIndex].length < endOffset)
1707             endIndex++;
1708         if (endIndex === snapshotLength)
1709             break;
1710
1711         var highlightNode = ownerDocument.createElement("span");
1712         highlightNode.className = styleClass;
1713         highlightNode.textContent = lineText.substring(startOffset, endOffset);
1714
1715         var lastTextNode = textNodeSnapshot.snapshotItem(endIndex);
1716         var lastText = lastTextNode.textContent;
1717         lastTextNode.textContent = lastText.substring(endOffset - nodeRanges[endIndex].offset);
1718         changes.push({ node: lastTextNode, type: "changed", oldText: lastText, newText: lastTextNode.textContent });
1719
1720         if (startIndex === endIndex) {
1721             lastTextNode.parentElement.insertBefore(highlightNode, lastTextNode);
1722             changes.push({ node: highlightNode, type: "added", nextSibling: lastTextNode, parent: lastTextNode.parentElement });
1723             highlightNodes.push(highlightNode);
1724
1725             var prefixNode = ownerDocument.createTextNode(lastText.substring(0, startOffset - nodeRanges[startIndex].offset));
1726             lastTextNode.parentElement.insertBefore(prefixNode, highlightNode);
1727             changes.push({ node: prefixNode, type: "added", nextSibling: highlightNode, parent: lastTextNode.parentElement });
1728         } else {
1729             var firstTextNode = textNodeSnapshot.snapshotItem(startIndex);
1730             var firstText = firstTextNode.textContent;
1731             var anchorElement = firstTextNode.nextSibling;
1732
1733             firstTextNode.parentElement.insertBefore(highlightNode, anchorElement);
1734             changes.push({ node: highlightNode, type: "added", nextSibling: anchorElement, parent: firstTextNode.parentElement });
1735             highlightNodes.push(highlightNode);
1736
1737             firstTextNode.textContent = firstText.substring(0, startOffset - nodeRanges[startIndex].offset);
1738             changes.push({ node: firstTextNode, type: "changed", oldText: firstText, newText: firstTextNode.textContent });
1739
1740             for (var j = startIndex + 1; j < endIndex; j++) {
1741                 var textNode = textNodeSnapshot.snapshotItem(j);
1742                 var text = textNode.textContent;
1743                 textNode.textContent = "";
1744                 changes.push({ node: textNode, type: "changed", oldText: text, newText: textNode.textContent });
1745             }
1746         }
1747         startIndex = endIndex;
1748         nodeRanges[startIndex].offset = endOffset;
1749         nodeRanges[startIndex].length = lastTextNode.textContent.length;
1750
1751     }
1752     return highlightNodes;
1753 };
1754
1755 WebInspector.revertDomChanges = function(domChanges)
1756 {
1757     for (var i = domChanges.length - 1; i >= 0; --i) {
1758         var entry = domChanges[i];
1759         switch (entry.type) {
1760         case "added":
1761             if (entry.node.parentElement)
1762                 entry.node.parentElement.removeChild(entry.node);
1763             break;
1764         case "changed":
1765             entry.node.textContent = entry.oldText;
1766             break;
1767         }
1768     }
1769 };
1770
1771 WebInspector.archiveMainFrame = function()
1772 {
1773     this.notifications.dispatchEventToListeners(WebInspector.Notification.PageArchiveStarted, event);
1774
1775     PageAgent.archive(function(error, data) {
1776         this.notifications.dispatchEventToListeners(WebInspector.Notification.PageArchiveEnded, event);
1777         if (error)
1778             return;
1779
1780         var mainFrame = WebInspector.frameResourceManager.mainFrame;
1781         var archiveName = mainFrame.mainResource.urlComponents.host || mainFrame.mainResource.displayName || "Archive";
1782         var url = "web-inspector:///" + encodeURI(archiveName) + ".webarchive";
1783         InspectorFrontendHost.save(url, data, true, true);
1784     }.bind(this));
1785 };
1786
1787 WebInspector.canArchiveMainFrame = function()
1788 {
1789     if (!PageAgent.archive || this.debuggableType !== WebInspector.DebuggableType.Web)
1790         return false;
1791
1792     return WebInspector.Resource.typeFromMIMEType(WebInspector.frameResourceManager.mainFrame.mainResource.mimeType) === WebInspector.Resource.Type.Document;
1793 };
1794
1795 WebInspector.addWindowKeydownListener = function(listener)
1796 {
1797     if (typeof listener.handleKeydownEvent !== "function")
1798         return;
1799
1800     this._windowKeydownListeners.push(listener);
1801
1802     this._updateWindowKeydownListener();
1803 };
1804
1805 WebInspector.removeWindowKeydownListener = function(listener)
1806 {
1807     this._windowKeydownListeners.remove(listener);
1808
1809     this._updateWindowKeydownListener();
1810 };
1811
1812 WebInspector._updateWindowKeydownListener = function()
1813 {
1814     if (this._windowKeydownListeners.length > 0)
1815         window.addEventListener("keydown", WebInspector._sharedWindowKeydownListener, true);
1816     else
1817         window.removeEventListener("keydown", WebInspector._sharedWindowKeydownListener, true);
1818 };
1819
1820 WebInspector._sharedWindowKeydownListener = function(event)
1821 {
1822     for (var i = WebInspector._windowKeydownListeners.length - 1; i >= 0; --i) {
1823         if (WebInspector._windowKeydownListeners[i].handleKeydownEvent(event)) {
1824             event.stopImmediatePropagation();
1825             event.preventDefault();
1826             break;
1827         }
1828     }
1829 };