|  | // Copyright 2021 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #import "ios/web/navigation/navigation_java_script_feature.h" | 
|  |  | 
|  | #import "base/no_destructor.h" | 
|  | #import "ios/web/public/js_messaging/java_script_feature_util.h" | 
|  | #import "ios/web/public/js_messaging/script_message.h" | 
|  | #import "ios/web/public/js_messaging/web_frame.h" | 
|  | #import "ios/web/public/js_messaging/web_frames_manager.h" | 
|  | #import "ios/web/web_state/ui/crw_web_controller.h" | 
|  | #import "ios/web/web_state/web_state_impl.h" | 
|  |  | 
|  | namespace web { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kScriptName[] = "navigation"; | 
|  | const char kListenersScriptName[] = "navigation_listeners"; | 
|  | const char kScriptHandlerName[] = "NavigationEventMessage"; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | NavigationJavaScriptFeature* NavigationJavaScriptFeature::GetInstance() { | 
|  | static base::NoDestructor<NavigationJavaScriptFeature> instance; | 
|  | return instance.get(); | 
|  | } | 
|  |  | 
|  | NavigationJavaScriptFeature::NavigationJavaScriptFeature() | 
|  | : JavaScriptFeature( | 
|  | ContentWorld::kPageContentWorld, | 
|  | {FeatureScript::CreateWithFilename( | 
|  | kScriptName, | 
|  | FeatureScript::InjectionTime::kDocumentStart, | 
|  | FeatureScript::TargetFrames::kMainFrame, | 
|  | FeatureScript::ReinjectionBehavior::kInjectOncePerWindow), | 
|  | FeatureScript::CreateWithFilename( | 
|  | kListenersScriptName, | 
|  | FeatureScript::InjectionTime::kDocumentStart, | 
|  | FeatureScript::TargetFrames::kMainFrame, | 
|  | FeatureScript::ReinjectionBehavior:: | 
|  | kReinjectOnDocumentRecreation)}, | 
|  | {web::java_script_features::GetCommonJavaScriptFeature()}) {} | 
|  |  | 
|  | NavigationJavaScriptFeature::~NavigationJavaScriptFeature() = default; | 
|  |  | 
|  | std::optional<std::string> | 
|  | NavigationJavaScriptFeature::GetScriptMessageHandlerName() const { | 
|  | return kScriptHandlerName; | 
|  | } | 
|  |  | 
|  | void NavigationJavaScriptFeature::ScriptMessageReceived( | 
|  | web::WebState* web_state, | 
|  | const web::ScriptMessage& message) { | 
|  | if (!message.body()) { | 
|  | // Ignore malformed responses. | 
|  | return; | 
|  | } | 
|  | auto* dict = message.body()->GetIfDict(); | 
|  | if (!dict) { | 
|  | // Ignore malformed responses. | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!message.is_main_frame()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const std::string* command = dict->FindString("command"); | 
|  | if (!command) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const std::string* frame_id = dict->FindString("frame_id"); | 
|  | if (!frame_id) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | WebFrame* main_frame = | 
|  | web_state->GetPageWorldWebFramesManager()->GetMainWebFrame(); | 
|  | std::string main_frame_id = main_frame ? main_frame->GetFrameId() : ""; | 
|  | if (main_frame_id != *frame_id) { | 
|  | // Frame has changed, do not send message to the web controller as it would | 
|  | // update the incorrect navigation item. | 
|  | return; | 
|  | } | 
|  |  | 
|  | CRWWebController* web_controller = | 
|  | WebStateImpl::FromWebState(web_state)->GetWebController(); | 
|  |  | 
|  | if (*command == "hashchange") { | 
|  | [web_controller handleNavigationHashChange]; | 
|  | } else if (*command == "willChangeState") { | 
|  | [web_controller handleNavigationWillChangeState]; | 
|  | } else if (*command == "didPushState") { | 
|  | [web_controller handleNavigationDidPushStateMessage:dict]; | 
|  | } else if (*command == "didReplaceState") { | 
|  | [web_controller handleNavigationDidReplaceStateMessage:dict]; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace web |