summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordbeam@chromium.org <dbeam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-09 23:00:47 +0000
committerdbeam@chromium.org <dbeam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-09 23:00:47 +0000
commit65a77f20f323cefb0bd68870abc064cf985d0980 (patch)
treed52fe0de9736795c836c6382e5d794771503f053
parenta340e3d88c5742860f9e2469a12029a1193a10c9 (diff)
downloadchromium_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.js7
-rw-r--r--chrome/browser/resources/options2/options.js11
-rw-r--r--chrome/browser/resources/options2/options_page.js67
-rw-r--r--chrome/browser/resources/uber/uber.js101
-rw-r--r--chrome/browser/resources/uber/uber_frame.html2
-rw-r--r--chrome/browser/ui/webui/sync_setup_handler.cc2
-rw-r--r--chrome/browser/ui/webui/uber/uber_ui.cc2
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;
}