// 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(),
web::java_script_features::GetMessageJavaScriptFeature()}) {}
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