1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Implements common functionality for the Chrome Extensions Cookies API.
7 #include "chrome/browser/extensions/api/cookies/cookies_helpers.h"
11 #include "base/logging.h"
12 #include "base/memory/linked_ptr.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/extensions/api/cookies/cookies_api_constants.h"
18 #include "chrome/browser/extensions/extension_tab_util.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
22 #include "chrome/common/extensions/api/cookies.h"
23 #include "chrome/common/url_constants.h"
24 #include "content/public/browser/web_contents.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/permissions/permissions_data.h"
27 #include "net/cookies/canonical_cookie.h"
28 #include "net/cookies/cookie_util.h"
31 using extensions::api::cookies::Cookie
;
32 using extensions::api::cookies::CookieStore
;
34 namespace GetAll
= extensions::api::cookies::GetAll
;
36 namespace extensions
{
38 namespace keys
= cookies_api_constants
;
40 namespace cookies_helpers
{
42 static const char kOriginalProfileStoreId
[] = "0";
43 static const char kOffTheRecordProfileStoreId
[] = "1";
45 Profile
* ChooseProfileFromStoreId(const std::string
& store_id
,
47 bool include_incognito
) {
49 bool allow_original
= !profile
->IsOffTheRecord();
50 bool allow_incognito
= profile
->IsOffTheRecord() ||
51 (include_incognito
&& profile
->HasOffTheRecordProfile());
52 if (store_id
== kOriginalProfileStoreId
&& allow_original
)
53 return profile
->GetOriginalProfile();
54 if (store_id
== kOffTheRecordProfileStoreId
&& allow_incognito
)
55 return profile
->GetOffTheRecordProfile();
59 const char* GetStoreIdFromProfile(Profile
* profile
) {
61 return profile
->IsOffTheRecord() ?
62 kOffTheRecordProfileStoreId
: kOriginalProfileStoreId
;
65 scoped_ptr
<Cookie
> CreateCookie(
66 const net::CanonicalCookie
& canonical_cookie
,
67 const std::string
& store_id
) {
68 scoped_ptr
<Cookie
> cookie(new Cookie());
70 // A cookie is a raw byte sequence. By explicitly parsing it as UTF-8, we
71 // apply error correction, so the string can be safely passed to the renderer.
72 cookie
->name
= base::UTF16ToUTF8(base::UTF8ToUTF16(canonical_cookie
.Name()));
74 base::UTF16ToUTF8(base::UTF8ToUTF16(canonical_cookie
.Value()));
75 cookie
->domain
= canonical_cookie
.Domain();
76 cookie
->host_only
= net::cookie_util::DomainIsHostOnly(
77 canonical_cookie
.Domain());
78 // A non-UTF8 path is invalid, so we just replace it with an empty string.
79 cookie
->path
= base::IsStringUTF8(canonical_cookie
.Path()) ?
80 canonical_cookie
.Path() : std::string();
81 cookie
->secure
= canonical_cookie
.IsSecure();
82 cookie
->http_only
= canonical_cookie
.IsHttpOnly();
83 cookie
->session
= !canonical_cookie
.IsPersistent();
84 if (canonical_cookie
.IsPersistent()) {
85 cookie
->expiration_date
.reset(
86 new double(canonical_cookie
.ExpiryDate().ToDoubleT()));
88 cookie
->store_id
= store_id
;
93 scoped_ptr
<CookieStore
> CreateCookieStore(Profile
* profile
,
94 base::ListValue
* tab_ids
) {
97 base::DictionaryValue dict
;
98 dict
.SetString(keys::kIdKey
, GetStoreIdFromProfile(profile
));
99 dict
.Set(keys::kTabIdsKey
, tab_ids
);
101 CookieStore
* cookie_store
= new CookieStore();
102 bool rv
= CookieStore::Populate(dict
, cookie_store
);
104 return scoped_ptr
<CookieStore
>(cookie_store
);
107 void GetCookieListFromStore(
108 net::CookieStore
* cookie_store
, const GURL
& url
,
109 const net::CookieMonster::GetCookieListCallback
& callback
) {
110 DCHECK(cookie_store
);
111 net::CookieMonster
* monster
= cookie_store
->GetCookieMonster();
112 if (!url
.is_empty()) {
113 DCHECK(url
.is_valid());
114 monster
->GetAllCookiesForURLAsync(url
, callback
);
116 monster
->GetAllCookiesAsync(callback
);
120 GURL
GetURLFromCanonicalCookie(const net::CanonicalCookie
& cookie
) {
121 const std::string
& domain_key
= cookie
.Domain();
122 const std::string scheme
=
123 cookie
.IsSecure() ? url::kHttpsScheme
: url::kHttpScheme
;
124 const std::string host
=
125 domain_key
.find('.') != 0 ? domain_key
: domain_key
.substr(1);
126 return GURL(scheme
+ url::kStandardSchemeSeparator
+ host
+ "/");
129 void AppendMatchingCookiesToVector(const net::CookieList
& all_cookies
,
131 const GetAll::Params::Details
* details
,
132 const Extension
* extension
,
133 LinkedCookieVec
* match_vector
) {
134 net::CookieList::const_iterator it
;
135 for (it
= all_cookies
.begin(); it
!= all_cookies
.end(); ++it
) {
136 // Ignore any cookie whose domain doesn't match the extension's
138 GURL cookie_domain_url
= GetURLFromCanonicalCookie(*it
);
139 if (!extension
->permissions_data()->HasHostPermission(cookie_domain_url
))
141 // Filter the cookie using the match filter.
142 cookies_helpers::MatchFilter
filter(details
);
143 if (filter
.MatchesCookie(*it
)) {
144 match_vector
->push_back(make_linked_ptr(
145 CreateCookie(*it
, *details
->store_id
).release()));
150 void AppendToTabIdList(Browser
* browser
, base::ListValue
* tab_ids
) {
153 TabStripModel
* tab_strip
= browser
->tab_strip_model();
154 for (int i
= 0; i
< tab_strip
->count(); ++i
) {
155 tab_ids
->Append(new base::FundamentalValue(
156 ExtensionTabUtil::GetTabId(tab_strip
->GetWebContentsAt(i
))));
160 MatchFilter::MatchFilter(const GetAll::Params::Details
* details
)
161 : details_(details
) {
165 bool MatchFilter::MatchesCookie(
166 const net::CanonicalCookie
& cookie
) {
167 if (details_
->name
.get() && *details_
->name
!= cookie
.Name())
170 if (!MatchesDomain(cookie
.Domain()))
173 if (details_
->path
.get() && *details_
->path
!= cookie
.Path())
176 if (details_
->secure
.get() && *details_
->secure
!= cookie
.IsSecure())
179 if (details_
->session
.get() && *details_
->session
!= !cookie
.IsPersistent())
185 bool MatchFilter::MatchesDomain(const std::string
& domain
) {
186 if (!details_
->domain
.get())
189 // Add a leading '.' character to the filter domain if it doesn't exist.
190 if (net::cookie_util::DomainIsHostOnly(*details_
->domain
))
191 details_
->domain
->insert(0, ".");
193 std::string
sub_domain(domain
);
194 // Strip any leading '.' character from the input cookie domain.
195 if (!net::cookie_util::DomainIsHostOnly(sub_domain
))
196 sub_domain
= sub_domain
.substr(1);
198 // Now check whether the domain argument is a subdomain of the filter domain.
199 for (sub_domain
.insert(0, ".");
200 sub_domain
.length() >= details_
->domain
->length();) {
201 if (sub_domain
== *details_
->domain
)
203 const size_t next_dot
= sub_domain
.find('.', 1); // Skip over leading dot.
204 sub_domain
.erase(0, next_dot
);
209 } // namespace cookies_helpers
210 } // namespace extensions