// 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 #include #include #include #include #include "libcef/browser/navigate_params.h" #include "libcef/common/cef_messages.h" #include "libcef/common/net/http_header_utils.h" #include "libcef/common/net/upload_data.h" #include "libcef/common/request_impl.h" #include "base/command_line.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/navigation_interception/navigation_params.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_request_info.h" #include "content/public/common/content_switches.h" #include "content/public/common/resource_type.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/url_fetcher.h" #include "net/url_request/url_request.h" #include "third_party/WebKit/public/platform/WebCachePolicy.h" #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebURLError.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" namespace { const char kReferrerLowerCase[] = "referer"; const char kContentTypeLowerCase[] = "content-type"; const char kCacheControlLowerCase[] = "cache-control"; const char kCacheControlDirectiveNoCacheLowerCase[] = "no-cache"; const char kCacheControlDirectiveOnlyIfCachedLowerCase[] = "only-if-cached"; const char kApplicationFormURLEncoded[] = "application/x-www-form-urlencoded"; // Mask of values that configure the cache policy. const int kURCachePolicyMask = (UR_FLAG_SKIP_CACHE | UR_FLAG_ONLY_FROM_CACHE); // A subclass of net::UploadBytesElementReader that keeps the associated // UploadElement alive until the request completes. class BytesElementReader : public net::UploadBytesElementReader { public: explicit BytesElementReader(std::unique_ptr element) : net::UploadBytesElementReader(element->bytes(), element->bytes_length()), element_(std::move(element)) { DCHECK_EQ(net::UploadElement::TYPE_BYTES, element_->type()); } private: std::unique_ptr element_; DISALLOW_COPY_AND_ASSIGN(BytesElementReader); }; base::TaskRunner* GetFileTaskRunner() { return content::BrowserThread::GetTaskRunnerForThread( content::BrowserThread::FILE) .get(); } // A subclass of net::UploadFileElementReader that keeps the associated // UploadElement alive until the request completes. class FileElementReader : public net::UploadFileElementReader { public: explicit FileElementReader(std::unique_ptr element) : net::UploadFileElementReader( GetFileTaskRunner(), element->file_path(), element->file_range_offset(), element->file_range_length(), element->expected_file_modification_time()), element_(std::move(element)) { DCHECK_EQ(net::UploadElement::TYPE_FILE, element_->type()); } private: std::unique_ptr element_; DISALLOW_COPY_AND_ASSIGN(FileElementReader); }; std::string GetURLRequestReferrer(const GURL& referrer_url) { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (!referrer_url.is_valid() || command_line->HasSwitch(switches::kNoReferrers)) { return std::string(); } return referrer_url.spec(); } // 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::LowerCaseEqualsASCII(it->first.ToString(), kCacheControlLowerCase)) { line = it->second; break; } } } int flags = 0; if (!line.empty()) { std::transform(line.begin(), line.end(), line.begin(), ::tolower); std::vector pieces = base::SplitStringPiece( line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); for (const auto& piece : pieces) { if (base::LowerCaseEqualsASCII(piece, kCacheControlDirectiveNoCacheLowerCase)) { flags |= UR_FLAG_SKIP_CACHE; } else if (base::LowerCaseEqualsASCII( piece, kCacheControlDirectiveOnlyIfCachedLowerCase)) { flags |= UR_FLAG_ONLY_FROM_CACHE; } } } return flags; } // Convert cef_urlrequest_flags_t to blink::WebCachePolicy. blink::WebCachePolicy GetWebCachePolicy(int ur_flags) { if ((ur_flags & kURCachePolicyMask) == kURCachePolicyMask) { return blink::WebCachePolicy::kBypassCacheLoadOnlyFromCache; } else if (ur_flags & UR_FLAG_SKIP_CACHE) { return blink::WebCachePolicy::kBypassingCache; } else if (ur_flags & UR_FLAG_ONLY_FROM_CACHE) { return blink::WebCachePolicy::kReturnCacheDataDontLoad; } return blink::WebCachePolicy::kUseProtocolCachePolicy; } // Convert blink::WebCachePolicy to cef_urlrequest_flags_t. int GetURCachePolicy(blink::WebCachePolicy web_policy) { if (web_policy == blink::WebCachePolicy::kBypassCacheLoadOnlyFromCache) { return kURCachePolicyMask; } else if (web_policy == blink::WebCachePolicy::kBypassingCache) { return UR_FLAG_SKIP_CACHE; } else if (web_policy == blink::WebCachePolicy::kReturnCacheDataDontLoad) { return UR_FLAG_ONLY_FROM_CACHE; } return 0; } blink::WebString FilePathStringToWebString( const base::FilePath::StringType& str) { #if defined(OS_POSIX) return blink::WebString::FromUTF8(str); #elif defined(OS_WIN) return blink::WebString::FromUTF16(str); #endif } // 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::LowerCaseEqualsASCII(name, kReferrerLowerCase)) map.insert(std::make_pair(name, it.value())); }; } // Read |request| into |map|. If a Referer value is specified populate // |referrer|. void GetHeaderMap(const blink::WebURLRequest& request, CefRequest::HeaderMap& map, GURL& referrer) { map.clear(); class HeaderVisitor : public blink::WebHTTPHeaderVisitor { public: HeaderVisitor(CefRequest::HeaderMap* map, GURL* referrer) : map_(map), referrer_(referrer) {} void VisitHeader(const blink::WebString& name, const blink::WebString& value) override { const base::string16& nameStr = name.Utf16(); const base::string16& valueStr = value.Utf16(); if (base::LowerCaseEqualsASCII(nameStr, kReferrerLowerCase)) *referrer_ = GURL(valueStr); else map_->insert(std::make_pair(nameStr, valueStr)); } private: CefRequest::HeaderMap* map_; GURL* referrer_; }; HeaderVisitor visitor(&map, &referrer); request.VisitHTTPHeaderFields(&visitor); } // 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::LowerCaseEqualsASCII(name.ToString(), kReferrerLowerCase)) map.insert(std::make_pair(name, it->second)); } } // Read |map| into |request|. void SetHeaderMap(const CefRequest::HeaderMap& map, blink::WebURLRequest& request) { CefRequest::HeaderMap::const_iterator it = map.begin(); for (; it != map.end(); ++it) { request.SetHTTPHeaderField(blink::WebString::FromUTF16(it->first), blink::WebString::FromUTF16(it->second)); } } // Type used in UploadDataStream. typedef std::vector> UploadElementReaders; } // 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::Create() { CefRefPtr request(new CefRequestImpl()); return request; } // CefRequestImpl ------------------------------------------------------------- CefRequestImpl::CefRequestImpl() : read_only_(false), track_changes_(false) { // Verify that our enum matches Chromium's values. static_assert(static_cast(REFERRER_POLICY_LAST_VALUE) == static_cast(net::URLRequest::MAX_REFERRER_POLICY), "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) { url_ = new_url; Changed(kChangedUrl); } } 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) { method_ = new_method; Changed(kChangedMethod); } } void CefRequestImpl::SetReferrer(const CefString& referrer_url, ReferrerPolicy policy) { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); // Call GetAsReferrer here for consistency since the same logic will later be // applied by URLRequest::SetReferrer(). const GURL& new_referrer_url = GURL(referrer_url.ToString()).GetAsReferrer(); if (referrer_url_ != new_referrer_url || referrer_policy_ != policy) { referrer_url_ = new_referrer_url; referrer_policy_ = policy; Changed(kChangedReferrer); } } 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 CefRequestImpl::GetPostData() { base::AutoLock lock_scope(lock_); return postdata_; } void CefRequestImpl::SetPostData(CefRefPtr postData) { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); postdata_ = postData; Changed(kChangedPostData); } 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(); ::GetHeaderMap(headerMap, headermap_); Changed(kChangedHeaderMap); } void CefRequestImpl::Set(const CefString& url, const CefString& method, CefRefPtr postData, const HeaderMap& headerMap) { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); const GURL& new_url = GURL(url.ToString()); if (url_ != new_url) { url_ = new_url; Changed(kChangedUrl); } const std::string& new_method = method; if (method_ != new_method) { method_ = new_method; Changed(kChangedMethod); } postdata_ = postData; ::GetHeaderMap(headerMap, headermap_); Changed(kChangedPostData | kChangedHeaderMap); } 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) { flags_ = flags; Changed(kChangedFlags); } } CefString CefRequestImpl::GetFirstPartyForCookies() { base::AutoLock lock_scope(lock_); return site_for_cookies_.spec(); } void CefRequestImpl::SetFirstPartyForCookies(const CefString& url) { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); const GURL& new_url = GURL(url.ToString()); if (site_for_cookies_ != new_url) { site_for_cookies_ = new_url; Changed(kChangedFirstPartyForCookies); } } 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(net::URLRequest* request) { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); Reset(); url_ = request->url(); method_ = request->method(); identifier_ = request->identifier(); // URLRequest::SetReferrer ensures that we do not send username and password // fields in the referrer. GURL referrer(request->referrer()); // Our consumer should have made sure that this is a safe referrer. See for // instance WebCore::FrameLoader::HideReferrer. if (referrer.is_valid()) { referrer_url_ = referrer; referrer_policy_ = static_cast(request->referrer_policy()); } // Transfer request headers. ::GetHeaderMap(request->extra_request_headers(), headermap_); // Transfer post data, if any. const net::UploadDataStream* data = request->get_upload(); if (data) { postdata_ = CefPostData::Create(); static_cast(postdata_.get())->Set(*data); } site_for_cookies_ = request->site_for_cookies(); const content::ResourceRequestInfo* info = content::ResourceRequestInfo::ForRequest(request); if (info) { resource_type_ = static_cast(info->GetResourceType()); transition_type_ = static_cast(info->GetPageTransition()); } } void CefRequestImpl::Get(net::URLRequest* request, bool changed_only) const { base::AutoLock lock_scope(lock_); if (ShouldSet(kChangedMethod, changed_only)) request->set_method(method_); if (ShouldSet(kChangedReferrer, changed_only)) { request->SetReferrer(GetURLRequestReferrer(referrer_url_)); request->set_referrer_policy( static_cast(referrer_policy_)); } if (ShouldSet(kChangedHeaderMap, changed_only)) { net::HttpRequestHeaders headers; headers.AddHeadersFromString(HttpHeaderUtils::GenerateHeaders(headermap_)); request->SetExtraRequestHeaders(headers); } if (ShouldSet(kChangedPostData, changed_only)) { if (postdata_.get()) { request->set_upload( static_cast(postdata_.get())->Get()); } else if (request->get_upload()) { request->set_upload(std::unique_ptr()); } } if (!site_for_cookies_.is_empty() && ShouldSet(kChangedFirstPartyForCookies, changed_only)) { request->set_site_for_cookies(site_for_cookies_); } } void CefRequestImpl::Set( const navigation_interception::NavigationParams& params, bool is_main_frame) { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); Reset(); url_ = params.url(); method_ = params.is_post() ? "POST" : "GET"; const content::Referrer& sanitized_referrer = content::Referrer::SanitizeForRequest(params.url(), params.referrer()); referrer_url_ = sanitized_referrer.url; referrer_policy_ = BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy); resource_type_ = is_main_frame ? RT_MAIN_FRAME : RT_SUB_FRAME; transition_type_ = static_cast(params.transition_type()); } void CefRequestImpl::Set(const blink::WebURLRequest& request) { DCHECK(!request.IsNull()); base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); Reset(); url_ = request.Url(); method_ = request.HttpMethod().Utf8(); ::GetHeaderMap(request, headermap_, referrer_url_); referrer_policy_ = BlinkReferrerPolicyToNetReferrerPolicy(request.GetReferrerPolicy()); const blink::WebHTTPBody& body = request.HttpBody(); if (!body.IsNull()) { postdata_ = new CefPostDataImpl(); static_cast(postdata_.get())->Set(body); } site_for_cookies_ = request.SiteForCookies(); flags_ |= GetURCachePolicy(request.GetCachePolicy()); if (request.AllowStoredCredentials()) flags_ |= UR_FLAG_ALLOW_STORED_CREDENTIALS; if (request.ReportUploadProgress()) flags_ |= UR_FLAG_REPORT_UPLOAD_PROGRESS; } void CefRequestImpl::Get(blink::WebURLRequest& request, int64& upload_data_size) const { base::AutoLock lock_scope(lock_); request.SetRequestContext(blink::WebURLRequest::kRequestContextInternal); request.SetURL(url_); request.SetHTTPMethod(blink::WebString::FromUTF8(method_)); if (!referrer_url_.is_empty()) { const blink::WebString& referrer = blink::WebSecurityPolicy::GenerateReferrerHeader( NetReferrerPolicyToBlinkReferrerPolicy(referrer_policy_), url_, blink::WebString::FromUTF8(referrer_url_.spec())); if (!referrer.IsEmpty()) { request.SetHTTPReferrer( referrer, NetReferrerPolicyToBlinkReferrerPolicy(referrer_policy_)); } } if (postdata_.get()) { blink::WebHTTPBody body; body.Initialize(); static_cast(postdata_.get())->Get(body); request.SetHTTPBody(body); if (flags_ & UR_FLAG_REPORT_UPLOAD_PROGRESS) { // Attempt to determine the upload data size. CefPostData::ElementVector elements; postdata_->GetElements(elements); if (elements.size() == 1 && elements[0]->GetType() == PDE_TYPE_BYTES) { CefPostDataElementImpl* impl = static_cast(elements[0].get()); upload_data_size = impl->GetBytesCount(); } } } ::SetHeaderMap(headermap_, request); if (!site_for_cookies_.is_empty()) request.SetSiteForCookies(site_for_cookies_); 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_); } request.SetCachePolicy(GetWebCachePolicy(flags)); SETBOOLFLAG(request, flags_, SetAllowStoredCredentials, UR_FLAG_ALLOW_STORED_CREDENTIALS); SETBOOLFLAG(request, flags_, SetReportUploadProgress, UR_FLAG_REPORT_UPLOAD_PROGRESS); } // static void CefRequestImpl::Get(const CefMsg_LoadRequest_Params& params, blink::WebURLRequest& request) { request.SetURL(params.url); if (!params.method.empty()) request.SetHTTPMethod(blink::WebString::FromASCII(params.method)); if (params.referrer.is_valid()) { const blink::WebString& referrer = blink::WebSecurityPolicy::GenerateReferrerHeader( NetReferrerPolicyToBlinkReferrerPolicy( static_cast(params.referrer_policy)), params.url, blink::WebString::FromUTF8(params.referrer.spec())); if (!referrer.IsEmpty()) { request.SetHTTPReferrer( referrer, NetReferrerPolicyToBlinkReferrerPolicy( static_cast(params.referrer_policy))); } } CefRequest::HeaderMap headerMap; if (!params.headers.empty()) { for (net::HttpUtil::HeadersIterator i(params.headers.begin(), params.headers.end(), "\n"); 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.get()) { const base::string16& method = request.HttpMethod().Utf16(); if (method == base::ASCIIToUTF16("GET") || method == base::ASCIIToUTF16("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(kApplicationFormURLEncoded)); } blink::WebHTTPBody body; body.Initialize(); for (const auto& element : params.upload_data->elements()) { if (element->type() == net::UploadElement::TYPE_BYTES) { blink::WebData data; data.Assign(element->bytes(), element->bytes_length()); body.AppendData(data); } else if (element->type() == net::UploadElement::TYPE_FILE) { body.AppendFile( FilePathStringToWebString(element->file_path().value())); } else { NOTREACHED(); } } request.SetHTTPBody(body); } if (params.site_for_cookies.is_valid()) 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.SetCachePolicy(GetWebCachePolicy(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(CefNavigateParams& 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.url = referrer_url_; params.referrer.policy = NetReferrerPolicyToBlinkReferrerPolicy(referrer_policy_); if (!headermap_.empty()) params.headers = HttpHeaderUtils::GenerateHeaders(headermap_); if (postdata_) { CefPostDataImpl* impl = static_cast(postdata_.get()); params.upload_data = new net::UploadData(); impl->Get(*params.upload_data.get()); } params.site_for_cookies = site_for_cookies_; params.load_flags = flags_; } void CefRequestImpl::Get(net::URLFetcher& fetcher, int64& upload_data_size) const { base::AutoLock lock_scope(lock_); if (!referrer_url_.is_empty()) { fetcher.SetReferrer(GetURLRequestReferrer(referrer_url_)); fetcher.SetReferrerPolicy( static_cast(referrer_policy_)); } CefRequest::HeaderMap headerMap = headermap_; std::string content_type; // Extract the Content-Type header value. { HeaderMap::iterator it = headerMap.begin(); for (; it != headerMap.end(); ++it) { if (base::LowerCaseEqualsASCII(it->first.ToString(), kContentTypeLowerCase)) { content_type = it->second; headerMap.erase(it); break; } } } fetcher.SetExtraRequestHeaders(HttpHeaderUtils::GenerateHeaders(headerMap)); if (postdata_.get()) { CefPostData::ElementVector elements; postdata_->GetElements(elements); if (elements.size() == 1) { // Default to URL encoding if not specified. if (content_type.empty()) content_type = kApplicationFormURLEncoded; CefPostDataElementImpl* impl = static_cast(elements[0].get()); switch (elements[0]->GetType()) { case PDE_TYPE_BYTES: { const size_t size = impl->GetBytesCount(); if (flags_ & UR_FLAG_REPORT_UPLOAD_PROGRESS) { // Return the upload data size. upload_data_size = size; } fetcher.SetUploadData( content_type, std::string(static_cast(impl->GetBytes()), size)); break; } case PDE_TYPE_FILE: fetcher.SetUploadFilePath( content_type, base::FilePath(impl->GetFile()), 0, std::numeric_limits::max(), GetFileTaskRunner()); break; case PDE_TYPE_EMPTY: break; } } else if (elements.size() > 1) { NOTIMPLEMENTED() << " multi-part form data is not supported"; } } if (!site_for_cookies_.is_empty()) fetcher.SetInitiator(url::Origin(site_for_cookies_)); 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); } if (flags & UR_FLAG_NO_RETRY_ON_5XX) fetcher.SetAutomaticallyRetryOn5xx(false); 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_ALLOW_STORED_CREDENTIALS)) { net_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA | net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; } fetcher.SetLoadFlags(net_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(postdata_.get())->SetReadOnly(read_only); } void CefRequestImpl::SetTrackChanges(bool track_changes) { base::AutoLock lock_scope(lock_); if (track_changes_ == track_changes) return; track_changes_ = track_changes; changes_ = kChangedNone; if (postdata_.get()) { static_cast(postdata_.get()) ->SetTrackChanges(track_changes); } } uint8_t CefRequestImpl::GetChanges() const { base::AutoLock lock_scope(lock_); uint8_t changes = changes_; if (postdata_.get() && static_cast(postdata_.get())->HasChanges()) { changes |= kChangedPostData; } return changes; } // From content/child/web_url_loader_impl.cc // static blink::WebReferrerPolicy CefRequestImpl::NetReferrerPolicyToBlinkReferrerPolicy( cef_referrer_policy_t net_policy) { switch (net_policy) { case REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE: return blink::kWebReferrerPolicyNoReferrerWhenDowngrade; case REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN: return blink:: kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin; case REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN: return blink::kWebReferrerPolicyOriginWhenCrossOrigin; case REFERRER_POLICY_NEVER_CLEAR_REFERRER: return blink::kWebReferrerPolicyAlways; case REFERRER_POLICY_ORIGIN: return blink::kWebReferrerPolicyOrigin; case REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN: return blink::kWebReferrerPolicySameOrigin; case REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE: return blink::kWebReferrerPolicyStrictOrigin; case REFERRER_POLICY_NO_REFERRER: return blink::kWebReferrerPolicyNever; case REFERRER_POLICY_LAST_VALUE: NOTREACHED(); return blink::kWebReferrerPolicyDefault; } NOTREACHED(); return blink::kWebReferrerPolicyDefault; } // static cef_referrer_policy_t CefRequestImpl::BlinkReferrerPolicyToNetReferrerPolicy( blink::WebReferrerPolicy blink_policy) { switch (blink_policy) { case blink::kWebReferrerPolicyNoReferrerWhenDowngrade: return REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE; case blink::kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin: return REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; case blink::kWebReferrerPolicyOriginWhenCrossOrigin: return REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; case blink::kWebReferrerPolicyAlways: return REFERRER_POLICY_NEVER_CLEAR_REFERRER; case blink::kWebReferrerPolicyOrigin: return REFERRER_POLICY_ORIGIN; case blink::kWebReferrerPolicySameOrigin: return REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN; case blink::kWebReferrerPolicyStrictOrigin: return REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE; case blink::kWebReferrerPolicyNever: return REFERRER_POLICY_NO_REFERRER; case blink::kWebReferrerPolicyDefault: return REFERRER_POLICY_DEFAULT; } NOTREACHED(); return REFERRER_POLICY_DEFAULT; } void CefRequestImpl::Changed(uint8_t changes) { lock_.AssertAcquired(); if (track_changes_) 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(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_ = NULL; headermap_.clear(); resource_type_ = RT_SUB_RESOURCE; transition_type_ = TT_EXPLICIT; identifier_ = 0U; flags_ = UR_FLAG_NONE; site_for_cookies_ = GURL(); changes_ = kChangedNone; } // CefPostData ---------------------------------------------------------------- // static CefRefPtr CefPostData::Create() { CefRefPtr 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 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 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 net::UploadData& data) { { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); } CefRefPtr postelem; for (const auto& element : data.elements()) { postelem = CefPostDataElement::Create(); static_cast(postelem.get())->Set(*element); AddElement(postelem); } } void CefPostDataImpl::Set(const net::UploadDataStream& data_stream) { { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); } CefRefPtr postelem; const UploadElementReaders* elements = data_stream.GetElementReaders(); if (elements) { UploadElementReaders::const_iterator it = elements->begin(); for (; it != elements->end(); ++it) { postelem = CefPostDataElement::Create(); static_cast(postelem.get())->Set(**it); if (postelem->GetType() != PDE_TYPE_EMPTY) AddElement(postelem); else if (!has_excluded_elements_) has_excluded_elements_ = true; } } } void CefPostDataImpl::Get(net::UploadData& data) const { base::AutoLock lock_scope(lock_); net::UploadData::ElementsVector data_elements; for (const auto& element : elements_) { std::unique_ptr data_element = base::MakeUnique(); static_cast(element.get()) ->Get(*data_element.get()); data_elements.push_back(std::move(data_element)); } data.swap_elements(&data_elements); } std::unique_ptr CefPostDataImpl::Get() const { base::AutoLock lock_scope(lock_); UploadElementReaders element_readers; for (const auto& element : elements_) { element_readers.push_back( static_cast(element.get())->Get()); } return base::MakeUnique( std::move(element_readers), 0); } void CefPostDataImpl::Set(const blink::WebHTTPBody& data) { { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); } CefRefPtr postelem; blink::WebHTTPBody::Element element; size_t size = data.ElementCount(); for (size_t i = 0; i < size; ++i) { if (data.ElementAt(i, element)) { postelem = CefPostDataElement::Create(); static_cast(postelem.get())->Set(element); AddElement(postelem); } } } void CefPostDataImpl::Get(blink::WebHTTPBody& data) const { base::AutoLock lock_scope(lock_); blink::WebHTTPBody::Element element; ElementVector::const_iterator it = elements_.begin(); for (; it != elements_.end(); ++it) { static_cast(it->get())->Get(element); if (element.type == blink::WebHTTPBody::Element::kTypeData) { data.AppendData(element.data); } else if (element.type == blink::WebHTTPBody::Element::kTypeFile) { data.AppendFile(element.file_path); } else { NOTREACHED(); } } } 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(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(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(it->get())->HasChanges()) return true; } return false; } void CefPostDataImpl::Changed() { lock_.AssertAcquired(); if (track_changes_ && !has_changes_) has_changes_ = true; } // CefPostDataElement --------------------------------------------------------- // static CefRefPtr CefPostDataElement::Create() { CefRefPtr 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 != NULL); if (data == NULL) 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 net::UploadElement& element) { { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); } if (element.type() == net::UploadElement::TYPE_BYTES) { SetToBytes(element.bytes_length(), element.bytes()); } else if (element.type() == net::UploadElement::TYPE_FILE) { SetToFile(element.file_path().value()); } else { NOTREACHED(); } } void CefPostDataElementImpl::Set( const net::UploadElementReader& element_reader) { { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); } const net::UploadBytesElementReader* bytes_reader = element_reader.AsBytesReader(); if (bytes_reader) { SetToBytes(bytes_reader->length(), bytes_reader->bytes()); return; } const net::UploadFileElementReader* file_reader = element_reader.AsFileReader(); if (file_reader) { SetToFile(file_reader->path().value()); return; } // Chunked uploads cannot currently be represented. SetToEmpty(); } void CefPostDataElementImpl::Get(net::UploadElement& element) const { base::AutoLock lock_scope(lock_); if (type_ == PDE_TYPE_BYTES) { element.SetToBytes(static_cast(data_.bytes.bytes), data_.bytes.size); } else if (type_ == PDE_TYPE_FILE) { base::FilePath path = base::FilePath(CefString(&data_.filename)); element.SetToFilePath(path); } else { NOTREACHED(); } } std::unique_ptr CefPostDataElementImpl::Get() const { base::AutoLock lock_scope(lock_); if (type_ == PDE_TYPE_BYTES) { net::UploadElement* element = new net::UploadElement(); element->SetToBytes(static_cast(data_.bytes.bytes), data_.bytes.size); return base::MakeUnique(base::WrapUnique(element)); } else if (type_ == PDE_TYPE_FILE) { net::UploadElement* element = new net::UploadElement(); base::FilePath path = base::FilePath(CefString(&data_.filename)); element->SetToFilePath(path); return base::MakeUnique(base::WrapUnique(element)); } else { NOTREACHED(); return nullptr; } } void CefPostDataElementImpl::Set(const blink::WebHTTPBody::Element& element) { { base::AutoLock lock_scope(lock_); CHECK_READONLY_RETURN_VOID(); } if (element.type == blink::WebHTTPBody::Element::kTypeData) { std::string file_contents; file_contents.reserve(element.data.size()); element.data.ForEachSegment([&file_contents](const char* segment, size_t segment_size, size_t segment_offset) { file_contents.append(segment, segment_size); return true; }); SetToBytes(file_contents.size(), file_contents.data()); } else if (element.type == blink::WebHTTPBody::Element::kTypeFile) { SetToFile(element.file_path.Utf16()); } else { NOTREACHED(); } } void CefPostDataElementImpl::Get(blink::WebHTTPBody::Element& element) const { base::AutoLock lock_scope(lock_); if (type_ == PDE_TYPE_BYTES) { element.type = blink::WebHTTPBody::Element::kTypeData; element.data.Assign(static_cast(data_.bytes.bytes), data_.bytes.size); } else if (type_ == PDE_TYPE_FILE) { element.type = blink::WebHTTPBody::Element::kTypeFile; element.file_path.Assign( blink::WebString::FromUTF16(CefString(&data_.filename))); } 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_)); }