blob: 0ca4569488167234e3bc2f3384aa76fddbcc91af [file] [log] [blame]
// Copyright (c) 2012 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.
// Implements common functionality for the Chrome Extensions Cookies API.
#include "chrome/browser/extensions/api/cookies/cookies_helpers.h"
#include <stddef.h>
#include <vector>
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/cookies/cookies_api_constants.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/extensions/api/cookies.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/permissions_data.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_store.h"
#include "net/cookies/cookie_util.h"
#include "url/gurl.h"
using extensions::api::cookies::Cookie;
using extensions::api::cookies::CookieStore;
namespace GetAll = extensions::api::cookies::GetAll;
namespace extensions {
namespace keys = cookies_api_constants;
namespace cookies_helpers {
static const char kOriginalProfileStoreId[] = "0";
static const char kOffTheRecordProfileStoreId[] = "1";
Profile* ChooseProfileFromStoreId(const std::string& store_id,
Profile* profile,
bool include_incognito) {
DCHECK(profile);
bool allow_original = !profile->IsOffTheRecord();
bool allow_incognito = profile->IsOffTheRecord() ||
(include_incognito && profile->HasOffTheRecordProfile());
if (store_id == kOriginalProfileStoreId && allow_original)
return profile->GetOriginalProfile();
if (store_id == kOffTheRecordProfileStoreId && allow_incognito)
return profile->GetOffTheRecordProfile();
return NULL;
}
const char* GetStoreIdFromProfile(Profile* profile) {
DCHECK(profile);
return profile->IsOffTheRecord() ?
kOffTheRecordProfileStoreId : kOriginalProfileStoreId;
}
Cookie CreateCookie(const net::CanonicalCookie& canonical_cookie,
const std::string& store_id) {
Cookie cookie;
// A cookie is a raw byte sequence. By explicitly parsing it as UTF-8, we
// apply error correction, so the string can be safely passed to the renderer.
cookie.name = base::UTF16ToUTF8(base::UTF8ToUTF16(canonical_cookie.Name()));
cookie.value = base::UTF16ToUTF8(base::UTF8ToUTF16(canonical_cookie.Value()));
cookie.domain = canonical_cookie.Domain();
cookie.host_only =
net::cookie_util::DomainIsHostOnly(canonical_cookie.Domain());
// A non-UTF8 path is invalid, so we just replace it with an empty string.
cookie.path = base::IsStringUTF8(canonical_cookie.Path())
? canonical_cookie.Path()
: std::string();
cookie.secure = canonical_cookie.IsSecure();
cookie.http_only = canonical_cookie.IsHttpOnly();
switch (canonical_cookie.SameSite()) {
case net::CookieSameSite::DEFAULT_MODE:
cookie.same_site = api::cookies::SAME_SITE_STATUS_NO_RESTRICTION;
break;
case net::CookieSameSite::LAX_MODE:
cookie.same_site = api::cookies::SAME_SITE_STATUS_LAX;
break;
case net::CookieSameSite::STRICT_MODE:
cookie.same_site = api::cookies::SAME_SITE_STATUS_STRICT;
break;
}
cookie.session = !canonical_cookie.IsPersistent();
if (canonical_cookie.IsPersistent()) {
cookie.expiration_date.reset(
new double(canonical_cookie.ExpiryDate().ToDoubleT()));
}
cookie.store_id = store_id;
return cookie;
}
CookieStore CreateCookieStore(Profile* profile, base::ListValue* tab_ids) {
DCHECK(profile);
DCHECK(tab_ids);
base::DictionaryValue dict;
dict.SetString(keys::kIdKey, GetStoreIdFromProfile(profile));
dict.Set(keys::kTabIdsKey, tab_ids);
CookieStore cookie_store;
bool rv = CookieStore::Populate(dict, &cookie_store);
CHECK(rv);
return cookie_store;
}
void GetCookieListFromStore(
net::CookieStore* cookie_store,
const GURL& url,
const net::CookieMonster::GetCookieListCallback& callback) {
DCHECK(cookie_store);
if (!url.is_empty()) {
DCHECK(url.is_valid());
cookie_store->GetAllCookiesForURLAsync(url, callback);
} else {
cookie_store->GetAllCookiesAsync(callback);
}
}
GURL GetURLFromCanonicalCookie(const net::CanonicalCookie& cookie) {
const std::string& domain_key = cookie.Domain();
const std::string scheme =
cookie.IsSecure() ? url::kHttpsScheme : url::kHttpScheme;
const std::string host =
domain_key.find('.') != 0 ? domain_key : domain_key.substr(1);
return GURL(scheme + url::kStandardSchemeSeparator + host + "/");
}
void AppendMatchingCookiesToVector(const net::CookieList& all_cookies,
const GURL& url,
const GetAll::Params::Details* details,
const Extension* extension,
std::vector<Cookie>* match_vector) {
for (const net::CanonicalCookie& cookie : all_cookies) {
// Ignore any cookie whose domain doesn't match the extension's
// host permissions.
GURL cookie_domain_url = GetURLFromCanonicalCookie(cookie);
if (!extension->permissions_data()->HasHostPermission(cookie_domain_url))
continue;
// Filter the cookie using the match filter.
cookies_helpers::MatchFilter filter(details);
if (filter.MatchesCookie(cookie))
match_vector->push_back(CreateCookie(cookie, *details->store_id));
}
}
void AppendToTabIdList(Browser* browser, base::ListValue* tab_ids) {
DCHECK(browser);
DCHECK(tab_ids);
TabStripModel* tab_strip = browser->tab_strip_model();
for (int i = 0; i < tab_strip->count(); ++i) {
tab_ids->AppendInteger(
ExtensionTabUtil::GetTabId(tab_strip->GetWebContentsAt(i)));
}
}
MatchFilter::MatchFilter(const GetAll::Params::Details* details)
: details_(details) {
DCHECK(details_);
}
bool MatchFilter::MatchesCookie(
const net::CanonicalCookie& cookie) {
if (details_->name.get() && *details_->name != cookie.Name())
return false;
if (!MatchesDomain(cookie.Domain()))
return false;
if (details_->path.get() && *details_->path != cookie.Path())
return false;
if (details_->secure.get() && *details_->secure != cookie.IsSecure())
return false;
if (details_->session.get() && *details_->session != !cookie.IsPersistent())
return false;
return true;
}
bool MatchFilter::MatchesDomain(const std::string& domain) {
if (!details_->domain.get())
return true;
// Add a leading '.' character to the filter domain if it doesn't exist.
if (net::cookie_util::DomainIsHostOnly(*details_->domain))
details_->domain->insert(0, ".");
std::string sub_domain(domain);
// Strip any leading '.' character from the input cookie domain.
if (!net::cookie_util::DomainIsHostOnly(sub_domain))
sub_domain = sub_domain.substr(1);
// Now check whether the domain argument is a subdomain of the filter domain.
for (sub_domain.insert(0, ".");
sub_domain.length() >= details_->domain->length();) {
if (sub_domain == *details_->domain)
return true;
const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot.
sub_domain.erase(0, next_dot);
}
return false;
}
} // namespace cookies_helpers
} // namespace extensions