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
27importScript("BreakpointsSidebarPane.js");
28importScript("CallStackSidebarPane.js");
29importScript("FilePathScoreFunction.js");
30importScript("FilteredItemSelectionDialog.js");
31importScript("UISourceCodeFrame.js");
32importScript("JavaScriptSourceFrame.js");
33importScript("CSSSourceFrame.js");
34importScript("NavigatorOverlayController.js");
35importScript("NavigatorView.js");
36importScript("RevisionHistoryView.js");
37importScript("ScopeChainSidebarPane.js");
38importScript("SourcesNavigator.js");
39importScript("SourcesSearchScope.js");
40importScript("StyleSheetOutlineDialog.js");
41importScript("TabbedEditorContainer.js");
42importScript("WatchExpressionsSidebarPane.js");
43importScript("WorkersSidebarPane.js");
44
45/**
46 * @constructor
47 * @implements {WebInspector.TabbedEditorContainerDelegate}
48 * @implements {WebInspector.ContextMenu.Provider}
49 * @implements {WebInspector.Searchable}
50 * @extends {WebInspector.Panel}
51 * @param {!WebInspector.Workspace=} workspaceForTest
52 */
53WebInspector.SourcesPanel = function(workspaceForTest)
54{
55    WebInspector.Panel.call(this, "sources");
56    this.registerRequiredCSS("sourcesPanel.css");
57    this.registerRequiredCSS("textPrompt.css"); // Watch Expressions autocomplete.
58
59    WebInspector.settings.navigatorWasOnceHidden = WebInspector.settings.createSetting("navigatorWasOnceHidden", false);
60    WebInspector.settings.debuggerSidebarHidden = WebInspector.settings.createSetting("debuggerSidebarHidden", false);
61    WebInspector.settings.showEditorInDrawer = WebInspector.settings.createSetting("showEditorInDrawer", true);
62
63    this._workspace = workspaceForTest || WebInspector.workspace;
64
65    /**
66     * @return {!WebInspector.View}
67     * @this {WebInspector.SourcesPanel}
68     */
69    function viewGetter()
70    {
71        return this.visibleView;
72    }
73    WebInspector.GoToLineDialog.install(this, viewGetter.bind(this));
74
75    var helpSection = WebInspector.shortcutsScreen.section(WebInspector.UIString("Sources Panel"));
76    this.debugToolbar = this._createDebugToolbar();
77
78    const initialDebugSidebarWidth = 225;
79    const minimumDebugSidebarWidthPercent = 0.5;
80    this.createSidebarView(this.element, WebInspector.SidebarView.SidebarPosition.End, initialDebugSidebarWidth);
81    this.splitView.element.id = "scripts-split-view";
82    this.splitView.setSidebarElementConstraints(Preferences.minScriptsSidebarWidth);
83    this.splitView.setMainElementConstraints(minimumDebugSidebarWidthPercent);
84
85    // Create scripts navigator
86    const initialNavigatorWidth = 225;
87    const minimumViewsContainerWidthPercent = 0.5;
88    this.editorView = new WebInspector.SidebarView(WebInspector.SidebarView.SidebarPosition.Start, "scriptsPanelNavigatorSidebarWidth", initialNavigatorWidth);
89    this.editorView.element.id = "scripts-editor-split-view";
90    this.editorView.element.tabIndex = 0;
91
92    this.editorView.setSidebarElementConstraints(Preferences.minScriptsSidebarWidth);
93    this.editorView.setMainElementConstraints(minimumViewsContainerWidthPercent);
94    this.editorView.show(this.splitView.mainElement);
95
96    this._navigator = new WebInspector.SourcesNavigator();
97    this._navigator.view.show(this.editorView.sidebarElement);
98
99    var tabbedEditorPlaceholderText = WebInspector.isMac() ? WebInspector.UIString("Hit Cmd+O to open a file") : WebInspector.UIString("Hit Ctrl+O to open a file");
100
101    this.editorView.mainElement.classList.add("vbox");
102    this.editorView.sidebarElement.classList.add("vbox");
103
104    this.sourcesView = new WebInspector.SourcesView();
105
106    this._searchableView = new WebInspector.SearchableView(this);
107    this._searchableView.setMinimalSearchQuerySize(0);
108    this._searchableView.show(this.sourcesView.element);
109
110    this._editorContainer = new WebInspector.TabbedEditorContainer(this, "previouslyViewedFiles", tabbedEditorPlaceholderText);
111    this._editorContainer.show(this._searchableView.element);
112
113    this._navigatorController = new WebInspector.NavigatorOverlayController(this.editorView, this._navigator.view, this._editorContainer.view);
114
115    this._navigator.addEventListener(WebInspector.SourcesNavigator.Events.SourceSelected, this._sourceSelected, this);
116    this._navigator.addEventListener(WebInspector.SourcesNavigator.Events.ItemSearchStarted, this._itemSearchStarted, this);
117    this._navigator.addEventListener(WebInspector.SourcesNavigator.Events.ItemCreationRequested, this._itemCreationRequested, this);
118    this._navigator.addEventListener(WebInspector.SourcesNavigator.Events.ItemRenamingRequested, this._itemRenamingRequested, this);
119
120    this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorSelected, this._editorSelected, this);
121    this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorClosed, this._editorClosed, this);
122
123    this._debugSidebarResizeWidgetElement = document.createElementWithClass("div", "resizer-widget");
124    this._debugSidebarResizeWidgetElement.id = "scripts-debug-sidebar-resizer-widget";
125    this.splitView.installResizer(this._debugSidebarResizeWidgetElement);
126
127    this.sidebarPanes = {};
128    this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane();
129    this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
130    this.sidebarPanes.callstack.addEventListener(WebInspector.CallStackSidebarPane.Events.CallFrameSelected, this._callFrameSelectedInSidebar.bind(this));
131    this.sidebarPanes.callstack.addEventListener(WebInspector.CallStackSidebarPane.Events.CallFrameRestarted, this._callFrameRestartedInSidebar.bind(this));
132
133    this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
134    this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane(WebInspector.breakpointManager, this._showSourceLocation.bind(this));
135    this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane.createProxy(this);
136    this.sidebarPanes.xhrBreakpoints = new WebInspector.XHRBreakpointsSidebarPane();
137    this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane();
138
139    if (Capabilities.canInspectWorkers && !WebInspector.WorkerManager.isWorkerFrontend()) {
140        WorkerAgent.enable();
141        this.sidebarPanes.workerList = new WebInspector.WorkersSidebarPane(WebInspector.workerManager);
142    }
143
144    this.sidebarPanes.callstack.registerShortcuts(this.registerShortcuts.bind(this));
145    this.registerShortcuts(WebInspector.SourcesPanelDescriptor.ShortcutKeys.GoToMember, this._showOutlineDialog.bind(this));
146    this.registerShortcuts(WebInspector.SourcesPanelDescriptor.ShortcutKeys.ToggleBreakpoint, this._toggleBreakpoint.bind(this));
147
148    this._extensionSidebarPanes = [];
149
150    this._toggleFormatSourceButton = new WebInspector.StatusBarButton(WebInspector.UIString("Pretty print"), "sources-toggle-pretty-print-status-bar-item");
151    this._toggleFormatSourceButton.toggled = false;
152    this._toggleFormatSourceButton.addEventListener("click", this._toggleFormatSource, this);
153
154    this._scriptViewStatusBarItemsContainer = document.createElement("div");
155    this._scriptViewStatusBarItemsContainer.className = "inline-block";
156
157    this._scriptViewStatusBarTextContainer = document.createElement("div");
158    this._scriptViewStatusBarTextContainer.className = "inline-block";
159
160    this._statusBarContainerElement = this.sourcesView.element.createChild("div", "sources-status-bar");
161    this._statusBarContainerElement.appendChild(this._toggleFormatSourceButton.element);
162    this._statusBarContainerElement.appendChild(this._scriptViewStatusBarItemsContainer);
163    this._statusBarContainerElement.appendChild(this._scriptViewStatusBarTextContainer);
164
165    this._installDebuggerSidebarController();
166
167    WebInspector.dockController.addEventListener(WebInspector.DockController.Events.DockSideChanged, this._dockSideChanged.bind(this));
168    WebInspector.settings.splitVerticallyWhenDockedToRight.addChangeListener(this._dockSideChanged.bind(this));
169    this._dockSideChanged();
170
171    /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.SourceFrame>} */
172    this._sourceFramesByUISourceCode = new Map();
173    this._updateDebuggerButtons();
174    this._pauseOnExceptionStateChanged();
175    if (WebInspector.debuggerModel.isPaused())
176        this._showDebuggerPausedDetails();
177
178    WebInspector.settings.pauseOnExceptionStateString.addChangeListener(this._pauseOnExceptionStateChanged, this);
179    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this);
180    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._debuggerWasDisabled, this);
181    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
182    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
183    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.CallFrameSelected, this._callFrameSelected, this);
184    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame, this._consoleCommandEvaluatedInSelectedCallFrame, this);
185    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointsActiveStateChanged, this._breakpointsActiveStateChanged, this);
186
187    WebInspector.startBatchUpdate();
188    this._workspace.uiSourceCodes().forEach(this._addUISourceCode.bind(this));
189    WebInspector.endBatchUpdate();
190
191    this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
192    this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
193    this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._projectWillReset.bind(this), this);
194    WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this);
195
196    WebInspector.advancedSearchController.registerSearchScope(new WebInspector.SourcesSearchScope(this._workspace));
197
198    this._boundOnKeyUp = this._onKeyUp.bind(this);
199    this._boundOnKeyDown = this._onKeyDown.bind(this);
200
201    function handleBeforeUnload(event)
202    {
203        if (event.returnValue)
204            return;
205        var unsavedSourceCodes = WebInspector.workspace.unsavedSourceCodes();
206        if (!unsavedSourceCodes.length)
207            return;
208
209        event.returnValue = WebInspector.UIString("DevTools have unsaved changes that will be permanently lost.");
210        WebInspector.showPanel("sources");
211        for (var i = 0; i < unsavedSourceCodes.length; ++i)
212            WebInspector.panels.sources.showUISourceCode(unsavedSourceCodes[i]);
213    }
214    window.addEventListener("beforeunload", handleBeforeUnload.bind(this), true);
215}
216
217WebInspector.SourcesPanel.prototype = {
218    defaultFocusedElement: function()
219    {
220        return this._editorContainer.view.defaultFocusedElement() || this._navigator.view.defaultFocusedElement();
221    },
222
223    get paused()
224    {
225        return this._paused;
226    },
227
228    wasShown: function()
229    {
230        WebInspector.inspectorView.closeViewInDrawer("editor");
231        this.sourcesView.show(this.editorView.mainElement);
232        WebInspector.Panel.prototype.wasShown.call(this);
233        this._navigatorController.wasShown();
234
235        this.element.addEventListener("keydown", this._boundOnKeyDown, false);
236        this.element.addEventListener("keyup", this._boundOnKeyUp, false);
237    },
238
239    willHide: function()
240    {
241        this.element.removeEventListener("keydown", this._boundOnKeyDown, false);
242        this.element.removeEventListener("keyup", this._boundOnKeyUp, false);
243
244        WebInspector.Panel.prototype.willHide.call(this);
245    },
246
247    /**
248     * @return {!WebInspector.SearchableView}
249     */
250    searchableView: function()
251    {
252        return this._searchableView;
253    },
254
255    /**
256     * @param {!WebInspector.Event} event
257     */
258    _uiSourceCodeAdded: function(event)
259    {
260        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
261        this._addUISourceCode(uiSourceCode);
262    },
263
264    /**
265     * @param {!WebInspector.UISourceCode} uiSourceCode
266     */
267    _addUISourceCode: function(uiSourceCode)
268    {
269        if (this._toggleFormatSourceButton.toggled)
270            uiSourceCode.setFormatted(true);
271        if (uiSourceCode.project().isServiceProject())
272            return;
273        this._navigator.addUISourceCode(uiSourceCode);
274        this._editorContainer.addUISourceCode(uiSourceCode);
275        // Replace debugger script-based uiSourceCode with a network-based one.
276        var currentUISourceCode = this._currentUISourceCode;
277        if (currentUISourceCode && currentUISourceCode.project().isServiceProject() && currentUISourceCode !== uiSourceCode && currentUISourceCode.url === uiSourceCode.url) {
278            this._showFile(uiSourceCode);
279            this._editorContainer.removeUISourceCode(currentUISourceCode);
280        }
281    },
282
283    _uiSourceCodeRemoved: function(event)
284    {
285        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
286        this._removeUISourceCodes([uiSourceCode]);
287    },
288
289    /**
290     * @param {!Array.<!WebInspector.UISourceCode>} uiSourceCodes
291     */
292    _removeUISourceCodes: function(uiSourceCodes)
293    {
294        for (var i = 0; i < uiSourceCodes.length; ++i) {
295            this._navigator.removeUISourceCode(uiSourceCodes[i]);
296            this._removeSourceFrame(uiSourceCodes[i]);
297        }
298        this._editorContainer.removeUISourceCodes(uiSourceCodes);
299    },
300
301    _consoleCommandEvaluatedInSelectedCallFrame: function(event)
302    {
303        this.sidebarPanes.scopechain.update(WebInspector.debuggerModel.selectedCallFrame());
304    },
305
306    _debuggerPaused: function()
307    {
308        WebInspector.inspectorView.setCurrentPanel(this);
309        this._showDebuggerPausedDetails();
310    },
311
312    _showDebuggerPausedDetails: function()
313    {
314        var details = WebInspector.debuggerModel.debuggerPausedDetails();
315
316        this._paused = true;
317        this._waitingToPause = false;
318        this._stepping = false;
319
320        this._updateDebuggerButtons();
321
322        this.sidebarPanes.callstack.update(details.callFrames, details.asyncStackTrace);
323
324        /**
325         * @param {!Element} element
326         * @this {WebInspector.SourcesPanel}
327         */
328        function didCreateBreakpointHitStatusMessage(element)
329        {
330            this.sidebarPanes.callstack.setStatus(element);
331        }
332
333        /**
334         * @param {!WebInspector.UILocation} uiLocation
335         * @this {WebInspector.SourcesPanel}
336         */
337        function didGetUILocation(uiLocation)
338        {
339            var breakpoint = WebInspector.breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber);
340            if (!breakpoint)
341                return;
342            this.sidebarPanes.jsBreakpoints.highlightBreakpoint(breakpoint);
343            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a JavaScript breakpoint."));
344        }
345
346        if (details.reason === WebInspector.DebuggerModel.BreakReason.DOM) {
347            WebInspector.domBreakpointsSidebarPane.highlightBreakpoint(details.auxData);
348            WebInspector.domBreakpointsSidebarPane.createBreakpointHitStatusMessage(details.auxData, didCreateBreakpointHitStatusMessage.bind(this));
349        } else if (details.reason === WebInspector.DebuggerModel.BreakReason.EventListener) {
350            var eventName = details.auxData.eventName;
351            this.sidebarPanes.eventListenerBreakpoints.highlightBreakpoint(details.auxData.eventName);
352            var eventNameForUI = WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI(eventName, details.auxData);
353            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a \"%s\" Event Listener.", eventNameForUI));
354        } else if (details.reason === WebInspector.DebuggerModel.BreakReason.XHR) {
355            this.sidebarPanes.xhrBreakpoints.highlightBreakpoint(details.auxData["breakpointURL"]);
356            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a XMLHttpRequest."));
357        } else if (details.reason === WebInspector.DebuggerModel.BreakReason.Exception)
358            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on exception: '%s'.", details.auxData.description));
359        else if (details.reason === WebInspector.DebuggerModel.BreakReason.Assert)
360            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on assertion."));
361        else if (details.reason === WebInspector.DebuggerModel.BreakReason.CSPViolation)
362            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a script blocked due to Content Security Policy directive: \"%s\".", details.auxData["directiveText"]));
363        else if (details.reason === WebInspector.DebuggerModel.BreakReason.DebugCommand)
364            this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a debugged function"));
365        else {
366            if (details.callFrames.length)
367                details.callFrames[0].createLiveLocation(didGetUILocation.bind(this));
368            else
369                console.warn("ScriptsPanel paused, but callFrames.length is zero."); // TODO remove this once we understand this case better
370        }
371
372        this._enableDebuggerSidebar(true);
373        this._toggleDebuggerSidebarButton.setEnabled(false);
374        window.focus();
375        InspectorFrontendHost.bringToFront();
376    },
377
378    _debuggerResumed: function()
379    {
380        this._paused = false;
381        this._waitingToPause = false;
382        this._stepping = false;
383
384        this._clearInterface();
385        this._toggleDebuggerSidebarButton.setEnabled(true);
386    },
387
388    _debuggerWasEnabled: function()
389    {
390        this._updateDebuggerButtons();
391    },
392
393    _debuggerWasDisabled: function()
394    {
395        this._debuggerReset();
396    },
397
398    _debuggerReset: function()
399    {
400        this._debuggerResumed();
401        this.sidebarPanes.watchExpressions.reset();
402        delete this._skipExecutionLineRevealing;
403    },
404
405    _projectWillReset: function(event)
406    {
407        var project = event.data;
408        var uiSourceCodes = project.uiSourceCodes();
409        this._removeUISourceCodes(uiSourceCodes);
410        if (project.type() === WebInspector.projectTypes.Network)
411            this._editorContainer.reset();
412    },
413
414    get visibleView()
415    {
416        return this._editorContainer.visibleView;
417    },
418
419    _updateScriptViewStatusBarItems: function()
420    {
421        this._scriptViewStatusBarItemsContainer.removeChildren();
422        this._scriptViewStatusBarTextContainer.removeChildren();
423
424        var sourceFrame = this.visibleView;
425        if (sourceFrame) {
426            var statusBarItems = sourceFrame.statusBarItems() || [];
427            for (var i = 0; i < statusBarItems.length; ++i)
428                this._scriptViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
429            var statusBarText = sourceFrame.statusBarText();
430            if (statusBarText)
431                this._scriptViewStatusBarTextContainer.appendChild(statusBarText);
432        }
433    },
434
435    /**
436     * @param {!Element} anchor
437     * @return {boolean}
438     */
439    showAnchorLocation: function(anchor)
440    {
441        if (!anchor.uiSourceCode) {
442            var uiSourceCode = WebInspector.workspace.uiSourceCodeForURL(anchor.href);
443            if (uiSourceCode)
444                anchor.uiSourceCode = uiSourceCode;
445        }
446        if (!anchor.uiSourceCode)
447            return false;
448
449        this._showSourceLocation(anchor.uiSourceCode, anchor.lineNumber, anchor.columnNumber);
450        return true;
451    },
452
453    /**
454     * @param {!WebInspector.UISourceCode} uiSourceCode
455     * @param {number=} lineNumber
456     * @param {number=} columnNumber
457     * @param {boolean=} forceShowInPanel
458     */
459    showUISourceCode: function(uiSourceCode, lineNumber, columnNumber, forceShowInPanel)
460    {
461        this._showSourceLocation(uiSourceCode, lineNumber, columnNumber, forceShowInPanel);
462    },
463
464    /**
465     * @param {boolean=} forceShowInPanel
466     */
467    _showEditor: function(forceShowInPanel)
468    {
469        if (this.sourcesView.isShowing())
470            return;
471        if (this._canShowEditorInDrawer() && !forceShowInPanel) {
472            var drawerEditorView = new WebInspector.DrawerEditorView();
473            this.sourcesView.show(drawerEditorView.element);
474            WebInspector.inspectorView.showCloseableViewInDrawer("editor", WebInspector.UIString("Editor"), drawerEditorView);
475        } else {
476            WebInspector.showPanel("sources");
477        }
478    },
479
480    /**
481     * @return {?WebInspector.UISourceCode}
482     */
483    currentUISourceCode: function()
484    {
485        return this._currentUISourceCode;
486    },
487
488    /**
489     * @param {!WebInspector.UILocation} uiLocation
490     * @param {boolean=} forceShowInPanel
491     */
492    showUILocation: function(uiLocation, forceShowInPanel)
493    {
494        this._showSourceLocation(uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber, forceShowInPanel);
495    },
496
497    /**
498     * @return {boolean}
499     */
500    _canShowEditorInDrawer: function()
501    {
502        return WebInspector.experimentsSettings.showEditorInDrawer.isEnabled() && WebInspector.settings.showEditorInDrawer.get();
503    },
504
505    /**
506     * @param {!WebInspector.UISourceCode} uiSourceCode
507     * @param {number=} lineNumber
508     * @param {number=} columnNumber
509     * @param {boolean=} forceShowInPanel
510     */
511    _showSourceLocation: function(uiSourceCode, lineNumber, columnNumber, forceShowInPanel)
512    {
513        this._showEditor(forceShowInPanel);
514        var sourceFrame = this._showFile(uiSourceCode);
515        if (typeof lineNumber === "number")
516            sourceFrame.highlightPosition(lineNumber, columnNumber);
517        sourceFrame.focus();
518
519        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
520            action: WebInspector.UserMetrics.UserActionNames.OpenSourceLink,
521            url: uiSourceCode.originURL(),
522            lineNumber: lineNumber
523        });
524    },
525
526    /**
527     * @param {!WebInspector.UISourceCode} uiSourceCode
528     * @return {!WebInspector.SourceFrame}
529     */
530    _showFile: function(uiSourceCode)
531    {
532        var sourceFrame = this._getOrCreateSourceFrame(uiSourceCode);
533        if (this._currentUISourceCode === uiSourceCode)
534            return sourceFrame;
535        this._currentUISourceCode = uiSourceCode;
536        if (!uiSourceCode.project().isServiceProject())
537            this._navigator.revealUISourceCode(uiSourceCode, true);
538        this._editorContainer.showFile(uiSourceCode);
539        this._updateScriptViewStatusBarItems();
540
541        if (this._currentUISourceCode.project().type() === WebInspector.projectTypes.Snippets)
542            this._runSnippetButton.element.classList.remove("hidden");
543        else
544            this._runSnippetButton.element.classList.add("hidden");
545
546        return sourceFrame;
547    },
548
549    /**
550     * @param {!WebInspector.UISourceCode} uiSourceCode
551     * @return {!WebInspector.SourceFrame}
552     */
553    _createSourceFrame: function(uiSourceCode)
554    {
555        var sourceFrame;
556        switch (uiSourceCode.contentType()) {
557        case WebInspector.resourceTypes.Script:
558            sourceFrame = new WebInspector.JavaScriptSourceFrame(this, uiSourceCode);
559            break;
560        case WebInspector.resourceTypes.Document:
561            sourceFrame = new WebInspector.JavaScriptSourceFrame(this, uiSourceCode);
562            break;
563        case WebInspector.resourceTypes.Stylesheet:
564            sourceFrame = new WebInspector.CSSSourceFrame(uiSourceCode);
565            break;
566        default:
567            sourceFrame = new WebInspector.UISourceCodeFrame(uiSourceCode);
568        break;
569        }
570        sourceFrame.setHighlighterType(uiSourceCode.highlighterType());
571        this._sourceFramesByUISourceCode.put(uiSourceCode, sourceFrame);
572        return sourceFrame;
573    },
574
575    /**
576     * @param {!WebInspector.UISourceCode} uiSourceCode
577     * @return {!WebInspector.SourceFrame}
578     */
579    _getOrCreateSourceFrame: function(uiSourceCode)
580    {
581        return this._sourceFramesByUISourceCode.get(uiSourceCode) || this._createSourceFrame(uiSourceCode);
582    },
583
584    /**
585     * @param {!WebInspector.SourceFrame} sourceFrame
586     * @param {!WebInspector.UISourceCode} uiSourceCode
587     * @return {boolean}
588     */
589    _sourceFrameMatchesUISourceCode: function(sourceFrame, uiSourceCode)
590    {
591        switch (uiSourceCode.contentType()) {
592        case WebInspector.resourceTypes.Script:
593        case WebInspector.resourceTypes.Document:
594            return sourceFrame instanceof WebInspector.JavaScriptSourceFrame;
595        case WebInspector.resourceTypes.Stylesheet:
596            return sourceFrame instanceof WebInspector.CSSSourceFrame;
597        default:
598            return !(sourceFrame instanceof WebInspector.JavaScriptSourceFrame);
599        }
600    },
601
602    /**
603     * @param {!WebInspector.UISourceCode} uiSourceCode
604     */
605    _recreateSourceFrameIfNeeded: function(uiSourceCode)
606    {
607        var oldSourceFrame = this._sourceFramesByUISourceCode.get(uiSourceCode);
608        if (!oldSourceFrame)
609            return;
610        if (this._sourceFrameMatchesUISourceCode(oldSourceFrame, uiSourceCode)) {
611            oldSourceFrame.setHighlighterType(uiSourceCode.highlighterType());
612        } else {
613            this._editorContainer.removeUISourceCode(uiSourceCode);
614            this._removeSourceFrame(uiSourceCode);
615        }
616    },
617
618    /**
619     * @param {!WebInspector.UISourceCode} uiSourceCode
620     * @return {!WebInspector.SourceFrame}
621     */
622    viewForFile: function(uiSourceCode)
623    {
624        return this._getOrCreateSourceFrame(uiSourceCode);
625    },
626
627    /**
628     * @param {!WebInspector.UISourceCode} uiSourceCode
629     */
630    _removeSourceFrame: function(uiSourceCode)
631    {
632        var sourceFrame = this._sourceFramesByUISourceCode.get(uiSourceCode);
633        if (!sourceFrame)
634            return;
635        this._sourceFramesByUISourceCode.remove(uiSourceCode);
636        sourceFrame.dispose();
637    },
638
639    _clearCurrentExecutionLine: function()
640    {
641        if (this._executionSourceFrame)
642            this._executionSourceFrame.clearExecutionLine();
643        delete this._executionSourceFrame;
644    },
645
646    _setExecutionLine: function(uiLocation)
647    {
648        var callFrame = WebInspector.debuggerModel.selectedCallFrame()
649        var sourceFrame = this._getOrCreateSourceFrame(uiLocation.uiSourceCode);
650        sourceFrame.setExecutionLine(uiLocation.lineNumber, callFrame);
651        this._executionSourceFrame = sourceFrame;
652    },
653
654    _executionLineChanged: function(uiLocation)
655    {
656        this._clearCurrentExecutionLine();
657        this._setExecutionLine(uiLocation);
658
659        var uiSourceCode = uiLocation.uiSourceCode;
660        var scriptFile = this._currentUISourceCode ? this._currentUISourceCode.scriptFile() : null;
661        if (this._skipExecutionLineRevealing)
662            return;
663        this._skipExecutionLineRevealing = true;
664        var sourceFrame = this._showFile(uiSourceCode);
665        sourceFrame.revealLine(uiLocation.lineNumber);
666        if (sourceFrame.canEditSource())
667            sourceFrame.setSelection(WebInspector.TextRange.createFromLocation(uiLocation.lineNumber, 0));
668        sourceFrame.focus();
669    },
670
671    _callFrameSelected: function(event)
672    {
673        var callFrame = event.data;
674
675        if (!callFrame)
676            return;
677
678        this.sidebarPanes.scopechain.update(callFrame);
679        this.sidebarPanes.watchExpressions.refreshExpressions();
680        this.sidebarPanes.callstack.setSelectedCallFrame(callFrame);
681        callFrame.createLiveLocation(this._executionLineChanged.bind(this));
682    },
683
684    _editorClosed: function(event)
685    {
686        this._navigatorController.hideNavigatorOverlay();
687        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
688
689        if (this._currentUISourceCode === uiSourceCode)
690            delete this._currentUISourceCode;
691
692        // SourcesNavigator does not need to update on EditorClosed.
693        this._updateScriptViewStatusBarItems();
694        this._searchableView.resetSearch();
695    },
696
697    _editorSelected: function(event)
698    {
699        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
700        var sourceFrame = this._showFile(uiSourceCode);
701        this._navigatorController.hideNavigatorOverlay();
702        if (!this._navigatorController.isNavigatorPinned())
703            sourceFrame.focus();
704        this._searchableView.setCanReplace(!!sourceFrame && sourceFrame.canEditSource());
705        this._searchableView.resetSearch();
706    },
707
708    _sourceSelected: function(event)
709    {
710        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.uiSourceCode);
711        var sourceFrame = this._showFile(uiSourceCode);
712        this._navigatorController.hideNavigatorOverlay();
713        if (sourceFrame && (!this._navigatorController.isNavigatorPinned() || event.data.focusSource))
714            sourceFrame.focus();
715    },
716
717    _itemSearchStarted: function(event)
718    {
719        var searchText = /** @type {string} */ (event.data);
720        WebInspector.OpenResourceDialog.show(this, this.editorView.mainElement, searchText);
721    },
722
723    _pauseOnExceptionStateChanged: function()
724    {
725        var pauseOnExceptionsState = WebInspector.settings.pauseOnExceptionStateString.get();
726        switch (pauseOnExceptionsState) {
727        case WebInspector.DebuggerModel.PauseOnExceptionsState.DontPauseOnExceptions:
728            this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions.");
729            break;
730        case WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnAllExceptions:
731            this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions.");
732            break;
733        case WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnUncaughtExceptions:
734            this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions.");
735            break;
736        }
737        this._pauseOnExceptionButton.state = pauseOnExceptionsState;
738    },
739
740    _updateDebuggerButtons: function()
741    {
742        if (this._paused) {
743            this._updateButtonTitle(this._pauseButton, WebInspector.UIString("Resume script execution (%s)."))
744            this._pauseButton.state = true;
745            this._pauseButton.setLongClickOptionsEnabled((function() { return [ this._longResumeButton ] }).bind(this));
746
747            this._pauseButton.setEnabled(true);
748            this._stepOverButton.setEnabled(true);
749            this._stepIntoButton.setEnabled(true);
750            this._stepOutButton.setEnabled(true);
751        } else {
752            this._updateButtonTitle(this._pauseButton, WebInspector.UIString("Pause script execution (%s)."))
753            this._pauseButton.state = false;
754            this._pauseButton.setLongClickOptionsEnabled(null);
755
756            this._pauseButton.setEnabled(!this._waitingToPause);
757            this._stepOverButton.setEnabled(false);
758            this._stepIntoButton.setEnabled(false);
759            this._stepOutButton.setEnabled(false);
760        }
761    },
762
763    _clearInterface: function()
764    {
765        this.sidebarPanes.callstack.update(null, null);
766        this.sidebarPanes.scopechain.update(null);
767        this.sidebarPanes.jsBreakpoints.clearBreakpointHighlight();
768        WebInspector.domBreakpointsSidebarPane.clearBreakpointHighlight();
769        this.sidebarPanes.eventListenerBreakpoints.clearBreakpointHighlight();
770        this.sidebarPanes.xhrBreakpoints.clearBreakpointHighlight();
771
772        this._clearCurrentExecutionLine();
773        this._updateDebuggerButtons();
774    },
775
776    _togglePauseOnExceptions: function()
777    {
778        var nextStateMap = {};
779        var stateEnum = WebInspector.DebuggerModel.PauseOnExceptionsState;
780        nextStateMap[stateEnum.DontPauseOnExceptions] = stateEnum.PauseOnAllExceptions;
781        nextStateMap[stateEnum.PauseOnAllExceptions] = stateEnum.PauseOnUncaughtExceptions;
782        nextStateMap[stateEnum.PauseOnUncaughtExceptions] = stateEnum.DontPauseOnExceptions;
783        WebInspector.settings.pauseOnExceptionStateString.set(nextStateMap[this._pauseOnExceptionButton.state]);
784    },
785
786    /**
787     * @return {boolean}
788     */
789    _runSnippet: function()
790    {
791        if (this._currentUISourceCode.project().type() !== WebInspector.projectTypes.Snippets)
792            return false;
793        WebInspector.scriptSnippetModel.evaluateScriptSnippet(this._currentUISourceCode);
794        return true;
795    },
796
797    /**
798     * @return {boolean}
799     */
800    _togglePause: function()
801    {
802        if (this._paused) {
803            delete this._skipExecutionLineRevealing;
804            this._paused = false;
805            this._waitingToPause = false;
806            WebInspector.debuggerModel.resume();
807        } else {
808            this._stepping = false;
809            this._waitingToPause = true;
810            // Make sure pauses didn't stick skipped.
811            WebInspector.debuggerModel.skipAllPauses(false);
812            DebuggerAgent.pause();
813        }
814
815        this._clearInterface();
816        return true;
817    },
818
819    /**
820     * @return {boolean}
821     */
822    _longResume: function()
823    {
824        if (!this._paused)
825            return true;
826
827        this._paused = false;
828        this._waitingToPause = false;
829        WebInspector.debuggerModel.skipAllPausesUntilReloadOrTimeout(500);
830        WebInspector.debuggerModel.resume();
831
832        this._clearInterface();
833        return true;
834    },
835
836    /**
837     * @return {boolean}
838     */
839    _stepOverClicked: function()
840    {
841        if (!this._paused)
842            return true;
843
844        delete this._skipExecutionLineRevealing;
845        this._paused = false;
846        this._stepping = true;
847
848        this._clearInterface();
849
850        WebInspector.debuggerModel.stepOver();
851        return true;
852    },
853
854    /**
855     * @return {boolean}
856     */
857    _stepIntoClicked: function()
858    {
859        if (!this._paused)
860            return true;
861
862        delete this._skipExecutionLineRevealing;
863        this._paused = false;
864        this._stepping = true;
865
866        this._clearInterface();
867
868        WebInspector.debuggerModel.stepInto();
869        return true;
870    },
871
872    /**
873     * @param {?Event=} event
874     * @return {boolean}
875     */
876    _stepIntoSelectionClicked: function(event)
877    {
878        if (!this._paused)
879            return true;
880
881        if (this._executionSourceFrame) {
882            var stepIntoMarkup = this._executionSourceFrame.stepIntoMarkup();
883            if (stepIntoMarkup)
884                stepIntoMarkup.iterateSelection(event.shiftKey);
885        }
886        return true;
887    },
888
889    doStepIntoSelection: function(rawLocation)
890    {
891        if (!this._paused)
892            return;
893
894        delete this._skipExecutionLineRevealing;
895        this._paused = false;
896        this._stepping = true;
897        this._clearInterface();
898        WebInspector.debuggerModel.stepIntoSelection(rawLocation);
899    },
900
901    /**
902     * @return {boolean}
903     */
904    _stepOutClicked: function()
905    {
906        if (!this._paused)
907            return true;
908
909        delete this._skipExecutionLineRevealing;
910        this._paused = false;
911        this._stepping = true;
912
913        this._clearInterface();
914
915        WebInspector.debuggerModel.stepOut();
916        return true;
917    },
918
919    /**
920     * @param {!WebInspector.Event} event
921     */
922    _callFrameSelectedInSidebar: function(event)
923    {
924        var callFrame = /** @type {!WebInspector.DebuggerModel.CallFrame} */ (event.data);
925        delete this._skipExecutionLineRevealing;
926        WebInspector.debuggerModel.setSelectedCallFrame(callFrame);
927    },
928
929    _callFrameRestartedInSidebar: function()
930    {
931        delete this._skipExecutionLineRevealing;
932    },
933
934    continueToLocation: function(rawLocation)
935    {
936        if (!this._paused)
937            return;
938
939        delete this._skipExecutionLineRevealing;
940        this._paused = false;
941        this._stepping = true;
942        this._clearInterface();
943        WebInspector.debuggerModel.continueToLocation(rawLocation);
944    },
945
946    _toggleBreakpointsClicked: function(event)
947    {
948        WebInspector.debuggerModel.setBreakpointsActive(!WebInspector.debuggerModel.breakpointsActive());
949    },
950
951    _breakpointsActiveStateChanged: function(event)
952    {
953        var active = event.data;
954        this._toggleBreakpointsButton.toggled = !active;
955        if (active) {
956            this._toggleBreakpointsButton.title = WebInspector.UIString("Deactivate breakpoints.");
957            this._editorContainer.view.element.classList.remove("breakpoints-deactivated");
958            this.sidebarPanes.jsBreakpoints.listElement.classList.remove("breakpoints-list-deactivated");
959        } else {
960            this._toggleBreakpointsButton.title = WebInspector.UIString("Activate breakpoints.");
961            this._editorContainer.view.element.classList.add("breakpoints-deactivated");
962            this.sidebarPanes.jsBreakpoints.listElement.classList.add("breakpoints-list-deactivated");
963        }
964    },
965
966    _createDebugToolbar: function()
967    {
968        var debugToolbar = document.createElement("div");
969        debugToolbar.className = "status-bar";
970        debugToolbar.id = "scripts-debug-toolbar";
971
972        var title, handler;
973        var platformSpecificModifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta;
974
975        // Run snippet.
976        title = WebInspector.UIString("Run snippet (%s).");
977        handler = this._runSnippet.bind(this);
978        this._runSnippetButton = this._createButtonAndRegisterShortcuts("scripts-run-snippet", title, handler, WebInspector.SourcesPanelDescriptor.ShortcutKeys.RunSnippet);
979        debugToolbar.appendChild(this._runSnippetButton.element);
980        this._runSnippetButton.element.classList.add("hidden");
981
982        // Continue.
983        handler = this._togglePause.bind(this);
984        this._pauseButton = this._createButtonAndRegisterShortcuts("scripts-pause", "", handler, WebInspector.SourcesPanelDescriptor.ShortcutKeys.PauseContinue);
985        debugToolbar.appendChild(this._pauseButton.element);
986
987        // Long resume.
988        title = WebInspector.UIString("Resume with all pauses blocked for 500 ms");
989        this._longResumeButton = new WebInspector.StatusBarButton(title, "scripts-long-resume");
990        this._longResumeButton.addEventListener("click", this._longResume.bind(this), this);
991
992        // Step over.
993        title = WebInspector.UIString("Step over next function call (%s).");
994        handler = this._stepOverClicked.bind(this);
995        this._stepOverButton = this._createButtonAndRegisterShortcuts("scripts-step-over", title, handler, WebInspector.SourcesPanelDescriptor.ShortcutKeys.StepOver);
996        debugToolbar.appendChild(this._stepOverButton.element);
997
998        // Step into.
999        title = WebInspector.UIString("Step into next function call (%s).");
1000        handler = this._stepIntoClicked.bind(this);
1001        this._stepIntoButton = this._createButtonAndRegisterShortcuts("scripts-step-into", title, handler, WebInspector.SourcesPanelDescriptor.ShortcutKeys.StepInto);
1002        debugToolbar.appendChild(this._stepIntoButton.element);
1003
1004        // Step into selection (keyboard shortcut only).
1005        this.registerShortcuts(WebInspector.SourcesPanelDescriptor.ShortcutKeys.StepIntoSelection, this._stepIntoSelectionClicked.bind(this))
1006
1007        // Step out.
1008        title = WebInspector.UIString("Step out of current function (%s).");
1009        handler = this._stepOutClicked.bind(this);
1010        this._stepOutButton = this._createButtonAndRegisterShortcuts("scripts-step-out", title, handler, WebInspector.SourcesPanelDescriptor.ShortcutKeys.StepOut);
1011        debugToolbar.appendChild(this._stepOutButton.element);
1012
1013        // Toggle Breakpoints
1014        this._toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate breakpoints."), "scripts-toggle-breakpoints");
1015        this._toggleBreakpointsButton.toggled = false;
1016        this._toggleBreakpointsButton.addEventListener("click", this._toggleBreakpointsClicked, this);
1017        debugToolbar.appendChild(this._toggleBreakpointsButton.element);
1018
1019        // Pause on Exception
1020        this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3);
1021        this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions, this);
1022        debugToolbar.appendChild(this._pauseOnExceptionButton.element);
1023
1024        return debugToolbar;
1025    },
1026
1027    /**
1028     * @param {!WebInspector.StatusBarButton} button
1029     * @param {string} buttonTitle
1030     */
1031    _updateButtonTitle: function(button, buttonTitle)
1032    {
1033        var hasShortcuts = button.shortcuts && button.shortcuts.length;
1034        if (hasShortcuts)
1035            button.title = String.vsprintf(buttonTitle, [button.shortcuts[0].name]);
1036        else
1037            button.title = buttonTitle;
1038    },
1039
1040    /**
1041     * @param {string} buttonId
1042     * @param {string} buttonTitle
1043     * @param {function(?Event=):boolean} handler
1044     * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} shortcuts
1045     * @return {!WebInspector.StatusBarButton}
1046     */
1047    _createButtonAndRegisterShortcuts: function(buttonId, buttonTitle, handler, shortcuts)
1048    {
1049        var button = new WebInspector.StatusBarButton(buttonTitle, buttonId);
1050        button.element.addEventListener("click", handler, false);
1051        button.shortcuts = shortcuts;
1052        this._updateButtonTitle(button, buttonTitle);
1053        this.registerShortcuts(shortcuts, handler);
1054        return button;
1055    },
1056
1057    searchCanceled: function()
1058    {
1059        if (this._searchView)
1060            this._searchView.searchCanceled();
1061
1062        delete this._searchView;
1063        delete this._searchQuery;
1064    },
1065
1066    /**
1067     * @param {string} query
1068     * @param {boolean} shouldJump
1069     */
1070    performSearch: function(query, shouldJump)
1071    {
1072        this._searchableView.updateSearchMatchesCount(0);
1073
1074        if (!this.visibleView)
1075            return;
1076
1077        this._searchView = this.visibleView;
1078        this._searchQuery = query;
1079
1080        /**
1081         * @param {!WebInspector.View} view
1082         * @param {number} searchMatches
1083         * @this {WebInspector.SourcesPanel}
1084         */
1085        function finishedCallback(view, searchMatches)
1086        {
1087            if (!searchMatches)
1088                return;
1089
1090            this._searchableView.updateSearchMatchesCount(searchMatches);
1091        }
1092
1093        /**
1094         * @param {number} currentMatchIndex
1095         * @this {WebInspector.SourcesPanel}
1096         */
1097        function currentMatchChanged(currentMatchIndex)
1098        {
1099            this._searchableView.updateCurrentMatchIndex(currentMatchIndex);
1100        }
1101
1102        /**
1103         * @this {WebInspector.SourcesPanel}
1104         */
1105        function searchResultsChanged()
1106        {
1107            this._searchableView.cancelSearch();
1108        }
1109
1110        this._searchView.performSearch(query, shouldJump, finishedCallback.bind(this), currentMatchChanged.bind(this), searchResultsChanged.bind(this));
1111    },
1112
1113    jumpToNextSearchResult: function()
1114    {
1115        if (!this._searchView)
1116            return;
1117
1118        if (this._searchView !== this.visibleView) {
1119            this.performSearch(this._searchQuery, true);
1120            return;
1121        }
1122
1123        this._searchView.jumpToNextSearchResult();
1124        return true;
1125    },
1126
1127    jumpToPreviousSearchResult: function()
1128    {
1129        if (!this._searchView)
1130            return;
1131
1132        if (this._searchView !== this.visibleView) {
1133            this.performSearch(this._searchQuery, true);
1134            if (this._searchView)
1135                this._searchView.jumpToLastSearchResult();
1136            return;
1137        }
1138
1139        this._searchView.jumpToPreviousSearchResult();
1140    },
1141
1142    /**
1143     * @param {string} text
1144     */
1145    replaceSelectionWith: function(text)
1146    {
1147        var view = /** @type {!WebInspector.SourceFrame} */ (this.visibleView);
1148        view.replaceSearchMatchWith(text);
1149    },
1150
1151    /**
1152     * @param {string} query
1153     * @param {string} text
1154     */
1155    replaceAllWith: function(query, text)
1156    {
1157        var view = /** @type {!WebInspector.SourceFrame} */ (this.visibleView);
1158        view.replaceAllWith(query, text);
1159    },
1160
1161    _onKeyDown: function(event)
1162    {
1163        if (event.keyCode !== WebInspector.KeyboardShortcut.Keys.CtrlOrMeta.code)
1164            return;
1165        if (!this._paused || !this._executionSourceFrame)
1166            return;
1167        var stepIntoMarkup = this._executionSourceFrame.stepIntoMarkup();
1168        if (stepIntoMarkup)
1169            stepIntoMarkup.startIteratingSelection();
1170    },
1171
1172    _onKeyUp: function(event)
1173    {
1174        if (event.keyCode !== WebInspector.KeyboardShortcut.Keys.CtrlOrMeta.code)
1175            return;
1176        if (!this._paused || !this._executionSourceFrame)
1177            return;
1178        var stepIntoMarkup = this._executionSourceFrame.stepIntoMarkup();
1179        if (!stepIntoMarkup)
1180            return;
1181        var currentPosition = stepIntoMarkup.getSelectedItemIndex();
1182        if (typeof currentPosition === "undefined") {
1183            stepIntoMarkup.stopIteratingSelection();
1184        } else {
1185            var rawLocation = stepIntoMarkup.getRawPosition(currentPosition);
1186            this.doStepIntoSelection(rawLocation);
1187        }
1188    },
1189
1190    _toggleFormatSource: function()
1191    {
1192        delete this._skipExecutionLineRevealing;
1193        this._toggleFormatSourceButton.toggled = !this._toggleFormatSourceButton.toggled;
1194        var uiSourceCodes = this._workspace.uiSourceCodes();
1195        for (var i = 0; i < uiSourceCodes.length; ++i)
1196            uiSourceCodes[i].setFormatted(this._toggleFormatSourceButton.toggled);
1197
1198        var currentFile = this._editorContainer.currentFile();
1199
1200        WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
1201            action: WebInspector.UserMetrics.UserActionNames.TogglePrettyPrint,
1202            enabled: this._toggleFormatSourceButton.toggled,
1203            url: currentFile ? currentFile.originURL() : null
1204        });
1205    },
1206
1207    addToWatch: function(expression)
1208    {
1209        this.sidebarPanes.watchExpressions.addExpression(expression);
1210    },
1211
1212    /**
1213     * @return {boolean}
1214     */
1215    _toggleBreakpoint: function()
1216    {
1217        var sourceFrame = this.visibleView;
1218        if (!sourceFrame)
1219            return false;
1220
1221        if (sourceFrame instanceof WebInspector.JavaScriptSourceFrame) {
1222            var javaScriptSourceFrame = /** @type {!WebInspector.JavaScriptSourceFrame} */ (sourceFrame);
1223            javaScriptSourceFrame.toggleBreakpointOnCurrentLine();
1224            return true;
1225        }
1226        return false;
1227    },
1228
1229    /**
1230     * @param {?Event=} event
1231     * @return {boolean}
1232     */
1233    _showOutlineDialog: function(event)
1234    {
1235        var uiSourceCode = this._editorContainer.currentFile();
1236        if (!uiSourceCode)
1237            return false;
1238
1239        switch (uiSourceCode.contentType()) {
1240        case WebInspector.resourceTypes.Document:
1241        case WebInspector.resourceTypes.Script:
1242            WebInspector.JavaScriptOutlineDialog.show(this.visibleView, uiSourceCode, this.highlightPosition.bind(this));
1243            return true;
1244        case WebInspector.resourceTypes.Stylesheet:
1245            WebInspector.StyleSheetOutlineDialog.show(this.visibleView, uiSourceCode, this.highlightPosition.bind(this));
1246            return true;
1247        }
1248        return false;
1249    },
1250
1251    _installDebuggerSidebarController: function()
1252    {
1253        this._toggleDebuggerSidebarButton = new WebInspector.StatusBarButton("", "right-sidebar-show-hide-button scripts-debugger-show-hide-button", 3);
1254        this._toggleDebuggerSidebarButton.addEventListener("click", clickHandler, this);
1255
1256        if (this.splitView.isVertical()) {
1257            this.editorView.element.appendChild(this._toggleDebuggerSidebarButton.element);
1258            this.splitView.mainElement.appendChild(this._debugSidebarResizeWidgetElement);
1259        } else {
1260            this._statusBarContainerElement.appendChild(this._debugSidebarResizeWidgetElement);
1261            this._statusBarContainerElement.appendChild(this._toggleDebuggerSidebarButton.element);
1262        }
1263
1264        this._enableDebuggerSidebar(!WebInspector.settings.debuggerSidebarHidden.get());
1265
1266        /**
1267         * @this {WebInspector.SourcesPanel}
1268         */
1269        function clickHandler()
1270        {
1271            this._enableDebuggerSidebar(this._toggleDebuggerSidebarButton.state === "left");
1272        }
1273    },
1274
1275    /**
1276     * @param {boolean} show
1277     */
1278    _enableDebuggerSidebar: function(show)
1279    {
1280        this._toggleDebuggerSidebarButton.state = show ? "right" : "left";
1281        this._toggleDebuggerSidebarButton.title = show ? WebInspector.UIString("Hide debugger") : WebInspector.UIString("Show debugger");
1282        if (show)
1283            this.splitView.showSidebarElement();
1284        else
1285            this.splitView.hideSidebarElement();
1286        this._debugSidebarResizeWidgetElement.enableStyleClass("hidden", !show);
1287        WebInspector.settings.debuggerSidebarHidden.set(!show);
1288    },
1289
1290    /**
1291     * @param {!WebInspector.Event} event
1292     */
1293    _itemCreationRequested: function(event)
1294    {
1295        var project = event.data.project;
1296        var path = event.data.path;
1297        var uiSourceCodeToCopy = event.data.uiSourceCode;
1298        var filePath;
1299        var shouldHideNavigator;
1300        var uiSourceCode;
1301
1302        /**
1303         * @param {?string} content
1304         * @this {WebInspector.SourcesPanel}
1305         */
1306        function contentLoaded(content)
1307        {
1308            createFile.call(this, content || "");
1309        }
1310
1311        if (uiSourceCodeToCopy)
1312            uiSourceCodeToCopy.requestContent(contentLoaded.bind(this));
1313        else
1314            createFile.call(this);
1315
1316        /**
1317         * @param {string=} content
1318         * @this {WebInspector.SourcesPanel}
1319         */
1320        function createFile(content)
1321        {
1322            project.createFile(path, null, content || "", fileCreated.bind(this));
1323        }
1324
1325        /**
1326         * @param {?string} path
1327         * @this {WebInspector.SourcesPanel}
1328         */
1329        function fileCreated(path)
1330        {
1331            if (!path)
1332                return;
1333            filePath = path;
1334            uiSourceCode = project.uiSourceCode(filePath);
1335            this._showSourceLocation(uiSourceCode);
1336
1337            shouldHideNavigator = !this._navigatorController.isNavigatorPinned();
1338            if (this._navigatorController.isNavigatorHidden())
1339                this._navigatorController.showNavigatorOverlay();
1340            this._navigator.rename(uiSourceCode, callback.bind(this));
1341        }
1342
1343        /**
1344         * @param {boolean} committed
1345         * @this {WebInspector.SourcesPanel}
1346         */
1347        function callback(committed)
1348        {
1349            if (shouldHideNavigator)
1350                this._navigatorController.hideNavigatorOverlay();
1351
1352            if (!committed) {
1353                project.deleteFile(uiSourceCode);
1354                return;
1355            }
1356
1357            this._recreateSourceFrameIfNeeded(uiSourceCode);
1358            this._navigator.updateIcon(uiSourceCode);
1359            this._showSourceLocation(uiSourceCode);
1360        }
1361    },
1362
1363    /**
1364     * @param {!WebInspector.Event} event
1365     */
1366    _itemRenamingRequested: function(event)
1367    {
1368        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
1369
1370        var shouldHideNavigator = !this._navigatorController.isNavigatorPinned();
1371        if (this._navigatorController.isNavigatorHidden())
1372            this._navigatorController.showNavigatorOverlay();
1373        this._navigator.rename(uiSourceCode, callback.bind(this));
1374
1375        /**
1376         * @param {boolean} committed
1377         * @this {WebInspector.SourcesPanel}
1378         */
1379        function callback(committed)
1380        {
1381            if (shouldHideNavigator && committed)
1382                this._navigatorController.hideNavigatorOverlay();
1383            this._recreateSourceFrameIfNeeded(uiSourceCode);
1384            this._navigator.updateIcon(uiSourceCode);
1385            this._showSourceLocation(uiSourceCode);
1386        }
1387    },
1388
1389    /**
1390     * @param {!WebInspector.UISourceCode} uiSourceCode
1391     */
1392    _showLocalHistory: function(uiSourceCode)
1393    {
1394        WebInspector.RevisionHistoryView.showHistory(uiSourceCode);
1395    },
1396
1397    /**
1398     * @param {!WebInspector.ContextMenu} contextMenu
1399     * @param {!Object} target
1400     */
1401    appendApplicableItems: function(event, contextMenu, target)
1402    {
1403        this._appendUISourceCodeItems(contextMenu, target);
1404        this._appendFunctionItems(contextMenu, target);
1405    },
1406
1407    /**
1408     * @param {!WebInspector.UISourceCode} uiSourceCode
1409     */
1410    _mapFileSystemToNetwork: function(uiSourceCode)
1411    {
1412        WebInspector.SelectUISourceCodeForProjectTypeDialog.show(uiSourceCode.name(), WebInspector.projectTypes.Network, mapFileSystemToNetwork.bind(this), this.editorView.mainElement)
1413
1414        /**
1415         * @param {!WebInspector.UISourceCode} networkUISourceCode
1416         * @this {WebInspector.SourcesPanel}
1417         */
1418        function mapFileSystemToNetwork(networkUISourceCode)
1419        {
1420            this._workspace.addMapping(networkUISourceCode, uiSourceCode, WebInspector.fileSystemWorkspaceProvider);
1421        }
1422    },
1423
1424    /**
1425     * @param {!WebInspector.UISourceCode} uiSourceCode
1426     */
1427    _removeNetworkMapping: function(uiSourceCode)
1428    {
1429        if (confirm(WebInspector.UIString("Are you sure you want to remove network mapping?")))
1430            this._workspace.removeMapping(uiSourceCode);
1431    },
1432
1433    /**
1434     * @param {!WebInspector.UISourceCode} networkUISourceCode
1435     */
1436    _mapNetworkToFileSystem: function(networkUISourceCode)
1437    {
1438        WebInspector.SelectUISourceCodeForProjectTypeDialog.show(networkUISourceCode.name(), WebInspector.projectTypes.FileSystem, mapNetworkToFileSystem.bind(this), this.editorView.mainElement)
1439
1440        /**
1441         * @param {!WebInspector.UISourceCode} uiSourceCode
1442         * @this {WebInspector.SourcesPanel}
1443         */
1444        function mapNetworkToFileSystem(uiSourceCode)
1445        {
1446            this._workspace.addMapping(networkUISourceCode, uiSourceCode, WebInspector.fileSystemWorkspaceProvider);
1447        }
1448    },
1449
1450    /**
1451     * @param {!WebInspector.ContextMenu} contextMenu
1452     * @param {!WebInspector.UISourceCode} uiSourceCode
1453     */
1454    _appendUISourceCodeMappingItems: function(contextMenu, uiSourceCode)
1455    {
1456        if (uiSourceCode.project().type() === WebInspector.projectTypes.FileSystem) {
1457            var hasMappings = !!uiSourceCode.url;
1458            if (!hasMappings)
1459                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Map to network resource\u2026" : "Map to Network Resource\u2026"), this._mapFileSystemToNetwork.bind(this, uiSourceCode));
1460            else
1461                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove network mapping" : "Remove Network Mapping"), this._removeNetworkMapping.bind(this, uiSourceCode));
1462        }
1463
1464        /**
1465         * @param {!WebInspector.Project} project
1466         */
1467        function filterProject(project)
1468        {
1469            return project.type() === WebInspector.projectTypes.FileSystem;
1470        }
1471
1472        if (uiSourceCode.project().type() === WebInspector.projectTypes.Network) {
1473            if (!this._workspace.projects().filter(filterProject).length)
1474                return;
1475            if (this._workspace.uiSourceCodeForURL(uiSourceCode.url) === uiSourceCode)
1476                contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Map to file system resource\u2026" : "Map to File System Resource\u2026"), this._mapNetworkToFileSystem.bind(this, uiSourceCode));
1477        }
1478    },
1479
1480    /**
1481     * @param {!WebInspector.ContextMenu} contextMenu
1482     * @param {!Object} target
1483     */
1484    _appendUISourceCodeItems: function(contextMenu, target)
1485    {
1486        if (!(target instanceof WebInspector.UISourceCode))
1487            return;
1488
1489        var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (target);
1490        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Local modifications\u2026" : "Local Modifications\u2026"), this._showLocalHistory.bind(this, uiSourceCode));
1491
1492        if (WebInspector.isolatedFileSystemManager.supportsFileSystems())
1493            this._appendUISourceCodeMappingItems(contextMenu, uiSourceCode);
1494    },
1495
1496    /**
1497     * @param {!WebInspector.ContextMenu} contextMenu
1498     * @param {!Object} target
1499     */
1500    _appendFunctionItems: function(contextMenu, target)
1501    {
1502        if (!(target instanceof WebInspector.RemoteObject))
1503            return;
1504        var remoteObject = /** @type {!WebInspector.RemoteObject} */ (target);
1505        if (remoteObject.type !== "function")
1506            return;
1507
1508        /**
1509         * @param {?Protocol.Error} error
1510         * @param {!DebuggerAgent.FunctionDetails} response
1511         * @this {WebInspector.SourcesPanel}
1512         */
1513        function didGetDetails(error, response)
1514        {
1515            if (error) {
1516                console.error(error);
1517                return;
1518            }
1519
1520            var uiLocation = WebInspector.debuggerModel.rawLocationToUILocation(response.location);
1521            if (!uiLocation)
1522                return;
1523
1524            this.showUILocation(uiLocation, true);
1525        }
1526
1527        /**
1528         * @this {WebInspector.SourcesPanel}
1529         */
1530        function revealFunction()
1531        {
1532            DebuggerAgent.getFunctionDetails(remoteObject.objectId, didGetDetails.bind(this));
1533        }
1534
1535        contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Show function definition" : "Show Function Definition"), revealFunction.bind(this));
1536    },
1537
1538    showGoToSourceDialog: function()
1539    {
1540        var uiSourceCodes = this._editorContainer.historyUISourceCodes();
1541        /** @type {!Map.<!WebInspector.UISourceCode, number>} */
1542        var defaultScores = new Map();
1543        for (var i = 1; i < uiSourceCodes.length; ++i) // Skip current element
1544            defaultScores.put(uiSourceCodes[i], uiSourceCodes.length - i);
1545        WebInspector.OpenResourceDialog.show(this, this.editorView.mainElement, undefined, defaultScores);
1546    },
1547
1548    _dockSideChanged: function()
1549    {
1550        var dockSide = WebInspector.dockController.dockSide();
1551        var vertically = dockSide === WebInspector.DockController.State.DockedToRight && WebInspector.settings.splitVerticallyWhenDockedToRight.get();
1552        this._splitVertically(vertically);
1553    },
1554
1555    /**
1556     * @param {boolean} vertically
1557     */
1558    _splitVertically: function(vertically)
1559    {
1560        if (this.sidebarPaneView && vertically === !this.splitView.isVertical())
1561            return;
1562
1563        if (this.sidebarPaneView)
1564            this.sidebarPaneView.detach();
1565
1566        this.splitView.setVertical(!vertically);
1567
1568        if (!vertically) {
1569            this.splitView.uninstallResizer(this._statusBarContainerElement);
1570            this.sidebarPaneView = new WebInspector.SidebarPaneStack();
1571            for (var pane in this.sidebarPanes)
1572                this.sidebarPaneView.addPane(this.sidebarPanes[pane]);
1573            this._extensionSidebarPanesContainer = this.sidebarPaneView;
1574            this.sidebarElement.appendChild(this.debugToolbar);
1575            this.editorView.element.appendChild(this._toggleDebuggerSidebarButton.element);
1576            this.splitView.mainElement.appendChild(this._debugSidebarResizeWidgetElement);
1577        } else {
1578            this.splitView.installResizer(this._statusBarContainerElement);
1579            this.sidebarPaneView = new WebInspector.SplitView(true, this.name + "PanelSplitSidebarRatio", 0.5);
1580
1581            var group1 = new WebInspector.SidebarPaneStack();
1582            group1.show(this.sidebarPaneView.firstElement());
1583            group1.element.id = "scripts-sidebar-stack-pane";
1584            group1.addPane(this.sidebarPanes.callstack);
1585            group1.addPane(this.sidebarPanes.jsBreakpoints);
1586            group1.addPane(this.sidebarPanes.domBreakpoints);
1587            group1.addPane(this.sidebarPanes.xhrBreakpoints);
1588            group1.addPane(this.sidebarPanes.eventListenerBreakpoints);
1589            if (this.sidebarPanes.workerList)
1590                group1.addPane(this.sidebarPanes.workerList);
1591
1592            var group2 = new WebInspector.SidebarTabbedPane();
1593            group2.show(this.sidebarPaneView.secondElement());
1594            group2.addPane(this.sidebarPanes.scopechain);
1595            group2.addPane(this.sidebarPanes.watchExpressions);
1596            this._extensionSidebarPanesContainer = group2;
1597            this.sidebarPaneView.firstElement().appendChild(this.debugToolbar);
1598            this._statusBarContainerElement.appendChild(this._debugSidebarResizeWidgetElement);
1599            this._statusBarContainerElement.appendChild(this._toggleDebuggerSidebarButton.element)
1600        }
1601        for (var i = 0; i < this._extensionSidebarPanes.length; ++i)
1602            this._extensionSidebarPanesContainer.addPane(this._extensionSidebarPanes[i]);
1603
1604        this.sidebarPaneView.element.id = "scripts-debug-sidebar-contents";
1605        this.sidebarPaneView.show(this.splitView.sidebarElement);
1606
1607        this.sidebarPanes.scopechain.expand();
1608        this.sidebarPanes.jsBreakpoints.expand();
1609        this.sidebarPanes.callstack.expand();
1610
1611        if (WebInspector.settings.watchExpressions.get().length > 0)
1612            this.sidebarPanes.watchExpressions.expand();
1613    },
1614
1615    canHighlightPosition: function()
1616    {
1617        return this.visibleView && this.visibleView.canHighlightPosition();
1618    },
1619
1620    /**
1621     * @param {number} line
1622     * @param {number=} column
1623     */
1624    highlightPosition: function(line, column)
1625    {
1626        if (!this.canHighlightPosition())
1627            return;
1628        this.visibleView.highlightPosition(line, column);
1629    },
1630
1631    /**
1632     * @param {string} id
1633     * @param {!WebInspector.SidebarPane} pane
1634     */
1635    addExtensionSidebarPane: function(id, pane)
1636    {
1637        this._extensionSidebarPanes.push(pane);
1638        this._extensionSidebarPanesContainer.addPane(pane);
1639        this.setHideOnDetach();
1640    },
1641
1642    /**
1643     * @return {!WebInspector.TabbedEditorContainer}
1644     */
1645    get tabbedEditorContainer()
1646    {
1647        return this._editorContainer;
1648    },
1649
1650    __proto__: WebInspector.Panel.prototype
1651}
1652
1653/**
1654 * @constructor
1655 * @extends {WebInspector.View}
1656 */
1657WebInspector.SourcesView = function()
1658{
1659    WebInspector.View.call(this);
1660    this.registerRequiredCSS("sourcesView.css");
1661    this.element.id = "sources-panel-sources-view";
1662    this.element.classList.add("vbox");
1663    this.element.addEventListener("dragenter", this._onDragEnter.bind(this), true);
1664    this.element.addEventListener("dragover", this._onDragOver.bind(this), true);
1665}
1666
1667WebInspector.SourcesView.dragAndDropFilesType = "Files";
1668
1669WebInspector.SourcesView.prototype = {
1670    _onDragEnter: function (event)
1671    {
1672        if (event.dataTransfer.types.indexOf(WebInspector.SourcesView.dragAndDropFilesType) === -1)
1673            return;
1674        event.consume(true);
1675    },
1676
1677    _onDragOver: function (event)
1678    {
1679        if (event.dataTransfer.types.indexOf(WebInspector.SourcesView.dragAndDropFilesType) === -1)
1680            return;
1681        event.consume(true);
1682        if (this._dragMaskElement)
1683            return;
1684        this._dragMaskElement = this.element.createChild("div", "fill drag-mask");
1685        this._dragMaskElement.addEventListener("drop", this._onDrop.bind(this), true);
1686        this._dragMaskElement.addEventListener("dragleave", this._onDragLeave.bind(this), true);
1687    },
1688
1689    _onDrop: function (event)
1690    {
1691        event.consume(true);
1692        this._removeMask();
1693        var items = /** @type {!Array.<!DataTransferItem>} */ (event.dataTransfer.items);
1694        if (!items.length)
1695            return;
1696        var entry = items[0].webkitGetAsEntry();
1697        if (!entry.isDirectory)
1698            return;
1699        InspectorFrontendHost.upgradeDraggedFileSystemPermissions(entry.filesystem);
1700    },
1701
1702    _onDragLeave: function (event)
1703    {
1704        event.consume(true);
1705        this._removeMask();
1706    },
1707
1708    _removeMask: function ()
1709    {
1710        this._dragMaskElement.remove();
1711        delete this._dragMaskElement;
1712    },
1713
1714    __proto__: WebInspector.View.prototype
1715}
1716
1717/**
1718 * @constructor
1719 * @extends {WebInspector.View}
1720 */
1721WebInspector.DrawerEditorView = function()
1722{
1723    WebInspector.View.call(this);
1724    this.element.id = "drawer-editor-view";
1725    this.element.classList.add("vbox");
1726}
1727
1728WebInspector.DrawerEditorView.prototype = {
1729    __proto__: WebInspector.View.prototype
1730}
1731