Bug 1031527 - Remove dup fd from ParamTraits<MagicGrallocBufferHandle>::Read(). r...
[gecko.git] / browser / base / content / browser-tabview.js
blobff449a24bd3f92cc99de0eabd0f1123e892fbead
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 let TabView = {
6   _deck: null,
7   _iframe: null,
8   _window: null,
9   _initialized: false,
10   _browserKeyHandlerInitialized: false,
11   _closedLastVisibleTabBeforeFrameInitialized: false,
12   _isFrameLoading: false,
13   _initFrameCallbacks: [],
14   PREF_BRANCH: "browser.panorama.",
15   PREF_FIRST_RUN: "browser.panorama.experienced_first_run",
16   PREF_STARTUP_PAGE: "browser.startup.page",
17   PREF_RESTORE_ENABLED_ONCE: "browser.panorama.session_restore_enabled_once",
18   GROUPS_IDENTIFIER: "tabview-groups",
19   VISIBILITY_IDENTIFIER: "tabview-visibility",
21   // ----------
22   get windowTitle() {
23     delete this.windowTitle;
24     let brandBundle = document.getElementById("bundle_brand");
25     let brandShortName = brandBundle.getString("brandShortName");
26     let title = gNavigatorBundle.getFormattedString("tabview.title", [brandShortName]);
27     return this.windowTitle = title;
28   },
30   // ----------
31   get firstUseExperienced() {
32     let pref = this.PREF_FIRST_RUN;
33     if (Services.prefs.prefHasUserValue(pref))
34       return Services.prefs.getBoolPref(pref);
36     return false;
37   },
39   // ----------
40   set firstUseExperienced(val) {
41     Services.prefs.setBoolPref(this.PREF_FIRST_RUN, val);
42   },
44   // ----------
45   get sessionRestoreEnabledOnce() {
46     let pref = this.PREF_RESTORE_ENABLED_ONCE;
47     if (Services.prefs.prefHasUserValue(pref))
48       return Services.prefs.getBoolPref(pref);
50     return false;
51   },
53   // ----------
54   set sessionRestoreEnabledOnce(val) {
55     Services.prefs.setBoolPref(this.PREF_RESTORE_ENABLED_ONCE, val);
56   },
58   // ----------
59   init: function TabView_init() {
60     // disable the ToggleTabView command for popup windows
61     goSetCommandEnabled("Browser:ToggleTabView", window.toolbar.visible);
62     if (!window.toolbar.visible)
63       return;
65     if (this._initialized)
66       return;
68     if (this.firstUseExperienced) {
69       // ___ visibility
71       let data = SessionStore.getWindowValue(window, this.VISIBILITY_IDENTIFIER);
72       if (data && data == "true") {
73         this.show();
74       } else {
75         try {
76           data = SessionStore.getWindowValue(window, this.GROUPS_IDENTIFIER);
77           if (data) {
78             let parsedData = JSON.parse(data);
79             this.updateGroupNumberBroadcaster(parsedData.totalNumber || 1);
80           }
81         } catch (e) { }
83         let self = this;
84         // if a tab is changed from hidden to unhidden and the iframe is not
85         // initialized, load the iframe and setup the tab.
86         this._tabShowEventListener = function(event) {
87           if (!self._window)
88             self._initFrame(function() {
89               self._window.UI.onTabSelect(gBrowser.selectedTab);
90               if (self._closedLastVisibleTabBeforeFrameInitialized) {
91                 self._closedLastVisibleTabBeforeFrameInitialized = false;
92                 self._window.UI.showTabView(false);
93               }
94             });
95         };
96         this._tabCloseEventListener = function(event) {
97           if (!self._window && gBrowser.visibleTabs.length == 0)
98             self._closedLastVisibleTabBeforeFrameInitialized = true;
99         };
100         gBrowser.tabContainer.addEventListener(
101           "TabShow", this._tabShowEventListener, false);
102         gBrowser.tabContainer.addEventListener(
103           "TabClose", this._tabCloseEventListener, false);
105        if (this._tabBrowserHasHiddenTabs()) {
106          this._setBrowserKeyHandlers();
107        } else {
108          // for restoring last session and undoing recently closed window
109          this._SSWindowStateReadyListener = function (event) {
110            if (this._tabBrowserHasHiddenTabs())
111              this._setBrowserKeyHandlers();
112          }.bind(this);
113          window.addEventListener(
114            "SSWindowStateReady", this._SSWindowStateReadyListener, false);
115         }
116       }
117     }
119     Services.prefs.addObserver(this.PREF_BRANCH, this, false);
121     this._initialized = true;
122   },
124   // ----------
125   // Observes topic changes.
126   observe: function TabView_observe(subject, topic, data) {
127     if (data == this.PREF_FIRST_RUN && this.firstUseExperienced) {
128       this._addToolbarButton();
129       this.enableSessionRestore();
130     }
131   },
133   // ----------
134   // Uninitializes TabView.
135   uninit: function TabView_uninit() {
136     if (!this._initialized)
137       return;
139     Services.prefs.removeObserver(this.PREF_BRANCH, this);
141     if (this._tabShowEventListener)
142       gBrowser.tabContainer.removeEventListener(
143         "TabShow", this._tabShowEventListener, false);
145     if (this._tabCloseEventListener)
146       gBrowser.tabContainer.removeEventListener(
147         "TabClose", this._tabCloseEventListener, false);
149     if (this._SSWindowStateReadyListener)
150       window.removeEventListener(
151         "SSWindowStateReady", this._SSWindowStateReadyListener, false);
153     this._initialized = false;
155     if (this._window) {
156       this._window = null;
157     }
159     if (this._iframe) {
160       this._iframe.remove();
161       this._iframe = null;
162     }
163   },
165   // ----------
166   // Creates the frame and calls the callback once it's loaded. 
167   // If the frame already exists, calls the callback immediately. 
168   _initFrame: function TabView__initFrame(callback) {
169     let hasCallback = typeof callback == "function";
171     // prevent frame to be initialized for popup windows
172     if (!window.toolbar.visible)
173       return;
175     if (this._window) {
176       if (hasCallback)
177         callback();
178       return;
179     }
181     if (hasCallback)
182       this._initFrameCallbacks.push(callback);
184     if (this._isFrameLoading)
185       return;
187     this._isFrameLoading = true;
189     TelemetryStopwatch.start("PANORAMA_INITIALIZATION_TIME_MS");
191     // ___ find the deck
192     this._deck = document.getElementById("tab-view-deck");
194     // ___ create the frame
195     this._iframe = document.createElement("iframe");
196     this._iframe.id = "tab-view";
197     this._iframe.setAttribute("transparent", "true");
198     this._iframe.setAttribute("tooltip", "tab-view-tooltip");
199     this._iframe.flex = 1;
201     let self = this;
203     window.addEventListener("tabviewframeinitialized", function onInit() {
204       window.removeEventListener("tabviewframeinitialized", onInit, false);
206       TelemetryStopwatch.finish("PANORAMA_INITIALIZATION_TIME_MS");
208       self._isFrameLoading = false;
209       self._window = self._iframe.contentWindow;
210       self._setBrowserKeyHandlers();
212       if (self._tabShowEventListener) {
213         gBrowser.tabContainer.removeEventListener(
214           "TabShow", self._tabShowEventListener, false);
215         self._tabShowEventListener = null;
216       }
217       if (self._tabCloseEventListener) {
218         gBrowser.tabContainer.removeEventListener(
219           "TabClose", self._tabCloseEventListener, false);
220         self._tabCloseEventListener = null;
221       }
222       if (self._SSWindowStateReadyListener) {
223         window.removeEventListener(
224           "SSWindowStateReady", self._SSWindowStateReadyListener, false);
225         self._SSWindowStateReadyListener = null;
226       }
228       self._initFrameCallbacks.forEach(function (cb) cb());
229       self._initFrameCallbacks = [];
230     }, false);
232     this._iframe.setAttribute("src", "chrome://browser/content/tabview.html");
233     this._deck.appendChild(this._iframe);
235     // ___ create tooltip
236     let tooltip = document.createElement("tooltip");
237     tooltip.id = "tab-view-tooltip";
238     tooltip.setAttribute("onpopupshowing", "return TabView.fillInTooltip(document.tooltipNode);");
239     document.getElementById("mainPopupSet").appendChild(tooltip);
240   },
242   // ----------
243   getContentWindow: function TabView_getContentWindow() {
244     return this._window;
245   },
247   // ----------
248   isVisible: function TabView_isVisible() {
249     return (this._deck ? this._deck.selectedPanel == this._iframe : false);
250   },
252   // ----------
253   show: function TabView_show() {
254     if (this.isVisible())
255       return;
257     let self = this;
258     this._initFrame(function() {
259       self._window.UI.showTabView(true);
260     });
261   },
263   // ----------
264   hide: function TabView_hide() {
265     if (this.isVisible() && this._window) {
266       this._window.UI.exit();
267     }
268   },
270   // ----------
271   toggle: function TabView_toggle() {
272     if (this.isVisible())
273       this.hide();
274     else 
275       this.show();
276   },
278   // ----------
279   _tabBrowserHasHiddenTabs: function TabView_tabBrowserHasHiddenTabs() {
280     return (gBrowser.tabs.length - gBrowser.visibleTabs.length) > 0;
281   },
283   // ----------
284   updateContextMenu: function TabView_updateContextMenu(tab, popup) {
285     let separator = document.getElementById("context_tabViewNamedGroups");
286     let isEmpty = true;
288     while (popup.firstChild && popup.firstChild != separator)
289       popup.removeChild(popup.firstChild);
291     let self = this;
292     this._initFrame(function() {
293       let activeGroup = tab._tabViewTabItem.parent;
294       let groupItems = self._window.GroupItems.groupItems;
296       groupItems.forEach(function(groupItem) {
297         // if group has title, it's not hidden and there is no active group or
298         // the active group id doesn't match the group id, a group menu item
299         // would be added.
300         if (!groupItem.hidden &&
301             (groupItem.getTitle().trim() || groupItem.getChildren().length) &&
302             (!activeGroup || activeGroup.id != groupItem.id)) {
303           let menuItem = self._createGroupMenuItem(groupItem);
304           popup.insertBefore(menuItem, separator);
305           isEmpty = false;
306         }
307       });
308       separator.hidden = isEmpty;
309     });
310   },
312   // ----------
313   _createGroupMenuItem: function TabView__createGroupMenuItem(groupItem) {
314     let menuItem = document.createElement("menuitem");
315     let title = groupItem.getTitle();
317     if (!title.trim()) {
318       let topChildLabel = groupItem.getTopChild().tab.label;
319       let childNum = groupItem.getChildren().length;
321       if (childNum > 1) {
322         let num = childNum - 1;
323         title =
324           gNavigatorBundle.getString("tabview.moveToUnnamedGroup.label");
325         title = PluralForm.get(num, title).replace("#1", topChildLabel).replace("#2", num);
326       } else {
327         title = topChildLabel;
328       }
329     }
331     menuItem.setAttribute("label", title);
332     menuItem.setAttribute("tooltiptext", title);
333     menuItem.setAttribute("crop", "center");
334     menuItem.setAttribute("class", "tabview-menuitem");
335     menuItem.setAttribute(
336       "oncommand",
337       "TabView.moveTabTo(TabContextMenu.contextTab,'" + groupItem.id + "')");
339     return menuItem;
340   },
342   // ----------
343   moveTabTo: function TabView_moveTabTo(tab, groupItemId) {
344     if (this._window) {
345       this._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
346     } else {
347       let self = this;
348       this._initFrame(function() {
349         self._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
350       });
351     }
352   },
354   // ----------
355   // Adds new key commands to the browser, for invoking the Tab Candy UI
356   // and for switching between groups of tabs when outside of the Tab Candy UI.
357   _setBrowserKeyHandlers: function TabView__setBrowserKeyHandlers() {
358     if (this._browserKeyHandlerInitialized)
359       return;
361     this._browserKeyHandlerInitialized = true;
363     let self = this;
364     window.addEventListener("keypress", function(event) {
365       if (self.isVisible() || !self._tabBrowserHasHiddenTabs())
366         return;
368       let charCode = event.charCode;
369       // Control (+ Shift) + `
370       if (event.ctrlKey && !event.metaKey && !event.altKey &&
371           (charCode == 96 || charCode == 126)) {
372         event.stopPropagation();
373         event.preventDefault();
375         self._initFrame(function() {
376           let groupItems = self._window.GroupItems;
377           let tabItem = groupItems.getNextGroupItemTab(event.shiftKey);
378           if (!tabItem)
379             return;
381           if (gBrowser.selectedTab.pinned)
382             groupItems.updateActiveGroupItemAndTabBar(tabItem, {dontSetActiveTabInGroup: true});
383           else
384             gBrowser.selectedTab = tabItem.tab;
385         });
386       }
387     }, true);
388   },
390   // ----------
391   // Prepares the tab view for undo close tab.
392   prepareUndoCloseTab: function TabView_prepareUndoCloseTab(blankTabToRemove) {
393     if (this._window) {
394       this._window.UI.restoredClosedTab = true;
396       if (blankTabToRemove && blankTabToRemove._tabViewTabItem)
397         blankTabToRemove._tabViewTabItem.isRemovedAfterRestore = true;
398     }
399   },
401   // ----------
402   // Cleans up the tab view after undo close tab.
403   afterUndoCloseTab: function TabView_afterUndoCloseTab() {
404     if (this._window)
405       this._window.UI.restoredClosedTab = false;
406   },
408   // ----------
409   // On move to group pop showing.
410   moveToGroupPopupShowing: function TabView_moveToGroupPopupShowing(event) {
411     // Update the context menu only if Panorama was already initialized or if
412     // there are hidden tabs.
413     let numHiddenTabs = gBrowser.tabs.length - gBrowser.visibleTabs.length;
414     if (this._window || numHiddenTabs > 0)
415       this.updateContextMenu(TabContextMenu.contextTab, event.target);
416   },
418   // ----------
419   // Function: _addToolbarButton
420   // Adds the TabView button to the TabsToolbar.
421   _addToolbarButton: function TabView__addToolbarButton() {
422     let buttonId = "tabview-button";
424     if (CustomizableUI.getPlacementOfWidget(buttonId))
425       return;
427     let allTabsBtnPlacement = CustomizableUI.getPlacementOfWidget("alltabs-button");
428     // allTabsBtnPlacement can never be null because the button isn't removable
429     let desiredPosition = allTabsBtnPlacement.position + 1;
430     CustomizableUI.addWidgetToArea(buttonId, "TabsToolbar", desiredPosition);
431     // NB: this is for backwards compatibility, and should be removed by
432     // https://bugzilla.mozilla.org/show_bug.cgi?id=976041
433     document.persist("TabsToolbar", "currentset");
434   },
436   // ----------
437   // Function: updateGroupNumberBroadcaster
438   // Updates the group number broadcaster.
439   updateGroupNumberBroadcaster: function TabView_updateGroupNumberBroadcaster(number) {
440     let groupsNumber = document.getElementById("tabviewGroupsNumber");
441     groupsNumber.setAttribute("groups", number);
442   },
444   // ----------
445   // Function: enableSessionRestore
446   // Enables automatic session restore when the browser is started. Does
447   // nothing if we already did that once in the past.
448   enableSessionRestore: function TabView_enableSessionRestore() {
449     if (!this._window || !this.firstUseExperienced)
450       return;
452     // do nothing if we already enabled session restore once
453     if (this.sessionRestoreEnabledOnce)
454       return;
456     this.sessionRestoreEnabledOnce = true;
458     // enable session restore if necessary
459     if (Services.prefs.getIntPref(this.PREF_STARTUP_PAGE) != 3) {
460       Services.prefs.setIntPref(this.PREF_STARTUP_PAGE, 3);
462       // show banner
463       this._window.UI.notifySessionRestoreEnabled();
464     }
465   },
467   // ----------
468   // Function: fillInTooltip
469   // Fills in the tooltip text.
470   fillInTooltip: function fillInTooltip(tipElement) {
471     let retVal = false;
472     let titleText = null;
473     let direction = tipElement.ownerDocument.dir;
475     while (!titleText && tipElement) {
476       if (tipElement.nodeType == Node.ELEMENT_NODE)
477         titleText = tipElement.getAttribute("title");
478       tipElement = tipElement.parentNode;
479     }
480     let tipNode = document.getElementById("tab-view-tooltip");
481     tipNode.style.direction = direction;
483     if (titleText) {
484       tipNode.setAttribute("label", titleText);
485       retVal = true;
486     }
488     return retVal;
489   }