blob: 8a8830c3d98fcc551e5414603eaefd163f3d69c3 [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 protocol {
namespace {
using GetCookiesCallback = protocol::Network::Backend::GetCookiesCallback;
using GetAllCookiesCallback = protocol::Network::Backend::GetAllCookiesCallback;
using SetCookieCallback = protocol::Network::Backend::SetCookieCallback;
using DeleteCookieCallback = protocol::Network::Backend::DeleteCookieCallback;
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 GotCookiesOnIO(
const net::CookieStore::GetCookieListCallback& 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 net::CookieStore::GetCookieListCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::URLRequestContext* request_context =
GetRequestContextOnIO(resource_context, context_getter, url);
request_context->cookie_store()->GetAllCookiesForURLAsync(
url, base::Bind(&GotCookiesOnIO, callback));
}
void GetAllCookiesOnIO(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const net::CookieStore::GetCookieListCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::URLRequestContext* request_context =
context_getter->GetURLRequestContext();
request_context->cookie_store()->GetAllCookiesAsync(
base::Bind(&GotCookiesOnIO, callback));
}
void DeletedCookieOnIO(std::unique_ptr<DeleteCookieCallback> callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&DeleteCookieCallback::sendSuccess,
base::Passed(std::move(callback))));
}
void DeleteCookieOnIO(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const std::string& cookie_name,
std::unique_ptr<DeleteCookieCallback> 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,
base::Passed(std::move(callback))));
}
void CookieSetOnIO(std::unique_ptr<SetCookieCallback> callback, bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&SetCookieCallback::sendSuccess,
base::Passed(std::move(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,
std::unique_ptr<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, base::Passed(std::move(callback))));
}
template <typename Callback>
class GetCookiesCommandBase {
public:
GetCookiesCommandBase(std::unique_ptr<Callback> callback)
: callback_(std::move(callback)), request_count_(0) {}
protected:
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_) {
SendResponse();
delete this;
}
}
void SendResponse() {
std::unique_ptr<protocol::Array<Network::Cookie>> cookies =
protocol::Array<Network::Cookie>::create();
for (const auto& pair : cookies_) {
const net::CanonicalCookie& cookie = pair.second;
std::unique_ptr<Network::Cookie> devtools_cookie =
Network::Cookie::Create()
.SetName(cookie.Name())
.SetValue(cookie.Value())
.SetDomain(cookie.Domain())
.SetPath(cookie.Path())
.SetExpires(cookie.ExpiryDate().ToDoubleT() * 1000)
.SetSize(cookie.Name().length() + cookie.Value().length())
.SetHttpOnly(cookie.IsHttpOnly())
.SetSecure(cookie.IsSecure())
.SetSession(!cookie.IsPersistent())
.Build();
switch (cookie.SameSite()) {
case net::CookieSameSite::STRICT_MODE:
devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::Strict);
break;
case net::CookieSameSite::LAX_MODE:
devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::Lax);
break;
case net::CookieSameSite::NO_RESTRICTION:
break;
}
cookies->addItem(std::move(devtools_cookie));
}
callback_->sendSuccess(std::move(cookies));
}
std::unique_ptr<Callback> callback_;
int request_count_;
base::hash_map<std::string, net::CanonicalCookie> cookies_;
};
class GetCookiesCommand : public GetCookiesCommandBase<GetCookiesCallback> {
public:
GetCookiesCommand(RenderFrameHostImpl* frame_host,
std::unique_ptr<GetCookiesCallback> callback)
: GetCookiesCommandBase(std::move(callback)) {
net::CookieStore::GetCookieListCallback 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_;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&GetCookiesForURLOnIO,
base::Unretained(frame_host->GetSiteInstance()
->GetBrowserContext()
->GetResourceContext()),
base::Unretained(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));
}
}
};
class GetAllCookiesCommand
: public GetCookiesCommandBase<GetAllCookiesCallback> {
public:
GetAllCookiesCommand(RenderFrameHostImpl* frame_host,
std::unique_ptr<GetAllCookiesCallback> callback)
: GetCookiesCommandBase(std::move(callback)) {
net::CookieStore::GetCookieListCallback got_cookies_callback = base::Bind(
&GetAllCookiesCommand::GotCookiesForURL, base::Unretained(this));
request_count_ = 1;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&GetAllCookiesOnIO,
base::Unretained(frame_host->GetSiteInstance()
->GetBrowserContext()
->GetResourceContext()),
base::Unretained(frame_host->GetProcess()
->GetStoragePartition()
->GetURLRequestContext()),
got_cookies_callback));
}
};
} // namespace
NetworkHandler::NetworkHandler()
: host_(nullptr), enabled_(false) {
}
NetworkHandler::~NetworkHandler() {
}
void NetworkHandler::Wire(UberDispatcher* dispatcher) {
Network::Dispatcher::wire(dispatcher, this);
}
void NetworkHandler::SetRenderFrameHost(RenderFrameHostImpl* host) {
host_ = host;
}
Response NetworkHandler::Enable(Maybe<int> max_total_size,
Maybe<int> max_resource_size) {
enabled_ = true;
return Response::FallThrough();
}
Response NetworkHandler::Disable() {
enabled_ = false;
return Response::FallThrough();
}
Response NetworkHandler::ClearBrowserCache() {
if (host_)
GetContentClient()->browser()->ClearCache(host_);
return Response::OK();
}
Response NetworkHandler::ClearBrowserCookies() {
if (host_)
GetContentClient()->browser()->ClearCookies(host_);
return Response::OK();
}
void NetworkHandler::GetCookies(
std::unique_ptr<GetCookiesCallback> callback) {
if (!host_)
callback->sendFailure(Response::InternalError());
else
new GetCookiesCommand(host_, std::move(callback));
}
void NetworkHandler::GetAllCookies(
std::unique_ptr<GetAllCookiesCallback> callback) {
if (!host_)
callback->sendFailure(Response::InternalError());
else
new GetAllCookiesCommand(host_, std::move(callback));
}
void NetworkHandler::SetCookie(
const std::string& url,
const std::string& name,
const std::string& value,
Maybe<std::string> domain,
Maybe<std::string> path,
Maybe<bool> secure,
Maybe<bool> http_only,
Maybe<std::string> same_site,
Maybe<double> expires,
std::unique_ptr<SetCookieCallback> callback) {
if (!host_) {
callback->sendFailure(Response::InternalError());
return;
}
net::CookieSameSite same_site_enum = net::CookieSameSite::DEFAULT_MODE;
if (same_site.isJust()) {
if (same_site.fromJust() == Network::CookieSameSiteEnum::Lax)
same_site_enum = net::CookieSameSite::LAX_MODE;
else if (same_site.fromJust() == Network::CookieSameSiteEnum::Strict)
same_site_enum = net::CookieSameSite::STRICT_MODE;
}
base::Time expiration_date;
if (expires.isJust()) {
expiration_date = expires.fromJust() == 0
? base::Time::UnixEpoch()
: base::Time::FromDoubleT(expires.fromJust());
}
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
&SetCookieOnIO,
base::Unretained(host_->GetSiteInstance()->GetBrowserContext()->
GetResourceContext()),
base::Unretained(host_->GetProcess()->GetStoragePartition()->
GetURLRequestContext()),
GURL(url), name, value, domain.fromMaybe(""), path.fromMaybe(""),
secure.fromMaybe(false), http_only.fromMaybe(false), same_site_enum,
expiration_date, base::Passed(std::move(callback))));
}
void NetworkHandler::DeleteCookie(
const std::string& cookie_name,
const std::string& url,
std::unique_ptr<DeleteCookieCallback> callback) {
if (!host_) {
callback->sendFailure(Response::InternalError());
return;
}
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
&DeleteCookieOnIO,
base::Unretained(host_->GetSiteInstance()->GetBrowserContext()->
GetResourceContext()),
base::Unretained(host_->GetProcess()->GetStoragePartition()->
GetURLRequestContext()),
GURL(url),
cookie_name,
base::Passed(std::move(callback))));
}
Response NetworkHandler::CanEmulateNetworkConditions(bool* result) {
*result = false;
return Response::OK();
}
} // namespace protocol
} // namespace content