uber.js revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cr.define('uber', function() {
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Options for how web history should be handled.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var HISTORY_STATE_OPTION = {
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PUSH: 1,    // Push a new history state.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    REPLACE: 2, // Replace the current history state.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NONE: 3,    // Ignore this history state change.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * We cache a reference to the #navigation frame here so we don't need to grab
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * it from the DOM on each scroll.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @type {Node}
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @private
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var navFrame;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Handles page initialization.
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function onLoad(e) {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame = $('navigation');
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.dataset.width = navFrame.offsetWidth;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Select a page based on the page-URL.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var params = resolvePageInfo();
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path);
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window.addEventListener('message', handleWindowMessage);
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window.setTimeout(function() {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      document.documentElement.classList.remove('loading');
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, 0);
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // HACK(dbeam): This makes the assumption that any second part to a path
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // will result in needing background navigation. We shortcut it to avoid
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // flicker on load.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // HACK(csilv): Search URLs aren't overlays, special case them.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (params.id == 'settings' && params.path &&
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.path.indexOf('search') != 0) {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      backgroundNavigation();
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Find page information from window.location. If the location doesn't
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * point to one of our pages, return default parameters.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {Object} An object containing the following parameters:
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     id - The 'id' of the page.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     path - A path into the page, including search and hash. Optional.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function resolvePageInfo() {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var params = {};
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var path = window.location.pathname;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (path.length > 1) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Split the path into id and the remaining path.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = path.slice(1);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = path.indexOf('/');
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index != -1) {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.id = path.slice(0, index);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.path = path.slice(index + 1);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.id = path;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var container = $(params.id);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (container) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The id is valid. Add the hash and search parts of the URL to path.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.path = (params.path || '') + window.location.search +
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            window.location.hash;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The target sub-page does not exist, discard the params we generated.
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.id = undefined;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.path = undefined;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we don't have a valid page, get a default.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!params.id)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      params.id = getDefaultIframe().id;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return params;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Handler for window.onpopstate.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Event} e The history event.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function onPopHistoryState(e) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (e.state && e.state.pageId)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      showPage(e.state.pageId, HISTORY_STATE_OPTION.NONE);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {Object} The default iframe container.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getDefaultIframe() {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $(loadTimeData.getString('helpHost'));
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {Object} The currently selected iframe container.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getSelectedIframe() {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return document.querySelector('.iframe-container.selected');
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Handles postMessage calls from the iframes of the contained pages.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The pages request functionality from this object by passing an object of
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * the following form:
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *  { method : "methodToInvoke",
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *    params : {...}
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *  }
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * |method| is required, while |params| is optional. Extra parameters required
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * by a method must be specified by that method's documentation.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Event} e The posted object.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function handleWindowMessage(e) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (e.data.method === 'beginInterceptingEvents')
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      backgroundNavigation();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (e.data.method === 'stopInterceptingEvents')
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      foregroundNavigation();
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (e.data.method === 'setPath')
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setPath(e.origin, e.data.params.path);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (e.data.method === 'setTitle')
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setTitle(e.origin, e.data.params.title);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (e.data.method === 'showPage')
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      showPage(e.data.params.pageId, HISTORY_STATE_OPTION.PUSH);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (e.data.method === 'navigationControlsLoaded')
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      onNavigationControlsLoaded();
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (e.data.method === 'adjustToScroll')
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      adjustToScroll(e.data.params);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (e.data.method === 'mouseWheel')
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      forwardMouseWheel(e.data.params);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      console.error('Received unexpected message', e.data);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Sends the navigation iframe to the background.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function backgroundNavigation() {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.classList.add('background');
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.firstChild.tabIndex = -1;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.firstChild.setAttribute('aria-hidden', true);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Retrieves the navigation iframe from the background.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function foregroundNavigation() {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.classList.remove('background');
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.firstChild.tabIndex = 0;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.firstChild.removeAttribute('aria-hidden');
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Enables or disables animated transitions when changing content while
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * horizontally scrolled.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {boolean} enabled True if enabled, else false to disable.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function setContentChanging(enabled) {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.classList[enabled ? 'add' : 'remove']('changing-content');
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (isRTL()) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow,
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                'setContentChanging',
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                enabled);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Get an iframe based on the origin of a received post message.
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} origin The origin of a post message.
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {!HTMLElement} The frame associated to |origin| or null.
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getIframeFromOrigin(origin) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(origin.substr(-1) != '/', 'invalid origin given');
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var query = '.iframe-container > iframe[src^="' + origin + '/"]';
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return document.querySelector(query);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Changes the path past the page title (i.e. chrome://chrome/settings/(.*)).
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} path The new /path/ to be set after the page name.
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {number} historyOption The type of history modification to make.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function changePathTo(path, historyOption) {
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(!path || path.substr(-1) != '/', 'invalid path given');
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var histFunc;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (historyOption == HISTORY_STATE_OPTION.PUSH)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      histFunc = window.history.pushState;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (historyOption == HISTORY_STATE_OPTION.REPLACE)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      histFunc = window.history.replaceState;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(histFunc, 'invalid historyOption given ' + historyOption);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var pageId = getSelectedIframe().id;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var args = [{pageId: pageId}, '', '/' + pageId + '/' + (path || '')];
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    histFunc.apply(window.history, args);
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Sets the "path" of the page (actually the path after the first '/' char).
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Object} origin The origin of the source iframe.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} title The new "path".
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function setPath(origin, path) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(!path || path[0] != '/', 'invalid path sent from ' + origin);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only update the currently displayed path if this is the visible frame.
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (getIframeFromOrigin(origin).parentNode == getSelectedIframe())
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      changePathTo(path, HISTORY_STATE_OPTION.REPLACE);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Sets the title of the page.
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Object} origin The origin of the source iframe.
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} title The title of the page.
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function setTitle(origin, title) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Cache the title for the client iframe, i.e., the iframe setting the
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // title. querySelector returns the actual iframe element, so use parentNode
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to get back to the container.
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var container = getIframeFromOrigin(origin).parentNode;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.dataset.title = title;
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only update the currently displayed title if this is the visible frame.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (container == getSelectedIframe())
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      document.title = title;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Selects a subpage. This is called from uber-frame.
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * @param {string} pageId Should matche an id of one of the iframe containers.
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {integer} historyOption Indicates whether we should push or replace
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     browser history.
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * @param {string} path A sub-page path.
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function showPage(pageId, historyOption, path) {
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var container = $(pageId);
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var lastSelected = document.querySelector('.iframe-container.selected');
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Lazy load of iframe contents.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var sourceUrl = container.dataset.url + (path || '');
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var frame = container.querySelector('iframe');
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!frame) {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      frame = container.ownerDocument.createElement('iframe');
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      container.appendChild(frame);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      frame.src = sourceUrl;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // There's no particularly good way to know what the current URL of the
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // content frame is as we don't have access to its contentWindow's
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // location, so just replace every time until necessary to do otherwise.
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      frame.contentWindow.location.replace(sourceUrl);
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the last selected container is already showing, ignore the rest.
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (lastSelected === container)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (lastSelected)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lastSelected.classList.remove('selected');
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.classList.add('selected');
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    setContentChanging(true);
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    adjustToScroll(0);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var selectedFrame = getSelectedIframe().querySelector('iframe');
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uber.invokeMethodOnWindow(selectedFrame.contentWindow, 'frameSelected');
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (historyOption != HISTORY_STATE_OPTION.NONE)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      changePathTo(path, historyOption);
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (container.dataset.title)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      document.title = container.dataset.title;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $('favicon').href = 'chrome://theme/' + container.dataset.favicon;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $('favicon2x').href = 'chrome://theme/' + container.dataset.favicon + '@2x';
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateNavigationControls();
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function onNavigationControlsLoaded() {
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateNavigationControls();
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Sends a message to uber-frame to update the appearance of the nav controls.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * It should be called whenever the selected iframe changes.
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function updateNavigationControls() {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var iframe = getSelectedIframe();
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow,
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              'changeSelection', {pageId: iframe.id});
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Forwarded scroll offset from a content frame's scroll handler.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {number} scrollOffset The scroll offset from the content frame.
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function adjustToScroll(scrollOffset) {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // NOTE: The scroll is reset to 0 and easing turned on every time a user
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // switches frames. If we receive a non-zero value it has to have come from
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // a real user scroll, so we disable easing when this happens.
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (scrollOffset != 0)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setContentChanging(false);
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (isRTL()) {
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow,
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                'adjustToScroll',
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                scrollOffset);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var navWidth = Math.max(0, +navFrame.dataset.width + scrollOffset);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      navFrame.style.width = navWidth + 'px';
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      navFrame.style.webkitTransform = 'translateX(' + -scrollOffset + 'px)';
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Forward scroll wheel events to subpages.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Object} params Relevant parameters of wheel event.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function forwardMouseWheel(params) {
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var iframe = getSelectedIframe().querySelector('iframe');
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uber.invokeMethodOnWindow(iframe.contentWindow, 'mouseWheel', params);
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onLoad: onLoad,
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onPopHistoryState: onPopHistoryState
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)});
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)window.addEventListener('popstate', uber.onPopHistoryState);
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)document.addEventListener('DOMContentLoaded', uber.onLoad);
344