Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / uber / uber_utils.js
blob9964321b1519e6f6003cb43cfd7e39f572d8a428
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.
5 /**
6  * @fileoverview A collection of utility methods for UberPage and its contained
7  *     pages.
8  */
10 cr.define('uber', function() {
11   /**
12    * Fixed position header elements on the page to be shifted by handleScroll.
13    * @type {NodeList}
14    */
15   var headerElements;
17   /**
18    * This should be called by uber content pages when DOM content has loaded.
19    */
20   function onContentFrameLoaded() {
21     headerElements = document.getElementsByTagName('header');
22     document.addEventListener('scroll', handleScroll);
23     document.addEventListener('mousedown', handleMouseDownInFrame, true);
25     invokeMethodOnParent('ready');
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');
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();
35     window.addEventListener('message', handleWindowMessage);
36   }
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    */
42   function handleScroll() {
43     var scrollLeft = scrollLeftForDocument(document);
44     var offset = scrollLeft * -1;
45     for (var i = 0; i < headerElements.length; i++) {
46       // As a workaround for http://crbug.com/231830, set the transform to
47       // 'none' rather than 0px.
48       headerElements[i].style.webkitTransform = offset ?
49           'translateX(' + offset + 'px)' : 'none';
50     }
52     invokeMethodOnParent('adjustToScroll', scrollLeft);
53   }
55   /**
56    * Tells the parent to focus the current frame if the mouse goes down in the
57    * current frame (and it doesn't already have focus).
58    * @param {Event} e A mousedown event.
59    */
60   function handleMouseDownInFrame(e) {
61     if (!e.isSynthetic && !document.hasFocus())
62       window.focus();
63   }
65   /**
66    * Handles 'message' events on window.
67    * @param {Event} e The message event.
68    */
69   function handleWindowMessage(e) {
70     e = /** @type {!MessageEvent<!{method: string, params: *}>} */(e);
71     if (e.data.method === 'frameSelected') {
72       handleFrameSelected();
73     } else if (e.data.method === 'mouseWheel') {
74       handleMouseWheel(
75           /** @type {{deltaX: number, deltaY: number}} */(e.data.params));
76     } else if (e.data.method === 'mouseDown') {
77       handleMouseDown();
78     } else if (e.data.method === 'popState') {
79       handlePopState(e.data.params.state, e.data.params.path);
80     }
81   }
83   /**
84    * This is called when a user selects this frame via the navigation bar
85    * frame (and is triggered via postMessage() from the uber page).
86    */
87   function handleFrameSelected() {
88     setScrollTopForDocument(document, 0);
89   }
91   /**
92    * Called when a user mouse wheels (or trackpad scrolls) over the nav frame.
93    * The wheel event is forwarded here and we scroll the body.
94    * There's no way to figure out the actual scroll amount for a given delta.
95    * It differs for every platform and even initWebKitWheelEvent takes a
96    * pixel amount instead of a wheel delta. So we just choose something
97    * reasonable and hope no one notices the difference.
98    * @param {{deltaX: number, deltaY: number}} params A structure that holds
99    *     wheel deltas in X and Y.
100    */
101   function handleMouseWheel(params) {
102     window.scrollBy(-params.deltaX * 49 / 120, -params.deltaY * 49 / 120);
103   }
105   /**
106    * Fire a synthetic mousedown on the body to dismiss transient things like
107    * bubbles or menus that listen for mouse presses outside of their UI. We
108    * dispatch a fake mousedown rather than a 'mousepressedinnavframe' so that
109    * settings/history/extensions don't need to know about their embedder.
110    */
111   function handleMouseDown() {
112     var mouseEvent = new MouseEvent('mousedown');
113     mouseEvent.isSynthetic = true;
114     document.dispatchEvent(mouseEvent);
115   }
117   /**
118    * Called when the parent window restores some state saved by uber.pushState
119    * or uber.replaceState. Simulates a popstate event.
120    * @param {PopStateEvent} state A state object for replaceState and pushState.
121    * @param {string} path The path the page navigated to.
122    * @suppress {checkTypes}
123    */
124   function handlePopState(state, path) {
125     window.history.replaceState(state, '', path);
126     window.dispatchEvent(new PopStateEvent('popstate', {state: state}));
127   }
129   /**
130    * @return {boolean} Whether this frame has a parent.
131    */
132   function hasParent() {
133     return window != window.parent;
134   }
136   /**
137    * Invokes a method on the parent window (UberPage). This is a convenience
138    * method for API calls into the uber page.
139    * @param {string} method The name of the method to invoke.
140    * @param {?=} opt_params Optional property bag of parameters to pass to the
141    *     invoked method.
142    */
143   function invokeMethodOnParent(method, opt_params) {
144     if (!hasParent())
145       return;
147     invokeMethodOnWindow(window.parent, method, opt_params, 'chrome://chrome');
148   }
150   /**
151    * Invokes a method on the target window.
152    * @param {string} method The name of the method to invoke.
153    * @param {?=} opt_params Optional property bag of parameters to pass to the
154    *     invoked method.
155    * @param {string=} opt_url The origin of the target window.
156    */
157   function invokeMethodOnWindow(targetWindow, method, opt_params, opt_url) {
158     var data = {method: method, params: opt_params};
159     targetWindow.postMessage(data, opt_url ? opt_url : '*');
160   }
162   /**
163    * Updates the page's history state. If the page is embedded in a child,
164    * forward the information to the parent for it to manage history for us. This
165    * is a replacement of history.replaceState and history.pushState.
166    * @param {Object} state A state object for replaceState and pushState.
167    * @param {string} path The path the page navigated to.
168    * @param {boolean} replace If true, navigate with replacement.
169    */
170   function updateHistory(state, path, replace) {
171     var historyFunction = replace ?
172         window.history.replaceState :
173         window.history.pushState;
175     if (hasParent()) {
176       // If there's a parent, always replaceState. The parent will do the actual
177       // pushState.
178       historyFunction = window.history.replaceState;
179       invokeMethodOnParent('updateHistory', {
180         state: state, path: path, replace: replace});
181     }
182     historyFunction.call(window.history, state, '', '/' + path);
183   }
185   /**
186    * Sets the current title for the page. If the page is embedded in a child,
187    * forward the information to the parent. This is a replacement for setting
188    * document.title.
189    * @param {string} title The new title for the page.
190    */
191   function setTitle(title) {
192     document.title = title;
193     invokeMethodOnParent('setTitle', {title: title});
194   }
196   /**
197    * Pushes new history state for the page. If the page is embedded in a child,
198    * forward the information to the parent; when embedded, all history entries
199    * are attached to the parent. This is a replacement of history.pushState.
200    * @param {Object} state A state object for replaceState and pushState.
201    * @param {string} path The path the page navigated to.
202    */
203   function pushState(state, path) {
204     updateHistory(state, path, false);
205   }
207   /**
208    * Replaces the page's history state. If the page is embedded in a child,
209    * forward the information to the parent; when embedded, all history entries
210    * are attached to the parent. This is a replacement of history.replaceState.
211    * @param {Object} state A state object for replaceState and pushState.
212    * @param {string} path The path the page navigated to.
213    */
214   function replaceState(state, path) {
215     updateHistory(state, path, true);
216   }
218   return {
219     invokeMethodOnParent: invokeMethodOnParent,
220     invokeMethodOnWindow: invokeMethodOnWindow,
221     onContentFrameLoaded: onContentFrameLoaded,
222     pushState: pushState,
223     replaceState: replaceState,
224     setTitle: setTitle,
225   };