• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * @fileoverview A collection of utility methods for UberPage and its contained
7 *     pages.
8 */
9
10cr.define('uber', function() {
11
12  /**
13   * Fixed position header elements on the page to be shifted by handleScroll.
14   * @type {NodeList}
15   */
16  var headerElements;
17
18  /**
19   * This should be called by uber content pages when DOM content has loaded.
20   */
21  function onContentFrameLoaded() {
22    headerElements = document.getElementsByTagName('header');
23    document.addEventListener('scroll', handleScroll);
24
25    invokeMethodOnParent('ready');
26
27    // Prevent the navigation from being stuck in a disabled state when a
28    // content page is reloaded while an overlay is visible (crbug.com/246939).
29    invokeMethodOnParent('stopInterceptingEvents');
30
31    // Trigger the scroll handler to tell the navigation if our page started
32    // with some scroll (happens when you use tab restore).
33    handleScroll();
34
35    window.addEventListener('message', handleWindowMessage);
36  }
37
38  /**
39   * Handles scroll events on the document. This adjusts the position of all
40   * headers and updates the parent frame when the page is scrolled.
41   * @private
42   */
43  function handleScroll() {
44    var scrollLeft = scrollLeftForDocument(document);
45    var offset = scrollLeft * -1;
46    for (var i = 0; i < headerElements.length; i++) {
47      // As a workaround for http://crbug.com/231830, set the transform to
48      // 'none' rather than 0px.
49      headerElements[i].style.webkitTransform = offset ?
50          'translateX(' + offset + 'px)' : 'none';
51    }
52
53    invokeMethodOnParent('adjustToScroll', scrollLeft);
54  };
55
56  /**
57   * Handles 'message' events on window.
58   * @param {Event} e The message event.
59   */
60  function handleWindowMessage(e) {
61    if (e.data.method === 'frameSelected')
62      handleFrameSelected();
63    else if (e.data.method === 'mouseWheel')
64      handleMouseWheel(e.data.params);
65    else if (e.data.method === 'popState')
66      handlePopState(e.data.params.state, e.data.params.path);
67  }
68
69  /**
70   * This is called when a user selects this frame via the navigation bar
71   * frame (and is triggered via postMessage() from the uber page).
72   * @private
73   */
74  function handleFrameSelected() {
75    setScrollTopForDocument(document, 0);
76  }
77
78  /**
79   * Called when a user mouse wheels (or trackpad scrolls) over the nav frame.
80   * The wheel event is forwarded here and we scroll the body.
81   * There's no way to figure out the actual scroll amount for a given delta.
82   * It differs for every platform and even initWebKitWheelEvent takes a
83   * pixel amount instead of a wheel delta. So we just choose something
84   * reasonable and hope no one notices the difference.
85   * @param {Object} params A structure that holds wheel deltas in X and Y.
86   */
87  function handleMouseWheel(params) {
88    window.scrollBy(-params.deltaX * 49 / 120, -params.deltaY * 49 / 120);
89  }
90
91  /**
92   * Called when the parent window restores some state saved by uber.pushState
93   * or uber.replaceState. Simulates a popstate event.
94   */
95  function handlePopState(state, path) {
96    history.replaceState(state, '', path);
97    window.dispatchEvent(new PopStateEvent('popstate', {state: state}));
98  }
99
100  /**
101   * @return {boolean} Whether this frame has a parent.
102   */
103  function hasParent() {
104    return window != window.parent;
105  }
106
107  /**
108   * Invokes a method on the parent window (UberPage). This is a convenience
109   * method for API calls into the uber page.
110   * @param {string} method The name of the method to invoke.
111   * @param {Object=} opt_params Optional property bag of parameters to pass to
112   *     the invoked method.
113   * @private
114   */
115  function invokeMethodOnParent(method, opt_params) {
116    if (!hasParent())
117      return;
118
119    invokeMethodOnWindow(window.parent, method, opt_params, 'chrome://chrome');
120  }
121
122  /**
123   * Invokes a method on the target window.
124   * @param {string} method The name of the method to invoke.
125   * @param {Object=} opt_params Optional property bag of parameters to pass to
126   *     the invoked method.
127   * @param {string=} opt_url The origin of the target window.
128   * @private
129   */
130  function invokeMethodOnWindow(targetWindow, method, opt_params, opt_url) {
131    var data = {method: method, params: opt_params};
132    targetWindow.postMessage(data, opt_url ? opt_url : '*');
133  }
134
135  /**
136   * Updates the page's history state. If the page is embedded in a child,
137   * forward the information to the parent for it to manage history for us. This
138   * is a replacement of history.replaceState and history.pushState.
139   * @param {Object} state A state object for replaceState and pushState.
140   * @param {string} title The title of the page to replace.
141   * @param {string} path The path the page navigated to.
142   * @param {boolean} replace If true, navigate with replacement.
143   * @private
144   */
145  function updateHistory(state, path, replace) {
146    var historyFunction = replace ?
147        window.history.replaceState :
148        window.history.pushState;
149
150    if (hasParent()) {
151      // If there's a parent, always replaceState. The parent will do the actual
152      // pushState.
153      historyFunction = window.history.replaceState;
154      invokeMethodOnParent('updateHistory', {
155        state: state, path: path, replace: replace});
156    }
157    historyFunction.call(window.history, state, '', '/' + path);
158  }
159
160  /**
161   * Sets the current title for the page. If the page is embedded in a child,
162   * forward the information to the parent. This is a replacement for setting
163   * document.title.
164   * @param {string} title The new title for the page.
165   */
166  function setTitle(title) {
167    document.title = title;
168    invokeMethodOnParent('setTitle', {title: title});
169  }
170
171  /**
172   * Pushes new history state for the page. If the page is embedded in a child,
173   * forward the information to the parent; when embedded, all history entries
174   * are attached to the parent. This is a replacement of history.pushState.
175   * @param {Object} state A state object for replaceState and pushState.
176   * @param {string} path The path the page navigated to.
177   */
178  function pushState(state, path) {
179    updateHistory(state, path, false);
180  }
181
182  /**
183   * Replaces the page's history state. If the page is embedded in a child,
184   * forward the information to the parent; when embedded, all history entries
185   * are attached to the parent. This is a replacement of history.replaceState.
186   * @param {Object} state A state object for replaceState and pushState.
187   * @param {string} path The path the page navigated to.
188   */
189  function replaceState(state, path) {
190    updateHistory(state, path, true);
191  }
192
193  return {
194    invokeMethodOnParent: invokeMethodOnParent,
195    invokeMethodOnWindow: invokeMethodOnWindow,
196    onContentFrameLoaded: onContentFrameLoaded,
197    pushState: pushState,
198    replaceState: replaceState,
199    setTitle: setTitle,
200  };
201});
202