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