diff options
Diffstat (limited to 'libcef/common/request_impl.cc')
-rw-r--r-- | libcef/common/request_impl.cc | 1229 |
1 files changed, 1229 insertions, 0 deletions
diff --git a/libcef/common/request_impl.cc b/libcef/common/request_impl.cc new file mode 100644 index 00000000..c2dcfb17 --- /dev/null +++ b/libcef/common/request_impl.cc @@ -0,0 +1,1229 @@ +// Copyright (c) 2008-2009 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include <algorithm> +#include <limits> +#include <string> +#include <utility> +#include <vector> + +#include "libcef/common/net/http_header_utils.h" +#include "libcef/common/net_service/net_service_util.h" +#include "libcef/common/request_impl.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/common/content_switches.h" +#include "net/base/elements_upload_data_stream.h" +#include "net/base/load_flags.h" +#include "net/base/upload_bytes_element_reader.h" +#include "net/base/upload_data_stream.h" +#include "net/base/upload_element_reader.h" +#include "net/base/upload_file_element_reader.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_util.h" +#include "net/url_request/redirect_info.h" +#include "net/url_request/referrer_policy.h" +#include "services/network/public/cpp/data_element.h" +#include "services/network/public/cpp/network_switches.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/resource_request_body.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h" +#include "third_party/blink/public/platform/web_http_body.h" +#include "third_party/blink/public/platform/web_security_origin.h" +#include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/public/platform/web_url.h" +#include "third_party/blink/public/platform/web_url_error.h" +#include "third_party/blink/public/platform/web_url_request.h" +#include "third_party/blink/public/platform/web_url_request_util.h" +#include "third_party/blink/public/web/web_security_policy.h" + +namespace { + +const char kCacheControlDirectiveNoCache[] = "no-cache"; +const char kCacheControlDirectiveNoStore[] = "no-store"; +const char kCacheControlDirectiveOnlyIfCached[] = "only-if-cached"; + +// Mask of values that configure the cache policy. +const int kURCachePolicyMask = + (UR_FLAG_SKIP_CACHE | UR_FLAG_ONLY_FROM_CACHE | UR_FLAG_DISABLE_CACHE); + +// Returns the cef_urlrequest_flags_t policy specified by the Cache-Control +// request header directives, if any. The directives are case-insensitive and +// some have an optional argument. Multiple directives are comma-separated. +// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control +// for details. +int GetCacheControlHeaderPolicy(CefRequest::HeaderMap headerMap) { + std::string line; + + // Extract the Cache-Control header line. + { + CefRequest::HeaderMap::const_iterator it = headerMap.begin(); + for (; it != headerMap.end(); ++it) { + if (base::EqualsCaseInsensitiveASCII( + it->first.ToString(), net::HttpRequestHeaders::kCacheControl)) { + line = it->second; + break; + } + } + } + + int flags = 0; + + if (!line.empty()) { + HttpHeaderUtils::MakeASCIILower(&line); + + std::vector<base::StringPiece> pieces = base::SplitStringPiece( + line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + for (const auto& piece : pieces) { + if (base::EqualsCaseInsensitiveASCII(piece, + kCacheControlDirectiveNoCache)) { + flags |= UR_FLAG_SKIP_CACHE; + } else if (base::EqualsCaseInsensitiveASCII( + piece, kCacheControlDirectiveOnlyIfCached)) { + flags |= UR_FLAG_ONLY_FROM_CACHE; + } else if (base::EqualsCaseInsensitiveASCII( + piece, kCacheControlDirectiveNoStore)) { + flags |= UR_FLAG_DISABLE_CACHE; + } + } + } + + return flags; +} + +// Convert cef_urlrequest_flags_t to blink::WebCachePolicy. +blink::mojom::FetchCacheMode GetFetchCacheMode(int ur_flags) { + const bool skip_cache{!!(ur_flags & UR_FLAG_SKIP_CACHE)}; + const bool only_from_cache{!!(ur_flags & UR_FLAG_ONLY_FROM_CACHE)}; + const bool disable_cache{!!(ur_flags & UR_FLAG_DISABLE_CACHE)}; + if (only_from_cache && (skip_cache || disable_cache)) { + // The request will always fail because only_from_cache and + // skip_cache/disable_cache are mutually exclusive. + return blink::mojom::FetchCacheMode::kUnspecifiedForceCacheMiss; + } else if (disable_cache) { + // This additionally implies the skip_cache behavior. + return blink::mojom::FetchCacheMode::kNoStore; + } else if (skip_cache) { + return blink::mojom::FetchCacheMode::kBypassCache; + } else if (only_from_cache) { + return blink::mojom::FetchCacheMode::kOnlyIfCached; + } + return blink::mojom::FetchCacheMode::kDefault; +} + +// Read |headers| into |map|. +void GetHeaderMap(const net::HttpRequestHeaders& headers, + CefRequest::HeaderMap& map) { + map.clear(); + + if (headers.IsEmpty()) { + return; + } + + net::HttpRequestHeaders::Iterator it(headers); + while (it.GetNext()) { + const std::string& name = it.name(); + + // Do not include Referer in the header map. + if (!base::EqualsCaseInsensitiveASCII(name, + net::HttpRequestHeaders::kReferer)) { + map.insert(std::make_pair(name, it.value())); + } + }; +} + +// Read |source| into |map|. +void GetHeaderMap(const CefRequest::HeaderMap& source, + CefRequest::HeaderMap& map) { + map.clear(); + + CefRequest::HeaderMap::const_iterator it = source.begin(); + for (; it != source.end(); ++it) { + const CefString& name = it->first; + + // Do not include Referer in the header map. + if (!base::EqualsCaseInsensitiveASCII(name.ToString(), + net::HttpRequestHeaders::kReferer)) { + map.insert(std::make_pair(name, it->second)); + } + } +} + +} // namespace + +#define CHECK_READONLY_RETURN(val) \ + if (read_only_) { \ + NOTREACHED() << "object is read only"; \ + return val; \ + } + +#define CHECK_READONLY_RETURN_VOID() \ + if (read_only_) { \ + NOTREACHED() << "object is read only"; \ + return; \ + } + +#define SETBOOLFLAG(obj, flags, method, FLAG) \ + obj.method((flags & (FLAG)) == (FLAG)) + +// CefRequest ----------------------------------------------------------------- + +// static +CefRefPtr<CefRequest> CefRequest::Create() { + CefRefPtr<CefRequest> request(new CefRequestImpl()); + return request; +} + +// CefRequestImpl ------------------------------------------------------------- + +CefRequestImpl::CefRequestImpl() { + // Verify that our enum matches Chromium's values. + static_assert(static_cast<int>(REFERRER_POLICY_LAST_VALUE) == + static_cast<int>(net::ReferrerPolicy::MAX), + "enum mismatch"); + + base::AutoLock lock_scope(lock_); + Reset(); +} + +bool CefRequestImpl::IsReadOnly() { + base::AutoLock lock_scope(lock_); + return read_only_; +} + +CefString CefRequestImpl::GetURL() { + base::AutoLock lock_scope(lock_); + return url_.spec(); +} + +void CefRequestImpl::SetURL(const CefString& url) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + const GURL& new_url = GURL(url.ToString()); + if (url_ != new_url) { + Changed(kChangedUrl); + url_ = new_url; + } +} + +CefString CefRequestImpl::GetMethod() { + base::AutoLock lock_scope(lock_); + return method_; +} + +void CefRequestImpl::SetMethod(const CefString& method) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + const std::string& new_method = method; + if (method_ != new_method) { + Changed(kChangedMethod); + method_ = new_method; + } +} + +void CefRequestImpl::SetReferrer(const CefString& referrer_url, + ReferrerPolicy policy) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + const auto& sanitized_referrer = content::Referrer::SanitizeForRequest( + url_, content::Referrer(GURL(referrer_url.ToString()), + NetReferrerPolicyToBlinkReferrerPolicy(policy))); + const auto sanitized_policy = + BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy); + + if (referrer_url_ != sanitized_referrer.url || + referrer_policy_ != sanitized_policy) { + Changed(kChangedReferrer); + referrer_url_ = sanitized_referrer.url; + referrer_policy_ = sanitized_policy; + } +} + +CefString CefRequestImpl::GetReferrerURL() { + base::AutoLock lock_scope(lock_); + return referrer_url_.spec(); +} + +CefRequestImpl::ReferrerPolicy CefRequestImpl::GetReferrerPolicy() { + base::AutoLock lock_scope(lock_); + return referrer_policy_; +} + +CefRefPtr<CefPostData> CefRequestImpl::GetPostData() { + base::AutoLock lock_scope(lock_); + return postdata_; +} + +void CefRequestImpl::SetPostData(CefRefPtr<CefPostData> postData) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + Changed(kChangedPostData); + postdata_ = postData; +} + +void CefRequestImpl::GetHeaderMap(HeaderMap& headerMap) { + base::AutoLock lock_scope(lock_); + headerMap = headermap_; +} + +void CefRequestImpl::SetHeaderMap(const HeaderMap& headerMap) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + Changed(kChangedHeaderMap); + ::GetHeaderMap(headerMap, headermap_); +} + +CefString CefRequestImpl::GetHeaderByName(const CefString& name) { + base::AutoLock lock_scope(lock_); + + std::string nameLower = name; + HttpHeaderUtils::MakeASCIILower(&nameLower); + + auto it = HttpHeaderUtils::FindHeaderInMap(nameLower, headermap_); + if (it != headermap_.end()) { + return it->second; + } + + return CefString(); +} + +void CefRequestImpl::SetHeaderByName(const CefString& name, + const CefString& value, + bool overwrite) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + const std::string& nameStr = name; + + // Do not include Referer in the header map. + if (base::EqualsCaseInsensitiveASCII(nameStr, + net::HttpRequestHeaders::kReferer)) { + return; + } + + Changed(kChangedHeaderMap); + + // There may be multiple values, so remove any first. + for (auto it = headermap_.begin(); it != headermap_.end();) { + if (base::EqualsCaseInsensitiveASCII(it->first.ToString(), nameStr)) { + if (!overwrite) { + return; + } + it = headermap_.erase(it); + } else { + ++it; + } + } + + headermap_.insert(std::make_pair(name, value)); +} + +void CefRequestImpl::Set(const CefString& url, + const CefString& method, + CefRefPtr<CefPostData> postData, + const HeaderMap& headerMap) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + const GURL& new_url = GURL(url.ToString()); + if (url_ != new_url) { + Changed(kChangedUrl); + url_ = new_url; + } + const std::string& new_method = method; + if (method_ != new_method) { + Changed(kChangedMethod); + method_ = new_method; + } + if (postdata_ != postData) { + Changed(kChangedPostData); + postdata_ = postData; + } + Changed(kChangedHeaderMap); + ::GetHeaderMap(headerMap, headermap_); +} + +int CefRequestImpl::GetFlags() { + base::AutoLock lock_scope(lock_); + return flags_; +} + +void CefRequestImpl::SetFlags(int flags) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + if (flags_ != flags) { + Changed(kChangedFlags); + flags_ = flags; + } +} + +CefString CefRequestImpl::GetFirstPartyForCookies() { + base::AutoLock lock_scope(lock_); + return site_for_cookies_.RepresentativeUrl().spec(); +} + +void CefRequestImpl::SetFirstPartyForCookies(const CefString& url) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + auto new_site = net::SiteForCookies::FromUrl(GURL(url.ToString())); + if (!new_site.IsEquivalent(site_for_cookies_)) { + Changed(kChangedSiteForCookies); + site_for_cookies_ = new_site; + } +} + +CefRequestImpl::ResourceType CefRequestImpl::GetResourceType() { + base::AutoLock lock_scope(lock_); + return resource_type_; +} + +CefRequestImpl::TransitionType CefRequestImpl::GetTransitionType() { + base::AutoLock lock_scope(lock_); + return transition_type_; +} + +uint64 CefRequestImpl::GetIdentifier() { + base::AutoLock lock_scope(lock_); + return identifier_; +} + +void CefRequestImpl::Set(const network::ResourceRequest* request, + uint64 identifier) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + Reset(); + + url_ = request->url; + method_ = request->method; + identifier_ = identifier; + + if (request->referrer.is_valid()) { + const auto& sanitized_referrer = content::Referrer::SanitizeForRequest( + request->url, + content::Referrer( + request->referrer, + NetReferrerPolicyToBlinkReferrerPolicy( + static_cast<cef_referrer_policy_t>(request->referrer_policy)))); + referrer_url_ = sanitized_referrer.url; + referrer_policy_ = + BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy); + } + + // Transfer request headers. + ::GetHeaderMap(request->headers, headermap_); + + // Transfer post data, if any. + if (request->request_body) { + postdata_ = CefPostData::Create(); + static_cast<CefPostDataImpl*>(postdata_.get())->Set(*request->request_body); + } + + site_for_cookies_ = request->site_for_cookies; + + resource_type_ = static_cast<cef_resource_type_t>(request->resource_type); + transition_type_ = + static_cast<cef_transition_type_t>(request->transition_type); +} + +void CefRequestImpl::Get(network::ResourceRequest* request, + bool changed_only) const { + base::AutoLock lock_scope(lock_); + + if (ShouldSet(kChangedUrl, changed_only)) { + request->url = url_; + } + + if (ShouldSet(kChangedMethod, changed_only)) { + request->method = method_; + } + + if (ShouldSet(kChangedReferrer, changed_only)) { + request->referrer = referrer_url_; + request->referrer_policy = + static_cast<net::ReferrerPolicy>(referrer_policy_); + } + + if (ShouldSet(kChangedHeaderMap, changed_only)) { + net::HttpRequestHeaders headers; + headers.AddHeadersFromString(HttpHeaderUtils::GenerateHeaders(headermap_)); + request->headers.Swap(&headers); + } + + if (ShouldSet(kChangedPostData, changed_only)) { + if (postdata_.get()) { + request->request_body = + static_cast<CefPostDataImpl*>(postdata_.get())->GetBody(); + } else if (request->request_body) { + request->request_body = nullptr; + } + } + + if (!site_for_cookies_.IsNull() && + ShouldSet(kChangedSiteForCookies, changed_only)) { + request->site_for_cookies = site_for_cookies_; + } + + if (ShouldSet(kChangedFlags, changed_only)) { + int flags = flags_; + if (!(flags & kURCachePolicyMask)) { + // Only consider the Cache-Control directives when a cache policy is not + // explicitly set on the request. + flags |= GetCacheControlHeaderPolicy(headermap_); + } + + int net_flags = 0; + + if (flags & UR_FLAG_SKIP_CACHE) { + net_flags |= net::LOAD_BYPASS_CACHE; + } + if (flags & UR_FLAG_ONLY_FROM_CACHE) { + net_flags |= net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION; + } + if (flags & UR_FLAG_DISABLE_CACHE) { + net_flags |= net::LOAD_DISABLE_CACHE; + } + + if (!(flags & UR_FLAG_ALLOW_STORED_CREDENTIALS)) { + // This will disable all credentials including cookies, auth tokens, etc. + request->credentials_mode = network::mojom::CredentialsMode::kOmit; + } + + request->load_flags = net_flags; + } +} + +void CefRequestImpl::Set(const net::RedirectInfo& redirect_info) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + url_ = redirect_info.new_url; + method_ = redirect_info.new_method; + site_for_cookies_ = redirect_info.new_site_for_cookies; + + const auto& sanitized_referrer = content::Referrer::SanitizeForRequest( + redirect_info.new_url, + content::Referrer(GURL(redirect_info.new_referrer), + NetReferrerPolicyToBlinkReferrerPolicy( + static_cast<cef_referrer_policy_t>( + redirect_info.new_referrer_policy)))); + referrer_url_ = sanitized_referrer.url; + referrer_policy_ = + BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy); +} + +void CefRequestImpl::Set(const net::HttpRequestHeaders& headers) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + ::GetHeaderMap(headers, headermap_); +} + +void CefRequestImpl::Set(content::NavigationHandle* navigation_handle) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + Reset(); + + url_ = navigation_handle->GetURL(); + method_ = navigation_handle->IsPost() ? "POST" : "GET"; + + const auto& sanitized_referrer = content::Referrer::SanitizeForRequest( + navigation_handle->GetURL(), navigation_handle->GetReferrer()); + referrer_url_ = sanitized_referrer->url; + referrer_policy_ = + BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer->policy); + + resource_type_ = + navigation_handle->IsInMainFrame() ? RT_MAIN_FRAME : RT_SUB_FRAME; + transition_type_ = static_cast<cef_transition_type_t>( + navigation_handle->GetPageTransition()); +} + +// static +void CefRequestImpl::Get(const cef::mojom::RequestParamsPtr& params, + blink::WebURLRequest& request) { + request.SetUrl(params->url); + request.SetRequestorOrigin(blink::WebSecurityOrigin::Create(params->url)); + if (!params->method.empty()) { + request.SetHttpMethod(blink::WebString::FromASCII(params->method)); + } + + if (params->referrer && params->referrer->url.is_valid()) { + const blink::WebString& referrer = + blink::WebSecurityPolicy::GenerateReferrerHeader( + params->referrer->policy, params->url, + blink::WebString::FromUTF8(params->referrer->url.spec())); + if (!referrer.IsEmpty()) { + request.SetReferrerString(referrer); + request.SetReferrerPolicy(params->referrer->policy); + } + } + + CefRequest::HeaderMap headerMap; + if (!params->headers.empty()) { + for (net::HttpUtil::HeadersIterator i(params->headers.begin(), + params->headers.end(), "\n\r"); + i.GetNext();) { + request.AddHttpHeaderField(blink::WebString::FromUTF8(i.name()), + blink::WebString::FromUTF8(i.values())); + headerMap.insert(std::make_pair(i.name(), i.values())); + } + } + + if (params->upload_data) { + const std::u16string& method = request.HttpMethod().Utf16(); + if (method == u"GET" || method == u"HEAD") { + request.SetHttpMethod(blink::WebString::FromASCII("POST")); + } + + // The comparison performed by httpHeaderField() is case insensitive. + if (request + .HttpHeaderField(blink::WebString::FromASCII( + net::HttpRequestHeaders::kContentType)) + .length() == 0) { + request.SetHttpHeaderField( + blink::WebString::FromASCII(net::HttpRequestHeaders::kContentType), + blink::WebString::FromASCII( + net_service::kContentTypeApplicationFormURLEncoded)); + } + + request.SetHttpBody( + blink::GetWebHTTPBodyForRequestBody(*params->upload_data)); + } + + if (!params->site_for_cookies.IsNull()) { + request.SetSiteForCookies(params->site_for_cookies); + } + + int flags = params->load_flags; + if (!(flags & kURCachePolicyMask)) { + // Only consider the Cache-Control directives when a cache policy is not + // explicitly set on the request. + flags |= GetCacheControlHeaderPolicy(headerMap); + } + request.SetCacheMode(GetFetchCacheMode(flags)); + + SETBOOLFLAG(request, params->load_flags, SetAllowStoredCredentials, + UR_FLAG_ALLOW_STORED_CREDENTIALS); + SETBOOLFLAG(request, params->load_flags, SetReportUploadProgress, + UR_FLAG_REPORT_UPLOAD_PROGRESS); +} + +void CefRequestImpl::Get(cef::mojom::RequestParamsPtr& params) const { + base::AutoLock lock_scope(lock_); + + params->url = url_; + params->method = method_; + + // Referrer policy will be applied later in the request pipeline. + params->referrer = blink::mojom::Referrer::New( + referrer_url_, NetReferrerPolicyToBlinkReferrerPolicy(referrer_policy_)); + + if (!headermap_.empty()) { + params->headers = HttpHeaderUtils::GenerateHeaders(headermap_); + } + + if (postdata_) { + CefPostDataImpl* impl = static_cast<CefPostDataImpl*>(postdata_.get()); + params->upload_data = impl->GetBody(); + } + + params->site_for_cookies = site_for_cookies_; + params->load_flags = flags_; +} + +void CefRequestImpl::SetReadOnly(bool read_only) { + base::AutoLock lock_scope(lock_); + if (read_only_ == read_only) { + return; + } + + read_only_ = read_only; + + if (postdata_.get()) { + static_cast<CefPostDataImpl*>(postdata_.get())->SetReadOnly(read_only); + } +} + +void CefRequestImpl::SetTrackChanges(bool track_changes, + bool backup_on_change) { + base::AutoLock lock_scope(lock_); + if (track_changes_ == track_changes) { + return; + } + + if (!track_changes && backup_on_change_) { + backup_.reset(); + } + + track_changes_ = track_changes; + backup_on_change_ = track_changes ? backup_on_change : false; + changes_ = kChangedNone; + + if (postdata_.get()) { + static_cast<CefPostDataImpl*>(postdata_.get()) + ->SetTrackChanges(track_changes); + } +} + +void CefRequestImpl::RevertChanges() { + base::AutoLock lock_scope(lock_); + DCHECK(!read_only_); + DCHECK(track_changes_); + DCHECK(backup_on_change_); + if (!backup_) { + return; + } + + // Restore the original values if a backup exists. + if (backup_->backups_ & kChangedUrl) { + url_ = backup_->url_; + } + if (backup_->backups_ & kChangedMethod) { + method_ = backup_->method_; + } + if (backup_->backups_ & kChangedReferrer) { + referrer_url_ = backup_->referrer_url_; + referrer_policy_ = backup_->referrer_policy_; + } + if (backup_->backups_ & kChangedPostData) { + postdata_ = backup_->postdata_; + } + if (backup_->backups_ & kChangedHeaderMap) { + DCHECK(backup_->headermap_); + headermap_.swap(*backup_->headermap_); + } + if (backup_->backups_ & kChangedFlags) { + flags_ = backup_->flags_; + } + if (backup_->backups_ & kChangedSiteForCookies) { + site_for_cookies_ = backup_->site_for_cookies_; + } + + backup_.reset(); +} + +void CefRequestImpl::DiscardChanges() { + base::AutoLock lock_scope(lock_); + DCHECK(track_changes_); + DCHECK(backup_on_change_); + backup_.reset(); +} + +uint8_t CefRequestImpl::GetChanges() const { + base::AutoLock lock_scope(lock_); + + uint8_t changes = changes_; + if (postdata_.get() && + static_cast<CefPostDataImpl*>(postdata_.get())->HasChanges()) { + changes |= kChangedPostData; + } + return changes; +} + +// From content/child/web_url_loader_impl.cc +// static +network::mojom::ReferrerPolicy +CefRequestImpl::NetReferrerPolicyToBlinkReferrerPolicy( + cef_referrer_policy_t net_policy) { + switch (net_policy) { + case REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE: + return network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade; + case REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN: + return network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin; + case REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN: + return network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin; + case REFERRER_POLICY_NEVER_CLEAR_REFERRER: + return network::mojom::ReferrerPolicy::kAlways; + case REFERRER_POLICY_ORIGIN: + return network::mojom::ReferrerPolicy::kOrigin; + case REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN: + return network::mojom::ReferrerPolicy::kSameOrigin; + case REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE: + return network::mojom::ReferrerPolicy::kStrictOrigin; + case REFERRER_POLICY_NO_REFERRER: + return network::mojom::ReferrerPolicy::kNever; + } + NOTREACHED(); + return network::mojom::ReferrerPolicy::kDefault; +} + +// static +cef_referrer_policy_t CefRequestImpl::BlinkReferrerPolicyToNetReferrerPolicy( + network::mojom::ReferrerPolicy blink_policy) { + switch (blink_policy) { + case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade: + return REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE; + case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin: + return REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; + case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin: + return REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; + case network::mojom::ReferrerPolicy::kAlways: + return REFERRER_POLICY_NEVER_CLEAR_REFERRER; + case network::mojom::ReferrerPolicy::kOrigin: + return REFERRER_POLICY_ORIGIN; + case network::mojom::ReferrerPolicy::kSameOrigin: + return REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN; + case network::mojom::ReferrerPolicy::kStrictOrigin: + return REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE; + case network::mojom::ReferrerPolicy::kNever: + return REFERRER_POLICY_NO_REFERRER; + case network::mojom::ReferrerPolicy::kDefault: + return REFERRER_POLICY_DEFAULT; + } + NOTREACHED(); + return REFERRER_POLICY_DEFAULT; +} + +void CefRequestImpl::Changed(uint8_t changes) { + lock_.AssertAcquired(); + if (!track_changes_) { + return; + } + + if (backup_on_change_) { + if (!backup_) { + backup_.reset(new Backup()); + } + + // Set the backup values if not already set. + if ((changes & kChangedUrl) && !(backup_->backups_ & kChangedUrl)) { + backup_->url_ = url_; + backup_->backups_ |= kChangedUrl; + } + if ((changes & kChangedMethod) && !(backup_->backups_ & kChangedMethod)) { + backup_->method_ = method_; + backup_->backups_ |= kChangedMethod; + } + if ((changes & kChangedReferrer) && + !(backup_->backups_ & kChangedReferrer)) { + backup_->referrer_url_ = referrer_url_; + backup_->referrer_policy_ = referrer_policy_; + backup_->backups_ |= kChangedReferrer; + } + if ((changes & kChangedPostData) && + !(backup_->backups_ & kChangedPostData)) { + backup_->postdata_ = postdata_; + backup_->backups_ |= kChangedPostData; + } + if ((changes & kChangedHeaderMap) && + !(backup_->backups_ & kChangedHeaderMap)) { + backup_->headermap_.reset(new HeaderMap()); + if (!headermap_.empty()) { + backup_->headermap_->insert(headermap_.begin(), headermap_.end()); + } + backup_->backups_ |= kChangedHeaderMap; + } + if ((changes & kChangedFlags) && !(backup_->backups_ & kChangedFlags)) { + backup_->flags_ = flags_; + backup_->backups_ |= kChangedFlags; + } + if ((changes & kChangedSiteForCookies) && + !(backup_->backups_ & kChangedSiteForCookies)) { + backup_->site_for_cookies_ = site_for_cookies_; + backup_->backups_ |= kChangedSiteForCookies; + } + } + + changes_ |= changes; +} + +bool CefRequestImpl::ShouldSet(uint8_t changes, bool changed_only) const { + lock_.AssertAcquired(); + + // Always change if changes are not being tracked. + if (!track_changes_) { + return true; + } + + // Always change if changed-only was not requested. + if (!changed_only) { + return true; + } + + // Change if the |changes| bit flag has been set. + if ((changes_ & changes) == changes) { + return true; + } + + if ((changes & kChangedPostData) == kChangedPostData) { + // Change if the post data object was modified directly. + if (postdata_.get() && + static_cast<CefPostDataImpl*>(postdata_.get())->HasChanges()) { + return true; + } + } + + return false; +} + +void CefRequestImpl::Reset() { + lock_.AssertAcquired(); + DCHECK(!read_only_); + + url_ = GURL(); + method_ = "GET"; + referrer_url_ = GURL(); + referrer_policy_ = REFERRER_POLICY_DEFAULT; + postdata_ = nullptr; + headermap_.clear(); + resource_type_ = RT_SUB_RESOURCE; + transition_type_ = TT_EXPLICIT; + identifier_ = 0U; + flags_ = UR_FLAG_NONE; + site_for_cookies_ = net::SiteForCookies(); + + changes_ = kChangedNone; +} + +// CefPostData ---------------------------------------------------------------- + +// static +CefRefPtr<CefPostData> CefPostData::Create() { + CefRefPtr<CefPostData> postdata(new CefPostDataImpl()); + return postdata; +} + +// CefPostDataImpl ------------------------------------------------------------ + +CefPostDataImpl::CefPostDataImpl() + : read_only_(false), + has_excluded_elements_(false), + track_changes_(false), + has_changes_(false) {} + +bool CefPostDataImpl::IsReadOnly() { + base::AutoLock lock_scope(lock_); + return read_only_; +} + +bool CefPostDataImpl::HasExcludedElements() { + base::AutoLock lock_scope(lock_); + return has_excluded_elements_; +} + +size_t CefPostDataImpl::GetElementCount() { + base::AutoLock lock_scope(lock_); + return elements_.size(); +} + +void CefPostDataImpl::GetElements(ElementVector& elements) { + base::AutoLock lock_scope(lock_); + elements = elements_; +} + +bool CefPostDataImpl::RemoveElement(CefRefPtr<CefPostDataElement> element) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN(false); + + ElementVector::iterator it = elements_.begin(); + for (; it != elements_.end(); ++it) { + if (it->get() == element.get()) { + elements_.erase(it); + Changed(); + return true; + } + } + + return false; +} + +bool CefPostDataImpl::AddElement(CefRefPtr<CefPostDataElement> element) { + bool found = false; + + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN(false); + + // check that the element isn't already in the list before adding + ElementVector::const_iterator it = elements_.begin(); + for (; it != elements_.end(); ++it) { + if (it->get() == element.get()) { + found = true; + break; + } + } + + if (!found) { + elements_.push_back(element); + Changed(); + } + + return !found; +} + +void CefPostDataImpl::RemoveElements() { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + elements_.clear(); + Changed(); +} + +void CefPostDataImpl::Set(const network::ResourceRequestBody& body) { + { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + } + + CefRefPtr<CefPostDataElement> postelem; + + for (const auto& element : *body.elements()) { + postelem = CefPostDataElement::Create(); + static_cast<CefPostDataElementImpl*>(postelem.get())->Set(element); + AddElement(postelem); + } +} + +scoped_refptr<network::ResourceRequestBody> CefPostDataImpl::GetBody() const { + base::AutoLock lock_scope(lock_); + + scoped_refptr<network::ResourceRequestBody> body = + new network::ResourceRequestBody(); + for (const auto& element : elements_) { + static_cast<CefPostDataElementImpl*>(element.get())->Get(*body); + } + return body; +} + +void CefPostDataImpl::SetReadOnly(bool read_only) { + base::AutoLock lock_scope(lock_); + if (read_only_ == read_only) { + return; + } + + read_only_ = read_only; + + ElementVector::const_iterator it = elements_.begin(); + for (; it != elements_.end(); ++it) { + static_cast<CefPostDataElementImpl*>(it->get())->SetReadOnly(read_only); + } +} + +void CefPostDataImpl::SetTrackChanges(bool track_changes) { + base::AutoLock lock_scope(lock_); + if (track_changes_ == track_changes) { + return; + } + + track_changes_ = track_changes; + has_changes_ = false; + + ElementVector::const_iterator it = elements_.begin(); + for (; it != elements_.end(); ++it) { + static_cast<CefPostDataElementImpl*>(it->get())->SetTrackChanges( + track_changes); + } +} + +bool CefPostDataImpl::HasChanges() const { + base::AutoLock lock_scope(lock_); + if (has_changes_) { + return true; + } + + ElementVector::const_iterator it = elements_.begin(); + for (; it != elements_.end(); ++it) { + if (static_cast<CefPostDataElementImpl*>(it->get())->HasChanges()) { + return true; + } + } + + return false; +} + +void CefPostDataImpl::Changed() { + lock_.AssertAcquired(); + if (track_changes_ && !has_changes_) { + has_changes_ = true; + } +} + +// CefPostDataElement --------------------------------------------------------- + +// static +CefRefPtr<CefPostDataElement> CefPostDataElement::Create() { + CefRefPtr<CefPostDataElement> element(new CefPostDataElementImpl()); + return element; +} + +// CefPostDataElementImpl ----------------------------------------------------- + +CefPostDataElementImpl::CefPostDataElementImpl() + : type_(PDE_TYPE_EMPTY), + read_only_(false), + track_changes_(false), + has_changes_(false) { + memset(&data_, 0, sizeof(data_)); +} + +CefPostDataElementImpl::~CefPostDataElementImpl() { + Cleanup(); +} + +bool CefPostDataElementImpl::IsReadOnly() { + base::AutoLock lock_scope(lock_); + return read_only_; +} + +void CefPostDataElementImpl::SetToEmpty() { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + Cleanup(); + Changed(); +} + +void CefPostDataElementImpl::SetToFile(const CefString& fileName) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + // Clear any data currently in the element + Cleanup(); + + // Assign the new data + type_ = PDE_TYPE_FILE; + cef_string_copy(fileName.c_str(), fileName.length(), &data_.filename); + + Changed(); +} + +void CefPostDataElementImpl::SetToBytes(size_t size, const void* bytes) { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + + // Clear any data currently in the element + Cleanup(); + + // Assign the new data + void* data = malloc(size); + DCHECK(data != nullptr); + if (data == nullptr) { + return; + } + + memcpy(data, bytes, size); + + type_ = PDE_TYPE_BYTES; + data_.bytes.bytes = data; + data_.bytes.size = size; + + Changed(); +} + +CefPostDataElement::Type CefPostDataElementImpl::GetType() { + base::AutoLock lock_scope(lock_); + return type_; +} + +CefString CefPostDataElementImpl::GetFile() { + base::AutoLock lock_scope(lock_); + DCHECK(type_ == PDE_TYPE_FILE); + CefString filename; + if (type_ == PDE_TYPE_FILE) { + filename.FromString(data_.filename.str, data_.filename.length, false); + } + return filename; +} + +size_t CefPostDataElementImpl::GetBytesCount() { + base::AutoLock lock_scope(lock_); + DCHECK(type_ == PDE_TYPE_BYTES); + size_t size = 0; + if (type_ == PDE_TYPE_BYTES) { + size = data_.bytes.size; + } + return size; +} + +size_t CefPostDataElementImpl::GetBytes(size_t size, void* bytes) { + base::AutoLock lock_scope(lock_); + DCHECK(type_ == PDE_TYPE_BYTES); + size_t rv = 0; + if (type_ == PDE_TYPE_BYTES) { + rv = (size < data_.bytes.size ? size : data_.bytes.size); + memcpy(bytes, data_.bytes.bytes, rv); + } + return rv; +} + +void CefPostDataElementImpl::Set(const network::DataElement& element) { + { + base::AutoLock lock_scope(lock_); + CHECK_READONLY_RETURN_VOID(); + } + + if (element.type() == network::DataElement::Tag::kBytes) { + const auto& bytes_element = element.As<network::DataElementBytes>(); + const auto& bytes = bytes_element.bytes(); + SetToBytes(bytes.size(), bytes.data()); + } else if (element.type() == network::DataElement::Tag::kFile) { + const auto& file_element = element.As<network::DataElementFile>(); + SetToFile(file_element.path().value()); + } +} + +void CefPostDataElementImpl::Get(network::ResourceRequestBody& body) const { + base::AutoLock lock_scope(lock_); + + if (type_ == PDE_TYPE_BYTES) { + body.AppendBytes(static_cast<char*>(data_.bytes.bytes), data_.bytes.size); + } else if (type_ == PDE_TYPE_FILE) { + base::FilePath path = base::FilePath(CefString(&data_.filename)); + body.AppendFileRange(path, 0, std::numeric_limits<uint64_t>::max(), + base::Time()); + } else { + NOTREACHED(); + } +} + +void CefPostDataElementImpl::SetReadOnly(bool read_only) { + base::AutoLock lock_scope(lock_); + if (read_only_ == read_only) { + return; + } + + read_only_ = read_only; +} + +void CefPostDataElementImpl::SetTrackChanges(bool track_changes) { + base::AutoLock lock_scope(lock_); + if (track_changes_ == track_changes) { + return; + } + + track_changes_ = track_changes; + has_changes_ = false; +} + +bool CefPostDataElementImpl::HasChanges() const { + base::AutoLock lock_scope(lock_); + return has_changes_; +} + +void CefPostDataElementImpl::Changed() { + lock_.AssertAcquired(); + if (track_changes_ && !has_changes_) { + has_changes_ = true; + } +} + +void CefPostDataElementImpl::Cleanup() { + if (type_ == PDE_TYPE_EMPTY) { + return; + } + + if (type_ == PDE_TYPE_BYTES) { + free(data_.bytes.bytes); + } else if (type_ == PDE_TYPE_FILE) { + cef_string_clear(&data_.filename); + } + type_ = PDE_TYPE_EMPTY; + memset(&data_, 0, sizeof(data_)); +} |