0

DevTools: handle Page.reload on the browser side with RenderDocument

- when RenderDocument is on, do not fall through in Page.reload handler;
- instead, pass script_to_evaluate_on_load to InspectorPageAgent in new
    render frame at construction time via AttachDevToolsSession() method;
- handle breakpoint unpause and script termination by an out-of-band
    method of DevToolsSession.
- get rid of using InspectorAgentState for storing the script to
    evaluate on load, as the RenderDocument code path does not require
    this anymore;
- the latter also takes care of a security sensitive race between
    reload and navigation to a different URL, so
    PageReloadScriptInjection is going away too.

This resolves a race between the page reload initiated by PageHandler::reload() on the browser side and and a round-trip to renderer resulting from fall-through of PageHandler::reload() intended to set script to evaluate on load in the agent state cookie, which occurs with RenderDocument enabled.

Bug: 357977710
Change-Id: Ice796b829b56b514d38b787aea826d04d69f3c76
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6261689
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
Reviewed-by: Nate Chapin <japhet@chromium.org>
Reviewed-by: Maks Orlovich <morlovich@chromium.org>
Reviewed-by: Lucas Radaelli <lucasradaelli@google.com>
Cr-Commit-Position: refs/heads/main@{#1424660}
This commit is contained in:
Andrey Kosyakov
2025-02-25 10:58:11 -08:00
committed by Chromium LUCI CQ
parent 71e25625ce
commit 98fc1edbcc
25 changed files with 167 additions and 160 deletions

@ -197,8 +197,8 @@ void DevToolsSession::AttachToAgent(blink::mojom::DevToolsAgent* agent,
receiver_.BindNewEndpointAndPassRemote(),
session_.BindNewEndpointAndPassReceiver(),
io_session_.BindNewPipeAndPassReceiver(), session_state_cookie_.Clone(),
client_->UsesBinaryProtocol(), client_->IsTrusted(), session_id_,
IsWaitingForDebuggerOnStart());
script_to_evaluate_on_load_, client_->UsesBinaryProtocol(),
client_->IsTrusted(), session_id_, IsWaitingForDebuggerOnStart());
session_.set_disconnect_handler(base::BindOnce(
&DevToolsSession::MojoConnectionDestroyed, base::Unretained(this)));
@ -206,6 +206,9 @@ void DevToolsSession::AttachToAgent(blink::mojom::DevToolsAgent* agent,
if (!session_state_cookie_)
session_state_cookie_ = blink::mojom::DevToolsSessionState::New();
// Only use script_to_evaluate_on_load_ once.
script_to_evaluate_on_load_.clear();
// We're attaching to a new agent while suspended; therefore, messages that
// have been sent previously either need to be terminated or re-sent once we
// resume, as we will not get any responses from the old agent at this point.
@ -482,6 +485,7 @@ void DevToolsSession::ResumeSendingMessagesToAgent() {
void DevToolsSession::ClearPendingMessages(bool did_crash) {
for (auto it = pending_messages_.begin(); it != pending_messages_.end();) {
const PendingMessage& message = *it;
// TODO(caseq): remove when non-RenderDocument code paths are gone.
if (message.method == "Page.reload") {
++it;
continue;
@ -640,4 +644,9 @@ void DevToolsSession::RemoveObserver(ChildObserver* obs) {
child_observers_.RemoveObserver(obs);
}
void DevToolsSession::PrepareForReload(std::string script_to_evaluate_on_load) {
script_to_evaluate_on_load_ = std::move(script_to_evaluate_on_load);
io_session_->UnpauseAndTerminate();
}
} // namespace content

@ -129,6 +129,11 @@ class DevToolsSession : public protocol::FrontendChannel,
void AddObserver(ChildObserver* obs);
void RemoveObserver(ChildObserver* obs);
base::RepeatingCallback<void(std::string)> MakePrepareForReloadCallback() {
return base::BindRepeating(&DevToolsSession::PrepareForReload,
base::Unretained(this));
}
private:
struct PendingMessage {
int call_id;
@ -211,6 +216,7 @@ class DevToolsSession : public protocol::FrontendChannel,
std::is_same<T, protocol::WebAuthnHandler>>;
}
void AddHandler(std::unique_ptr<protocol::DevToolsDomainHandler> handler);
void PrepareForReload(std::string script_to_evaluate_on_load);
const raw_ptr<DevToolsAgentHostClient> client_;
const raw_ptr<DevToolsSession> root_session_ = nullptr;
@ -239,13 +245,13 @@ class DevToolsSession : public protocol::FrontendChannel,
// any of the waiting for response messages have been handled.
// |session_state_cookie_| is nullptr before first attach.
blink::mojom::DevToolsSessionStatePtr session_state_cookie_;
std::string script_to_evaluate_on_load_;
base::flat_map<std::string, raw_ptr<DevToolsSession, CtnExperimental>>
child_sessions_;
base::OnceClosure runtime_resume_;
raw_ptr<DevToolsExternalAgentProxyDelegate> proxy_delegate_ = nullptr;
base::ObserverList<ChildObserver, true, false> child_observers_;
base::WeakPtrFactory<DevToolsSession> weak_factory_{this};
};

@ -464,12 +464,14 @@ struct PageHandler::PendingScreenshotRequest {
gfx::Size requested_image_size;
};
PageHandler::PageHandler(EmulationHandler* emulation_handler,
BrowserHandler* browser_handler,
bool allow_unsafe_operations,
bool is_trusted,
std::optional<url::Origin> navigation_initiator_origin,
bool may_read_local_files)
PageHandler::PageHandler(
EmulationHandler* emulation_handler,
BrowserHandler* browser_handler,
bool allow_unsafe_operations,
bool is_trusted,
std::optional<url::Origin> navigation_initiator_origin,
bool may_read_local_files,
base::RepeatingCallback<void(std::string)> prepare_for_reload_callback)
: DevToolsDomainHandler(Page::Metainfo::domainName),
allow_unsafe_operations_(allow_unsafe_operations),
is_trusted_(is_trusted),
@ -484,7 +486,8 @@ PageHandler::PageHandler(EmulationHandler* emulation_handler,
frames_in_flight_(0),
host_(nullptr),
emulation_handler_(emulation_handler),
browser_handler_(browser_handler) {
browser_handler_(browser_handler),
prepare_for_reload_callback_(std::move(prepare_for_reload_callback)) {
#if BUILDFLAG(IS_ANDROID)
constexpr auto kScreencastPixelFormat = media::PIXEL_FORMAT_I420;
#else
@ -696,13 +699,27 @@ void PageHandler::Reload(std::optional<bool> bypassCache,
}
}
// It is important to fallback before triggering reload, so that
// renderer could prepare beforehand.
callback->fallThrough();
outermost_main_frame->frame_tree()->controller().Reload(
bypassCache.value_or(false) ? ReloadType::BYPASSING_CACHE
: ReloadType::NORMAL,
false);
const auto reload_type = bypassCache.value_or(false)
? ReloadType::BYPASSING_CACHE
: ReloadType::NORMAL;
NavigationController& navigation_controller =
outermost_main_frame->frame_tree()->controller();
// For RenderDocument, the path is different: we don't fall through to the
// renderer and process `scriptToEvaluateOnLoad` in the browser instead.
const bool handle_reload_on_browser_side =
outermost_main_frame->ShouldChangeRenderFrameHostOnSameSiteNavigation();
if (handle_reload_on_browser_side) {
have_pending_reload_ = true;
pending_script_to_evaluate_on_load_ =
script_to_evaluate_on_load.value_or("");
navigation_controller.Reload(reload_type, false);
callback->sendSuccess();
} else {
// It is important to fallback before triggering reload, so that
// renderer could prepare beforehand.
callback->fallThrough();
navigation_controller.Reload(reload_type, false);
}
}
static network::mojom::ReferrerPolicy ParsePolicyFromString(
@ -2295,6 +2312,20 @@ void PageHandler::IsPrerenderingAllowed(bool& is_allowed) {
is_allowed &= is_prerendering_allowed_;
}
void PageHandler::ReadyToCommitNavigation(
NavigationRequest* navigation_request) {
if (navigation_request->GetReloadType() == ReloadType::NONE) {
// Disregard pending reload if the navigation being committed is not a
// reload.
have_pending_reload_ = false;
pending_script_to_evaluate_on_load_.clear();
} else if (have_pending_reload_) {
prepare_for_reload_callback_.Run(
std::move(pending_script_to_evaluate_on_load_));
have_pending_reload_ = false;
}
}
Response PageHandler::SetPrerenderingAllowed(bool is_allowed) {
Response response = AssureTopLevelActiveFrame();
if (response.IsError()) {

@ -62,12 +62,14 @@ class PageHandler : public DevToolsDomainHandler,
public RenderWidgetHostObserver,
public download::DownloadItem::Observer {
public:
PageHandler(EmulationHandler* emulation_handler,
BrowserHandler* browser_handler,
bool allow_unsafe_operations,
bool is_trusted,
std::optional<url::Origin> navigation_initiator_origin,
bool may_read_local_files);
PageHandler(
EmulationHandler* emulation_handler,
BrowserHandler* browser_handler,
bool allow_unsafe_operations,
bool is_trusted,
std::optional<url::Origin> navigation_initiator_origin,
bool may_read_local_files,
base::RepeatingCallback<void(std::string)> prepare_for_reload_callback);
PageHandler(const PageHandler&) = delete;
PageHandler& operator=(const PageHandler&) = delete;
@ -113,6 +115,7 @@ class PageHandler : public DevToolsDomainHandler,
const BackForwardCacheCanStoreTreeResult* tree_result);
void IsPrerenderingAllowed(bool& is_allowed);
void ReadyToCommitNavigation(NavigationRequest* navigation_request);
Response Enable(
std::optional<bool> enable_file_chooser_opened_event) override;
@ -270,6 +273,9 @@ class PageHandler : public DevToolsDomainHandler,
pending_downloads_;
bool is_prerendering_allowed_ = true;
base::RepeatingCallback<void(std::string)> prepare_for_reload_callback_;
bool have_pending_reload_ = false;
std::string pending_script_to_evaluate_on_load_;
base::WeakPtrFactory<PageHandler> weak_factory_{this};
};

@ -391,7 +391,8 @@ bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) {
session->GetClient()->AllowUnsafeOperations(),
session->GetClient()->IsTrusted(),
session->GetClient()->GetNavigationInitiatorOrigin(),
session->GetClient()->MayReadLocalFiles());
session->GetClient()->MayReadLocalFiles(),
session->MakePrepareForReloadCallback());
session->CreateAndAddHandler<protocol::SecurityHandler>();
if (!frame_tree_node_ || !frame_tree_node_->parent()) {
DevToolsSession* root_session = session->GetRootSession();
@ -456,8 +457,12 @@ void RenderFrameDevToolsAgentHost::ReadyToCommitNavigation(
return;
}
NavigationRequest* request = NavigationRequest::From(navigation_handle);
for (auto* tracing : protocol::TracingHandler::ForAgentHost(this))
for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) {
tracing->ReadyToCommitNavigation(request);
}
for (auto* page : protocol::PageHandler::ForAgentHost(this)) {
page->ReadyToCommitNavigation(request);
}
if (request->frame_tree_node() != frame_tree_node_) {
if (ShouldForceCreation() && request->GetRenderFrameHost() &&

@ -68,11 +68,13 @@ void AuctionV8DevToolsAgent::AttachDevToolsSession(
session_receiver,
mojo::PendingReceiver<blink::mojom::DevToolsSession> io_session_receiver,
blink::mojom::DevToolsSessionStatePtr reattach_session_state,
const std::string& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const std::string& session_id,
bool session_waits_for_debugger) {
DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
CHECK(script_to_evaluate_on_load.empty()); // This is for frames only so far.
int context_group_id = receivers_.current_context();
ContextGroupInfo& context_group_info = context_groups_[context_group_id];

@ -104,6 +104,7 @@ class AuctionV8DevToolsAgent : public blink::mojom::DevToolsAgent,
session_receiver,
mojo::PendingReceiver<blink::mojom::DevToolsSession> io_session_receiver,
blink::mojom::DevToolsSessionStatePtr reattach_session_state,
const std::string& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const std::string& session_id,

@ -129,6 +129,8 @@ class AuctionV8DevToolsSession::IOSession
std::vector<uint8_t>(message.begin(), message.end())));
}
void UnpauseAndTerminate() override { NOTREACHED(); }
private:
IOSession(scoped_refptr<DebugCommandQueue> debug_command_queue,
RunDispatch v8_thread_dispatch)
@ -255,6 +257,11 @@ void AuctionV8DevToolsSession::DispatchProtocolCommand(
}
}
void AuctionV8DevToolsSession::UnpauseAndTerminate() {
// This is currently only invoked for frame targets.
NOTREACHED();
}
void AuctionV8DevToolsSession::sendResponse(
int call_id,
std::unique_ptr<v8_inspector::StringBuffer> message) {

@ -86,6 +86,7 @@ class AuctionV8DevToolsSession : public blink::mojom::DevToolsSession,
void DispatchProtocolCommand(int32_t call_id,
const std::string& method,
base::span<const uint8_t> message) override;
void UnpauseAndTerminate() override;
// V8Inspector::Channel implementation:
void sendResponse(

@ -51,7 +51,8 @@ TestDevToolsAgentClient::TestDevToolsAgentClient(
agent_->AttachDevToolsSession(receiver_.BindNewEndpointAndPassRemote(),
session_.BindNewEndpointAndPassReceiver(),
io_session_.BindNewPipeAndPassReceiver(),
nullptr, use_binary_protocol_,
nullptr, /*script_to_evaluate_on_load*/ "",
use_binary_protocol_,
/*client_is_trusted=*/true, session_id_,
/*session_waits_for_debugger=*/false);
}

@ -67,11 +67,13 @@ void OSDevToolsAgent::AttachDevToolsSession(
session_receiver,
mojo::PendingReceiver<blink::mojom::DevToolsSession> io_session_receiver,
blink::mojom::DevToolsSessionStatePtr reattach_session_state,
const std::string& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const std::string& session_id,
bool session_waits_for_debugger) {
DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
CHECK(script_to_evaluate_on_load.empty()); // This is for frames only so far.
auto session_destroyed_callback = base::BindOnce(
&OSDevToolsAgent::SessionDestroyed, weak_ptr_factory_.GetWeakPtr());

@ -53,6 +53,7 @@ class OSDevToolsAgent : public blink::mojom::DevToolsAgent,
session_receiver,
mojo::PendingReceiver<blink::mojom::DevToolsSession> io_session_receiver,
blink::mojom::DevToolsSessionStatePtr reattach_session_state,
const std::string& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const std::string& session_id,

@ -80,6 +80,8 @@ class OSDevToolsSession::IOSession : public blink::mojom::DevToolsSession {
std::vector<uint8_t>(message.begin(), message.end())));
}
void UnpauseAndTerminate() override { NOTREACHED(); }
private:
IOSession(const scoped_refptr<DebugCommandQueue> debug_command_queue,
DispatchCallback v8_thread_dispatch)
@ -200,6 +202,10 @@ void OSDevToolsSession::DispatchProtocolCommand(
}
}
void OSDevToolsSession::UnpauseAndTerminate() {
NOTREACHED();
}
void OSDevToolsSession::sendResponse(
int call_id,
std::unique_ptr<v8_inspector::StringBuffer> message) {

@ -63,6 +63,7 @@ class OSDevToolsSession : public blink::mojom::DevToolsSession,
void DispatchProtocolCommand(int32_t call_id,
const std::string& method,
base::span<const uint8_t> message) override;
void UnpauseAndTerminate() override;
// V8Inspector::Channel
void sendResponse(

@ -270,7 +270,8 @@ class OSDevToolsTest : public testing::Test {
fake_session_host_.receiver.BindNewEndpointAndPassRemote(),
session_remote_.BindNewEndpointAndPassReceiver(),
io_session_remote_.BindNewPipeAndPassReceiver(),
std::move(reattach_session_state_), /*client_expects_binary*/ true,
std::move(reattach_session_state_), /*script_to_evaluate_on_load=*/"",
/*client_expects_binary=*/true,
/*client_is_trusted=*/true, /*session_id=*/"session",
/*session_waits_for_debugger=*/false);
}

@ -99,6 +99,7 @@ interface DevToolsAgent {
pending_associated_receiver<DevToolsSession> session,
pending_receiver<DevToolsSession> io_session,
DevToolsSessionState? reattach_session_state,
string script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
string session_id,
@ -167,6 +168,9 @@ interface DevToolsSession {
DispatchProtocolCommand(int32 call_id,
string method,
mojo_base.mojom.ReadOnlyBuffer message);
// Disables all breakpoints and terminates script currently executing.
UnpauseAndTerminate();
};
// A peer of DevToolsSession representing a remote debugging client

@ -358,7 +358,7 @@ void WebDevToolsAgentImpl::AttachSession(DevToolsSession* session,
auto* page_agent = session->CreateAndAppend<InspectorPageAgent>(
inspected_frames, this, resource_content_loader_.Get(),
session->V8Session());
session->V8Session(), session->script_to_evaluate_on_load());
session->CreateAndAppend<InspectorLogAgent>(
&inspected_frames->Root()->GetPage()->GetConsoleMessageStorage(),

@ -149,7 +149,6 @@ blink_core_tests_inspector = [
"inspector_ghost_rules_test.cc",
"inspector_highlight_test.cc",
"inspector_history_test.cc",
"inspector_page_agent_unittest.cc",
"inspector_preload_agent_unittest.cc",
"inspector_media_context_impl_unittest.cc",
"inspector_session_state_test.cc",

@ -118,6 +118,7 @@ class DevToolsAgent::IOAgent : public mojom::blink::DevToolsAgent {
main_session,
mojo::PendingReceiver<mojom::blink::DevToolsSession> io_session,
mojom::blink::DevToolsSessionStatePtr reattach_session_state,
const WTF::String& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const WTF::String& session_id,
@ -128,8 +129,9 @@ class DevToolsAgent::IOAgent : public mojom::blink::DevToolsAgent {
&::blink::DevToolsAgent::AttachDevToolsSessionImpl,
MakeUnwrappingCrossThreadWeakHandle(agent_), std::move(host),
std::move(main_session), std::move(io_session),
std::move(reattach_session_state), client_expects_binary_responses,
client_is_trusted, session_id, session_waits_for_debugger));
std::move(reattach_session_state), script_to_evaluate_on_load,
client_expects_binary_responses, client_is_trusted, session_id,
session_waits_for_debugger));
}
void InspectElement(const gfx::Point& point) override {
@ -257,6 +259,7 @@ void DevToolsAgent::AttachDevToolsSessionImpl(
session_receiver,
mojo::PendingReceiver<mojom::blink::DevToolsSession> io_session_receiver,
mojom::blink::DevToolsSessionStatePtr reattach_session_state,
const WTF::String& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const WTF::String& session_id,
@ -266,8 +269,8 @@ void DevToolsAgent::AttachDevToolsSessionImpl(
DevToolsSession* session = MakeGarbageCollected<DevToolsSession>(
this, std::move(host), std::move(session_receiver),
std::move(io_session_receiver), std::move(reattach_session_state),
client_expects_binary_responses, client_is_trusted, session_id,
session_waits_for_debugger,
script_to_evaluate_on_load, client_expects_binary_responses,
client_is_trusted, session_id, session_waits_for_debugger,
// crbug.com/333093232: Mojo ignores the task runner passed to Bind for
// channel associated interfaces but uses it for disconnect. Since
// devtools relies on a disconnect handler for detaching and is sensitive
@ -293,6 +296,7 @@ void DevToolsAgent::AttachDevToolsSession(
session_receiver,
mojo::PendingReceiver<mojom::blink::DevToolsSession> io_session_receiver,
mojom::blink::DevToolsSessionStatePtr reattach_session_state,
const WTF::String& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const WTF::String& session_id,
@ -304,14 +308,15 @@ void DevToolsAgent::AttachDevToolsSession(
AttachDevToolsSessionImpl(
std::move(host), std::move(session_receiver),
std::move(io_session_receiver), std::move(reattach_session_state),
client_expects_binary_responses, client_is_trusted, session_id,
script_to_evaluate_on_load, client_expects_binary_responses,
client_is_trusted, session_id,
/* session_waits_for_debugger */ false);
} else {
io_agent_->AttachDevToolsSession(
std::move(host), std::move(session_receiver),
std::move(io_session_receiver), std::move(reattach_session_state),
client_expects_binary_responses, client_is_trusted, session_id,
session_waits_for_debugger);
script_to_evaluate_on_load, client_expects_binary_responses,
client_is_trusted, session_id, session_waits_for_debugger);
}
}

@ -98,6 +98,7 @@ class CORE_EXPORT DevToolsAgent : public GarbageCollected<DevToolsAgent>,
main_session,
mojo::PendingReceiver<mojom::blink::DevToolsSession> io_session,
mojom::blink::DevToolsSessionStatePtr reattach_session_state,
const WTF::String& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const WTF::String& session_id,
@ -130,6 +131,7 @@ class CORE_EXPORT DevToolsAgent : public GarbageCollected<DevToolsAgent>,
main_session,
mojo::PendingReceiver<mojom::blink::DevToolsSession> io_session,
mojom::blink::DevToolsSessionStatePtr reattach_session_state,
const WTF::String& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const WTF::String& session_id,

@ -128,6 +128,12 @@ class DevToolsSession::IOSession : public mojom::blink::DevToolsSession {
}
}
void UnpauseAndTerminate() override {
inspector_task_runner_->AppendTask(
CrossThreadBindOnce(&::blink::DevToolsSession::UnpauseAndTerminate,
MakeUnwrappingCrossThreadWeakHandle(session_)));
}
private:
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
scoped_refptr<InspectorTaskRunner> inspector_task_runner_;
@ -143,6 +149,7 @@ DevToolsSession::DevToolsSession(
main_receiver,
mojo::PendingReceiver<mojom::blink::DevToolsSession> io_receiver,
mojom::blink::DevToolsSessionStatePtr reattach_session_state,
const String& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const String& session_id,
@ -155,6 +162,7 @@ DevToolsSession::DevToolsSession(
client_is_trusted_(client_is_trusted),
v8_session_state_(kV8StateKey),
v8_session_state_cbor_(&v8_session_state_, /*default_value=*/{}),
script_to_evaluate_on_load_(script_to_evaluate_on_load),
session_id_(session_id),
session_waits_for_debugger_(session_waits_for_debugger) {
receiver_.Bind(std::move(main_receiver), mojo_task_runner);
@ -277,9 +285,8 @@ void DevToolsSession::DispatchProtocolCommandImpl(
}
void DevToolsSession::DidStartProvisionalLoad(LocalFrame* frame) {
if (v8_session_ && agent_->inspected_frames_->Root() == frame) {
v8_session_->setSkipAllPauses(true);
v8_session_->resume(true /* terminate on resume */);
if (agent_->inspected_frames_->Root() == frame) {
UnpauseAndTerminate();
}
}
@ -433,4 +440,12 @@ blink::mojom::blink::DevToolsMessagePtr DevToolsSession::FinalizeMessage(
return mojo_msg;
}
void DevToolsSession::UnpauseAndTerminate() {
if (!v8_session_) {
return;
}
v8_session_->setSkipAllPauses(true);
v8_session_->resume(true /* terminate on resume */);
}
} // namespace blink

@ -62,6 +62,7 @@ class CORE_EXPORT DevToolsSession : public GarbageCollected<DevToolsSession>,
main_receiver,
mojo::PendingReceiver<mojom::blink::DevToolsSession> io_receiver,
mojom::blink::DevToolsSessionStatePtr reattach_session_state,
const String& script_to_evaluate_on_load,
bool client_expects_binary_responses,
bool client_is_trusted,
const String& session_id,
@ -97,6 +98,10 @@ class CORE_EXPORT DevToolsSession : public GarbageCollected<DevToolsSession>,
void PaintTiming(Document* document, const char* name, double timestamp);
void DomContentLoadedEventFired(LocalFrame*);
const String& script_to_evaluate_on_load() const {
return script_to_evaluate_on_load_;
}
private:
class IOSession;
@ -104,6 +109,8 @@ class CORE_EXPORT DevToolsSession : public GarbageCollected<DevToolsSession>,
void DispatchProtocolCommand(int call_id,
const String& method,
base::span<const uint8_t> message) override;
void UnpauseAndTerminate() override;
void DispatchProtocolCommandImpl(int call_id,
const String& method,
base::span<const uint8_t> message);
@ -174,6 +181,7 @@ class CORE_EXPORT DevToolsSession : public GarbageCollected<DevToolsSession>,
const bool client_is_trusted_;
InspectorAgentState v8_session_state_;
InspectorAgentState::Bytes v8_session_state_cbor_;
String script_to_evaluate_on_load_;
const String session_id_;
// This is only relevant until the initial attach to v8 and is never reset
// once the session stops waiting.

@ -481,47 +481,12 @@ String InspectorPageAgent::CachedResourceTypeJson(
return ResourceTypeJson(ToResourceType(cached_resource.GetType()));
}
InspectorPageAgent::PageReloadScriptInjection::PageReloadScriptInjection(
InspectorAgentState& agent_state)
: pending_script_to_evaluate_on_load_once_(&agent_state,
/*default_value=*/{}),
target_url_for_pending_script_(&agent_state,
/*default_value=*/{}) {}
void InspectorPageAgent::PageReloadScriptInjection::clear() {
script_to_evaluate_on_load_once_ = {};
pending_script_to_evaluate_on_load_once_.Set({});
target_url_for_pending_script_.Set({});
}
void InspectorPageAgent::PageReloadScriptInjection::SetPending(
String script,
const KURL& target_url) {
pending_script_to_evaluate_on_load_once_.Set(script);
target_url_for_pending_script_.Set(target_url.GetString().GetString());
}
void InspectorPageAgent::PageReloadScriptInjection::PromoteToLoadOnce() {
script_to_evaluate_on_load_once_ =
pending_script_to_evaluate_on_load_once_.Get();
target_url_for_active_script_ = target_url_for_pending_script_.Get();
pending_script_to_evaluate_on_load_once_.Set({});
target_url_for_pending_script_.Set({});
}
String InspectorPageAgent::PageReloadScriptInjection::GetScriptForInjection(
const KURL& target_url) {
if (target_url_for_active_script_ == target_url.GetString()) {
return script_to_evaluate_on_load_once_;
}
return {};
}
InspectorPageAgent::InspectorPageAgent(
InspectedFrames* inspected_frames,
Client* client,
InspectorResourceContentLoader* resource_content_loader,
v8_inspector::V8InspectorSession* v8_session)
v8_inspector::V8InspectorSession* v8_session,
const String& script_to_evaluate_on_load)
: inspected_frames_(inspected_frames),
v8_session_(v8_session),
client_(client),
@ -545,7 +510,7 @@ InspectorPageAgent::InspectorPageAgent(
standard_font_size_(&agent_state_, /*default_value=*/0),
fixed_font_size_(&agent_state_, /*default_value=*/0),
script_font_families_cbor_(&agent_state_, std::vector<uint8_t>()),
script_injection_on_load_(agent_state_) {}
pending_script_injection_on_load_(script_to_evaluate_on_load) {}
void InspectorPageAgent::Restore() {
if (enabled_.Get()) {
@ -591,7 +556,8 @@ protocol::Response InspectorPageAgent::enable(
protocol::Response InspectorPageAgent::disable() {
agent_state_.ClearAllFields();
pending_isolated_worlds_.clear();
script_injection_on_load_.clear();
script_injection_on_load_once_ = {};
pending_script_injection_on_load_ = {};
instrumenting_agents_->RemoveInspectorPageAgent(this);
inspector_resource_content_loader_->Cancel(
resource_content_loader_client_id_);
@ -729,9 +695,8 @@ protocol::Response InspectorPageAgent::reload(
.ToString() != loader_id->Ascii()) {
return protocol::Response::InvalidParams("Document already navigated");
}
script_injection_on_load_.SetPending(
optional_script_to_evaluate_on_load.value_or(""),
inspected_frames_->Root()->Loader().GetDocumentLoader()->Url());
pending_script_injection_on_load_ =
optional_script_to_evaluate_on_load.value_or("");
v8_session_->setSkipAllPauses(true);
v8_session_->resume(true /* terminate on resume */);
return protocol::Response::Success();
@ -1096,11 +1061,10 @@ void InspectorPageAgent::DidCreateMainWorldContext(LocalFrame* frame) {
EvaluateScriptOnNewDocument(*frame, key);
}
String script = script_injection_on_load_.GetScriptForInjection(
frame->Loader().GetDocumentLoader()->Url());
if (script.empty()) {
if (script_injection_on_load_once_.empty()) {
return;
}
String script = std::move(script_injection_on_load_once_);
ScriptState* script_state = ToScriptStateForMainWorld(frame);
if (!script_state || !v8_session_) {
return;
@ -1159,7 +1123,8 @@ void InspectorPageAgent::LoadEventFired(LocalFrame* frame) {
void InspectorPageAgent::WillCommitLoad(LocalFrame*, DocumentLoader* loader) {
if (loader->GetFrame() == inspected_frames_->Root()) {
script_injection_on_load_.PromoteToLoadOnce();
script_injection_on_load_once_ =
std::move(pending_script_injection_on_load_);
}
GetFrontend()->frameNavigated(BuildObjectForFrame(loader->GetFrame()),
protocol::Page::NavigationTypeEnum::Navigation);

@ -93,22 +93,6 @@ class CORE_EXPORT InspectorPageAgent final
kOtherResource
};
class CORE_EXPORT PageReloadScriptInjection {
private:
String script_to_evaluate_on_load_once_;
String target_url_for_active_script_;
InspectorAgentState::String pending_script_to_evaluate_on_load_once_;
InspectorAgentState::String target_url_for_pending_script_;
public:
explicit PageReloadScriptInjection(InspectorAgentState&);
void clear();
void SetPending(String script, const KURL& target_url);
void PromoteToLoadOnce();
String GetScriptForInjection(const KURL& target_url);
};
static bool CachedResourceContent(const Resource*,
String* result,
bool* base64_encoded,
@ -126,7 +110,8 @@ class CORE_EXPORT InspectorPageAgent final
InspectorPageAgent(InspectedFrames*,
Client*,
InspectorResourceContentLoader*,
v8_inspector::V8InspectorSession*);
v8_inspector::V8InspectorSession*,
const String& script_to_evaluate_on_load);
InspectorPageAgent(const InspectorPageAgent&) = delete;
InspectorPageAgent& operator=(const InspectorPageAgent&) = delete;
@ -356,7 +341,8 @@ class CORE_EXPORT InspectorPageAgent final
InspectorAgentState::Integer standard_font_size_;
InspectorAgentState::Integer fixed_font_size_;
InspectorAgentState::Bytes script_font_families_cbor_;
PageReloadScriptInjection script_injection_on_load_;
String script_injection_on_load_once_;
String pending_script_injection_on_load_;
};
} // namespace blink

@ -1,57 +0,0 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/inspector/inspector_page_agent.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/inspector/inspector_session_state.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
class PageReloadScriptInjectionTest : public testing::Test {
protected:
blink::mojom::blink::DevToolsSessionStatePtr session_state_cookie_;
blink::InspectorAgentState agent_state_;
blink::InspectorPageAgent::PageReloadScriptInjection injection_;
blink::InspectorSessionState state_;
public:
PageReloadScriptInjectionTest()
: agent_state_("page"),
injection_(agent_state_),
state_(session_state_cookie_.Clone()) {}
void SetUp() override { agent_state_.InitFrom(&state_); }
};
TEST_F(PageReloadScriptInjectionTest, PromotesScript) {
blink::KURL url("http://example.com");
injection_.SetPending("script", url);
ASSERT_TRUE(injection_.GetScriptForInjection(url).empty());
injection_.PromoteToLoadOnce();
ASSERT_EQ(injection_.GetScriptForInjection(url), "script");
injection_.PromoteToLoadOnce();
ASSERT_TRUE(injection_.GetScriptForInjection(url).empty());
}
TEST_F(PageReloadScriptInjectionTest, ClearsScript) {
blink::KURL url("http://example.com");
injection_.SetPending("script", url);
injection_.clear();
injection_.PromoteToLoadOnce();
ASSERT_TRUE(injection_.GetScriptForInjection(url).empty());
injection_.SetPending("script", url);
injection_.PromoteToLoadOnce();
ASSERT_EQ(injection_.GetScriptForInjection(url), "script");
injection_.clear();
ASSERT_TRUE(injection_.GetScriptForInjection(url).empty());
}
TEST_F(PageReloadScriptInjectionTest, ChecksLoaderId) {
blink::KURL url("http://example.com");
blink::KURL url2("about:blank");
injection_.SetPending("script", url);
injection_.PromoteToLoadOnce();
ASSERT_TRUE(injection_.GetScriptForInjection(url2).empty());
}