blob: 33d00f66c63d77f8ea21c994ba10bc56fa2c10c2 [file] [log] [blame]
// Copyright 2014 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/network_handler.h"
#include <stddef.h>
#include "base/containers/hash_tables.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "net/cookies/cookie_store.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
namespace content {
namespace devtools {
namespace network {
using Response = DevToolsProtocolClient::Response;
using CookieListCallback = net::CookieStore::GetCookieListCallback;
using SetCookieCallback = net::CookieStore::SetCookiesCallback;
namespace {
net::URLRequestContext* GetRequestContextOnIO(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::URLRequestContext* context =
GetContentClient()->browser()->OverrideRequestContextForURL(
url, resource_context);
if (!context)
context = context_getter->GetURLRequestContext();
return context;
}
void GotCookiesForURLOnIO(
const CookieListCallback& callback,
const net::CookieList& cookie_list) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(callback, cookie_list));
}
void GetCookiesForURLOnIO(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const CookieListCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::URLRequestContext* request_context =
GetRequestContextOnIO(resource_context, context_getter, url);
request_context->cookie_store()->GetAllCookiesForURLAsync(
url, base::Bind(&GotCookiesForURLOnIO, callback));
}
void GetCookiesForURLOnUI(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const CookieListCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&GetCookiesForURLOnIO,
base::Unretained(resource_context),
base::Unretained(context_getter),
url,
callback));
}
void DeletedCookieOnIO(const base::Closure& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
callback);
}
void DeleteCookieOnIO(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const std::string& cookie_name,
const base::Closure& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::URLRequestContext* request_context =
GetRequestContextOnIO(resource_context, context_getter, url);
request_context->cookie_store()->DeleteCookieAsync(
url, cookie_name, base::Bind(&DeletedCookieOnIO, callback));
}
void DeleteCookieOnUI(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const std::string& cookie_name,
const base::Closure& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&DeleteCookieOnIO,
base::Unretained(resource_context),
base::Unretained(context_getter),
url,
cookie_name,
callback));
}
void CookieSetOnIO(const SetCookieCallback& callback, bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(callback, success));
}
void SetCookieOnIO(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const std::string& name,
const std::string& value,
const std::string& domain,
const std::string& path,
bool secure,
bool http_only,
net::CookieSameSite same_site,
base::Time expires,
const SetCookieCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::URLRequestContext* request_context =
GetRequestContextOnIO(resource_context, context_getter, url);
bool are_experimental_cookie_features_enabled =
request_context->network_delegate()
->AreExperimentalCookieFeaturesEnabled();
request_context->cookie_store()->SetCookieWithDetailsAsync(
url, name, value, domain, path,
base::Time(),
expires,
base::Time(),
secure,
http_only,
same_site,
are_experimental_cookie_features_enabled,
net::COOKIE_PRIORITY_DEFAULT,
base::Bind(&CookieSetOnIO, callback));
}
void SetCookieOnUI(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const std::string& name,
const std::string& value,
const std::string& domain,
const std::string& path,
bool secure,
bool http_only,
net::CookieSameSite same_site,
base::Time expires,
const SetCookieCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&SetCookieOnIO,
base::Unretained(resource_context),
base::Unretained(context_getter),
url, name, value, domain, path, secure, http_only,
same_site, expires, callback));
}
class GetCookiesCommand {
public:
explicit GetCookiesCommand(
RenderFrameHostImpl* frame_host,
const CookieListCallback& callback)
: callback_(callback),
request_count_(0) {
CookieListCallback got_cookies_callback = base::Bind(
&GetCookiesCommand::GotCookiesForURL, base::Unretained(this));
std::queue<FrameTreeNode*> queue;
queue.push(frame_host->frame_tree_node());
while (!queue.empty()) {
FrameTreeNode* node = queue.front();
queue.pop();
// Only traverse nodes with the same local root.
if (node->current_frame_host()->IsCrossProcessSubframe())
continue;
++request_count_;
GetCookiesForURLOnUI(
frame_host->GetSiteInstance()->GetBrowserContext()->
GetResourceContext(),
frame_host->GetProcess()->GetStoragePartition()->
GetURLRequestContext(),
node->current_url(),
got_cookies_callback);
for (size_t i = 0; i < node->child_count(); ++i)
queue.push(node->child_at(i));
}
}
private:
void GotCookiesForURL(const net::CookieList& cookie_list) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (const net::CanonicalCookie& cookie : cookie_list) {
std::string key = base::StringPrintf(
"%s::%s::%s::%d",
cookie.Name().c_str(),
cookie.Domain().c_str(),
cookie.Path().c_str(),
cookie.IsSecure());
cookies_[key] = cookie;
}
--request_count_;
if (!request_count_) {
net::CookieList list;
list.reserve(cookies_.size());
for (const auto& pair : cookies_)
list.push_back(pair.second);
callback_.Run(list);
delete this;
}
}
CookieListCallback callback_;
int request_count_;
base::hash_map<std::string, net::CanonicalCookie> cookies_;
};
} // namespace
typedef DevToolsProtocolClient::Response Response;
NetworkHandler::NetworkHandler() : host_(nullptr), weak_factory_(this) {
}
NetworkHandler::~NetworkHandler() {
}
void NetworkHandler::SetRenderFrameHost(RenderFrameHostImpl* host) {
host_ = host;
}
void NetworkHandler::SetClient(std::unique_ptr<Client> client) {
client_.swap(client);
}
Response NetworkHandler::ClearBrowserCache() {
if (host_)
GetContentClient()->browser()->ClearCache(host_);
return Response::OK();
}
Response NetworkHandler::ClearBrowserCookies() {
if (host_)
GetContentClient()->browser()->ClearCookies(host_);
return Response::OK();
}
Response NetworkHandler::GetCookies(DevToolsCommandId command_id) {
if (!host_)
return Response::InternalError("Could not connect to view");
new GetCookiesCommand(
host_,
base::Bind(&NetworkHandler::SendGetCookiesResponse,
weak_factory_.GetWeakPtr(),
command_id));
return Response::OK();
}
Response NetworkHandler::SetCookie(DevToolsCommandId command_id,
const std::string& url,
const std::string& name,
const std::string& value,
const std::string* domain,
const std::string* path,
bool* secure,
bool* http_only,
const std::string* same_site,
double* expires) {
if (!host_)
return Response::InternalError("Could not connect to view");
net::CookieSameSite same_site_enum = net::CookieSameSite::DEFAULT_MODE;
if (same_site && *same_site == kCookieSameSiteLax)
same_site_enum = net::CookieSameSite::LAX_MODE;
else if (same_site && *same_site == kCookieSameSiteStrict)
same_site_enum = net::CookieSameSite::STRICT_MODE;
base::Time expiration_date;
if (expires)
expiration_date = (*expires == 0)
? base::Time::UnixEpoch()
: base::Time::FromDoubleT(*expires);
SetCookieOnUI(
host_->GetSiteInstance()->GetBrowserContext()->GetResourceContext(),
host_->GetProcess()->GetStoragePartition()->GetURLRequestContext(),
GURL(url), name, value,
domain ? *domain : std::string(), path ? *path : std::string(),
secure ? *secure : false, http_only ? *http_only : false,
same_site_enum, expiration_date,
base::Bind(&NetworkHandler::SendSetCookieResponse,
weak_factory_.GetWeakPtr(),
command_id));
return Response::OK();
}
void NetworkHandler::SendSetCookieResponse(DevToolsCommandId command_id,
bool success) {
client_->SendSetCookieResponse(command_id,
SetCookieResponse::Create()->set_success(success));
}
void NetworkHandler::SendGetCookiesResponse(
DevToolsCommandId command_id,
const net::CookieList& cookie_list) {
std::vector<scoped_refptr<Cookie>> cookies;
for (size_t i = 0; i < cookie_list.size(); ++i) {
const net::CanonicalCookie& cookie = cookie_list[i];
scoped_refptr<Cookie> devtools_cookie = Cookie::Create()
->set_name(cookie.Name())
->set_value(cookie.Value())
->set_domain(cookie.Domain())
->set_path(cookie.Path())
->set_expires(cookie.ExpiryDate().ToDoubleT() * 1000)
->set_size(cookie.Name().length() + cookie.Value().length())
->set_http_only(cookie.IsHttpOnly())
->set_secure(cookie.IsSecure())
->set_session(!cookie.IsPersistent());
switch (cookie.SameSite()) {
case net::CookieSameSite::STRICT_MODE:
devtools_cookie->set_same_site(kCookieSameSiteStrict);
break;
case net::CookieSameSite::LAX_MODE:
devtools_cookie->set_same_site(kCookieSameSiteLax);
break;
case net::CookieSameSite::NO_RESTRICTION:
break;
}
cookies.push_back(devtools_cookie);
}
client_->SendGetCookiesResponse(command_id,
GetCookiesResponse::Create()->set_cookies(cookies));
}
Response NetworkHandler::DeleteCookie(
DevToolsCommandId command_id,
const std::string& cookie_name,
const std::string& url) {
if (!host_)
return Response::InternalError("Could not connect to view");
DeleteCookieOnUI(
host_->GetSiteInstance()->GetBrowserContext()->GetResourceContext(),
host_->GetProcess()->GetStoragePartition()->GetURLRequestContext(),
GURL(url),
cookie_name,
base::Bind(&NetworkHandler::SendDeleteCookieResponse,
weak_factory_.GetWeakPtr(),
command_id));
return Response::OK();
}
void NetworkHandler::SendDeleteCookieResponse(DevToolsCommandId command_id) {
client_->SendDeleteCookieResponse(command_id,
DeleteCookieResponse::Create());
}
Response NetworkHandler::CanEmulateNetworkConditions(bool* result) {
*result = false;
return Response::OK();
}
Response NetworkHandler::EmulateNetworkConditions(
bool offline,
double latency,
double download_throughput,
double upload_throughput,
const std::string* connection_type) {
return Response::FallThrough();
}
} // namespace network
} // namespace devtools
} // namespace content