Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / sources / SourcesView.js
blob1d3bf4588ee37412457949aa1b26b25deba2dbdf
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6  * @constructor
7  * @implements {WebInspector.TabbedEditorContainerDelegate}
8  * @implements {WebInspector.Searchable}
9  * @implements {WebInspector.Replaceable}
10  * @extends {WebInspector.VBox}
11  * @param {!WebInspector.Workspace} workspace
12  * @param {!WebInspector.SourcesPanel} sourcesPanel
13  * @suppressGlobalPropertiesCheck
14  */
15 WebInspector.SourcesView = function(workspace, sourcesPanel)
17     WebInspector.VBox.call(this);
18     this.registerRequiredCSS("sources/sourcesView.css");
19     this.element.id = "sources-panel-sources-view";
20     this.setMinimumAndPreferredSizes(50, 52, 150, 100);
22     this._workspace = workspace;
23     this._sourcesPanel = sourcesPanel;
25     this._searchableView = new WebInspector.SearchableView(this, "sourcesViewSearchConfig");
26     this._searchableView.setMinimalSearchQuerySize(0);
27     this._searchableView.show(this.element);
29     /** @type {!Map.<!WebInspector.UISourceCode, !WebInspector.UISourceCodeFrame>} */
30     this._sourceFramesByUISourceCode = new Map();
32     var tabbedEditorPlaceholderText = WebInspector.isMac() ? WebInspector.UIString("Hit Cmd+P to open a file") : WebInspector.UIString("Hit Ctrl+P to open a file");
33     this._editorContainer = new WebInspector.TabbedEditorContainer(this, WebInspector.settings.createLocalSetting("previouslyViewedFiles", []), tabbedEditorPlaceholderText);
34     this._editorContainer.show(this._searchableView.element);
35     this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorSelected, this._editorSelected, this);
36     this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorClosed, this._editorClosed, this);
38     this._historyManager = new WebInspector.EditingLocationHistoryManager(this, this.currentSourceFrame.bind(this));
40     this._toolbarContainerElement = this.element.createChild("div", "sources-toolbar");
41     this._toolbarEditorActions = new WebInspector.Toolbar(this._toolbarContainerElement);
43     self.runtime.instancesPromise(WebInspector.SourcesView.EditorAction).then(appendButtonsForExtensions.bind(this));
44     /**
45      * @param {!Array.<!WebInspector.SourcesView.EditorAction>} actions
46      * @this {WebInspector.SourcesView}
47      */
48     function appendButtonsForExtensions(actions)
49     {
50         for (var i = 0; i < actions.length; ++i)
51             this._toolbarEditorActions.appendToolbarItem(actions[i].button(this));
52     }
53     this._scriptViewToolbarText = new WebInspector.Toolbar(this._toolbarContainerElement);
55     WebInspector.startBatchUpdate();
56     this._workspace.uiSourceCodes().forEach(this._addUISourceCode.bind(this));
57     WebInspector.endBatchUpdate();
59     this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
60     this._workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
61     this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved.bind(this), this);
63     function handleBeforeUnload(event)
64     {
65         if (event.returnValue)
66             return;
67         var unsavedSourceCodes = WebInspector.workspace.unsavedSourceCodes();
68         if (!unsavedSourceCodes.length)
69             return;
71         event.returnValue = WebInspector.UIString("DevTools have unsaved changes that will be permanently lost.");
72         WebInspector.inspectorView.setCurrentPanel(WebInspector.SourcesPanel.instance());
73         for (var i = 0; i < unsavedSourceCodes.length; ++i)
74             WebInspector.Revealer.reveal(unsavedSourceCodes[i]);
75     }
76     if (!window.opener)
77         window.addEventListener("beforeunload", handleBeforeUnload, true);
79     this._shortcuts = {};
80     this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false);
83 WebInspector.SourcesView.Events = {
84     EditorClosed: "EditorClosed",
85     EditorSelected: "EditorSelected",
88 /**
89  * @param {!WebInspector.UISourceCode} uiSourceCode
90  * @return {string}
91  */
92 WebInspector.SourcesView.uiSourceCodeHighlighterType = function(uiSourceCode)
94     var networkContentType = WebInspector.NetworkProject.uiSourceCodeContentType(uiSourceCode);
95     if (networkContentType)
96         return networkContentType.canonicalMimeType();
98     var mimeType = WebInspector.ResourceType.mimeTypesForExtensions[uiSourceCode.extension().toLowerCase()];
99     return mimeType || uiSourceCode.contentType().canonicalMimeType();
102 WebInspector.SourcesView.prototype = {
103     /**
104      * @param {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(!Event=):boolean)} registerShortcutDelegate
105      */
106     registerShortcuts: function(registerShortcutDelegate)
107     {
108         /**
109          * @this {WebInspector.SourcesView}
110          * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} shortcuts
111          * @param {function(!Event=):boolean} handler
112          */
113         function registerShortcut(shortcuts, handler)
114         {
115             registerShortcutDelegate(shortcuts, handler);
116             this._registerShortcuts(shortcuts, handler);
117         }
119         registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.JumpToPreviousLocation, this._onJumpToPreviousLocation.bind(this));
120         registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.JumpToNextLocation, this._onJumpToNextLocation.bind(this));
121         registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.CloseEditorTab, this._onCloseEditorTab.bind(this));
122         registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.GoToLine, this._showGoToLineDialog.bind(this));
123         registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.GoToMember, this._showOutlineDialog.bind(this));
124         registerShortcut.call(this, [WebInspector.KeyboardShortcut.makeDescriptor("o", WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta | WebInspector.KeyboardShortcut.Modifiers.Shift)], this._showOutlineDialog.bind(this));
125         registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.ToggleBreakpoint, this._toggleBreakpoint.bind(this));
126         registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.Save, this._save.bind(this));
127         registerShortcut.call(this, WebInspector.ShortcutsScreen.SourcesPanelShortcuts.SaveAll, this._saveAll.bind(this));
128     },
130     /**
131      * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} keys
132      * @param {function(!Event=):boolean} handler
133      */
134     _registerShortcuts: function(keys, handler)
135     {
136         for (var i = 0; i < keys.length; ++i)
137             this._shortcuts[keys[i].key] = handler;
138     },
140     _handleKeyDown: function(event)
141     {
142         var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
143         var handler = this._shortcuts[shortcutKey];
144         if (handler && handler())
145             event.consume(true);
146     },
148     wasShown: function()
149     {
150         WebInspector.VBox.prototype.wasShown.call(this);
151         WebInspector.context.setFlavor(WebInspector.SourcesView, this);
152     },
154     willHide: function()
155     {
156         WebInspector.context.setFlavor(WebInspector.SourcesView, null);
157         WebInspector.VBox.prototype.willHide.call(this);
158     },
160     /**
161      * @return {!Element}
162      */
163     toolbarContainerElement: function()
164     {
165         return this._toolbarContainerElement;
166     },
168     /**
169      * @override
170      * @return {!Element}
171      */
172     defaultFocusedElement: function()
173     {
174         return this._editorContainer.view.defaultFocusedElement();
175     },
177     /**
178      * @return {!WebInspector.SearchableView}
179      */
180     searchableView: function()
181     {
182         return this._searchableView;
183     },
185     /**
186      * @return {!WebInspector.Widget}
187      */
188     visibleView: function()
189     {
190         return this._editorContainer.visibleView;
191     },
193     /**
194      * @return {?WebInspector.SourceFrame}
195      */
196     currentSourceFrame: function()
197     {
198         var view = this.visibleView();
199         if (!(view instanceof WebInspector.SourceFrame))
200             return null;
201         return /** @type {!WebInspector.SourceFrame} */ (view);
202     },
204     /**
205      * @return {?WebInspector.UISourceCode}
206      */
207     currentUISourceCode: function()
208     {
209         return this._currentUISourceCode;
210     },
212     /**
213      * @param {!Event=} event
214      */
215     _onCloseEditorTab: function(event)
216     {
217         var uiSourceCode = this.currentUISourceCode();
218         if (!uiSourceCode)
219             return false;
220         this._editorContainer.closeFile(uiSourceCode);
221         return true;
222     },
224     /**
225      * @param {!Event=} event
226      */
227     _onJumpToPreviousLocation: function(event)
228     {
229         this._historyManager.rollback();
230         return true;
231     },
233     /**
234      * @param {!Event=} event
235      */
236     _onJumpToNextLocation: function(event)
237     {
238         this._historyManager.rollover();
239         return true;
240     },
242     /**
243      * @param {!WebInspector.Event} event
244      */
245     _uiSourceCodeAdded: function(event)
246     {
247         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
248         this._addUISourceCode(uiSourceCode);
249     },
251     /**
252      * @param {!WebInspector.UISourceCode} uiSourceCode
253      */
254     _addUISourceCode: function(uiSourceCode)
255     {
256         if (uiSourceCode.project().isServiceProject())
257             return;
258         this._editorContainer.addUISourceCode(uiSourceCode);
259         // Replace debugger script-based uiSourceCode with a network-based one.
260         var currentUISourceCode = this._currentUISourceCode;
261         if (!currentUISourceCode)
262             return;
263         var networkURL = WebInspector.networkMapping.networkURL(uiSourceCode);
264         var currentNetworkURL = WebInspector.networkMapping.networkURL(currentUISourceCode);
265         if (currentUISourceCode.project().isServiceProject() && currentUISourceCode !== uiSourceCode && currentNetworkURL === networkURL && networkURL) {
266             this._showFile(uiSourceCode);
267             this._editorContainer.removeUISourceCode(currentUISourceCode);
268         }
269     },
271     _uiSourceCodeRemoved: function(event)
272     {
273         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
274         this._removeUISourceCodes([uiSourceCode]);
275     },
277     /**
278      * @param {!Array.<!WebInspector.UISourceCode>} uiSourceCodes
279      */
280     _removeUISourceCodes: function(uiSourceCodes)
281     {
282         this._editorContainer.removeUISourceCodes(uiSourceCodes);
283         for (var i = 0; i < uiSourceCodes.length; ++i) {
284             this._removeSourceFrame(uiSourceCodes[i]);
285             this._historyManager.removeHistoryForSourceCode(uiSourceCodes[i]);
286         }
287     },
289     _projectRemoved: function(event)
290     {
291         var project = event.data;
292         var uiSourceCodes = project.uiSourceCodes();
293         this._removeUISourceCodes(uiSourceCodes);
294         if (project.type() === WebInspector.projectTypes.Network)
295             this._editorContainer.reset();
296     },
298     _updateScriptViewToolbarItems: function()
299     {
300         this._scriptViewToolbarText.removeToolbarItems();
301         var sourceFrame = this.currentSourceFrame();
302         if (!sourceFrame)
303             return;
305         var toolbarText = sourceFrame.toolbarText();
306         this._scriptViewToolbarText.appendToolbarItem(toolbarText);
307     },
309     /**
310      * @param {!WebInspector.UISourceCode} uiSourceCode
311      * @param {number=} lineNumber 0-based
312      * @param {number=} columnNumber
313      * @param {boolean=} omitFocus
314      * @param {boolean=} omitHighlight
315      */
316     showSourceLocation: function(uiSourceCode, lineNumber, columnNumber, omitFocus, omitHighlight)
317     {
318         this._historyManager.updateCurrentState();
319         var sourceFrame = this._showFile(uiSourceCode);
320         if (typeof lineNumber === "number")
321             sourceFrame.revealPosition(lineNumber, columnNumber, !omitHighlight);
322         this._historyManager.pushNewState();
323         if (!omitFocus)
324             sourceFrame.focus();
325     },
327     /**
328      * @param {!WebInspector.UISourceCode} uiSourceCode
329      * @return {!WebInspector.SourceFrame}
330      */
331     _showFile: function(uiSourceCode)
332     {
333         var sourceFrame = this._getOrCreateSourceFrame(uiSourceCode);
334         if (this._currentUISourceCode === uiSourceCode)
335             return sourceFrame;
337         this._currentUISourceCode = uiSourceCode;
338         this._editorContainer.showFile(uiSourceCode);
339         this._updateScriptViewToolbarItems();
340         return sourceFrame;
341     },
343     /**
344      * @param {!WebInspector.UISourceCode} uiSourceCode
345      * @return {!WebInspector.UISourceCodeFrame}
346      */
347     _createSourceFrame: function(uiSourceCode)
348     {
349         var sourceFrame;
350         switch (uiSourceCode.contentType()) {
351         case WebInspector.resourceTypes.Script:
352             sourceFrame = new WebInspector.JavaScriptSourceFrame(this._sourcesPanel, uiSourceCode);
353             break;
354         case WebInspector.resourceTypes.Document:
355             sourceFrame = new WebInspector.JavaScriptSourceFrame(this._sourcesPanel, uiSourceCode);
356             break;
357         case WebInspector.resourceTypes.Stylesheet:
358             sourceFrame = new WebInspector.CSSSourceFrame(uiSourceCode);
359             break;
360         default:
361             sourceFrame = new WebInspector.UISourceCodeFrame(uiSourceCode);
362         break;
363         }
364         sourceFrame.setHighlighterType(WebInspector.SourcesView.uiSourceCodeHighlighterType(uiSourceCode));
365         this._sourceFramesByUISourceCode.set(uiSourceCode, sourceFrame);
366         this._historyManager.trackSourceFrameCursorJumps(sourceFrame);
367         return sourceFrame;
368     },
370     /**
371      * @param {!WebInspector.UISourceCode} uiSourceCode
372      * @return {!WebInspector.UISourceCodeFrame}
373      */
374     _getOrCreateSourceFrame: function(uiSourceCode)
375     {
376         return this._sourceFramesByUISourceCode.get(uiSourceCode) || this._createSourceFrame(uiSourceCode);
377     },
379     /**
380      * @param {!WebInspector.SourceFrame} sourceFrame
381      * @param {!WebInspector.UISourceCode} uiSourceCode
382      * @return {boolean}
383      */
384     _sourceFrameMatchesUISourceCode: function(sourceFrame, uiSourceCode)
385     {
386         switch (uiSourceCode.contentType()) {
387         case WebInspector.resourceTypes.Script:
388         case WebInspector.resourceTypes.Document:
389             return sourceFrame instanceof WebInspector.JavaScriptSourceFrame;
390         case WebInspector.resourceTypes.Stylesheet:
391             return sourceFrame instanceof WebInspector.CSSSourceFrame;
392         default:
393             return !(sourceFrame instanceof WebInspector.JavaScriptSourceFrame);
394         }
395     },
397     /**
398      * @param {!WebInspector.UISourceCode} uiSourceCode
399      */
400     _recreateSourceFrameIfNeeded: function(uiSourceCode)
401     {
402         var oldSourceFrame = this._sourceFramesByUISourceCode.get(uiSourceCode);
403         if (!oldSourceFrame)
404             return;
405         if (this._sourceFrameMatchesUISourceCode(oldSourceFrame, uiSourceCode)) {
406             oldSourceFrame.setHighlighterType(WebInspector.SourcesView.uiSourceCodeHighlighterType(uiSourceCode));
407         } else {
408             this._editorContainer.removeUISourceCode(uiSourceCode);
409             this._removeSourceFrame(uiSourceCode);
410         }
411     },
413     /**
414      * @override
415      * @param {!WebInspector.UISourceCode} uiSourceCode
416      * @return {!WebInspector.UISourceCodeFrame}
417      */
418     viewForFile: function(uiSourceCode)
419     {
420         return this._getOrCreateSourceFrame(uiSourceCode);
421     },
423     /**
424      * @param {!WebInspector.UISourceCode} uiSourceCode
425      */
426     _removeSourceFrame: function(uiSourceCode)
427     {
428         var sourceFrame = this._sourceFramesByUISourceCode.get(uiSourceCode);
429         if (!sourceFrame)
430             return;
431         this._sourceFramesByUISourceCode.remove(uiSourceCode);
432         sourceFrame.dispose();
433     },
435     clearCurrentExecutionLine: function()
436     {
437         if (this._executionSourceFrame)
438             this._executionSourceFrame.clearExecutionLine();
439         delete this._executionSourceFrame;
440     },
442     /**
443      * @param {!WebInspector.UILocation} uiLocation
444      */
445     setExecutionLocation: function(uiLocation)
446     {
447         var sourceFrame = this._getOrCreateSourceFrame(uiLocation.uiSourceCode);
448         sourceFrame.setExecutionLocation(uiLocation);
449         this._executionSourceFrame = sourceFrame;
450     },
452     _editorClosed: function(event)
453     {
454         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data);
455         this._historyManager.removeHistoryForSourceCode(uiSourceCode);
457         var wasSelected = false;
458         if (this._currentUISourceCode === uiSourceCode) {
459             delete this._currentUISourceCode;
460             wasSelected = true;
461         }
463         // SourcesNavigator does not need to update on EditorClosed.
464         this._updateScriptViewToolbarItems();
465         this._searchableView.resetSearch();
467         var data = {};
468         data.uiSourceCode = uiSourceCode;
469         data.wasSelected = wasSelected;
470         this.dispatchEventToListeners(WebInspector.SourcesView.Events.EditorClosed, data);
471     },
473     _editorSelected: function(event)
474     {
475         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data.currentFile);
476         var shouldUseHistoryManager = uiSourceCode !== this._currentUISourceCode && event.data.userGesture;
477         if (shouldUseHistoryManager)
478             this._historyManager.updateCurrentState();
479         var sourceFrame = this._showFile(uiSourceCode);
480         if (shouldUseHistoryManager)
481             this._historyManager.pushNewState();
483         this._searchableView.setReplaceable(!!sourceFrame && sourceFrame.canEditSource());
484         this._searchableView.refreshSearch();
486         this.dispatchEventToListeners(WebInspector.SourcesView.Events.EditorSelected, uiSourceCode);
487     },
489     /**
490      * @param {!WebInspector.UISourceCode} uiSourceCode
491      */
492     sourceRenamed: function(uiSourceCode)
493     {
494         this._recreateSourceFrameIfNeeded(uiSourceCode);
495     },
497     /**
498      * @override
499      */
500     searchCanceled: function()
501     {
502         if (this._searchView)
503             this._searchView.searchCanceled();
505         delete this._searchView;
506         delete this._searchConfig;
507     },
509     /**
510      * @override
511      * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
512      * @param {boolean} shouldJump
513      * @param {boolean=} jumpBackwards
514      */
515     performSearch: function(searchConfig, shouldJump, jumpBackwards)
516     {
517         this._searchableView.updateSearchMatchesCount(0);
519         var sourceFrame = this.currentSourceFrame();
520         if (!sourceFrame)
521             return;
523         this._searchView = sourceFrame;
524         this._searchConfig = searchConfig;
526         /**
527          * @param {!WebInspector.Widget} view
528          * @param {number} searchMatches
529          * @this {WebInspector.SourcesView}
530          */
531         function finishedCallback(view, searchMatches)
532         {
533             if (!searchMatches)
534                 return;
536             this._searchableView.updateSearchMatchesCount(searchMatches);
537         }
539         /**
540          * @param {number} currentMatchIndex
541          * @this {WebInspector.SourcesView}
542          */
543         function currentMatchChanged(currentMatchIndex)
544         {
545             this._searchableView.updateCurrentMatchIndex(currentMatchIndex);
546         }
548         /**
549          * @this {WebInspector.SourcesView}
550          */
551         function searchResultsChanged()
552         {
553             this.performSearch(this._searchConfig, false, false);
554         }
556         this._searchView.performSearch(this._searchConfig, shouldJump, !!jumpBackwards, finishedCallback.bind(this), currentMatchChanged.bind(this), searchResultsChanged.bind(this));
557     },
559     /**
560      * @override
561      */
562     jumpToNextSearchResult: function()
563     {
564         if (!this._searchView)
565             return;
567         if (this._searchView !== this.currentSourceFrame()) {
568             this.performSearch(this._searchConfig, true);
569             return;
570         }
572         this._searchView.jumpToNextSearchResult();
573     },
575     /**
576      * @override
577      */
578     jumpToPreviousSearchResult: function()
579     {
580         if (!this._searchView)
581             return;
583         if (this._searchView !== this.currentSourceFrame()) {
584             this.performSearch(this._searchConfig, true);
585             if (this._searchView)
586                 this._searchView.jumpToLastSearchResult();
587             return;
588         }
590         this._searchView.jumpToPreviousSearchResult();
591     },
593     /**
594      * @override
595      * @return {boolean}
596      */
597     supportsCaseSensitiveSearch: function()
598     {
599         return true;
600     },
602     /**
603      * @override
604      * @return {boolean}
605      */
606     supportsRegexSearch: function()
607     {
608         return true;
609     },
611     /**
612      * @override
613      * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
614      * @param {string} replacement
615      */
616     replaceSelectionWith: function(searchConfig, replacement)
617     {
618         var sourceFrame = this.currentSourceFrame();
619         if (!sourceFrame) {
620             console.assert(sourceFrame);
621             return;
622         }
623         sourceFrame.replaceSelectionWith(searchConfig, replacement);
624     },
626     /**
627      * @override
628      * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
629      * @param {string} replacement
630      */
631     replaceAllWith: function(searchConfig, replacement)
632     {
633         var sourceFrame = this.currentSourceFrame();
634         if (!sourceFrame) {
635             console.assert(sourceFrame);
636             return;
637         }
638         sourceFrame.replaceAllWith(searchConfig, replacement);
639     },
641     /**
642      * @param {!Event=} event
643      * @return {boolean}
644      */
645     _showOutlineDialog: function(event)
646     {
647         var uiSourceCode = this._editorContainer.currentFile();
648         if (!uiSourceCode)
649             return false;
651         switch (uiSourceCode.contentType()) {
652         case WebInspector.resourceTypes.Document:
653         case WebInspector.resourceTypes.Script:
654             WebInspector.JavaScriptOutlineDialog.show(this, uiSourceCode, this.showSourceLocation.bind(this, uiSourceCode));
655             return true;
656         case WebInspector.resourceTypes.Stylesheet:
657             WebInspector.StyleSheetOutlineDialog.show(this, uiSourceCode, this.showSourceLocation.bind(this, uiSourceCode));
658             return true;
659         default:
660             // We don't want default browser shortcut to be executed, so pretend to handle this event.
661             return true;
662         }
663     },
665     /**
666      * @param {string=} query
667      */
668     showOpenResourceDialog: function(query)
669     {
670         var uiSourceCodes = this._editorContainer.historyUISourceCodes();
671         /** @type {!Map.<!WebInspector.UISourceCode, number>} */
672         var defaultScores = new Map();
673         for (var i = 1; i < uiSourceCodes.length; ++i) // Skip current element
674             defaultScores.set(uiSourceCodes[i], uiSourceCodes.length - i);
675         WebInspector.OpenResourceDialog.show(this, this.element, query, defaultScores);
676     },
678     /**
679      * @param {!Event=} event
680      * @return {boolean}
681      */
682     _showGoToLineDialog: function(event)
683     {
684         if (this._currentUISourceCode)
685             this.showOpenResourceDialog(":");
686         return true;
687     },
689     /**
690      * @return {boolean}
691      */
692     _save: function()
693     {
694         this._saveSourceFrame(this.currentSourceFrame());
695         return true;
696     },
698     /**
699      * @return {boolean}
700      */
701     _saveAll: function()
702     {
703         var sourceFrames = this._editorContainer.fileViews();
704         sourceFrames.forEach(this._saveSourceFrame.bind(this));
705         return true;
706     },
708     /**
709      * @param {?WebInspector.SourceFrame} sourceFrame
710      */
711     _saveSourceFrame: function(sourceFrame)
712     {
713         if (!sourceFrame)
714             return;
715         if (!(sourceFrame instanceof WebInspector.UISourceCodeFrame))
716             return;
717         var uiSourceCodeFrame = /** @type {!WebInspector.UISourceCodeFrame} */ (sourceFrame);
718         uiSourceCodeFrame.commitEditing();
719     },
720     /**
721      * @return {boolean}
722      */
723     _toggleBreakpoint: function()
724     {
725         var sourceFrame = this.currentSourceFrame();
726         if (!sourceFrame)
727             return false;
729         if (sourceFrame instanceof WebInspector.JavaScriptSourceFrame) {
730             var javaScriptSourceFrame = /** @type {!WebInspector.JavaScriptSourceFrame} */ (sourceFrame);
731             javaScriptSourceFrame.toggleBreakpointOnCurrentLine();
732             return true;
733         }
734         return false;
735     },
737     /**
738      * @param {boolean} active
739      */
740     toggleBreakpointsActiveState: function(active)
741     {
742         this._editorContainer.view.element.classList.toggle("breakpoints-deactivated", !active);
743     },
745     __proto__: WebInspector.VBox.prototype
749  * @interface
750  */
751 WebInspector.SourcesView.EditorAction = function()
755 WebInspector.SourcesView.EditorAction.prototype = {
756     /**
757      * @param {!WebInspector.SourcesView} sourcesView
758      * @return {!WebInspector.ToolbarButton}
759      */
760     button: function(sourcesView) { }
764  * @constructor
765  * @implements {WebInspector.ActionDelegate}
766  */
767 WebInspector.SourcesView.SwitchFileActionDelegate = function()
772  * @param {!WebInspector.UISourceCode} currentUISourceCode
773  * @return {?WebInspector.UISourceCode}
774  */
775 WebInspector.SourcesView.SwitchFileActionDelegate._nextFile = function(currentUISourceCode)
777     /**
778      * @param {string} name
779      * @return {string}
780      */
781     function fileNamePrefix(name)
782     {
783         var lastDotIndex = name.lastIndexOf(".");
784         var namePrefix = name.substr(0, lastDotIndex !== -1 ? lastDotIndex : name.length);
785         return namePrefix.toLowerCase();
786     }
788     var uiSourceCodes = currentUISourceCode.project().uiSourceCodes();
789     var candidates = [];
790     var path = currentUISourceCode.parentPath();
791     var name = currentUISourceCode.name();
792     var namePrefix = fileNamePrefix(name);
793     for (var i = 0; i < uiSourceCodes.length; ++i) {
794         var uiSourceCode = uiSourceCodes[i];
795         if (path !== uiSourceCode.parentPath())
796             continue;
797         if (fileNamePrefix(uiSourceCode.name()) === namePrefix)
798             candidates.push(uiSourceCode.name());
799     }
800     candidates.sort(String.naturalOrderComparator);
801     var index = mod(candidates.indexOf(name) + 1, candidates.length);
802     var fullPath = (path ? path + "/" : "") + candidates[index];
803     var nextUISourceCode = currentUISourceCode.project().uiSourceCode(fullPath);
804     return nextUISourceCode !== currentUISourceCode ? nextUISourceCode : null;
808 WebInspector.SourcesView.SwitchFileActionDelegate.prototype = {
809     /**
810      * @override
811      * @param {!WebInspector.Context} context
812      * @param {string} actionId
813      */
814     handleAction: function(context, actionId)
815     {
816         var sourcesView = WebInspector.context.flavor(WebInspector.SourcesView);
817         var currentUISourceCode = sourcesView.currentUISourceCode();
818         if (!currentUISourceCode)
819             return;
820         var nextUISourceCode = WebInspector.SourcesView.SwitchFileActionDelegate._nextFile(currentUISourceCode);
821         if (!nextUISourceCode)
822             return;
823         sourcesView.showSourceLocation(nextUISourceCode);
824     }