Commit b7484adf authored by Joey Arhar's avatar Joey Arhar Committed by Commit Bot

[DevTools] Emit Network.*ExtraInfo events for service workers

This patch emits ExtraInfo events for service worker main and
subresource requests by setting the render_frame_id of all
network::ResourceRequests made by service workers from -2 to a routing
id for DevTools which is already sent from the browser process to the
renderer process when creating a service worker. Setting the
render_frame_id allows us to identify which service worker a request
came from in the browser process through NetworkServiceClient.

Bug: 991471
Change-Id: I8031644c10cba177917e5b6bc25e4ada088d95cb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1746204
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#700871}
parent 05ebc8ad
......@@ -51,6 +51,22 @@ void DispatchToAgents(int frame_tree_node_id,
DispatchToAgents(ftn, method, std::forward<Args>(args)...);
}
template <typename Handler, typename... MethodArgs, typename... Args>
void DispatchToWorkerAgents(int32_t worker_process_id,
int32_t worker_route_id,
void (Handler::*method)(MethodArgs...),
Args&&... args) {
ServiceWorkerDevToolsAgentHost* service_worker_host =
ServiceWorkerDevToolsManager::GetInstance()
->GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id);
if (!service_worker_host)
return;
for (auto* h : Handler::ForAgentHost(service_worker_host))
(h->*method)(std::forward<Args>(args)...);
// TODO(crbug.com/1004979): Look for shared worker hosts here as well.
}
FrameTreeNode* GetFtnForNetworkRequest(int process_id, int routing_id) {
// Navigation requests start in the browser, before process_id is assigned, so
// the id is set to 0. In these situations, the routing_id is the frame tree
......@@ -451,11 +467,22 @@ void OnRequestWillBeSentExtraInfo(
const net::CookieStatusList& request_cookie_list,
const std::vector<network::mojom::HttpRawHeaderPairPtr>& request_headers) {
FrameTreeNode* ftn = GetFtnForNetworkRequest(process_id, routing_id);
if (!ftn)
if (ftn) {
DispatchToAgents(ftn,
&protocol::NetworkHandler::OnRequestWillBeSentExtraInfo,
devtools_request_id, request_cookie_list, request_headers);
return;
}
DispatchToAgents(ftn, &protocol::NetworkHandler::OnRequestWillBeSentExtraInfo,
devtools_request_id, request_cookie_list, request_headers);
// In the case of service worker network requests, there is no
// FrameTreeNode to use so instead we use the "routing_id" created with the
// worker and sent to the renderer process to send as the render_frame_id in
// the renderer's network::ResourceRequest which gets plubmed to here as
// routing_id.
DispatchToWorkerAgents(
process_id, routing_id,
&protocol::NetworkHandler::OnRequestWillBeSentExtraInfo,
devtools_request_id, request_cookie_list, request_headers);
}
void OnResponseReceivedExtraInfo(
......@@ -466,12 +493,19 @@ void OnResponseReceivedExtraInfo(
const std::vector<network::mojom::HttpRawHeaderPairPtr>& response_headers,
const base::Optional<std::string>& response_headers_text) {
FrameTreeNode* ftn = GetFtnForNetworkRequest(process_id, routing_id);
if (!ftn)
if (ftn) {
DispatchToAgents(ftn,
&protocol::NetworkHandler::OnResponseReceivedExtraInfo,
devtools_request_id, response_cookie_list,
response_headers, response_headers_text);
return;
}
DispatchToAgents(ftn, &protocol::NetworkHandler::OnResponseReceivedExtraInfo,
devtools_request_id, response_cookie_list, response_headers,
response_headers_text);
// See comment on DispatchToWorkerAgents in OnRequestWillBeSentExtraInfo.
DispatchToWorkerAgents(process_id, routing_id,
&protocol::NetworkHandler::OnResponseReceivedExtraInfo,
devtools_request_id, response_cookie_list,
response_headers, response_headers_text);
}
} // namespace devtools_instrumentation
......
......@@ -210,15 +210,13 @@ void SetupOnUIThread(
}
// Register to DevTools and update params accordingly.
// TODO(dgozman): we can now remove this routing id and use something else
// as id when talking to ServiceWorkerDevToolsManager.
const int routing_id = rph->GetNextRoutingID();
ServiceWorkerDevToolsManager::GetInstance()->WorkerCreated(
process_id, routing_id, context, weak_context,
params->service_worker_version_id, params->script_url, params->scope,
params->is_installed, &params->devtools_worker_token,
&params->wait_for_debugger);
params->worker_devtools_agent_route_id = routing_id;
params->service_worker_route_id = routing_id;
// Create DevToolsProxy here to ensure that the WorkerCreated() call is
// balanced by DevToolsProxy's destructor calling WorkerDestroyed().
devtools_proxy = std::make_unique<EmbeddedWorkerInstance::DevToolsProxy>(
......@@ -718,7 +716,8 @@ void EmbeddedWorkerInstance::Start(
for (auto& observer : listener_list_)
observer.OnStarting();
params->worker_devtools_agent_route_id = MSG_ROUTING_NONE;
// service_worker_route_id will be set later in SetupOnUIThread
params->service_worker_route_id = MSG_ROUTING_NONE;
params->wait_for_debugger = false;
params->subresource_loader_updater =
subresource_loader_updater_.BindNewPipeAndPassReceiver();
......
......@@ -89,7 +89,8 @@ void EmbeddedWorkerInstanceClientImpl::StartWorker(
std::move(params->preference_watcher_request),
std::move(params->subresource_loader_factories),
std::move(params->subresource_loader_updater),
params->script_url_to_skip_throttling, initiator_thread_task_runner_);
params->script_url_to_skip_throttling, initiator_thread_task_runner_,
params->service_worker_route_id);
// Record UMA to indicate StartWorker is received on renderer.
StartWorkerHistogramEnum metric =
params->is_installed ? StartWorkerHistogramEnum::RECEIVED_ON_INSTALLED
......
......@@ -110,7 +110,8 @@ ServiceWorkerContextClient::ServiceWorkerContextClient(
mojo::PendingReceiver<blink::mojom::SubresourceLoaderUpdater>
subresource_loader_updater,
const GURL& script_url_to_skip_throttling,
scoped_refptr<base::SingleThreadTaskRunner> initiator_thread_task_runner)
scoped_refptr<base::SingleThreadTaskRunner> initiator_thread_task_runner,
int32_t service_worker_route_id)
: service_worker_version_id_(service_worker_version_id),
service_worker_scope_(service_worker_scope),
script_url_(script_url),
......@@ -125,7 +126,8 @@ ServiceWorkerContextClient::ServiceWorkerContextClient(
pending_subresource_loader_updater_(
std::move(subresource_loader_updater)),
owner_(owner),
start_timing_(std::move(start_timing)) {
start_timing_(std::move(start_timing)),
service_worker_route_id_(service_worker_route_id) {
DCHECK(initiator_thread_task_runner_->RunsTasksInCurrentSequence());
DCHECK(owner_);
DCHECK(subresource_loaders);
......@@ -386,7 +388,7 @@ ServiceWorkerContextClient::CreateWorkerFetchContextOnInitiatorThread() {
->renderer()
->CreateWebSocketHandshakeThrottleProvider(),
std::move(preference_watcher_receiver_),
std::move(pending_subresource_loader_updater_));
std::move(pending_subresource_loader_updater_), service_worker_route_id_);
}
void ServiceWorkerContextClient::OnNavigationPreloadResponse(
......
......@@ -108,7 +108,8 @@ class CONTENT_EXPORT ServiceWorkerContextClient
mojo::PendingReceiver<blink::mojom::SubresourceLoaderUpdater>
subresource_loader_updater,
const GURL& script_url_to_skip_throttling,
scoped_refptr<base::SingleThreadTaskRunner> initiator_thread_task_runner);
scoped_refptr<base::SingleThreadTaskRunner> initiator_thread_task_runner,
int32_t service_worker_route_id);
// Called on the initiator thread.
~ServiceWorkerContextClient() override;
......@@ -276,6 +277,8 @@ class CONTENT_EXPORT ServiceWorkerContextClient
std::unique_ptr<blink::WebEmbeddedWorker> worker_;
int32_t service_worker_route_id_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContextClient);
};
......
......@@ -33,7 +33,8 @@ ServiceWorkerFetchContextImpl::ServiceWorkerFetchContextImpl(
mojo::PendingReceiver<blink::mojom::RendererPreferenceWatcher>
preference_watcher_receiver,
mojo::PendingReceiver<blink::mojom::SubresourceLoaderUpdater>
pending_subresource_loader_updater)
pending_subresource_loader_updater,
int32_t service_worker_route_id)
: renderer_preferences_(renderer_preferences),
worker_script_url_(worker_script_url),
url_loader_factory_info_(std::move(url_loader_factory_info)),
......@@ -45,7 +46,8 @@ ServiceWorkerFetchContextImpl::ServiceWorkerFetchContextImpl(
preference_watcher_pending_receiver_(
std::move(preference_watcher_receiver)),
pending_subresource_loader_updater_(
std::move(pending_subresource_loader_updater)) {}
std::move(pending_subresource_loader_updater)),
service_worker_route_id_(service_worker_route_id) {}
ServiceWorkerFetchContextImpl::~ServiceWorkerFetchContextImpl() {}
......@@ -110,6 +112,7 @@ void ServiceWorkerFetchContextImpl::WillSendRequest(
}
auto extra_data = std::make_unique<RequestExtraData>();
extra_data->set_originated_from_service_worker(true);
extra_data->set_render_frame_id(service_worker_route_id_);
const bool needs_to_skip_throttling =
static_cast<GURL>(request.Url()) == script_url_to_skip_throttling_ &&
......
......@@ -50,7 +50,8 @@ class CONTENT_EXPORT ServiceWorkerFetchContextImpl final
mojo::PendingReceiver<blink::mojom::RendererPreferenceWatcher>
preference_watcher_receiver,
mojo::PendingReceiver<blink::mojom::SubresourceLoaderUpdater>
pending_subresource_loader_updater);
pending_subresource_loader_updater,
int32_t service_worker_route_id);
// blink::WebWorkerFetchContext implementation:
void SetTerminateSyncLoadEvent(base::WaitableEvent*) override;
......@@ -123,6 +124,8 @@ class CONTENT_EXPORT ServiceWorkerFetchContextImpl final
base::WaitableEvent* terminate_sync_load_event_ = nullptr;
blink::AcceptLanguagesWatcher* accept_languages_watcher_ = nullptr;
int32_t service_worker_route_id_;
};
} // namespace content
......
......@@ -48,7 +48,8 @@ TEST_F(ServiceWorkerFetchContextImplTest, SkipThrottling) {
/*script_loader_factory_info=*/nullptr, kScriptUrlToSkipThrottling,
std::make_unique<FakeURLLoaderThrottleProvider>(),
/*websocket_handshake_throttle_provider=*/nullptr, mojo::NullReceiver(),
mojo::NullReceiver());
mojo::NullReceiver(),
/*service_worker_route_id=*/-1);
{
// Call WillSendRequest() for kScriptURL.
......
......@@ -253,11 +253,10 @@ struct URLRequest {
// created by a frame.
// - MSG_ROUTING_NONE for nested dedicated workers.
// - MSG_ROUTING_NONE for subresource requests from a dedicated/shared worker.
// - service_worker_route_id from EmbeddedWorkerStartParams for service worker
// main script requests and subresource requests.
// - The frame tree node ID for navigation requests only. Please do not use
// frame tree node ID for other requests.
// TODO(nhiroki): Clarify which frame id should be used for service workers.
// Probably the render frame id of the frame calling register() or update(),
// and MSG_ROUTING_NONE for the soft update.
int32 render_frame_id;
// True if |frame_id| is the main frame of a RenderView.
......
......@@ -47,8 +47,9 @@ struct EmbeddedWorkerStartParams {
// The string used for "user-agent" HTTP header.
string user_agent;
// The id to talk with the DevTools agent for the worker.
int32 worker_devtools_agent_route_id;
// The id this service worker uses as render_frame_id in
// network::ResourceRequests it makes.
int32 service_worker_route_id;
// Unique token identifying this worker for DevTools.
mojo_base.mojom.UnguessableToken devtools_worker_token;
......
......@@ -328,8 +328,8 @@ SDK.NetworkDispatcher = class {
networkRequest.setTransferSize(response.encodedDataLength);
if (response.requestHeaders && !networkRequest.hasExtraRequestInfo()) {
// TODO(http://crbug.com/991471): Stop using response.requestHeaders and
// response.requestHeadersText once service workers and shared workers
// TODO(http://crbug.com/1004979): Stop using response.requestHeaders and
// response.requestHeadersText once shared workers
// emit Network.*ExtraInfo events for their network requests.
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders));
networkRequest.setRequestHeadersText(response.requestHeadersText || '');
......
......@@ -5,13 +5,8 @@ document resource from CSS.enable:
<html>
<head>
<title>webpage for repeat-fetch-service-worker.js</title>
<script>
function installSW() {
navigator.serviceWorker.register('repeat-fetch-service-worker.js');
}
</script>
</head>
<body onload="installSW()"><p>injected by service worker</p></body>
<body><p>injected by service worker</p></body>
</html>
......@@ -2,25 +2,21 @@
var {page, session, dp} = await testRunner.startURL(
'resources/repeat-fetch-service-worker.html',
'Verifies that service workers do not throw errors from devtools css enable initiated fetch.');
const swHelper = (await testRunner.loadScript('resources/service-worker-helper.js'))(dp, session);
dp.ServiceWorker.onWorkerErrorReported(error => {
testRunner.log(
'serivce worker reported error: ' + JSON.stringify(error, null, 2));
'service worker reported error: ' + JSON.stringify(error, null, 2));
testRunner.completeTest();
});
await dp.Runtime.enable();
await dp.ServiceWorker.enable();
let versions;
do {
const result = await dp.ServiceWorker.onceWorkerVersionUpdated();
versions = result.params.versions;
} while (!versions.length || versions[0].status !== 'activated');
await versions[0].registrationId;
await swHelper.installSWAndWaitForActivated('/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js');
await dp.Page.enable();
await dp.Page.reload();
await swHelper.installSWAndWaitForActivated('/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js');
await dp.DOM.enable();
await dp.CSS.enable();
......
Verifies that requests made for service worker main scripts get Network.*ExtraInfo events for them.
requestWillBeSent url: http://127.0.0.1:8000/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js
requestIds match: true
(async function(testRunner) {
var {page, session, dp} = await testRunner.startBlank(
'Verifies that requests made for service worker main scripts get Network.*ExtraInfo events for them.');
const swHelper = (await testRunner.loadScript('resources/service-worker-helper.js'))(dp, session);
await dp.Target.setAutoAttach(
{autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
await session.navigate('resources/repeat-fetch-service-worker.html');
swHelper.installSWAndWaitForActivated('/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js');
const attachedToTarget = await dp.Target.onceAttachedToTarget();
const swdp = session.createChild(attachedToTarget.params.sessionId).protocol;
await swdp.Network.enable();
swdp.Runtime.runIfWaitingForDebugger();
const [
requestWillBeSent,
requestWillBeSentExtraInfo,
responseReceived,
responseReceivedExtraInfo
] = await Promise.all([
swdp.Network.onceRequestWillBeSent(),
swdp.Network.onceRequestWillBeSentExtraInfo(),
swdp.Network.onceResponseReceived(),
swdp.Network.onceResponseReceivedExtraInfo()
]);
const idsMatch = requestWillBeSent.params.requestId === requestWillBeSentExtraInfo.params.requestId
&& requestWillBeSent.params.requestId === responseReceived.params.requestId
&& requestWillBeSent.params.requestId === responseReceivedExtraInfo.params.requestId;
testRunner.log('requestWillBeSent url: ' + requestWillBeSent.params.request.url);
testRunner.log('requestIds match: ' + idsMatch);
testRunner.completeTest();
});
Verifies that subresource requests made by service workers get Network.*ExtraInfo events for them.
requestWillBeSent url: http://127.0.0.1:8000/inspector-protocol/service-worker/resources/hello-world.txt
requestIds match: true
(async function(testRunner) {
var {page, session, dp} = await testRunner.startBlank(
'Verifies that subresource requests made by service workers get Network.*ExtraInfo events for them.');
const swHelper = (await testRunner.loadScript('resources/service-worker-helper.js'))(dp, session);
let swdp = null;
await dp.Target.setAutoAttach(
{autoAttach: true, waitForDebuggerOnStart: false, flatten: true});
dp.Target.onAttachedToTarget(async event => {
swdp = session.createChild(event.params.sessionId).protocol;
});
await session.navigate('resources/repeat-fetch-service-worker.html');
await swHelper.installSWAndWaitForActivated('/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js');
await dp.Page.enable();
await dp.Page.reload();
await swHelper.installSWAndWaitForActivated('/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js');
await swdp.Network.enable();
const [
requestWillBeSent,
requestWillBeSentExtraInfo,
responseReceived,
responseReceivedExtraInfo,
evaluate
] = await Promise.all([
swdp.Network.onceRequestWillBeSent(),
swdp.Network.onceRequestWillBeSentExtraInfo(),
swdp.Network.onceResponseReceived(),
swdp.Network.onceResponseReceivedExtraInfo(),
session.evaluate(`fetch('${testRunner.url('./resources/hello-world.txt')}')`)
]);
const idsMatch = requestWillBeSent.params.requestId === requestWillBeSentExtraInfo.params.requestId
&& requestWillBeSent.params.requestId === responseReceived.params.requestId
&& requestWillBeSent.params.requestId === responseReceivedExtraInfo.params.requestId;
testRunner.log('requestWillBeSent url: ' + requestWillBeSent.params.request.url);
testRunner.log('requestIds match: ' + idsMatch);
testRunner.completeTest();
});
......@@ -2,11 +2,6 @@
<html>
<head>
<title>webpage for repeat-fetch-service-worker.js</title>
<script>
function installSW() {
navigator.serviceWorker.register('repeat-fetch-service-worker.js');
}
</script>
</head>
<body onload="installSW()"></body>
<body></body>
</html>
(function() {
class ServiceWorkerHelper {
constructor(dp, session) {
this._dp = dp;
this._session = session;
}
async installSWAndWaitForActivated(swUrl) {
await this._session.evaluateAsync(`
(async function() {
const reg = await navigator.serviceWorker.register('${swUrl}');
const worker = reg.installing || reg.waiting || reg.active;
if (worker.state === 'activated')
return;
return new Promise(resolve => {
worker.addEventListener('statechange', () => {
if (worker.state === 'activated')
resolve();
});
});
})()`);
}
};
return (dp, session) => {
return new ServiceWorkerHelper(dp, session);
};
})()
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