browser/base/content/browser-tabview.js

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

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

mercurial