Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: sourcemaps over file:// not loading with network service #21494

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
176 changes: 140 additions & 36 deletions shell/browser/ui/inspectable_web_contents_impl.cc
Expand Up @@ -27,10 +27,12 @@
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/file_select_listener.h"
#include "content/public/browser/file_url_loader.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/user_agent.h"
#include "ipc/ipc_channel.h"
Expand Down Expand Up @@ -61,7 +63,7 @@ const char kChromeUIDevToolsURL[] =
"textColor=rgba(0,0,0,1)&"
"experiments=true";
const char kChromeUIDevToolsRemoteFrontendBase[] =
"https://devtools-frontend.appspot.com/";
"https://chrome-devtools-frontend.appspot.com/";
const char kChromeUIDevToolsRemoteFrontendPath[] = "serve_file";

const char kDevToolsBoundsPref[] = "electron.devtools.bounds";
Expand Down Expand Up @@ -149,29 +151,87 @@ GURL GetDevToolsURL(bool can_dock) {
return GURL(url_string);
}

constexpr base::TimeDelta kInitialBackoffDelay =
base::TimeDelta::FromMilliseconds(250);
constexpr base::TimeDelta kMaxBackoffDelay = base::TimeDelta::FromSeconds(10);

} // namespace

class InspectableWebContentsImpl::NetworkResourceLoader
: public network::SimpleURLLoaderStreamConsumer {
public:
NetworkResourceLoader(int stream_id,
InspectableWebContentsImpl* bindings,
std::unique_ptr<network::SimpleURLLoader> loader,
network::mojom::URLLoaderFactory* url_loader_factory,
const DispatchCallback& callback)
class URLLoaderFactoryHolder {
public:
network::mojom::URLLoaderFactory* get() {
return ptr_.get() ? ptr_.get() : refptr_.get();
}
void operator=(std::unique_ptr<network::mojom::URLLoaderFactory>&& ptr) {
ptr_ = std::move(ptr);
}
void operator=(scoped_refptr<network::SharedURLLoaderFactory>&& refptr) {
refptr_ = std::move(refptr);
}

private:
std::unique_ptr<network::mojom::URLLoaderFactory> ptr_;
scoped_refptr<network::SharedURLLoaderFactory> refptr_;
};

static void Create(int stream_id,
InspectableWebContentsImpl* bindings,
const network::ResourceRequest& resource_request,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
URLLoaderFactoryHolder url_loader_factory,
const DispatchCallback& callback,
base::TimeDelta retry_delay = base::TimeDelta()) {
auto resource_loader =
std::make_unique<InspectableWebContentsImpl::NetworkResourceLoader>(
stream_id, bindings, resource_request, traffic_annotation,
std::move(url_loader_factory), callback, retry_delay);
bindings->loaders_.insert(std::move(resource_loader));
}

NetworkResourceLoader(
int stream_id,
InspectableWebContentsImpl* bindings,
const network::ResourceRequest& resource_request,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
URLLoaderFactoryHolder url_loader_factory,
const DispatchCallback& callback,
base::TimeDelta delay)
: stream_id_(stream_id),
bindings_(bindings),
loader_(std::move(loader)),
callback_(callback) {
resource_request_(resource_request),
traffic_annotation_(traffic_annotation),
loader_(network::SimpleURLLoader::Create(
std::make_unique<network::ResourceRequest>(resource_request),
traffic_annotation)),
url_loader_factory_(std::move(url_loader_factory)),
callback_(callback),
retry_delay_(delay) {
loader_->SetOnResponseStartedCallback(base::BindOnce(
&NetworkResourceLoader::OnResponseStarted, base::Unretained(this)));
loader_->DownloadAsStream(url_loader_factory, this);
timer_.Start(FROM_HERE, delay,
base::BindRepeating(&NetworkResourceLoader::DownloadAsStream,
base::Unretained(this)));
}

NetworkResourceLoader(const NetworkResourceLoader&) = delete;
NetworkResourceLoader& operator=(const NetworkResourceLoader&) = delete;

private:
void DownloadAsStream() {
loader_->DownloadAsStream(url_loader_factory_.get(), this);
}

base::TimeDelta GetNextExponentialBackoffDelay(const base::TimeDelta& delta) {
if (delta.is_zero()) {
return kInitialBackoffDelay;
} else {
return delta * 1.3;
}
}

void OnResponseStarted(const GURL& final_url,
const network::ResourceResponseHead& response_head) {
response_headers_ = response_head.headers;
Expand All @@ -198,21 +258,34 @@ class InspectableWebContentsImpl::NetworkResourceLoader
}

void OnComplete(bool success) override {
base::DictionaryValue response;
response.SetInteger("statusCode", response_headers_
? response_headers_->response_code()
: 200);

auto headers = std::make_unique<base::DictionaryValue>();
size_t iterator = 0;
std::string name;
std::string value;
while (response_headers_ &&
response_headers_->EnumerateHeaderLines(&iterator, &name, &value))
headers->SetString(name, value);

response.Set("headers", std::move(headers));
callback_.Run(&response);
if (!success && loader_->NetError() == net::ERR_INSUFFICIENT_RESOURCES &&
retry_delay_ < kMaxBackoffDelay) {
const base::TimeDelta delay =
GetNextExponentialBackoffDelay(retry_delay_);
LOG(WARNING) << "InspectableWebContentsImpl::NetworkResourceLoader id = "
<< stream_id_
<< " failed with insufficient resources, retrying in "
<< delay << "." << std::endl;
NetworkResourceLoader::Create(
stream_id_, bindings_, resource_request_, traffic_annotation_,
std::move(url_loader_factory_), callback_, delay);
} else {
base::DictionaryValue response;
response.SetInteger("statusCode", response_headers_
? response_headers_->response_code()
: 200);

auto headers = std::make_unique<base::DictionaryValue>();
size_t iterator = 0;
std::string name;
std::string value;
while (response_headers_ &&
response_headers_->EnumerateHeaderLines(&iterator, &name, &value))
headers->SetString(name, value);

response.Set("headers", std::move(headers));
callback_.Run(&response);
}

bindings_->loaders_.erase(bindings_->loaders_.find(this));
}
Expand All @@ -221,9 +294,14 @@ class InspectableWebContentsImpl::NetworkResourceLoader

const int stream_id_;
InspectableWebContentsImpl* const bindings_;
const network::ResourceRequest resource_request_;
const net::NetworkTrafficAnnotationTag traffic_annotation_;
std::unique_ptr<network::SimpleURLLoader> loader_;
URLLoaderFactoryHolder url_loader_factory_;
DispatchCallback callback_;
scoped_refptr<net::HttpResponseHeaders> response_headers_;
base::OneShotTimer timer_;
base::TimeDelta retry_delay_;
};

// Implemented separately on each platform.
Expand Down Expand Up @@ -527,19 +605,45 @@ void InspectableWebContentsImpl::LoadNetworkResource(
return;
}

auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = gurl;
resource_request->headers.AddHeadersFromString(headers);

auto* partition = content::BrowserContext::GetDefaultStoragePartition(
GetDevToolsWebContents()->GetBrowserContext());
auto factory = partition->GetURLLoaderFactoryForBrowserProcess();
// Create traffic annotation tag.
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("devtools_network_resource", R"(
semantics {
sender: "Developer Tools"
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."
})");

network::ResourceRequest resource_request;
resource_request.url = gurl;
resource_request.site_for_cookies = gurl;
resource_request.headers.AddHeadersFromString(headers);

NetworkResourceLoader::URLLoaderFactoryHolder url_loader_factory;
if (gurl.SchemeIsFile()) {
url_loader_factory = content::CreateFileURLLoaderFactory(
base::FilePath() /* profile_path */,
nullptr /* shared_cors_origin_access_list */);
} else {
auto* partition = content::BrowserContext::GetDefaultStoragePartition(
GetDevToolsWebContents()->GetBrowserContext());
url_loader_factory = partition->GetURLLoaderFactoryForBrowserProcess();
}

auto simple_url_loader = network::SimpleURLLoader::Create(
std::move(resource_request), MISSING_TRAFFIC_ANNOTATION);
auto resource_loader = std::make_unique<NetworkResourceLoader>(
stream_id, this, std::move(simple_url_loader), factory.get(), callback);
loaders_.insert(std::move(resource_loader));
NetworkResourceLoader::Create(stream_id, this, resource_request,
traffic_annotation,
std::move(url_loader_factory), callback);
}

void InspectableWebContentsImpl::SetIsDocked(const DispatchCallback& callback,
Expand Down