Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / sources / TabbedEditorContainer.js
blob9e7aaa9cadd07c91b2fa916ddc0842c3c19553d7
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC.
20  * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
29 /**
30  * @interface
31  */
32 WebInspector.TabbedEditorContainerDelegate = function() { }
34 WebInspector.TabbedEditorContainerDelegate.prototype = {
35     /**
36      * @param {!WebInspector.UISourceCode} uiSourceCode
37      * @return {!WebInspector.SourceFrame}
38      */
39     viewForFile: function(uiSourceCode) { },
42 /**
43  * @constructor
44  * @extends {WebInspector.Object}
45  * @param {!WebInspector.TabbedEditorContainerDelegate} delegate
46  * @param {!WebInspector.Setting} setting
47  * @param {string} placeholderText
48  */
49 WebInspector.TabbedEditorContainer = function(delegate, setting, placeholderText)
51     WebInspector.Object.call(this);
52     this._delegate = delegate;
54     this._tabbedPane = new WebInspector.TabbedPane();
55     this._tabbedPane.setPlaceholderText(placeholderText);
56     this._tabbedPane.setTabDelegate(new WebInspector.EditorContainerTabDelegate(this));
58     this._tabbedPane.setCloseableTabs(true);
59     this._tabbedPane.setAllowTabReorder(true, true);
60     this._tabbedPane.insertBeforeTabStrip(createElementWithClass("div", "sources-editor-tabstrip-left"));
61     this._tabbedPane.appendAfterTabStrip(createElementWithClass("div", "sources-editor-tabstrip-right"));
63     this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this);
64     this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
66     this._tabIds = new Map();
67     this._files = {};
69     this._previouslyViewedFilesSetting = setting;
70     this._history = WebInspector.TabbedEditorContainer.History.fromObject(this._previouslyViewedFilesSetting.get());
73 WebInspector.TabbedEditorContainer.Events = {
74     EditorSelected: "EditorSelected",
75     EditorClosed: "EditorClosed"
78 WebInspector.TabbedEditorContainer._tabId = 0;
80 WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount = 30;
82 WebInspector.TabbedEditorContainer.prototype = {
83     /**
84      * @return {!WebInspector.Widget}
85      */
86     get view()
87     {
88         return this._tabbedPane;
89     },
91     /**
92      * @type {!WebInspector.SourceFrame}
93      */
94     get visibleView()
95     {
96         return this._tabbedPane.visibleView;
97     },
99     /**
100      * @return {!Array.<!WebInspector.SourceFrame>}
101      */
102     fileViews: function()
103     {
104         return /** @type {!Array.<!WebInspector.SourceFrame>} */ (this._tabbedPane.tabViews());
105     },
107     /**
108      * @param {!Element} parentElement
109      */
110     show: function(parentElement)
111     {
112         this._tabbedPane.show(parentElement);
113     },
115     /**
116      * @param {!WebInspector.UISourceCode} uiSourceCode
117      */
118     showFile: function(uiSourceCode)
119     {
120         this._innerShowFile(uiSourceCode, true);
121     },
123     /**
124      * @param {!WebInspector.UISourceCode} uiSourceCode
125      */
126     closeFile: function(uiSourceCode)
127     {
128         var tabId = this._tabIds.get(uiSourceCode);
129         if (!tabId)
130             return;
131         this._closeTabs([tabId]);
132     },
134     /**
135      * @return {!Array.<!WebInspector.UISourceCode>}
136      */
137     historyUISourceCodes: function()
138     {
139         // FIXME: there should be a way to fetch UISourceCode for its uri.
140         var uriToUISourceCode = {};
141         for (var id in this._files) {
142             var uiSourceCode = this._files[id];
143             uriToUISourceCode[uiSourceCode.uri()] = uiSourceCode;
144         }
146         var result = [];
147         var uris = this._history._urls();
148         for (var i = 0; i < uris.length; ++i) {
149             var uiSourceCode = uriToUISourceCode[uris[i]];
150             if (uiSourceCode)
151                 result.push(uiSourceCode);
152         }
153         return result;
154     },
156     _addViewListeners: function()
157     {
158         if (!this._currentView)
159             return;
160         this._currentView.addEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this);
161         this._currentView.addEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this);
162     },
164     _removeViewListeners: function()
165     {
166         if (!this._currentView)
167             return;
168         this._currentView.removeEventListener(WebInspector.SourceFrame.Events.ScrollChanged, this._scrollChanged, this);
169         this._currentView.removeEventListener(WebInspector.SourceFrame.Events.SelectionChanged, this._selectionChanged, this);
170     },
172     /**
173      * @param {!WebInspector.Event} event
174      */
175     _scrollChanged: function(event)
176     {
177         var lineNumber = /** @type {number} */ (event.data);
178         this._history.updateScrollLineNumber(this._currentFile.uri(), lineNumber);
179         this._history.save(this._previouslyViewedFilesSetting);
180     },
182     /**
183      * @param {!WebInspector.Event} event
184      */
185     _selectionChanged: function(event)
186     {
187         var range = /** @type {!WebInspector.TextRange} */ (event.data);
188         this._history.updateSelectionRange(this._currentFile.uri(), range);
189         this._history.save(this._previouslyViewedFilesSetting);
190     },
192     /**
193      * @param {!WebInspector.UISourceCode} uiSourceCode
194      * @param {boolean=} userGesture
195      */
196     _innerShowFile: function(uiSourceCode, userGesture)
197     {
198         if (this._currentFile === uiSourceCode)
199             return;
201         this._removeViewListeners();
202         this._currentFile = uiSourceCode;
204         var tabId = this._tabIds.get(uiSourceCode) || this._appendFileTab(uiSourceCode, userGesture);
206         this._tabbedPane.selectTab(tabId, userGesture);
207         if (userGesture)
208             this._editorSelectedByUserAction();
210         this._currentView = this.visibleView;
211         this._addViewListeners();
213         var eventData = { currentFile: this._currentFile, userGesture: userGesture };
214         this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorSelected, eventData);
215     },
217     /**
218      * @param {!WebInspector.UISourceCode} uiSourceCode
219      * @return {string}
220      */
221     _titleForFile: function(uiSourceCode)
222     {
223         var maxDisplayNameLength = 30;
224         var title = uiSourceCode.displayName(true).trimMiddle(maxDisplayNameLength);
225         if (uiSourceCode.isDirty() || uiSourceCode.hasUnsavedCommittedChanges())
226             title += "*";
227         return title;
228     },
230     /**
231      * @param {string} id
232      * @param {string} nextTabId
233      */
234     _maybeCloseTab: function(id, nextTabId)
235     {
236         var uiSourceCode = this._files[id];
237         var shouldPrompt = uiSourceCode.isDirty() && uiSourceCode.project().canSetFileContent();
238         // FIXME: this should be replaced with common Save/Discard/Cancel dialog.
239         if (!shouldPrompt || confirm(WebInspector.UIString("Are you sure you want to close unsaved file: %s?", uiSourceCode.name()))) {
240             uiSourceCode.resetWorkingCopy();
241             if (nextTabId)
242                 this._tabbedPane.selectTab(nextTabId, true);
243             this._tabbedPane.closeTab(id, true);
244             return true;
245         }
246         return false;
247     },
249     /**
250      * @param {!Array.<string>} ids
251      */
252     _closeTabs: function(ids)
253     {
254         var dirtyTabs = [];
255         var cleanTabs = [];
256         for (var i = 0; i < ids.length; ++i) {
257             var id = ids[i];
258             var uiSourceCode = this._files[id];
259             if (uiSourceCode.isDirty())
260                 dirtyTabs.push(id);
261             else
262                 cleanTabs.push(id);
263         }
264         if (dirtyTabs.length)
265             this._tabbedPane.selectTab(dirtyTabs[0], true);
266         this._tabbedPane.closeTabs(cleanTabs, true);
267         for (var i = 0; i < dirtyTabs.length; ++i) {
268             var nextTabId = i + 1 < dirtyTabs.length ? dirtyTabs[i + 1] : null;
269             if (!this._maybeCloseTab(dirtyTabs[i], nextTabId))
270                 break;
271         }
272     },
274     /**
275      * @param {string} tabId
276      * @param {!WebInspector.ContextMenu} contextMenu
277      */
278     _onContextMenu: function(tabId, contextMenu)
279     {
280         var uiSourceCode = this._files[tabId];
281         if (uiSourceCode)
282             contextMenu.appendApplicableItems(uiSourceCode);
283     },
285     /**
286      * @param {!WebInspector.UISourceCode} uiSourceCode
287      */
288     addUISourceCode: function(uiSourceCode)
289     {
290         var uri = uiSourceCode.uri();
291         if (this._userSelectedFiles)
292             return;
294         var index = this._history.index(uri);
295         if (index === -1)
296             return;
298         if (!this._tabIds.has(uiSourceCode))
299             this._appendFileTab(uiSourceCode, false);
301         // Select tab if this file was the last to be shown.
302         if (!index) {
303             this._innerShowFile(uiSourceCode, false);
304             return;
305         }
307         if (!this._currentFile)
308             return;
309         var currentProjectType = this._currentFile.project().type();
310         var addedProjectType = uiSourceCode.project().type();
311         var snippetsProjectType = WebInspector.projectTypes.Snippets;
312         if (this._history.index(this._currentFile.uri()) && currentProjectType === snippetsProjectType && addedProjectType !== snippetsProjectType)
313             this._innerShowFile(uiSourceCode, false);
314     },
316     /**
317      * @param {!WebInspector.UISourceCode} uiSourceCode
318      */
319     removeUISourceCode: function(uiSourceCode)
320     {
321         this.removeUISourceCodes([uiSourceCode]);
322     },
324     /**
325      * @param {!Array.<!WebInspector.UISourceCode>} uiSourceCodes
326      */
327     removeUISourceCodes: function(uiSourceCodes)
328     {
329         var tabIds = [];
330         for (var i = 0; i < uiSourceCodes.length; ++i) {
331             var uiSourceCode = uiSourceCodes[i];
332             var tabId = this._tabIds.get(uiSourceCode);
333             if (tabId)
334                 tabIds.push(tabId);
335         }
336         this._tabbedPane.closeTabs(tabIds);
337     },
339     /**
340      * @param {!WebInspector.UISourceCode} uiSourceCode
341      */
342     _editorClosedByUserAction: function(uiSourceCode)
343     {
344         this._userSelectedFiles = true;
345         this._history.remove(uiSourceCode.uri());
346         this._updateHistory();
347     },
349     _editorSelectedByUserAction: function()
350     {
351         this._userSelectedFiles = true;
352         this._updateHistory();
353     },
355     _updateHistory: function()
356     {
357         var tabIds = this._tabbedPane.lastOpenedTabIds(WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount);
359         /**
360          * @param {string} tabId
361          * @this {WebInspector.TabbedEditorContainer}
362          */
363         function tabIdToURI(tabId)
364         {
365             return this._files[tabId].uri();
366         }
368         this._history.update(tabIds.map(tabIdToURI.bind(this)));
369         this._history.save(this._previouslyViewedFilesSetting);
370     },
372     /**
373      * @param {!WebInspector.UISourceCode} uiSourceCode
374      * @return {string}
375      */
376     _tooltipForFile: function(uiSourceCode)
377     {
378         return uiSourceCode.originURL();
379     },
381     /**
382      * @param {!WebInspector.UISourceCode} uiSourceCode
383      * @param {boolean=} userGesture
384      * @return {string}
385      */
386     _appendFileTab: function(uiSourceCode, userGesture)
387     {
388         var view = this._delegate.viewForFile(uiSourceCode);
389         var title = this._titleForFile(uiSourceCode);
390         var tooltip = this._tooltipForFile(uiSourceCode);
392         var tabId = this._generateTabId();
393         this._tabIds.set(uiSourceCode, tabId);
394         this._files[tabId] = uiSourceCode;
396         var savedSelectionRange = this._history.selectionRange(uiSourceCode.uri());
397         if (savedSelectionRange)
398             view.setSelection(savedSelectionRange);
399         var savedScrollLineNumber = this._history.scrollLineNumber(uiSourceCode.uri());
400         if (savedScrollLineNumber)
401             view.scrollToLine(savedScrollLineNumber);
403         this._tabbedPane.appendTab(tabId, title, view, tooltip, userGesture);
405         this._updateFileTitle(uiSourceCode);
406         this._addUISourceCodeListeners(uiSourceCode);
407         return tabId;
408     },
410     /**
411      * @param {!WebInspector.Event} event
412      */
413     _tabClosed: function(event)
414     {
415         var tabId = /** @type {string} */ (event.data.tabId);
416         var userGesture = /** @type {boolean} */ (event.data.isUserGesture);
418         var uiSourceCode = this._files[tabId];
419         if (this._currentFile === uiSourceCode) {
420             this._removeViewListeners();
421             delete this._currentView;
422             delete this._currentFile;
423         }
424         this._tabIds.remove(uiSourceCode);
425         delete this._files[tabId];
427         this._removeUISourceCodeListeners(uiSourceCode);
429         this.dispatchEventToListeners(WebInspector.TabbedEditorContainer.Events.EditorClosed, uiSourceCode);
431         if (userGesture)
432             this._editorClosedByUserAction(uiSourceCode);
433     },
435     /**
436      * @param {!WebInspector.Event} event
437      */
438     _tabSelected: function(event)
439     {
440         var tabId = /** @type {string} */ (event.data.tabId);
441         var userGesture = /** @type {boolean} */ (event.data.isUserGesture);
443         var uiSourceCode = this._files[tabId];
444         this._innerShowFile(uiSourceCode, userGesture);
445     },
447     /**
448      * @param {!WebInspector.UISourceCode} uiSourceCode
449      */
450     _addUISourceCodeListeners: function(uiSourceCode)
451     {
452         uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
453         uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
454         uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
455         uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this);
456     },
458     /**
459      * @param {!WebInspector.UISourceCode} uiSourceCode
460      */
461     _removeUISourceCodeListeners: function(uiSourceCode)
462     {
463         uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.TitleChanged, this._uiSourceCodeTitleChanged, this);
464         uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._uiSourceCodeWorkingCopyChanged, this);
465         uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._uiSourceCodeWorkingCopyCommitted, this);
466         uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SavedStateUpdated, this._uiSourceCodeSavedStateUpdated, this);
467     },
469     /**
470      * @param {!WebInspector.UISourceCode} uiSourceCode
471      */
472     _updateFileTitle: function(uiSourceCode)
473     {
474         var tabId = this._tabIds.get(uiSourceCode);
475         if (tabId) {
476             var title = this._titleForFile(uiSourceCode);
477             this._tabbedPane.changeTabTitle(tabId, title);
478             if (uiSourceCode.hasUnsavedCommittedChanges())
479                 this._tabbedPane.setTabIcon(tabId, "warning-icon", WebInspector.UIString("Changes to this file were not saved to file system."));
480             else
481                 this._tabbedPane.setTabIcon(tabId, "");
482         }
483     },
485     _uiSourceCodeTitleChanged: function(event)
486     {
487         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
488         this._updateFileTitle(uiSourceCode);
489         this._updateHistory();
490     },
492     _uiSourceCodeWorkingCopyChanged: function(event)
493     {
494         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
495         this._updateFileTitle(uiSourceCode);
496     },
498     _uiSourceCodeWorkingCopyCommitted: function(event)
499     {
500         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
501         this._updateFileTitle(uiSourceCode);
502     },
504     _uiSourceCodeSavedStateUpdated: function(event)
505     {
506         var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.target);
507         this._updateFileTitle(uiSourceCode);
508     },
510     reset: function()
511     {
512         delete this._userSelectedFiles;
513     },
515     /**
516      * @return {string}
517      */
518     _generateTabId: function()
519     {
520         return "tab_" + (WebInspector.TabbedEditorContainer._tabId++);
521     },
523     /**
524      * @return {!WebInspector.UISourceCode} uiSourceCode
525      */
526     currentFile: function()
527     {
528         return this._currentFile;
529     },
531     __proto__: WebInspector.Object.prototype
535  * @constructor
536  * @param {string} url
537  * @param {!WebInspector.TextRange=} selectionRange
538  * @param {number=} scrollLineNumber
539  */
540 WebInspector.TabbedEditorContainer.HistoryItem = function(url, selectionRange, scrollLineNumber)
542     /** @const */ this.url = url;
543     /** @const */ this._isSerializable = url.length < WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit;
544     this.selectionRange = selectionRange;
545     this.scrollLineNumber = scrollLineNumber;
548 WebInspector.TabbedEditorContainer.HistoryItem.serializableUrlLengthLimit = 4096;
551  * @param {!Object} serializedHistoryItem
552  * @return {!WebInspector.TabbedEditorContainer.HistoryItem}
553  */
554 WebInspector.TabbedEditorContainer.HistoryItem.fromObject = function (serializedHistoryItem)
556     var selectionRange = serializedHistoryItem.selectionRange ? WebInspector.TextRange.fromObject(serializedHistoryItem.selectionRange) : undefined;
557     return new WebInspector.TabbedEditorContainer.HistoryItem(serializedHistoryItem.url, selectionRange, serializedHistoryItem.scrollLineNumber);
560 WebInspector.TabbedEditorContainer.HistoryItem.prototype = {
561     /**
562      * @return {?Object}
563      */
564     serializeToObject: function()
565     {
566         if (!this._isSerializable)
567             return null;
568         var serializedHistoryItem = {};
569         serializedHistoryItem.url = this.url;
570         serializedHistoryItem.selectionRange = this.selectionRange;
571         serializedHistoryItem.scrollLineNumber = this.scrollLineNumber;
572         return serializedHistoryItem;
573     }
577  * @constructor
578  * @param {!Array.<!WebInspector.TabbedEditorContainer.HistoryItem>} items
579  */
580 WebInspector.TabbedEditorContainer.History = function(items)
582     this._items = items;
583     this._rebuildItemIndex();
587  * @param {!Array.<!Object>} serializedHistory
588  * @return {!WebInspector.TabbedEditorContainer.History}
589  */
590 WebInspector.TabbedEditorContainer.History.fromObject = function(serializedHistory)
592     var items = [];
593     for (var i = 0; i < serializedHistory.length; ++i)
594         items.push(WebInspector.TabbedEditorContainer.HistoryItem.fromObject(serializedHistory[i]));
595     return new WebInspector.TabbedEditorContainer.History(items);
598 WebInspector.TabbedEditorContainer.History.prototype = {
599     /**
600      * @param {string} url
601      * @return {number}
602      */
603     index: function(url)
604     {
605         var index = this._itemsIndex[url];
606         if (typeof index === "number")
607             return index;
608         return -1;
609     },
611     _rebuildItemIndex: function()
612     {
613         this._itemsIndex = {};
614         for (var i = 0; i < this._items.length; ++i) {
615             console.assert(!this._itemsIndex.hasOwnProperty(this._items[i].url));
616             this._itemsIndex[this._items[i].url] = i;
617         }
618     },
620     /**
621      * @param {string} url
622      * @return {!WebInspector.TextRange|undefined}
623      */
624     selectionRange: function(url)
625     {
626         var index = this.index(url);
627         return index !== -1 ? this._items[index].selectionRange : undefined;
628     },
630     /**
631      * @param {string} url
632      * @param {!WebInspector.TextRange=} selectionRange
633      */
634     updateSelectionRange: function(url, selectionRange)
635     {
636         if (!selectionRange)
637             return;
638         var index = this.index(url);
639         if (index === -1)
640             return;
641         this._items[index].selectionRange = selectionRange;
642     },
644     /**
645      * @param {string} url
646      * @return {number|undefined}
647      */
648     scrollLineNumber: function(url)
649     {
650         var index = this.index(url);
651         return index !== -1 ? this._items[index].scrollLineNumber : undefined;
652     },
654     /**
655      * @param {string} url
656      * @param {number} scrollLineNumber
657      */
658     updateScrollLineNumber: function(url, scrollLineNumber)
659     {
660         var index = this.index(url);
661         if (index === -1)
662             return;
663         this._items[index].scrollLineNumber = scrollLineNumber;
664     },
666     /**
667      * @param {!Array.<string>} urls
668      */
669     update: function(urls)
670     {
671         for (var i = urls.length - 1; i >= 0; --i) {
672             var index = this.index(urls[i]);
673             var item;
674             if (index !== -1) {
675                 item = this._items[index];
676                 this._items.splice(index, 1);
677             } else
678                 item = new WebInspector.TabbedEditorContainer.HistoryItem(urls[i]);
679             this._items.unshift(item);
680             this._rebuildItemIndex();
681         }
682     },
684     /**
685      * @param {string} url
686      */
687     remove: function(url)
688     {
689         var index = this.index(url);
690         if (index !== -1) {
691             this._items.splice(index, 1);
692             this._rebuildItemIndex();
693         }
694     },
696     /**
697      * @param {!WebInspector.Setting} setting
698      */
699     save: function(setting)
700     {
701         setting.set(this._serializeToObject());
702     },
704     /**
705      * @return {!Array.<!Object>}
706      */
707     _serializeToObject: function()
708     {
709         var serializedHistory = [];
710         for (var i = 0; i < this._items.length; ++i) {
711             var serializedItem = this._items[i].serializeToObject();
712             if (serializedItem)
713                 serializedHistory.push(serializedItem);
714             if (serializedHistory.length === WebInspector.TabbedEditorContainer.maximalPreviouslyViewedFilesCount)
715                 break;
716         }
717         return serializedHistory;
718     },
721     /**
722      * @return {!Array.<string>}
723      */
724     _urls: function()
725     {
726         var result = [];
727         for (var i = 0; i < this._items.length; ++i)
728             result.push(this._items[i].url);
729         return result;
730     }
734  * @constructor
735  * @implements {WebInspector.TabbedPaneTabDelegate}
736  * @param {!WebInspector.TabbedEditorContainer} editorContainer
737  */
738 WebInspector.EditorContainerTabDelegate = function(editorContainer)
740     this._editorContainer = editorContainer;
743 WebInspector.EditorContainerTabDelegate.prototype = {
744     /**
745      * @override
746      * @param {!WebInspector.TabbedPane} tabbedPane
747      * @param {!Array.<string>} ids
748      */
749     closeTabs: function(tabbedPane, ids)
750     {
751         this._editorContainer._closeTabs(ids);
752     },
754     /**
755      * @override
756      * @param {string} tabId
757      * @param {!WebInspector.ContextMenu} contextMenu
758      */
759     onContextMenu: function(tabId, contextMenu)
760     {
761         this._editorContainer._onContextMenu(tabId, contextMenu);
762     }