OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 cr.define('uber', function() { | 5 cr.define('uber', function() { |
6 /** | 6 /** |
7 * Options for how web history should be handled. | 7 * Options for how web history should be handled. |
8 */ | 8 */ |
9 var HISTORY_STATE_OPTION = { | 9 var HISTORY_STATE_OPTION = { |
10 PUSH: 1, // Push a new history state. | 10 PUSH: 1, // Push a new history state. |
11 REPLACE: 2, // Replace the current history state. | 11 REPLACE: 2, // Replace the current history state. |
12 NONE: 3, // Ignore this history state change. | 12 NONE: 3, // Ignore this history state change. |
13 }; | 13 }; |
14 | 14 |
15 /** | 15 /** |
16 * We cache a reference to the #navigation frame here so we don't need to grab | 16 * We cache a reference to the #navigation frame here so we don't need to grab |
17 * it from the DOM on each scroll. | 17 * it from the DOM on each scroll. |
18 * @type {Node} | 18 * @type {Node} |
19 * @private | 19 * @private |
20 */ | 20 */ |
21 var navFrame; | 21 var navFrame; |
22 | 22 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 if (index != -1) { | 74 if (index != -1) { |
75 params.id = path.slice(0, index); | 75 params.id = path.slice(0, index); |
76 params.path = path.slice(index + 1); | 76 params.path = path.slice(index + 1); |
77 } else { | 77 } else { |
78 params.id = path; | 78 params.id = path; |
79 } | 79 } |
80 | 80 |
81 var container = $(params.id); | 81 var container = $(params.id); |
82 if (container) { | 82 if (container) { |
83 // The id is valid. Add the hash and search parts of the URL to path. | 83 // The id is valid. Add the hash and search parts of the URL to path. |
84 params.path = (params.path || '') + window.location.search + | 84 params.path = |
85 window.location.hash; | 85 (params.path || '') + window.location.search + window.location.hash; |
86 } else { | 86 } else { |
87 // The target sub-page does not exist, discard the params we generated. | 87 // The target sub-page does not exist, discard the params we generated. |
88 params.id = undefined; | 88 params.id = undefined; |
89 params.path = undefined; | 89 params.path = undefined; |
90 } | 90 } |
91 } | 91 } |
92 // If we don't have a valid page, get a default. | 92 // If we don't have a valid page, get a default. |
93 if (!params.id) | 93 if (!params.id) |
94 params.id = getDefaultIframe().id; | 94 params.id = getDefaultIframe().id; |
95 | 95 |
(...skipping 12 matching lines...) Expand all Loading... |
108 // already loaded, it may have state not reflected in the URL, such as the | 108 // already loaded, it may have state not reflected in the URL, such as the |
109 // history page's "Remove selected items" overlay. http://crbug.com/377386 | 109 // history page's "Remove selected items" overlay. http://crbug.com/377386 |
110 if (getRequiredElement(params.id) !== getSelectedIframeContainer()) | 110 if (getRequiredElement(params.id) !== getSelectedIframeContainer()) |
111 showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path); | 111 showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path); |
112 | 112 |
113 // Either way, send the state down to it. | 113 // Either way, send the state down to it. |
114 // | 114 // |
115 // Note: This assumes that the state and path parameters for every page | 115 // Note: This assumes that the state and path parameters for every page |
116 // under this origin are compatible. All of the downstream pages which | 116 // under this origin are compatible. All of the downstream pages which |
117 // navigate use pushState and replaceState. | 117 // navigate use pushState and replaceState. |
118 invokeMethodOnPage(params.id, 'popState', | 118 invokeMethodOnPage( |
119 {state: e.state, path: '/' + params.path}); | 119 params.id, 'popState', {state: e.state, path: '/' + params.path}); |
120 } | 120 } |
121 | 121 |
122 /** | 122 /** |
123 * @return {Object} The default iframe container. | 123 * @return {Object} The default iframe container. |
124 */ | 124 */ |
125 function getDefaultIframe() { | 125 function getDefaultIframe() { |
126 return $(loadTimeData.getString('helpHost')); | 126 return $(loadTimeData.getString('helpHost')); |
127 } | 127 } |
128 | 128 |
129 /** | 129 /** |
(...skipping 19 matching lines...) Expand all Loading... |
149 * { method : "methodToInvoke", | 149 * { method : "methodToInvoke", |
150 * params : {...} | 150 * params : {...} |
151 * } | 151 * } |
152 * | 152 * |
153 * |method| is required, while |params| is optional. Extra parameters required | 153 * |method| is required, while |params| is optional. Extra parameters required |
154 * by a method must be specified by that method's documentation. | 154 * by a method must be specified by that method's documentation. |
155 * | 155 * |
156 * @param {Event} e The posted object. | 156 * @param {Event} e The posted object. |
157 */ | 157 */ |
158 function handleWindowMessage(e) { | 158 function handleWindowMessage(e) { |
159 e = /** @type {!MessageEvent<!{method: string, params: *}>} */(e); | 159 e = /** @type {!MessageEvent<!{method: string, params: *}>} */ (e); |
160 if (e.data.method === 'beginInterceptingEvents') { | 160 if (e.data.method === 'beginInterceptingEvents') { |
161 backgroundNavigation(); | 161 backgroundNavigation(); |
162 } else if (e.data.method === 'stopInterceptingEvents') { | 162 } else if (e.data.method === 'stopInterceptingEvents') { |
163 foregroundNavigation(); | 163 foregroundNavigation(); |
164 } else if (e.data.method === 'ready') { | 164 } else if (e.data.method === 'ready') { |
165 pageReady(e.origin); | 165 pageReady(e.origin); |
166 } else if (e.data.method === 'updateHistory') { | 166 } else if (e.data.method === 'updateHistory') { |
167 updateHistory(e.origin, e.data.params.state, e.data.params.path, | 167 updateHistory( |
168 e.data.params.replace); | 168 e.origin, e.data.params.state, e.data.params.path, |
| 169 e.data.params.replace); |
169 } else if (e.data.method === 'setTitle') { | 170 } else if (e.data.method === 'setTitle') { |
170 setTitle(e.origin, e.data.params.title); | 171 setTitle(e.origin, e.data.params.title); |
171 } else if (e.data.method === 'showPage') { | 172 } else if (e.data.method === 'showPage') { |
172 showPage(e.data.params.pageId, | 173 showPage( |
173 HISTORY_STATE_OPTION.PUSH, | 174 e.data.params.pageId, HISTORY_STATE_OPTION.PUSH, e.data.params.path); |
174 e.data.params.path); | |
175 } else if (e.data.method === 'navigationControlsLoaded') { | 175 } else if (e.data.method === 'navigationControlsLoaded') { |
176 onNavigationControlsLoaded(); | 176 onNavigationControlsLoaded(); |
177 } else if (e.data.method === 'adjustToScroll') { | 177 } else if (e.data.method === 'adjustToScroll') { |
178 adjustToScroll(/** @type {number} */(e.data.params)); | 178 adjustToScroll(/** @type {number} */ (e.data.params)); |
179 } else if (e.data.method === 'mouseWheel') { | 179 } else if (e.data.method === 'mouseWheel') { |
180 forwardMouseWheel(/** @type {Object} */(e.data.params)); | 180 forwardMouseWheel(/** @type {Object} */ (e.data.params)); |
181 } else if (e.data.method === 'mouseDown') { | 181 } else if (e.data.method === 'mouseDown') { |
182 forwardMouseDown(); | 182 forwardMouseDown(); |
183 } else { | 183 } else { |
184 console.error('Received unexpected message', e.data); | 184 console.error('Received unexpected message', e.data); |
185 } | 185 } |
186 } | 186 } |
187 | 187 |
188 /** | 188 /** |
189 * Sends the navigation iframe to the background. | 189 * Sends the navigation iframe to the background. |
190 */ | 190 */ |
(...skipping 14 matching lines...) Expand all Loading... |
205 | 205 |
206 /** | 206 /** |
207 * Enables or disables animated transitions when changing content while | 207 * Enables or disables animated transitions when changing content while |
208 * horizontally scrolled. | 208 * horizontally scrolled. |
209 * @param {boolean} enabled True if enabled, else false to disable. | 209 * @param {boolean} enabled True if enabled, else false to disable. |
210 */ | 210 */ |
211 function setContentChanging(enabled) { | 211 function setContentChanging(enabled) { |
212 navFrame.classList[enabled ? 'add' : 'remove']('changing-content'); | 212 navFrame.classList[enabled ? 'add' : 'remove']('changing-content'); |
213 | 213 |
214 if (isRTL()) { | 214 if (isRTL()) { |
215 uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow, | 215 uber.invokeMethodOnWindow( |
216 'setContentChanging', enabled); | 216 navFrame.firstChild.contentWindow, 'setContentChanging', enabled); |
217 } | 217 } |
218 } | 218 } |
219 | 219 |
220 /** | 220 /** |
221 * Get an iframe based on the origin of a received post message. | 221 * Get an iframe based on the origin of a received post message. |
222 * @param {string} origin The origin of a post message. | 222 * @param {string} origin The origin of a post message. |
223 * @return {!Element} The frame associated to |origin| or null. | 223 * @return {!Element} The frame associated to |origin| or null. |
224 */ | 224 */ |
225 function getIframeFromOrigin(origin) { | 225 function getIframeFromOrigin(origin) { |
226 assert(origin.substr(-1) != '/', 'invalid origin given'); | 226 assert(origin.substr(-1) != '/', 'invalid origin given'); |
227 var query = '.iframe-container > iframe[src^="' + origin + '/"]'; | 227 var query = '.iframe-container > iframe[src^="' + origin + '/"]'; |
228 var element = document.querySelector(query); | 228 var element = document.querySelector(query); |
229 assert(element); | 229 assert(element); |
230 return /** @type {!Element} */(element); | 230 return /** @type {!Element} */ (element); |
231 } | 231 } |
232 | 232 |
233 /** | 233 /** |
234 * Changes the path past the page title (i.e. chrome://chrome/settings/(.*)). | 234 * Changes the path past the page title (i.e. chrome://chrome/settings/(.*)). |
235 * @param {Object} state The page's state object for the navigation. | 235 * @param {Object} state The page's state object for the navigation. |
236 * @param {string} path The new /path/ to be set after the page name. | 236 * @param {string} path The new /path/ to be set after the page name. |
237 * @param {number} historyOption The type of history modification to make. | 237 * @param {number} historyOption The type of history modification to make. |
238 */ | 238 */ |
239 function changePathTo(state, path, historyOption) { | 239 function changePathTo(state, path, historyOption) { |
240 assert(!path || path.substr(-1) != '/', 'invalid path given'); | 240 assert(!path || path.substr(-1) != '/', 'invalid path given'); |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 uber.invokeMethodOnWindow(selectedWindow, 'frameSelected'); | 380 uber.invokeMethodOnWindow(selectedWindow, 'frameSelected'); |
381 selectedWindow.focus(); | 381 selectedWindow.focus(); |
382 | 382 |
383 if (historyOption != HISTORY_STATE_OPTION.NONE) | 383 if (historyOption != HISTORY_STATE_OPTION.NONE) |
384 changePathTo({}, path, historyOption); | 384 changePathTo({}, path, historyOption); |
385 | 385 |
386 if (container.dataset.title) | 386 if (container.dataset.title) |
387 document.title = container.dataset.title; | 387 document.title = container.dataset.title; |
388 assert('favicon' in container.dataset); | 388 assert('favicon' in container.dataset); |
389 | 389 |
390 var dataset = /** @type {{favicon: string}} */(container.dataset); | 390 var dataset = /** @type {{favicon: string}} */ (container.dataset); |
391 $('favicon').href = 'chrome://theme/' + dataset.favicon; | 391 $('favicon').href = 'chrome://theme/' + dataset.favicon; |
392 $('favicon2x').href = 'chrome://theme/' + dataset.favicon + '@2x'; | 392 $('favicon2x').href = 'chrome://theme/' + dataset.favicon + '@2x'; |
393 | 393 |
394 updateNavigationControls(); | 394 updateNavigationControls(); |
395 } | 395 } |
396 | 396 |
397 function onNavigationControlsLoaded() { | 397 function onNavigationControlsLoaded() { |
398 updateNavigationControls(); | 398 updateNavigationControls(); |
399 } | 399 } |
400 | 400 |
401 /** | 401 /** |
402 * Sends a message to uber-frame to update the appearance of the nav controls. | 402 * Sends a message to uber-frame to update the appearance of the nav controls. |
403 * It should be called whenever the selected iframe changes. | 403 * It should be called whenever the selected iframe changes. |
404 */ | 404 */ |
405 function updateNavigationControls() { | 405 function updateNavigationControls() { |
406 var container = getSelectedIframeContainer(); | 406 var container = getSelectedIframeContainer(); |
407 uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow, | 407 uber.invokeMethodOnWindow( |
408 'changeSelection', {pageId: container.id}); | 408 navFrame.firstChild.contentWindow, 'changeSelection', |
| 409 {pageId: container.id}); |
409 } | 410 } |
410 | 411 |
411 /** | 412 /** |
412 * Forwarded scroll offset from a content frame's scroll handler. | 413 * Forwarded scroll offset from a content frame's scroll handler. |
413 * @param {number} scrollOffset The scroll offset from the content frame. | 414 * @param {number} scrollOffset The scroll offset from the content frame. |
414 */ | 415 */ |
415 function adjustToScroll(scrollOffset) { | 416 function adjustToScroll(scrollOffset) { |
416 // NOTE: The scroll is reset to 0 and easing turned on every time a user | 417 // NOTE: The scroll is reset to 0 and easing turned on every time a user |
417 // switches frames. If we receive a non-zero value it has to have come from | 418 // switches frames. If we receive a non-zero value it has to have come from |
418 // a real user scroll, so we disable easing when this happens. | 419 // a real user scroll, so we disable easing when this happens. |
419 if (scrollOffset != 0) | 420 if (scrollOffset != 0) |
420 setContentChanging(false); | 421 setContentChanging(false); |
421 | 422 |
422 if (isRTL()) { | 423 if (isRTL()) { |
423 uber.invokeMethodOnWindow(navFrame.firstChild.contentWindow, | 424 uber.invokeMethodOnWindow( |
424 'adjustToScroll', | 425 navFrame.firstChild.contentWindow, 'adjustToScroll', scrollOffset); |
425 scrollOffset); | |
426 var navWidth = Math.max(0, +navFrame.dataset.width + scrollOffset); | 426 var navWidth = Math.max(0, +navFrame.dataset.width + scrollOffset); |
427 navFrame.style.width = navWidth + 'px'; | 427 navFrame.style.width = navWidth + 'px'; |
428 } else { | 428 } else { |
429 navFrame.style.webkitTransform = 'translateX(' + -scrollOffset + 'px)'; | 429 navFrame.style.webkitTransform = 'translateX(' + -scrollOffset + 'px)'; |
430 } | 430 } |
431 } | 431 } |
432 | 432 |
433 /** | 433 /** |
434 * Forward scroll wheel events to subpages. | 434 * Forward scroll wheel events to subpages. |
435 * @param {Object} params Relevant parameters of wheel event. | 435 * @param {Object} params Relevant parameters of wheel event. |
(...skipping 23 matching lines...) Expand all Loading... |
459 container.hidden = true; | 459 container.hidden = true; |
460 container.setAttribute('aria-hidden', 'true'); | 460 container.setAttribute('aria-hidden', 'true'); |
461 } | 461 } |
462 container.addEventListener('webkitTransitionEnd', function(event) { | 462 container.addEventListener('webkitTransitionEnd', function(event) { |
463 if (!event.target.classList.contains('selected')) | 463 if (!event.target.classList.contains('selected')) |
464 event.target.hidden = true; | 464 event.target.hidden = true; |
465 }); | 465 }); |
466 } | 466 } |
467 } | 467 } |
468 | 468 |
469 return { | 469 return {onLoad: onLoad, onPopHistoryState: onPopHistoryState}; |
470 onLoad: onLoad, | |
471 onPopHistoryState: onPopHistoryState | |
472 }; | |
473 }); | 470 }); |
474 | 471 |
475 window.addEventListener('popstate', uber.onPopHistoryState); | 472 window.addEventListener('popstate', uber.onPopHistoryState); |
476 document.addEventListener('DOMContentLoaded', uber.onLoad); | 473 document.addEventListener('DOMContentLoaded', uber.onLoad); |
OLD | NEW |