blob: 078c941319d07a2e9f746cca7452286457c85d0f [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 "chrome/browser/prerender/prerender_cookie_store.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
namespace prerender {
PrerenderCookieStore::PrerenderCookieStore(
scoped_refptr<net::CookieMonster> default_cookie_monster,
const base::Closure& cookie_conflict_cb)
: in_forwarding_mode_(false),
default_cookie_monster_(default_cookie_monster),
changes_cookie_monster_(new net::CookieMonster(NULL, NULL)),
cookie_conflict_cb_(cookie_conflict_cb),
cookie_conflict_(false) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(default_cookie_monster_ != NULL);
DCHECK(default_cookie_monster_->loaded());
}
PrerenderCookieStore::~PrerenderCookieStore() {
}
void PrerenderCookieStore::SetCookieWithOptionsAsync(
const GURL& url,
const std::string& cookie_line,
const net::CookieOptions& options,
const SetCookiesCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
CookieOperation op;
op.op = COOKIE_OP_SET_COOKIE_WITH_OPTIONS_ASYNC;
op.url = url;
op.cookie_line = cookie_line;
op.options = options;
GetCookieStoreForCookieOpAndLog(op)->
SetCookieWithOptionsAsync(url, cookie_line, options, callback);
}
void PrerenderCookieStore::GetCookiesWithOptionsAsync(
const GURL& url,
const net::CookieOptions& options,
const GetCookiesCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
CookieOperation op;
op.op = COOKIE_OP_GET_COOKIES_WITH_OPTIONS_ASYNC;
op.url = url;
op.options = options;
GetCookieStoreForCookieOpAndLog(op)->
GetCookiesWithOptionsAsync(url, options, callback);
}
void PrerenderCookieStore::GetAllCookiesForURLAsync(
const GURL& url,
const GetCookieListCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
CookieOperation op;
op.op = COOKIE_OP_GET_ALL_COOKIES_FOR_URL_ASYNC;
op.url = url;
GetCookieStoreForCookieOpAndLog(op)->GetAllCookiesForURLAsync(url, callback);
}
void PrerenderCookieStore::DeleteCookieAsync(const GURL& url,
const std::string& cookie_name,
const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
CookieOperation op;
op.op = COOKIE_OP_DELETE_COOKIE_ASYNC;
op.url = url;
op.cookie_name = cookie_name;
GetCookieStoreForCookieOpAndLog(op)->DeleteCookieAsync(url, cookie_name,
callback);
}
void PrerenderCookieStore::DeleteAllCreatedBetweenAsync(
const base::Time& delete_begin,
const base::Time& delete_end,
const DeleteCallback& callback) {
NOTREACHED();
}
void PrerenderCookieStore::DeleteAllCreatedBetweenForHostAsync(
const base::Time delete_begin,
const base::Time delete_end,
const GURL& url,
const DeleteCallback& callback) {
NOTREACHED();
}
void PrerenderCookieStore::DeleteSessionCookiesAsync(const DeleteCallback&) {
NOTREACHED();
}
net::CookieMonster* PrerenderCookieStore::GetCookieMonster() {
NOTREACHED();
return NULL;
}
net::CookieStore* PrerenderCookieStore::GetCookieStoreForCookieOpAndLog(
const CookieOperation& op) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
std::string key = default_cookie_monster_->GetKey(op.url.host());
bool is_read_only = (op.op == COOKIE_OP_GET_COOKIES_WITH_OPTIONS_ASYNC ||
op.op == COOKIE_OP_GET_ALL_COOKIES_FOR_URL_ASYNC);
if (in_forwarding_mode_)
return default_cookie_monster_;
DCHECK(changes_cookie_monster_ != NULL);
cookie_ops_.push_back(op);
bool key_copied = ContainsKey(copied_keys_, key);
if (key_copied)
return changes_cookie_monster_;
if (is_read_only) {
// Insert this key into the set of read keys, if it doesn't exist yet.
if (!ContainsKey(read_keys_, key))
read_keys_.insert(key);
return default_cookie_monster_;
}
// If this method hasn't returned yet, the key has not been copied yet,
// and we must copy it due to the requested write operation.
bool copy_success = default_cookie_monster_->
CopyCookiesForKeyToOtherCookieMonster(key, changes_cookie_monster_);
// The copy must succeed.
DCHECK(copy_success);
copied_keys_.insert(key);
return changes_cookie_monster_;
}
void PrerenderCookieStore::ApplyChanges(std::vector<GURL>* cookie_change_urls) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (in_forwarding_mode_)
return;
// Apply all changes to the underlying cookie store.
for (std::vector<CookieOperation>::const_iterator it = cookie_ops_.begin();
it != cookie_ops_.end();
++it) {
switch (it->op) {
case COOKIE_OP_SET_COOKIE_WITH_OPTIONS_ASYNC:
cookie_change_urls->push_back(it->url);
default_cookie_monster_->SetCookieWithOptionsAsync(
it->url, it->cookie_line, it->options, SetCookiesCallback());
break;
case COOKIE_OP_GET_COOKIES_WITH_OPTIONS_ASYNC:
default_cookie_monster_->GetCookiesWithOptionsAsync(
it->url, it->options, GetCookiesCallback());
break;
case COOKIE_OP_GET_ALL_COOKIES_FOR_URL_ASYNC:
default_cookie_monster_->GetAllCookiesForURLAsync(
it->url, GetCookieListCallback());
break;
case COOKIE_OP_DELETE_COOKIE_ASYNC:
cookie_change_urls->push_back(it->url);
default_cookie_monster_->DeleteCookieAsync(
it->url, it->cookie_name, base::Closure());
break;
case COOKIE_OP_MAX:
NOTREACHED();
}
}
in_forwarding_mode_ = true;
copied_keys_.clear();
cookie_ops_.clear();
changes_cookie_monster_ = NULL;
}
void PrerenderCookieStore::OnCookieChangedForURL(
net::CookieMonster* cookie_monster,
const GURL& url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// If the cookie was changed in a different cookie monster than the one
// being decorated, there is nothing to do).
if (cookie_monster != default_cookie_monster_)
return;
if (in_forwarding_mode_)
return;
// If we have encountered a conflict before, it has already been recorded
// and the cb has been issued, so nothing to do.
if (cookie_conflict_)
return;
std::string key = default_cookie_monster_->GetKey(url.host());
// If the key for the cookie which was modified was neither read nor written,
// nothing to do.
if ((!ContainsKey(read_keys_, key)) && (!ContainsKey(copied_keys_, key)))
return;
// There was a conflict in cookies. Call the conflict callback, which should
// cancel the prerender if necessary (i.e. if it hasn't already been
// cancelled for some other reason).
// Notice that there is a race here with swapping in the prerender, but this
// is the same issue that occurs when two tabs modify cookies for the
// same domain concurrently. Therefore, there is no need to do anything
// special to prevent this race condition.
cookie_conflict_ = true;
if (!cookie_conflict_cb_.is_null()) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
cookie_conflict_cb_);
}
}
PrerenderCookieStore::CookieOperation::CookieOperation() {
}
PrerenderCookieStore::CookieOperation::~CookieOperation() {
}
} // namespace prerender