browser/base/content/browser-tabPreviews.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/base/content/browser-tabPreviews.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,556 @@
     1.4 +/*
     1.5 +#ifdef 0
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.9 +#endif
    1.10 + */
    1.11 +
    1.12 +/**
    1.13 + * Tab previews utility, produces thumbnails
    1.14 + */
    1.15 +var tabPreviews = {
    1.16 +  aspectRatio: 0.5625, // 16:9
    1.17 +
    1.18 +  get width() {
    1.19 +    delete this.width;
    1.20 +    return this.width = Math.ceil(screen.availWidth / 5.75);
    1.21 +  },
    1.22 +
    1.23 +  get height() {
    1.24 +    delete this.height;
    1.25 +    return this.height = Math.round(this.width * this.aspectRatio);
    1.26 +  },
    1.27 +
    1.28 +  init: function tabPreviews_init() {
    1.29 +    if (this._selectedTab)
    1.30 +      return;
    1.31 +    this._selectedTab = gBrowser.selectedTab;
    1.32 +
    1.33 +    gBrowser.tabContainer.addEventListener("TabSelect", this, false);
    1.34 +    gBrowser.tabContainer.addEventListener("SSTabRestored", this, false);
    1.35 +  },
    1.36 +
    1.37 +  get: function tabPreviews_get(aTab) {
    1.38 +    let uri = aTab.linkedBrowser.currentURI.spec;
    1.39 +
    1.40 +    if (aTab.__thumbnail_lastURI &&
    1.41 +        aTab.__thumbnail_lastURI != uri) {
    1.42 +      aTab.__thumbnail = null;
    1.43 +      aTab.__thumbnail_lastURI = null;
    1.44 +    }
    1.45 +
    1.46 +    if (aTab.__thumbnail)
    1.47 +      return aTab.__thumbnail;
    1.48 +
    1.49 +    if (aTab.getAttribute("pending") == "true") {
    1.50 +      let img = new Image;
    1.51 +      img.src = PageThumbs.getThumbnailURL(uri);
    1.52 +      return img;
    1.53 +    }
    1.54 +
    1.55 +    return this.capture(aTab, !aTab.hasAttribute("busy"));
    1.56 +  },
    1.57 +
    1.58 +  capture: function tabPreviews_capture(aTab, aStore) {
    1.59 +    var thumbnail = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
    1.60 +    thumbnail.mozOpaque = true;
    1.61 +    thumbnail.height = this.height;
    1.62 +    thumbnail.width = this.width;
    1.63 +
    1.64 +    var ctx = thumbnail.getContext("2d");
    1.65 +    var win = aTab.linkedBrowser.contentWindow;
    1.66 +    var snippetWidth = win.innerWidth * .6;
    1.67 +    var scale = this.width / snippetWidth;
    1.68 +    ctx.scale(scale, scale);
    1.69 +    ctx.drawWindow(win, win.scrollX, win.scrollY,
    1.70 +                   snippetWidth, snippetWidth * this.aspectRatio, "rgb(255,255,255)");
    1.71 +
    1.72 +    if (aStore &&
    1.73 +        aTab.linkedBrowser /* bug 795608: the tab may got removed while drawing the thumbnail */) {
    1.74 +      aTab.__thumbnail = thumbnail;
    1.75 +      aTab.__thumbnail_lastURI = aTab.linkedBrowser.currentURI.spec;
    1.76 +    }
    1.77 +
    1.78 +    return thumbnail;
    1.79 +  },
    1.80 +
    1.81 +  handleEvent: function tabPreviews_handleEvent(event) {
    1.82 +    switch (event.type) {
    1.83 +      case "TabSelect":
    1.84 +        if (this._selectedTab &&
    1.85 +            this._selectedTab.parentNode &&
    1.86 +            !this._pendingUpdate) {
    1.87 +          // Generate a thumbnail for the tab that was selected.
    1.88 +          // The timeout keeps the UI snappy and prevents us from generating thumbnails
    1.89 +          // for tabs that will be closed. During that timeout, don't generate other
    1.90 +          // thumbnails in case multiple TabSelect events occur fast in succession.
    1.91 +          this._pendingUpdate = true;
    1.92 +          setTimeout(function (self, aTab) {
    1.93 +            self._pendingUpdate = false;
    1.94 +            if (aTab.parentNode &&
    1.95 +                !aTab.hasAttribute("busy") &&
    1.96 +                !aTab.hasAttribute("pending"))
    1.97 +              self.capture(aTab, true);
    1.98 +          }, 2000, this, this._selectedTab);
    1.99 +        }
   1.100 +        this._selectedTab = event.target;
   1.101 +        break;
   1.102 +      case "SSTabRestored":
   1.103 +        this.capture(event.target, true);
   1.104 +        break;
   1.105 +    }
   1.106 +  }
   1.107 +};
   1.108 +
   1.109 +var tabPreviewPanelHelper = {
   1.110 +  opening: function (host) {
   1.111 +    host.panel.hidden = false;
   1.112 +
   1.113 +    var handler = this._generateHandler(host);
   1.114 +    host.panel.addEventListener("popupshown", handler, false);
   1.115 +    host.panel.addEventListener("popuphiding", handler, false);
   1.116 +
   1.117 +    host._prevFocus = document.commandDispatcher.focusedElement;
   1.118 +  },
   1.119 +  _generateHandler: function (host) {
   1.120 +    var self = this;
   1.121 +    return function (event) {
   1.122 +      if (event.target == host.panel) {
   1.123 +        host.panel.removeEventListener(event.type, arguments.callee, false);
   1.124 +        self["_" + event.type](host);
   1.125 +      }
   1.126 +    };
   1.127 +  },
   1.128 +  _popupshown: function (host) {
   1.129 +    if ("setupGUI" in host)
   1.130 +      host.setupGUI();
   1.131 +  },
   1.132 +  _popuphiding: function (host) {
   1.133 +    if ("suspendGUI" in host)
   1.134 +      host.suspendGUI();
   1.135 +
   1.136 +    if (host._prevFocus) {
   1.137 +      Cc["@mozilla.org/focus-manager;1"]
   1.138 +        .getService(Ci.nsIFocusManager)
   1.139 +        .setFocus(host._prevFocus, Ci.nsIFocusManager.FLAG_NOSCROLL);
   1.140 +      host._prevFocus = null;
   1.141 +    } else
   1.142 +      gBrowser.selectedBrowser.focus();
   1.143 +
   1.144 +    if (host.tabToSelect) {
   1.145 +      gBrowser.selectedTab = host.tabToSelect;
   1.146 +      host.tabToSelect = null;
   1.147 +    }
   1.148 +  }
   1.149 +};
   1.150 +
   1.151 +/**
   1.152 + * Ctrl-Tab panel
   1.153 + */
   1.154 +var ctrlTab = {
   1.155 +  get panel () {
   1.156 +    delete this.panel;
   1.157 +    return this.panel = document.getElementById("ctrlTab-panel");
   1.158 +  },
   1.159 +  get showAllButton () {
   1.160 +    delete this.showAllButton;
   1.161 +    return this.showAllButton = document.getElementById("ctrlTab-showAll");
   1.162 +  },
   1.163 +  get previews () {
   1.164 +    delete this.previews;
   1.165 +    return this.previews = this.panel.getElementsByClassName("ctrlTab-preview");
   1.166 +  },
   1.167 +  get keys () {
   1.168 +    var keys = {};
   1.169 +    ["close", "find", "selectAll"].forEach(function (key) {
   1.170 +      keys[key] = document.getElementById("key_" + key)
   1.171 +                          .getAttribute("key")
   1.172 +                          .toLocaleLowerCase().charCodeAt(0);
   1.173 +    });
   1.174 +    delete this.keys;
   1.175 +    return this.keys = keys;
   1.176 +  },
   1.177 +  _selectedIndex: 0,
   1.178 +  get selected () this._selectedIndex < 0 ?
   1.179 +                    document.activeElement :
   1.180 +                    this.previews.item(this._selectedIndex),
   1.181 +  get isOpen   () this.panel.state == "open" || this.panel.state == "showing" || this._timer,
   1.182 +  get tabCount () this.tabList.length,
   1.183 +  get tabPreviewCount () Math.min(this.previews.length - 1, this.tabCount),
   1.184 +  get canvasWidth () Math.min(tabPreviews.width,
   1.185 +                              Math.ceil(screen.availWidth * .85 / this.tabPreviewCount)),
   1.186 +  get canvasHeight () Math.round(this.canvasWidth * tabPreviews.aspectRatio),
   1.187 +
   1.188 +  get tabList () {
   1.189 +    return this._recentlyUsedTabs;
   1.190 +  },
   1.191 +
   1.192 +  init: function ctrlTab_init() {
   1.193 +    if (!this._recentlyUsedTabs) {
   1.194 +      tabPreviews.init();
   1.195 +
   1.196 +      this._initRecentlyUsedTabs();
   1.197 +      this._init(true);
   1.198 +    }
   1.199 +  },
   1.200 +
   1.201 +  uninit: function ctrlTab_uninit() {
   1.202 +    this._recentlyUsedTabs = null;
   1.203 +    this._init(false);
   1.204 +  },
   1.205 +
   1.206 +  prefName: "browser.ctrlTab.previews",
   1.207 +  readPref: function ctrlTab_readPref() {
   1.208 +    var enable =
   1.209 +      gPrefService.getBoolPref(this.prefName) &&
   1.210 +      (!gPrefService.prefHasUserValue("browser.ctrlTab.disallowForScreenReaders") ||
   1.211 +       !gPrefService.getBoolPref("browser.ctrlTab.disallowForScreenReaders"));
   1.212 +
   1.213 +    if (enable)
   1.214 +      this.init();
   1.215 +    else
   1.216 +      this.uninit();
   1.217 +  },
   1.218 +  observe: function (aSubject, aTopic, aPrefName) {
   1.219 +    this.readPref();
   1.220 +  },
   1.221 +
   1.222 +  updatePreviews: function ctrlTab_updatePreviews() {
   1.223 +    for (let i = 0; i < this.previews.length; i++)
   1.224 +      this.updatePreview(this.previews[i], this.tabList[i]);
   1.225 +
   1.226 +    var showAllLabel = gNavigatorBundle.getString("ctrlTab.showAll.label");
   1.227 +    this.showAllButton.label =
   1.228 +      PluralForm.get(this.tabCount, showAllLabel).replace("#1", this.tabCount);
   1.229 +    this.showAllButton.hidden = !allTabs.canOpen;
   1.230 +  },
   1.231 +
   1.232 +  updatePreview: function ctrlTab_updatePreview(aPreview, aTab) {
   1.233 +    if (aPreview == this.showAllButton)
   1.234 +      return;
   1.235 +
   1.236 +    aPreview._tab = aTab;
   1.237 +
   1.238 +    if (aPreview.firstChild)
   1.239 +      aPreview.removeChild(aPreview.firstChild);
   1.240 +    if (aTab) {
   1.241 +      let canvasWidth = this.canvasWidth;
   1.242 +      let canvasHeight = this.canvasHeight;
   1.243 +      aPreview.appendChild(tabPreviews.get(aTab));
   1.244 +      aPreview.setAttribute("label", aTab.label);
   1.245 +      aPreview.setAttribute("tooltiptext", aTab.label);
   1.246 +      aPreview.setAttribute("crop", aTab.crop);
   1.247 +      aPreview.setAttribute("canvaswidth", canvasWidth);
   1.248 +      aPreview.setAttribute("canvasstyle",
   1.249 +                            "max-width:" + canvasWidth + "px;" +
   1.250 +                            "min-width:" + canvasWidth + "px;" +
   1.251 +                            "max-height:" + canvasHeight + "px;" +
   1.252 +                            "min-height:" + canvasHeight + "px;");
   1.253 +      if (aTab.image)
   1.254 +        aPreview.setAttribute("image", aTab.image);
   1.255 +      else
   1.256 +        aPreview.removeAttribute("image");
   1.257 +      aPreview.hidden = false;
   1.258 +    } else {
   1.259 +      aPreview.hidden = true;
   1.260 +      aPreview.removeAttribute("label");
   1.261 +      aPreview.removeAttribute("tooltiptext");
   1.262 +      aPreview.removeAttribute("image");
   1.263 +    }
   1.264 +  },
   1.265 +
   1.266 +  advanceFocus: function ctrlTab_advanceFocus(aForward) {
   1.267 +    let selectedIndex = Array.indexOf(this.previews, this.selected);
   1.268 +    do {
   1.269 +      selectedIndex += aForward ? 1 : -1;
   1.270 +      if (selectedIndex < 0)
   1.271 +        selectedIndex = this.previews.length - 1;
   1.272 +      else if (selectedIndex >= this.previews.length)
   1.273 +        selectedIndex = 0;
   1.274 +    } while (this.previews[selectedIndex].hidden);
   1.275 +
   1.276 +    if (this._selectedIndex == -1) {
   1.277 +      // Focus is already in the panel.
   1.278 +      this.previews[selectedIndex].focus();
   1.279 +    } else {
   1.280 +      this._selectedIndex = selectedIndex;
   1.281 +    }
   1.282 +
   1.283 +    if (this._timer) {
   1.284 +      clearTimeout(this._timer);
   1.285 +      this._timer = null;
   1.286 +      this._openPanel();
   1.287 +    }
   1.288 +  },
   1.289 +
   1.290 +  _mouseOverFocus: function ctrlTab_mouseOverFocus(aPreview) {
   1.291 +    if (this._trackMouseOver)
   1.292 +      aPreview.focus();
   1.293 +  },
   1.294 +
   1.295 +  pick: function ctrlTab_pick(aPreview) {
   1.296 +    if (!this.tabCount)
   1.297 +      return;
   1.298 +
   1.299 +    var select = (aPreview || this.selected);
   1.300 +
   1.301 +    if (select == this.showAllButton)
   1.302 +      this.showAllTabs();
   1.303 +    else
   1.304 +      this.close(select._tab);
   1.305 +  },
   1.306 +
   1.307 +  showAllTabs: function ctrlTab_showAllTabs(aPreview) {
   1.308 +    this.close();
   1.309 +    document.getElementById("Browser:ShowAllTabs").doCommand();
   1.310 +  },
   1.311 +
   1.312 +  remove: function ctrlTab_remove(aPreview) {
   1.313 +    if (aPreview._tab)
   1.314 +      gBrowser.removeTab(aPreview._tab);
   1.315 +  },
   1.316 +
   1.317 +  attachTab: function ctrlTab_attachTab(aTab, aPos) {
   1.318 +    if (aTab.closing)
   1.319 +      return;
   1.320 +
   1.321 +    if (aPos == 0)
   1.322 +      this._recentlyUsedTabs.unshift(aTab);
   1.323 +    else if (aPos)
   1.324 +      this._recentlyUsedTabs.splice(aPos, 0, aTab);
   1.325 +    else
   1.326 +      this._recentlyUsedTabs.push(aTab);
   1.327 +  },
   1.328 +
   1.329 +  detachTab: function ctrlTab_detachTab(aTab) {
   1.330 +    var i = this._recentlyUsedTabs.indexOf(aTab);
   1.331 +    if (i >= 0)
   1.332 +      this._recentlyUsedTabs.splice(i, 1);
   1.333 +  },
   1.334 +
   1.335 +  open: function ctrlTab_open() {
   1.336 +    if (this.isOpen)
   1.337 +      return;
   1.338 +
   1.339 +    document.addEventListener("keyup", this, true);
   1.340 +
   1.341 +    this.updatePreviews();
   1.342 +    this._selectedIndex = 1;
   1.343 +
   1.344 +    // Add a slight delay before showing the UI, so that a quick
   1.345 +    // "ctrl-tab" keypress just flips back to the MRU tab.
   1.346 +    this._timer = setTimeout(function (self) {
   1.347 +      self._timer = null;
   1.348 +      self._openPanel();
   1.349 +    }, 200, this);
   1.350 +  },
   1.351 +
   1.352 +  _openPanel: function ctrlTab_openPanel() {
   1.353 +    tabPreviewPanelHelper.opening(this);
   1.354 +
   1.355 +    this.panel.width = Math.min(screen.availWidth * .99,
   1.356 +                                this.canvasWidth * 1.25 * this.tabPreviewCount);
   1.357 +    var estimateHeight = this.canvasHeight * 1.25 + 75;
   1.358 +    this.panel.openPopupAtScreen(screen.availLeft + (screen.availWidth - this.panel.width) / 2,
   1.359 +                                 screen.availTop + (screen.availHeight - estimateHeight) / 2,
   1.360 +                                 false);
   1.361 +  },
   1.362 +
   1.363 +  close: function ctrlTab_close(aTabToSelect) {
   1.364 +    if (!this.isOpen)
   1.365 +      return;
   1.366 +
   1.367 +    if (this._timer) {
   1.368 +      clearTimeout(this._timer);
   1.369 +      this._timer = null;
   1.370 +      this.suspendGUI();
   1.371 +      if (aTabToSelect)
   1.372 +        gBrowser.selectedTab = aTabToSelect;
   1.373 +      return;
   1.374 +    }
   1.375 +
   1.376 +    this.tabToSelect = aTabToSelect;
   1.377 +    this.panel.hidePopup();
   1.378 +  },
   1.379 +
   1.380 +  setupGUI: function ctrlTab_setupGUI() {
   1.381 +    this.selected.focus();
   1.382 +    this._selectedIndex = -1;
   1.383 +
   1.384 +    // Track mouse movement after a brief delay so that the item that happens
   1.385 +    // to be under the mouse pointer initially won't be selected unintentionally.
   1.386 +    this._trackMouseOver = false;
   1.387 +    setTimeout(function (self) {
   1.388 +      if (self.isOpen)
   1.389 +        self._trackMouseOver = true;
   1.390 +    }, 0, this);
   1.391 +  },
   1.392 +
   1.393 +  suspendGUI: function ctrlTab_suspendGUI() {
   1.394 +    document.removeEventListener("keyup", this, true);
   1.395 +
   1.396 +    Array.forEach(this.previews, function (preview) {
   1.397 +      this.updatePreview(preview, null);
   1.398 +    }, this);
   1.399 +  },
   1.400 +
   1.401 +  onKeyPress: function ctrlTab_onKeyPress(event) {
   1.402 +    var isOpen = this.isOpen;
   1.403 +
   1.404 +    if (isOpen) {
   1.405 +      event.preventDefault();
   1.406 +      event.stopPropagation();
   1.407 +    }
   1.408 +
   1.409 +    switch (event.keyCode) {
   1.410 +      case event.DOM_VK_TAB:
   1.411 +        if (event.ctrlKey && !event.altKey && !event.metaKey) {
   1.412 +          if (isOpen) {
   1.413 +            this.advanceFocus(!event.shiftKey);
   1.414 +          } else if (!event.shiftKey) {
   1.415 +            event.preventDefault();
   1.416 +            event.stopPropagation();
   1.417 +            let tabs = gBrowser.visibleTabs;
   1.418 +            if (tabs.length > 2) {
   1.419 +              this.open();
   1.420 +            } else if (tabs.length == 2) {
   1.421 +              let index = tabs[0].selected ? 1 : 0;
   1.422 +              gBrowser.selectedTab = tabs[index];
   1.423 +            }
   1.424 +          }
   1.425 +        }
   1.426 +        break;
   1.427 +      default:
   1.428 +        if (isOpen && event.ctrlKey) {
   1.429 +          if (event.keyCode == event.DOM_VK_DELETE) {
   1.430 +            this.remove(this.selected);
   1.431 +            break;
   1.432 +          }
   1.433 +          switch (event.charCode) {
   1.434 +            case this.keys.close:
   1.435 +              this.remove(this.selected);
   1.436 +              break;
   1.437 +            case this.keys.find:
   1.438 +            case this.keys.selectAll:
   1.439 +              this.showAllTabs();
   1.440 +              break;
   1.441 +          }
   1.442 +        }
   1.443 +    }
   1.444 +  },
   1.445 +
   1.446 +  removeClosingTabFromUI: function ctrlTab_removeClosingTabFromUI(aTab) {
   1.447 +    if (this.tabCount == 2) {
   1.448 +      this.close();
   1.449 +      return;
   1.450 +    }
   1.451 +
   1.452 +    this.updatePreviews();
   1.453 +
   1.454 +    if (this.selected.hidden)
   1.455 +      this.advanceFocus(false);
   1.456 +    if (this.selected == this.showAllButton)
   1.457 +      this.advanceFocus(false);
   1.458 +
   1.459 +    // If the current tab is removed, another tab can steal our focus.
   1.460 +    if (aTab.selected && this.panel.state == "open") {
   1.461 +      setTimeout(function (selected) {
   1.462 +        selected.focus();
   1.463 +      }, 0, this.selected);
   1.464 +    }
   1.465 +  },
   1.466 +
   1.467 +  handleEvent: function ctrlTab_handleEvent(event) {
   1.468 +    switch (event.type) {
   1.469 +      case "SSWindowStateReady":
   1.470 +        this._initRecentlyUsedTabs();
   1.471 +        break;
   1.472 +      case "TabAttrModified":
   1.473 +        // tab attribute modified (e.g. label, crop, busy, image, selected)
   1.474 +        for (let i = this.previews.length - 1; i >= 0; i--) {
   1.475 +          if (this.previews[i]._tab && this.previews[i]._tab == event.target) {
   1.476 +            this.updatePreview(this.previews[i], event.target);
   1.477 +            break;
   1.478 +          }
   1.479 +        }
   1.480 +        break;
   1.481 +      case "TabSelect":
   1.482 +        this.detachTab(event.target);
   1.483 +        this.attachTab(event.target, 0);
   1.484 +        break;
   1.485 +      case "TabOpen":
   1.486 +        this.attachTab(event.target, 1);
   1.487 +        break;
   1.488 +      case "TabClose":
   1.489 +        this.detachTab(event.target);
   1.490 +        if (this.isOpen)
   1.491 +          this.removeClosingTabFromUI(event.target);
   1.492 +        break;
   1.493 +      case "keypress":
   1.494 +        this.onKeyPress(event);
   1.495 +        break;
   1.496 +      case "keyup":
   1.497 +        if (event.keyCode == event.DOM_VK_CONTROL)
   1.498 +          this.pick();
   1.499 +        break;
   1.500 +      case "popupshowing":
   1.501 +        if (event.target.id == "menu_viewPopup")
   1.502 +          document.getElementById("menu_showAllTabs").hidden = !allTabs.canOpen;
   1.503 +        break;
   1.504 +    }
   1.505 +  },
   1.506 +
   1.507 +  _initRecentlyUsedTabs: function () {
   1.508 +    this._recentlyUsedTabs =
   1.509 +      Array.filter(gBrowser.tabs, tab => !tab.closing)
   1.510 +           .sort((tab1, tab2) => tab2.lastAccessed - tab1.lastAccessed);
   1.511 +  },
   1.512 +
   1.513 +  _init: function ctrlTab__init(enable) {
   1.514 +    var toggleEventListener = enable ? "addEventListener" : "removeEventListener";
   1.515 +
   1.516 +    window[toggleEventListener]("SSWindowStateReady", this, false);
   1.517 +
   1.518 +    var tabContainer = gBrowser.tabContainer;
   1.519 +    tabContainer[toggleEventListener]("TabOpen", this, false);
   1.520 +    tabContainer[toggleEventListener]("TabAttrModified", this, false);
   1.521 +    tabContainer[toggleEventListener]("TabSelect", this, false);
   1.522 +    tabContainer[toggleEventListener]("TabClose", this, false);
   1.523 +
   1.524 +    document[toggleEventListener]("keypress", this, false);
   1.525 +    gBrowser.mTabBox.handleCtrlTab = !enable;
   1.526 +
   1.527 +    // If we're not running, hide the "Show All Tabs" menu item,
   1.528 +    // as Shift+Ctrl+Tab will be handled by the tab bar.
   1.529 +    document.getElementById("menu_showAllTabs").hidden = !enable;
   1.530 +    document.getElementById("menu_viewPopup")[toggleEventListener]("popupshowing", this);
   1.531 +
   1.532 +    // Also disable the <key> to ensure Shift+Ctrl+Tab never triggers
   1.533 +    // Show All Tabs.
   1.534 +    var key_showAllTabs = document.getElementById("key_showAllTabs");
   1.535 +    if (enable)
   1.536 +      key_showAllTabs.removeAttribute("disabled");
   1.537 +    else
   1.538 +      key_showAllTabs.setAttribute("disabled", "true");
   1.539 +  }
   1.540 +};
   1.541 +
   1.542 +
   1.543 +/**
   1.544 + * All Tabs menu
   1.545 + */
   1.546 +var allTabs = {
   1.547 +  get toolbarButton() document.getElementById("alltabs-button"),
   1.548 +  get canOpen() isElementVisible(this.toolbarButton),
   1.549 +
   1.550 +  open: function allTabs_open() {
   1.551 +    if (this.canOpen) {
   1.552 +      // Without setTimeout, the menupopup won't stay open when invoking
   1.553 +      // "View > Show All Tabs" and the menu bar auto-hides.
   1.554 +      setTimeout(function () {
   1.555 +        allTabs.toolbarButton.open = true;
   1.556 +      }, 0);
   1.557 +    }
   1.558 +  }
   1.559 +};

mercurial