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.
6 * @fileoverview A collection of utility methods for UberPage and its contained
10 cr.define('uber', function() {
12 * Fixed position header elements on the page to be shifted by handleScroll.
18 * This should be called by uber content pages when DOM content has loaded.
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).
35 window.addEventListener('message', handleWindowMessage);
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.
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';
52 invokeMethodOnParent('adjustToScroll', scrollLeft);
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.
60 function handleMouseDownInFrame(e) {
61 if (!e.isSynthetic && !document.hasFocus())
66 * Handles 'message' events on window.
67 * @param {Event} e The message event.
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') {
75 /** @type {{deltaX: number, deltaY: number}} */(e.data.params));
76 } else if (e.data.method === 'mouseDown') {
78 } else if (e.data.method === 'popState') {
79 handlePopState(e.data.params.state, e.data.params.path);
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).
87 function handleFrameSelected() {
88 setScrollTopForDocument(document, 0);
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.
101 function handleMouseWheel(params) {
102 window.scrollBy(-params.deltaX * 49 / 120, -params.deltaY * 49 / 120);
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.
111 function handleMouseDown() {
112 var mouseEvent = new MouseEvent('mousedown');
113 mouseEvent.isSynthetic = true;
114 document.dispatchEvent(mouseEvent);
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}
124 function handlePopState(state, path) {
125 window.history.replaceState(state, '', path);
126 window.dispatchEvent(new PopStateEvent('popstate', {state: state}));
130 * @return {boolean} Whether this frame has a parent.
132 function hasParent() {
133 return window != window.parent;
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
143 function invokeMethodOnParent(method, opt_params) {
147 invokeMethodOnWindow(window.parent, method, opt_params, 'chrome://chrome');
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
155 * @param {string=} opt_url The origin of the target window.
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 : '*');
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.
170 function updateHistory(state, path, replace) {
171 var historyFunction = replace ?
172 window.history.replaceState :
173 window.history.pushState;
176 // If there's a parent, always replaceState. The parent will do the actual
178 historyFunction = window.history.replaceState;
179 invokeMethodOnParent('updateHistory', {
180 state: state, path: path, replace: replace});
182 historyFunction.call(window.history, state, '', '/' + path);
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
189 * @param {string} title The new title for the page.
191 function setTitle(title) {
192 document.title = title;
193 invokeMethodOnParent('setTitle', {title: title});
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.
203 function pushState(state, path) {
204 updateHistory(state, path, false);
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.
214 function replaceState(state, path) {
215 updateHistory(state, path, true);
219 invokeMethodOnParent: invokeMethodOnParent,
220 invokeMethodOnWindow: invokeMethodOnWindow,
221 onContentFrameLoaded: onContentFrameLoaded,
222 pushState: pushState,
223 replaceState: replaceState,