in libcef/browser/devtools/devtools_frontend.cc [369:545]
void CefDevToolsFrontend::HandleMessageFromDevToolsFrontend(
base::Value message) {
if (!message.is_dict())
return;
const std::string* method = message.FindStringKey("method");
if (!method)
return;
int request_id = message.FindIntKey("id").value_or(0);
base::Value* params_value = message.FindListKey("params");
// Since we've received message by value, we can take the list.
base::Value::ListStorage params;
if (params_value) {
params = std::move(*params_value).TakeList();
}
if (*method == "dispatchProtocolMessage") {
if (params.size() < 1)
return;
const std::string* protocol_message = params[0].GetIfString();
if (!agent_host_ || !protocol_message)
return;
if (ProtocolLoggingEnabled()) {
LogProtocolMessage(ProtocolMessageType::METHOD, *protocol_message);
}
agent_host_->DispatchProtocolMessage(
this, base::as_bytes(base::make_span(*protocol_message)));
} else if (*method == "loadCompleted") {
web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
u"DevToolsAPI.setUseSoftMenu(true);", base::NullCallback());
} else if (*method == "loadNetworkResource") {
if (params.size() < 3)
return;
// TODO(pfeldman): handle some of the embedder messages in content.
const std::string* url = params[0].GetIfString();
const std::string* headers = params[1].GetIfString();
absl::optional<const int> stream_id = params[2].GetIfInt();
if (!url || !headers || !stream_id.has_value()) {
return;
}
GURL gurl(*url);
if (!gurl.is_valid()) {
base::DictionaryValue response;
response.SetInteger("statusCode", 404);
response.SetBoolean("urlValid", false);
SendMessageAck(request_id, std::move(response));
return;
}
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation(
"devtools_handle_front_end_messages", 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: OTHER
}
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
}
}
})");
// Based on DevToolsUIBindings::LoadNetworkResource.
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = gurl;
// TODO(caseq): this preserves behavior of URLFetcher-based
// implementation. We really need to pass proper first party origin from
// the front-end.
resource_request->site_for_cookies = net::SiteForCookies::FromUrl(gurl);
resource_request->headers.AddHeadersFromString(*headers);
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
if (gurl.SchemeIsFile()) {
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote =
content::CreateFileURLLoaderFactory(
base::FilePath() /* profile_path */,
nullptr /* shared_cors_origin_access_list */);
url_loader_factory = network::SharedURLLoaderFactory::Create(
std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
std::move(pending_remote)));
} else if (content::HasWebUIScheme(gurl)) {
base::DictionaryValue response;
response.SetInteger("statusCode", 403);
SendMessageAck(request_id, std::move(response));
return;
} else {
auto* partition =
web_contents()->GetBrowserContext()->GetStoragePartitionForUrl(gurl);
url_loader_factory = partition->GetURLLoaderFactoryForBrowserProcess();
}
auto simple_url_loader = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation);
auto resource_loader = std::make_unique<NetworkResourceLoader>(
*stream_id, this, std::move(simple_url_loader),
url_loader_factory.get(), request_id);
loaders_.insert(std::move(resource_loader));
return;
} else if (*method == "getPreferences") {
SendMessageAck(
request_id,
GetPrefs()->GetDictionary(prefs::kDevToolsPreferences)->Clone());
return;
} else if (*method == "setPreference") {
if (params.size() < 2)
return;
const std::string* name = params[0].GetIfString();
// We're just setting params[1] as a value anyways, so just make sure it's
// the type we want, but don't worry about getting it.
if (!name || !params[1].is_string())
return;
DictionaryPrefUpdate update(GetPrefs(), prefs::kDevToolsPreferences);
update.Get()->SetKey(*name, std::move(params[1]));
} else if (*method == "removePreference") {
const std::string* name = params[0].GetIfString();
if (!name)
return;
DictionaryPrefUpdate update(GetPrefs(), prefs::kDevToolsPreferences);
update.Get()->RemoveKey(*name);
} else if (*method == "requestFileSystems") {
web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
u"DevToolsAPI.fileSystemsLoaded([]);", base::NullCallback());
} else if (*method == "reattach") {
if (!agent_host_)
return;
agent_host_->DetachClient(this);
agent_host_->AttachClient(this);
} else if (*method == "registerExtensionsAPI") {
if (params.size() < 2)
return;
const std::string* origin = params[0].GetIfString();
const std::string* script = params[1].GetIfString();
if (!origin || !script)
return;
extensions_api_[*origin + "/"] = *script;
} else if (*method == "save") {
if (params.size() < 3)
return;
const std::string* url = params[0].GetIfString();
const std::string* content = params[1].GetIfString();
absl::optional<bool> save_as = params[2].GetIfBool();
if (!url || !content || !save_as.has_value())
return;
file_manager_.SaveToFile(*url, *content, *save_as);
} else if (*method == "append") {
if (params.size() < 2)
return;
const std::string* url = params[0].GetIfString();
const std::string* content = params[1].GetIfString();
if (!url || !content)
return;
file_manager_.AppendToFile(*url, *content);
} else {
return;
}
if (request_id)
SendMessageAck(request_id, base::Value());
}