atom_api_cookies.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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 <memory>
  6. #include <utility>
  7. #include "atom/browser/atom_browser_context.h"
  8. #include "atom/browser/cookie_change_notifier.h"
  9. #include "atom/common/native_mate_converters/callback.h"
  10. #include "atom/common/native_mate_converters/gurl_converter.h"
  11. #include "atom/common/native_mate_converters/value_converter.h"
  12. #include "base/task/post_task.h"
  13. #include "base/time/time.h"
  14. #include "base/values.h"
  15. #include "content/public/browser/browser_context.h"
  16. #include "content/public/browser/browser_task_traits.h"
  17. #include "content/public/browser/browser_thread.h"
  18. #include "native_mate/dictionary.h"
  19. #include "native_mate/object_template_builder.h"
  20. #include "net/cookies/canonical_cookie.h"
  21. #include "net/cookies/cookie_store.h"
  22. #include "net/cookies/cookie_util.h"
  23. #include "net/url_request/url_request_context.h"
  24. #include "net/url_request/url_request_context_getter.h"
  25. using content::BrowserThread;
  26. namespace mate {
  27. template <>
  28. struct Converter<atom::api::Cookies::Error> {
  29. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  30. atom::api::Cookies::Error val) {
  31. if (val == atom::api::Cookies::SUCCESS)
  32. return v8::Null(isolate);
  33. else
  34. return v8::Exception::Error(StringToV8(isolate, "Setting cookie failed"));
  35. }
  36. };
  37. template <>
  38. struct Converter<net::CanonicalCookie> {
  39. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  40. const net::CanonicalCookie& val) {
  41. mate::Dictionary dict(isolate, v8::Object::New(isolate));
  42. dict.Set("name", val.Name());
  43. dict.Set("value", val.Value());
  44. dict.Set("domain", val.Domain());
  45. dict.Set("hostOnly", net::cookie_util::DomainIsHostOnly(val.Domain()));
  46. dict.Set("path", val.Path());
  47. dict.Set("secure", val.IsSecure());
  48. dict.Set("httpOnly", val.IsHttpOnly());
  49. dict.Set("session", !val.IsPersistent());
  50. if (val.IsPersistent())
  51. dict.Set("expirationDate", val.ExpiryDate().ToDoubleT());
  52. return dict.GetHandle();
  53. }
  54. };
  55. template <>
  56. struct Converter<network::mojom::CookieChangeCause> {
  57. static v8::Local<v8::Value> ToV8(
  58. v8::Isolate* isolate,
  59. const network::mojom::CookieChangeCause& val) {
  60. switch (val) {
  61. case network::mojom::CookieChangeCause::INSERTED:
  62. case network::mojom::CookieChangeCause::EXPLICIT:
  63. return mate::StringToV8(isolate, "explicit");
  64. case network::mojom::CookieChangeCause::OVERWRITE:
  65. return mate::StringToV8(isolate, "overwrite");
  66. case network::mojom::CookieChangeCause::EXPIRED:
  67. return mate::StringToV8(isolate, "expired");
  68. case network::mojom::CookieChangeCause::EVICTED:
  69. return mate::StringToV8(isolate, "evicted");
  70. case network::mojom::CookieChangeCause::EXPIRED_OVERWRITE:
  71. return mate::StringToV8(isolate, "expired-overwrite");
  72. default:
  73. return mate::StringToV8(isolate, "unknown");
  74. }
  75. }
  76. };
  77. } // namespace mate
  78. namespace atom {
  79. namespace api {
  80. namespace {
  81. // Returns whether |domain| matches |filter|.
  82. bool MatchesDomain(std::string filter, const std::string& domain) {
  83. // Add a leading '.' character to the filter domain if it doesn't exist.
  84. if (net::cookie_util::DomainIsHostOnly(filter))
  85. filter.insert(0, ".");
  86. std::string sub_domain(domain);
  87. // Strip any leading '.' character from the input cookie domain.
  88. if (!net::cookie_util::DomainIsHostOnly(sub_domain))
  89. sub_domain = sub_domain.substr(1);
  90. // Now check whether the domain argument is a subdomain of the filter domain.
  91. for (sub_domain.insert(0, "."); sub_domain.length() >= filter.length();) {
  92. if (sub_domain == filter)
  93. return true;
  94. const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot.
  95. sub_domain.erase(0, next_dot);
  96. }
  97. return false;
  98. }
  99. // Returns whether |cookie| matches |filter|.
  100. bool MatchesCookie(const base::DictionaryValue* filter,
  101. const net::CanonicalCookie& cookie) {
  102. std::string str;
  103. bool b;
  104. if (filter->GetString("name", &str) && str != cookie.Name())
  105. return false;
  106. if (filter->GetString("path", &str) && str != cookie.Path())
  107. return false;
  108. if (filter->GetString("domain", &str) && !MatchesDomain(str, cookie.Domain()))
  109. return false;
  110. if (filter->GetBoolean("secure", &b) && b != cookie.IsSecure())
  111. return false;
  112. if (filter->GetBoolean("session", &b) && b != !cookie.IsPersistent())
  113. return false;
  114. return true;
  115. }
  116. // Helper to returns the CookieStore.
  117. inline net::CookieStore* GetCookieStore(
  118. scoped_refptr<net::URLRequestContextGetter> getter) {
  119. return getter->GetURLRequestContext()->cookie_store();
  120. }
  121. void ResolvePromiseWithCookies(util::Promise promise,
  122. const net::CookieList& cookie_list) {
  123. promise.Resolve(cookie_list);
  124. }
  125. void ResolvePromise(util::Promise promise) {
  126. promise.Resolve();
  127. }
  128. // Resolve |promise| in UI thread.
  129. void ResolvePromiseInUI(util::Promise promise) {
  130. base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
  131. base::BindOnce(ResolvePromise, std::move(promise)));
  132. }
  133. // Run |callback| on UI thread.
  134. void RunCallbackInUI(base::OnceClosure callback) {
  135. base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, std::move(callback));
  136. }
  137. // Remove cookies from |list| not matching |filter|, and pass it to |callback|.
  138. void FilterCookies(std::unique_ptr<base::DictionaryValue> filter,
  139. util::Promise promise,
  140. const net::CookieList& list) {
  141. net::CookieList result;
  142. for (const auto& cookie : list) {
  143. if (MatchesCookie(filter.get(), cookie))
  144. result.push_back(cookie);
  145. }
  146. base::PostTaskWithTraits(
  147. FROM_HERE, {BrowserThread::UI},
  148. base::BindOnce(ResolvePromiseWithCookies, std::move(promise), result));
  149. }
  150. // Receives cookies matching |filter| in IO thread.
  151. void GetCookiesOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
  152. std::unique_ptr<base::DictionaryValue> filter,
  153. util::Promise promise) {
  154. std::string url;
  155. filter->GetString("url", &url);
  156. auto filtered_callback =
  157. base::BindOnce(FilterCookies, std::move(filter), std::move(promise));
  158. // Empty url will match all url cookies.
  159. if (url.empty())
  160. GetCookieStore(getter)->GetAllCookiesAsync(std::move(filtered_callback));
  161. else
  162. GetCookieStore(getter)->GetAllCookiesForURLAsync(
  163. GURL(url), std::move(filtered_callback));
  164. }
  165. // Removes cookie with |url| and |name| in IO thread.
  166. void RemoveCookieOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
  167. const GURL& url,
  168. const std::string& name,
  169. util::Promise promise) {
  170. GetCookieStore(getter)->DeleteCookieAsync(
  171. url, name, base::BindOnce(ResolvePromiseInUI, std::move(promise)));
  172. }
  173. // Resolves/rejects the |promise| in UI thread.
  174. void SettlePromiseInUI(util::Promise promise, const std::string& errmsg) {
  175. if (errmsg.empty()) {
  176. promise.Resolve();
  177. } else {
  178. promise.RejectWithErrorMessage(errmsg);
  179. }
  180. }
  181. // Callback of SetCookie.
  182. void OnSetCookie(util::Promise promise, bool success) {
  183. const std::string errmsg = success ? "" : "Setting cookie failed";
  184. RunCallbackInUI(
  185. base::BindOnce(SettlePromiseInUI, std::move(promise), errmsg));
  186. }
  187. // Flushes cookie store in IO thread.
  188. void FlushCookieStoreOnIOThread(
  189. scoped_refptr<net::URLRequestContextGetter> getter,
  190. util::Promise promise) {
  191. GetCookieStore(getter)->FlushStore(
  192. base::BindOnce(ResolvePromiseInUI, std::move(promise)));
  193. }
  194. // Sets cookie with |details| in IO thread.
  195. void SetCookieOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
  196. std::unique_ptr<base::DictionaryValue> details,
  197. util::Promise promise) {
  198. std::string url_string, name, value, domain, path;
  199. bool secure = false;
  200. bool http_only = false;
  201. double creation_date;
  202. double expiration_date;
  203. double last_access_date;
  204. details->GetString("url", &url_string);
  205. details->GetString("name", &name);
  206. details->GetString("value", &value);
  207. details->GetString("domain", &domain);
  208. details->GetString("path", &path);
  209. details->GetBoolean("secure", &secure);
  210. details->GetBoolean("httpOnly", &http_only);
  211. base::Time creation_time;
  212. if (details->GetDouble("creationDate", &creation_date)) {
  213. creation_time = (creation_date == 0)
  214. ? base::Time::UnixEpoch()
  215. : base::Time::FromDoubleT(creation_date);
  216. }
  217. base::Time expiration_time;
  218. if (details->GetDouble("expirationDate", &expiration_date)) {
  219. expiration_time = (expiration_date == 0)
  220. ? base::Time::UnixEpoch()
  221. : base::Time::FromDoubleT(expiration_date);
  222. }
  223. base::Time last_access_time;
  224. if (details->GetDouble("lastAccessDate", &last_access_date)) {
  225. last_access_time = (last_access_date == 0)
  226. ? base::Time::UnixEpoch()
  227. : base::Time::FromDoubleT(last_access_date);
  228. }
  229. auto completion_callback = base::BindOnce(OnSetCookie, std::move(promise));
  230. GURL url(url_string);
  231. if (!url.is_valid()) {
  232. std::move(completion_callback).Run(false);
  233. return;
  234. }
  235. if (name.empty()) {
  236. std::move(completion_callback).Run(false);
  237. return;
  238. }
  239. std::unique_ptr<net::CanonicalCookie> canonical_cookie(
  240. net::CanonicalCookie::CreateSanitizedCookie(
  241. url, name, value, domain, path, creation_time, expiration_time,
  242. last_access_time, secure, http_only,
  243. net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT));
  244. if (!canonical_cookie || !canonical_cookie->IsCanonical()) {
  245. std::move(completion_callback).Run(false);
  246. return;
  247. }
  248. GetCookieStore(getter)->SetCanonicalCookieAsync(
  249. std::move(canonical_cookie), secure, http_only,
  250. std::move(completion_callback));
  251. }
  252. } // namespace
  253. Cookies::Cookies(v8::Isolate* isolate, AtomBrowserContext* browser_context)
  254. : browser_context_(browser_context) {
  255. Init(isolate);
  256. cookie_change_subscription_ =
  257. browser_context_->cookie_change_notifier()->RegisterCookieChangeCallback(
  258. base::Bind(&Cookies::OnCookieChanged, base::Unretained(this)));
  259. }
  260. Cookies::~Cookies() {}
  261. v8::Local<v8::Promise> Cookies::Get(const base::DictionaryValue& filter) {
  262. util::Promise promise(isolate());
  263. v8::Local<v8::Promise> handle = promise.GetHandle();
  264. auto copy = base::DictionaryValue::From(
  265. base::Value::ToUniquePtrValue(filter.Clone()));
  266. auto* getter = browser_context_->GetRequestContext();
  267. base::PostTaskWithTraits(
  268. FROM_HERE, {BrowserThread::IO},
  269. base::BindOnce(GetCookiesOnIO, base::RetainedRef(getter), std::move(copy),
  270. std::move(promise)));
  271. return handle;
  272. }
  273. v8::Local<v8::Promise> Cookies::Remove(const GURL& url,
  274. const std::string& name) {
  275. util::Promise promise(isolate());
  276. v8::Local<v8::Promise> handle = promise.GetHandle();
  277. auto* getter = browser_context_->GetRequestContext();
  278. base::PostTaskWithTraits(
  279. FROM_HERE, {BrowserThread::IO},
  280. base::BindOnce(RemoveCookieOnIO, base::RetainedRef(getter), url, name,
  281. std::move(promise)));
  282. return handle;
  283. }
  284. v8::Local<v8::Promise> Cookies::Set(const base::DictionaryValue& details) {
  285. util::Promise promise(isolate());
  286. v8::Local<v8::Promise> handle = promise.GetHandle();
  287. auto copy = base::DictionaryValue::From(
  288. base::Value::ToUniquePtrValue(details.Clone()));
  289. auto* getter = browser_context_->GetRequestContext();
  290. base::PostTaskWithTraits(
  291. FROM_HERE, {BrowserThread::IO},
  292. base::BindOnce(SetCookieOnIO, base::RetainedRef(getter), std::move(copy),
  293. std::move(promise)));
  294. return handle;
  295. }
  296. v8::Local<v8::Promise> Cookies::FlushStore() {
  297. util::Promise promise(isolate());
  298. v8::Local<v8::Promise> handle = promise.GetHandle();
  299. auto* getter = browser_context_->GetRequestContext();
  300. base::PostTaskWithTraits(
  301. FROM_HERE, {BrowserThread::IO},
  302. base::BindOnce(FlushCookieStoreOnIOThread, base::RetainedRef(getter),
  303. std::move(promise)));
  304. return handle;
  305. }
  306. void Cookies::OnCookieChanged(const CookieDetails* details) {
  307. Emit("changed", *(details->cookie), details->cause, details->removed);
  308. }
  309. // static
  310. mate::Handle<Cookies> Cookies::Create(v8::Isolate* isolate,
  311. AtomBrowserContext* browser_context) {
  312. return mate::CreateHandle(isolate, new Cookies(isolate, browser_context));
  313. }
  314. // static
  315. void Cookies::BuildPrototype(v8::Isolate* isolate,
  316. v8::Local<v8::FunctionTemplate> prototype) {
  317. prototype->SetClassName(mate::StringToV8(isolate, "Cookies"));
  318. mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
  319. .SetMethod("get", &Cookies::Get)
  320. .SetMethod("remove", &Cookies::Remove)
  321. .SetMethod("set", &Cookies::Set)
  322. .SetMethod("flushStore", &Cookies::FlushStore);
  323. }
  324. } // namespace api
  325. } // namespace atom