336582dacb6363b5379661589366ca0a2bc48257
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sources / SourcesPanel.js
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2011 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 /**
28  * @constructor
29  * @extends {WebInspector.Panel}
30  * @implements {WebInspector.ContextMenu.Provider}
31  * @implements {WebInspector.TargetManager.Observer}
32  * @param {!WebInspector.Workspace=} workspaceForTest
33  */
34 WebInspector.SourcesPanel = function(workspaceForTest)
35 {
36     WebInspector.Panel.call(this, "sources");
37     this.registerRequiredCSS("sourcesPanel.css");
38     new WebInspector.UpgradeFileSystemDropTarget(this.element);
39
40     this._workspace = workspaceForTest || WebInspector.workspace;
41
42     this.debugToolbar = this._createDebugToolbar();
43     this._debugToolbarDrawer = this._createDebugToolbarDrawer();
44
45     const initialDebugSidebarWidth = 225;
46     this._splitView = new WebInspector.SplitView(true, true, "sourcesPanelSplitViewState", initialDebugSidebarWidth);
47     this._splitView.enableShowModeSaving();
48     this._splitView.show(this.element);
49
50     // Create scripts navigator
51     const initialNavigatorWidth = 225;
52     this.editorView = new WebInspector.SplitView(true, false, "sourcesPanelNavigatorSplitViewState", initialNavigatorWidth);
53     this.editorView.enableShowModeSaving();
54     this.editorView.element.id = "scripts-editor-split-view";
55     this.editorView.element.tabIndex = 0;
56     this.editorView.show(this._splitView.mainElement());
57
58     this._navigator = new WebInspector.SourcesNavigator(this._workspace);
59     this._navigator.view.setMinimumSize(100, 25);
60     this._navigator.view.show(this.editorView.sidebarElement());
61     this._navigator.addEventListener(WebInspector.SourcesNavigator.Events.SourceSelected, this._sourceSelected, this);
62     this._navigator.addEventListener(WebInspector.SourcesNavigator.Events.SourceRenamed, this._sourceRenamed, this);
63
64     this._sourcesView = new WebInspector.SourcesView(this._workspace, this);
65     this._sourcesView.addEventListener(WebInspector.SourcesView.Events.EditorSelected, this._editorSelected.bind(this));
66     this._sourcesView.addEventListener(WebInspector.SourcesView.Events.EditorClosed, this._editorClosed.bind(this));
67     this._sourcesView.registerShortcuts(this.registerShortcuts.bind(this));
68     this._sourcesView.show(this.editorView.mainElement());
69
70     this._debugSidebarResizeWidgetElement = document.createElementWithClass("div", "resizer-widget");
71     this._debugSidebarResizeWidgetElement.id = "scripts-debug-sidebar-resizer-widget";
72     this._splitView.addEventListener(WebInspector.SplitView.Events.ShowModeChanged, this._updateDebugSidebarResizeWidget, this);
73     this._updateDebugSidebarResizeWidget();
74     this._splitView.installResizer(this._debugSidebarResizeWidgetElement);
75
76     // FIXME: This is a temporary solution which should be removed as soon as the documentation module is released from experiment.
77     if (Runtime.experiments.isEnabled("documentation"))
78         self.runtime.loadModule("documentation");
79
80     this.sidebarPanes = {};
81     this.sidebarPanes.threads = new WebInspector.ThreadsSidebarPane();
82     this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane();
83     this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
84     this.sidebarPanes.callstack.addEventListener(WebInspector.CallStackSidebarPane.Events.CallFrameSelected, this._callFrameSelectedInSidebar.bind(this));
85     this.sidebarPanes.callstack.registerShortcuts(this.registerShortcuts.bind(this));
86
87     this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
88     this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane(WebInspector.breakpointManager, this.showUISourceCode.bind(this));
89     this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane.createProxy(this);
90     this.sidebarPanes.xhrBreakpoints = new WebInspector.XHRBreakpointsSidebarPane();
91     this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane();
92
93     this._extensionSidebarPanes = [];
94     this._installDebuggerSidebarController();
95
96     WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this));
97     WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this));
98     this._dockSideChanged();
99
100     this._updateDebuggerButtons();
101     this._pauseOnExceptionEnabledChanged();
102     WebInspector.settings.pauseOnExceptionEnabled.addChangeListener(this._pauseOnExceptionEnabledChanged, this);
103     this._setTarget(WebInspector.context.flavor(WebInspector.Target));
104     WebInspector.breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointsActiveStateChanged, this._breakpointsActiveStateChanged, this);
105     WebInspector.context.addFlavorChangeListener(WebInspector.Target, this._onCurrentTargetChanged, this);
106     WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this);
107     WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._debuggerReset, this);
108     WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
109     WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
110     WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.CallFrameSelected, this._callFrameSelected, this);
111     WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame, this._consoleCommandEvaluatedInSelectedCallFrame, this);
112     WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
113     WebInspector.targetManager.observeTargets(this);
114 }
115
116 WebInspector.SourcesPanel.minToolbarWidth = 215;
117
118 WebInspector.SourcesPanel._infobarSymbol = Symbol("infobar");
119
120 WebInspector.SourcesPanel.prototype = {
121     /**
122      * @param {?WebInspector.Target} target
123      */
124     _setTarget: function(target)
125     {
126         if (!target)
127             return;
128
129         if (target.debuggerModel.isPaused()) {
130             this._showDebuggerPausedDetails(/** @type {!WebInspector.DebuggerPausedDetails} */ (target.debuggerModel.debuggerPausedDetails()));
131             var callFrame = target.debuggerModel.selectedCallFrame();
132             if (callFrame)
133                 this._selectCallFrame(callFrame);
134         } else {
135             this._paused = false;
136             this._clearInterface();
137             this._toggleDebuggerSidebarButton.setEnabled(true);
138         }
139     },
140
141     /**
142      * @param {!WebInspector.Event} event
143      */
144     _onCurrentTargetChanged: function(event)
145     {
146         var target = /** @type {?WebInspector.Target} */ (event.data);
147         this._setTarget(target);
148     },
149
150     /**
151      * @return {!Element}
152      */
153     defaultFocusedElement: function()
154     {
155         return this._sourcesView.defaultFocusedElement();
156     },
157
158     /**
159      * @return {boolean}
160      */
161     paused: function()
162     {
163         return this._paused;
164     },
165
166     wasShown: function()
167     {
168         WebInspector.context.setFlavor(WebInspector.SourcesPanel, this);
169         WebInspector.Panel.prototype.wasShown.call(this);
170     },
171
172     willHide: function()
173     {
174         WebInspector.Panel.prototype.willHide.call(this);
175         WebInspector.context.setFlavor(WebInspector.SourcesPanel, null);
176     },
177
178     /**
179      * @return {!WebInspector.SearchableView}
180      */
181     searchableView: function()
182     {
183         return this._sourcesView.searchableView();
184     },
185
186     _consoleCommandEvaluatedInSelectedCallFrame: function(event)
187     {
188         var target = /** @type {!WebInspector.Target} */  (event.target.target());
189         if (WebInspector.context.flavor(WebInspector.Target) !== target)
190             return;
191         this.sidebarPanes.scopechain.update(target.debuggerModel.selectedCallFrame());
192     },
193
194     /**
195      * @param {!WebInspector.Event} event
196      */
197     _debuggerPaused: function(event)
198     {
199         var details = /** @type {!WebInspector.DebuggerPausedDetails} */ (event.data);
200         if (!this._paused)
201             WebInspector.inspectorView.setCurrentPanel(this);
202
203         if (WebInspector.context.flavor(WebInspector.Target) === details.target())
204             this._showDebuggerPausedDetails(details);
205         else if (!this._paused)
206             WebInspector.context.setFlavor(WebInspector.Target, details.target());
207     },
208
209     /**
210      * @param {!WebInspector.DebuggerPausedDetails} details
211      */
212     _showDebuggerPausedDetails: function(details)
213     {
214         this._paused = true;
215         this._updateDebuggerButtons();
216
217         this.sidebarPanes.callstack.update(details);
218
219         /**
220          * @param {!Element} element
221          * @this {WebInspector.SourcesPanel}
222          */
223         function didCreateBreakpointHitStatusMessage(element)
224         {
225             this.sidebarPanes.callstack.setStatus(element);
226         }
227
228         /**
229          * @param {!WebInspector.UILocation} uiLocation
230          * @this {WebInspector.SourcesPanel}
231          */
232         function didGetUILocation(uiLocation)
233         {
234             var breakpoint = WebInspector.breakpointManager.findBreakpointOnLine(uiLocation.uiSourceCode, uiLocation.lineNumber);
235             if (!breakpoint)
236                 return;
237             this.sidebarPanes.jsBreakpoints.highlightBreakpoint(breakpoint);
238             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a JavaScript breakpoint."));
239         }
240
241         if (details.reason === WebInspector.DebuggerModel.BreakReason.DOM) {
242             WebInspector.domBreakpointsSidebarPane.highlightBreakpoint(details.auxData);
243             WebInspector.domBreakpointsSidebarPane.createBreakpointHitStatusMessage(details, didCreateBreakpointHitStatusMessage.bind(this));
244         } else if (details.reason === WebInspector.DebuggerModel.BreakReason.EventListener) {
245             var eventName = details.auxData["eventName"];
246             var targetName = details.auxData["targetName"];
247             this.sidebarPanes.eventListenerBreakpoints.highlightBreakpoint(eventName, targetName);
248             var eventNameForUI = WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI(eventName, details.auxData);
249             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a \"%s\" Event Listener.", eventNameForUI));
250         } else if (details.reason === WebInspector.DebuggerModel.BreakReason.XHR) {
251             this.sidebarPanes.xhrBreakpoints.highlightBreakpoint(details.auxData["breakpointURL"]);
252             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a XMLHttpRequest."));
253         } else if (details.reason === WebInspector.DebuggerModel.BreakReason.Exception)
254             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on exception: '%s'.", details.auxData["description"]));
255         else if (details.reason === WebInspector.DebuggerModel.BreakReason.Assert)
256             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on assertion."));
257         else if (details.reason === WebInspector.DebuggerModel.BreakReason.CSPViolation)
258             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a script blocked due to Content Security Policy directive: \"%s\".", details.auxData["directiveText"]));
259         else if (details.reason === WebInspector.DebuggerModel.BreakReason.DebugCommand)
260             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a debugged function"));
261         else {
262             if (details.callFrames.length)
263                 WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(details.callFrames[0], didGetUILocation.bind(this));
264             else
265                 console.warn("ScriptsPanel paused, but callFrames.length is zero."); // TODO remove this once we understand this case better
266         }
267
268         this._splitView.showBoth(true);
269         this._toggleDebuggerSidebarButton.setEnabled(false);
270         window.focus();
271         InspectorFrontendHost.bringToFront();
272     },
273
274     /**
275      * @param {!WebInspector.Event} event
276      */
277     _debuggerResumed: function(event)
278     {
279         var target = /** @type {!WebInspector.Target} */  (event.target.target());
280         if (WebInspector.context.flavor(WebInspector.Target) !== target)
281             return;
282         this._paused = false;
283         this._clearInterface();
284         this._toggleDebuggerSidebarButton.setEnabled(true);
285     },
286
287     /**
288      * @param {!WebInspector.Event} event
289      */
290     _debuggerWasEnabled: function(event)
291     {
292         var target = /** @type {!WebInspector.Target} */  (event.target.target());
293         if (WebInspector.context.flavor(WebInspector.Target) !== target)
294             return;
295
296         this._updateDebuggerButtons();
297     },
298
299     /**
300      * @param {!WebInspector.Event} event
301      */
302     _debuggerReset: function(event)
303     {
304         this._debuggerResumed(event);
305     },
306
307     /**
308      * @return {!WebInspector.View}
309      */
310     get visibleView()
311     {
312         return this._sourcesView.visibleView();
313     },
314
315     /**
316      * @param {!WebInspector.UISourceCode} uiSourceCode
317      * @param {number=} lineNumber 0-based
318      * @param {number=} columnNumber
319      * @param {boolean=} forceShowInPanel
320      */
321     showUISourceCode: function(uiSourceCode, lineNumber, columnNumber, forceShowInPanel)
322     {
323         this._showEditor(forceShowInPanel);
324         this._sourcesView.showSourceLocation(uiSourceCode, lineNumber, columnNumber);
325     },
326
327     _showEditor: function(forceShowInPanel)
328     {
329         WebInspector.inspectorView.showPanel("sources");
330     },
331
332     /**
333      * @param {!WebInspector.UILocation} uiLocation
334      * @param {boolean=} forceShowInPanel
335      */
336     showUILocation: function(uiLocation, forceShowInPanel)
337     {
338         this.showUISourceCode(uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber, forceShowInPanel);
339     },
340
341     /**
342      * @param {!WebInspector.UISourceCode} uiSourceCode
343      */
344     _revealInNavigator: function(uiSourceCode)
345     {
346         this._navigator.revealUISourceCode(uiSourceCode);
347     },
348
349     /**
350      * @param {boolean} ignoreExecutionLineEvents
351      */
352     setIgnoreExecutionLineEvents: function(ignoreExecutionLineEvents)
353     {
354         this._ignoreExecutionLineEvents = ignoreExecutionLineEvents;
355     },
356
357     _executionLineChanged: function(uiLocation)
358     {
359         this._sourcesView.clearCurrentExecutionLine();
360         this._sourcesView.setExecutionLine(uiLocation);
361         if (this._ignoreExecutionLineEvents)
362             return;
363         this._sourcesView.showSourceLocation(uiLocation.uiSourceCode, uiLocation.lineNumber, 0, undefined, true);
364     },
365
366     /**
367      * @param {!WebInspector.Event} event
368      */
369     _callFrameSelected: function(event)
370     {
371         var callFrame = /** @type {?WebInspector.DebuggerModel.CallFrame} */ (event.data);
372
373         if (!callFrame || callFrame.target() !== WebInspector.context.flavor(WebInspector.Target))
374             return;
375
376         this._selectCallFrame(callFrame);
377     },
378
379     /**
380      * @param {!WebInspector.DebuggerModel.CallFrame}  callFrame
381      */
382     _selectCallFrame: function(callFrame)
383     {
384         this.sidebarPanes.scopechain.update(callFrame);
385         this.sidebarPanes.watchExpressions.refreshExpressions();
386         this.sidebarPanes.callstack.setSelectedCallFrame(callFrame);
387         WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(callFrame, this._executionLineChanged.bind(this));
388     },
389
390     /**
391      * @param {!WebInspector.Event} event
392      */
393     _sourceSelected: function(event)
394     {
395         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.uiSourceCode);
396         this._sourcesView.showSourceLocation(uiSourceCode, undefined, undefined, !event.data.focusSource)
397     },
398
399     /**
400      * @param {!WebInspector.Event} event
401      */
402     _sourceRenamed: function(event)
403     {
404         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
405         this._sourcesView.sourceRenamed(uiSourceCode);
406     },
407
408     _pauseOnExceptionEnabledChanged: function()
409     {
410         var enabled = WebInspector.settings.pauseOnExceptionEnabled.get();
411         this._pauseOnExceptionButton.toggled = enabled;
412         this._pauseOnExceptionButton.title = WebInspector.UIString(enabled ? "Don't pause on exceptions." : "Pause on exceptions.");
413         this._debugToolbarDrawer.classList.toggle("expanded", enabled);
414     },
415
416     _updateDebuggerButtons: function()
417     {
418         var currentTarget = WebInspector.context.flavor(WebInspector.Target);
419         if (!currentTarget)
420             return;
421
422         if (this._paused) {
423             this._updateButtonTitle(this._pauseButton, WebInspector.UIString("Resume script execution (%s)."))
424             this._pauseButton.state = true;
425             this._pauseButton.setLongClickOptionsEnabled((function() { return [ this._longResumeButton ] }).bind(this));
426
427             this._pauseButton.setEnabled(true);
428             this._stepOverButton.setEnabled(true);
429             this._stepIntoButton.setEnabled(true);
430             this._stepOutButton.setEnabled(true);
431         } else {
432             this._updateButtonTitle(this._pauseButton, WebInspector.UIString("Pause script execution (%s)."))
433             this._pauseButton.state = false;
434             this._pauseButton.setLongClickOptionsEnabled(null);
435
436             this._pauseButton.setEnabled(!currentTarget.debuggerModel.isPausing());
437             this._stepOverButton.setEnabled(false);
438             this._stepIntoButton.setEnabled(false);
439             this._stepOutButton.setEnabled(false);
440         }
441     },
442
443     _clearInterface: function()
444     {
445         this.sidebarPanes.callstack.update(null);
446         this.sidebarPanes.scopechain.update(null);
447         this.sidebarPanes.jsBreakpoints.clearBreakpointHighlight();
448         WebInspector.domBreakpointsSidebarPane.clearBreakpointHighlight();
449         this.sidebarPanes.eventListenerBreakpoints.clearBreakpointHighlight();
450         this.sidebarPanes.xhrBreakpoints.clearBreakpointHighlight();
451
452         this._sourcesView.clearCurrentExecutionLine();
453         this._updateDebuggerButtons();
454     },
455
456     _togglePauseOnExceptions: function()
457     {
458         WebInspector.settings.pauseOnExceptionEnabled.set(!this._pauseOnExceptionButton.toggled);
459     },
460
461     /**
462      * @return {boolean}
463      */
464     _runSnippet: function()
465     {
466         var uiSourceCode = this._sourcesView.currentUISourceCode();
467         if (uiSourceCode.project().type() !== WebInspector.projectTypes.Snippets)
468             return false;
469
470         var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
471         if (!currentExecutionContext)
472             return false;
473
474         WebInspector.scriptSnippetModel.evaluateScriptSnippet(currentExecutionContext, uiSourceCode);
475         return true;
476     },
477
478     /**
479      * @param {!WebInspector.Event} event
480      */
481     _editorSelected: function(event)
482     {
483         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
484         this._editorChanged(uiSourceCode);
485         if (Runtime.experiments.isEnabled("suggestUsingWorkspace")) {
486             if (this._editorSelectedTimer)
487                 clearTimeout(this._editorSelectedTimer);
488             this._editorSelectedTimer = setTimeout(this._updateSuggestedMappingInfobar.bind(this, uiSourceCode), 3000);
489         }
490     },
491
492     /**
493      * @param {!WebInspector.UISourceCode} uiSourceCode
494      */
495     _updateSuggestedMappingInfobar: function(uiSourceCode)
496     {
497         if (!this.isShowing())
498             return;
499         if (uiSourceCode[WebInspector.SourcesPanel._infobarSymbol])
500             return;
501
502         // First try mapping filesystem -> network.
503         var uiSourceCodeFrame = this._sourcesView.viewForFile(uiSourceCode);
504         if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSystem) {
505             var hasMappings = !!uiSourceCode.url;
506             if (hasMappings)
507                 return;
508
509             var targets = WebInspector.targetManager.targets();
510             for (var i = 0; i < targets.length; ++i)
511                 targets[i].resourceTreeModel.forAllResources(matchResource.bind(this));
512         }
513
514         /**
515          * @param {!WebInspector.Resource} resource
516          * @return {boolean}
517          * @this {WebInspector.SourcesPanel}
518          */
519         function matchResource(resource)
520         {
521             if (resource.contentURL().endsWith(uiSourceCode.name())) {
522                 createMappingInfobar.call(this, false);
523                 return true;
524             }
525             return false;
526         }
527
528         // Then map network -> filesystem.
529         if (uiSourceCode.project().type() === WebInspector.projectTypes.Network || uiSourceCode.project().type() === WebInspector.projectTypes.ContentScripts) {
530             if (this._workspace.uiSourceCodeForURL(uiSourceCode.url) !== uiSourceCode)
531                 return;
532
533             var filesystemProjects = this._workspace.projectsForType(WebInspector.projectTypes.FileSystem);
534             for (var i = 0; i < filesystemProjects.length; ++i) {
535                 var name = uiSourceCode.name();
536                 var fsUiSourceCodes = filesystemProjects[i].uiSourceCodes();
537                 for (var j = 0; j < fsUiSourceCodes.length; ++j) {
538                     if (fsUiSourceCodes[j].name() === name) {
539                         createMappingInfobar.call(this, true);
540                         return;
541                     }
542                 }
543             }
544
545             // There are no matching filesystems. Suggest adding a filesystem in case of localhost.
546             var originURL = uiSourceCode.originURL().asParsedURL();
547             if (originURL && originURL.host === "localhost")
548                 createWorkspaceInfobar();
549         }
550
551         /**
552          * @param {boolean} isNetwork
553          * @this {WebInspector.SourcesPanel}
554          */
555         function createMappingInfobar(isNetwork)
556         {
557             var title;
558             if (isNetwork)
559                 title = WebInspector.UIString("Map network resource '%s' to workspace?", uiSourceCode.originURL());
560             else
561                 title = WebInspector.UIString("Map workspace resource '%s' to network?", uiSourceCode.path());
562
563             var infobar = new WebInspector.UISourceCodeFrame.Infobar(WebInspector.UISourceCodeFrame.Infobar.Level.Info, title);
564             infobar.createDetailsRowMessage(WebInspector.UIString("You can map files in your workspace to the ones loaded over the network. As a result, changes made in DevTools will be persisted to disk."));
565             infobar.createDetailsRowMessage(WebInspector.UIString("Use context menu to establish the mapping at any time."));
566             var actionLink = infobar.createDetailsRowMessage("").createChild("a");
567             actionLink.href = "";
568             actionLink.onclick = establishTheMapping.bind(this);
569             actionLink.textContent = WebInspector.UIString("Establish the mapping now...");
570             appendInfobar(infobar);
571         }
572
573         /**
574          * @param {?Event} event
575          * @this {WebInspector.SourcesPanel}
576          */
577         function establishTheMapping(event)
578         {
579             event.consume(true);
580             if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSystem)
581                 this._mapFileSystemToNetwork(uiSourceCode);
582             else
583                 this._mapNetworkToFileSystem(uiSourceCode);
584         }
585
586         function createWorkspaceInfobar()
587         {
588             var infobar = new WebInspector.UISourceCodeFrame.Infobar(WebInspector.UISourceCodeFrame.Infobar.Level.Info, WebInspector.UIString("Serving from the file system? Add your files into the workspace."));
589             infobar.createDetailsRowMessage(WebInspector.UIString("If you add files into your DevTools workspace, your changes will be persisted to disk."));
590             infobar.createDetailsRowMessage(WebInspector.UIString("To add a folder into the workspace, drag and drop it into the Sources panel."));
591             appendInfobar(infobar);
592         }
593
594         /**
595          * @param {!WebInspector.UISourceCodeFrame.Infobar} infobar
596          */
597         function appendInfobar(infobar)
598         {
599             infobar.createDetailsRowMessage("").createChild("br");
600             var rowElement = infobar.createDetailsRowMessage(WebInspector.UIString("For more information on workspaces, refer to the "));
601             var a = rowElement.createChild("a");
602             a.textContent = "workspaces documentation";
603             a.href = "https://developer.chrome.com/devtools/docs/workspaces";
604             rowElement.createTextChild(".");
605             uiSourceCode[WebInspector.SourcesPanel._infobarSymbol] = infobar;
606             uiSourceCodeFrame.attachInfobars([infobar]);
607         }
608     },
609
610     /**
611      * @param {!WebInspector.Event} event
612      */
613     _editorClosed: function(event)
614     {
615         var wasSelected = /** @type {boolean} */ (event.data.wasSelected);
616         if (wasSelected)
617             this._editorChanged(null);
618     },
619
620     /**
621      * @param {?WebInspector.UISourceCode} uiSourceCode
622      */
623     _editorChanged: function(uiSourceCode)
624     {
625         var isSnippet = uiSourceCode && uiSourceCode.project().type() === WebInspector.projectTypes.Snippets;
626         this._runSnippetButton.element.classList.toggle("hidden", !isSnippet);
627     },
628
629     /**
630      * @return {boolean}
631      */
632     togglePause: function()
633     {
634         var target = WebInspector.context.flavor(WebInspector.Target);
635         if (!target)
636             return true;
637
638         if (this._paused) {
639             this._paused = false;
640             target.debuggerModel.resume();
641         } else {
642             // Make sure pauses didn't stick skipped.
643             target.debuggerModel.pause();
644         }
645
646         this._clearInterface();
647         return true;
648     },
649
650     /**
651      * @return {?WebInspector.DebuggerModel}
652      */
653     _prepareToResume: function()
654     {
655         if (!this._paused)
656             return null;
657
658         this._paused = false;
659
660         this._clearInterface();
661         var target = WebInspector.context.flavor(WebInspector.Target);
662         return target ? target.debuggerModel : null;
663     },
664
665     /**
666      * @return {boolean}
667      */
668     _longResume: function()
669     {
670         var debuggerModel = this._prepareToResume();
671         if (!debuggerModel)
672             return true;
673
674         debuggerModel.skipAllPausesUntilReloadOrTimeout(500);
675         debuggerModel.resume();
676         return true;
677     },
678
679     /**
680      * @return {boolean}
681      */
682     _stepOverClicked: function()
683     {
684         var debuggerModel = this._prepareToResume();
685         if (!debuggerModel)
686             return true;
687
688         debuggerModel.stepOver();
689         return true;
690     },
691
692     /**
693      * @return {boolean}
694      */
695     _stepIntoClicked: function()
696     {
697         var debuggerModel = this._prepareToResume();
698         if (!debuggerModel)
699             return true;
700
701         debuggerModel.stepInto();
702         return true;
703     },
704
705     /**
706      * @return {boolean}
707      */
708     _stepOutClicked: function()
709     {
710         var debuggerModel = this._prepareToResume();
711         if (!debuggerModel)
712             return true;
713
714         debuggerModel.stepOut();
715         return true;
716     },
717
718     /**
719      * @param {!WebInspector.Event} event
720      */
721     _callFrameSelectedInSidebar: function(event)
722     {
723         var callFrame = /** @type {!WebInspector.DebuggerModel.CallFrame} */ (event.data);
724         callFrame.target().debuggerModel.setSelectedCallFrame(callFrame);
725     },
726
727     /**
728      * @param {!WebInspector.DebuggerModel.Location} rawLocation
729      */
730     continueToLocation: function(rawLocation)
731     {
732         if (!this._prepareToResume())
733             return;
734
735         rawLocation.continueToLocation();
736     },
737
738     _toggleBreakpointsClicked: function(event)
739     {
740         WebInspector.breakpointManager.setBreakpointsActive(!WebInspector.breakpointManager.breakpointsActive());
741     },
742
743     _breakpointsActiveStateChanged: function(event)
744     {
745         var active = event.data;
746         this._toggleBreakpointsButton.toggled = !active;
747         this.sidebarPanes.jsBreakpoints.listElement.classList.toggle("breakpoints-list-deactivated", !active);
748         this._sourcesView.toggleBreakpointsActiveState(active);
749         if (active)
750             this._toggleBreakpointsButton.title = WebInspector.UIString("Deactivate breakpoints.");
751         else
752             this._toggleBreakpointsButton.title = WebInspector.UIString("Activate breakpoints.");
753     },
754
755     _createDebugToolbar: function()
756     {
757         var debugToolbar = document.createElementWithClass("div", "scripts-debug-toolbar");
758
759         var title, handler;
760         var platformSpecificModifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta;
761
762         // Run snippet.
763         title = WebInspector.UIString("Run snippet (%s).");
764         handler = this._runSnippet.bind(this);
765         this._runSnippetButton = this._createButtonAndRegisterShortcuts("scripts-run-snippet", title, handler, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.RunSnippet);
766         debugToolbar.appendChild(this._runSnippetButton.element);
767         this._runSnippetButton.element.classList.add("hidden");
768
769         // Continue.
770         this._pauseButton = this._createButtonAndRegisterShortcutsForAction("scripts-pause", "", "debugger.toggle-pause");
771         debugToolbar.appendChild(this._pauseButton.element);
772
773         // Long resume.
774         title = WebInspector.UIString("Resume with all pauses blocked for 500 ms");
775         this._longResumeButton = new WebInspector.StatusBarButton(title, "scripts-long-resume");
776         this._longResumeButton.addEventListener("click", this._longResume.bind(this), this);
777
778         // Step over.
779         title = WebInspector.UIString("Step over next function call (%s).");
780         handler = this._stepOverClicked.bind(this);
781         this._stepOverButton = this._createButtonAndRegisterShortcuts("scripts-step-over", title, handler, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.StepOver);
782         debugToolbar.appendChild(this._stepOverButton.element);
783
784         // Step into.
785         title = WebInspector.UIString("Step into next function call (%s).");
786         handler = this._stepIntoClicked.bind(this);
787         this._stepIntoButton = this._createButtonAndRegisterShortcuts("scripts-step-into", title, handler, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.StepInto);
788         debugToolbar.appendChild(this._stepIntoButton.element);
789
790         // Step out.
791         title = WebInspector.UIString("Step out of current function (%s).");
792         handler = this._stepOutClicked.bind(this);
793         this._stepOutButton = this._createButtonAndRegisterShortcuts("scripts-step-out", title, handler, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.StepOut);
794         debugToolbar.appendChild(this._stepOutButton.element);
795
796         // Toggle Breakpoints
797         this._toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate breakpoints."), "scripts-toggle-breakpoints");
798         this._toggleBreakpointsButton.toggled = false;
799         this._toggleBreakpointsButton.addEventListener("click", this._toggleBreakpointsClicked, this);
800         debugToolbar.appendChild(this._toggleBreakpointsButton.element);
801
802         // Pause on Exception
803         this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item");
804         this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions, this);
805         debugToolbar.appendChild(this._pauseOnExceptionButton.element);
806
807         return debugToolbar;
808     },
809
810     _createDebugToolbarDrawer: function()
811     {
812         var debugToolbarDrawer = document.createElementWithClass("div", "scripts-debug-toolbar-drawer");
813
814         var label = WebInspector.UIString("Pause On Caught Exceptions");
815         var setting = WebInspector.settings.pauseOnCaughtException;
816         debugToolbarDrawer.appendChild(WebInspector.SettingsUI.createSettingCheckbox(label, setting, true));
817
818         return debugToolbarDrawer;
819     },
820
821     /**
822      * @param {!WebInspector.StatusBarButton} button
823      * @param {string} buttonTitle
824      */
825     _updateButtonTitle: function(button, buttonTitle)
826     {
827         var hasShortcuts = button.shortcuts && button.shortcuts.length;
828         if (hasShortcuts)
829             button.title = String.vsprintf(buttonTitle, [button.shortcuts[0].name]);
830         else
831             button.title = buttonTitle;
832     },
833
834     /**
835      * @param {string} buttonId
836      * @param {string} buttonTitle
837      * @param {function(!Event=):boolean} handler
838      * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} shortcuts
839      * @return {!WebInspector.StatusBarButton}
840      */
841     _createButtonAndRegisterShortcuts: function(buttonId, buttonTitle, handler, shortcuts)
842     {
843         var button = new WebInspector.StatusBarButton(buttonTitle, buttonId);
844         button.element.addEventListener("click", handler, false);
845         button.shortcuts = shortcuts;
846         this._updateButtonTitle(button, buttonTitle);
847         this.registerShortcuts(shortcuts, handler);
848         return button;
849     },
850
851     /**
852      * @param {string} buttonId
853      * @param {string} buttonTitle
854      * @param {string} actionId
855      * @return {!WebInspector.StatusBarButton}
856      */
857     _createButtonAndRegisterShortcutsForAction: function(buttonId, buttonTitle, actionId)
858     {
859         /**
860          * @return {boolean}
861          */
862         function handler()
863         {
864             return WebInspector.actionRegistry.execute(actionId);
865         }
866         var shortcuts = WebInspector.shortcutRegistry.shortcutDescriptorsForAction(actionId);
867         return this._createButtonAndRegisterShortcuts(buttonId, buttonTitle, handler, shortcuts);
868     },
869
870     addToWatch: function(expression)
871     {
872         this.sidebarPanes.watchExpressions.addExpression(expression);
873     },
874
875     _installDebuggerSidebarController: function()
876     {
877         this._toggleNavigatorSidebarButton = this.editorView.createShowHideSidebarButton("navigator", "scripts-navigator-show-hide-button");
878         this.editorView.mainElement().appendChild(this._toggleNavigatorSidebarButton.element);
879
880         this._toggleDebuggerSidebarButton = this._splitView.createShowHideSidebarButton("debugger", "scripts-debugger-show-hide-button");
881
882         this._splitView.mainElement().appendChild(this._toggleDebuggerSidebarButton.element);
883         this._splitView.mainElement().appendChild(this._debugSidebarResizeWidgetElement);
884     },
885
886     _updateDebugSidebarResizeWidget: function()
887     {
888         this._debugSidebarResizeWidgetElement.classList.toggle("hidden", this._splitView.showMode() !== WebInspector.SplitView.ShowMode.Both);
889     },
890
891     /**
892      * @param {!WebInspector.UISourceCode} uiSourceCode
893      */
894     _showLocalHistory: function(uiSourceCode)
895     {
896         WebInspector.RevisionHistoryView.showHistory(uiSourceCode);
897     },
898
899     /**
900      * @param {!Event} event
901      * @param {!WebInspector.ContextMenu} contextMenu
902      * @param {!Object} target
903      */
904     appendApplicableItems: function(event, contextMenu, target)
905     {
906         this._appendUISourceCodeItems(event, contextMenu, target);
907         this._appendRemoteObjectItems(contextMenu, target);
908     },
909
910     _suggestReload: function()
911     {
912         if (window.confirm(WebInspector.UIString("It is recommended to restart inspector after making these changes. Would you like to restart it?")))
913             WebInspector.reload();
914     },
915
916     /**
917      * @param {!WebInspector.UISourceCode} uiSourceCode
918      */
919     _mapFileSystemToNetwork: function(uiSourceCode)
920     {
921         WebInspector.SelectUISourceCodeForProjectTypesDialog.show(uiSourceCode.name(), [WebInspector.projectTypes.Network, WebInspector.projectTypes.ContentScripts], mapFileSystemToNetwork.bind(this), this.editorView.mainElement())
922
923         /**
924          * @param {!WebInspector.UISourceCode} networkUISourceCode
925          * @this {WebInspector.SourcesPanel}
926          */
927         function mapFileSystemToNetwork(networkUISourceCode)
928         {
929             this._workspace.addMapping(networkUISourceCode, uiSourceCode, WebInspector.fileSystemWorkspaceBinding);
930             this._suggestReload();
931         }
932     },
933
934     /**
935      * @param {!WebInspector.UISourceCode} uiSourceCode
936      */
937     _removeNetworkMapping: function(uiSourceCode)
938     {
939         if (confirm(WebInspector.UIString("Are you sure you want to remove network mapping?"))) {
940             this._workspace.removeMapping(uiSourceCode);
941             this._suggestReload();
942         }
943     },
944
945     /**
946      * @param {!WebInspector.UISourceCode} networkUISourceCode
947      */
948     _mapNetworkToFileSystem: function(networkUISourceCode)
949     {
950         WebInspector.SelectUISourceCodeForProjectTypesDialog.show(networkUISourceCode.name(), [WebInspector.projectTypes.FileSystem], mapNetworkToFileSystem.bind(this), this.editorView.mainElement())
951
952         /**
953          * @param {!WebInspector.UISourceCode} uiSourceCode
954          * @this {WebInspector.SourcesPanel}
955          */
956         function mapNetworkToFileSystem(uiSourceCode)
957         {
958             this._workspace.addMapping(networkUISourceCode, uiSourceCode, WebInspector.fileSystemWorkspaceBinding);
959             this._suggestReload();
960         }
961     },
962
963     /**
964      * @param {!WebInspector.ContextMenu} contextMenu
965      * @param {!WebInspector.UISourceCode} uiSourceCode
966      */
967     _appendUISourceCodeMappingItems: function(contextMenu, uiSourceCode)
968     {
969         if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSystem) {
970             var hasMappings = !!uiSourceCode.url;
971             if (!hasMappings)
972                 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Map to network resource\u2026" : "Map to Network Resource\u2026"), this._mapFileSystemToNetwork.bind(this, uiSourceCode));
973             else
974                 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove network mapping" : "Remove Network Mapping"), this._removeNetworkMapping.bind(this, uiSourceCode));
975         }
976
977         /**
978          * @param {!WebInspector.Project} project
979          */
980         function filterProject(project)
981         {
982             return project.type() === WebInspector.projectTypes.FileSystem;
983         }
984
985         if (uiSourceCode.project().type() === WebInspector.projectTypes.Network || uiSourceCode.project().type() === WebInspector.projectTypes.ContentScripts) {
986             if (!this._workspace.projects().filter(filterProject).length)
987                 return;
988             if (this._workspace.uiSourceCodeForURL(uiSourceCode.url) === uiSourceCode)
989                 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Map to file system resource\u2026" : "Map to File System Resource\u2026"), this._mapNetworkToFileSystem.bind(this, uiSourceCode));
990         }
991     },
992
993     /**
994      * @param {!Event} event
995      * @param {!WebInspector.ContextMenu} contextMenu
996      * @param {!Object} target
997      */
998     _appendUISourceCodeItems: function(event, contextMenu, target)
999     {
1000         if (!(target instanceof WebInspector.UISourceCode))
1001             return;
1002
1003         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
1004         var projectType = uiSourceCode.project().type();
1005         if (projectType !== WebInspector.projectTypes.FileSystem)
1006             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Local modifications\u2026" : "Local Modifications\u2026"), this._showLocalHistory.bind(this, uiSourceCode));
1007         this._appendUISourceCodeMappingItems(contextMenu, uiSourceCode);
1008
1009         var contentType = uiSourceCode.contentType();
1010         if ((contentType === WebInspector.resourceTypes.Script || contentType === WebInspector.resourceTypes.Document) && projectType !== WebInspector.projectTypes.Snippets)
1011             this.sidebarPanes.callstack.appendBlackboxURLContextMenuItems(contextMenu, uiSourceCode.url, projectType === WebInspector.projectTypes.ContentScripts);
1012
1013         if (!event.target.isSelfOrDescendant(this.editorView.sidebarElement())) {
1014             contextMenu.appendSeparator();
1015             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in navigator" : "Reveal in Navigator"), this._handleContextMenuReveal.bind(this, uiSourceCode));
1016         }
1017     },
1018
1019     /**
1020      * @param {!WebInspector.UISourceCode} uiSourceCode
1021      */
1022     _handleContextMenuReveal: function(uiSourceCode)
1023     {
1024         this.editorView.showBoth();
1025         this._revealInNavigator(uiSourceCode);
1026     },
1027
1028     /**
1029      * @param {!WebInspector.ContextMenu} contextMenu
1030      * @param {!Object} target
1031      */
1032     _appendRemoteObjectItems: function(contextMenu, target)
1033     {
1034         if (!(target instanceof WebInspector.RemoteObject))
1035             return;
1036         var remoteObject = /** @type {!WebInspector.RemoteObject} */ (target);
1037         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Store as global variable" : "Store as Global Variable"), this._saveToTempVariable.bind(this, remoteObject));
1038         if (remoteObject.type === "function")
1039             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Show function definition" : "Show Function Definition"), this._showFunctionDefinition.bind(this, remoteObject));
1040     },
1041
1042     /**
1043      * @param {!WebInspector.RemoteObject} remoteObject
1044      */
1045     _saveToTempVariable: function(remoteObject)
1046     {
1047         var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
1048         if (!currentExecutionContext)
1049             return;
1050
1051         currentExecutionContext.evaluate("this", "", false, true, false, false, didGetGlobalObject.bind(null, currentExecutionContext.target()));
1052         /**
1053          * @param {!WebInspector.Target} target
1054          * @param {?WebInspector.RemoteObject} global
1055          * @param {boolean=} wasThrown
1056          */
1057         function didGetGlobalObject(target, global, wasThrown)
1058         {
1059             /**
1060              * @suppressReceiverCheck
1061              * @this {Window}
1062              */
1063             function remoteFunction(value)
1064             {
1065                 var prefix = "temp";
1066                 var index = 1;
1067                 while ((prefix + index) in this)
1068                     ++index;
1069                 var name = prefix + index;
1070                 this[name] = value;
1071                 return name;
1072             }
1073
1074             if (wasThrown || !global)
1075                 failedToSave(target, global);
1076             else
1077                 global.callFunction(remoteFunction, [WebInspector.RemoteObject.toCallArgument(remoteObject)], didSave.bind(null, global));
1078         }
1079
1080         /**
1081          * @param {!WebInspector.RemoteObject} global
1082          * @param {?WebInspector.RemoteObject} result
1083          * @param {boolean=} wasThrown
1084          */
1085         function didSave(global, result, wasThrown)
1086         {
1087             var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
1088             global.release();
1089             if (!currentExecutionContext || wasThrown || !result || result.type !== "string")
1090                 failedToSave(global.target(), result);
1091             else
1092                 WebInspector.ConsoleModel.evaluateCommandInConsole(currentExecutionContext, result.value);
1093         }
1094
1095         /**
1096          * @param {!WebInspector.Target} target
1097          * @param {?WebInspector.RemoteObject} result
1098          */
1099         function failedToSave(target, result)
1100         {
1101             var message = WebInspector.UIString("Failed to save to temp variable.");
1102             if (result) {
1103                 message += " " + result.description;
1104                 result.release();
1105             }
1106             WebInspector.console.error(message);
1107         }
1108     },
1109
1110     /**
1111      * @param {!WebInspector.RemoteObject} remoteObject
1112      */
1113     _showFunctionDefinition: function(remoteObject)
1114     {
1115         var debuggerModel = remoteObject.target().debuggerModel;
1116
1117         /**
1118          * @param {?WebInspector.DebuggerModel.FunctionDetails} response
1119          * @this {WebInspector.SourcesPanel}
1120          */
1121         function didGetFunctionDetails(response)
1122         {
1123             if (!response || !response.location)
1124                 return;
1125
1126             var location = response.location;
1127             if (!location)
1128                 return;
1129
1130             var uiLocation = WebInspector.debuggerWorkspaceBinding.rawLocationToUILocation(location);
1131             if (uiLocation)
1132                 this.showUILocation(uiLocation, true);
1133         }
1134         debuggerModel.functionDetails(remoteObject, didGetFunctionDetails.bind(this));
1135     },
1136
1137     showGoToSourceDialog: function()
1138     {
1139         this._sourcesView.showOpenResourceDialog();
1140     },
1141
1142     _dockSideChanged: function()
1143     {
1144         var vertically = WebInspector.dockController.isVertical() && WebInspector.settings.splitVerticallyWhenDockedToRight.get();
1145         this._splitVertically(vertically);
1146     },
1147
1148     /**
1149      * @param {boolean} vertically
1150      */
1151     _splitVertically: function(vertically)
1152     {
1153         if (this.sidebarPaneView && vertically === !this._splitView.isVertical())
1154             return;
1155
1156         if (this.sidebarPaneView)
1157             this.sidebarPaneView.detach();
1158
1159         this._splitView.setVertical(!vertically);
1160
1161         if (!vertically)
1162             this._splitView.uninstallResizer(this._sourcesView.statusBarContainerElement());
1163         else
1164             this._splitView.installResizer(this._sourcesView.statusBarContainerElement());
1165
1166         // Create vertical box with stack.
1167         var vbox = new WebInspector.VBox();
1168         vbox.element.appendChild(this._debugToolbarDrawer);
1169         vbox.element.appendChild(this.debugToolbar);
1170         vbox.setMinimumAndPreferredSizes(25, 25, WebInspector.SourcesPanel.minToolbarWidth, 100);
1171         var sidebarPaneStack = new WebInspector.SidebarPaneStack();
1172         sidebarPaneStack.element.classList.add("flex-auto");
1173         sidebarPaneStack.show(vbox.element);
1174
1175         if (!vertically) {
1176             // Populate the only stack.
1177             for (var pane in this.sidebarPanes)
1178                 sidebarPaneStack.addPane(this.sidebarPanes[pane]);
1179             this._extensionSidebarPanesContainer = sidebarPaneStack;
1180             this.sidebarPaneView = vbox;
1181         } else {
1182             var splitView = new WebInspector.SplitView(true, true, "sourcesPanelDebuggerSidebarSplitViewState", 0.5);
1183             vbox.show(splitView.mainElement());
1184
1185             // Populate the left stack.
1186             sidebarPaneStack.addPane(this.sidebarPanes.threads);
1187             sidebarPaneStack.addPane(this.sidebarPanes.callstack);
1188             sidebarPaneStack.addPane(this.sidebarPanes.jsBreakpoints);
1189             sidebarPaneStack.addPane(this.sidebarPanes.domBreakpoints);
1190             sidebarPaneStack.addPane(this.sidebarPanes.xhrBreakpoints);
1191             sidebarPaneStack.addPane(this.sidebarPanes.eventListenerBreakpoints);
1192
1193             var tabbedPane = new WebInspector.SidebarTabbedPane();
1194             tabbedPane.show(splitView.sidebarElement());
1195             tabbedPane.addPane(this.sidebarPanes.scopechain);
1196             tabbedPane.addPane(this.sidebarPanes.watchExpressions);
1197             this._extensionSidebarPanesContainer = tabbedPane;
1198
1199             this.sidebarPaneView = splitView;
1200         }
1201         for (var i = 0; i < this._extensionSidebarPanes.length; ++i)
1202             this._extensionSidebarPanesContainer.addPane(this._extensionSidebarPanes[i]);
1203
1204         this.sidebarPaneView.show(this._splitView.sidebarElement());
1205         this.sidebarPanes.threads.expand();
1206         this.sidebarPanes.scopechain.expand();
1207         this.sidebarPanes.jsBreakpoints.expand();
1208         this.sidebarPanes.callstack.expand();
1209         this._sidebarPaneStack = sidebarPaneStack;
1210         this._updateTargetsSidebarVisibility();
1211         if (WebInspector.settings.watchExpressions.get().length > 0)
1212             this.sidebarPanes.watchExpressions.expand();
1213     },
1214
1215     /**
1216      * @param {string} id
1217      * @param {!WebInspector.SidebarPane} pane
1218      */
1219     addExtensionSidebarPane: function(id, pane)
1220     {
1221         this._extensionSidebarPanes.push(pane);
1222         this._extensionSidebarPanesContainer.addPane(pane);
1223         this.setHideOnDetach();
1224     },
1225
1226     /**
1227      * @return {!WebInspector.SourcesView}
1228      */
1229     sourcesView: function()
1230     {
1231         return this._sourcesView;
1232     },
1233
1234     /**
1235      * @param {!WebInspector.Target} target
1236      */
1237     targetAdded: function(target)
1238     {
1239         this._updateTargetsSidebarVisibility();
1240     },
1241
1242     /**
1243      * @param {!WebInspector.Target} target
1244      */
1245     targetRemoved: function(target)
1246     {
1247         this._updateTargetsSidebarVisibility();
1248     },
1249
1250     _updateTargetsSidebarVisibility: function()
1251     {
1252         if (!this._sidebarPaneStack)
1253             return;
1254         this._sidebarPaneStack.togglePaneHidden(this.sidebarPanes.threads, WebInspector.targetManager.targets().length < 2);
1255     },
1256
1257     __proto__: WebInspector.Panel.prototype
1258 }
1259
1260 /**
1261  * @constructor
1262  * @param {!Element} element
1263  */
1264 WebInspector.UpgradeFileSystemDropTarget = function(element)
1265 {
1266     element.addEventListener("dragenter", this._onDragEnter.bind(this), true);
1267     element.addEventListener("dragover", this._onDragOver.bind(this), true);
1268     this._element = element;
1269 }
1270
1271 WebInspector.UpgradeFileSystemDropTarget.dragAndDropFilesType = "Files";
1272
1273 WebInspector.UpgradeFileSystemDropTarget.prototype = {
1274     _onDragEnter: function (event)
1275     {
1276         if (event.dataTransfer.types.indexOf(WebInspector.UpgradeFileSystemDropTarget.dragAndDropFilesType) === -1)
1277             return;
1278         event.consume(true);
1279     },
1280
1281     _onDragOver: function (event)
1282     {
1283         if (event.dataTransfer.types.indexOf(WebInspector.UpgradeFileSystemDropTarget.dragAndDropFilesType) === -1)
1284             return;
1285         event.dataTransfer.dropEffect = "copy";
1286         event.consume(true);
1287         if (this._dragMaskElement)
1288             return;
1289         this._dragMaskElement = this._element.createChild("div", "fill drag-mask");
1290         this._dragMaskElement.createChild("div", "fill drag-mask-inner").textContent = WebInspector.UIString("Drop workspace folder here");
1291         this._dragMaskElement.addEventListener("drop", this._onDrop.bind(this), true);
1292         this._dragMaskElement.addEventListener("dragleave", this._onDragLeave.bind(this), true);
1293     },
1294
1295     _onDrop: function (event)
1296     {
1297         event.consume(true);
1298         this._removeMask();
1299         var items = /** @type {!Array.<!DataTransferItem>} */ (event.dataTransfer.items);
1300         if (!items.length)
1301             return;
1302         var entry = items[0].webkitGetAsEntry();
1303         if (!entry.isDirectory)
1304             return;
1305         InspectorFrontendHost.upgradeDraggedFileSystemPermissions(entry.filesystem);
1306     },
1307
1308     _onDragLeave: function (event)
1309     {
1310         event.consume(true);
1311         this._removeMask();
1312     },
1313
1314     _removeMask: function ()
1315     {
1316         this._dragMaskElement.remove();
1317         delete this._dragMaskElement;
1318     }
1319 }
1320
1321 /**
1322  * @constructor
1323  * @implements {WebInspector.ContextMenu.Provider}
1324  */
1325 WebInspector.SourcesPanel.ContextMenuProvider = function()
1326 {
1327 }
1328
1329 WebInspector.SourcesPanel.ContextMenuProvider.prototype = {
1330     /**
1331      * @param {!Event} event
1332      * @param {!WebInspector.ContextMenu} contextMenu
1333      * @param {!Object} target
1334      */
1335     appendApplicableItems: function(event, contextMenu, target)
1336     {
1337         WebInspector.inspectorView.panel("sources").appendApplicableItems(event, contextMenu, target);
1338     }
1339 }
1340
1341 /**
1342  * @constructor
1343  * @implements {WebInspector.Revealer}
1344  */
1345 WebInspector.SourcesPanel.UILocationRevealer = function()
1346 {
1347 }
1348
1349 WebInspector.SourcesPanel.UILocationRevealer.prototype = {
1350     /**
1351      * @param {!Object} uiLocation
1352      */
1353     reveal: function(uiLocation)
1354     {
1355         if (uiLocation instanceof WebInspector.UILocation)
1356             /** @type {!WebInspector.SourcesPanel} */ (WebInspector.inspectorView.panel("sources")).showUILocation(uiLocation);
1357     }
1358 }
1359
1360 /**
1361  * @constructor
1362  * @implements {WebInspector.Revealer}
1363  */
1364 WebInspector.SourcesPanel.UISourceCodeRevealer = function()
1365 {
1366 }
1367
1368 WebInspector.SourcesPanel.UISourceCodeRevealer.prototype = {
1369     /**
1370      * @param {!Object} uiSourceCode
1371      */
1372     reveal: function(uiSourceCode)
1373     {
1374         if (uiSourceCode instanceof WebInspector.UISourceCode)
1375             /** @type {!WebInspector.SourcesPanel} */ (WebInspector.inspectorView.panel("sources")).showUISourceCode(uiSourceCode);
1376     }
1377 }
1378
1379 /**
1380  * @constructor
1381  * @implements {WebInspector.ActionDelegate}
1382  */
1383 WebInspector.SourcesPanel.ShowGoToSourceDialogActionDelegate = function() {}
1384
1385 WebInspector.SourcesPanel.ShowGoToSourceDialogActionDelegate.prototype = {
1386     /**
1387      * @return {boolean}
1388      */
1389     handleAction: function()
1390     {
1391         var panel = /** @type {?WebInspector.SourcesPanel} */ (WebInspector.inspectorView.showPanel("sources"));
1392         if (!panel)
1393             return false;
1394         panel.showGoToSourceDialog();
1395         return true;
1396     }
1397 }
1398
1399 /**
1400  * @constructor
1401  * @extends {WebInspector.UISettingDelegate}
1402  */
1403 WebInspector.SourcesPanel.DisableJavaScriptSettingDelegate = function()
1404 {
1405     WebInspector.UISettingDelegate.call(this);
1406 }
1407
1408 WebInspector.SourcesPanel.DisableJavaScriptSettingDelegate.prototype = {
1409     /**
1410      * @override
1411      * @return {!Element}
1412      */
1413     settingElement: function()
1414     {
1415         var disableJSElement = WebInspector.SettingsUI.createSettingCheckbox(WebInspector.UIString("Disable JavaScript"), WebInspector.settings.javaScriptDisabled);
1416         this._disableJSCheckbox = disableJSElement.getElementsByTagName("input")[0];
1417         WebInspector.settings.javaScriptDisabled.addChangeListener(this._settingChanged, this);
1418         var disableJSInfoParent = this._disableJSCheckbox.parentElement.createChild("span", "monospace");
1419         this._disableJSInfo = disableJSInfoParent.createChild("span", "object-info-state-note hidden");
1420         this._disableJSInfo.title = WebInspector.UIString("JavaScript is blocked on the inspected page (may be disabled in browser settings).");
1421
1422         WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.MainFrameNavigated, this._updateScriptDisabledCheckbox, this);
1423         this._updateScriptDisabledCheckbox();
1424         return disableJSElement;
1425     },
1426
1427     /**
1428      * @param {!WebInspector.Event} event
1429      */
1430     _settingChanged: function(event)
1431     {
1432         PageAgent.setScriptExecutionDisabled(event.data, this._updateScriptDisabledCheckbox.bind(this));
1433     },
1434
1435     _updateScriptDisabledCheckbox: function()
1436     {
1437         PageAgent.getScriptExecutionStatus(executionStatusCallback.bind(this));
1438
1439         /**
1440          * @param {?Protocol.Error} error
1441          * @param {string} status
1442          * @this {WebInspector.SourcesPanel.DisableJavaScriptSettingDelegate}
1443          */
1444         function executionStatusCallback(error, status)
1445         {
1446             if (error || !status)
1447                 return;
1448
1449             var forbidden = (status === "forbidden");
1450             var disabled = forbidden || (status === "disabled");
1451
1452             this._disableJSInfo.classList.toggle("hidden", !forbidden);
1453             this._disableJSCheckbox.checked = disabled;
1454             this._disableJSCheckbox.disabled = forbidden;
1455         }
1456     },
1457
1458     __proto__: WebInspector.UISettingDelegate.prototype
1459 }
1460
1461 /**
1462  * @constructor
1463  * @implements {WebInspector.ActionDelegate}
1464  */
1465 WebInspector.SourcesPanel.TogglePauseActionDelegate = function()
1466 {
1467 }
1468
1469 WebInspector.SourcesPanel.TogglePauseActionDelegate.prototype = {
1470     /**
1471      * @return {boolean}
1472      */
1473     handleAction: function()
1474     {
1475         var panel = /** @type {?WebInspector.SourcesPanel} */ (WebInspector.inspectorView.showPanel("sources"));
1476         if (!panel)
1477             return false;
1478         panel.togglePause();
1479         return true;
1480     }
1481 }