diff options
Diffstat (limited to 'chromium/chrome/browser/resources/uber/uber.js')
-rw-r--r-- | chromium/chrome/browser/resources/uber/uber.js | 124 |
1 files changed, 98 insertions, 26 deletions
diff --git a/chromium/chrome/browser/resources/uber/uber.js b/chromium/chrome/browser/resources/uber/uber.js index 44609c0d570..488310f8a30 100644 --- a/chromium/chrome/browser/resources/uber/uber.js +++ b/chromium/chrome/browser/resources/uber/uber.js @@ -21,6 +21,15 @@ cr.define('uber', function() { var navFrame; /** + * A queue of method invocations on one of the iframes; if the iframe has not + * loaded by the time there is a method to invoke, delay the invocation until + * it is ready. + * @type {Object} + * @private + */ + var queuedInvokes = {}; + + /** * Handles page initialization. */ function onLoad(e) { @@ -92,8 +101,22 @@ cr.define('uber', function() { * @param {Event} e The history event. */ function onPopHistoryState(e) { - if (e.state && e.state.pageId) - showPage(e.state.pageId, HISTORY_STATE_OPTION.NONE); + // Use the URL to determine which page to route to. + var params = resolvePageInfo(); + + // If the page isn't the current page, load it fresh. Even if the page is + // already loaded, it may have state not reflected in the URL, such as the + // history page's "Remove selected items" overlay. http://crbug.com/377386 + if (getRequiredElement(params.id) !== getSelectedIframe()) + showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path); + + // Either way, send the state down to it. + // + // Note: This assumes that the state and path parameters for every page + // under this origin are compatible. All of the downstream pages which + // navigate use pushState and replaceState. + invokeMethodOnPage(params.id, 'popState', + {state: e.state, path: '/' + params.path}); } /** @@ -126,24 +149,30 @@ cr.define('uber', function() { * @param {Event} e The posted object. */ function handleWindowMessage(e) { - if (e.data.method === 'beginInterceptingEvents') + if (e.data.method === 'beginInterceptingEvents') { backgroundNavigation(); - else if (e.data.method === 'stopInterceptingEvents') + } else if (e.data.method === 'stopInterceptingEvents') { foregroundNavigation(); - else if (e.data.method === 'setPath') - setPath(e.origin, e.data.params.path); - else if (e.data.method === 'setTitle') + } else if (e.data.method === 'ready') { + pageReady(e.origin); + } else if (e.data.method === 'updateHistory') { + updateHistory(e.origin, e.data.params.state, e.data.params.path, + e.data.params.replace); + } else if (e.data.method === 'setTitle') { setTitle(e.origin, e.data.params.title); - else if (e.data.method === 'showPage') - showPage(e.data.params.pageId, HISTORY_STATE_OPTION.PUSH); - else if (e.data.method === 'navigationControlsLoaded') + } else if (e.data.method === 'showPage') { + showPage(e.data.params.pageId, + HISTORY_STATE_OPTION.PUSH, + e.data.params.path); + } else if (e.data.method === 'navigationControlsLoaded') { onNavigationControlsLoaded(); - else if (e.data.method === 'adjustToScroll') + } else if (e.data.method === 'adjustToScroll') { adjustToScroll(e.data.params); - else if (e.data.method === 'mouseWheel') + } else if (e.data.method === 'mouseWheel') { forwardMouseWheel(e.data.params); - else + } else { console.error('Received unexpected message', e.data); + } } /** @@ -192,10 +221,11 @@ cr.define('uber', function() { /** * Changes the path past the page title (i.e. chrome://chrome/settings/(.*)). + * @param {Object} state The page's state object for the navigation. * @param {string} path The new /path/ to be set after the page name. * @param {number} historyOption The type of history modification to make. */ - function changePathTo(path, historyOption) { + function changePathTo(state, path, historyOption) { assert(!path || path.substr(-1) != '/', 'invalid path given'); var histFunc; @@ -207,25 +237,31 @@ cr.define('uber', function() { assert(histFunc, 'invalid historyOption given ' + historyOption); var pageId = getSelectedIframe().id; - var args = [{pageId: pageId}, '', '/' + pageId + '/' + (path || '')]; + var args = [state, '', '/' + pageId + '/' + (path || '')]; histFunc.apply(window.history, args); } /** - * Sets the "path" of the page (actually the path after the first '/' char). - * @param {Object} origin The origin of the source iframe. - * @param {string} title The new "path". + * Adds or replaces the current history entry based on a navigation from the + * source iframe. + * @param {string} origin The origin of the source iframe. + * @param {Object} state The source iframe's state object. + * @param {string} path The new "path" (e.g. "/createProfile"). + * @param {boolean} replace Whether to replace the current history entry. */ - function setPath(origin, path) { + function updateHistory(origin, state, path, replace) { assert(!path || path[0] != '/', 'invalid path sent from ' + origin); + var historyOption = + replace ? HISTORY_STATE_OPTION.REPLACE : HISTORY_STATE_OPTION.PUSH; // Only update the currently displayed path if this is the visible frame. - if (getIframeFromOrigin(origin).parentNode == getSelectedIframe()) - changePathTo(path, HISTORY_STATE_OPTION.REPLACE); + var container = getIframeFromOrigin(origin).parentNode; + if (container == getSelectedIframe()) + changePathTo(state, path, historyOption); } /** * Sets the title of the page. - * @param {Object} origin The origin of the source iframe. + * @param {string} origin The origin of the source iframe. * @param {string} title The title of the page. */ function setTitle(origin, title) { @@ -241,21 +277,55 @@ cr.define('uber', function() { } /** - * Selects a subpage. This is called from uber-frame. - * @param {string} pageId Should matche an id of one of the iframe containers. + * Invokes a method on a subpage. If the subpage has not signaled readiness, + * queue the message for when it does. + * @param {string} pageId Should match an id of one of the iframe containers. + * @param {string} method The name of the method to invoke. + * @param {Object=} opt_params Optional property page of parameters to pass to + * the invoked method. + */ + function invokeMethodOnPage(pageId, method, opt_params) { + var frame = $(pageId).querySelector('iframe'); + if (!frame || !frame.dataset.ready) { + queuedInvokes[pageId] = (queuedInvokes[pageId] || []); + queuedInvokes[pageId].push([method, opt_params]); + } else { + uber.invokeMethodOnWindow(frame.contentWindow, method, opt_params); + } + } + + /** + * Called in response to a page declaring readiness. Calls any deferred method + * invocations from invokeMethodOnPage. + * @param {string} origin The origin of the source iframe. + */ + function pageReady(origin) { + var frame = getIframeFromOrigin(origin); + var container = frame.parentNode; + frame.dataset.ready = true; + var queue = queuedInvokes[container.id] || []; + queuedInvokes[container.id] = undefined; + for (var i = 0; i < queue.length; i++) { + uber.invokeMethodOnWindow(frame.contentWindow, queue[i][0], queue[i][1]); + } + } + + /** + * Selects and navigates a subpage. This is called from uber-frame. + * @param {string} pageId Should match an id of one of the iframe containers. * @param {integer} historyOption Indicates whether we should push or replace * browser history. * @param {string} path A sub-page path. */ function showPage(pageId, historyOption, path) { var container = $(pageId); - var lastSelected = document.querySelector('.iframe-container.selected'); // Lazy load of iframe contents. var sourceUrl = container.dataset.url + (path || ''); var frame = container.querySelector('iframe'); if (!frame) { frame = container.ownerDocument.createElement('iframe'); + frame.name = pageId; container.appendChild(frame); frame.src = sourceUrl; } else { @@ -263,9 +333,11 @@ cr.define('uber', function() { // content frame is as we don't have access to its contentWindow's // location, so just replace every time until necessary to do otherwise. frame.contentWindow.location.replace(sourceUrl); + frame.dataset.ready = false; } // If the last selected container is already showing, ignore the rest. + var lastSelected = document.querySelector('.iframe-container.selected'); if (lastSelected === container) return; @@ -295,7 +367,7 @@ cr.define('uber', function() { uber.invokeMethodOnWindow(selectedFrame.contentWindow, 'frameSelected'); if (historyOption != HISTORY_STATE_OPTION.NONE) - changePathTo(path, historyOption); + changePathTo({}, path, historyOption); if (container.dataset.title) document.title = container.dataset.title; |