Commit 7c94a513 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[devtools] Add CDP method to fetch resources

This CL adds a method to the Network domain that DevTools can use to
fetch resources in a more secure way. Such resources are, for example,
source maps, or files referenced by source maps. The improvement over
the old implementation is that the fetch now is very similar to a fetch
in the page and uses the correct settings for SiteForCookies and NIK.

Initially, we had planed to change source map fetching to CORS, but this
would be a breaking change, so due to COVID-19 and the hold on breaking
changes, this CL uses a no-CORS fetch with CORB blocking disabled to
fetch source maps. CORB-blocking must be disabled because source maps
may be valid JSON.

The front-end can provide a frame_id (in DevTools terms, a devtools
frame token in Chromium terms) and the page will look for this frame
id in its sub resources. The fetch will then occur using a loader that
is very similar to the loaders for that frame.

Change-Id: Ideb36adbd79b9a36e6d3333299dcd036eb7a1332
Bug: chromium:1069378
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2027416
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Reviewed-by: default avatarŁukasz Anforowicz <lukasza@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810177}
parent 35135bbe
...@@ -788,6 +788,8 @@ source_set("browser") { ...@@ -788,6 +788,8 @@ source_set("browser") {
"devtools/protocol/devtools_download_manager_delegate.h", "devtools/protocol/devtools_download_manager_delegate.h",
"devtools/protocol/devtools_mhtml_helper.cc", "devtools/protocol/devtools_mhtml_helper.cc",
"devtools/protocol/devtools_mhtml_helper.h", "devtools/protocol/devtools_mhtml_helper.h",
"devtools/protocol/devtools_network_resource_loader.cc",
"devtools/protocol/devtools_network_resource_loader.h",
"devtools/protocol/dom_handler.cc", "devtools/protocol/dom_handler.cc",
"devtools/protocol/dom_handler.h", "devtools/protocol/dom_handler.h",
"devtools/protocol/emulation_handler.cc", "devtools/protocol/emulation_handler.cc",
......
...@@ -390,4 +390,29 @@ void DevToolsAgentHostImpl::NotifyDestroyed() { ...@@ -390,4 +390,29 @@ void DevToolsAgentHostImpl::NotifyDestroyed() {
g_devtools_instances.Get().erase(id_); g_devtools_instances.Get().erase(id_);
} }
DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
NetworkLoaderFactoryParamsAndInfo() = default;
DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
NetworkLoaderFactoryParamsAndInfo(
url::Origin origin,
net::SiteForCookies site_for_cookies,
network::mojom::URLLoaderFactoryParamsPtr factory_params)
: origin(std::move(origin)),
site_for_cookies(std::move(site_for_cookies)),
factory_params(std::move(factory_params)) {}
DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
NetworkLoaderFactoryParamsAndInfo(
DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo&&) = default;
DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
~NetworkLoaderFactoryParamsAndInfo() = default;
DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo
DevToolsAgentHostImpl::CreateNetworkFactoryParamsForDevTools() {
return {};
}
RenderProcessHost* DevToolsAgentHostImpl::GetProcessHost() {
return nullptr;
}
} // namespace content } // namespace content
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/certificate_request_result_type.h" #include "content/public/browser/certificate_request_result_type.h"
#include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_agent_host.h"
#include "net/cookies/site_for_cookies.h"
namespace content { namespace content {
...@@ -53,6 +54,24 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost { ...@@ -53,6 +54,24 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost {
WebContents* GetWebContents() override; WebContents* GetWebContents() override;
void DisconnectWebContents() override; void DisconnectWebContents() override;
void ConnectWebContents(WebContents* wc) override; void ConnectWebContents(WebContents* wc) override;
RenderProcessHost* GetProcessHost() override;
struct NetworkLoaderFactoryParamsAndInfo {
NetworkLoaderFactoryParamsAndInfo();
NetworkLoaderFactoryParamsAndInfo(
url::Origin,
net::SiteForCookies,
network::mojom::URLLoaderFactoryParamsPtr);
NetworkLoaderFactoryParamsAndInfo(NetworkLoaderFactoryParamsAndInfo&&);
~NetworkLoaderFactoryParamsAndInfo();
url::Origin origin;
net::SiteForCookies site_for_cookies;
network::mojom::URLLoaderFactoryParamsPtr factory_params;
};
// Creates network factory parameters for devtools-initiated subresource
// requests.
virtual NetworkLoaderFactoryParamsAndInfo
CreateNetworkFactoryParamsForDevTools();
bool Inspect(); bool Inspect();
...@@ -70,7 +89,7 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost { ...@@ -70,7 +89,7 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost {
} }
protected: protected:
DevToolsAgentHostImpl(const std::string& id); explicit DevToolsAgentHostImpl(const std::string& id);
~DevToolsAgentHostImpl() override; ~DevToolsAgentHostImpl() override;
static bool ShouldForceCreation(); static bool ShouldForceCreation();
......
// Copyright 2020 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.
#include "content/browser/devtools/protocol/devtools_network_resource_loader.h"
#include <cstddef>
#include "base/bind.h"
#include "base/json/json_writer.h"
#include "content/browser/devtools/protocol/devtools_network_resource_loader.h"
#include "net/base/load_flags.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace content {
namespace protocol {
DevToolsNetworkResourceLoader::DevToolsNetworkResourceLoader(
network::ResourceRequest resource_request,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory,
CompletionCallback completion_callback)
: resource_request_(std::move(resource_request)),
traffic_annotation_(traffic_annotation),
url_loader_factory_(std::move(url_loader_factory)),
completion_callback_(std::move(completion_callback)) {
DownloadAsStream();
}
DevToolsNetworkResourceLoader::~DevToolsNetworkResourceLoader() = default;
// We can trust the |origin| parameter here, as it is the last committed origin
// of a render frame host identified by a DevTools frame token. Note that there
// is a potential race condition when DevTools sends a request while the frame
// already navigates away. This is difficult to fix before the
// RenderDocumentHost refactoring is done.
// static
std::unique_ptr<DevToolsNetworkResourceLoader>
DevToolsNetworkResourceLoader::Create(
mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory,
GURL gurl,
const url::Origin& origin,
net::SiteForCookies site_for_cookies,
Caching caching,
Credentials include_credentials,
int32_t render_frame_id,
CompletionCallback completion_callback) {
DCHECK(gurl.SchemeIsHTTPOrHTTPS());
network::ResourceRequest resource_request;
resource_request.url = std::move(gurl);
resource_request.request_initiator = origin;
resource_request.render_frame_id = render_frame_id;
resource_request.site_for_cookies = site_for_cookies;
if (caching == Caching::kBypass) {
resource_request.load_flags |= net::LOAD_BYPASS_CACHE;
}
resource_request.mode = network::mojom::RequestMode::kNoCors;
resource_request.credentials_mode =
include_credentials == Credentials::kInclude
? network::mojom::CredentialsMode::kInclude
: network::mojom::CredentialsMode::kSameOrigin;
resource_request.redirect_mode = network::mojom::RedirectMode::kFollow;
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("devtools_cdp_network_resource", R"(
semantics {
sender: "Developer Tools via CDP"
description:
"When user opens Developer Tools, the browser may fetch additional "
"resources from the network to enrich the debugging experience "
"(e.g. source map resources)."
trigger: "User opens Developer Tools to debug a web page."
data: "Any resources requested by Developer Tools."
destination: WEBSITE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting:
"It's not possible to disable this feature from settings."
chrome_policy {
DeveloperToolsAvailability {
policy_options {mode: MANDATORY}
DeveloperToolsAvailability: 2
}
}
})");
return base::WrapUnique(new DevToolsNetworkResourceLoader(
std::move(resource_request), traffic_annotation,
std::move(url_loader_factory), std::move(completion_callback)));
}
void DevToolsNetworkResourceLoader::OnRetry(base::OnceClosure start_retry) {
NOTREACHED();
}
void DevToolsNetworkResourceLoader::DownloadAsStream() {
content_.erase();
loader_ = network::SimpleURLLoader::Create(
std::make_unique<network::ResourceRequest>(resource_request_),
traffic_annotation_);
loader_->DownloadAsStream(url_loader_factory_.get(), this);
}
void DevToolsNetworkResourceLoader::OnDataReceived(base::StringPiece chunk,
base::OnceClosure resume) {
content_.append(chunk.data(), chunk.size());
std::move(resume).Run();
}
void DevToolsNetworkResourceLoader::OnComplete(bool success) {
const network::mojom::URLResponseHead* info = loader_->ResponseInfo();
const net::HttpResponseHeaders* response_headers = nullptr;
std::string mime_type;
if (info && info->headers) {
response_headers = info->headers.get();
}
std::move(completion_callback_)
.Run(this, response_headers, success, loader_->NetError(),
std::move(content_));
}
} // namespace protocol
} // namespace content
// Copyright 2020 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.
#ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_DEVTOOLS_NETWORK_RESOURCE_LOADER_H_
#define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_DEVTOOLS_NETWORK_RESOURCE_LOADER_H_
#include <memory>
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
namespace content {
namespace protocol {
// The DevToolsNetworkResourceLoader loads a network resource for DevTools
// and passes it to the provided call-back once loading completed. Currently,
// the resource is provided as a string, but in the future this will use
// a DevToolsStreamPipe. This is why we don't just use DownloadToString.
class CONTENT_EXPORT DevToolsNetworkResourceLoader
: public network::SimpleURLLoaderStreamConsumer {
public:
using CompletionCallback =
base::OnceCallback<void(DevToolsNetworkResourceLoader*,
const net::HttpResponseHeaders* rh,
bool success,
int net_error,
std::string content)>;
enum class Caching { kBypass, kDefault };
enum class Credentials { kInclude, kSameSite };
// The |origin| and |site_for_cookies| parameters are supplied by the caller,
// and we trust the caller that these values are reasonable. They are usually
// taken from a renderer host / worker host that was identified by the
// DevTools front-end based on the inspected page.
static std::unique_ptr<DevToolsNetworkResourceLoader> Create(
mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory,
GURL gurl,
const url::Origin& origin,
net::SiteForCookies site_for_cookies,
Caching caching,
Credentials include_credentials,
int32_t render_frame_id,
CompletionCallback complete_callback);
~DevToolsNetworkResourceLoader() override;
// Disallow copy and assignment.
DevToolsNetworkResourceLoader(const DevToolsNetworkResourceLoader&) = delete;
DevToolsNetworkResourceLoader& operator=(
const DevToolsNetworkResourceLoader&) = delete;
private:
DevToolsNetworkResourceLoader(
network::ResourceRequest resource_request,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory,
CompletionCallback complete_callback);
void DownloadAsStream();
void OnDataReceived(base::StringPiece chunk,
base::OnceClosure resume) override;
void OnComplete(bool success) override;
void OnRetry(base::OnceClosure start_retry) override;
const network::ResourceRequest resource_request_;
const net::NetworkTrafficAnnotationTag traffic_annotation_;
std::unique_ptr<network::SimpleURLLoader> loader_;
mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory_;
CompletionCallback completion_callback_;
std::string content_;
};
} // namespace protocol
} // namespace content
#endif // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_DEVTOOLS_NETWORK_RESOURCE_LOADER_H_
// Copyright (c) 2020 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.
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/devtools/protocol/devtools_network_resource_loader.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/url_loader_factory_params_helper.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_paths.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/public/test/url_loader_monitor.h"
#include "content/shell/browser/shell.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/features.h"
#include "net/base/network_isolation_key.h"
#include "net/cookies/site_for_cookies.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
class DevtoolsNetworkResourceLoaderTest : public ContentBrowserTest {
public:
DevtoolsNetworkResourceLoaderTest() = default;
// ContentBrowserTest implementation:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url1 = embedded_test_server()->GetURL("a.com", "/title1.html");
EXPECT_TRUE(NavigateToURL(web_contents(), url1));
EXPECT_TRUE(WaitForLoadStop(web_contents()));
base::FilePath test_source_map_path;
base::PathService::Get(content::DIR_TEST_DATA, &test_source_map_path);
test_source_map_path =
test_source_map_path.AppendASCII("devtools").AppendASCII("source.map");
base::ReadFileToString(test_source_map_path, &source_map_contents_);
}
const std::string& source_map_contents() { return source_map_contents_; }
WebContentsImpl* web_contents() const {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
RenderFrameHostImpl* current_frame_host() {
return web_contents()->GetFrameTree()->root()->current_frame_host();
}
mojo::Remote<network::mojom::URLLoaderFactory> CreateURLLoaderFactory() {
mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory;
auto* frame = current_frame_host();
auto params = URLLoaderFactoryParamsHelper::CreateForFrame(
frame, frame->GetLastCommittedOrigin(),
mojo::Clone(frame->last_committed_client_security_state()),
/**coep_reporter=*/mojo::NullRemote(), frame->GetProcess(),
network::mojom::TrustTokenRedemptionPolicy::kForbid,
"NetworkHandler::LoadNetworkResource");
// Let DevTools fetch resources without CORS and CORB. Source maps are valid
// JSON and would otherwise require a CORS fetch + correct response headers.
// See BUG(chromium:1076435) for more context.
params->is_corb_enabled = false;
current_frame_host()->GetProcess()->CreateURLLoaderFactory(
url_loader_factory.BindNewPipeAndPassReceiver(), std::move(params));
return url_loader_factory;
}
// Repeats |number_of_error_A| times |error_A|, then continues with |error_B|.
static std::unique_ptr<content::URLLoaderInterceptor> SetupRequestFailForURL(
const GURL& url,
net::Error error) {
return std::make_unique<content::URLLoaderInterceptor>(
base::BindLambdaForTesting(
[error,
&url](content::URLLoaderInterceptor::RequestParams* params) {
if (params->url_request.url != url) {
return false;
}
params->client->OnComplete(
network::URLLoaderCompletionStatus(error));
return true;
}));
}
static void CheckSuccess(DevtoolsNetworkResourceLoaderTest* test,
base::RunLoop* run_loop,
protocol::DevToolsNetworkResourceLoader* loader,
const net::HttpResponseHeaders* rh,
bool success,
int net_error,
std::string content) {
EXPECT_TRUE(success);
EXPECT_EQ(net_error, net::OK);
ASSERT_TRUE(rh);
EXPECT_EQ(rh->response_code(), 200);
EXPECT_EQ(content, test->source_map_contents());
size_t iterator = 0;
std::string name, value;
EXPECT_FALSE(rh->EnumerateHeaderLines(&iterator, &name, &value));
run_loop->Quit();
}
std::unique_ptr<protocol::DevToolsNetworkResourceLoader> CreateLoader(
GURL url,
protocol::DevToolsNetworkResourceLoader::Caching caching,
protocol::DevToolsNetworkResourceLoader::CompletionCallback callback) {
return protocol::DevToolsNetworkResourceLoader::Create(
CreateURLLoaderFactory(), std::move(url),
current_frame_host()->GetLastCommittedOrigin(),
current_frame_host()->ComputeSiteForCookies(), caching,
protocol::DevToolsNetworkResourceLoader::Credentials::kInclude,
MSG_ROUTING_NONE, std::move(callback));
}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
IsolateAllSitesForTesting(command_line);
}
private:
base::test::ScopedFeatureList feature_list_;
std::string source_map_contents_;
};
// Tests that basic download works.
IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest, BasicDownload) {
base::RunLoop run_loop;
const GURL source_map_url(
embedded_test_server()->GetURL("a.com", "/devtools/source.map"));
auto loader =
CreateLoader(source_map_url,
protocol::DevToolsNetworkResourceLoader::Caching::kDefault,
base::BindOnce(CheckSuccess, this, &run_loop));
run_loop.Run();
}
// This test is fetching a source map from a cross-origin URL. While the fetch
// isn't a CORS fetch, the source map is JSON, so this tests that CORB is
// disabled.
IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest,
BasicDownloadCrossOrigin) {
base::RunLoop run_loop;
const GURL source_map_url(
embedded_test_server()->GetURL("b.com", "/devtools/source.map"));
auto loader =
CreateLoader(source_map_url,
protocol::DevToolsNetworkResourceLoader::Caching::kDefault,
base::BindOnce(CheckSuccess, this, &run_loop));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest,
BasicDownloadCrossOriginNoCache) {
base::RunLoop run_loop;
const GURL source_map_url(
embedded_test_server()->GetURL("b.com", "/devtools/source.map"));
URLLoaderMonitor monitor({source_map_url});
auto loader = CreateLoader(
source_map_url, protocol::DevToolsNetworkResourceLoader::Caching::kBypass,
base::BindOnce(CheckSuccess, this, &run_loop));
run_loop.Run();
base::Optional<network::ResourceRequest> request =
monitor.GetRequestInfo(source_map_url);
EXPECT_TRUE(request->load_flags & net::LOAD_BYPASS_CACHE);
}
IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest,
BasicDownloadCacheControl) {
base::RunLoop run_loop;
const GURL source_map_url(
embedded_test_server()->GetURL("a.com", "/echoheader?Cache-Control"));
auto complete_callback = base::BindOnce(
[](base::RunLoop* run_loop,
protocol::DevToolsNetworkResourceLoader* loader,
const net::HttpResponseHeaders* rh, bool success, int net_error,
std::string content) {
EXPECT_TRUE(success);
EXPECT_EQ(net_error, net::OK);
EXPECT_EQ(rh->response_code(), 200);
EXPECT_EQ(content, "no-cache");
// Not asserting headers for echoheader response.
run_loop->Quit();
},
&run_loop);
auto loader = CreateLoader(
source_map_url, protocol::DevToolsNetworkResourceLoader::Caching::kBypass,
std::move(complete_callback));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest, HTTPError404) {
base::RunLoop run_loop;
const GURL source_map_url(embedded_test_server()->GetURL("/page404.html"));
auto complete_callback = base::BindOnce(
[](base::RunLoop* run_loop,
protocol::DevToolsNetworkResourceLoader* loader,
const net::HttpResponseHeaders* rh, bool success, int net_error,
std::string content) {
EXPECT_FALSE(success);
ASSERT_EQ(net_error, net::ERR_HTTP_RESPONSE_CODE_FAILURE);
EXPECT_EQ(rh->response_code(), 404);
EXPECT_EQ(content, "");
size_t iterator = 0;
std::string name, value;
EXPECT_TRUE(rh->EnumerateHeaderLines(&iterator, &name, &value));
EXPECT_EQ(name, "Content-type");
EXPECT_EQ(value, "text/html");
EXPECT_FALSE(rh->EnumerateHeaderLines(&iterator, &name, &value));
run_loop->Quit();
},
&run_loop);
auto loader = CreateLoader(
source_map_url, protocol::DevToolsNetworkResourceLoader::Caching::kBypass,
std::move(complete_callback));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest, GenericFailure) {
const GURL source_map_url(
embedded_test_server()->GetURL("a.com", "/devtools/source.map"));
auto interceptor = SetupRequestFailForURL(source_map_url, net::ERR_FAILED);
base::RunLoop run_loop;
auto complete_callback = base::BindOnce(
[](base::RunLoop* run_loop,
protocol::DevToolsNetworkResourceLoader* loader,
const net::HttpResponseHeaders* rh, bool success, int net_error,
std::string content) {
EXPECT_FALSE(success);
EXPECT_EQ(net_error, net::ERR_FAILED);
EXPECT_EQ(content, "");
EXPECT_FALSE(rh);
run_loop->Quit();
},
&run_loop);
auto loader = CreateLoader(
source_map_url, protocol::DevToolsNetworkResourceLoader::Caching::kBypass,
std::move(complete_callback));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest,
BasicDownloadRequestParams) {
RenderFrameHostImpl* frame = current_frame_host();
const GURL source_map_url(
embedded_test_server()->GetURL("a.com", "/devtools/source.map"));
URLLoaderMonitor monitor({source_map_url});
base::RunLoop run_loop;
auto loader =
CreateLoader(source_map_url,
protocol::DevToolsNetworkResourceLoader::Caching::kDefault,
base::BindOnce(CheckSuccess, this, &run_loop));
run_loop.Run();
base::Optional<network::ResourceRequest> request =
monitor.GetRequestInfo(source_map_url);
EXPECT_TRUE(
frame->ComputeSiteForCookies().IsEquivalent(request->site_for_cookies));
EXPECT_EQ(frame->GetLastCommittedOrigin(), request->request_initiator);
EXPECT_FALSE(request->load_flags & net::LOAD_BYPASS_CACHE);
}
} // namespace content
...@@ -24,18 +24,22 @@ ...@@ -24,18 +24,22 @@
#include "content/browser/background_sync/background_sync_manager.h" #include "content/browser/background_sync/background_sync_manager.h"
#include "content/browser/devtools/devtools_agent_host_impl.h" #include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/devtools/devtools_io_context.h" #include "content/browser/devtools/devtools_io_context.h"
#include "content/browser/devtools/devtools_stream_file.h"
#include "content/browser/devtools/devtools_stream_pipe.h" #include "content/browser/devtools/devtools_stream_pipe.h"
#include "content/browser/devtools/devtools_url_loader_interceptor.h" #include "content/browser/devtools/devtools_url_loader_interceptor.h"
#include "content/browser/devtools/protocol/devtools_network_resource_loader.h"
#include "content/browser/devtools/protocol/handler_helpers.h" #include "content/browser/devtools/protocol/handler_helpers.h"
#include "content/browser/devtools/protocol/network.h" #include "content/browser/devtools/protocol/network.h"
#include "content/browser/devtools/protocol/page.h" #include "content/browser/devtools/protocol/page.h"
#include "content/browser/devtools/protocol/security.h" #include "content/browser/devtools/protocol/security.h"
#include "content/browser/devtools/render_frame_devtools_agent_host.h"
#include "content/browser/devtools/service_worker_devtools_agent_host.h" #include "content/browser/devtools/service_worker_devtools_agent_host.h"
#include "content/browser/devtools/service_worker_devtools_manager.h" #include "content/browser/devtools/service_worker_devtools_manager.h"
#include "content/browser/renderer_host/frame_tree_node.h" #include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_request.h" #include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/storage_partition_impl.h" #include "content/browser/storage_partition_impl.h"
#include "content/browser/url_loader_factory_params_helper.h"
#include "content/browser/web_package/signed_exchange_envelope.h" #include "content/browser/web_package/signed_exchange_envelope.h"
#include "content/browser/web_package/signed_exchange_error.h" #include "content/browser/web_package/signed_exchange_error.h"
#include "content/common/navigation_params.h" #include "content/common/navigation_params.h"
...@@ -56,6 +60,7 @@ ...@@ -56,6 +60,7 @@
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h" #include "content/public/common/content_client.h"
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/base/host_port_pair.h" #include "net/base/host_port_pair.h"
#include "net/base/ip_endpoint.h" #include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
...@@ -2274,5 +2279,164 @@ void NetworkHandler::OnResponseReceivedExtraInfo( ...@@ -2274,5 +2279,164 @@ void NetworkHandler::OnResponseReceivedExtraInfo(
: Maybe<String>()); : Maybe<String>());
} }
void NetworkHandler::OnLoadNetworkResourceFinished(
DevToolsNetworkResourceLoader* loader,
const net::HttpResponseHeaders* rh,
bool success,
int net_error,
std::string content) {
auto it = loaders_.find(loader);
CHECK(it != loaders_.end());
auto callback = std::move(it->second);
auto result = Network::LoadNetworkResourcePageResult::Create()
.SetSuccess(success)
.Build();
if (net_error != net::OK) {
result->SetNetError(net_error);
result->SetNetErrorName(net::ErrorToString(net_error));
}
if (success) {
bool is_binary = true;
std::string mime_type;
if (rh && rh->GetMimeType(&mime_type)) {
is_binary = !DevToolsIOContext::IsTextMimeType(mime_type);
}
// TODO(sigurds): Use the data-pipe from the network loader.
scoped_refptr<DevToolsStreamFile> stream =
DevToolsStreamFile::Create(io_context_, is_binary);
stream->Append(std::make_unique<std::string>(std::move(content)));
result->SetStream(stream->handle());
}
if (rh) {
result->SetHttpStatusCode(rh->response_code());
std::unique_ptr<protocol::DictionaryValue> headers_object =
protocol::DictionaryValue::create();
size_t iterator = 0;
std::string name;
std::string value;
// TODO(chromium:1069378): This probably needs to handle duplicate header
// names correctly by folding them.
while (rh->EnumerateHeaderLines(&iterator, &name, &value)) {
headers_object->setString(name, value);
}
protocol::ErrorSupport errors;
result->SetHeaders(
protocol::Network::Headers::fromValue(headers_object.get(), &errors));
}
callback->sendSuccess(std::move(result));
loaders_.erase(it);
}
namespace {
mojo::PendingRemote<network::mojom::URLLoaderFactory>
CreateNetworkFactoryForDevTools(
RenderProcessHost* host,
network::mojom::URLLoaderFactoryParamsPtr params) {
if (!host || !params) {
// Return an invalid remote by default.
return {};
}
// Don't allow trust token redemption.
params->trust_token_redemption_policy =
network::mojom::TrustTokenRedemptionPolicy::kForbid;
// Let DevTools fetch resources without CORS and CORB. Source maps are valid
// JSON and would otherwise require a CORS fetch + correct response headers.
// See BUG(chromium:1076435) for more context.
params->is_corb_enabled = false;
mojo::PendingRemote<network::mojom::URLLoaderFactory> remote;
host->CreateURLLoaderFactory(remote.InitWithNewPipeAndPassReceiver(),
std::move(params));
return remote;
}
} // namespace
void NetworkHandler::LoadNetworkResource(
const String& frame_id,
const String& url,
std::unique_ptr<protocol::Network::LoadNetworkResourceOptions> options,
std::unique_ptr<LoadNetworkResourceCallback> callback) {
GURL gurl(url);
const bool is_gurl_valid = gurl.is_valid() && gurl.SchemeIsHTTPOrHTTPS();
if (!is_gurl_valid) {
callback->sendFailure(Response::InvalidParams(
"The url must be valid and have scheme http or https"));
return;
}
const DevToolsNetworkResourceLoader::Caching caching =
options->GetDisableCache()
? DevToolsNetworkResourceLoader::Caching::kBypass
: DevToolsNetworkResourceLoader::Caching::kDefault;
const DevToolsNetworkResourceLoader::Credentials include_credentials =
options->GetIncludeCredentials()
? DevToolsNetworkResourceLoader::Credentials::kInclude
: DevToolsNetworkResourceLoader::Credentials::kSameSite;
DevToolsNetworkResourceLoader::CompletionCallback complete_callback =
base::BindOnce(&NetworkHandler::OnLoadNetworkResourceFinished,
base::Unretained(this));
mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory;
if (host_) {
FrameTreeNode* node =
FrameTreeNodeFromDevToolsFrameToken(host_->frame_tree_node(), frame_id);
RenderFrameHostImpl* frame = node ? node->current_frame_host() : nullptr;
if (!frame) {
callback->sendFailure(Response::InvalidParams("Frame not found"));
return;
}
// Don't allow fetching resources for frames goverened by different
// DevToolsAgentHosts.
if (GetFrameTreeNodeAncestor(node) !=
GetFrameTreeNodeAncestor(host_->frame_tree_node())) {
callback->sendFailure(
Response::InvalidParams("Frame not under control of agent host"));
return;
}
auto params = URLLoaderFactoryParamsHelper::CreateForFrame(
frame, frame->GetLastCommittedOrigin(),
mojo::Clone(frame->last_committed_client_security_state()),
/**coep_reporter=*/mojo::NullRemote(), frame->GetProcess(),
network::mojom::TrustTokenRedemptionPolicy::kForbid,
"NetworkHandler::LoadNetworkResource");
auto factory =
CreateNetworkFactoryForDevTools(frame->GetProcess(), std::move(params));
url_loader_factory.Bind(std::move(factory));
auto loader = DevToolsNetworkResourceLoader::Create(
std::move(url_loader_factory), std::move(gurl),
frame->GetLastCommittedOrigin(), frame->ComputeSiteForCookies(),
caching, include_credentials, frame->GetRoutingID(),
std::move(complete_callback));
loaders_.emplace(std::move(loader), std::move(callback));
return;
}
scoped_refptr<DevToolsAgentHostImpl> host =
DevToolsAgentHostImpl::GetForId(host_id_);
if (host) {
// TODO(sigurds): Support dedicated workers.
auto info = host->CreateNetworkFactoryParamsForDevTools();
auto factory = CreateNetworkFactoryForDevTools(
host->GetProcessHost(), std::move(info.factory_params));
if (factory.is_valid()) {
url_loader_factory.Bind(std::move(factory));
auto loader = DevToolsNetworkResourceLoader::Create(
std::move(url_loader_factory), std::move(gurl),
std::move(info.origin), std::move(info.site_for_cookies), caching,
include_credentials, MSG_ROUTING_NONE, std::move(complete_callback));
loaders_.emplace(std::move(loader), std::move(callback));
return;
}
}
callback->sendFailure(Response::ServerError("Target not supported"));
}
} // namespace protocol } // namespace protocol
} // namespace content } // namespace content
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h" #include "base/optional.h"
...@@ -52,6 +53,7 @@ struct SignedExchangeError; ...@@ -52,6 +53,7 @@ struct SignedExchangeError;
namespace protocol { namespace protocol {
class BackgroundSyncRestorer; class BackgroundSyncRestorer;
class DevToolsNetworkResourceLoader;
class NetworkHandler : public DevToolsDomainHandler, class NetworkHandler : public DevToolsDomainHandler,
public Network::Backend { public Network::Backend {
...@@ -220,7 +222,18 @@ class NetworkHandler : public DevToolsDomainHandler, ...@@ -220,7 +222,18 @@ class NetworkHandler : public DevToolsDomainHandler,
const network::ResourceRequest& request, const network::ResourceRequest& request,
const std::string& cookie_line); const std::string& cookie_line);
void LoadNetworkResource(
const String& frameId,
const String& url,
std::unique_ptr<protocol::Network::LoadNetworkResourceOptions> options,
std::unique_ptr<LoadNetworkResourceCallback> callback) override;
private: private:
void OnLoadNetworkResourceFinished(DevToolsNetworkResourceLoader* loader,
const net::HttpResponseHeaders* rh,
bool success,
int net_error,
std::string content);
void RequestIntercepted(std::unique_ptr<InterceptedRequestInfo> request_info); void RequestIntercepted(std::unique_ptr<InterceptedRequestInfo> request_info);
void SetNetworkConditions(network::mojom::NetworkConditionsPtr conditions); void SetNetworkConditions(network::mojom::NetworkConditionsPtr conditions);
...@@ -247,6 +260,10 @@ class NetworkHandler : public DevToolsDomainHandler, ...@@ -247,6 +260,10 @@ class NetworkHandler : public DevToolsDomainHandler,
bool cache_disabled_; bool cache_disabled_;
std::unique_ptr<BackgroundSyncRestorer> background_sync_restorer_; std::unique_ptr<BackgroundSyncRestorer> background_sync_restorer_;
base::RepeatingClosure update_loader_factories_callback_; base::RepeatingClosure update_loader_factories_callback_;
std::map<std::unique_ptr<DevToolsNetworkResourceLoader>,
std::unique_ptr<LoadNetworkResourceCallback>,
base::UniquePtrComparator>
loaders_;
base::WeakPtrFactory<NetworkHandler> weak_factory_{this}; base::WeakPtrFactory<NetworkHandler> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(NetworkHandler); DISALLOW_COPY_AND_ASSIGN(NetworkHandler);
......
...@@ -53,9 +53,9 @@ ...@@ -53,9 +53,9 @@
}, },
{ {
"domain": "Network", "domain": "Network",
"include": ["enable", "disable", "clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "setExtraHTTPHeaders", "canEmulateNetworkConditions", "emulateNetworkConditions", "setBypassServiceWorker", "setRequestInterception", "continueInterceptedRequest", "getResponseBodyForInterception", "setCacheDisabled", "takeResponseBodyForInterceptionAsStream", "getSecurityIsolationStatus"], "include": ["enable", "disable", "clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "setExtraHTTPHeaders", "canEmulateNetworkConditions", "emulateNetworkConditions", "setBypassServiceWorker", "setRequestInterception", "continueInterceptedRequest", "getResponseBodyForInterception", "setCacheDisabled", "takeResponseBodyForInterceptionAsStream", "getSecurityIsolationStatus", "loadNetworkResource"],
"include_events": ["requestWillBeSent", "responseReceived", "loadingFinished", "loadingFailed", "requestIntercepted", "signedExchangeReceived", "requestWillBeSentExtraInfo", "responseReceivedExtraInfo"], "include_events": ["requestWillBeSent", "responseReceived", "loadingFinished", "loadingFailed", "requestIntercepted", "signedExchangeReceived", "requestWillBeSentExtraInfo", "responseReceivedExtraInfo"],
"async": ["clearBrowserCookies", "clearBrowserCache", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "continueInterceptedRequest", "getResponseBodyForInterception", "takeResponseBodyForInterceptionAsStream"] "async": ["clearBrowserCookies", "clearBrowserCache", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "continueInterceptedRequest", "getResponseBodyForInterception", "takeResponseBodyForInterceptionAsStream", "loadNetworkResource"]
}, },
{ {
"domain": "Overlay", "domain": "Overlay",
......
...@@ -20,9 +20,13 @@ ...@@ -20,9 +20,13 @@
#include "content/browser/devtools/service_worker_devtools_manager.h" #include "content/browser/devtools/service_worker_devtools_manager.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_version.h" #include "content/browser/service_worker/service_worker_version.h"
#include "content/browser/url_loader_factory_params_helper.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/cookies/site_for_cookies.h"
#include "services/network/public/mojom/network_context.mojom-forward.h"
namespace content { namespace content {
...@@ -252,4 +256,23 @@ void ServiceWorkerDevToolsAgentHost::UpdateLoaderFactories( ...@@ -252,4 +256,23 @@ void ServiceWorkerDevToolsAgentHost::UpdateLoaderFactories(
} }
} }
DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo
ServiceWorkerDevToolsAgentHost::CreateNetworkFactoryParamsForDevTools() {
RenderProcessHost* rph = RenderProcessHost::FromID(worker_process_id_);
const url::Origin origin = url::Origin::Create(url_);
auto factory = URLLoaderFactoryParamsHelper::CreateForWorker(
rph, origin,
net::IsolationInfo::Create(
net::IsolationInfo::RedirectMode::kUpdateNothing, origin, origin,
net::SiteForCookies::FromOrigin(origin)),
/*coep_reporter=*/mojo::NullRemote(), /*debug_tag=*/
"ServiceWorkerDevToolsAgentHost::CreateNetworkFactoryParamsForDevTools");
return {url::Origin::Create(GetURL()), net::SiteForCookies::FromUrl(GetURL()),
std::move(factory)};
}
RenderProcessHost* ServiceWorkerDevToolsAgentHost::GetProcessHost() {
return RenderProcessHost::FromID(worker_process_id_);
}
} // namespace content } // namespace content
...@@ -50,6 +50,9 @@ class ServiceWorkerDevToolsAgentHost : public DevToolsAgentHostImpl { ...@@ -50,6 +50,9 @@ class ServiceWorkerDevToolsAgentHost : public DevToolsAgentHostImpl {
bool Activate() override; bool Activate() override;
void Reload() override; void Reload() override;
bool Close() override; bool Close() override;
NetworkLoaderFactoryParamsAndInfo CreateNetworkFactoryParamsForDevTools()
override;
RenderProcessHost* GetProcessHost() override;
void WorkerRestarted(int worker_process_id, int worker_route_id); void WorkerRestarted(int worker_process_id, int worker_route_id);
void WorkerReadyForInspection( void WorkerReadyForInspection(
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "content/browser/worker_host/shared_worker_service_impl.h" #include "content/browser/worker_host/shared_worker_service_impl.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "net/cookies/site_for_cookies.h"
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h" #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
namespace content { namespace content {
...@@ -57,6 +58,10 @@ GURL SharedWorkerDevToolsAgentHost::GetURL() { ...@@ -57,6 +58,10 @@ GURL SharedWorkerDevToolsAgentHost::GetURL() {
return instance_.url(); return instance_.url();
} }
url::Origin SharedWorkerDevToolsAgentHost::GetConstructorOrigin() {
return instance_.constructor_origin();
}
bool SharedWorkerDevToolsAgentHost::Activate() { bool SharedWorkerDevToolsAgentHost::Activate() {
return false; return false;
} }
...@@ -127,4 +132,16 @@ void SharedWorkerDevToolsAgentHost::WorkerDestroyed() { ...@@ -127,4 +132,16 @@ void SharedWorkerDevToolsAgentHost::WorkerDestroyed() {
ChildProcessHost::kInvalidUniqueID); ChildProcessHost::kInvalidUniqueID);
} }
DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo
SharedWorkerDevToolsAgentHost::CreateNetworkFactoryParamsForDevTools() {
DCHECK(worker_host_);
return {GetConstructorOrigin(), net::SiteForCookies::FromUrl(GetURL()),
worker_host_->CreateNetworkFactoryParamsForSubresources()};
}
RenderProcessHost* SharedWorkerDevToolsAgentHost::GetProcessHost() {
DCHECK(worker_host_);
return worker_host_->GetProcessHost();
}
} // namespace content } // namespace content
...@@ -34,6 +34,12 @@ class SharedWorkerDevToolsAgentHost : public DevToolsAgentHostImpl { ...@@ -34,6 +34,12 @@ class SharedWorkerDevToolsAgentHost : public DevToolsAgentHostImpl {
void Reload() override; void Reload() override;
bool Close() override; bool Close() override;
NetworkLoaderFactoryParamsAndInfo CreateNetworkFactoryParamsForDevTools()
override;
RenderProcessHost* GetProcessHost() override;
url::Origin GetConstructorOrigin();
bool Matches(SharedWorkerHost* worker_host); bool Matches(SharedWorkerHost* worker_host);
void WorkerReadyForInspection( void WorkerReadyForInspection(
mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote, mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote,
......
...@@ -28,7 +28,8 @@ WorkerDevToolsAgentHost::WorkerDevToolsAgentHost( ...@@ -28,7 +28,8 @@ WorkerDevToolsAgentHost::WorkerDevToolsAgentHost(
url_(url), url_(url),
name_(name), name_(name),
parent_id_(parent_id), parent_id_(parent_id),
destroyed_callback_(std::move(destroyed_callback)) { destroyed_callback_(std::move(destroyed_callback)),
devtools_worker_token_(devtools_worker_token) {
DCHECK(agent_remote); DCHECK(agent_remote);
DCHECK(!devtools_worker_token.is_empty()); DCHECK(!devtools_worker_token.is_empty());
AddRef(); // Self keep-alive while the worker agent is alive. AddRef(); // Self keep-alive while the worker agent is alive.
...@@ -87,6 +88,8 @@ bool WorkerDevToolsAgentHost::AttachSession(DevToolsSession* session, ...@@ -87,6 +88,8 @@ bool WorkerDevToolsAgentHost::AttachSession(DevToolsSession* session,
session->AddHandler(std::make_unique<protocol::TargetHandler>( session->AddHandler(std::make_unique<protocol::TargetHandler>(
protocol::TargetHandler::AccessMode::kAutoAttachOnly, GetId(), protocol::TargetHandler::AccessMode::kAutoAttachOnly, GetId(),
GetRendererChannel(), session->GetRootSession())); GetRendererChannel(), session->GetRootSession()));
session->AddHandler(std::make_unique<protocol::NetworkHandler>(
GetId(), devtools_worker_token_, GetIOContext(), base::DoNothing()));
return true; return true;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/unguessable_token.h" #include "base/unguessable_token.h"
#include "content/browser/devtools/devtools_agent_host_impl.h" #include "content/browser/devtools/devtools_agent_host_impl.h"
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h" #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -48,6 +49,7 @@ class WorkerDevToolsAgentHost : public DevToolsAgentHostImpl { ...@@ -48,6 +49,7 @@ class WorkerDevToolsAgentHost : public DevToolsAgentHostImpl {
const std::string name_; const std::string name_;
const std::string parent_id_; const std::string parent_id_;
base::OnceCallback<void(DevToolsAgentHostImpl*)> destroyed_callback_; base::OnceCallback<void(DevToolsAgentHostImpl*)> destroyed_callback_;
const base::UnguessableToken devtools_worker_token_;
DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost); DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost);
}; };
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_URL_LOADER_FACTORY_PARAMS_HELPER_H_ #define CONTENT_BROWSER_URL_LOADER_FACTORY_PARAMS_HELPER_H_
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h" #include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h"
#include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/network_context.mojom.h"
...@@ -42,7 +43,8 @@ class URLLoaderFactoryParamsHelper { ...@@ -42,7 +43,8 @@ class URLLoaderFactoryParamsHelper {
// |process| is exposed as a separate parameter, to accommodate creating // |process| is exposed as a separate parameter, to accommodate creating
// factories for dedicated workers (where the |process| hosting the worker // factories for dedicated workers (where the |process| hosting the worker
// might be different from the process hosting the |frame|). // might be different from the process hosting the |frame|).
static network::mojom::URLLoaderFactoryParamsPtr CreateForFrame( CONTENT_EXPORT static network::mojom::URLLoaderFactoryParamsPtr
CreateForFrame(
RenderFrameHostImpl* frame, RenderFrameHostImpl* frame,
const url::Origin& origin, const url::Origin& origin,
network::mojom::ClientSecurityStatePtr client_security_state, network::mojom::ClientSecurityStatePtr client_security_state,
......
...@@ -270,19 +270,11 @@ SharedWorkerHost::CreateNetworkFactoryForSubresources( ...@@ -270,19 +270,11 @@ SharedWorkerHost::CreateNetworkFactoryForSubresources(
default_factory_receiver = default_factory_receiver =
pending_default_factory.InitWithNewPipeAndPassReceiver(); pending_default_factory.InitWithNewPipeAndPassReceiver();
url::Origin origin = url::Origin::Create(instance_.url());
// TODO(https://crbug.com/1060832): Implement COEP reporter for shared // TODO(https://crbug.com/1060832): Implement COEP reporter for shared
// workers. // workers.
network::mojom::URLLoaderFactoryParamsPtr factory_params = network::mojom::URLLoaderFactoryParamsPtr factory_params =
URLLoaderFactoryParamsHelper::CreateForWorker( CreateNetworkFactoryParamsForSubresources();
worker_process_host_, instance_.constructor_origin(), url::Origin origin = url::Origin::Create(instance_.url());
net::IsolationInfo::Create(
net::IsolationInfo::RedirectMode::kUpdateNothing, origin, origin,
net::SiteForCookies::FromOrigin(origin)),
/*coep_reporter=*/mojo::NullRemote(),
/*debug_tag=*/
"SharedWorkerHost::CreateNetworkFactoryForSubresources");
GetContentClient()->browser()->WillCreateURLLoaderFactory( GetContentClient()->browser()->WillCreateURLLoaderFactory(
worker_process_host_->GetBrowserContext(), worker_process_host_->GetBrowserContext(),
/*frame=*/nullptr, worker_process_host_->GetID(), /*frame=*/nullptr, worker_process_host_->GetID(),
...@@ -302,6 +294,23 @@ SharedWorkerHost::CreateNetworkFactoryForSubresources( ...@@ -302,6 +294,23 @@ SharedWorkerHost::CreateNetworkFactoryForSubresources(
return pending_default_factory; return pending_default_factory;
} }
network::mojom::URLLoaderFactoryParamsPtr
SharedWorkerHost::CreateNetworkFactoryParamsForSubresources() {
url::Origin origin = url::Origin::Create(instance_.url());
// TODO(https://crbug.com/1060832): Implement COEP reporter for shared
// workers.
network::mojom::URLLoaderFactoryParamsPtr factory_params =
URLLoaderFactoryParamsHelper::CreateForWorker(
worker_process_host_, instance_.constructor_origin(),
net::IsolationInfo::Create(
net::IsolationInfo::RedirectMode::kUpdateNothing, origin, origin,
net::SiteForCookies::FromOrigin(origin)),
/*coep_reporter=*/mojo::NullRemote(), /*debug_tag=*/
"SharedWorkerHost::CreateNetworkFactoryForSubresources");
return factory_params;
}
void SharedWorkerHost::AllowFileSystem( void SharedWorkerHost::AllowFileSystem(
const GURL& url, const GURL& url,
base::OnceCallback<void(bool)> callback) { base::OnceCallback<void(bool)> callback) {
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
#include "services/metrics/public/cpp/ukm_source_id.h" #include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
#include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/appcache/appcache.mojom.h" #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h" #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
...@@ -152,6 +152,10 @@ class CONTENT_EXPORT SharedWorkerHost : public blink::mojom::SharedWorkerHost, ...@@ -152,6 +152,10 @@ class CONTENT_EXPORT SharedWorkerHost : public blink::mojom::SharedWorkerHost,
void ReportNoBinderForInterface(const std::string& error); void ReportNoBinderForInterface(const std::string& error);
// Creates a network factory params for subresource requests from this worker.
network::mojom::URLLoaderFactoryParamsPtr
CreateNetworkFactoryParamsForSubresources();
private: private:
friend class SharedWorkerHostTest; friend class SharedWorkerHostTest;
......
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/devtools_agent_host_client.h" #include "content/public/browser/devtools_agent_host_client.h"
#include "content/public/browser/devtools_agent_host_observer.h" #include "content/public/browser/devtools_agent_host_observer.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/network/public/mojom/network_context.mojom-forward.h"
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace base { namespace base {
...@@ -35,6 +38,7 @@ class DevToolsExternalAgentProxyDelegate; ...@@ -35,6 +38,7 @@ class DevToolsExternalAgentProxyDelegate;
class DevToolsSocketFactory; class DevToolsSocketFactory;
class RenderFrameHost; class RenderFrameHost;
class WebContents; class WebContents;
class RenderProcessHost;
// Describes interface for managing devtools agents from browser process. // Describes interface for managing devtools agents from browser process.
class CONTENT_EXPORT DevToolsAgentHost class CONTENT_EXPORT DevToolsAgentHost
...@@ -203,6 +207,8 @@ class CONTENT_EXPORT DevToolsAgentHost ...@@ -203,6 +207,8 @@ class CONTENT_EXPORT DevToolsAgentHost
// Terminates all debugging sessions and detaches all clients. // Terminates all debugging sessions and detaches all clients.
static void DetachAllClients(); static void DetachAllClients();
virtual RenderProcessHost* GetProcessHost() = 0;
protected: protected:
friend class base::RefCounted<DevToolsAgentHost>; friend class base::RefCounted<DevToolsAgentHost>;
virtual ~DevToolsAgentHost() {} virtual ~DevToolsAgentHost() {}
......
...@@ -946,6 +946,7 @@ test("content_browsertests") { ...@@ -946,6 +946,7 @@ test("content_browsertests") {
"../browser/device_sensors/device_sensor_browsertest.cc", "../browser/device_sensors/device_sensor_browsertest.cc",
"../browser/devtools/devtools_issue_storage_browsertest.cc", "../browser/devtools/devtools_issue_storage_browsertest.cc",
"../browser/devtools/devtools_video_consumer_browsertest.cc", "../browser/devtools/devtools_video_consumer_browsertest.cc",
"../browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc",
"../browser/devtools/protocol/devtools_protocol_browsertest.cc", "../browser/devtools/protocol/devtools_protocol_browsertest.cc",
"../browser/devtools/protocol/devtools_protocol_test_support.cc", "../browser/devtools/protocol/devtools_protocol_test_support.cc",
"../browser/devtools/protocol/devtools_protocol_test_support.h", "../browser/devtools/protocol/devtools_protocol_test_support.h",
......
{"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
...@@ -5400,6 +5400,38 @@ domain Network ...@@ -5400,6 +5400,38 @@ domain Network
returns returns
SecurityIsolationStatus status SecurityIsolationStatus status
# An object providing the result of a network resource load.
experimental type LoadNetworkResourcePageResult extends object
properties
boolean success
# Optional values used for error reporting.
optional number netError
optional string netErrorName
optional number httpStatusCode
# If successful, one of the following two fields holds the result.
optional IO.StreamHandle stream
# Response headers.
optional Network.Headers headers
# An options object that may be extended later to better support CORS,
# CORB and streaming.
experimental type LoadNetworkResourceOptions extends object
properties
boolean disableCache
boolean includeCredentials
# Fetches the resource and returns the content.
experimental command loadNetworkResource
parameters
# Frame id to get the resource for.
Page.FrameId frameId
# URL of the resource to get content for.
string url
# Options for the request.
LoadNetworkResourceOptions options
returns
LoadNetworkResourcePageResult resource
# This domain provides various functionality related to drawing atop the inspected page. # This domain provides various functionality related to drawing atop the inspected page.
experimental domain Overlay experimental domain Overlay
depends on DOM depends on DOM
......
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
"domain": "Network", "domain": "Network",
"exclude": ["clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "canEmulateNetworkConditions", "exclude": ["clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "canEmulateNetworkConditions",
"setRequestInterception", "continueInterceptedRequest", "getResponseBodyForInterception", "setRequestInterception", "continueInterceptedRequest", "getResponseBodyForInterception",
"takeResponseBodyForInterceptionAsStream", "getSecurityIsolationStatus"], "takeResponseBodyForInterceptionAsStream", "getSecurityIsolationStatus", "loadNetworkResource"],
"async": ["getResponseBody", "getRequestPostData"] "async": ["getResponseBody", "getRequestPostData"]
}, },
{ {
......
Tests Page.loadNetworkResource for different frames with cookies
Number of frames in page: 3
Response for fetch: {
id : <number>
result : {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
sessionId : <string>
}
Response for fetch: {
id : <number>
result : {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
sessionId : <string>
}
Response for fetch: {
id : <number>
result : {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
sessionId : <string>
}
Tests for Network.loadNetworkResource on the same origin
Response for fetch with existing resource with text content type: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
{
id : <number>
result : {
base64Encoded : false
data : {"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
eof : false
}
sessionId : <string>
}
Response for fetch with existing resource without content type: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
{
id : <number>
result : {
base64Encoded : true
data : eyJ2ZXJzaW9uIjozLCJmaWxlIjoic291cmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic291cmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUEifQo=
eof : false
}
sessionId : <string>
}
Response for fetch with non-existing resource: {
resource : {
headers : <object>
httpStatusCode : 404
netError : -379
netErrorName : net::ERR_HTTP_RESPONSE_CODE_FAILURE
success : false
}
}
(async function(testRunner) {
const {page, session, dp} = await testRunner.startBlank(
`Tests for Network.loadNetworkResource on the same origin`);
await dp.Network.enable();
const frameId = (await dp.Target.getTargetInfo()).result.targetInfo.targetId;
async function requestSourceMap(frameId, testExplanation, url) {
const response = await dp.Network.loadNetworkResource({frameId, url, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response.result, testExplanation, ["headers", "stream"]);
if (response.result.resource.success) {
let result = await dp.IO.read({handle: response.result.resource.stream, size: 1000*1000});
testRunner.log(result);
await dp.IO.close({handle: response.result.resource.stream});
}
}
const urlWithMimeType = `http://localhost:8000/inspector-protocol/network/resources/source.map.php`;
await requestSourceMap(frameId, `Response for fetch with existing resource with text content type: `, urlWithMimeType);
const urlWithoutMimeType = `http://localhost:8000/inspector-protocol/network/resources/source.map`;
await requestSourceMap(frameId, `Response for fetch with existing resource without content type: `, urlWithoutMimeType);
const nonExistentUrl = `http://localhost:8000/inspector-protocol/network/resources/source.map-DOES-NOT-EXIST`;
await requestSourceMap(frameId, `Response for fetch with non-existing resource: `, nonExistentUrl);
testRunner.completeTest();
})
Tests Page.loadNetworkResource for different frames with cookies
Number of frames in page: 3
Response for fetch: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
{"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
Response for fetch: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
{"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
Response for fetch: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
{"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
Response for fetching
https://127.0.0.1:8443/inspector-protocol/network/resources/echo-headers.php?headers=HTTP_COOKIE
from
https://127.0.0.1:8443
after setting cookie including credentials: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
HTTP_COOKIE: <not set>
Response for fetching
https://127.0.0.1:8443/inspector-protocol/network/resources/echo-headers.php?headers=HTTP_COOKIE
from
https://127.0.0.1:8443
after setting cookie including only samesite credentials: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
HTTP_COOKIE: <not set>
Response for fetching
https://devtools.oopif-a.test:8443/inspector-protocol/network/resources/echo-headers.php?headers=HTTP_COOKIE
from
https://devtools.oopif-a.test:8443
after setting cookie including credentials: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
HTTP_COOKIE: name=value
Response for fetching
https://devtools.oopif-a.test:8443/inspector-protocol/network/resources/echo-headers.php?headers=HTTP_COOKIE
from
https://devtools.oopif-a.test:8443
after setting cookie including only samesite credentials: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
HTTP_COOKIE: name=value
Response for fetching
https://devtools.oopif-b.test:8443/inspector-protocol/network/resources/echo-headers.php?headers=HTTP_COOKIE
from
https://devtools.oopif-b.test:8443
after setting cookie including credentials: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
HTTP_COOKIE: <not set>
Response for fetching
https://devtools.oopif-b.test:8443/inspector-protocol/network/resources/echo-headers.php?headers=HTTP_COOKIE
from
https://devtools.oopif-b.test:8443
after setting cookie including only samesite credentials: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
Steam content:
HTTP_COOKIE: <not set>
(async function(testRunner) {
const {page, session, dp} = await testRunner.startBlank(
`Tests Page.loadNetworkResource for different frames with cookies`);
await session.protocol.Network.clearBrowserCache();
await session.protocol.Network.setCacheDisabled({cacheDisabled: true});
await dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
let loadCount = 5;
let loadCallback;
const loadPromise = new Promise(fulfill => loadCallback = fulfill);
const allTargets = [];
async function initalizeTarget(dp) {
allTargets.push(dp);
await dp.Page.enable();
await dp.Network.enable();
dp.Page.onFrameStoppedLoading(e => {
if (!--loadCount)
loadCallback();
});
await dp.Runtime.runIfWaitingForDebugger();
}
dp.Target.onAttachedToTarget(async e => {
const child = session.createChild(e.params.sessionId);
const targetProtocol = child.protocol;
await initalizeTarget(targetProtocol);
});
await initalizeTarget(dp);
await dp.Page.navigate({url: 'https://127.0.0.1:8443/inspector-protocol/resources/iframe-navigation-secure.html'});
const frames = new Map();
function getFrameIds(dp, frameTree) {
frames.set(frameTree.frame.securityOrigin, {frameId: frameTree.frame.id, dp});
(frameTree.childFrames || []).forEach(getFrameIds.bind(null, dp));
}
await loadPromise;
const frameTargetList = [];
for (const dp of allTargets) {
const {result} = await dp.Page.getFrameTree();
frameTargetList.push({dp, frameTree: result.frameTree});
}
frameTargetList.sort((a,b) => a.frameTree.frame.url.localeCompare(b.frameTree.frame.url));
frameTargetList.forEach(({dp, frameTree}) => getFrameIds(dp, frameTree));
testRunner.log(`Number of frames in page: ${frames.size}`);
async function requestSourceMap(dp, frameId, testExplanation, url, options) {
const response = await dp.Network.loadNetworkResource({frameId, url, options: {disableCache: false, ...options}});
testRunner.log(response.result, testExplanation, ["headers", "stream"]);
if (response.result.resource.success) {
let result = await dp.IO.read({handle: response.result.resource.stream, size: 1000*1000});
testRunner.log(`Steam content:`)
testRunner.log(result.result.data);
await dp.IO.close({handle: response.result.resource.stream});
}
testRunner.log(``);
}
for (const {frameId, dp} of frames.values()) {
const url = `https://localhost:8443/inspector-protocol/network/resources/source.map.php`;
await requestSourceMap(dp, frameId, `Response for fetch: `, url, {includeCredentials: true});
}
// Now test cookie behavior.
const setCookieUrl = 'https://devtools.oopif-a.test:8443/inspector-protocol/network/resources/set-cookie.php?cookie='
+ encodeURIComponent('name=value; SameSite=None; Secure');
await session.evaluate(`fetch('${setCookieUrl}', {method: 'POST', credentials: 'include'})`);
const setCookieUrl2 = 'https://devtools.oopif-a.test:8443/inspector-protocol/network/resources/set-cookie.php?cookie='
+ encodeURIComponent('nameStrict=value2; SameSite=Strict; Secure');
await session.evaluate(`fetch('${setCookieUrl2}', {method: 'POST', credentials: 'include'})`);
const setCookieUrl3 = 'https://devtools.oopif-a.test:8443/inspector-protocol/network/resources/set-cookie.php?cookie='
+ encodeURIComponent('nameOther=value3; SameSite=Lax; Secure');
await session.evaluate(`fetch('${setCookieUrl3}', {method: 'POST', credentials: 'include'})`);
for (const [frameUrl, {dp, frameId}] of frames.entries()) {
const parsedURL = new URL(frameUrl);
const url = `${parsedURL.protocol}//${parsedURL.host}/inspector-protocol/network/resources/echo-headers.php?headers=HTTP_COOKIE`;
for (const includeCredentials of [true, false]) {
await requestSourceMap(dp, frameId, `Response for fetching\n${url}\n from\n${frameUrl}\nafter setting cookie ${includeCredentials?"including":"including only samesite"} credentials: `, url, {includeCredentials});
}
}
testRunner.completeTest();
})
Tests Network.loadNetworkResource for different frames
Number of frames in page: 3
Response for fetch: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
{
id : <number>
result : {
base64Encoded : false
data : {"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
eof : false
}
sessionId : <string>
}
Response for fetch: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
{
id : <number>
result : {
base64Encoded : false
data : {"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
eof : false
}
sessionId : <string>
}
Response for fetch: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
{
id : <number>
result : {
base64Encoded : false
data : {"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
eof : false
}
sessionId : <string>
}
(async function(testRunner) {
const {page, session, dp} = await testRunner.startBlank(
`Tests Network.loadNetworkResource for different frames`);
await session.protocol.Network.clearBrowserCache();
await session.protocol.Network.setCacheDisabled({cacheDisabled: true});
await dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
let loadCount = 5;
let loadCallback;
const loadPromise = new Promise(fulfill => loadCallback = fulfill);
const allTargets = [];
async function initalizeTarget(dp) {
allTargets.push(dp);
await dp.Page.enable();
await dp.Network.enable();
dp.Page.onFrameStoppedLoading(e => {
if (!--loadCount)
loadCallback();
});
await dp.Runtime.runIfWaitingForDebugger();
}
dp.Target.onAttachedToTarget(async e => {
const child = session.createChild(e.params.sessionId);
await initalizeTarget(child.protocol);
});
await initalizeTarget(dp);
await dp.Page.navigate({url: 'https://127.0.0.1:8443/inspector-protocol/resources/iframe-navigation-secure.html'});
const frames = new Map();
function getFrameIds(dp, frameTree) {
frames.set(frameTree.frame.securityOrigin, {frameId: frameTree.frame.id, dp});
(frameTree.childFrames || []).forEach(getFrameIds.bind(null, dp));
}
await loadPromise;
const frameTargetList = [];
for (const dp of allTargets) {
const {result} = await dp.Page.getFrameTree();
frameTargetList.push({dp, frameTree: result.frameTree});
}
frameTargetList.sort((a,b) => a.frameTree.frame.url.localeCompare(b.frameTree.frame.url));
frameTargetList.forEach(({dp, frameTree}) => getFrameIds(dp, frameTree));
testRunner.log(`Number of frames in page: ${frames.size}`);
for (const {frameId, dp} of frames.values()) {
const url = `https://localhost:8443/inspector-protocol/network/resources/source.map.php`;
const response = await dp.Network.loadNetworkResource({frameId, url, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response.result, `Response for fetch: `, ["headers", "stream"]);
if (response.result.resource.success) {
let result = await dp.IO.read({handle: response.result.resource.stream, size: 1000*1000});
testRunner.log(result);
await dp.IO.close({handle: response.result.resource.stream});
}
}
testRunner.completeTest();
})
Tests Page.loadNetworkResource for different frames with cookies
Number of frames in page: 3
Response for fetch: {
id : <number>
result : {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
sessionId : <string>
}
Response for fetch: {
error : {
code : -32602
message : Frame not under control of agent host
}
id : <number>
sessionId : <string>
}
Response for fetch: {
error : {
code : -32602
message : Frame not under control of agent host
}
id : <number>
sessionId : <string>
}
(async function(testRunner) {
const {page, session, dp} = await testRunner.startBlank(
`Tests Page.loadNetworkResource for different frames with cookies`);
await session.protocol.Network.clearBrowserCache();
await session.protocol.Network.setCacheDisabled({cacheDisabled: true});
await dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
let loadCount = 5;
let loadCallback;
const loadPromise = new Promise(fulfill => loadCallback = fulfill);
const allTargets = [];
async function initalizeTarget(dp) {
allTargets.push(dp);
await dp.Page.enable();
await dp.Network.enable();
dp.Page.onFrameStoppedLoading(e => {
if (!--loadCount)
loadCallback();
});
await dp.Runtime.runIfWaitingForDebugger();
}
dp.Target.onAttachedToTarget(async e => {
const child = session.createChild(e.params.sessionId);
const targetProtocol = child.protocol;
await initalizeTarget(targetProtocol);
});
await initalizeTarget(dp);
await dp.Page.navigate({url: 'https://127.0.0.1:8443/inspector-protocol/resources/iframe-navigation-secure.html'});
const frames = new Map();
function getFrameIds(dp, frameTree) {
frames.set(frameTree.frame.securityOrigin, {frameId: frameTree.frame.id, dp});
(frameTree.childFrames || []).forEach(getFrameIds.bind(null, dp));
}
await loadPromise;
const frameTargetList = [];
for (const dp of allTargets) {
const {result} = await dp.Page.getFrameTree();
frameTargetList.push({dp, frameTree: result.frameTree});
}
frameTargetList.sort((a,b) => a.frameTree.frame.url.localeCompare(b.frameTree.frame.url));
frameTargetList.forEach(({dp, frameTree}) => getFrameIds(dp, frameTree));
testRunner.log(`Number of frames in page: ${frames.size}`);
// Try to fetch resources on behalf of OOPIFs to provocate an error.
for (const {frameId} of frames.values()) {
const url = `https://localhost:8443/inspector-protocol/network/resources/source.map`;
const response1 = await dp.Network.loadNetworkResource({frameId, url, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response1, `Response for fetch: `, ["headers", "id", "sessionId", "stream"]);
}
testRunner.completeTest();
})
Tests basic error validation for Network.loadNetworkResource
Response for invalid target and invalid url: {
error : {
code : -32602
message : The url must be valid and have scheme http or https
}
id : <number>
sessionId : <string>
}
Response for invalid target and valid url: {
error : {
code : -32602
message : Frame not found
}
id : <number>
sessionId : <string>
}
Response for valid target and invalid url: {
error : {
code : -32602
message : The url must be valid and have scheme http or https
}
id : <number>
sessionId : <string>
}
(async function(testRunner) {
const {page, session, dp} = await testRunner.startBlank(
`Tests basic error validation for Network.loadNetworkResource`);
await dp.Network.enable();
const response1 = await dp.Network.loadNetworkResource({frameId: `invalid`, url: `invalid`, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response1, `Response for invalid target and invalid url: `);
const response2 = await dp.Network.loadNetworkResource({frameId: `invalid`, url: `https://example.com/source.map`, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response2, `Response for invalid target and valid url: `);
const frameId = (await dp.Target.getTargetInfo()).result.targetInfo.targetId;
const response3 = await dp.Network.loadNetworkResource({frameId, url: `invalid`, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response3, `Response for valid target and invalid url: `);
testRunner.completeTest();
})
Tests that we can load resources from a service worker.
Response for fetch with existing resource: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
(async function(testRunner) {
var {page, session, dp} = await testRunner.startBlank(
'Tests that we can load resources from a service worker.');
const swHelper = (await testRunner.loadScript('../service-worker/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;
});
const serviceWorkerURL = '/inspector-protocol/service-worker/resources/blank-service-worker.js';
await swHelper.installSWAndWaitForActivated(serviceWorkerURL);
swdp.Network.enable();
const url = `http://localhost:8000/inspector-protocol/network/resources/source.map`;
const response1 = await swdp.Network.loadNetworkResource({frameId: "", url, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response1.result, `Response for fetch with existing resource: `, ["headers", "stream"]);
testRunner.completeTest();
});
Tests that we can load resources from a dedicated worker.
1
Response for fetch with existing resource: {
resource : {
headers : <object>
httpStatusCode : 200
stream : <string>
success : true
}
}
(async function(testRunner) {
const {page, session, dp} = await testRunner.startURL(
'resources/page-with-shared-worker.html',
`Tests that we can load resources from a dedicated worker.`);
const target = testRunner.browserP().Target;
await target.setDiscoverTargets({discover: true});
await Promise.all([
target.setDiscoverTargets({discover: true}),
target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false, flatten: true}),
]);
const response = await target.getTargets();
const sharedWorkers = response.result.targetInfos.filter(info => info.type === "shared_worker");
testRunner.log(sharedWorkers.length, `Number of discovered shared workers`);
const [sharedWorker] = sharedWorkers;
const {result} = await target.attachToTarget({targetId: sharedWorker.targetId, flatten: true});
const swdp = session.createChild(result.sessionId).protocol;
await swdp.Network.enable();
const url = `http://localhost:8000/inspector-protocol/network/resources/source.map`;
const response1 = await swdp.Network.loadNetworkResource({frameId: "", url, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response1.result, `Response for fetch with existing resource: `, ["headers", "stream"]);
testRunner.completeTest();
});
Verifies that we can retrieve a request body consisting of blob in service worker.
OK
{
id : <number>
result : {
}
sessionId : <string>
}
Response for fetch with existing resource: {
error : {
code : -32000
message : Target not supported
}
id : <number>
sessionId : <string>
}
(async function(testRunner) {
var {page, session, dp} = await testRunner.startBlank(
'Verifies that we can retrieve a request body consisting of blob in service worker.');
await Promise.all([
dp.Target.setDiscoverTargets({discover: true}),
dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false, flatten: true}),
]);
const swTargetPromises = [
dp.Target.onceTargetCreated(),
dp.Target.onceAttachedToTarget(),
];
await session.evaluate(`new Worker('/inspector-protocol/network/resources/worker.js')`);
const [swTarget, swAttachedEvent] = await Promise.all(swTargetPromises);
testRunner.log("OK");
const swdp = session.createChild(swAttachedEvent.params.sessionId).protocol;
const result = await swdp.Network.enable();
testRunner.log(result);
const url = `http://localhost:8000/inspector-protocol/network/resources/source.map`;
const response1 = await swdp.Network.loadNetworkResource({frameId: "", url, options: {disableCache:false, includeCredentials: false}});
testRunner.log(response1, `Response for fetch with existing resource: `, ["headers", "id", "sessionId"]);
testRunner.completeTest();
});
<script>
const worker = new SharedWorker('/inspector-protocol/network/resources/worker.js');
</script>
{"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}
<?php
header('Content-Type: application/json');
echo '{"version":3,"file":"source.js","sourceRoot":"","sources":["source.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA"}'
?>
console.log("Worker");
onmessage = function(e) {
console.log(e);
};
<html>
<body>
Dummy page.
<iframe src="https://devtools.oopif-a.test:8443/inspector-protocol/resources/meta-tag.html"></iframe>
<iframe src="https://devtools.oopif-b.test:8443/inspector-protocol/resources/meta-tag.html"></iframe>
<iframe src="inspector-protocol/resources/meta-tag.html"></iframe>
</body>
</html>
...@@ -75,6 +75,7 @@ Refer to README.md for content description and update process. ...@@ -75,6 +75,7 @@ Refer to README.md for content description and update process.
<item id="desktop_ios_promotion" added_in_milestone="63" hash_code="13694792" type="0" deprecated="2018-11-04" content_hash_code="19776951" file_path=""/> <item id="desktop_ios_promotion" added_in_milestone="63" hash_code="13694792" type="0" deprecated="2018-11-04" content_hash_code="19776951" file_path=""/>
<item id="device_geolocation_request" added_in_milestone="62" hash_code="77673751" type="0" deprecated="2017-10-20" content_hash_code="97181773" file_path=""/> <item id="device_geolocation_request" added_in_milestone="62" hash_code="77673751" type="0" deprecated="2017-10-20" content_hash_code="97181773" file_path=""/>
<item id="device_management_service" added_in_milestone="62" hash_code="117782019" type="0" content_hash_code="104419970" os_list="linux,windows" file_path="components/policy/core/common/cloud/device_management_service.cc"/> <item id="device_management_service" added_in_milestone="62" hash_code="117782019" type="0" content_hash_code="104419970" os_list="linux,windows" file_path="components/policy/core/common/cloud/device_management_service.cc"/>
<item id="devtools_cdp_network_resource" added_in_milestone="87" hash_code="60744935" type="0" content_hash_code="85833926" os_list="linux,windows" file_path="content/browser/devtools/protocol/devtools_network_resource_loader.cc"/>
<item id="devtools_free_data_source" added_in_milestone="62" hash_code="22774132" type="0" content_hash_code="35733000" os_list="linux,windows" file_path="chrome/browser/ui/webui/devtools_ui_data_source.cc"/> <item id="devtools_free_data_source" added_in_milestone="62" hash_code="22774132" type="0" content_hash_code="35733000" os_list="linux,windows" file_path="chrome/browser/ui/webui/devtools_ui_data_source.cc"/>
<item id="devtools_handle_front_end_messages" added_in_milestone="62" hash_code="135636011" type="0" content_hash_code="76808593" os_list="linux,windows" file_path="content/shell/browser/shell_devtools_bindings.cc"/> <item id="devtools_handle_front_end_messages" added_in_milestone="62" hash_code="135636011" type="0" content_hash_code="76808593" os_list="linux,windows" file_path="content/shell/browser/shell_devtools_bindings.cc"/>
<item id="devtools_hard_coded_data_source" added_in_milestone="62" hash_code="111565057" type="0" content_hash_code="75183720" os_list="linux,windows" file_path="chrome/browser/ui/webui/devtools_ui_data_source.cc"/> <item id="devtools_hard_coded_data_source" added_in_milestone="62" hash_code="111565057" type="0" content_hash_code="75183720" os_list="linux,windows" file_path="chrome/browser/ui/webui/devtools_ui_data_source.cc"/>
......
...@@ -38,6 +38,7 @@ hidden="true" so that these annotations don't show up in the document. ...@@ -38,6 +38,7 @@ hidden="true" so that these annotations don't show up in the document.
<traffic_annotation unique_id="browser_switcher_ieem_sitelist"/> <traffic_annotation unique_id="browser_switcher_ieem_sitelist"/>
<traffic_annotation unique_id="adb_client_socket"/> <traffic_annotation unique_id="adb_client_socket"/>
<traffic_annotation unique_id="devtools_network_resource"/> <traffic_annotation unique_id="devtools_network_resource"/>
<traffic_annotation unique_id="devtools_cdp_network_resource"/>
<traffic_annotation unique_id="downloads_api_run_async"/> <traffic_annotation unique_id="downloads_api_run_async"/>
<traffic_annotation unique_id="media_feeds_checker"/> <traffic_annotation unique_id="media_feeds_checker"/>
<traffic_annotation unique_id="isolated_prerender_loader"/> <traffic_annotation unique_id="isolated_prerender_loader"/>
......
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