Source: js/app_chrome.js

/* global BookmarksDatabase */
/* global eventSafety */
/* global IconsHelper */
/* global LazyLoader */
/* global ModalDialog */
/* global MozActivity */
/* global SettingsListener */
/* global Service */

'use strict';

(function(exports) {
  var _id = 0;

  var newTabManifestURL = null;
  SettingsListener.observe('rocketbar.newTabAppURL', '',
    function(url) {
      // The application list in applications.js is not yet ready, so we store
      // only the manifestURL for now and we look up the application whenever
      // we trigger a new window.
      newTabManifestURL = url ? url.match(/(^.*?:\/\/.*?\/)/)[1] +
        'manifest.webapp' : '';
    });

  /**
   * The chrome UI of the AppWindow.
   *
   * @class AppChrome
   * @param {AppWindow} app The app window instance this chrome belongs to.
   * @extends BaseUI
   */
  var AppChrome = function AppChrome(app) {
    this.app = app;
    this.instanceID = _id++;
    this.containerElement = app.element;
    this._recentTitle = false;
    this._themeChanged = false;
    this._titleTimeout = null;
    this.scrollable = app.browserContainer;
    this.render();

    if (this.app.themeColor) {
      this.setThemeColor(this.app.themeColor);
    }

    var chrome = this.app.config.chrome;
    if (!this.app.isBrowser() && chrome && !chrome.scrollable) {
      this._fixedTitle = true;
      this.title.dataset.l10nId = 'search-the-web';
    } else if (!this.app.isBrowser() && this.app.name) {
      this._gotName = true;
      this.setFreshTitle(this.app.name);
    }

    this.reConfig();
  };

  AppChrome.prototype = Object.create(window.BaseUI.prototype);

  AppChrome.prototype.CLASS_NAME = 'AppChrome';

  AppChrome.prototype.EVENT_PREFIX = 'chrome';

  AppChrome.prototype.FRESH_TITLE = 500;

  AppChrome.prototype.LOCATION_COALESCE = 250;

  AppChrome.prototype._DEBUG = false;

  AppChrome.prototype.reConfig = function() {
    var chrome = this.app.config.chrome;
    if (!chrome) {
      return;
    }

    if (this.isSearchApp()) {
      this.app.element.classList.add('search-app');
      this.title.setAttribute('data-l10n-id', 'search-or-enter-address');
    } else {
      this.app.element.classList.remove('search-app');
    }

    if (chrome.bar) {
      this.app.element.classList.add('bar');
      this.bar.classList.add('visible');
    }

    if (chrome.scrollable) {
      this.app.element.classList.add('collapsible');

      if (this.app.isPrivateBrowser()) {
        this.element.classList.add('private');
      } else {
        this.app.element.classList.add('light');
      }

      this.scrollable.scrollgrab = true;
    }

    if (chrome.maximized) {
      this.element.classList.add('maximized');

      if (!this.app.isBrowser()) {
        this.app.element.classList.add('scrollable');
      }
    }
  };

  AppChrome.prototype.combinedView = function an_combinedView() {
    var className = this.CLASS_NAME + this.instanceID;

    return `<div class="chrome chrome-combined" id="${className}">
              <gaia-progress></gaia-progress>
              <div class="controls">
                <button type="button" class="back-button"
                        data-l10n-id="back-button" disabled></button>
                <button type="button" class="forward-button"
                        data-l10n-id="forward-button" disabled></button>
                <div class="urlbar js-chrome-ssl-information">
                  <span class="pb-icon"></span>
                  <div class="chrome-ssl-indicator chrome-title-container">
                    <span class="title" dir="auto"></span>
                  </div>
                  <button type="button" class="reload-button"
                          data-l10n-id="reload-button" disabled></button>
                  <button type="button" class="stop-button"
                          data-l10n-id="stop-button"></button>
                </div>
                <button type="button" class="menu-button" alt="Menu"></button>
                <button type="button" class="windows-button"
                        data-l10n-id="windows-button"></button>
              </div>
            </div>`;
  };

  AppChrome.prototype.view = function an_view() {
    var className = this.CLASS_NAME + this.instanceID;

    return `<div class="chrome chrome-plain" id="${className}">
            <gaia-progress></gaia-progress>
            <section role="region" class="bar">
              <gaia-header action="close" class='js-chrome-ssl-information'>
                <div class="chrome-ssl-indicator chrome-ssl-indicator-ltr">
                </div>
                <h1 class="chrome-title-container">
                  <bdi dir="auto" class="title"></bdi>
                </h1>
                <div class="chrome-ssl-indicator chrome-ssl-indicator-rtl">
                </div>
              </gaia-header>
            </section>
          </div>`;
  };

  AppChrome.prototype.overflowMenuView = function an_overflowMenuView() {
    var template = `<gaia-overflow-menu>

     <button id="new-window" data-l10n-id="new-window">
     </button>

     <button id="new-private-window" data-l10n-id="new-private-window">
     </button>

     <button id="add-to-home" data-l10n-id="add-to-home-screen" hidden>
     </button>

     <button id="share" data-l10n-id="share">
     </button>

    </gaia-overflow-menu>`;
    return template;
  };

  AppChrome.prototype.__defineGetter__('height', function ac_getHeight() {
    if (this._height) {
      return this._height;
    }

    this._height = this.element.getBoundingClientRect().height;
    return this._height;
  });

  AppChrome.prototype._fetchElements = function ac__fetchElements() {
    this.element = this.containerElement.querySelector('.chrome');

    this.progress = this.element.querySelector('gaia-progress');
    this.reloadButton = this.element.querySelector('.reload-button');
    this.forwardButton = this.element.querySelector('.forward-button');
    this.stopButton = this.element.querySelector('.stop-button');
    this.backButton = this.element.querySelector('.back-button');
    this.menuButton = this.element.querySelector('.menu-button');
    this.windowsButton = this.element.querySelector('.windows-button');
    this.title = this.element.querySelector('.chrome-title-container > .title');
    this.sslIndicator =
      this.element.querySelector('.js-chrome-ssl-information');

    this.bar = this.element.querySelector('.bar');
    if (this.bar) {
      this.header = this.element.querySelector('gaia-header');
    }

    if (this.reloadButton) {
      this.reloadButton.disabled = !this.hasNavigation();
    }
  };

  AppChrome.prototype.handleEvent = function ac_handleEvent(evt) {
    switch (evt.type) {
      case 'rocketbar-overlayclosed':
        this.collapse();
        break;

      case 'click':
        this.handleClickEvent(evt);
        break;

      case 'action':
        this.handleActionEvent(evt);
        break;

      case 'scroll':
        this.handleScrollEvent(evt);
        break;

      case '_loading':
        this.show(this.progress);
        this.progress.start();
        break;

      case '_loaded':
        this.hide(this.progress);
        this.progress.stop();
        break;

      case 'mozbrowserloadstart':
        this.handleLoadStart(evt);
        break;

      case 'mozbrowserloadend':
        this.handleLoadEnd(evt);
        break;

      case 'mozbrowsererror':
        this.handleError(evt);
        break;

      case 'mozbrowserlocationchange':
        this.handleLocationChanged(evt);
        break;

      case 'mozbrowserscrollareachanged':
        this.handleScrollAreaChanged(evt);
        break;

      case '_securitychange':
        this.handleSecurityChanged(evt);
        break;

      case 'mozbrowsertitlechange':
        this.handleTitleChanged(evt);
        break;

      case 'mozbrowsermetachange':
        this.handleMetaChange(evt);
        break;

      case '_namechanged':
        this.handleNameChanged(evt);
        break;
    }
  };

  AppChrome.prototype.handleClickEvent = function ac_handleClickEvent(evt) {
    switch (evt.target) {
      case this.reloadButton:
        this.app.reload();
        break;

      case this.stopButton:
        this.app.stop();
        break;

      case this.backButton:
        this.app.back();
        break;

      case this.forwardButton:
        this.app.forward();
        break;

      case this.title:
        this.titleClicked();
        break;

      case this.menuButton:
        this.showOverflowMenu();
        break;

      case this.windowsButton:
        this.showWindows();
        break;

      case this.newWindowButton:
        evt.stopImmediatePropagation();
        this.onNewWindow();
        break;

      case this.newPrivateWinButton:
        // Currently not in use, awaiting shared menu web components work.
        evt.stopImmediatePropagation();
        this.onNewPrivateWindow();
        break;

      case this.addToHomeButton:
        evt.stopImmediatePropagation();
        this.onAddToHome();
        break;

      case this.shareButton:
        evt.stopImmediatePropagation();
        this.onShare();
        break;
    }
  };

  AppChrome.prototype.titleClicked = function ac_titleClicked() {
    var contextMenu = this.app.contextmenu && this.app.contextmenu.isShown();
    var locked = Service && Service.query('locked');

    if (locked || contextMenu) {
      return;
    }
    window.dispatchEvent(new CustomEvent('global-search-request'));
  };

  AppChrome.prototype.handleActionEvent = function ac_handleActionEvent(evt) {
    if (evt.detail.type === 'close') {
      this.app.kill();
    }
  };

  AppChrome.prototype.handleScrollEvent = function ac_handleScrollEvent(evt) {
    if (!this.containerElement.classList.contains('scrollable')) {
      return;
    }

    // Ideally we'd animate based on scroll position, but until we have
    // the necessary spec and implementation, we'll animate completely to
    // the expanded or collapsed state depending on whether it's at the
    // top or not.
    // XXX Open a bug since I wonder if there is scrollgrab rounding issue
    // somewhere. While panning from the bottom to the top, there is often
    // a scrollTop position of scrollTopMax - 1, which triggers the transition!
    var element = this.element;
    if (this.scrollable.scrollTop >= this.scrollable.scrollTopMax - 1) {
      element.classList.remove('maximized');
    } else {
      element.classList.add('maximized');
    }

    if (this.app.isActive()) {
      this.app.publish('titlestatechanged');
    }
  };

  AppChrome.prototype._registerEvents = function ac__registerEvents() {
    if (this.useCombinedChrome()) {
      LazyLoader.load('shared/js/bookmarks_database.js').then(() => {
        this.updateAddToHomeButton();
      }).catch((err) => {
        console.error(err);
      });
      LazyLoader.load('shared/elements/gaia_overflow_menu/script.js');

      this.stopButton.addEventListener('click', this);
      this.reloadButton.addEventListener('click', this);
      this.backButton.addEventListener('click', this);
      this.forwardButton.addEventListener('click', this);
      this.title.addEventListener('click', this);
      this.scrollable.addEventListener('scroll', this);
      this.menuButton.addEventListener('click', this);
      this.windowsButton.addEventListener('click', this);
    } else {
      this.header.addEventListener('action', this);
    }

    this.app.element.addEventListener('mozbrowserloadstart', this);
    this.app.element.addEventListener('mozbrowserloadend', this);
    this.app.element.addEventListener('mozbrowsererror', this);
    this.app.element.addEventListener('mozbrowserlocationchange', this);
    this.app.element.addEventListener('mozbrowsertitlechange', this);
    this.app.element.addEventListener('mozbrowsermetachange', this);
    this.app.element.addEventListener('mozbrowserscrollareachanged', this);
    this.app.element.addEventListener('_securitychange', this);
    this.app.element.addEventListener('_loading', this);
    this.app.element.addEventListener('_loaded', this);
    this.app.element.addEventListener('_namechanged', this);

    var element = this.element;

    var animEnd = function(evt) {
      if (evt && evt.target !== element) {
        return;
      }
      var publishEvent = this.isMaximized() ? 'expanded' : 'collapsed';
      this.app.publish('chrome' + publishEvent);
    }.bind(this);

    element.addEventListener('transitionend', animEnd);
  };

  AppChrome.prototype._unregisterEvents = function ac__unregisterEvents() {

    if (this.useCombinedChrome()) {
      this.stopButton.removeEventListener('click', this);
      this.menuButton.removeEventListener('click', this);
      this.windowsButton.removeEventListener('click', this);
      this.reloadButton.removeEventListener('click', this);
      this.backButton.removeEventListener('click', this);
      this.forwardButton.removeEventListener('click', this);
      this.title.removeEventListener('click', this);
      this.scrollable.removeEventListener('scroll', this);

      if (this.newWindowButton) {
        this.newWindowButton.removeEventListener('click', this);
      }

      if (this.addToHomeButton) {
        this.addToHomeButton.removeEventListener('click', this);
      }

      if (this.shareButton) {
        this.shareButton.removeEventListener('click', this);
      }
    } else {
      this.header.removeEventListener('action', this);
    }

    if (!this.app) {
      return;
    }

    this.app.element.removeEventListener('mozbrowserloadstart', this);
    this.app.element.removeEventListener('mozbrowserloadend', this);
    this.app.element.removeEventListener('mozbrowsererror', this);
    this.app.element.removeEventListener('mozbrowserlocationchange', this);
    this.app.element.removeEventListener('mozbrowsertitlechange', this);
    this.app.element.removeEventListener('mozbrowsermetachange', this);
    this.app.element.removeEventListener('_loading', this);
    this.app.element.removeEventListener('_loaded', this);
    this.app.element.removeEventListener('_namechanged', this);
    this.app = null;
  };

  // Name has priority over the rest
  AppChrome.prototype.handleNameChanged =
    function ac_handleNameChanged(evt) {
      if (this._fixedTitle) {
        return;
      }
      this.title.textContent = this.app.name;
      this._gotName = true;
    };

  AppChrome.prototype.setFreshTitle = function ac_setFreshTitle(title) {
    if (this.isSearchApp()) {
      return;
    }
    this.title.textContent = title;
    clearTimeout(this._titleTimeout);
    this._recentTitle = true;
    this._titleTimeout = setTimeout((function() {
      this._recentTitle = false;
    }).bind(this), this.FRESH_TITLE);
  };

  AppChrome.prototype.handleScrollAreaChanged = function(evt) {
    // Check if the page has become scrollable and add the scrollable class.
    // We don't check if a page has stopped being scrollable to avoid oddness
    // with a page oscillating between scrollable/non-scrollable states, and
    // other similar issues that Firefox for Android is still dealing with
    // today.
    if (this.containerElement.classList.contains('scrollable')) {
      return;
    }

    // We allow the bar to collapse if the page is greater than or equal to
    // the area of the window with a collapsed bar. Strictly speaking, we'd
    // allow it to collapse if it was greater than the area of the window with
    // the expanded bar, but due to prevalent use of -webkit-box-sizing and
    // plain mistakes, this causes too many false-positives.
    if (evt.detail.height >= this.containerElement.clientHeight) {
      this.containerElement.classList.add('scrollable');
    }
  };

  AppChrome.prototype.handleSecurityChanged = function(evt) {
    var sslState = this.app.getSSLState();
    this.sslIndicator.dataset.ssl = sslState;
    this.sslIndicator.classList.toggle(
      'chrome-has-ssl-indicator', sslState === 'broken' || sslState === 'secure'
    );
  };

  AppChrome.prototype.handleTitleChanged = function(evt) {
    if (this._gotName || this._fixedTitle) {
      return;
    }

    this.setFreshTitle(evt.detail || this._currentURL);
    this._titleChanged = true;
  };

  AppChrome.prototype.handleMetaChange =
    function ac__handleMetaChange(evt) {
      var detail = evt.detail;
      if (detail.name !== 'theme-color' || !detail.type) {
        return;
      }

      // If the theme-color meta is removed, let's reset the color.
      var color = '';

      // Otherwise, set it to the color that has been asked.
      if (detail.type !== 'removed') {
        color = detail.content;
      }

      this._themeChanged = true;
      this.setThemeColor(color);
    };

  AppChrome.prototype.setThemeColor = function ac_setThemColor(color) {
    // Do not set theme color for private windows
    if (this.app.isPrivateBrowser()) {
      return;
    }

    var bottomApp = this.app.getBottomMostWindow();

    if (this.app.CLASS_NAME === 'PopupWindow' &&
      bottomApp &&
      bottomApp.themeColor) {
      color = bottomApp.themeColor;
    }

    this.app.themeColor = color;
    this.element.style.backgroundColor = color;

    if (!this.app.isHomescreen) {
      this.scrollable.style.backgroundColor = color;
    }

    if (color === 'transparent' || color === '') {
      this.app.element.classList.remove('light');
      this.app.publish('titlestatechanged');
      return;
    }

    var self = this;
    var finishedFade = false;
    var endBackgroundFade = function(evt) {
      if (evt && evt.propertyName != 'background-color') {
        return;
      }
      finishedFade = true;
      if (self.element) {
        self.element.removeEventListener('transitionend', endBackgroundFade);
      }
    };
    eventSafety(this.element, 'transitionend', endBackgroundFade, 1000);

    window.requestAnimationFrame(function updateAppColor() {
      if (finishedFade || !self.element) {
        return;
      }

      var computedColor = window.getComputedStyle(self.element).backgroundColor;
      var colorCodes = /rgb\((\d+), (\d+), (\d+)\)/.exec(computedColor);
      if (!colorCodes || colorCodes.length === 0) {
        return;
      }

      var r = parseInt(colorCodes[1]);
      var g = parseInt(colorCodes[2]);
      var b = parseInt(colorCodes[3]);
      var brightness =
        Math.sqrt((r*r) * 0.241 + (g*g) * 0.691 + (b*b) * 0.068);

      var wasLight = self.app.element.classList.contains('light');
      var isLight  = brightness > 200;
      if (wasLight != isLight) {
        self.app.element.classList.toggle('light', isLight);
        self.app.publish('titlestatechanged');
      }
      window.requestAnimationFrame(updateAppColor);
    });
  };

  AppChrome.prototype.render = function() {
    this.publish('willrender');

    var view = this.useCombinedChrome() ? this.combinedView() : this.view();
    this.app.element.insertAdjacentHTML('afterbegin', view);

    this._fetchElements();
    this._registerEvents();
    this.publish('rendered');
  };

  AppChrome.prototype.useLightTheming = function ac_useLightTheming() {
    // The rear window should dictate the status bar color when the front
    // window is a popup.
    if (this.app.CLASS_NAME == 'PopupWindow' &&
        this.app.rearWindow &&
        this.app.rearWindow.appChrome) {
      return this.app.rearWindow.appChrome.useLightTheming();
    }
    // All other cases can use the front window.
    return this.app.element.classList.contains('light');
  };

  AppChrome.prototype.useCombinedChrome = function ac_useCombinedChrome(evt) {
    return this.app.config.chrome && !this.app.config.chrome.bar;
  };

  AppChrome.prototype._updateLocation =
    function ac_updateTitle(title) {
      if (this._titleChanged || this._gotName || this._recentTitle ||
          this._fixedTitle) {
        return;
      }
      this.title.textContent = title;
    };

  AppChrome.prototype.updateAddToHomeButton =
    function ac_updateAddToHomeButton() {
      if (!this.addToHomeButton || !BookmarksDatabase) {
        return;
      }

      // Enable/disable the bookmark option
      BookmarksDatabase.get(this._currentURL).then(function resolve(result) {
        this.addToHomeButton.hidden = !!result;
      }.bind(this),
      function reject() {
        this.addToHomeButton.hidden = true;
      }.bind(this));
    };

  AppChrome.prototype.handleLocationChanged =
    function ac_handleLocationChange(evt) {
      if (!this.app) {
        return;
      }

      // Check if this is just a location-change to an anchor tag.
      var anchorChange = false;
      if (this._currentURL && evt.detail) {
        anchorChange =
          this._currentURL.replace(/#.*/g, '') ===
          evt.detail.replace(/#.*/g, '');
      }

      // We wait a small while because if we get a title/name it's even better
      // and we don't want the label to flash
      setTimeout(this._updateLocation.bind(this, evt.detail),
                 this.LOCATION_COALESCE);
      this._currentURL = evt.detail;

      if (this.backButton && this.forwardButton) {
        this.app.canGoForward(function forwardSuccess(result) {
          if (!this.hasNavigation()) {
            return;
          }
          this.forwardButton.disabled = !result;
        }.bind(this));

        this.app.canGoBack(function backSuccess(result) {
          if (!this.hasNavigation()) {
            return;
          }
          this.backButton.disabled = !result;
        }.bind(this));
      }

      this.updateAddToHomeButton();

      if (!this.app.isBrowser()) {
        return;
      }

      // We havent got a name for this location
      this._gotName = false;

      if (!anchorChange) {
        // Make the rocketbar unscrollable until the page resizes to the
        // appropriate height.
        this.containerElement.classList.remove('scrollable');

        // Expand
        if (!this.isMaximized()) {
          this.element.classList.add('maximized');
        }
        this.scrollable.scrollTop = 0;
      }

      // Set the title for the private browser landing page.
      // This is explicitly placed in the locationchange handler as it's
      // currently possibly to navigate back to the landing page with the
      // back button. Otherwise it could be in the constructor.
      if (this.app.isPrivateBrowser() &&
        this.app.config.url.startsWith('app:')) {
        this._gotName = true;
        this.title.dataset.l10nId = 'search-or-enter-address';
      }
    };

  AppChrome.prototype.handleLoadStart = function ac_handleLoadStart(evt) {
    this.containerElement.classList.add('loading');
    this._titleChanged = false;
    this._themeChanged = false;
  };

  AppChrome.prototype.handleLoadEnd = function ac_handleLoadEnd(evt) {
    this.containerElement.classList.remove('loading');
    if (!this._themeChanged) {
      this.setThemeColor('');
    }
  };

  AppChrome.prototype.handleError = function ac_handleError(evt) {
    if (evt.detail && evt.detail.type === 'fatal') {
      return;
    }
    if (this.useCombinedChrome() && this.app.config.chrome.scrollable) {
      // When we get an error, keep the rocketbar maximized.
      this.element.classList.add('maximized');
      this.containerElement.classList.remove('scrollable');
    }
  };

  AppChrome.prototype.maximize = function ac_maximize(callback) {
    var element = this.element;
    element.classList.add('maximized');
    window.addEventListener('rocketbar-overlayclosed', this);

    if (!callback) {
      return;
    }
    eventSafety(element, 'transitionend', callback, 250);
  };

  AppChrome.prototype.collapse = function ac_collapse() {
    window.removeEventListener('rocketbar-overlayclosed', this);
    this.element.classList.remove('maximized');
  };

  AppChrome.prototype.isMaximized = function ac_isMaximized() {
    return this.element.classList.contains('maximized');
  };

  AppChrome.prototype.isSearch = function ac_isSearch() {
    var dataset = this.app.config;
    return dataset.searchURL && this._currentURL === dataset.searchURL;
  };

  AppChrome.prototype.isSearchApp = function() {
    return this.app.config.manifest &&
      this.app.config.manifest.role === 'search';
  };

  AppChrome.prototype.hasNavigation = function ac_hasNavigation(evt) {
    return this.app.isBrowser() ||
      (this.app.config.chrome && this.app.config.chrome.navigation);
  };

  AppChrome.prototype.addBookmark = function ac_addBookmark() {
    var dataset = this.app.config;
    var favicons = this.app.favicons;

    var name;
    if (this.isSearch()) {
      name = dataset.searchName;
    } else {
      name = this.title.textContent;
    }
    var url = this._currentURL;

    LazyLoader.load('shared/js/icons_helper.js').then(() => {
      IconsHelper.getIcon(url, null, {icons: favicons}).then(icon => {
        var activity = new MozActivity({
          name: 'save-bookmark',
          data: {
            type: 'url',
            url: url,
            name: name,
            icon: icon,
            iconable: false
          }
        });

        if (this.addToHomeButton) {
          activity.onsuccess = function onsuccess() {
            this.addToHomeButton.hidden = true;
          }.bind(this);
        }
      });
    }).catch((err) => {
      console.error(err);
    });
  };

  AppChrome.prototype.onAddBookmark = function ac_onAddBookmark() {
    var self = this;
    function selected(value) {
      if (value) {
        self.addBookmark();
      }
    }

    var title = 'add-to-home-screen';
    var options = [];

    if (this.isSearch()) {
      var dataset = this.app.config;
      options.push({
        id: 'search',
        text: {
          raw: dataset.searchName
        }
      });
    } else {
      options.push({
        id: 'origin',
        text: {
          raw: this.title.textContent
        }
      });
    }

    ModalDialog.selectOne(title, options, selected);
  };

  AppChrome.prototype.showWindows = function ac_showWindows() {
    window.dispatchEvent(
      new CustomEvent('taskmanagershow',
                      { detail: { filter: 'browser-only' }})
    );
  };

  AppChrome.prototype.__defineGetter__('overflowMenu',
    // Instantiate the overflow menu when it's needed
    function ac_getOverflowMenu() {
      if (!this._overflowMenu && this.useCombinedChrome() &&
          window.GaiaOverflowMenu) {
        this.app.element.insertAdjacentHTML('afterbegin',
                                            this.overflowMenuView());
        this._overflowMenu = this.containerElement.
          querySelector('gaia-overflow-menu');
        this.newWindowButton = this._overflowMenu.
          querySelector('#new-window');
        this.newPrivateWinButton = this._overflowMenu.
          querySelector('#new-private-window');
        this.addToHomeButton = this._overflowMenu.
          querySelector('#add-to-home');
        this.shareButton = this._overflowMenu.
          querySelector('#share');

        this.newWindowButton.addEventListener('click', this);
        this.newPrivateWinButton.addEventListener('click', this);
        this.addToHomeButton.addEventListener('click', this);
        this.shareButton.addEventListener('click', this);

        this.updateAddToHomeButton();
      }

      return this._overflowMenu;
    });

  AppChrome.prototype.showOverflowMenu = function ac_showOverflowMenu() {
    this.overflowMenu.show();
  };

  AppChrome.prototype.hideOverflowMenu = function ac_hideOverflowMenu() {
    this.overflowMenu.hide();
  };

  /* Bug 1054466 switched the browser overflow menu to use the system style,
   * but we eventually want to switch back to the new style. We can do that
   * by removing this function.
   */
  AppChrome.prototype.showOverflowMenu = function ac_showOverflowMenu() {
    if (this.app.contextmenu) {
      var name = this.isSearch() ?
        this.app.config.searchName : this.title.textContent;
      this.app.contextmenu.showDefaultMenu(newTabManifestURL, name);
    }
  };

  exports.AppChrome = AppChrome;
}(window));