Web Inspector: Make showing a content view work in the tab world
[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.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.MainFrameDidChange, this._mainFrameDidChange, this);
131     this.frameResourceManager.addEventListener(WebInspector.FrameResourceManager.Event.FrameWasAdded, this._frameWasAdded, this);
132
133     WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
134
135     document.addEventListener("DOMContentLoaded", this.contentLoaded.bind(this));
136
137     // Create settings.
138     this._lastInspectorViewStateCookieSetting = new WebInspector.Setting("last-content-view-state-cookie", {});
139
140     this._navigationSidebarCollapsedSetting = new WebInspector.Setting("navigation-sidebar-collapsed", false);
141     this._navigationSidebarWidthSetting = new WebInspector.Setting("navigation-sidebar-width", null);
142
143     this._lastSelectedDetailsSidebarPanelSetting = new WebInspector.Setting("last-selected-details-sidebar-panel", null);
144     this._detailsSidebarCollapsedSetting = new WebInspector.Setting("details-sidebar-collapsed", true);
145     this._detailsSidebarWidthSetting = new WebInspector.Setting("details-sidebar-width", null);
146
147     this._toolbarDockedRightDisplayModeSetting = new WebInspector.Setting("toolbar-docked-right-display-mode", WebInspector.Toolbar.DisplayMode.IconAndLabelVertical);
148     this._toolbarDockedRightSizeModeSetting = new WebInspector.Setting("toolbar-docked-right-size-mode",WebInspector.Toolbar.SizeMode.Normal);
149
150     this._toolbarDockedBottomDisplayModeSetting = new WebInspector.Setting("toolbar-docked-display-mode", WebInspector.Toolbar.DisplayMode.IconAndLabelHorizontal);
151     this._toolbarDockedBottomSizeModeSetting = new WebInspector.Setting("toolbar-docked-size-mode",WebInspector.Toolbar.SizeMode.Small);
152
153     this._toolbarUndockedDisplayModeSetting = new WebInspector.Setting("toolbar-undocked-display-mode", WebInspector.Toolbar.DisplayMode.IconAndLabelVertical);
154     this._toolbarUndockedSizeModeSetting = new WebInspector.Setting("toolbar-undocked-size-mode",WebInspector.Toolbar.SizeMode.Normal);
155
156     this._showingSplitConsoleSetting = new WebInspector.Setting("showing-split-console", false);
157     this._splitConsoleHeightSetting = new WebInspector.Setting("split-console-height", 150);
158
159     this._dockButtonToggledSetting = new WebInspector.Setting("dock-button-toggled", false);
160
161     this.showShadowDOMSetting = new WebInspector.Setting("show-shadow-dom", false);
162     this.showReplayInterfaceSetting = new WebInspector.Setting("show-web-replay", false);
163
164     this.showJavaScriptTypeInformationSetting = new WebInspector.Setting("show-javascript-type-information", false);
165     if (this.showJavaScriptTypeInformationSetting.value && window.RuntimeAgent && RuntimeAgent.enableTypeProfiler)
166         RuntimeAgent.enableTypeProfiler();
167
168     this.showPaintRectsSetting = new WebInspector.Setting("show-paint-rects", false);
169     if (this.showPaintRectsSetting.value && window.PageAgent && PageAgent.setShowPaintRects)
170         PageAgent.setShowPaintRects(true);
171
172     this.mouseCoords = {
173         x: 0,
174         y: 0
175     };
176
177     this._windowKeydownListeners = [];
178 };
179
180 WebInspector.contentLoaded = function()
181 {
182     // Register for global events.
183     document.addEventListener("beforecopy", this._beforecopy.bind(this));
184     document.addEventListener("copy", this._copy.bind(this));
185
186     document.addEventListener("click", this._mouseWasClicked.bind(this));
187     document.addEventListener("dragover", this._dragOver.bind(this));
188     document.addEventListener("focus", WebInspector._focusChanged.bind(this), true);
189
190     window.addEventListener("focus", this._windowFocused.bind(this));
191     window.addEventListener("blur", this._windowBlurred.bind(this));
192     window.addEventListener("resize", this._windowResized.bind(this));
193     window.addEventListener("keydown", this._windowKeyDown.bind(this));
194     window.addEventListener("keyup", this._windowKeyUp.bind(this));
195     window.addEventListener("mousemove", this._mouseMoved.bind(this), true);
196     window.addEventListener("pagehide", this._pageHidden.bind(this));
197
198     // Add platform style classes so the UI can be tweaked per-platform.
199     document.body.classList.add(WebInspector.Platform.name + "-platform");
200     if (WebInspector.Platform.isNightlyBuild)
201         document.body.classList.add("nightly-build");
202     if (WebInspector.Platform.isLegacyMacOS)
203         document.body.classList.add("legacy");
204     if (WebInspector.Platform.version.name)
205         document.body.classList.add(WebInspector.Platform.version.name);
206
207     document.body.classList.add(this.debuggableType);
208
209     // Create the user interface elements.
210     this.toolbar = new WebInspector.Toolbar(document.getElementById("toolbar"));
211     this.toolbar.addEventListener(WebInspector.Toolbar.Event.DisplayModeDidChange, this._toolbarDisplayModeDidChange, this);
212     this.toolbar.addEventListener(WebInspector.Toolbar.Event.SizeModeDidChange, this._toolbarSizeModeDidChange, this);
213
214     var contentElement = document.getElementById("content");
215     contentElement.setAttribute("role", "main");
216     contentElement.setAttribute("aria-label", WebInspector.UIString("Content"));
217
218     this.contentBrowser = new WebInspector.ContentBrowser(document.getElementById("content-browser"), this);
219     this.contentBrowser.addEventListener(WebInspector.ContentBrowser.Event.CurrentRepresentedObjectsDidChange, this._contentBrowserRepresentedObjectsDidChange, this);
220     this.contentBrowser.addEventListener(WebInspector.ContentBrowser.Event.CurrentContentViewDidChange, this._contentBrowserCurrentContentViewDidChange, this);
221
222     this.splitContentBrowser = new WebInspector.ContentBrowser(document.getElementById("split-content-browser"), this, true);
223     this.splitContentBrowser.navigationBar.element.addEventListener("mousedown", this._consoleResizerMouseDown.bind(this));
224
225     this.quickConsole = new WebInspector.QuickConsole(document.getElementById("quick-console"));
226     this.quickConsole.addEventListener(WebInspector.QuickConsole.Event.DidResize, this._quickConsoleDidResize, this);
227
228     this._consoleRepresentedObject = new WebInspector.LogObject;
229     this._consoleTreeElement = new WebInspector.LogTreeElement(this._consoleRepresentedObject);
230     this.consoleContentView = WebInspector.contentBrowser.contentViewForRepresentedObject(this._consoleRepresentedObject);
231     this.consoleLogViewController = this.consoleContentView.logViewController;
232
233     // FIXME: The sidebars should be flipped in RTL languages.
234     this.leftSidebar = this.navigationSidebar = new WebInspector.Sidebar(document.getElementById("navigation-sidebar"), WebInspector.Sidebar.Sides.Left);
235     this.navigationSidebar.addEventListener(WebInspector.Sidebar.Event.CollapsedStateDidChange, this._sidebarCollapsedStateDidChange, this);
236     this.navigationSidebar.addEventListener(WebInspector.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
237     this.navigationSidebar.addEventListener(WebInspector.Sidebar.Event.SidebarPanelSelected, this._navigationSidebarPanelSelected, this);
238
239     this.rightSidebar = this.detailsSidebar = new WebInspector.Sidebar(document.getElementById("details-sidebar"), WebInspector.Sidebar.Sides.Right, null, null, WebInspector.UIString("Details"));
240     this.detailsSidebar.addEventListener(WebInspector.Sidebar.Event.CollapsedStateDidChange, this._sidebarCollapsedStateDidChange, this);
241     this.detailsSidebar.addEventListener(WebInspector.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
242     this.detailsSidebar.addEventListener(WebInspector.Sidebar.Event.SidebarPanelSelected, this._detailsSidebarPanelSelected, this);
243
244     this.navigationSidebarKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "0", this.toggleNavigationSidebar.bind(this));
245     this.detailsSidebarKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Option, "0", this.toggleDetailsSidebar.bind(this));
246
247     this.tabBrowser = new WebInspector.TabBrowser(document.getElementById("tab-browser"), this.tabBar, this.navigationSidebar, this.detailsSidebar);
248     this.tabBrowser.addEventListener(WebInspector.TabBrowser.Event.SelectedTabContentViewDidChange, this._tabBrowserSelectedTabContentViewDidChange, this);
249
250     this._reloadPageKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "R", this._reloadPage.bind(this));
251     this._reloadPageIgnoringCacheKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "R", this._reloadPageIgnoringCache.bind(this));
252
253     this._consoleKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Option | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "C", this.toggleConsoleView.bind(this));
254
255     this._inspectModeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "C", this._toggleInspectMode.bind(this));
256
257     this._undoKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Z", this._undoKeyboardShortcut.bind(this));
258     this._redoKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl | WebInspector.KeyboardShortcut.Modifier.Shift, "Z", this._redoKeyboardShortcut.bind(this));
259     this._undoKeyboardShortcut.implicitlyPreventsDefault = this._redoKeyboardShortcut.implicitlyPreventsDefault = false;
260
261     this.toggleBreakpointsKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this.debuggerToggleBreakpoints.bind(this));
262     this.pauseOrResumeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this.debuggerPauseResumeToggle.bind(this));
263     this.stepOverKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F6, this.debuggerStepOver.bind(this));
264     this.stepIntoKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F7, this.debuggerStepInto.bind(this));
265     this.stepOutKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F8, this.debuggerStepOut.bind(this));
266
267     this.pauseOrResumeAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Backslash, this.debuggerPauseResumeToggle.bind(this));
268     this.stepOverAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.SingleQuote, this.debuggerStepOver.bind(this));
269     this.stepIntoAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this.debuggerStepInto.bind(this));
270     this.stepOutAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Shift | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this.debuggerStepOut.bind(this));
271
272     this.undockButtonNavigationItem = new WebInspector.ToggleControlToolbarItem("undock", WebInspector.UIString("Detach into separate window"), "", platformImagePath("Undock.svg"), "", 16, 14);
273     this.undockButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._undock, this);
274
275     this.closeButtonNavigationItem = new WebInspector.ControlToolbarItem("dock-close", WebInspector.UIString("Close"), platformImagePath("Close.svg"), 16, 14);
276     this.closeButtonNavigationItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this.close, this);
277
278     this.toolbar.addToolbarItem(this.closeButtonNavigationItem, WebInspector.Toolbar.Section.Control);
279     this.toolbar.addToolbarItem(this.undockButtonNavigationItem, WebInspector.Toolbar.Section.Control);
280
281     this.resourceSidebarPanel = new WebInspector.ResourceSidebarPanel;
282     this.timelineSidebarPanel = new WebInspector.TimelineSidebarPanel;
283     this.debuggerSidebarPanel = new WebInspector.DebuggerSidebarPanel;
284
285     this.navigationSidebar.addSidebarPanel(this.resourceSidebarPanel);
286     // FIXME: Enable timelines panel for JavaScript inspection.
287     if (this.debuggableType !== WebInspector.DebuggableType.JavaScript)
288         this.navigationSidebar.addSidebarPanel(this.timelineSidebarPanel);
289     this.navigationSidebar.addSidebarPanel(this.debuggerSidebarPanel);
290
291     this.toolbar.addToolbarItem(this.resourceSidebarPanel.toolbarItem, WebInspector.Toolbar.Section.Left);
292     // FIXME: Enable timelines panel for JavaScript inspection.
293     if (this.debuggableType !== WebInspector.DebuggableType.JavaScript)
294         this.toolbar.addToolbarItem(this.timelineSidebarPanel.toolbarItem, WebInspector.Toolbar.Section.Left);
295     this.toolbar.addToolbarItem(this.debuggerSidebarPanel.toolbarItem, WebInspector.Toolbar.Section.Left);
296
297     // The toolbar button for the console.
298     var toolTip = WebInspector.UIString("Show console (%s)").format(WebInspector._consoleKeyboardShortcut.displayName);
299     var activatedToolTip = WebInspector.UIString("Hide console (%s)").format(WebInspector._consoleKeyboardShortcut.displayName);
300     this._consoleToolbarButton = new WebInspector.ActivateButtonToolbarItem("console", toolTip, activatedToolTip, WebInspector.UIString("Console"), "Images/NavigationItemLog.svg");
301     this._consoleToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this.toggleConsoleView, this);
302     this.toolbar.addToolbarItem(this._consoleToolbarButton, WebInspector.Toolbar.Section.Center);
303
304     this.dashboardContainer = new WebInspector.DashboardContainerView;
305     this.dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.default);
306
307     this.toolbar.addToolbarItem(this.dashboardContainer.toolbarItem, WebInspector.Toolbar.Section.Center);
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, WebInspector.UIString("Inspect"), "Images/Crosshair.svg");
314         this._inspectModeToolbarButton.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._toggleInspectMode, this);
315         this.toolbar.addToolbarItem(this._inspectModeToolbarButton, WebInspector.Toolbar.Section.Center);
316     }
317
318     this.resourceDetailsSidebarPanel = new WebInspector.ResourceDetailsSidebarPanel;
319     this.domNodeDetailsSidebarPanel = new WebInspector.DOMNodeDetailsSidebarPanel;
320     this.cssStyleDetailsSidebarPanel = new WebInspector.CSSStyleDetailsSidebarPanel;
321     this.applicationCacheDetailsSidebarPanel = new WebInspector.ApplicationCacheDetailsSidebarPanel;
322     this.scopeChainDetailsSidebarPanel = new WebInspector.ScopeChainDetailsSidebarPanel;
323     this.probeDetailsSidebarPanel = new WebInspector.ProbeDetailsSidebarPanel;
324     this.renderingFrameDetailsSidebarPanel = new WebInspector.RenderingFrameDetailsSidebarPanel;
325
326     this.detailsSidebarPanels = [this.resourceDetailsSidebarPanel, this.applicationCacheDetailsSidebarPanel, this.scopeChainDetailsSidebarPanel,
327         this.domNodeDetailsSidebarPanel, this.cssStyleDetailsSidebarPanel, this.probeDetailsSidebarPanel, this.renderingFrameDetailsSidebarPanel];
328
329     if (window.LayerTreeAgent) {
330         this.layerTreeDetailsSidebarPanel = new WebInspector.LayerTreeDetailsSidebarPanel;
331         this.detailsSidebarPanels.splice(this.detailsSidebarPanels.length - 1, 0, this.layerTreeDetailsSidebarPanel);
332     }
333
334     this.modifierKeys = {altKey: false, metaKey: false, shiftKey: false};
335
336     // Add the items in reverse order since the last items appear and disappear the least. So they
337     // will not cause the other buttons to visually shift around, keeping things more stable.
338     for (var i = this.detailsSidebarPanels.length - 1; i >= 0; --i) {
339         var toolbarItem = this.detailsSidebarPanels[i].toolbarItem;
340         toolbarItem.hidden = true;
341         this.toolbar.addToolbarItem(toolbarItem, WebInspector.Toolbar.Section.Right);
342     }
343
344     this.toolbar.element.addEventListener("mousedown", this._toolbarMouseDown.bind(this));
345     document.getElementById("docked-resizer").addEventListener("mousedown", this._dockedResizerMouseDown.bind(this));
346
347     this._updateDockNavigationItems();
348     this._updateToolbarHeight();
349
350     if (this._navigationSidebarWidthSetting.value)
351         this.navigationSidebar.width = this._navigationSidebarWidthSetting.value;
352
353     if (this._detailsSidebarWidthSetting.value)
354         this.detailsSidebar.width = this._detailsSidebarWidthSetting.value;
355
356     // Signal that the frontend is now ready to receive messages.
357     InspectorFrontendAPI.loadCompleted();
358
359     // Tell the InspectorFrontendHost we loaded, which causes the window to display
360     // and pending InspectorFrontendAPI commands to be sent.
361     InspectorFrontendHost.loaded();
362
363     // Set collapsed after loading the pending frontend commands are dispatched so only the final
364     // selected sidebar panel gets shown and has a say in what content view gets shown.
365     this.navigationSidebar.collapsed = this.navigationSidebar.selectedSidebarPanel ? this._navigationSidebarCollapsedSetting.value : true;
366
367     // If InspectorFrontendAPI didn't show a content view, then try to restore the last saved view state.
368     if (!this.contentBrowser.currentContentView && !this.ignoreLastContentCookie)
369         this._restoreInspectorViewStateFromCookie(this._lastInspectorViewStateCookieSetting.value);
370
371     this._updateSplitConsoleHeight(this._splitConsoleHeightSetting.value);
372
373     if (this._showingSplitConsoleSetting.value)
374         this.showSplitConsole();
375
376     this._contentLoaded = true;
377
378     this.runBootstrapOperations();
379 };
380
381 WebInspector.activateExtraDomains = function(domains)
382 {
383     this.hasExtraDomains = true;
384
385     for (var domain of domains) {
386         var agent = InspectorBackend.activateDomain(domain);
387         if (agent.enable)
388             agent.enable();
389     }
390
391     this.notifications.dispatchEventToListeners(WebInspector.Notification.ExtraDomainsActivated, {"domains": domains});
392
393     WebInspector.CSSCompletions.requestCSSNameCompletions();
394 };
395
396 WebInspector.sidebarPanelForCurrentContentView = function()
397 {
398     var currentContentView = this.contentBrowser.currentContentView;
399     if (!currentContentView)
400         return null;
401     return this.sidebarPanelForRepresentedObject(currentContentView.representedObject);
402 };
403
404 WebInspector.sidebarPanelForRepresentedObject = function(representedObject)
405 {
406     if (representedObject instanceof WebInspector.Frame || representedObject instanceof WebInspector.Resource ||
407         representedObject instanceof WebInspector.Script || representedObject instanceof WebInspector.ContentFlow)
408         return this.resourceSidebarPanel;
409
410     if (representedObject instanceof WebInspector.DOMStorageObject || representedObject instanceof WebInspector.CookieStorageObject ||
411         representedObject instanceof WebInspector.DatabaseTableObject || representedObject instanceof WebInspector.DatabaseObject ||
412         representedObject instanceof WebInspector.ApplicationCacheFrame || representedObject instanceof WebInspector.IndexedDatabaseObjectStore ||
413         representedObject instanceof WebInspector.IndexedDatabaseObjectStoreIndex)
414         return this.resourceSidebarPanel;
415
416     if (representedObject instanceof WebInspector.TimelineRecording)
417         return this.timelineSidebarPanel;
418
419     // The console does not have a sidebar.
420     if (representedObject instanceof WebInspector.LogObject)
421         return null;
422
423     console.error("Unknown representedObject: ", representedObject);
424     return null;
425 };
426
427 WebInspector.contentBrowserTreeElementForRepresentedObject = function(contentBrowser, representedObject)
428 {
429     // The console does not have a sidebar, so return a tree element here so something is shown.
430     if (representedObject instanceof WebInspector.LogObject)
431         return this._consoleTreeElement;
432
433     var sidebarPanel = this.sidebarPanelForRepresentedObject(representedObject);
434     if (sidebarPanel)
435         return sidebarPanel.treeElementForRepresentedObject(representedObject);
436     return null;
437 };
438
439 WebInspector.updateWindowTitle = function()
440 {
441     var mainFrame = this.frameResourceManager.mainFrame;
442     console.assert(mainFrame);
443
444     var urlComponents = mainFrame.mainResource.urlComponents;
445
446     var lastPathComponent;
447     try {
448         lastPathComponent = decodeURIComponent(urlComponents.lastPathComponent || "");
449     } catch (e) {
450         lastPathComponent = urlComponents.lastPathComponent;
451     }
452
453     // Build a title based on the URL components.
454     if (urlComponents.host && lastPathComponent)
455         var title = this.displayNameForHost(urlComponents.host) + " \u2014 " + lastPathComponent;
456     else if (urlComponents.host)
457         var title = this.displayNameForHost(urlComponents.host);
458     else if (lastPathComponent)
459         var title = lastPathComponent;
460     else
461         var title = mainFrame.url;
462
463     // The name "inspectedURLChanged" sounds like the whole URL is required, however this is only
464     // used for updating the window title and it can be any string.
465     InspectorFrontendHost.inspectedURLChanged(title);
466 };
467
468 WebInspector.updateDockedState = function(side)
469 {
470     if (this._dockSide === side)
471         return;
472
473     this._dockSide = side;
474
475     this.docked = side !== "undocked";
476
477     this._ignoreToolbarModeDidChangeEvents = true;
478
479     if (side === "bottom") {
480         document.body.classList.add("docked", "bottom");
481         document.body.classList.remove("window-inactive", "right");
482
483         this.toolbar.displayMode = this._toolbarDockedBottomDisplayModeSetting.value;
484         this.toolbar.sizeMode = this._toolbarDockedBottomSizeModeSetting.value;
485     } else if (side === "right") {
486         document.body.classList.add("docked", "right");
487         document.body.classList.remove("window-inactive", "bottom");
488
489         this.toolbar.displayMode = this._toolbarDockedRightDisplayModeSetting.value;
490         this.toolbar.sizeMode = this._toolbarDockedRightSizeModeSetting.value;
491     } else {
492         document.body.classList.remove("docked", "right", "bottom");
493
494         this.toolbar.displayMode = this._toolbarUndockedDisplayModeSetting.value;
495         this.toolbar.sizeMode = this._toolbarUndockedSizeModeSetting.value;
496     }
497
498     this._ignoreToolbarModeDidChangeEvents = false;
499
500     this._updateDockNavigationItems();
501     this._updateToolbarHeight();
502 };
503
504 WebInspector.handlePossibleLinkClick = function(event, frame, alwaysOpenExternally)
505 {
506     var anchorElement = event.target.enclosingNodeOrSelfWithNodeName("a");
507     if (!anchorElement || !anchorElement.href)
508         return false;
509
510     if (WebInspector.isBeingEdited(anchorElement)) {
511         // Don't follow the link when it is being edited.
512         return false;
513     }
514
515     // Prevent the link from navigating, since we don't do any navigation by following links normally.
516     event.preventDefault();
517     event.stopPropagation();
518
519     this.openURL(anchorElement.href, frame, false, anchorElement.lineNumber);
520
521     return true;
522 };
523
524 WebInspector.openURL = function(url, frame, alwaysOpenExternally, lineNumber)
525 {
526     console.assert(url);
527     if (!url)
528         return;
529
530     // If alwaysOpenExternally is not defined, base it off the command/meta key for the current event.
531     if (alwaysOpenExternally === undefined || alwaysOpenExternally === null)
532         alwaysOpenExternally = window.event ? window.event.metaKey : false;
533
534     if (alwaysOpenExternally) {
535         InspectorFrontendHost.openInNewTab(url);
536         return;
537     }
538
539     var searchChildFrames = false;
540     if (!frame) {
541         frame = this.frameResourceManager.mainFrame;
542         searchChildFrames = true;
543     }
544
545     console.assert(frame);
546
547     // WebInspector.Frame.resourceForURL does not check the main resource, only sub-resources. So check both.
548     var resource = frame.url === url ? frame.mainResource : frame.resourceForURL(url, searchChildFrames);
549     if (resource) {
550         var position = new WebInspector.SourceCodePosition(lineNumber, 0);
551         this.showSourceCode(resource, position);
552         return;
553     }
554
555     InspectorFrontendHost.openInNewTab(url);
556 };
557
558 WebInspector.close = function()
559 {
560     if (this._isClosing)
561         return;
562
563     this._isClosing = true;
564
565     InspectorFrontendHost.closeWindow();
566 };
567
568 WebInspector.isConsoleFocused = function()
569 {
570     return this.quickConsole.prompt.focused;
571 };
572
573 WebInspector.isShowingSplitConsole = function()
574 {
575     return !this.splitContentBrowser.element.classList.contains("hidden");
576 };
577
578 WebInspector.currentViewSupportsSplitContentBrowser = function()
579 {
580     var currentContentView = this.contentBrowser.currentContentView;
581     return !currentContentView || currentContentView.supportsSplitContentBrowser;
582 };
583
584 WebInspector.toggleSplitConsole = function()
585 {
586     if (!this.currentViewSupportsSplitContentBrowser()) {
587         this.toggleConsoleView();
588         return;
589     }
590
591     if (this.isShowingSplitConsole())
592         this.hideSplitConsole();
593     else
594         this.showSplitConsole();
595 };
596
597 WebInspector.showSplitConsole = function()
598 {
599     if (!this.currentViewSupportsSplitContentBrowser()) {
600         this.showFullHeightConsole();
601         return;
602     }
603
604     this.splitContentBrowser.element.classList.remove("hidden");
605
606     this._showingSplitConsoleSetting.value = true;
607
608     if (this.splitContentBrowser.currentContentView !== this.consoleContentView) {
609         var wasShowingFullConsole = this.isShowingConsoleView();
610
611         // Be sure to close any existing log view in the main content browser before showing it in the
612         // split content browser. We can only show a content view in one browser at a time.
613         this.contentBrowser.contentViewContainer.closeAllContentViewsOfPrototype(WebInspector.LogContentView);
614         this.splitContentBrowser.showContentView(this.consoleContentView);
615
616         if (wasShowingFullConsole && !this.contentBrowser.currentContentView)
617             this.resourceSidebarPanel.showDefaultContentView();
618     } else {
619         // This causes the view to know it was shown and focus the prompt.
620         this.splitContentBrowser.contentViewContainer.shown();
621     }
622
623     this.quickConsole.consoleLogVisibilityChanged(true);
624 };
625
626 WebInspector.hideSplitConsole = function()
627 {
628     this.splitContentBrowser.element.classList.add("hidden");
629
630     this._showingSplitConsoleSetting.value = false;
631
632     // This causes the view to know it was hidden.
633     this.splitContentBrowser.contentViewContainer.hidden();
634
635     this.quickConsole.consoleLogVisibilityChanged(false);
636 };
637
638 WebInspector.showFullHeightConsole = function(requestedScope)
639 {
640     this.splitContentBrowser.element.classList.add("hidden");
641
642     this._showingSplitConsoleSetting.value = false;
643
644     var scope = requestedScope || WebInspector.LogContentView.Scopes.All;
645
646     // If the requested scope is already selected and the console is showing, then switch back to All.
647     if (this.isShowingConsoleView() && this.consoleContentView.scopeBar.item(scope).selected)
648         scope = WebInspector.LogContentView.Scopes.All;
649
650     if (requestedScope || !this.consoleContentView.scopeBar.selectedItems.length)
651         this.consoleContentView.scopeBar.item(scope).selected = true;
652
653     if (!this.contentBrowser.currentContentView || this.contentBrowser.currentContentView !== this.consoleContentView) {
654         // Be sure to close any existing log view in the split content browser before showing it in the
655         // main content browser. We can only show a content view in one browser at a time.
656         this.splitContentBrowser.contentViewContainer.closeAllContentViewsOfPrototype(WebInspector.LogContentView);
657         this.contentBrowser.showContentView(this.consoleContentView);
658     }
659
660     console.assert(this.isShowingConsoleView());
661     console.assert(this._consoleToolbarButton.activated);
662
663     this.quickConsole.consoleLogVisibilityChanged(true);
664 };
665
666 WebInspector.isShowingConsoleView = function()
667 {
668     return this.contentBrowser.currentContentView instanceof WebInspector.LogContentView;
669 };
670
671 WebInspector.showConsoleView = function(scope)
672 {
673     this.showFullHeightConsole(scope);
674 };
675
676 WebInspector.toggleConsoleView = function()
677 {
678     if (this.isShowingConsoleView()) {
679         if (this.contentBrowser.canGoBack())
680             this.contentBrowser.goBack();
681         else
682             this.resourceSidebarPanel.showDefaultContentView();
683     } else
684         this.showFullHeightConsole();
685 };
686
687 WebInspector.UIString = function(string, vararg)
688 {
689     if (WebInspector.dontLocalizeUserInterface)
690         return string;
691
692     if (window.localizedStrings && string in window.localizedStrings)
693         return window.localizedStrings[string];
694
695     if (!this._missingLocalizedStrings)
696         this._missingLocalizedStrings = {};
697
698     if (!(string in this._missingLocalizedStrings)) {
699         console.error("Localized string \"" + string + "\" was not found.");
700         this._missingLocalizedStrings[string] = true;
701     }
702
703     return "LOCALIZED STRING NOT FOUND";
704 };
705
706 WebInspector.restoreFocusFromElement = function(element)
707 {
708     if (element && element.isSelfOrAncestor(this.currentFocusElement))
709         this.previousFocusElement.focus();
710 };
711
712 WebInspector.toggleNavigationSidebar = function(event)
713 {
714     if (!this.navigationSidebar.collapsed || !this.navigationSidebar.sidebarPanels.length) {
715         this.navigationSidebar.collapsed = true;
716         return;
717     }
718
719     if (!this.navigationSidebar.selectedSidebarPanel)
720         this.navigationSidebar.selectedSidebarPanel = this.navigationSidebar.sidebarPanels[0];
721     this.navigationSidebar.collapsed = false;
722 };
723
724 WebInspector.toggleDetailsSidebar = function(event)
725 {
726     if (!this.detailsSidebar.collapsed || !this.detailsSidebar.sidebarPanels.length) {
727         this.detailsSidebar.collapsed = true;
728         return;
729     }
730
731     if (!this.detailsSidebar.selectedSidebarPanel)
732         this.detailsSidebar.selectedSidebarPanel = this.detailsSidebar.sidebarPanels[0];
733     this.detailsSidebar.collapsed = false;
734 };
735
736 WebInspector.tabContentViewClassForRepresentedObject = function(representedObject)
737 {
738     if (representedObject instanceof WebInspector.DOMTree)
739         return WebInspector.ElementsTabContentView;
740
741     if (representedObject instanceof WebInspector.TimelineRecording)
742         return WebInspector.TimelineTabContentView;
743
744     // We only support one console tab right now. So this isn't an instanceof check.
745     if (representedObject === this._consoleRepresentedObject)
746         return WebInspector.ConsoleTabContentView;
747
748     if (WebInspector.debuggerManager.paused) {
749         if (representedObject instanceof WebInspector.Script)
750             return WebInspector.DebuggerTabContentView;
751
752         if (representedObject instanceof WebInspector.Resource && (representedObject.type === WebInspector.Resource.Type.Document || representedObject.type === WebInspector.Resource.Type.Script))
753             return WebInspector.DebuggerTabContentView;
754     }
755
756     if (representedObject instanceof WebInspector.Frame || representedObject instanceof WebInspector.Resource || representedObject instanceof WebInspector.Script)
757         return WebInspector.ResourcesTabContentView;
758
759     // FIXME: Move Content Flows to the Elements tab?
760     if (representedObject instanceof WebInspector.ContentFlow)
761         return WebInspector.ResourcesTabContentView;
762
763     // FIXME: Move these to a Storage tab.
764     if (representedObject instanceof WebInspector.DOMStorageObject || representedObject instanceof WebInspector.CookieStorageObject ||
765         representedObject instanceof WebInspector.DatabaseTableObject || representedObject instanceof WebInspector.DatabaseObject ||
766         representedObject instanceof WebInspector.ApplicationCacheFrame || representedObject instanceof WebInspector.IndexedDatabaseObjectStore ||
767         representedObject instanceof WebInspector.IndexedDatabaseObjectStoreIndex)
768         return WebInspector.ResourcesTabContentView;
769
770     return null;
771 };
772
773 WebInspector.tabContentViewForRepresentedObject = function(representedObject)
774 {
775     var tabContentView = this.tabBrowser.bestTabContentViewForRepresentedObject(representedObject);
776     if (tabContentView)
777         return tabContentView;
778
779     var tabContentViewClass = this.tabContentViewClassForRepresentedObject(representedObject);
780     if (!tabContentViewClass) {
781         console.error("Unknown representedObject, couldn't create TabContentView.", representedObject);
782         return null;
783     }
784
785     tabContentView = new tabContentViewClass;
786
787     this.tabBrowser.addTabForContentView(tabContentView);
788
789     return tabContentView;
790 };
791
792 WebInspector.showRepresentedObject = function(representedObject, cookie, forceShowTab)
793 {
794     var tabContentView = this.tabContentViewForRepresentedObject(representedObject);
795     console.assert(tabContentView);
796     if (!tabContentView)
797         return;
798
799     if (window.event || forceShowTab)
800         this.tabBrowser.showTabForContentView(tabContentView);
801
802     tabContentView.showRepresentedObject(representedObject, cookie);
803 };
804
805 WebInspector.showMainFrameDOMTree = function(nodeToSelect, forceShowTab)
806 {
807     console.assert(WebInspector.frameResourceManager.mainFrame);
808     if (!WebInspector.frameResourceManager.mainFrame)
809         return;
810     this.showRepresentedObject(WebInspector.frameResourceManager.mainFrame.domTree, {nodeToSelect}, forceShowTab);
811 };
812
813 WebInspector.showContentFlowDOMTree = function(contentFlow, nodeToSelect, forceShowTab)
814 {
815     this.showRepresentedObject(contentFlow, {nodeToSelect}, forceShowTab);
816 };
817
818 WebInspector.showSourceCodeForFrame = function(frameIdentifier, forceShowTab)
819 {
820     var frame = WebInspector.frameResourceManager.frameForIdentifier(frameIdentifier);
821     if (!frame) {
822         this._frameIdentifierToShowSourceCodeWhenAvailable = frameIdentifier;
823         return;
824     }
825
826     this._frameIdentifierToShowSourceCodeWhenAvailable = undefined;
827
828     this.showRepresentedObject(frame, null, forceShowTab);
829 };
830
831 WebInspector.showSourceCode = function(sourceCode, positionToReveal, textRangeToSelect, forceUnformatted, forceShowTab)
832 {
833     console.assert(!positionToReveal || positionToReveal instanceof WebInspector.SourceCodePosition, positionToReveal);
834     var representedObject = sourceCode;
835
836     if (representedObject instanceof WebInspector.Script) {
837         // A script represented by a resource should always show the resource.
838         representedObject = representedObject.resource || representedObject;
839     }
840
841     var cookie = positionToReveal ? {lineNumber: positionToReveal.lineNumber, columnNumber: positionToReveal.columnNumber} : {};
842     this.showRepresentedObject(representedObject, cookie, forceShowTab);
843 };
844
845 WebInspector.showSourceCodeLocation = function(sourceCodeLocation, forceShowTab)
846 {
847     this.showSourceCode(sourceCodeLocation.displaySourceCode, sourceCodeLocation.displayPosition(), null, false, forceShowTab);
848 };
849
850 WebInspector.showOriginalUnformattedSourceCodeLocation = function(sourceCodeLocation, forceShowTab)
851 {
852     this.showSourceCode(sourceCodeLocation.sourceCode, sourceCodeLocation.position(), null, true);
853 };
854
855 WebInspector.showOriginalOrFormattedSourceCodeLocation = function(sourceCodeLocation, forceShowTab)
856 {
857     this.showSourceCode(sourceCodeLocation.sourceCode, sourceCodeLocation.formattedPosition(), null, false, forceShowTab);
858 };
859
860 WebInspector.showOriginalOrFormattedSourceCodeTextRange = function(sourceCodeTextRange, forceShowTab)
861 {
862     var textRangeToSelect = sourceCodeTextRange.formattedTextRange;
863     this.showSourceCode(sourceCodeTextRange.sourceCode, textRangeToSelect.startPosition(), textRangeToSelect, false, forceShowTab);
864 };
865
866 WebInspector.showResourceRequest = function(resource, forceShowTab)
867 {
868     this.showRepresentedObject(resource, {[WebInspector.ResourceClusterContentView.ContentViewIdentifierCookieKey]: WebInspector.ResourceClusterContentView.RequestIdentifier}, forceShowTab);
869 };
870
871 WebInspector.debuggerToggleBreakpoints = function(event)
872 {
873     WebInspector.debuggerManager.breakpointsEnabled = !WebInspector.debuggerManager.breakpointsEnabled;
874 };
875
876 WebInspector.debuggerPauseResumeToggle = function(event)
877 {
878     if (WebInspector.debuggerManager.paused)
879         WebInspector.debuggerManager.resume();
880     else
881         WebInspector.debuggerManager.pause();
882 };
883
884 WebInspector.debuggerStepOver = function(event)
885 {
886     WebInspector.debuggerManager.stepOver();
887 };
888
889 WebInspector.debuggerStepInto = function(event)
890 {
891     WebInspector.debuggerManager.stepInto();
892 };
893
894 WebInspector.debuggerStepOut = function(event)
895 {
896     WebInspector.debuggerManager.stepOut();
897 };
898
899 WebInspector._focusChanged = function(event)
900 {
901     // Make a caret selection inside the focused element if there isn't a range selection and there isn't already
902     // a caret selection inside. This is needed (at least) to remove caret from console when focus is moved.
903     // The selection change should not apply to text fields and text areas either.
904
905     if (WebInspector.isEventTargetAnEditableField(event))
906         return;
907
908     var selection = window.getSelection();
909     if (!selection.isCollapsed)
910         return;
911
912     var element = event.target;
913
914     if (element !== this.currentFocusElement) {
915         this.previousFocusElement = this.currentFocusElement;
916         this.currentFocusElement = element;
917     }
918
919     if (element.isInsertionCaretInside())
920         return;
921
922     var selectionRange = element.ownerDocument.createRange();
923     selectionRange.setStart(element, 0);
924     selectionRange.setEnd(element, 0);
925
926     selection.removeAllRanges();
927     selection.addRange(selectionRange);
928 };
929
930 WebInspector._mouseWasClicked = function(event)
931 {
932     this.handlePossibleLinkClick(event);
933 };
934
935 WebInspector._dragOver = function(event)
936 {
937     // Do nothing if another event listener handled the event already.
938     if (event.defaultPrevented)
939         return;
940
941     // Allow dropping into editable areas.
942     if (WebInspector.isEventTargetAnEditableField(event))
943         return;
944
945     // Prevent the drop from being accepted.
946     event.dataTransfer.dropEffect = "none";
947     event.preventDefault();
948 };
949
950 WebInspector._captureDidStart = function(event)
951 {
952     this.dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.replay);
953 };
954
955 WebInspector._debuggerDidPause = function(event)
956 {
957     this.debuggerSidebarPanel.show();
958     this.dashboardContainer.showDashboardViewForRepresentedObject(this.dashboardManager.dashboards.debugger);
959
960     // Since the Scope Chain details sidebar panel might not be in the sidebar yet,
961     // set a flag to select and show it when it does become available.
962     this._selectAndShowScopeChainDetailsSidebarPanelWhenAvailable = true;
963
964     InspectorFrontendHost.bringToFront();
965 };
966
967 WebInspector._debuggerDidResume = function(event)
968 {
969     this.dashboardContainer.closeDashboardViewForRepresentedObject(this.dashboardManager.dashboards.debugger);
970 };
971
972 WebInspector._frameWasAdded = function(event)
973 {
974     if (!this._frameIdentifierToShowSourceCodeWhenAvailable)
975         return;
976
977     var frame = event.data.frame;
978     if (frame.id !== this._frameIdentifierToShowSourceCodeWhenAvailable)
979         return;
980
981     this.showSourceCodeForFrame(frame.id);
982 };
983
984 WebInspector._mainFrameDidChange = function(event)
985 {
986     this.updateWindowTitle();
987 };
988
989 WebInspector._mainResourceDidChange = function(event)
990 {
991     if (!event.target.isMainFrame())
992         return;
993
994     this._inProvisionalLoad = false;
995
996     this._restoreInspectorViewStateFromCookie(this._lastInspectorViewStateCookieSetting.value, true);
997
998     this.updateWindowTitle();
999 };
1000
1001 WebInspector._provisionalLoadStarted = function(event)
1002 {
1003     if (!event.target.isMainFrame())
1004         return;
1005
1006     this._updateCookieForInspectorViewState();
1007
1008     this._inProvisionalLoad = true;
1009 };
1010
1011 WebInspector._windowFocused = function(event)
1012 {
1013     if (event.target.document.nodeType !== Node.DOCUMENT_NODE || this.docked)
1014         return;
1015
1016     // FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
1017     document.body.classList.remove("window-inactive");
1018 };
1019
1020 WebInspector._windowBlurred = function(event)
1021 {
1022     if (event.target.document.nodeType !== Node.DOCUMENT_NODE || this.docked)
1023         return;
1024
1025     // FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
1026     document.body.classList.add("window-inactive");
1027 };
1028
1029 WebInspector._windowResized = function(event)
1030 {
1031     this.toolbar.updateLayout();
1032
1033     this._contentBrowserSizeDidChange(event);
1034 };
1035
1036 WebInspector._updateModifierKeys = function(event)
1037 {
1038     var didChange = this.modifierKeys.altKey !== event.altKey || this.modifierKeys.metaKey !== event.metaKey || this.modifierKeys.shiftKey !== event.shiftKey;
1039
1040     this.modifierKeys = {altKey: event.altKey, metaKey: event.metaKey, shiftKey: event.shiftKey};
1041
1042     if (didChange)
1043         this.notifications.dispatchEventToListeners(WebInspector.Notification.GlobalModifierKeysDidChange, event);
1044 };
1045
1046 WebInspector._windowKeyDown = function(event)
1047 {
1048     this._updateModifierKeys(event);
1049
1050     var opposite = !this._dockButtonToggledSetting.value;
1051     this.undockButtonNavigationItem.toggled = (event.altKey && !event.metaKey && !event.shiftKey) ? opposite : !opposite;
1052 };
1053
1054 WebInspector._windowKeyUp = function(event)
1055 {
1056     this._updateModifierKeys(event);
1057
1058     var opposite = !this._dockButtonToggledSetting.value;
1059     this.undockButtonNavigationItem.toggled = (event.altKey && !event.metaKey && !event.shiftKey) ? opposite : !opposite;
1060 };
1061
1062 WebInspector._mouseMoved = function(event)
1063 {
1064     this._updateModifierKeys(event);
1065     this.mouseCoords = {
1066         x: event.pageX,
1067         y: event.pageY
1068     };
1069 };
1070
1071 WebInspector._pageHidden = function(event)
1072 {
1073     this._updateCookieForInspectorViewState();
1074 };
1075
1076 WebInspector._undock = function(event)
1077 {
1078     this._dockButtonToggledSetting.value = this.undockButtonNavigationItem.toggled;
1079
1080     if (this.undockButtonNavigationItem.toggled)
1081         InspectorFrontendHost.requestSetDockSide(this._dockSide === "bottom" ? "right" : "bottom");
1082     else
1083         InspectorFrontendHost.requestSetDockSide("undocked");
1084 };
1085
1086 WebInspector._updateDockNavigationItems = function()
1087 {
1088     // The close and undock buttons are only available when docked.
1089     var docked = this.docked;
1090     this.closeButtonNavigationItem.hidden = !docked;
1091     this.undockButtonNavigationItem.hidden = !docked;
1092
1093     if (docked) {
1094         this.undockButtonNavigationItem.alternateImage = this._dockSide === "bottom" ? platformImagePath("DockRight.svg") : platformImagePath("DockBottom.svg");
1095         this.undockButtonNavigationItem.alternateToolTip = this._dockSide === "bottom" ? WebInspector.UIString("Dock to right of window") : WebInspector.UIString("Dock to bottom of window");
1096     }
1097
1098     this.undockButtonNavigationItem.toggled = this._dockButtonToggledSetting.value;
1099 };
1100
1101 WebInspector._sidebarCollapsedStateDidChange = function(event)
1102 {
1103     if (event.target === this.navigationSidebar) {
1104         if (!this._ignoreNavigationSidebarPanelCollapsedEvent) {
1105             this._navigationSidebarCollapsedSetting.value = this.navigationSidebar.collapsed;
1106             this._updateContentViewForCurrentNavigationSidebar();
1107         }
1108     } else if (event.target === this.detailsSidebar) {
1109         if (!this._ignoreDetailsSidebarPanelCollapsedEvent)
1110             this._detailsSidebarCollapsedSetting.value = this.detailsSidebar.collapsed;
1111     }
1112 };
1113
1114 WebInspector._detailsSidebarPanelSelected = function(event)
1115 {
1116     if (!this.detailsSidebar.selectedSidebarPanel || this._ignoreDetailsSidebarPanelSelectedEvent)
1117         return;
1118
1119     this._lastSelectedDetailsSidebarPanelSetting.value = this.detailsSidebar.selectedSidebarPanel.identifier;
1120 };
1121
1122 WebInspector._revealAndSelectRepresentedObjectInNavigationSidebar = function(representedObject)
1123 {
1124     if (this.navigationSidebar.collapsed)
1125         return;
1126
1127     var selectedSidebarPanel = this.navigationSidebar.selectedSidebarPanel;
1128     if (!selectedSidebarPanel)
1129         return;
1130
1131     // If a tree outline is processing a selection currently then we can assume the selection does not
1132     // need to be changed. This is needed to allow breakpoint and call frame tree elements to be selected
1133     // without jumping back to selecting the resource tree element.
1134     for (var contentTreeOutline of selectedSidebarPanel.visibleContentTreeOutlines) {
1135         if (contentTreeOutline.processingSelectionChange)
1136             return;
1137     }
1138
1139     var treeElement = selectedSidebarPanel.treeElementForRepresentedObject(representedObject);
1140
1141     if (treeElement)
1142         treeElement.revealAndSelect(true, false, false, true);
1143     else if (selectedSidebarPanel.contentTreeOutline.selectedTreeElement)
1144         selectedSidebarPanel.contentTreeOutline.selectedTreeElement.deselect(true);
1145 };
1146
1147 WebInspector._updateContentViewForCurrentNavigationSidebar = function()
1148 {
1149     if (this.navigationSidebar.collapsed)
1150         return;
1151
1152     var selectedSidebarPanel = this.navigationSidebar.selectedSidebarPanel;
1153     if (!selectedSidebarPanel)
1154         return;
1155
1156     var currentContentView = this.contentBrowser.currentContentView;
1157     if (!currentContentView)
1158         return;
1159
1160     // Ensure the navigation sidebar panel is allowed by the current content view, if not ask the sidebar panel
1161     // to show the content view for the current selection.
1162     var allowedNavigationSidebarPanels = currentContentView.allowedNavigationSidebarPanels;
1163     if (allowedNavigationSidebarPanels && (!allowedNavigationSidebarPanels.length || allowedNavigationSidebarPanels.includes(selectedSidebarPanel.identifier))) {
1164         this._revealAndSelectRepresentedObjectInNavigationSidebar(currentContentView.representedObject);
1165         return;
1166     }
1167
1168     var backForwardList = WebInspector.contentBrowser.contentViewContainer.backForwardList;
1169     var index = backForwardList.length;
1170     while (index--) {
1171         var contentView = backForwardList[index].contentView;
1172         var allowedNavigationSidebarPanels = contentView.allowedNavigationSidebarPanels;
1173         if (allowedNavigationSidebarPanels && (!allowedNavigationSidebarPanels.length || allowedNavigationSidebarPanels.includes(selectedSidebarPanel.identifier))) {
1174             WebInspector.contentBrowser.showContentView(contentView);
1175             return;
1176         }
1177     }
1178
1179     selectedSidebarPanel.showDefaultContentView();
1180 };
1181
1182 WebInspector._navigationSidebarPanelSelected = function(event)
1183 {
1184     if (this._ignoreNavigationSidebarPanelSelectedEvent)
1185         return;
1186
1187     if (!this.navigationSidebar.selectedSidebarPanel) {
1188         this._ignoreNavigationSidebarPanelCollapsedEvent = true;
1189         this.navigationSidebar.collapsed = true;
1190         delete this._ignoreNavigationSidebarPanelCollapsedEvent;
1191         return;
1192     }
1193
1194     // Restore the sidebar if was forced collapsed earlier because of no selectedSidebarPanel.
1195     this.navigationSidebar.collapsed = this._navigationSidebarCollapsedSetting.value;
1196
1197     if (this.navigationSidebar.collapsed)
1198         return;
1199
1200     this._updateContentViewForCurrentNavigationSidebar();
1201 };
1202
1203 WebInspector._domNodeWasInspected = function(event)
1204 {
1205     WebInspector.domTreeManager.highlightDOMNodeForTwoSeconds(event.data.node.id);
1206
1207     // Select the Style details sidebar panel if one of the DOM details sidebar panels isn't already selected.
1208     if (!(this.detailsSidebar.selectedSidebarPanel instanceof WebInspector.DOMDetailsSidebarPanel))
1209         this.detailsSidebar.selectedSidebarPanel = this.cssStyleDetailsSidebarPanel;
1210
1211     InspectorFrontendHost.bringToFront();
1212 };
1213
1214 WebInspector._contentBrowserSizeDidChange = function(event)
1215 {
1216     this.contentBrowser.updateLayout();
1217     this.splitContentBrowser.updateLayout();
1218     this.quickConsole.updateLayout();
1219 };
1220
1221 WebInspector._quickConsoleDidResize = function(event)
1222 {
1223     this.contentBrowser.updateLayout();
1224 };
1225
1226 WebInspector._sidebarWidthDidChange = function(event)
1227 {
1228     if (!event.target.collapsed) {
1229         if (event.target === this.navigationSidebar)
1230             this._navigationSidebarWidthSetting.value = this.navigationSidebar.width;
1231         else if (event.target === this.detailsSidebar)
1232             this._detailsSidebarWidthSetting.value = this.detailsSidebar.width;
1233     }
1234
1235     this._contentBrowserSizeDidChange(event);
1236 };
1237
1238 WebInspector._updateToolbarHeight = function()
1239 {
1240     if (WebInspector.Platform.isLegacyMacOS)
1241         InspectorFrontendHost.setToolbarHeight(this.toolbar.element.offsetHeight);
1242 };
1243
1244 WebInspector._toolbarDisplayModeDidChange = function(event)
1245 {
1246     if (this._ignoreToolbarModeDidChangeEvents)
1247         return;
1248
1249     if (this._dockSide === "bottom")
1250         this._toolbarDockedBottomDisplayModeSetting.value = this.toolbar.displayMode;
1251     else if (this._dockSide === "right")
1252         this._toolbarDockedRightDisplayModeSetting.value = this.toolbar.displayMode;
1253     else
1254         this._toolbarUndockedDisplayModeSetting.value = this.toolbar.displayMode;
1255
1256     this._updateToolbarHeight();
1257 };
1258
1259 WebInspector._toolbarSizeModeDidChange = function(event)
1260 {
1261     if (this._ignoreToolbarModeDidChangeEvents)
1262         return;
1263
1264     if (this._dockSide === "bottom")
1265         this._toolbarDockedBottomSizeModeSetting.value = this.toolbar.sizeMode;
1266     else if (this._dockSide === "right")
1267         this._toolbarDockedRightSizeModeSetting.value = this.toolbar.sizeMode;
1268     else
1269         this._toolbarUndockedSizeModeSetting.value = this.toolbar.sizeMode;
1270
1271     this._updateToolbarHeight();
1272 };
1273
1274 WebInspector._updateCookieForInspectorViewState = function()
1275 {
1276     var cookie = {};
1277     var currentContentView = this.contentBrowser.currentContentView;
1278
1279     // The console does not have a sidebar, so create a cookie here.
1280     if (currentContentView && currentContentView.representedObject instanceof WebInspector.LogObject) {
1281         cookie[WebInspector.SelectedSidebarPanelCookieKey] = "console";
1282         this._lastInspectorViewStateCookieSetting.value = cookie;
1283         return;
1284     }
1285
1286     // Ignore saving the sidebar state for provisional loads. The currently selected sidebar
1287     // may have been the result of content views closing as a result of a page navigation,
1288     // but those content views may come back very soon.
1289     if (this._inProvisionalLoad)
1290         return;
1291
1292     var selectedSidebarPanel = this.navigationSidebar.selectedSidebarPanel;
1293     if (!selectedSidebarPanel)
1294         return;
1295
1296     // Restoring view state after inspector re-open or page reload is delegated to navigation sidebars.
1297     // This is because some navigation sidebar state (such as breakpoint selections) cannot be inferred
1298     // solely based on which content view is visible, or multiple navigation sidebars could be shown.
1299     cookie[WebInspector.SelectedSidebarPanelCookieKey] = selectedSidebarPanel.identifier;
1300     selectedSidebarPanel.saveStateToCookie(cookie);
1301     this._lastInspectorViewStateCookieSetting.value = cookie;
1302 };
1303
1304 WebInspector._contentBrowserCurrentContentViewDidChange = function(event)
1305 {
1306     var consoleViewShowing = this.isShowingConsoleView();
1307     this._consoleToolbarButton.activated = consoleViewShowing;
1308
1309     if (!this.isShowingSplitConsole())
1310         this.quickConsole.consoleLogVisibilityChanged(consoleViewShowing);
1311
1312     if (!this.currentViewSupportsSplitContentBrowser())
1313         this.hideSplitConsole();
1314
1315     var currentContentView = this.contentBrowser.currentContentView;
1316     if (!currentContentView)
1317         return;
1318
1319     var selectedSidebarPanel = this.navigationSidebar.selectedSidebarPanel;
1320     if (!selectedSidebarPanel)
1321         return;
1322
1323     this._ignoreNavigationSidebarPanelSelectedEvent = true;
1324
1325     // Ensure the navigation sidebar panel is allowed by the current content view, if not change the navigation sidebar panel
1326     // to the last navigation sidebar panel used with the content view or the first one allowed.
1327     var selectedSidebarPanelIdentifier = selectedSidebarPanel.identifier;
1328     var allowedNavigationSidebarPanels = currentContentView.allowedNavigationSidebarPanels;
1329
1330     if (allowedNavigationSidebarPanels) {
1331         if (allowedNavigationSidebarPanels.length && !allowedNavigationSidebarPanels.includes(selectedSidebarPanelIdentifier)) {
1332             console.assert(!currentContentView.__lastNavigationSidebarPanelIdentifier || allowedNavigationSidebarPanels.includes(currentContentView.__lastNavigationSidebarPanelIdentifier));
1333             this.navigationSidebar.selectedSidebarPanel = currentContentView.__lastNavigationSidebarPanelIdentifier || allowedNavigationSidebarPanels[0];
1334             console.assert(this.navigationSidebar.selectedSidebarPanel);
1335         }
1336
1337         if (this._wasShowingNavigationSidebarBeforeForcedCollapsed) {
1338             this._ignoreNavigationSidebarPanelCollapsedEvent = true;
1339             this.navigationSidebar.collapsed = false;
1340             delete this._ignoreNavigationSidebarPanelCollapsedEvent;
1341
1342             delete this._wasShowingNavigationSidebarBeforeForcedCollapsed;
1343         }
1344     } else if (!allowedNavigationSidebarPanels && !this.navigationSidebar.collapsed) {
1345         this._wasShowingNavigationSidebarBeforeForcedCollapsed = true;
1346
1347         this._ignoreNavigationSidebarPanelCollapsedEvent = true;
1348         this.navigationSidebar.collapsed = true;
1349         delete this._ignoreNavigationSidebarPanelCollapsedEvent;
1350     }
1351
1352     if (allowedNavigationSidebarPanels && !this.navigationSidebar.collapsed)
1353         currentContentView.__lastNavigationSidebarPanelIdentifier = this.navigationSidebar.selectedSidebarPanel.identifier;
1354
1355     this._revealAndSelectRepresentedObjectInNavigationSidebar(currentContentView.representedObject);
1356
1357     delete this._ignoreNavigationSidebarPanelSelectedEvent;
1358 };
1359
1360 WebInspector._contentBrowserRepresentedObjectsDidChange = function(event)
1361 {
1362     var currentRepresentedObjects = this.contentBrowser.currentRepresentedObjects;
1363     var currentSidebarPanels = this.detailsSidebar.sidebarPanels;
1364     var wasSidebarEmpty = !currentSidebarPanels.length;
1365
1366     // Ignore any changes to the selected sidebar panel during this function so only user initiated
1367     // changes are recorded in _lastSelectedDetailsSidebarPanelSetting.
1368     this._ignoreDetailsSidebarPanelSelectedEvent = true;
1369
1370     for (var i = 0; i < this.detailsSidebarPanels.length; ++i) {
1371         var sidebarPanel = this.detailsSidebarPanels[i];
1372         if (sidebarPanel.inspect(currentRepresentedObjects)) {
1373             var currentSidebarPanelIndex = currentSidebarPanels.indexOf(sidebarPanel);
1374             if (currentSidebarPanelIndex !== -1) {
1375                 // Already showing the panel.
1376                 continue;
1377             }
1378
1379             // The sidebar panel was not previously showing, so add the panel and show the toolbar item.
1380             this.detailsSidebar.addSidebarPanel(sidebarPanel);
1381             sidebarPanel.toolbarItem.hidden = false;
1382
1383             if (this._selectAndShowScopeChainDetailsSidebarPanelWhenAvailable && sidebarPanel === this.scopeChainDetailsSidebarPanel) {
1384                 // Select the scope chain sidebar panel since it needs to be shown after pausing in the debugger.
1385                 delete this._selectAndShowScopeChainDetailsSidebarPanelWhenAvailable;
1386                 this.detailsSidebar.selectedSidebarPanel = this.scopeChainDetailsSidebarPanel;
1387
1388                 this._ignoreDetailsSidebarPanelCollapsedEvent = true;
1389                 this.detailsSidebar.collapsed = false;
1390                 delete this._ignoreDetailsSidebarPanelCollapsedEvent;
1391             } else if (this._lastSelectedDetailsSidebarPanelSetting.value === sidebarPanel.identifier) {
1392                 // Restore the sidebar panel selection if this sidebar panel was the last one selected by the user.
1393                 this.detailsSidebar.selectedSidebarPanel = sidebarPanel;
1394             }
1395         } else {
1396             // The sidebar panel can't inspect the current represented objects, so remove the panel and hide the toolbar item.
1397             this.detailsSidebar.removeSidebarPanel(sidebarPanel);
1398             sidebarPanel.toolbarItem.hidden = true;
1399         }
1400     }
1401
1402     if (!this.detailsSidebar.selectedSidebarPanel && currentSidebarPanels.length)
1403         this.detailsSidebar.selectedSidebarPanel = currentSidebarPanels[0];
1404
1405     this._ignoreDetailsSidebarPanelCollapsedEvent = true;
1406
1407     if (!this.detailsSidebar.sidebarPanels.length)
1408         this.detailsSidebar.collapsed = true;
1409     else if (wasSidebarEmpty)
1410         this.detailsSidebar.collapsed = this._detailsSidebarCollapsedSetting.value;
1411
1412     delete this._ignoreDetailsSidebarPanelCollapsedEvent;
1413
1414     // Stop ignoring the sidebar panel selected event.
1415     delete this._ignoreDetailsSidebarPanelSelectedEvent;
1416 };
1417
1418 WebInspector._restoreInspectorViewStateFromCookie = function(cookie, causedByReload)
1419 {
1420     if (!cookie) {
1421         this.navigationSidebar.selectedSidebarPanel = this.resourceSidebarPanel;
1422         return;
1423     }
1424
1425     // The console does not have a sidebar, so handle its special cookie here.
1426     if (cookie[WebInspector.SelectedSidebarPanelCookieKey] === "console") {
1427         this.showFullHeightConsole();
1428         return;
1429     }
1430
1431     const matchTypeOnlyDelayForReload = 2000;
1432     const matchTypeOnlyDelayForReopen = 1000;
1433
1434     var sidebarPanelIdentifier = cookie[WebInspector.SelectedSidebarPanelCookieKey];
1435     var sidebarPanel = this.navigationSidebar.findSidebarPanel(sidebarPanelIdentifier);
1436     if (!sidebarPanel) {
1437         this.navigationSidebar.selectedSidebarPanel = this.resourceSidebarPanel;
1438         return;
1439     }
1440
1441     this.navigationSidebar.selectedSidebarPanel = sidebarPanel;
1442
1443     var relaxMatchDelay = causedByReload ? matchTypeOnlyDelayForReload : matchTypeOnlyDelayForReopen;
1444     sidebarPanel.restoreStateFromCookie(cookie, relaxMatchDelay);
1445 };
1446
1447 WebInspector._initializeWebSocketIfNeeded = function()
1448 {
1449     if (!InspectorFrontendHost.initializeWebSocket)
1450         return;
1451
1452     var queryParams = parseLocationQueryParameters();
1453
1454     if ("ws" in queryParams)
1455         var url = "ws://" + queryParams.ws;
1456     else if ("page" in queryParams) {
1457         var page = queryParams.page;
1458         var host = "host" in queryParams ? queryParams.host : window.location.host;
1459         var url = "ws://" + host + "/devtools/page/" + page;
1460     }
1461
1462     if (!url)
1463         return;
1464
1465     InspectorFrontendHost.initializeWebSocket(url);
1466 };
1467
1468 WebInspector._updateSplitConsoleHeight = function(height)
1469 {
1470     const minimumHeight = 64;
1471     const maximumHeight = window.innerHeight * 0.55;
1472
1473     height = Math.max(minimumHeight, Math.min(height, maximumHeight));
1474
1475     this.splitContentBrowser.element.style.height = height + "px";
1476 };
1477
1478 WebInspector._consoleResizerMouseDown = function(event)
1479 {
1480     if (event.button !== 0 || event.ctrlKey)
1481         return;
1482
1483     // Only start dragging if the target is one of the elements that we expect.
1484     if (!event.target.classList.contains("navigation-bar") && !event.target.classList.contains("flexible-space"))
1485         return;
1486
1487     var resizerElement = event.target;
1488     var mouseOffset = resizerElement.offsetHeight - (event.pageY - resizerElement.totalOffsetTop);
1489
1490     function dockedResizerDrag(event)
1491     {
1492         if (event.button !== 0)
1493             return;
1494
1495         var height = window.innerHeight - event.pageY - mouseOffset;
1496
1497         this._splitConsoleHeightSetting.value = height;
1498
1499         this._updateSplitConsoleHeight(height);
1500
1501         this.quickConsole.dispatchEventToListeners(WebInspector.QuickConsole.Event.DidResize);
1502     }
1503
1504     function dockedResizerDragEnd(event)
1505     {
1506         if (event.button !== 0)
1507             return;
1508
1509         this.elementDragEnd(event);
1510     }
1511
1512     this.elementDragStart(resizerElement, dockedResizerDrag.bind(this), dockedResizerDragEnd.bind(this), event, "row-resize");
1513 };
1514
1515 WebInspector._toolbarMouseDown = function(event)
1516 {
1517     if (event.ctrlKey)
1518         return;
1519
1520     if (this._dockSide === "right")
1521         return;
1522
1523     if (this.docked)
1524         this._dockedResizerMouseDown(event);
1525     else
1526         this._moveWindowMouseDown(event);
1527 };
1528
1529 WebInspector._dockedResizerMouseDown = function(event)
1530 {
1531     if (event.button !== 0 || event.ctrlKey)
1532         return;
1533
1534     if (!this.docked)
1535         return;
1536
1537     // Only start dragging if the target is one of the elements that we expect.
1538     if (event.target.id !== "docked-resizer" && !event.target.classList.contains("toolbar") &&
1539         !event.target.classList.contains("flexible-space") && !event.target.classList.contains("item-section"))
1540         return;
1541
1542     var windowProperty = this._dockSide === "bottom" ? "innerHeight" : "innerWidth";
1543     var eventScreenProperty = this._dockSide === "bottom" ? "screenY" : "screenX";
1544     var eventClientProperty = this._dockSide === "bottom" ? "clientY" : "clientX";
1545
1546     var resizerElement = event.target;
1547     var firstClientPosition = event[eventClientProperty];
1548     var lastScreenPosition = event[eventScreenProperty];
1549
1550     function dockedResizerDrag(event)
1551     {
1552         if (event.button !== 0)
1553             return;
1554
1555         var position = event[eventScreenProperty];
1556         var delta = position - lastScreenPosition;
1557         var clientPosition = event[eventClientProperty];
1558
1559         lastScreenPosition = position;
1560
1561         // If delta is positive the docked Inspector size is decreasing, in which case the cursor client position
1562         // with respect to the target cannot be less than the first mouse down position within the target.
1563         if (delta > 0 && clientPosition < firstClientPosition)
1564             return;
1565
1566         // If delta is negative the docked Inspector size is increasing, in which case the cursor client position
1567         // with respect to the target cannot be greater than the first mouse down position within the target.
1568         if (delta < 0 && clientPosition > firstClientPosition)
1569             return;
1570
1571         var dimension = Math.max(0, window[windowProperty] - delta);
1572
1573         if (this._dockSide === "bottom")
1574             InspectorFrontendHost.setAttachedWindowHeight(dimension);
1575         else
1576             InspectorFrontendHost.setAttachedWindowWidth(dimension);
1577     }
1578
1579     function dockedResizerDragEnd(event)
1580     {
1581         if (event.button !== 0)
1582             return;
1583
1584         WebInspector.elementDragEnd(event);
1585     }
1586
1587     WebInspector.elementDragStart(resizerElement, dockedResizerDrag.bind(this), dockedResizerDragEnd.bind(this), event, this._dockSide === "bottom" ? "row-resize" : "col-resize");
1588 };
1589
1590 WebInspector._moveWindowMouseDown = function(event)
1591 {
1592     console.assert(!this.docked);
1593
1594     if (event.button !== 0 || event.ctrlKey)
1595         return;
1596
1597     // Only start dragging if the target is one of the elements that we expect.
1598     if (!event.target.classList.contains("toolbar") && !event.target.classList.contains("flexible-space") &&
1599         !event.target.classList.contains("item-section"))
1600         return;
1601
1602     // Ignore dragging on the top of the toolbar on Mac where the inspector content fills the entire window.
1603     if (WebInspector.Platform.name === "mac" && WebInspector.Platform.version.release >= 10) {
1604         const windowDragHandledTitleBarHeight = 22;
1605         if (event.pageY < windowDragHandledTitleBarHeight) {
1606             event.preventDefault();
1607             return;
1608         }
1609     }
1610
1611     var lastScreenX = event.screenX;
1612     var lastScreenY = event.screenY;
1613
1614     function toolbarDrag(event)
1615     {
1616         if (event.button !== 0)
1617             return;
1618
1619         var x = event.screenX - lastScreenX;
1620         var y = event.screenY - lastScreenY;
1621
1622         InspectorFrontendHost.moveWindowBy(x, y);
1623
1624         lastScreenX = event.screenX;
1625         lastScreenY = event.screenY;
1626     }
1627
1628     function toolbarDragEnd(event)
1629     {
1630         if (event.button !== 0)
1631             return;
1632
1633         WebInspector.elementDragEnd(event);
1634     }
1635
1636     WebInspector.elementDragStart(event.target, toolbarDrag, toolbarDragEnd, event, "default");
1637 };
1638
1639 WebInspector._inspectModeStateChanged = function(event)
1640 {
1641     this._inspectModeToolbarButton.activated = WebInspector.domTreeManager.inspectModeEnabled;
1642 };
1643
1644 WebInspector._toggleInspectMode = function(event)
1645 {
1646     WebInspector.domTreeManager.inspectModeEnabled = !WebInspector.domTreeManager.inspectModeEnabled;
1647 };
1648
1649 WebInspector._reloadPage = function(event)
1650 {
1651     PageAgent.reload();
1652 };
1653
1654 WebInspector._reloadPageIgnoringCache = function(event)
1655 {
1656     PageAgent.reload(true);
1657 };
1658
1659 WebInspector._toggleInspectMode = function(event)
1660 {
1661     this.domTreeManager.inspectModeEnabled = !this.domTreeManager.inspectModeEnabled;
1662 };
1663
1664 WebInspector._focusedContentView = function()
1665 {
1666     if (this.contentBrowser.element.isSelfOrAncestor(this.currentFocusElement))
1667         return this.contentBrowser.currentContentView;
1668     if (this.splitContentBrowser.element.isSelfOrAncestor(this.currentFocusElement))
1669         return  this.splitContentBrowser.currentContentView;
1670     return null;
1671 };
1672
1673 WebInspector._beforecopy = function(event)
1674 {
1675     var selection = window.getSelection();
1676
1677     // If there is no selection, see if the focused element or focused ContentView can handle the copy event.
1678     if (selection.isCollapsed && !WebInspector.isEventTargetAnEditableField(event)) {
1679         var focusedCopyHandler = this.currentFocusElement && this.currentFocusElement.copyHandler;
1680         if (focusedCopyHandler && typeof focusedCopyHandler.handleBeforeCopyEvent === "function") {
1681             focusedCopyHandler.handleBeforeCopyEvent(event);
1682             if (event.defaultPrevented)
1683                 return;
1684         }
1685
1686         var focusedContentView = this._focusedContentView();
1687         if (focusedContentView && typeof focusedContentView.handleCopyEvent === "function") {
1688             event.preventDefault();
1689             return;
1690         }
1691
1692         return;
1693     }
1694
1695     if (selection.isCollapsed)
1696         return;
1697
1698     // Say we can handle it (by preventing default) to remove word break characters.
1699     event.preventDefault();
1700 };
1701
1702 WebInspector._copy = function(event)
1703 {
1704     var selection = window.getSelection();
1705
1706     // If there is no selection, pass the copy event on to the focused element or focused ContentView.
1707     if (selection.isCollapsed && !WebInspector.isEventTargetAnEditableField(event)) {
1708         var focusedCopyHandler = this.currentFocusElement && this.currentFocusElement.copyHandler;
1709         if (focusedCopyHandler && typeof focusedCopyHandler.handleCopyEvent === "function") {
1710             focusedCopyHandler.handleCopyEvent(event);
1711             if (event.defaultPrevented)
1712                 return;
1713         }
1714
1715         var focusedContentView = this._focusedContentView();
1716         if (focusedContentView && typeof focusedContentView.handleCopyEvent === "function") {
1717             focusedContentView.handleCopyEvent(event);
1718             return;
1719         }
1720
1721         return;
1722     }
1723
1724     if (selection.isCollapsed)
1725         return;
1726
1727     // Remove word break characters from the selection before putting it on the pasteboard.
1728     var selectionString = selection.toString().removeWordBreakCharacters();
1729     event.clipboardData.setData("text/plain", selectionString);
1730     event.preventDefault();
1731 };
1732
1733 WebInspector._generateDisclosureTriangleImages = function()
1734 {
1735     var specifications = {};
1736     specifications["normal"] = {fillColor: [140, 140, 140, 1]};
1737     specifications["normal-active"] = {fillColor: [128, 128, 128, 1]};
1738
1739     generateColoredImagesForCSS("Images/DisclosureTriangleSmallOpen.svg", specifications, 13, 13, "disclosure-triangle-small-open-");
1740     generateColoredImagesForCSS("Images/DisclosureTriangleSmallClosed.svg", specifications, 13, 13, "disclosure-triangle-small-closed-");
1741
1742     specifications["selected"] = {fillColor: [255, 255, 255, 1]};
1743
1744     generateColoredImagesForCSS("Images/DisclosureTriangleTinyOpen.svg", specifications, 8, 8, "disclosure-triangle-tiny-open-");
1745     generateColoredImagesForCSS("Images/DisclosureTriangleTinyClosed.svg", specifications, 8, 8, "disclosure-triangle-tiny-closed-");
1746 };
1747
1748 WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor, eventTarget)
1749 {
1750     if (WebInspector._elementDraggingEventListener || WebInspector._elementEndDraggingEventListener)
1751         WebInspector.elementDragEnd(event);
1752
1753     if (element) {
1754         // Install glass pane
1755         if (WebInspector._elementDraggingGlassPane)
1756             WebInspector._elementDraggingGlassPane.parentElement.removeChild(WebInspector._elementDraggingGlassPane);
1757
1758         var glassPane = document.createElement("div");
1759         glassPane.style.cssText = "position:absolute;top:0;bottom:0;left:0;right:0;opacity:0;z-index:1";
1760         glassPane.id = "glass-pane-for-drag";
1761         element.ownerDocument.body.appendChild(glassPane);
1762         WebInspector._elementDraggingGlassPane = glassPane;
1763     }
1764
1765     WebInspector._elementDraggingEventListener = dividerDrag;
1766     WebInspector._elementEndDraggingEventListener = elementDragEnd;
1767
1768     var targetDocument = event.target.ownerDocument;
1769
1770     WebInspector._elementDraggingEventTarget = eventTarget || targetDocument;
1771     WebInspector._elementDraggingEventTarget.addEventListener("mousemove", dividerDrag, true);
1772     WebInspector._elementDraggingEventTarget.addEventListener("mouseup", elementDragEnd, true);
1773
1774     targetDocument.body.style.cursor = cursor;
1775
1776     event.preventDefault();
1777 };
1778
1779 WebInspector.elementDragEnd = function(event)
1780 {
1781     WebInspector._elementDraggingEventTarget.removeEventListener("mousemove", WebInspector._elementDraggingEventListener, true);
1782     WebInspector._elementDraggingEventTarget.removeEventListener("mouseup", WebInspector._elementEndDraggingEventListener, true);
1783
1784     event.target.ownerDocument.body.style.removeProperty("cursor");
1785
1786     if (WebInspector._elementDraggingGlassPane)
1787         WebInspector._elementDraggingGlassPane.parentElement.removeChild(WebInspector._elementDraggingGlassPane);
1788
1789     delete WebInspector._elementDraggingGlassPane;
1790     delete WebInspector._elementDraggingEventTarget;
1791     delete WebInspector._elementDraggingEventListener;
1792     delete WebInspector._elementEndDraggingEventListener;
1793
1794     event.preventDefault();
1795 };
1796
1797 WebInspector.createMessageTextView = function(message, isError)
1798 {
1799     var messageElement = document.createElement("div");
1800     messageElement.className = "message-text-view";
1801     if (isError)
1802         messageElement.classList.add("error");
1803
1804     messageElement.textContent = message;
1805
1806     return messageElement;
1807 };
1808
1809 WebInspector.createGoToArrowButton = function()
1810 {
1811     if (!WebInspector._generatedGoToArrowButtonImages) {
1812         WebInspector._generatedGoToArrowButtonImages = true;
1813
1814         var specifications = {};
1815         specifications["go-to-arrow-normal"] = {fillColor: [0, 0, 0, 0.5]};
1816         specifications["go-to-arrow-normal-active"] = {fillColor: [0, 0, 0, 0.7]};
1817         specifications["go-to-arrow-selected"] = {fillColor: [255, 255, 255, 0.8]};
1818         specifications["go-to-arrow-selected-active"] = {fillColor: [255, 255, 255, 1]};
1819
1820         generateColoredImagesForCSS("Images/GoToArrow.svg", specifications, 10, 10);
1821     }
1822
1823     function stopPropagation(event)
1824     {
1825         event.stopPropagation()
1826     }
1827
1828     var button = document.createElement("button");
1829     button.addEventListener("mousedown", stopPropagation, true);
1830     button.className = "go-to-arrow";
1831     button.tabIndex = -1;
1832     return button;
1833 };
1834
1835 WebInspector.createSourceCodeLocationLink = function(sourceCodeLocation, dontFloat, useGoToArrowButton)
1836 {
1837     console.assert(sourceCodeLocation);
1838     if (!sourceCodeLocation)
1839         return null;
1840
1841     function showSourceCodeLocation(event)
1842     {
1843         event.stopPropagation();
1844         event.preventDefault();
1845
1846         if (event.metaKey)
1847             this.showOriginalUnformattedSourceCodeLocation(sourceCodeLocation);
1848         else
1849             this.showSourceCodeLocation(sourceCodeLocation);
1850     }
1851
1852     var linkElement = document.createElement("a");
1853     linkElement.className = "go-to-link";
1854     linkElement.addEventListener("click", showSourceCodeLocation.bind(this));
1855     sourceCodeLocation.populateLiveDisplayLocationTooltip(linkElement);
1856
1857     if (useGoToArrowButton)
1858         linkElement.appendChild(WebInspector.createGoToArrowButton());
1859     else
1860         sourceCodeLocation.populateLiveDisplayLocationString(linkElement, "textContent");
1861
1862     if (dontFloat)
1863         linkElement.classList.add("dont-float");
1864
1865     return linkElement;
1866 };
1867
1868 WebInspector.linkifyLocation = function(url, lineNumber, columnNumber, className)
1869 {
1870     var sourceCode = WebInspector.frameResourceManager.resourceForURL(url);
1871     if (!sourceCode) {
1872         sourceCode = WebInspector.debuggerManager.scriptsForURL(url)[0];
1873         if (sourceCode)
1874             sourceCode = sourceCode.resource || sourceCode;
1875     }
1876
1877     if (!sourceCode) {
1878         var anchor = document.createElement("a");
1879         anchor.href  = url;
1880         anchor.lineNumber = lineNumber;
1881         if (className)
1882             anchor.className = className;
1883         anchor.appendChild(document.createTextNode(WebInspector.displayNameForURL(url) + ":" + lineNumber));
1884         return anchor;
1885     }
1886
1887     var sourceCodeLocation = sourceCode.createSourceCodeLocation(lineNumber, columnNumber);
1888     var linkElement = WebInspector.createSourceCodeLocationLink(sourceCodeLocation, true);
1889     if (className)
1890         linkElement.classList.add(className);
1891     return linkElement;
1892 };
1893
1894 WebInspector.linkifyURLAsNode = function(url, linkText, classes, tooltipText)
1895 {
1896     if (!linkText)
1897         linkText = url;
1898
1899     classes = (classes ? classes + " " : "");
1900
1901     var a = document.createElement("a");
1902     a.href = url;
1903     a.className = classes;
1904
1905     if (tooltipText === undefined)
1906         a.title = url;
1907     else if (typeof tooltipText !== "string" || tooltipText.length)
1908         a.title = tooltipText;
1909
1910     a.textContent = linkText;
1911     a.style.maxWidth = "100%";
1912
1913     return a;
1914 };
1915
1916 WebInspector.linkifyStringAsFragmentWithCustomLinkifier = function(string, linkifier)
1917 {
1918     var container = document.createDocumentFragment();
1919     var linkStringRegEx = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/|www\.)[\w$\-_+*'=\|\/\\(){}[\]%@&#~,:;.!?]{2,}[\w$\-_+*=\|\/\\({%@&#~]/;
1920     var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
1921
1922     while (string) {
1923         var linkString = linkStringRegEx.exec(string);
1924         if (!linkString)
1925             break;
1926
1927         linkString = linkString[0];
1928         var linkIndex = string.indexOf(linkString);
1929         var nonLink = string.substring(0, linkIndex);
1930         container.appendChild(document.createTextNode(nonLink));
1931
1932         var title = linkString;
1933         var realURL = (linkString.startsWith("www.") ? "http://" + linkString : linkString);
1934         var lineColumnMatch = lineColumnRegEx.exec(realURL);
1935         if (lineColumnMatch)
1936             realURL = realURL.substring(0, realURL.length - lineColumnMatch[0].length);
1937
1938         var linkNode = linkifier(title, realURL, lineColumnMatch ? lineColumnMatch[1] : undefined);
1939         container.appendChild(linkNode);
1940         string = string.substring(linkIndex + linkString.length, string.length);
1941     }
1942
1943     if (string)
1944         container.appendChild(document.createTextNode(string));
1945
1946     return container;
1947 };
1948
1949 WebInspector.linkifyStringAsFragment = function(string)
1950 {
1951     function linkifier(title, url, lineNumber)
1952     {
1953         var urlNode = WebInspector.linkifyURLAsNode(url, title, undefined);
1954         if (lineNumber !== undefined)
1955             urlNode.lineNumber = lineNumber;
1956
1957         return urlNode;
1958     }
1959
1960     return WebInspector.linkifyStringAsFragmentWithCustomLinkifier(string, linkifier);
1961 };
1962
1963 WebInspector._undoKeyboardShortcut = function(event)
1964 {
1965     if (!this.isEditingAnyField() && !this.isEventTargetAnEditableField(event)) {
1966         this.undo();
1967         event.preventDefault();
1968     }
1969 };
1970
1971 WebInspector._redoKeyboardShortcut = function(event)
1972 {
1973     if (!this.isEditingAnyField() && !this.isEventTargetAnEditableField(event)) {
1974         this.redo();
1975         event.preventDefault();
1976     }
1977 };
1978
1979 WebInspector.undo = function()
1980 {
1981     DOMAgent.undo();
1982 };
1983
1984 WebInspector.redo = function()
1985 {
1986     DOMAgent.redo();
1987 };
1988
1989 WebInspector.highlightRangesWithStyleClass = function(element, resultRanges, styleClass, changes)
1990 {
1991     changes = changes || [];
1992     var highlightNodes = [];
1993     var lineText = element.textContent;
1994     var ownerDocument = element.ownerDocument;
1995     var textNodeSnapshot = ownerDocument.evaluate(".//text()", element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1996
1997     var snapshotLength = textNodeSnapshot.snapshotLength;
1998     if (snapshotLength === 0)
1999         return highlightNodes;
2000
2001     var nodeRanges = [];
2002     var rangeEndOffset = 0;
2003     for (var i = 0; i < snapshotLength; ++i) {
2004         var range = {};
2005         range.offset = rangeEndOffset;
2006         range.length = textNodeSnapshot.snapshotItem(i).textContent.length;
2007         rangeEndOffset = range.offset + range.length;
2008         nodeRanges.push(range);
2009     }
2010
2011     var startIndex = 0;
2012     for (var i = 0; i < resultRanges.length; ++i) {
2013         var startOffset = resultRanges[i].offset;
2014         var endOffset = startOffset + resultRanges[i].length;
2015
2016         while (startIndex < snapshotLength && nodeRanges[startIndex].offset + nodeRanges[startIndex].length <= startOffset)
2017             startIndex++;
2018         var endIndex = startIndex;
2019         while (endIndex < snapshotLength && nodeRanges[endIndex].offset + nodeRanges[endIndex].length < endOffset)
2020             endIndex++;
2021         if (endIndex === snapshotLength)
2022             break;
2023
2024         var highlightNode = ownerDocument.createElement("span");
2025         highlightNode.className = styleClass;
2026         highlightNode.textContent = lineText.substring(startOffset, endOffset);
2027
2028         var lastTextNode = textNodeSnapshot.snapshotItem(endIndex);
2029         var lastText = lastTextNode.textContent;
2030         lastTextNode.textContent = lastText.substring(endOffset - nodeRanges[endIndex].offset);
2031         changes.push({ node: lastTextNode, type: "changed", oldText: lastText, newText: lastTextNode.textContent });
2032
2033         if (startIndex === endIndex) {
2034             lastTextNode.parentElement.insertBefore(highlightNode, lastTextNode);
2035             changes.push({ node: highlightNode, type: "added", nextSibling: lastTextNode, parent: lastTextNode.parentElement });
2036             highlightNodes.push(highlightNode);
2037
2038             var prefixNode = ownerDocument.createTextNode(lastText.substring(0, startOffset - nodeRanges[startIndex].offset));
2039             lastTextNode.parentElement.insertBefore(prefixNode, highlightNode);
2040             changes.push({ node: prefixNode, type: "added", nextSibling: highlightNode, parent: lastTextNode.parentElement });
2041         } else {
2042             var firstTextNode = textNodeSnapshot.snapshotItem(startIndex);
2043             var firstText = firstTextNode.textContent;
2044             var anchorElement = firstTextNode.nextSibling;
2045
2046             firstTextNode.parentElement.insertBefore(highlightNode, anchorElement);
2047             changes.push({ node: highlightNode, type: "added", nextSibling: anchorElement, parent: firstTextNode.parentElement });
2048             highlightNodes.push(highlightNode);
2049
2050             firstTextNode.textContent = firstText.substring(0, startOffset - nodeRanges[startIndex].offset);
2051             changes.push({ node: firstTextNode, type: "changed", oldText: firstText, newText: firstTextNode.textContent });
2052
2053             for (var j = startIndex + 1; j < endIndex; j++) {
2054                 var textNode = textNodeSnapshot.snapshotItem(j);
2055                 var text = textNode.textContent;
2056                 textNode.textContent = "";
2057                 changes.push({ node: textNode, type: "changed", oldText: text, newText: textNode.textContent });
2058             }
2059         }
2060         startIndex = endIndex;
2061         nodeRanges[startIndex].offset = endOffset;
2062         nodeRanges[startIndex].length = lastTextNode.textContent.length;
2063
2064     }
2065     return highlightNodes;
2066 };
2067
2068 WebInspector.revertDomChanges = function(domChanges)
2069 {
2070     for (var i = domChanges.length - 1; i >= 0; --i) {
2071         var entry = domChanges[i];
2072         switch (entry.type) {
2073         case "added":
2074             if (entry.node.parentElement)
2075                 entry.node.parentElement.removeChild(entry.node);
2076             break;
2077         case "changed":
2078             entry.node.textContent = entry.oldText;
2079             break;
2080         }
2081     }
2082 };
2083
2084 WebInspector.archiveMainFrame = function()
2085 {
2086     this.notifications.dispatchEventToListeners(WebInspector.Notification.PageArchiveStarted, event);
2087
2088     PageAgent.archive(function(error, data) {
2089         this.notifications.dispatchEventToListeners(WebInspector.Notification.PageArchiveEnded, event);
2090         if (error)
2091             return;
2092
2093         var mainFrame = WebInspector.frameResourceManager.mainFrame;
2094         var archiveName = mainFrame.mainResource.urlComponents.host || mainFrame.mainResource.displayName || "Archive";
2095         var url = "web-inspector:///" + encodeURI(archiveName) + ".webarchive";
2096         InspectorFrontendHost.save(url, data, true, true);
2097     }.bind(this));
2098 };
2099
2100 WebInspector.canArchiveMainFrame = function()
2101 {
2102     if (!PageAgent.archive || this.debuggableType !== WebInspector.DebuggableType.Web)
2103         return false;
2104
2105     return WebInspector.Resource.typeFromMIMEType(WebInspector.frameResourceManager.mainFrame.mainResource.mimeType) === WebInspector.Resource.Type.Document;
2106 };
2107
2108 WebInspector.addWindowKeydownListener = function(listener)
2109 {
2110     if (typeof listener.handleKeydownEvent !== "function")
2111         return;
2112
2113     this._windowKeydownListeners.push(listener);
2114
2115     this._updateWindowKeydownListener();
2116 };
2117
2118 WebInspector.removeWindowKeydownListener = function(listener)
2119 {
2120     this._windowKeydownListeners.remove(listener);
2121
2122     this._updateWindowKeydownListener();
2123 };
2124
2125 WebInspector._updateWindowKeydownListener = function()
2126 {
2127     if (this._windowKeydownListeners.length > 0)
2128         window.addEventListener("keydown", WebInspector._sharedWindowKeydownListener, true);
2129     else
2130         window.removeEventListener("keydown", WebInspector._sharedWindowKeydownListener, true);
2131 };
2132
2133 WebInspector._sharedWindowKeydownListener = function(event)
2134 {
2135     for (var i = WebInspector._windowKeydownListeners.length - 1; i >= 0; --i) {
2136         if (WebInspector._windowKeydownListeners[i].handleKeydownEvent(event)) {
2137             event.stopImmediatePropagation();
2138             event.preventDefault();
2139             break;
2140         }
2141     }
2142 };