cookie_manager_impl.cc 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright 2019 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.
  4. #include "fuchsia_web/webengine/browser/cookie_manager_impl.h"
  5. #include <lib/fidl/cpp/binding.h>
  6. #include "base/fuchsia/fuchsia_logging.h"
  7. #include "mojo/public/cpp/bindings/pending_remote.h"
  8. #include "mojo/public/cpp/bindings/receiver.h"
  9. #include "net/cookies/canonical_cookie.h"
  10. #include "net/cookies/cookie_change_dispatcher.h"
  11. #include "services/network/public/mojom/network_context.mojom.h"
  12. #include "url/gurl.h"
  13. namespace {
  14. fuchsia::web::Cookie ConvertCanonicalCookie(
  15. const net::CanonicalCookie& canonical_cookie,
  16. net::CookieChangeCause cause) {
  17. fuchsia::web::CookieId id;
  18. id.set_name(canonical_cookie.Name());
  19. id.set_domain(canonical_cookie.Domain());
  20. id.set_path(canonical_cookie.Path());
  21. fuchsia::web::Cookie cookie;
  22. cookie.set_id(std::move(id));
  23. switch (cause) {
  24. case net::CookieChangeCause::INSERTED:
  25. cookie.set_value(canonical_cookie.Value());
  26. break;
  27. case net::CookieChangeCause::EXPLICIT:
  28. case net::CookieChangeCause::UNKNOWN_DELETION:
  29. case net::CookieChangeCause::OVERWRITE:
  30. case net::CookieChangeCause::EXPIRED:
  31. case net::CookieChangeCause::EVICTED:
  32. case net::CookieChangeCause::EXPIRED_OVERWRITE:
  33. break;
  34. };
  35. return cookie;
  36. }
  37. class CookiesIteratorImpl final : public fuchsia::web::CookiesIterator,
  38. public network::mojom::CookieChangeListener {
  39. public:
  40. // |this| will delete itself when |mojo_request| or |changes| disconnect.
  41. CookiesIteratorImpl(
  42. mojo::PendingReceiver<network::mojom::CookieChangeListener> mojo_receiver,
  43. fidl::InterfaceRequest<fuchsia::web::CookiesIterator> changes)
  44. : CookiesIteratorImpl(std::move(changes)) {
  45. mojo_receiver_.Bind(std::move(mojo_receiver));
  46. mojo_receiver_.set_disconnect_handler(base::BindOnce(
  47. &CookiesIteratorImpl::OnMojoError, base::Unretained(this)));
  48. }
  49. // |this| will delete itself when |iterator| disconnects, or if a GetNext()
  50. // leaves |queued_cookies_| empty.
  51. CookiesIteratorImpl(
  52. const std::vector<net::CanonicalCookie>& cookies,
  53. fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator)
  54. : CookiesIteratorImpl(std::move(iterator)) {
  55. for (const auto& cookie : cookies) {
  56. queued_cookies_[cookie.UniqueKey()] =
  57. ConvertCanonicalCookie(cookie, net::CookieChangeCause::INSERTED);
  58. }
  59. }
  60. // Same as above except it takes CookieAccessResultList instead of just
  61. // CookieList.
  62. CookiesIteratorImpl(
  63. const std::vector<net::CookieWithAccessResult>&
  64. cookies_with_access_results,
  65. fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator)
  66. : CookiesIteratorImpl(std::move(iterator)) {
  67. for (const auto& cookie_with_access_result : cookies_with_access_results) {
  68. queued_cookies_[cookie_with_access_result.cookie.UniqueKey()] =
  69. ConvertCanonicalCookie(cookie_with_access_result.cookie,
  70. net::CookieChangeCause::INSERTED);
  71. }
  72. }
  73. CookiesIteratorImpl(const CookiesIteratorImpl&) = delete;
  74. CookiesIteratorImpl& operator=(const CookiesIteratorImpl&) = delete;
  75. ~CookiesIteratorImpl() override = default;
  76. // fuchsia::web::CookiesIterator implementation:
  77. void GetNext(GetNextCallback callback) override {
  78. DCHECK(!get_next_callback_);
  79. get_next_callback_ = std::move(callback);
  80. MaybeSendQueuedCookies();
  81. }
  82. private:
  83. explicit CookiesIteratorImpl(
  84. fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator)
  85. : mojo_receiver_(this), fidl_binding_(this) {
  86. fidl_binding_.Bind(std::move(iterator));
  87. fidl_binding_.set_error_handler([this](zx_status_t status) {
  88. ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status)
  89. << "CookieChangeListener disconnected.";
  90. delete this;
  91. });
  92. }
  93. void OnMojoError() {
  94. LOG(ERROR) << "NetworkService disconnected CookiesIterator.";
  95. fidl_binding_.Close(ZX_ERR_UNAVAILABLE);
  96. delete this;
  97. }
  98. void MaybeSendQueuedCookies() {
  99. // Assuming cookies values never exceed 4KB in size, plus some overhead for
  100. // the name, domain and path, and that Zircon messages can be up to 64KB.
  101. constexpr int kMaxCookiesPerMessage = 8;
  102. if (!get_next_callback_)
  103. return;
  104. if (mojo_receiver_.is_bound() && queued_cookies_.empty())
  105. return;
  106. // Build a vector of Cookies to return to the caller.
  107. fuchsia::web::CookiesIterator::GetNextCallback callback(
  108. std::move(get_next_callback_));
  109. std::vector<fuchsia::web::Cookie> cookies;
  110. while (!queued_cookies_.empty() && cookies.size() < kMaxCookiesPerMessage) {
  111. auto cookie = queued_cookies_.begin();
  112. cookies.emplace_back(std::move(cookie->second));
  113. queued_cookies_.erase(cookie);
  114. }
  115. callback(std::move(cookies));
  116. // If this is a one-off CookieIterator then tear down once |queued_cookies_|
  117. // is empty.
  118. if (queued_cookies_.empty() && !mojo_receiver_.is_bound())
  119. delete this;
  120. }
  121. // network::mojom::CookieChangeListener implementation:
  122. void OnCookieChange(const net::CookieChangeInfo& change) override {
  123. queued_cookies_[change.cookie.UniqueKey()] =
  124. ConvertCanonicalCookie(change.cookie, change.cause);
  125. MaybeSendQueuedCookies();
  126. }
  127. mojo::Receiver<network::mojom::CookieChangeListener> mojo_receiver_;
  128. fidl::Binding<fuchsia::web::CookiesIterator> fidl_binding_;
  129. GetNextCallback get_next_callback_;
  130. // Map from "unique key"s (see net::CanonicalCookie::UniqueKey()) to the
  131. // corresponding fuchsia::web::Cookie.
  132. std::map<net::CanonicalCookie::UniqueCookieKey, fuchsia::web::Cookie>
  133. queued_cookies_;
  134. };
  135. void OnAllCookiesReceived(
  136. fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator,
  137. const std::vector<net::CanonicalCookie>& cookies) {
  138. new CookiesIteratorImpl(cookies, std::move(iterator));
  139. }
  140. void OnCookiesAndExcludedReceived(
  141. fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator,
  142. const std::vector<net::CookieWithAccessResult>& cookies_with_access_results,
  143. const std::vector<net::CookieWithAccessResult>& excluded_cookies) {
  144. // Since CookieOptions::set_return_excluded_cookies() is not used when calling
  145. // the Mojo GetCookieList() API, |excluded_cookies| should be empty.
  146. DCHECK(excluded_cookies.empty());
  147. new CookiesIteratorImpl(cookies_with_access_results, std::move(iterator));
  148. }
  149. } // namespace
  150. CookieManagerImpl::CookieManagerImpl(
  151. GetNetworkContextCallback get_network_context)
  152. : get_network_context_(std::move(get_network_context)) {}
  153. CookieManagerImpl::~CookieManagerImpl() = default;
  154. void CookieManagerImpl::ObserveCookieChanges(
  155. fidl::StringPtr url,
  156. fidl::StringPtr name,
  157. fidl::InterfaceRequest<fuchsia::web::CookiesIterator> changes) {
  158. EnsureCookieManager();
  159. mojo::PendingRemote<network::mojom::CookieChangeListener> mojo_listener;
  160. new CookiesIteratorImpl(mojo_listener.InitWithNewPipeAndPassReceiver(),
  161. std::move(changes));
  162. if (url) {
  163. absl::optional<std::string> maybe_name;
  164. if (name)
  165. maybe_name = *name;
  166. cookie_manager_->AddCookieChangeListener(GURL(*url), maybe_name,
  167. std::move(mojo_listener));
  168. } else {
  169. cookie_manager_->AddGlobalChangeListener(std::move(mojo_listener));
  170. }
  171. }
  172. void CookieManagerImpl::GetCookieList(
  173. fidl::StringPtr url,
  174. fidl::StringPtr name,
  175. fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator) {
  176. EnsureCookieManager();
  177. if (!url && !name) {
  178. cookie_manager_->GetAllCookies(
  179. base::BindOnce(&OnAllCookiesReceived, std::move(iterator)));
  180. } else {
  181. if (!name) {
  182. // Include HTTP and 1st-party-only cookies in those returned.
  183. net::CookieOptions options;
  184. options.set_include_httponly();
  185. options.set_same_site_cookie_context(
  186. net::CookieOptions::SameSiteCookieContext::MakeInclusive());
  187. cookie_manager_->GetCookieList(
  188. GURL(*url), options, net::CookiePartitionKeyCollection::Todo(),
  189. base::BindOnce(&OnCookiesAndExcludedReceived, std::move(iterator)));
  190. } else {
  191. // TODO(858853): Support filtering by name.
  192. iterator.Close(ZX_ERR_NOT_SUPPORTED);
  193. }
  194. }
  195. }
  196. void CookieManagerImpl::EnsureCookieManager() {
  197. if (cookie_manager_.is_bound())
  198. return;
  199. get_network_context_.Run()->GetCookieManager(
  200. cookie_manager_.BindNewPipeAndPassReceiver());
  201. cookie_manager_.set_disconnect_handler(base::BindOnce(
  202. &CookieManagerImpl::OnMojoDisconnect, base::Unretained(this)));
  203. }
  204. void CookieManagerImpl::OnMojoDisconnect() {
  205. LOG(ERROR) << "NetworkService disconnected CookieManager.";
  206. if (on_mojo_disconnected_for_test_)
  207. std::move(on_mojo_disconnected_for_test_).Run();
  208. cookie_manager_.reset();
  209. }