browser/base/content/browser-tabview.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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   }
   490 };

mercurial