| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/web/navigation/session_restore_java_script_feature.h" |
| |
| #import "ios/web/js_messaging/web_view_js_utils.h" |
| #import "ios/web/js_messaging/web_view_web_state_map.h" |
| #include "ios/web/public/browser_state.h" |
| #import "ios/web/public/js_messaging/script_message.h" |
| #import "ios/web/public/navigation/navigation_manager.h" |
| #include "ios/web/public/web_state.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace web { |
| |
| namespace { |
| |
| // Key for storing feature on the associated BrowserState. |
| const char kSessionRestoreJavaScriptFeatureKeyName[] = |
| "session_restore_java_script_feature"; |
| |
| // Script message name for session restore. |
| NSString* const kSessionRestoreScriptHandlerName = @"session_restore"; |
| |
| } // namespace |
| |
| // static |
| SessionRestoreJavaScriptFeature* |
| SessionRestoreJavaScriptFeature::FromBrowserState(BrowserState* browser_state) { |
| DCHECK(browser_state); |
| |
| SessionRestoreJavaScriptFeature* feature = |
| static_cast<SessionRestoreJavaScriptFeature*>( |
| browser_state->GetUserData(kSessionRestoreJavaScriptFeatureKeyName)); |
| if (!feature) { |
| feature = new SessionRestoreJavaScriptFeature(browser_state); |
| browser_state->SetUserData(kSessionRestoreJavaScriptFeatureKeyName, |
| base::WrapUnique(feature)); |
| } |
| return feature; |
| } |
| |
| SessionRestoreJavaScriptFeature::SessionRestoreJavaScriptFeature( |
| BrowserState* browser_state) |
| : JavaScriptFeature( |
| // This feature operates in the page content world because the |
| // script message is sent from restore_session.html which is loaded |
| // into the webview. |
| ContentWorld::kPageContentWorld, |
| {}), |
| browser_state_(browser_state), |
| weak_factory_(this) {} |
| |
| SessionRestoreJavaScriptFeature::~SessionRestoreJavaScriptFeature() = default; |
| |
| void SessionRestoreJavaScriptFeature::ConfigureHandlers( |
| WKUserContentController* user_content_controller) { |
| // Reset the old handler first as handlers with the same name can not be |
| // added simultaneously. |
| session_restore_handler_.reset(); |
| |
| session_restore_handler_ = std::make_unique<ScopedWKScriptMessageHandler>( |
| user_content_controller, kSessionRestoreScriptHandlerName, |
| base::BindRepeating( |
| &SessionRestoreJavaScriptFeature::SessionRestorationMessageReceived, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void SessionRestoreJavaScriptFeature::SessionRestorationMessageReceived( |
| WKScriptMessage* message) { |
| WebState* web_state = WebViewWebStateMap::FromBrowserState(browser_state_) |
| ->GetWebStateForWebView(message.webView); |
| if (!web_state || |
| !web_state->GetNavigationManager()->IsRestoreSessionInProgress()) { |
| // Ignore this message if |message.webView| is no longer associated with a |
| // WebState or if session restore is not in progress. |
| return; |
| } |
| |
| if (![message.body[@"offset"] isKindOfClass:[NSNumber class]]) { |
| return; |
| } |
| NSString* method = |
| [NSString stringWithFormat:@"_crFinishSessionRestoration('%@')", |
| message.body[@"offset"]]; |
| // Don't use |CallJavaScriptFunction| here, as it relies on |windowID| being |
| // injected before window.onload starts. |
| // Note that |web::ExecuteJavaScript| assumes the page content world, which is |
| // ok in this case as restore_session.html is loaded as a webpage. |
| web::ExecuteJavaScript(message.webView, method, nil); |
| } |
| |
| } // namespace web |