Commit df50c179 authored by Yi Su's avatar Yi Su Committed by Commit Bot

Move navigation.didReplaceState handler into CRWJSNavigationHandler.

This CL moves the handler method for navigation.didReplaceState message
from WebController into CRWJSNavigationHandler.

Bug: 956511
Change-Id: I8b507a944848565043e7115057e983bd2758c1d4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1660496
Commit-Queue: Yi Su <mrsuyi@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#669601}
parent b40b0b77
...@@ -55,6 +55,8 @@ GURL URLEscapedForHistory(const GURL& url) { ...@@ -55,6 +55,8 @@ GURL URLEscapedForHistory(const GURL& url) {
@property(nonatomic, readonly, weak) WKWebView* webView; @property(nonatomic, readonly, weak) WKWebView* webView;
// Returns CRWJSInjector from self.delegate. // Returns CRWJSInjector from self.delegate.
@property(nonatomic, readonly, weak) CRWJSInjector* JSInjector; @property(nonatomic, readonly, weak) CRWJSInjector* JSInjector;
// Returns current URL from self.delegate.
@property(nonatomic, readonly, assign) GURL currentURL;
@end @end
...@@ -74,13 +76,17 @@ GURL URLEscapedForHistory(const GURL& url) { ...@@ -74,13 +76,17 @@ GURL URLEscapedForHistory(const GURL& url) {
const std::string* command = message.FindStringKey("command"); const std::string* command = message.FindStringKey("command");
DCHECK(command); DCHECK(command);
if (*command == "navigation.hashchange") { if (*command == "navigation.hashchange") {
[weakSelf handleWindowHashChangeInFrame:senderFrame]; [weakSelf handleNavigationHashChangeInFrame:senderFrame];
return true; return true;
} else if (*command == "navigation.willChangeState") { } else if (*command == "navigation.willChangeState") {
[weakSelf handleWindowWillChangeStateInFrame:senderFrame]; [weakSelf handleNavigationWillChangeStateInFrame:senderFrame];
return true; return true;
} else if (*command == "navigation.didPushState") { } else if (*command == "navigation.didPushState") {
[weakSelf handleWindowHistoryDidPushStateMessage:message [weakSelf handleNavigationDidPushStateMessage:message
inFrame:senderFrame];
return true;
} else if (*command == "navigation.didReplaceState") {
[weakSelf handleNavigationDidReplaceStateMessage:message
inFrame:senderFrame]; inFrame:senderFrame];
return true; return true;
} }
...@@ -129,8 +135,12 @@ GURL URLEscapedForHistory(const GURL& url) { ...@@ -129,8 +135,12 @@ GURL URLEscapedForHistory(const GURL& url) {
return [self.delegate JSInjectorForJSNavigationHandler:self]; return [self.delegate JSInjectorForJSNavigationHandler:self];
} }
- (GURL)currentURL {
return [self.delegate currentURLForJSNavigationHandler:self];
}
// Handles the navigation.hashchange event emitted from |senderFrame|. // Handles the navigation.hashchange event emitted from |senderFrame|.
- (void)handleWindowHashChangeInFrame:(web::WebFrame*)senderFrame { - (void)handleNavigationHashChangeInFrame:(web::WebFrame*)senderFrame {
if (!senderFrame->IsMainFrame()) if (!senderFrame->IsMainFrame())
return; return;
...@@ -147,16 +157,16 @@ GURL URLEscapedForHistory(const GURL& url) { ...@@ -147,16 +157,16 @@ GURL URLEscapedForHistory(const GURL& url) {
} }
// Handles the navigation.willChangeState message sent from |senderFrame|. // Handles the navigation.willChangeState message sent from |senderFrame|.
- (void)handleWindowWillChangeStateInFrame:(web::WebFrame*)senderFrame { - (void)handleNavigationWillChangeStateInFrame:(web::WebFrame*)senderFrame {
if (senderFrame->IsMainFrame()) { if (senderFrame->IsMainFrame()) {
self.changingHistoryState = YES; self.changingHistoryState = YES;
} }
} }
// Handles the navigation.didChangeState message sent from |senderFrame|. // Handles the navigation.didChangeState message sent from |senderFrame|.
- (void)handleWindowHistoryDidPushStateMessage: - (void)handleNavigationDidPushStateMessage:
(const base::DictionaryValue&)message (const base::DictionaryValue&)message
inFrame:(web::WebFrame*)senderFrame { inFrame:(web::WebFrame*)senderFrame {
if (!senderFrame->IsMainFrame()) if (!senderFrame->IsMainFrame())
return; return;
DCHECK(self.changingHistoryState); DCHECK(self.changingHistoryState);
...@@ -178,8 +188,7 @@ GURL URLEscapedForHistory(const GURL& url) { ...@@ -178,8 +188,7 @@ GURL URLEscapedForHistory(const GURL& url) {
return; return;
} }
GURL pushURL = web::history_state_util::GetHistoryStateChangeUrl( GURL pushURL = web::history_state_util::GetHistoryStateChangeUrl(
[self.delegate currentURLForJSNavigationHandler:self], GURL(*baseURL), self.currentURL, GURL(*baseURL), *pageURL);
*pageURL);
// UIWebView seems to choke on unicode characters that haven't been // UIWebView seems to choke on unicode characters that haven't been
// escaped; escape the URL now so the expected load URL is correct. // escaped; escape the URL now so the expected load URL is correct.
pushURL = URLEscapedForHistory(pushURL); pushURL = URLEscapedForHistory(pushURL);
...@@ -240,6 +249,69 @@ GURL URLEscapedForHistory(const GURL& url) { ...@@ -240,6 +249,69 @@ GURL URLEscapedForHistory(const GURL& url) {
}]; }];
} }
// Handles the navigation.didReplaceState message sent from |senderFrame|.
- (void)handleNavigationDidReplaceStateMessage:
(const base::DictionaryValue&)message
inFrame:(web::WebFrame*)senderFrame {
if (!senderFrame->IsMainFrame())
return;
DCHECK(self.changingHistoryState);
self.changingHistoryState = NO;
const std::string* pageURL = message.FindStringKey("pageUrl");
const std::string* baseURL = message.FindStringKey("baseUrl");
if (!pageURL || !baseURL) {
DLOG(WARNING) << "JS message parameter not found: pageUrl or baseUrl";
return;
}
GURL replaceURL = web::history_state_util::GetHistoryStateChangeUrl(
self.currentURL, GURL(*baseURL), *pageURL);
// UIWebView seems to choke on unicode characters that haven't been
// escaped; escape the URL now so the expected load URL is correct.
replaceURL = URLEscapedForHistory(replaceURL);
if (!replaceURL.is_valid())
return;
web::NavigationItemImpl* navItem =
self.navigationManagerImpl
? self.navigationManagerImpl->GetCurrentItemImpl()
: nullptr;
// ReplaceState happened before first navigation entry or called right
// after window.open when the url is empty/not valid.
if (!navItem || (self.navigationManagerImpl->GetItemCount() <= 1 &&
navItem->GetURL().is_empty()))
return;
if (!web::history_state_util::IsHistoryStateChangeValid(navItem->GetURL(),
replaceURL)) {
// If the current session entry URL origin still doesn't match
// replaceURL's origin, ignore the replaceState. This can happen if a
// new URL is loaded just before the replaceState.
return;
}
const std::string* stateObjectJSON = message.FindStringKey("stateObject");
if (!stateObjectJSON) {
DLOG(WARNING) << "JS message parameter not found: stateObject";
return;
}
NSString* stateObject = base::SysUTF8ToNSString(*stateObjectJSON);
[self replaceStateWithPageURL:replaceURL
stateObject:stateObject
hasUserGesture:self.userInteractionState->IsUserInteracting(
self.webView)];
NSString* replaceStateJS = [self javaScriptToReplaceWebViewURL:replaceURL
stateObjectJSON:stateObject];
__weak CRWJSNavigationHandler* weakSelf = self;
[self.JSInjector executeJavaScript:replaceStateJS
completionHandler:^(id, NSError*) {
CRWJSNavigationHandler* strongSelf = weakSelf;
if (!strongSelf || strongSelf.beingDestroyed)
return;
[strongSelf.delegate JSNavigationHandler:self
didFinishNavigation:nullptr];
}];
return;
}
// Adds a new NavigationItem with the given URL and state object to the // Adds a new NavigationItem with the given URL and state object to the
// history stack. A state object is a serialized generic JavaScript object // history stack. A state object is a serialized generic JavaScript object
// that contains details of the UI's state for a given NavigationItem/URL. // that contains details of the UI's state for a given NavigationItem/URL.
...@@ -262,4 +334,21 @@ GURL URLEscapedForHistory(const GURL& url) { ...@@ -262,4 +334,21 @@ GURL URLEscapedForHistory(const GURL& url) {
self.userInteractionState->SetUserInteractionRegisteredSincePageLoaded(false); self.userInteractionState->SetUserInteractionRegisteredSincePageLoaded(false);
} }
// Assigns the given URL and state object to the current NavigationItem.
- (void)replaceStateWithPageURL:(const GURL&)pageURL
stateObject:(NSString*)stateObject
hasUserGesture:(BOOL)hasUserGesture {
std::unique_ptr<web::NavigationContextImpl> context =
web::NavigationContextImpl::CreateNavigationContext(
self.webStateImpl, pageURL, hasUserGesture,
ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
/*is_renderer_initiated=*/true);
context->SetIsSameDocument(true);
self.webStateImpl->OnNavigationStarted(context.get());
self.navigationManagerImpl->UpdateCurrentItemForReplaceState(pageURL,
stateObject);
context->SetHasCommitted(true);
self.webStateImpl->OnNavigationFinished(context.get());
}
@end @end
...@@ -98,7 +98,7 @@ window.history.replaceState = function(stateObject, pageTitle, pageUrl) { ...@@ -98,7 +98,7 @@ window.history.replaceState = function(stateObject, pageTitle, pageUrl) {
originalWindowHistoryReplaceState.call( originalWindowHistoryReplaceState.call(
history, stateObject, pageTitle, pageUrl); history, stateObject, pageTitle, pageUrl);
__gCrWeb.message.invokeOnHost({ __gCrWeb.message.invokeOnHost({
'command': 'window.history.didReplaceState', 'command': 'navigation.didReplaceState',
'stateObject': serializedState, 'stateObject': serializedState,
'baseUrl': document.baseURI, 'baseUrl': document.baseURI,
'pageUrl': pageUrl.toString() 'pageUrl': pageUrl.toString()
......
...@@ -162,17 +162,6 @@ NSString* const kFrameBecameAvailableMessageName = @"FrameBecameAvailable"; ...@@ -162,17 +162,6 @@ NSString* const kFrameBecameAvailableMessageName = @"FrameBecameAvailable";
// Message command sent when a frame is unloading. // Message command sent when a frame is unloading.
NSString* const kFrameBecameUnavailableMessageName = @"FrameBecameUnavailable"; NSString* const kFrameBecameUnavailableMessageName = @"FrameBecameUnavailable";
// URLs that are fed into UIWebView as history push/replace get escaped,
// potentially changing their format. Code that attempts to determine whether a
// URL hasn't changed can be confused by those differences though, so method
// will round-trip a URL through the escaping process so that it can be adjusted
// pre-storing, to allow later comparisons to work as expected.
GURL URLEscapedForHistory(const GURL& url) {
// TODO(stuartmorgan): This is a very large hammer; see if limited unicode
// escaping would be sufficient.
return net::GURLWithNSURL(net::NSURLWithGURL(url));
}
} // namespace } // namespace
@interface CRWWebController () <BrowsingDataRemoverObserver, @interface CRWWebController () <BrowsingDataRemoverObserver,
...@@ -1823,8 +1812,6 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*); ...@@ -1823,8 +1812,6 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
(*handlers)["window.error"] = @selector(handleWindowErrorMessage:context:); (*handlers)["window.error"] = @selector(handleWindowErrorMessage:context:);
(*handlers)["window.history.back"] = (*handlers)["window.history.back"] =
@selector(handleWindowHistoryBackMessage:context:); @selector(handleWindowHistoryBackMessage:context:);
(*handlers)["window.history.didReplaceState"] =
@selector(handleWindowHistoryDidReplaceStateMessage:context:);
(*handlers)["window.history.forward"] = (*handlers)["window.history.forward"] =
@selector(handleWindowHistoryForwardMessage:context:); @selector(handleWindowHistoryForwardMessage:context:);
(*handlers)["window.history.go"] = @selector(handleWindowHistoryGoMessage: (*handlers)["window.history.go"] = @selector(handleWindowHistoryGoMessage:
...@@ -2052,66 +2039,6 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*); ...@@ -2052,66 +2039,6 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
return NO; return NO;
} }
// Handles 'window.history.didReplaceState' message.
- (BOOL)handleWindowHistoryDidReplaceStateMessage:
(base::DictionaryValue*)message
context:(NSDictionary*)context {
if (![context[kIsMainFrame] boolValue])
return NO;
DCHECK(self.JSNavigationHandler.changingHistoryState);
self.JSNavigationHandler.changingHistoryState = NO;
std::string pageURL;
std::string baseURL;
if (!message->GetString("pageUrl", &pageURL) ||
!message->GetString("baseUrl", &baseURL)) {
DLOG(WARNING) << "JS message parameter not found: pageUrl or baseUrl";
return NO;
}
GURL replaceURL = web::history_state_util::GetHistoryStateChangeUrl(
[self currentURL], GURL(baseURL), pageURL);
// UIWebView seems to choke on unicode characters that haven't been
// escaped; escape the URL now so the expected load URL is correct.
replaceURL = URLEscapedForHistory(replaceURL);
if (!replaceURL.is_valid())
return YES;
web::NavigationItem* navItem = self.currentNavItem;
// ReplaceState happened before first navigation entry or called right
// after window.open when the url is empty/not valid.
if (!navItem || (self.navigationManagerImpl->GetItemCount() <= 1 &&
navItem->GetURL().is_empty()))
return YES;
if (!web::history_state_util::IsHistoryStateChangeValid(
self.currentNavItem->GetURL(), replaceURL)) {
// If the current session entry URL origin still doesn't match
// replaceURL's origin, ignore the replaceState. This can happen if a
// new URL is loaded just before the replaceState.
return YES;
}
std::string stateObjectJSON;
if (!message->GetString("stateObject", &stateObjectJSON)) {
DLOG(WARNING) << "JS message parameter not found: stateObject";
return NO;
}
NSString* stateObject = base::SysUTF8ToNSString(stateObjectJSON);
[self replaceStateWithPageURL:replaceURL
stateObject:stateObject
hasUserGesture:[context[kUserIsInteractingKey] boolValue]];
NSString* replaceStateJS =
[self.JSNavigationHandler javaScriptToReplaceWebViewURL:replaceURL
stateObjectJSON:stateObject];
__weak CRWWebController* weakSelf = self;
[_jsInjector executeJavaScript:replaceStateJS
completionHandler:^(id, NSError*) {
CRWWebController* strongSelf = weakSelf;
if (!strongSelf || strongSelf->_isBeingDestroyed)
return;
[strongSelf didFinishNavigation:nullptr];
}];
return YES;
}
// Handles 'restoresession.error' message. // Handles 'restoresession.error' message.
- (BOOL)handleRestoreSessionErrorMessage:(base::DictionaryValue*)message - (BOOL)handleRestoreSessionErrorMessage:(base::DictionaryValue*)message
context:(NSDictionary*)context { context:(NSDictionary*)context {
...@@ -2136,23 +2063,6 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*); ...@@ -2136,23 +2063,6 @@ typedef void (^ViewportStateCompletion)(const web::PageViewportState*);
#pragma mark - Navigation Helpers #pragma mark - Navigation Helpers
// Assigns the given URL and state object to the current NavigationItem.
- (void)replaceStateWithPageURL:(const GURL&)pageURL
stateObject:(NSString*)stateObject
hasUserGesture:(BOOL)hasUserGesture {
std::unique_ptr<web::NavigationContextImpl> context =
web::NavigationContextImpl::CreateNavigationContext(
self.webStateImpl, pageURL, hasUserGesture,
ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
/*is_renderer_initiated=*/true);
context->SetIsSameDocument(true);
self.webStateImpl->OnNavigationStarted(context.get());
self.navigationManagerImpl->UpdateCurrentItemForReplaceState(pageURL,
stateObject);
context->SetHasCommitted(true);
self.webStateImpl->OnNavigationFinished(context.get());
}
// Navigates forwards or backwards by |delta| pages. No-op if delta is out of // Navigates forwards or backwards by |delta| pages. No-op if delta is out of
// bounds. Reloads if delta is 0. // bounds. Reloads if delta is 0.
// TODO(crbug.com/661316): Move this method to NavigationManager. // TODO(crbug.com/661316): Move this method to NavigationManager.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment