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)  /**
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * A queue of method invocations on one of the iframes; if the iframe has not
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * loaded by the time there is a method to invoke, delay the invocation until
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * it is ready.
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @type {Object}
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @private
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   */
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  var queuedInvokes = {};
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /**
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Handles page initialization.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function onLoad(e) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame = $('navigation');
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.dataset.width = navFrame.offsetWidth;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Select a page based on the page-URL.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var params = resolvePageInfo();
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path);
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window.addEventListener('message', handleWindowMessage);
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window.setTimeout(function() {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      document.documentElement.classList.remove('loading');
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, 0);
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // HACK(dbeam): This makes the assumption that any second part to a path
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // will result in needing background navigation. We shortcut it to avoid
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // flicker on load.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // HACK(csilv): Search URLs aren't overlays, special case them.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (params.id == 'settings' && params.path &&
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.path.indexOf('search') != 0) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      backgroundNavigation();
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
56a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
57a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    ensureNonSelectedFrameContainersAreHidden();
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Find page information from window.location. If the location doesn't
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * point to one of our pages, return default parameters.
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {Object} An object containing the following parameters:
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     id - The 'id' of the page.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     path - A path into the page, including search and hash. Optional.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function resolvePageInfo() {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var params = {};
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var path = window.location.pathname;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (path.length > 1) {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Split the path into id and the remaining path.
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = path.slice(1);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var index = path.indexOf('/');
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index != -1) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.id = path.slice(0, index);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.path = path.slice(index + 1);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.id = path;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var container = $(params.id);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (container) {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The id is valid. Add the hash and search parts of the URL to path.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.path = (params.path || '') + window.location.search +
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            window.location.hash;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The target sub-page does not exist, discard the params we generated.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.id = undefined;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params.path = undefined;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we don't have a valid page, get a default.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!params.id)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      params.id = getDefaultIframe().id;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return params;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Handler for window.onpopstate.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Event} e The history event.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function onPopHistoryState(e) {
10446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // Use the URL to determine which page to route to.
10546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    var params = resolvePageInfo();
10646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
10746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // If the page isn't the current page, load it fresh. Even if the page is
10846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // already loaded, it may have state not reflected in the URL, such as the
10946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // history page's "Remove selected items" overlay. http://crbug.com/377386
11046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    if (getRequiredElement(params.id) !== getSelectedIframe())
11146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path);
11246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
11346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // Either way, send the state down to it.
11446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    //
11546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // Note: This assumes that the state and path parameters for every page
11646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // under this origin are compatible. All of the downstream pages which
11746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    // navigate use pushState and replaceState.
11846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    invokeMethodOnPage(params.id, 'popState',
119f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                       {state: e.state, path: '/' + params.path});
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {Object} The default iframe container.
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getDefaultIframe() {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $(loadTimeData.getString('helpHost'));
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @return {Object} The currently selected iframe container.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getSelectedIframe() {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return document.querySelector('.iframe-container.selected');
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Handles postMessage calls from the iframes of the contained pages.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The pages request functionality from this object by passing an object of
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * the following form:
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *  { method : "methodToInvoke",
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *    params : {...}
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *  }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * |method| is required, while |params| is optional. Extra parameters required
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * by a method must be specified by that method's documentation.
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Event} e The posted object.
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function handleWindowMessage(e) {
1526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    e = /** @type{!MessageEvent.<!{method: string, params: *}>} */(e);
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (e.data.method === 'beginInterceptingEvents') {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      backgroundNavigation();
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else if (e.data.method === 'stopInterceptingEvents') {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      foregroundNavigation();
157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    } else if (e.data.method === 'ready') {
158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      pageReady(e.origin);
159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    } else if (e.data.method === 'updateHistory') {
160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      updateHistory(e.origin, e.data.params.state, e.data.params.path,
161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                    e.data.params.replace);
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else if (e.data.method === 'setTitle') {
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setTitle(e.origin, e.data.params.title);
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else if (e.data.method === 'showPage') {
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      showPage(e.data.params.pageId,
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               HISTORY_STATE_OPTION.PUSH,
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               e.data.params.path);
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else if (e.data.method === 'navigationControlsLoaded') {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      onNavigationControlsLoaded();
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else if (e.data.method === 'adjustToScroll') {
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      adjustToScroll(/** @type {number} */(e.data.params));
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else if (e.data.method === 'mouseWheel') {
1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      forwardMouseWheel(/** @type {Object} */(e.data.params));
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      console.error('Received unexpected message', e.data);
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Sends the navigation iframe to the background.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function backgroundNavigation() {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.classList.add('background');
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.firstChild.tabIndex = -1;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.firstChild.setAttribute('aria-hidden', true);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Retrieves the navigation iframe from the background.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function foregroundNavigation() {
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.classList.remove('background');
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.firstChild.tabIndex = 0;
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.firstChild.removeAttribute('aria-hidden');
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Enables or disables animated transitions when changing content while
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * horizontally scrolled.
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {boolean} enabled True if enabled, else false to disable.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function setContentChanging(enabled) {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    navFrame.classList[enabled ? 'add' : 'remove']('changing-content');
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (isRTL()) {
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow,
2076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                'setContentChanging', enabled);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Get an iframe based on the origin of a received post message.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} origin The origin of a post message.
2146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)   * @return {!Element} The frame associated to |origin| or null.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function getIframeFromOrigin(origin) {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(origin.substr(-1) != '/', 'invalid origin given');
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var query = '.iframe-container > iframe[src^="' + origin + '/"]';
2196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    var element = document.querySelector(query);
2206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    assert(element);
2216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return /** @type {!Element} */(element);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Changes the path past the page title (i.e. chrome://chrome/settings/(.*)).
226cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {Object} state The page's state object for the navigation.
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} path The new /path/ to be set after the page name.
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {number} historyOption The type of history modification to make.
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  function changePathTo(state, path, historyOption) {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(!path || path.substr(-1) != '/', 'invalid path given');
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var histFunc;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (historyOption == HISTORY_STATE_OPTION.PUSH)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      histFunc = window.history.pushState;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (historyOption == HISTORY_STATE_OPTION.REPLACE)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      histFunc = window.history.replaceState;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(histFunc, 'invalid historyOption given ' + historyOption);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var pageId = getSelectedIframe().id;
24246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    var args = [state, '', '/' + pageId + '/' + (path || '')];
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    histFunc.apply(window.history, args);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * Adds or replaces the current history entry based on a navigation from the
248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * source iframe.
249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {string} origin The origin of the source iframe.
250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {Object} state The source iframe's state object.
251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {string} path The new "path" (e.g. "/createProfile").
252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {boolean} replace Whether to replace the current history entry.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  function updateHistory(origin, state, path, replace) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(!path || path[0] != '/', 'invalid path sent from ' + origin);
256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var historyOption =
257cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        replace ? HISTORY_STATE_OPTION.REPLACE : HISTORY_STATE_OPTION.PUSH;
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only update the currently displayed path if this is the visible frame.
259cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var container = getIframeFromOrigin(origin).parentNode;
260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (container == getSelectedIframe())
261cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      changePathTo(state, path, historyOption);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Sets the title of the page.
266cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {string} origin The origin of the source iframe.
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {string} title The title of the page.
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function setTitle(origin, title) {
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Cache the title for the client iframe, i.e., the iframe setting the
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // title. querySelector returns the actual iframe element, so use parentNode
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to get back to the container.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var container = getIframeFromOrigin(origin).parentNode;
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.dataset.title = title;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only update the currently displayed title if this is the visible frame.
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (container == getSelectedIframe())
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      document.title = title;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
282cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * Invokes a method on a subpage. If the subpage has not signaled readiness,
283cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * queue the message for when it does.
284cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {string} pageId Should match an id of one of the iframe containers.
285cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {string} method The name of the method to invoke.
286cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {Object=} opt_params Optional property page of parameters to pass to
287cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   *     the invoked method.
288cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   */
289cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  function invokeMethodOnPage(pageId, method, opt_params) {
290cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var frame = $(pageId).querySelector('iframe');
291cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (!frame || !frame.dataset.ready) {
292cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      queuedInvokes[pageId] = (queuedInvokes[pageId] || []);
293cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      queuedInvokes[pageId].push([method, opt_params]);
294cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    } else {
295cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      uber.invokeMethodOnWindow(frame.contentWindow, method, opt_params);
296cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
297cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
298cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
299cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /**
300cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * Called in response to a page declaring readiness. Calls any deferred method
301cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * invocations from invokeMethodOnPage.
302cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * @param {string} origin The origin of the source iframe.
303cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   */
304cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  function pageReady(origin) {
305cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var frame = getIframeFromOrigin(origin);
306cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var container = frame.parentNode;
307cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    frame.dataset.ready = true;
308cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var queue = queuedInvokes[container.id] || [];
309cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    queuedInvokes[container.id] = undefined;
310cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    for (var i = 0; i < queue.length; i++) {
311cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      uber.invokeMethodOnWindow(frame.contentWindow, queue[i][0], queue[i][1]);
312cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
313cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
314cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
315cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  /**
316cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   * Selects and navigates a subpage. This is called from uber-frame.
317effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   * @param {string} pageId Should match an id of one of the iframe containers.
3186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)   * @param {number} historyOption Indicates whether we should push or replace
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   *     browser history.
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * @param {string} path A sub-page path.
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function showPage(pageId, historyOption, path) {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var container = $(pageId);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Lazy load of iframe contents.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var sourceUrl = container.dataset.url + (path || '');
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var frame = container.querySelector('iframe');
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!frame) {
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      frame = container.ownerDocument.createElement('iframe');
330effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      frame.name = pageId;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      container.appendChild(frame);
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      frame.src = sourceUrl;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // There's no particularly good way to know what the current URL of the
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // content frame is as we don't have access to its contentWindow's
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // location, so just replace every time until necessary to do otherwise.
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      frame.contentWindow.location.replace(sourceUrl);
338cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      frame.dataset.ready = false;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the last selected container is already showing, ignore the rest.
342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    var lastSelected = document.querySelector('.iframe-container.selected');
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (lastSelected === container)
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
346a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (lastSelected) {
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lastSelected.classList.remove('selected');
348a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      // Setting aria-hidden hides the container from assistive technology
349a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      // immediately. The 'hidden' attribute is set after the transition
350a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      // finishes - that ensures it's not possible to accidentally focus
351a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      // an element in an unselected container.
352a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      lastSelected.setAttribute('aria-hidden', 'true');
353a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
354a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
355a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // Containers that aren't selected have to be hidden so that their
356a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // content isn't focusable.
357a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    container.hidden = false;
358a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    container.setAttribute('aria-hidden', 'false');
359a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
360a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // Trigger a layout after making it visible and before setting
361a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    // the class to 'selected', so that it animates in.
3621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    /** @suppress {uselessCode} */
363a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    container.offsetTop;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    container.classList.add('selected');
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    setContentChanging(true);
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    adjustToScroll(0);
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var selectedFrame = getSelectedIframe().querySelector('iframe');
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uber.invokeMethodOnWindow(selectedFrame.contentWindow, 'frameSelected');
37134680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)    selectedFrame.contentWindow.focus();
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (historyOption != HISTORY_STATE_OPTION.NONE)
374cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      changePathTo({}, path, historyOption);
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (container.dataset.title)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      document.title = container.dataset.title;
3786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    assert('favicon' in container.dataset);
3796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    var dataset = /** @type {{favicon: string}} */(container.dataset);
3816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    $('favicon').href = 'chrome://theme/' + dataset.favicon;
3826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    $('favicon2x').href = 'chrome://theme/' + dataset.favicon + '@2x';
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateNavigationControls();
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function onNavigationControlsLoaded() {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updateNavigationControls();
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Sends a message to uber-frame to update the appearance of the nav controls.
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * It should be called whenever the selected iframe changes.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function updateNavigationControls() {
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var iframe = getSelectedIframe();
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              'changeSelection', {pageId: iframe.id});
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Forwarded scroll offset from a content frame's scroll handler.
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {number} scrollOffset The scroll offset from the content frame.
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function adjustToScroll(scrollOffset) {
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // NOTE: The scroll is reset to 0 and easing turned on every time a user
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // switches frames. If we receive a non-zero value it has to have come from
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // a real user scroll, so we disable easing when this happens.
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (scrollOffset != 0)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setContentChanging(false);
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (isRTL()) {
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow,
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                'adjustToScroll',
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                scrollOffset);
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var navWidth = Math.max(0, +navFrame.dataset.width + scrollOffset);
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      navFrame.style.width = navWidth + 'px';
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      navFrame.style.webkitTransform = 'translateX(' + -scrollOffset + 'px)';
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * Forward scroll wheel events to subpages.
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @param {Object} params Relevant parameters of wheel event.
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function forwardMouseWheel(params) {
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var iframe = getSelectedIframe().querySelector('iframe');
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uber.invokeMethodOnWindow(iframe.contentWindow, 'mouseWheel', params);
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
432a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  /**
433a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)   * Make sure that iframe containers that are not selected are
434a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)   * hidden, so that elements in those frames aren't part of the
435a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)   * focus order. Containers that are unselected later get hidden
436a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)   * when the transition ends. We also set the aria-hidden attribute
437a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)   * because that hides the container from assistive technology
438a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)   * immediately, rather than only after the transition ends.
439a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)   */
440a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  function ensureNonSelectedFrameContainersAreHidden() {
441a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    var containers = document.querySelectorAll('.iframe-container');
442a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    for (var i = 0; i < containers.length; i++) {
443a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      var container = containers[i];
444a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      if (!container.classList.contains('selected')) {
445a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        container.hidden = true;
446a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        container.setAttribute('aria-hidden', 'true');
447a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      }
448a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      container.addEventListener('webkitTransitionEnd', function(event) {
449a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        if (!event.target.classList.contains('selected'))
450a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          event.target.hidden = true;
451a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      });
452a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
453a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
454a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onLoad: onLoad,
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onPopHistoryState: onPopHistoryState
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)});
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)window.addEventListener('popstate', uber.onPopHistoryState);
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)document.addEventListener('DOMContentLoaded', uber.onLoad);
463