diff options
author | dbeam@chromium.org <dbeam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-09 23:00:47 +0000 |
---|---|---|
committer | dbeam@chromium.org <dbeam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-09 23:00:47 +0000 |
commit | 65a77f20f323cefb0bd68870abc064cf985d0980 (patch) | |
tree | d52fe0de9736795c836c6382e5d794771503f053 | |
parent | a340e3d88c5742860f9e2469a12029a1193a10c9 (diff) | |
download | chromium_src-65a77f20f323cefb0bd68870abc064cf985d0980.zip chromium_src-65a77f20f323cefb0bd68870abc064cf985d0980.tar.gz chromium_src-65a77f20f323cefb0bd68870abc064cf985d0980.tar.bz2 |
[uber page] Push navigation urls for subpages
R=estade@chromium.org,csilv@chromium.org
BUG=112377,116196
TEST=Settings sub URLs come through to the uber page and do reasonable things
when refreshed or navigated to / from.
NOTRY=true
Review URL: http://codereview.chromium.org/9436002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125928 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/resources/options2/content_settings.js | 7 | ||||
-rw-r--r-- | chrome/browser/resources/options2/options.js | 11 | ||||
-rw-r--r-- | chrome/browser/resources/options2/options_page.js | 67 | ||||
-rw-r--r-- | chrome/browser/resources/uber/uber.js | 101 | ||||
-rw-r--r-- | chrome/browser/resources/uber/uber_frame.html | 2 | ||||
-rw-r--r-- | chrome/browser/ui/webui/sync_setup_handler.cc | 2 | ||||
-rw-r--r-- | chrome/browser/ui/webui/uber/uber_ui.cc | 2 |
7 files changed, 132 insertions, 60 deletions
diff --git a/chrome/browser/resources/options2/content_settings.js b/chrome/browser/resources/options2/content_settings.js index 1ec2743..0663650 100644 --- a/chrome/browser/resources/options2/content_settings.js +++ b/chrome/browser/resources/options2/content_settings.js @@ -55,8 +55,11 @@ cr.define('options', function() { // Add on the proper hash for the content type, and store that in the // history so back/forward and tab restore works. var hash = event.target.getAttribute('contentType'); - window.history.replaceState({pageName: page.name}, page.title, - '/' + page.name + '#' + hash); + var url = page.name + '#' + hash; + window.history.replaceState({pageName: page.name}, + page.title, + '/' + url); + uber.invokeMethodOnParent('setPath', {path: url}); }; } diff --git a/chrome/browser/resources/options2/options.js b/chrome/browser/resources/options2/options.js index 55287dc..bb86d9f 100644 --- a/chrome/browser/resources/options2/options.js +++ b/chrome/browser/resources/options2/options.js @@ -189,11 +189,14 @@ function load() { if (path.length > 1) { // Skip starting slash and remove trailing slash (if any). var pageName = path.slice(1).replace(/\/$/, ''); - // Proxy page is now per network and only reachable from internet details. - if (pageName != 'proxy') { - // Show page, but don't update history (there's already an entry for it). - OptionsPage.showPageByName(pageName, false); + + if (pageName == 'proxy') { + // The following page doesn't have a unique URL at the moment, so do + // something sensible if a user pastes this link or refreshes on this URL. + pageName = ProxyOptions ? ProxyOptions.getInstance().parentPage.name : + AdvancedOptions.getInstance().name; } + OptionsPage.showPageByName(pageName, true, {replaceState: true}); } else { OptionsPage.showDefaultPage(); } diff --git a/chrome/browser/resources/options2/options_page.js b/chrome/browser/resources/options2/options_page.js index 0f4ca81..e0afb0e 100644 --- a/chrome/browser/resources/options2/options_page.js +++ b/chrome/browser/resources/options2/options_page.js @@ -77,9 +77,16 @@ cr.define('options', function() { * @param {string} pageName Page name. * @param {boolean} updateHistory True if we should update the history after * showing the page. + * @param {Object=} opt_propertyBag An optional bag of properties including + * replaceState (if history state should be replaced instead of pushed). * @private */ - OptionsPage.showPageByName = function(pageName, updateHistory) { + OptionsPage.showPageByName = function(pageName, + updateHistory, + opt_propertyBag) { + // If |opt_propertyBag| is non-truthy, homogenize to object. + opt_propertyBag = opt_propertyBag || {}; + // Find the currently visible root-level page. var rootPage = null; for (var name in this.registeredPages) { @@ -96,7 +103,7 @@ cr.define('options', function() { // If it's not a page, try it as an overlay. if (!targetPage && this.showOverlay_(pageName, rootPage)) { if (updateHistory) - this.updateHistoryState_(); + this.updateHistoryState_(!!opt_propertyBag.replaceState); return; } else { targetPage = this.getDefaultPage(); @@ -112,9 +119,15 @@ cr.define('options', function() { var isRootPageLocked = rootPage && rootPage.sticky && targetPage.parentPage; + var allPageNames = Array.prototype.concat.call( + Object.keys(this.registeredPages), + Object.keys(this.registeredOverlayPages)); + // Notify pages if they will be hidden. - for (var name in this.registeredPages) { - var page = this.registeredPages[name]; + for (var i = 0; i < allPageNames.length; ++i) { + var name = allPageNames[i]; + var page = this.registeredPages[name] || + this.registeredOverlayPages[name]; if (!page.parentPage && isRootPageLocked) continue; if (page.willHidePage && name != pageName && @@ -124,8 +137,10 @@ cr.define('options', function() { } // Update visibilities to show only the hierarchy of the target page. - for (var name in this.registeredPages) { - var page = this.registeredPages[name]; + for (var i = 0; i < allPageNames.length; ++i) { + var name = allPageNames[i]; + var page = this.registeredPages[name] || + this.registeredOverlayPages[name]; if (!page.parentPage && isRootPageLocked) continue; page.visible = name == pageName || page.isAncestorOfPage(targetPage); @@ -133,14 +148,16 @@ cr.define('options', function() { // Update the history and current location. if (updateHistory) - this.updateHistoryState_(); + this.updateHistoryState_(!!opt_propertyBag.replaceState); // Update tab title. this.setTitle_(targetPage.title); // Notify pages if they were shown. - for (var name in this.registeredPages) { - var page = this.registeredPages[name]; + for (var i = 0; i < allPageNames.length; ++i) { + var name = allPageNames[i]; + var page = this.registeredPages[name] || + this.registeredOverlayPages[name]; if (!page.parentPage && isRootPageLocked) continue; if (!targetPageWasVisible && page.didShowPage && @@ -175,22 +192,31 @@ cr.define('options', function() { /** * Pushes the current page onto the history stack, overriding the last page * if it is the generic chrome://settings/. + * @param {boolean} replace If true, allow no history events to be created. * @private */ - OptionsPage.updateHistoryState_ = function() { + OptionsPage.updateHistoryState_ = function(replace) { var page = this.getTopmostVisiblePage(); - var path = location.pathname; + var path = window.location.pathname + window.location.hash; if (path) - path = path.slice(1).replace(/\/$/, ''); // Remove trailing slash. + path = path.slice(1).replace(/\/(?:#|$)/, ''); // Remove trailing slash. // The page is already in history (the user may have clicked the same link // twice). Do nothing. - if (path == page.name) + if (path == page.name && + !document.documentElement.classList.contains('loading')) { return; + } + + // If settings are embedded, tell the outer page to set its "path" to the + // inner frame's path. + var outerPath = (page == this.getDefaultPage() ? '' : page.name) + + window.location.hash; + uber.invokeMethodOnParent('setPath', {path: outerPath}); // If there is no path, the current location is chrome://settings/. // Override this with the new page. - var historyFunction = path ? window.history.pushState : - window.history.replaceState; + var historyFunction = path && !replace ? window.history.pushState : + window.history.replaceState; historyFunction.call(window.history, {pageName: page.name}, page.title, @@ -262,7 +288,7 @@ cr.define('options', function() { overlay.visible = false; if (overlay.didClosePage) overlay.didClosePage(); - this.updateHistoryState_(); + this.updateHistoryState_(false); }; /** @@ -440,12 +466,7 @@ cr.define('options', function() { */ OptionsPage.setState = function(data) { if (data && data.pageName) { - // It's possible an overlay may be the last top-level page shown. - if (this.isOverlayVisible_() && - !this.registeredOverlayPages[data.pageName.toLowerCase()]) { - this.hideOverlay_(); - } - + this.willClose(); this.showPageByName(data.pageName, false); } }; @@ -603,7 +624,7 @@ cr.define('options', function() { OptionsPage.reinitializeCore = function() { if (this.initialized_) chrome.send('coreOptionsInitialize'); - } + }; OptionsPage.prototype = { __proto__: cr.EventTarget.prototype, diff --git a/chrome/browser/resources/uber/uber.js b/chrome/browser/resources/uber/uber.js index 583ebc1..69a89ed 100644 --- a/chrome/browser/resources/uber/uber.js +++ b/chrome/browser/resources/uber/uber.js @@ -7,18 +7,19 @@ cr.define('uber', function() { /** * Options for how web history should be handled. - **/ + */ var HISTORY_STATE_OPTION = { PUSH: 1, // Push a new history state. - REPLACE: 2 // Replace the current history state. + REPLACE: 2, // Replace the current history state. + NONE: 3, // Ignore this history state change. }; /** - * We cache a reference to the #navigation frame here to so we don't need to - * grab it from the DOM on each scroll. - * @type {Node} - * @private - */ + * We cache a reference to the #navigation frame here so we don't need to grab + * it from the DOM on each scroll. + * @type {Node} + * @private + */ var navFrame; /** @@ -30,7 +31,7 @@ cr.define('uber', function() { // Select a page based on the page-URL. var params = resolvePageInfo(); - showPage(params.id, HISTORY_STATE_OPTION.REPLACE, params.path); + showPage(params.id, HISTORY_STATE_OPTION.NONE, params.path); window.addEventListener('message', handleWindowMessage); window.setTimeout(function() { @@ -40,7 +41,7 @@ cr.define('uber', function() { // HACK(dbeam): This makes the assumption that any second part to a path // will result in needing background navigation. We shortcut it to avoid // flicker on load. - if (params.path && window.location.pathname.indexOf('settings') == 0) + if (params.path && params.id == 'settings') backgroundNavigation(); } @@ -89,7 +90,7 @@ cr.define('uber', function() { */ function onPopHistoryState(e) { if (e.state && e.state.pageId) - showPage(e.state.pageId, HISTORY_STATE_OPTION.PUSH); + showPage(e.state.pageId, HISTORY_STATE_OPTION.NONE); } /** @@ -126,8 +127,10 @@ cr.define('uber', function() { backgroundNavigation(); 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') - setTitle_(e.origin, e.data.params.title); + 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') @@ -170,19 +173,59 @@ cr.define('uber', function() { } /** + * Get an iframe based on the origin of a received post message. + * @param {string} origin The origin of a post message. + * @return {!HTMLElement} The frame associated to |origin| or null. + */ + function getIframeFromOrigin(origin) { + assert(origin.substr(-1) != '/', 'invalid origin given'); + var query = '.iframe-container > iframe[src^="' + origin + '/"]'; + return document.querySelector(query); + } + + /** + * Changes the path past the page title (i.e. chrome://chrome/settings/(.*)). + * @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) { + assert(!path || path.substr(-1) != '/', 'invalid path given'); + + var histFunc; + if (historyOption == HISTORY_STATE_OPTION.PUSH) + histFunc = window.history.pushState; + else if (historyOption == HISTORY_STATE_OPTION.REPLACE) + histFunc = window.history.replaceState; + + assert(histFunc, 'invalid historyOption given ' + historyOption); + + var pageId = getSelectedIframe().id; + var args = [{pageId: pageId}, '', '/' + 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". + */ + function setPath(origin, path) { + assert(!path || path[0] != '/', 'invalid path sent from ' + origin); + // Only update the currently displayed path if this is the visible frame. + if (getIframeFromOrigin(origin).parentNode == getSelectedIframe()) + changePathTo(path, HISTORY_STATE_OPTION.REPLACE); + } + + /** * Sets the title of the page. * @param {Object} origin The origin of the source iframe. * @param {string} title The title of the page. */ - function setTitle_(origin, title) { - // |iframe.src| always contains a trailing backslash while |origin| does not - // so add the trailing source for normalization. - var query = '.iframe-container > iframe[src="' + origin + '/"]'; - + function setTitle(origin, title) { // Cache the title for the client iframe, i.e., the iframe setting the // title. querySelector returns the actual iframe element, so use parentNode // to get back to the container. - var container = document.querySelector(query).parentNode; + var container = getIframeFromOrigin(origin).parentNode; container.dataset.title = title; // Only update the currently displayed title if this is the visible frame. @@ -195,25 +238,29 @@ cr.define('uber', function() { * @param {String} pageId Should matche an id of one of the iframe containers. * @param {integer} historyOption Indicates whether we should push or replace * browser history. - * @param {String=} path An optional sub-page path. + * @param {String} path A sub-page path. */ function showPage(pageId, historyOption, path) { var container = $(pageId); var lastSelected = document.querySelector('.iframe-container.selected'); - if (lastSelected === container) - return; // Lazy load of iframe contents. + var sourceUrl = container.dataset.url + (path || ''); var frame = container.querySelector('iframe'); - var sourceUrl = container.dataset.url; if (!frame) { frame = container.ownerDocument.createElement('iframe'); container.appendChild(frame); frame.src = sourceUrl; + } else { + // There's no particularly good way to know what the current URL of the + // 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); } - // If there is a non-empty path, alter the location of the frame. - if (path && path.length) - frame.contentWindow.location.replace(sourceUrl + path); + + // If the last selected container is already showing, ignore the rest. + if (lastSelected === container) + return; if (lastSelected) lastSelected.classList.remove('selected'); @@ -228,10 +275,8 @@ cr.define('uber', function() { var selectedFrame = getSelectedIframe().querySelector('iframe'); uber.invokeMethodOnWindow(selectedFrame.contentWindow, 'frameSelected'); - if (historyOption == HISTORY_STATE_OPTION.PUSH) - window.history.pushState({pageId: pageId}, '', '/' + pageId); - else if (historyOption == HISTORY_STATE_OPTION.REPLACE) - window.history.replaceState({pageId: pageId}, '', '/' + pageId); + if (historyOption != HISTORY_STATE_OPTION.NONE) + changePathTo(path, historyOption); updateNavigationControls(); } diff --git a/chrome/browser/resources/uber/uber_frame.html b/chrome/browser/resources/uber/uber_frame.html index 390aa10..fdefd6e 100644 --- a/chrome/browser/resources/uber/uber_frame.html +++ b/chrome/browser/resources/uber/uber_frame.html @@ -14,7 +14,6 @@ <h1 i18n-content="shortProductName"></h1> <ul> - </li> <li i18n-content="historyDisplayName" i18n-values="controls:historyHost"> </li> @@ -23,6 +22,7 @@ </li> <li i18n-content="settingsDisplayName" i18n-values="controls:settingsHost"> + </li> <li id="helpNavItem" i18n-content="helpDisplayName" i18n-values="controls:helpHost"> </li> diff --git a/chrome/browser/ui/webui/sync_setup_handler.cc b/chrome/browser/ui/webui/sync_setup_handler.cc index 93627ef..6b2d043 100644 --- a/chrome/browser/ui/webui/sync_setup_handler.cc +++ b/chrome/browser/ui/webui/sync_setup_handler.cc @@ -741,7 +741,7 @@ void SyncSetupHandler::OpenSyncSetup() { // If the wizard is already visible, just focus that one. if (FocusExistingWizardIfPresent()) { if (!IsActiveLogin()) - CloseOverlay(); + CloseOverlay(); return; } diff --git a/chrome/browser/ui/webui/uber/uber_ui.cc b/chrome/browser/ui/webui/uber/uber_ui.cc index 6273992..e05fce7 100644 --- a/chrome/browser/ui/webui/uber/uber_ui.cc +++ b/chrome/browser/ui/webui/uber/uber_ui.cc @@ -109,7 +109,7 @@ void UberUI::RegisterSubpage(const std::string& page_url) { content::WebUI* webui = web_ui()->GetWebContents()->CreateWebUI(GURL(page_url)); - webui->SetFrameXPath("//iframe[@src='" + page_url + "']"); + webui->SetFrameXPath("//iframe[starts-with(@src,'" + page_url + "')]"); sub_uis_[page_url] = webui; } |