atom_api_cookies.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Copyright (c) 2015 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/api/atom_api_cookies.h"
  5. #include "atom/browser/atom_browser_context.h"
  6. #include "atom/common/native_mate_converters/callback.h"
  7. #include "atom/common/native_mate_converters/gurl_converter.h"
  8. #include "atom/common/native_mate_converters/value_converter.h"
  9. #include "base/time/time.h"
  10. #include "base/values.h"
  11. #include "content/public/browser/browser_context.h"
  12. #include "content/public/browser/browser_thread.h"
  13. #include "native_mate/dictionary.h"
  14. #include "native_mate/object_template_builder.h"
  15. #include "net/cookies/cookie_monster.h"
  16. #include "net/cookies/cookie_store.h"
  17. #include "net/cookies/cookie_util.h"
  18. #include "net/url_request/url_request_context.h"
  19. #include "net/url_request/url_request_context_getter.h"
  20. using content::BrowserThread;
  21. namespace mate {
  22. template <>
  23. struct Converter<atom::api::Cookies::Error> {
  24. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  25. atom::api::Cookies::Error val) {
  26. if (val == atom::api::Cookies::SUCCESS)
  27. return v8::Null(isolate);
  28. else
  29. return v8::Exception::Error(StringToV8(isolate, "Setting cookie failed"));
  30. }
  31. };
  32. template <>
  33. struct Converter<net::CanonicalCookie> {
  34. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  35. const net::CanonicalCookie& val) {
  36. mate::Dictionary dict(isolate, v8::Object::New(isolate));
  37. dict.Set("name", val.Name());
  38. dict.Set("value", val.Value());
  39. dict.Set("domain", val.Domain());
  40. dict.Set("hostOnly", net::cookie_util::DomainIsHostOnly(val.Domain()));
  41. dict.Set("path", val.Path());
  42. dict.Set("secure", val.IsSecure());
  43. dict.Set("httpOnly", val.IsHttpOnly());
  44. dict.Set("session", !val.IsPersistent());
  45. if (val.IsPersistent())
  46. dict.Set("expirationDate", val.ExpiryDate().ToDoubleT());
  47. return dict.GetHandle();
  48. }
  49. };
  50. template <>
  51. struct Converter<net::CookieStore::ChangeCause> {
  52. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  53. const net::CookieStore::ChangeCause& val) {
  54. switch (val) {
  55. case net::CookieStore::ChangeCause::INSERTED:
  56. case net::CookieStore::ChangeCause::EXPLICIT:
  57. case net::CookieStore::ChangeCause::EXPLICIT_DELETE_BETWEEN:
  58. case net::CookieStore::ChangeCause::EXPLICIT_DELETE_PREDICATE:
  59. case net::CookieStore::ChangeCause::EXPLICIT_DELETE_SINGLE:
  60. case net::CookieStore::ChangeCause::EXPLICIT_DELETE_CANONICAL:
  61. return mate::StringToV8(isolate, "explicit");
  62. case net::CookieStore::ChangeCause::OVERWRITE:
  63. return mate::StringToV8(isolate, "overwrite");
  64. case net::CookieStore::ChangeCause::EXPIRED:
  65. return mate::StringToV8(isolate, "expired");
  66. case net::CookieStore::ChangeCause::EVICTED:
  67. return mate::StringToV8(isolate, "evicted");
  68. case net::CookieStore::ChangeCause::EXPIRED_OVERWRITE:
  69. return mate::StringToV8(isolate, "expired-overwrite");
  70. default:
  71. return mate::StringToV8(isolate, "unknown");
  72. }
  73. }
  74. };
  75. } // namespace mate
  76. namespace atom {
  77. namespace api {
  78. namespace {
  79. // Returns whether |domain| matches |filter|.
  80. bool MatchesDomain(std::string filter, const std::string& domain) {
  81. // Add a leading '.' character to the filter domain if it doesn't exist.
  82. if (net::cookie_util::DomainIsHostOnly(filter))
  83. filter.insert(0, ".");
  84. std::string sub_domain(domain);
  85. // Strip any leading '.' character from the input cookie domain.
  86. if (!net::cookie_util::DomainIsHostOnly(sub_domain))
  87. sub_domain = sub_domain.substr(1);
  88. // Now check whether the domain argument is a subdomain of the filter domain.
  89. for (sub_domain.insert(0, "."); sub_domain.length() >= filter.length();) {
  90. if (sub_domain == filter)
  91. return true;
  92. const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot.
  93. sub_domain.erase(0, next_dot);
  94. }
  95. return false;
  96. }
  97. // Returns whether |cookie| matches |filter|.
  98. bool MatchesCookie(const base::DictionaryValue* filter,
  99. const net::CanonicalCookie& cookie) {
  100. std::string str;
  101. bool b;
  102. if (filter->GetString("name", &str) && str != cookie.Name())
  103. return false;
  104. if (filter->GetString("path", &str) && str != cookie.Path())
  105. return false;
  106. if (filter->GetString("domain", &str) && !MatchesDomain(str, cookie.Domain()))
  107. return false;
  108. if (filter->GetBoolean("secure", &b) && b != cookie.IsSecure())
  109. return false;
  110. if (filter->GetBoolean("session", &b) && b != !cookie.IsPersistent())
  111. return false;
  112. return true;
  113. }
  114. // Helper to returns the CookieStore.
  115. inline net::CookieStore* GetCookieStore(
  116. scoped_refptr<net::URLRequestContextGetter> getter) {
  117. return getter->GetURLRequestContext()->cookie_store();
  118. }
  119. // Run |callback| on UI thread.
  120. void RunCallbackInUI(const base::Closure& callback) {
  121. BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
  122. }
  123. // Remove cookies from |list| not matching |filter|, and pass it to |callback|.
  124. void FilterCookies(std::unique_ptr<base::DictionaryValue> filter,
  125. const Cookies::GetCallback& callback,
  126. const net::CookieList& list) {
  127. net::CookieList result;
  128. for (const auto& cookie : list) {
  129. if (MatchesCookie(filter.get(), cookie))
  130. result.push_back(cookie);
  131. }
  132. RunCallbackInUI(base::Bind(callback, Cookies::SUCCESS, result));
  133. }
  134. // Receives cookies matching |filter| in IO thread.
  135. void GetCookiesOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
  136. std::unique_ptr<base::DictionaryValue> filter,
  137. const Cookies::GetCallback& callback) {
  138. std::string url;
  139. filter->GetString("url", &url);
  140. auto filtered_callback =
  141. base::Bind(FilterCookies, base::Passed(&filter), callback);
  142. // Empty url will match all url cookies.
  143. if (url.empty())
  144. GetCookieStore(getter)->GetAllCookiesAsync(filtered_callback);
  145. else
  146. GetCookieStore(getter)->GetAllCookiesForURLAsync(GURL(url),
  147. filtered_callback);
  148. }
  149. // Removes cookie with |url| and |name| in IO thread.
  150. void RemoveCookieOnIOThread(scoped_refptr<net::URLRequestContextGetter> getter,
  151. const GURL& url,
  152. const std::string& name,
  153. const base::Closure& callback) {
  154. GetCookieStore(getter)->DeleteCookieAsync(
  155. url, name, base::BindOnce(RunCallbackInUI, callback));
  156. }
  157. // Callback of SetCookie.
  158. void OnSetCookie(const Cookies::SetCallback& callback, bool success) {
  159. RunCallbackInUI(
  160. base::Bind(callback, success ? Cookies::SUCCESS : Cookies::FAILED));
  161. }
  162. // Flushes cookie store in IO thread.
  163. void FlushCookieStoreOnIOThread(
  164. scoped_refptr<net::URLRequestContextGetter> getter,
  165. const base::Closure& callback) {
  166. GetCookieStore(getter)->FlushStore(base::BindOnce(RunCallbackInUI, callback));
  167. }
  168. // Sets cookie with |details| in IO thread.
  169. void SetCookieOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
  170. std::unique_ptr<base::DictionaryValue> details,
  171. const Cookies::SetCallback& callback) {
  172. std::string url, name, value, domain, path;
  173. bool secure = false;
  174. bool http_only = false;
  175. double creation_date;
  176. double expiration_date;
  177. double last_access_date;
  178. details->GetString("url", &url);
  179. details->GetString("name", &name);
  180. details->GetString("value", &value);
  181. details->GetString("domain", &domain);
  182. details->GetString("path", &path);
  183. details->GetBoolean("secure", &secure);
  184. details->GetBoolean("httpOnly", &http_only);
  185. base::Time creation_time;
  186. if (details->GetDouble("creationDate", &creation_date)) {
  187. creation_time = (creation_date == 0)
  188. ? base::Time::UnixEpoch()
  189. : base::Time::FromDoubleT(creation_date);
  190. }
  191. base::Time expiration_time;
  192. if (details->GetDouble("expirationDate", &expiration_date)) {
  193. expiration_time = (expiration_date == 0)
  194. ? base::Time::UnixEpoch()
  195. : base::Time::FromDoubleT(expiration_date);
  196. }
  197. base::Time last_access_time;
  198. if (details->GetDouble("lastAccessDate", &last_access_date)) {
  199. last_access_time = (last_access_date == 0)
  200. ? base::Time::UnixEpoch()
  201. : base::Time::FromDoubleT(last_access_date);
  202. }
  203. GetCookieStore(getter)->SetCookieWithDetailsAsync(
  204. GURL(url), name, value, domain, path, creation_time, expiration_time,
  205. last_access_time, secure, http_only, net::CookieSameSite::DEFAULT_MODE,
  206. net::COOKIE_PRIORITY_DEFAULT, base::BindOnce(OnSetCookie, callback));
  207. }
  208. } // namespace
  209. Cookies::Cookies(v8::Isolate* isolate, AtomBrowserContext* browser_context)
  210. : browser_context_(browser_context) {
  211. Init(isolate);
  212. auto subscription = browser_context->RegisterCookieChangeCallback(
  213. base::Bind(&Cookies::OnCookieChanged, base::Unretained(this)));
  214. browser_context->set_cookie_change_subscription(std::move(subscription));
  215. }
  216. Cookies::~Cookies() {}
  217. void Cookies::Get(const base::DictionaryValue& filter,
  218. const GetCallback& callback) {
  219. std::unique_ptr<base::DictionaryValue> copied(filter.CreateDeepCopy());
  220. auto* getter = browser_context_->GetRequestContext();
  221. content::BrowserThread::PostTask(
  222. BrowserThread::IO, FROM_HERE,
  223. base::BindOnce(GetCookiesOnIO, base::RetainedRef(getter),
  224. std::move(copied), callback));
  225. }
  226. void Cookies::Remove(const GURL& url,
  227. const std::string& name,
  228. const base::Closure& callback) {
  229. auto* getter = browser_context_->GetRequestContext();
  230. content::BrowserThread::PostTask(
  231. BrowserThread::IO, FROM_HERE,
  232. base::BindOnce(RemoveCookieOnIOThread, base::RetainedRef(getter), url,
  233. name, callback));
  234. }
  235. void Cookies::Set(const base::DictionaryValue& details,
  236. const SetCallback& callback) {
  237. std::unique_ptr<base::DictionaryValue> copied(details.CreateDeepCopy());
  238. auto* getter = browser_context_->GetRequestContext();
  239. content::BrowserThread::PostTask(
  240. BrowserThread::IO, FROM_HERE,
  241. base::BindOnce(SetCookieOnIO, base::RetainedRef(getter),
  242. std::move(copied), callback));
  243. }
  244. void Cookies::FlushStore(const base::Closure& callback) {
  245. auto* getter = browser_context_->GetRequestContext();
  246. content::BrowserThread::PostTask(
  247. BrowserThread::IO, FROM_HERE,
  248. base::BindOnce(FlushCookieStoreOnIOThread, base::RetainedRef(getter),
  249. callback));
  250. }
  251. void Cookies::OnCookieChanged(const CookieDetails* details) {
  252. Emit("changed", *(details->cookie), details->cause, details->removed);
  253. }
  254. // static
  255. mate::Handle<Cookies> Cookies::Create(v8::Isolate* isolate,
  256. AtomBrowserContext* browser_context) {
  257. return mate::CreateHandle(isolate, new Cookies(isolate, browser_context));
  258. }
  259. // static
  260. void Cookies::BuildPrototype(v8::Isolate* isolate,
  261. v8::Local<v8::FunctionTemplate> prototype) {
  262. prototype->SetClassName(mate::StringToV8(isolate, "Cookies"));
  263. mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
  264. .SetMethod("get", &Cookies::Get)
  265. .SetMethod("remove", &Cookies::Remove)
  266. .SetMethod("set", &Cookies::Set)
  267. .SetMethod("flushStore", &Cookies::FlushStore);
  268. }
  269. } // namespace api
  270. } // namespace atom