forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			489 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			489 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| # This Source Code Form is subject to the terms of the Mozilla Public
 | |
| # License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | |
| 
 | |
| let TabView = {
 | |
|   _deck: null,
 | |
|   _iframe: null,
 | |
|   _window: null,
 | |
|   _initialized: false,
 | |
|   _browserKeyHandlerInitialized: false,
 | |
|   _closedLastVisibleTabBeforeFrameInitialized: false,
 | |
|   _isFrameLoading: false,
 | |
|   _initFrameCallbacks: [],
 | |
|   PREF_BRANCH: "browser.panorama.",
 | |
|   PREF_FIRST_RUN: "browser.panorama.experienced_first_run",
 | |
|   PREF_STARTUP_PAGE: "browser.startup.page",
 | |
|   PREF_RESTORE_ENABLED_ONCE: "browser.panorama.session_restore_enabled_once",
 | |
|   GROUPS_IDENTIFIER: "tabview-groups",
 | |
|   VISIBILITY_IDENTIFIER: "tabview-visibility",
 | |
| 
 | |
|   // ----------
 | |
|   get windowTitle() {
 | |
|     delete this.windowTitle;
 | |
|     let brandBundle = document.getElementById("bundle_brand");
 | |
|     let brandShortName = brandBundle.getString("brandShortName");
 | |
|     let title = gNavigatorBundle.getFormattedString("tabview.title", [brandShortName]);
 | |
|     return this.windowTitle = title;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   get firstUseExperienced() {
 | |
|     let pref = this.PREF_FIRST_RUN;
 | |
|     if (Services.prefs.prefHasUserValue(pref))
 | |
|       return Services.prefs.getBoolPref(pref);
 | |
| 
 | |
|     return false;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   set firstUseExperienced(val) {
 | |
|     Services.prefs.setBoolPref(this.PREF_FIRST_RUN, val);
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   get sessionRestoreEnabledOnce() {
 | |
|     let pref = this.PREF_RESTORE_ENABLED_ONCE;
 | |
|     if (Services.prefs.prefHasUserValue(pref))
 | |
|       return Services.prefs.getBoolPref(pref);
 | |
| 
 | |
|     return false;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   set sessionRestoreEnabledOnce(val) {
 | |
|     Services.prefs.setBoolPref(this.PREF_RESTORE_ENABLED_ONCE, val);
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   init: function TabView_init() {
 | |
|     // disable the ToggleTabView command for popup windows
 | |
|     goSetCommandEnabled("Browser:ToggleTabView", window.toolbar.visible);
 | |
|     if (!window.toolbar.visible)
 | |
|       return;
 | |
| 
 | |
|     if (this._initialized)
 | |
|       return;
 | |
| 
 | |
|     if (this.firstUseExperienced) {
 | |
|       // ___ visibility
 | |
|       let sessionstore =
 | |
|         Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 | |
| 
 | |
|       let data = sessionstore.getWindowValue(window, this.VISIBILITY_IDENTIFIER);
 | |
|       if (data && data == "true") {
 | |
|         this.show();
 | |
|       } else {
 | |
|         try {
 | |
|           data = sessionstore.getWindowValue(window, this.GROUPS_IDENTIFIER);
 | |
|           if (data) {
 | |
|             let parsedData = JSON.parse(data);
 | |
|             this.updateGroupNumberBroadcaster(parsedData.totalNumber || 1);
 | |
|           }
 | |
|         } catch (e) { }
 | |
| 
 | |
|         let self = this;
 | |
|         // if a tab is changed from hidden to unhidden and the iframe is not
 | |
|         // initialized, load the iframe and setup the tab.
 | |
|         this._tabShowEventListener = function(event) {
 | |
|           if (!self._window)
 | |
|             self._initFrame(function() {
 | |
|               self._window.UI.onTabSelect(gBrowser.selectedTab);
 | |
|               if (self._closedLastVisibleTabBeforeFrameInitialized) {
 | |
|                 self._closedLastVisibleTabBeforeFrameInitialized = false;
 | |
|                 self._window.UI.showTabView(false);
 | |
|               }
 | |
|             });
 | |
|         };
 | |
|         this._tabCloseEventListener = function(event) {
 | |
|           if (!self._window && gBrowser.visibleTabs.length == 0)
 | |
|             self._closedLastVisibleTabBeforeFrameInitialized = true;
 | |
|         };
 | |
|         gBrowser.tabContainer.addEventListener(
 | |
|           "TabShow", this._tabShowEventListener, false);
 | |
|         gBrowser.tabContainer.addEventListener(
 | |
|           "TabClose", this._tabCloseEventListener, false);
 | |
| 
 | |
|        if (this._tabBrowserHasHiddenTabs()) {
 | |
|          this._setBrowserKeyHandlers();
 | |
|        } else {
 | |
|          // for restoring last session and undoing recently closed window
 | |
|          this._SSWindowStateReadyListener = function (event) {
 | |
|            if (this._tabBrowserHasHiddenTabs())
 | |
|              this._setBrowserKeyHandlers();
 | |
|          }.bind(this);
 | |
|          window.addEventListener(
 | |
|            "SSWindowStateReady", this._SSWindowStateReadyListener, false);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Services.prefs.addObserver(this.PREF_BRANCH, this, false);
 | |
| 
 | |
|     this._initialized = true;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Observes topic changes.
 | |
|   observe: function TabView_observe(subject, topic, data) {
 | |
|     if (data == this.PREF_FIRST_RUN && this.firstUseExperienced) {
 | |
|       this._addToolbarButton();
 | |
|       this.enableSessionRestore();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Uninitializes TabView.
 | |
|   uninit: function TabView_uninit() {
 | |
|     if (!this._initialized)
 | |
|       return;
 | |
| 
 | |
|     Services.prefs.removeObserver(this.PREF_BRANCH, this);
 | |
| 
 | |
|     if (this._tabShowEventListener)
 | |
|       gBrowser.tabContainer.removeEventListener(
 | |
|         "TabShow", this._tabShowEventListener, false);
 | |
| 
 | |
|     if (this._tabCloseEventListener)
 | |
|       gBrowser.tabContainer.removeEventListener(
 | |
|         "TabClose", this._tabCloseEventListener, false);
 | |
| 
 | |
|     if (this._SSWindowStateReadyListener)
 | |
|       window.removeEventListener(
 | |
|         "SSWindowStateReady", this._SSWindowStateReadyListener, false);
 | |
| 
 | |
|     this._initialized = false;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Creates the frame and calls the callback once it's loaded. 
 | |
|   // If the frame already exists, calls the callback immediately. 
 | |
|   _initFrame: function TabView__initFrame(callback) {
 | |
|     let hasCallback = typeof callback == "function";
 | |
| 
 | |
|     // prevent frame to be initialized for popup windows
 | |
|     if (!window.toolbar.visible)
 | |
|       return;
 | |
| 
 | |
|     if (this._window) {
 | |
|       if (hasCallback)
 | |
|         callback();
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (hasCallback)
 | |
|       this._initFrameCallbacks.push(callback);
 | |
| 
 | |
|     if (this._isFrameLoading)
 | |
|       return;
 | |
| 
 | |
|     this._isFrameLoading = true;
 | |
| 
 | |
|     TelemetryStopwatch.start("PANORAMA_INITIALIZATION_TIME_MS");
 | |
| 
 | |
|     // ___ find the deck
 | |
|     this._deck = document.getElementById("tab-view-deck");
 | |
| 
 | |
|     // ___ create the frame
 | |
|     this._iframe = document.createElement("iframe");
 | |
|     this._iframe.id = "tab-view";
 | |
|     this._iframe.setAttribute("transparent", "true");
 | |
|     this._iframe.setAttribute("tooltip", "tab-view-tooltip");
 | |
|     this._iframe.flex = 1;
 | |
| 
 | |
|     let self = this;
 | |
| 
 | |
|     window.addEventListener("tabviewframeinitialized", function onInit() {
 | |
|       window.removeEventListener("tabviewframeinitialized", onInit, false);
 | |
| 
 | |
|       TelemetryStopwatch.finish("PANORAMA_INITIALIZATION_TIME_MS");
 | |
| 
 | |
|       self._isFrameLoading = false;
 | |
|       self._window = self._iframe.contentWindow;
 | |
|       self._setBrowserKeyHandlers();
 | |
| 
 | |
|       if (self._tabShowEventListener) {
 | |
|         gBrowser.tabContainer.removeEventListener(
 | |
|           "TabShow", self._tabShowEventListener, false);
 | |
|         self._tabShowEventListener = null;
 | |
|       }
 | |
|       if (self._tabCloseEventListener) {
 | |
|         gBrowser.tabContainer.removeEventListener(
 | |
|           "TabClose", self._tabCloseEventListener, false);
 | |
|         self._tabCloseEventListener = null;
 | |
|       }
 | |
|       if (self._SSWindowStateReadyListener) {
 | |
|         window.removeEventListener(
 | |
|           "SSWindowStateReady", self._SSWindowStateReadyListener, false);
 | |
|         self._SSWindowStateReadyListener = null;
 | |
|       }
 | |
| 
 | |
|       self._initFrameCallbacks.forEach(function (cb) cb());
 | |
|       self._initFrameCallbacks = [];
 | |
|     }, false);
 | |
| 
 | |
|     this._iframe.setAttribute("src", "chrome://browser/content/tabview.html");
 | |
|     this._deck.appendChild(this._iframe);
 | |
| 
 | |
|     // ___ create tooltip
 | |
|     let tooltip = document.createElement("tooltip");
 | |
|     tooltip.id = "tab-view-tooltip";
 | |
|     tooltip.setAttribute("onpopupshowing", "return TabView.fillInTooltip(document.tooltipNode);");
 | |
|     document.getElementById("mainPopupSet").appendChild(tooltip);
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   getContentWindow: function TabView_getContentWindow() {
 | |
|     return this._window;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   isVisible: function TabView_isVisible() {
 | |
|     return (this._deck ? this._deck.selectedPanel == this._iframe : false);
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   show: function TabView_show() {
 | |
|     if (this.isVisible())
 | |
|       return;
 | |
| 
 | |
|     let self = this;
 | |
|     this._initFrame(function() {
 | |
|       self._window.UI.showTabView(true);
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   hide: function TabView_hide() {
 | |
|     if (!this.isVisible())
 | |
|       return;
 | |
| 
 | |
|     this._window.UI.exit();
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   toggle: function TabView_toggle() {
 | |
|     if (this.isVisible())
 | |
|       this.hide();
 | |
|     else 
 | |
|       this.show();
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   _tabBrowserHasHiddenTabs: function TabView_tabBrowserHasHiddenTabs() {
 | |
|     return (gBrowser.tabs.length - gBrowser.visibleTabs.length) > 0;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   updateContextMenu: function TabView_updateContextMenu(tab, popup) {
 | |
|     let separator = document.getElementById("context_tabViewNamedGroups");
 | |
|     let isEmpty = true;
 | |
| 
 | |
|     while (popup.firstChild && popup.firstChild != separator)
 | |
|       popup.removeChild(popup.firstChild);
 | |
| 
 | |
|     let self = this;
 | |
|     this._initFrame(function() {
 | |
|       let activeGroup = tab._tabViewTabItem.parent;
 | |
|       let groupItems = self._window.GroupItems.groupItems;
 | |
| 
 | |
|       groupItems.forEach(function(groupItem) {
 | |
|         // if group has title, it's not hidden and there is no active group or
 | |
|         // the active group id doesn't match the group id, a group menu item
 | |
|         // would be added.
 | |
|         if (!groupItem.hidden &&
 | |
|             (groupItem.getTitle().trim() || groupItem.getChildren().length) &&
 | |
|             (!activeGroup || activeGroup.id != groupItem.id)) {
 | |
|           let menuItem = self._createGroupMenuItem(groupItem);
 | |
|           popup.insertBefore(menuItem, separator);
 | |
|           isEmpty = false;
 | |
|         }
 | |
|       });
 | |
|       separator.hidden = isEmpty;
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   _createGroupMenuItem: function TabView__createGroupMenuItem(groupItem) {
 | |
|     let menuItem = document.createElement("menuitem");
 | |
|     let title = groupItem.getTitle();
 | |
| 
 | |
|     if (!title.trim()) {
 | |
|       let topChildLabel = groupItem.getTopChild().tab.label;
 | |
|       let childNum = groupItem.getChildren().length;
 | |
| 
 | |
|       if (childNum > 1) {
 | |
|         let num = childNum - 1;
 | |
|         title =
 | |
|           gNavigatorBundle.getString("tabview.moveToUnnamedGroup.label");
 | |
|         title = PluralForm.get(num, title).replace("#1", topChildLabel).replace("#2", num);
 | |
|       } else {
 | |
|         title = topChildLabel;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     menuItem.setAttribute("label", title);
 | |
|     menuItem.setAttribute("tooltiptext", title);
 | |
|     menuItem.setAttribute("crop", "center");
 | |
|     menuItem.setAttribute("class", "tabview-menuitem");
 | |
|     menuItem.setAttribute(
 | |
|       "oncommand",
 | |
|       "TabView.moveTabTo(TabContextMenu.contextTab,'" + groupItem.id + "')");
 | |
| 
 | |
|     return menuItem;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   moveTabTo: function TabView_moveTabTo(tab, groupItemId) {
 | |
|     if (this._window) {
 | |
|       this._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
 | |
|     } else {
 | |
|       let self = this;
 | |
|       this._initFrame(function() {
 | |
|         self._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
 | |
|       });
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Adds new key commands to the browser, for invoking the Tab Candy UI
 | |
|   // and for switching between groups of tabs when outside of the Tab Candy UI.
 | |
|   _setBrowserKeyHandlers: function TabView__setBrowserKeyHandlers() {
 | |
|     if (this._browserKeyHandlerInitialized)
 | |
|       return;
 | |
| 
 | |
|     this._browserKeyHandlerInitialized = true;
 | |
| 
 | |
|     let self = this;
 | |
|     window.addEventListener("keypress", function(event) {
 | |
|       if (self.isVisible() || !self._tabBrowserHasHiddenTabs())
 | |
|         return;
 | |
| 
 | |
|       let charCode = event.charCode;
 | |
|       // Control (+ Shift) + `
 | |
|       if (event.ctrlKey && !event.metaKey && !event.altKey &&
 | |
|           (charCode == 96 || charCode == 126)) {
 | |
|         event.stopPropagation();
 | |
|         event.preventDefault();
 | |
| 
 | |
|         self._initFrame(function() {
 | |
|           let groupItems = self._window.GroupItems;
 | |
|           let tabItem = groupItems.getNextGroupItemTab(event.shiftKey);
 | |
|           if (!tabItem)
 | |
|             return;
 | |
| 
 | |
|           if (gBrowser.selectedTab.pinned)
 | |
|             groupItems.updateActiveGroupItemAndTabBar(tabItem, {dontSetActiveTabInGroup: true});
 | |
|           else
 | |
|             gBrowser.selectedTab = tabItem.tab;
 | |
|         });
 | |
|       }
 | |
|     }, true);
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Prepares the tab view for undo close tab.
 | |
|   prepareUndoCloseTab: function TabView_prepareUndoCloseTab(blankTabToRemove) {
 | |
|     if (this._window) {
 | |
|       this._window.UI.restoredClosedTab = true;
 | |
| 
 | |
|       if (blankTabToRemove && blankTabToRemove._tabViewTabItem)
 | |
|         blankTabToRemove._tabViewTabItem.isRemovedAfterRestore = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Cleans up the tab view after undo close tab.
 | |
|   afterUndoCloseTab: function TabView_afterUndoCloseTab() {
 | |
|     if (this._window)
 | |
|       this._window.UI.restoredClosedTab = false;
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // On move to group pop showing.
 | |
|   moveToGroupPopupShowing: function TabView_moveToGroupPopupShowing(event) {
 | |
|     // Update the context menu only if Panorama was already initialized or if
 | |
|     // there are hidden tabs.
 | |
|     let numHiddenTabs = gBrowser.tabs.length - gBrowser.visibleTabs.length;
 | |
|     if (this._window || numHiddenTabs > 0)
 | |
|       this.updateContextMenu(TabContextMenu.contextTab, event.target);
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Function: _addToolbarButton
 | |
|   // Adds the TabView button to the TabsToolbar.
 | |
|   _addToolbarButton: function TabView__addToolbarButton() {
 | |
|     let buttonId = "tabview-button";
 | |
| 
 | |
|     if (document.getElementById(buttonId))
 | |
|       return;
 | |
| 
 | |
|     let toolbar = document.getElementById("TabsToolbar");
 | |
|     let currentSet = toolbar.currentSet.split(",");
 | |
| 
 | |
|     let alltabsPos = currentSet.indexOf("alltabs-button");
 | |
|     if (-1 == alltabsPos)
 | |
|       return;
 | |
| 
 | |
|     currentSet[alltabsPos] += "," + buttonId;
 | |
|     currentSet = currentSet.join(",");
 | |
|     toolbar.currentSet = currentSet;
 | |
|     toolbar.setAttribute("currentset", currentSet);
 | |
|     document.persist(toolbar.id, "currentset");
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Function: updateGroupNumberBroadcaster
 | |
|   // Updates the group number broadcaster.
 | |
|   updateGroupNumberBroadcaster: function TabView_updateGroupNumberBroadcaster(number) {
 | |
|     let groupsNumber = document.getElementById("tabviewGroupsNumber");
 | |
|     groupsNumber.setAttribute("groups", number);
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Function: enableSessionRestore
 | |
|   // Enables automatic session restore when the browser is started. Does
 | |
|   // nothing if we already did that once in the past.
 | |
|   enableSessionRestore: function TabView_enableSessionRestore() {
 | |
|     if (!this._window || !this.firstUseExperienced)
 | |
|       return;
 | |
| 
 | |
|     // do nothing if we already enabled session restore once
 | |
|     if (this.sessionRestoreEnabledOnce)
 | |
|       return;
 | |
| 
 | |
|     this.sessionRestoreEnabledOnce = true;
 | |
| 
 | |
|     // enable session restore if necessary
 | |
|     if (Services.prefs.getIntPref(this.PREF_STARTUP_PAGE) != 3) {
 | |
|       Services.prefs.setIntPref(this.PREF_STARTUP_PAGE, 3);
 | |
| 
 | |
|       // show banner
 | |
|       this._window.UI.notifySessionRestoreEnabled();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // ----------
 | |
|   // Function: fillInTooltip
 | |
|   // Fills in the tooltip text.
 | |
|   fillInTooltip: function fillInTooltip(tipElement) {
 | |
|     let retVal = false;
 | |
|     let titleText = null;
 | |
|     let direction = tipElement.ownerDocument.dir;
 | |
| 
 | |
|     while (!titleText && tipElement) {
 | |
|       if (tipElement.nodeType == Node.ELEMENT_NODE)
 | |
|         titleText = tipElement.getAttribute("title");
 | |
|       tipElement = tipElement.parentNode;
 | |
|     }
 | |
|     let tipNode = document.getElementById("tab-view-tooltip");
 | |
|     tipNode.style.direction = direction;
 | |
| 
 | |
|     if (titleText) {
 | |
|       tipNode.setAttribute("label", titleText);
 | |
|       retVal = true;
 | |
|     }
 | |
| 
 | |
|     return retVal;
 | |
|   }
 | |
| };
 | 
