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