1 // Copyright (c) 2008-2009 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include <algorithm>
6 #include <limits>
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "libcef/common/net/http_header_utils.h"
12 #include "libcef/common/net_service/net_service_util.h"
13 #include "libcef/common/request_impl.h"
14
15 #include "base/command_line.h"
16 #include "base/logging.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "components/navigation_interception/navigation_params.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/common/content_switches.h"
23 #include "net/base/elements_upload_data_stream.h"
24 #include "net/base/load_flags.h"
25 #include "net/base/upload_bytes_element_reader.h"
26 #include "net/base/upload_data_stream.h"
27 #include "net/base/upload_element_reader.h"
28 #include "net/base/upload_file_element_reader.h"
29 #include "net/http/http_request_headers.h"
30 #include "net/http/http_util.h"
31 #include "net/url_request/redirect_info.h"
32 #include "net/url_request/referrer_policy.h"
33 #include "services/network/public/cpp/data_element.h"
34 #include "services/network/public/cpp/network_switches.h"
35 #include "services/network/public/cpp/resource_request.h"
36 #include "services/network/public/cpp/resource_request_body.h"
37 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
38 #include "third_party/blink/public/platform/web_http_body.h"
39 #include "third_party/blink/public/platform/web_security_origin.h"
40 #include "third_party/blink/public/platform/web_string.h"
41 #include "third_party/blink/public/platform/web_url.h"
42 #include "third_party/blink/public/platform/web_url_error.h"
43 #include "third_party/blink/public/platform/web_url_request.h"
44 #include "third_party/blink/public/platform/web_url_request_util.h"
45 #include "third_party/blink/public/web/web_security_policy.h"
46
47 namespace {
48
49 const char kReferrerLowerCase[] = "referer";
50 const char kCacheControlLowerCase[] = "cache-control";
51 const char kCacheControlDirectiveNoCacheLowerCase[] = "no-cache";
52 const char kCacheControlDirectiveNoStoreLowerCase[] = "no-store";
53 const char kCacheControlDirectiveOnlyIfCachedLowerCase[] = "only-if-cached";
54
55 // Mask of values that configure the cache policy.
56 const int kURCachePolicyMask =
57 (UR_FLAG_SKIP_CACHE | UR_FLAG_ONLY_FROM_CACHE | UR_FLAG_DISABLE_CACHE);
58
59 // Returns the cef_urlrequest_flags_t policy specified by the Cache-Control
60 // request header directives, if any. The directives are case-insensitive and
61 // some have an optional argument. Multiple directives are comma-separated.
62 // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
63 // for details.
GetCacheControlHeaderPolicy(CefRequest::HeaderMap headerMap)64 int GetCacheControlHeaderPolicy(CefRequest::HeaderMap headerMap) {
65 std::string line;
66
67 // Extract the Cache-Control header line.
68 {
69 CefRequest::HeaderMap::const_iterator it = headerMap.begin();
70 for (; it != headerMap.end(); ++it) {
71 if (base::LowerCaseEqualsASCII(it->first.ToString(),
72 kCacheControlLowerCase)) {
73 line = it->second;
74 break;
75 }
76 }
77 }
78
79 int flags = 0;
80
81 if (!line.empty()) {
82 HttpHeaderUtils::MakeASCIILower(&line);
83
84 std::vector<base::StringPiece> pieces = base::SplitStringPiece(
85 line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
86 for (const auto& piece : pieces) {
87 if (base::LowerCaseEqualsASCII(piece,
88 kCacheControlDirectiveNoCacheLowerCase)) {
89 flags |= UR_FLAG_SKIP_CACHE;
90 } else if (base::LowerCaseEqualsASCII(
91 piece, kCacheControlDirectiveOnlyIfCachedLowerCase)) {
92 flags |= UR_FLAG_ONLY_FROM_CACHE;
93 } else if (base::LowerCaseEqualsASCII(
94 piece, kCacheControlDirectiveNoStoreLowerCase)) {
95 flags |= UR_FLAG_DISABLE_CACHE;
96 }
97 }
98 }
99
100 return flags;
101 }
102
103 // Convert cef_urlrequest_flags_t to blink::WebCachePolicy.
GetFetchCacheMode(int ur_flags)104 blink::mojom::FetchCacheMode GetFetchCacheMode(int ur_flags) {
105 const bool skip_cache{!!(ur_flags & UR_FLAG_SKIP_CACHE)};
106 const bool only_from_cache{!!(ur_flags & UR_FLAG_ONLY_FROM_CACHE)};
107 const bool disable_cache{!!(ur_flags & UR_FLAG_DISABLE_CACHE)};
108 if (only_from_cache && (skip_cache || disable_cache)) {
109 // The request will always fail because only_from_cache and
110 // skip_cache/disable_cache are mutually exclusive.
111 return blink::mojom::FetchCacheMode::kUnspecifiedForceCacheMiss;
112 } else if (disable_cache) {
113 // This additionally implies the skip_cache behavior.
114 return blink::mojom::FetchCacheMode::kNoStore;
115 } else if (skip_cache) {
116 return blink::mojom::FetchCacheMode::kBypassCache;
117 } else if (only_from_cache) {
118 return blink::mojom::FetchCacheMode::kOnlyIfCached;
119 }
120 return blink::mojom::FetchCacheMode::kDefault;
121 }
122
123 // Read |headers| into |map|.
GetHeaderMap(const net::HttpRequestHeaders & headers,CefRequest::HeaderMap & map)124 void GetHeaderMap(const net::HttpRequestHeaders& headers,
125 CefRequest::HeaderMap& map) {
126 map.clear();
127
128 if (headers.IsEmpty())
129 return;
130
131 net::HttpRequestHeaders::Iterator it(headers);
132 while (it.GetNext()) {
133 const std::string& name = it.name();
134
135 // Do not include Referer in the header map.
136 if (!base::LowerCaseEqualsASCII(name, kReferrerLowerCase))
137 map.insert(std::make_pair(name, it.value()));
138 };
139 }
140
141 // Read |source| into |map|.
GetHeaderMap(const CefRequest::HeaderMap & source,CefRequest::HeaderMap & map)142 void GetHeaderMap(const CefRequest::HeaderMap& source,
143 CefRequest::HeaderMap& map) {
144 map.clear();
145
146 CefRequest::HeaderMap::const_iterator it = source.begin();
147 for (; it != source.end(); ++it) {
148 const CefString& name = it->first;
149
150 // Do not include Referer in the header map.
151 if (!base::LowerCaseEqualsASCII(name.ToString(), kReferrerLowerCase))
152 map.insert(std::make_pair(name, it->second));
153 }
154 }
155
156 } // namespace
157
158 #define CHECK_READONLY_RETURN(val) \
159 if (read_only_) { \
160 NOTREACHED() << "object is read only"; \
161 return val; \
162 }
163
164 #define CHECK_READONLY_RETURN_VOID() \
165 if (read_only_) { \
166 NOTREACHED() << "object is read only"; \
167 return; \
168 }
169
170 #define SETBOOLFLAG(obj, flags, method, FLAG) \
171 obj.method((flags & (FLAG)) == (FLAG))
172
173 // CefRequest -----------------------------------------------------------------
174
175 // static
Create()176 CefRefPtr<CefRequest> CefRequest::Create() {
177 CefRefPtr<CefRequest> request(new CefRequestImpl());
178 return request;
179 }
180
181 // CefRequestImpl -------------------------------------------------------------
182
CefRequestImpl()183 CefRequestImpl::CefRequestImpl() {
184 // Verify that our enum matches Chromium's values.
185 static_assert(static_cast<int>(REFERRER_POLICY_LAST_VALUE) ==
186 static_cast<int>(net::ReferrerPolicy::MAX),
187 "enum mismatch");
188
189 base::AutoLock lock_scope(lock_);
190 Reset();
191 }
192
IsReadOnly()193 bool CefRequestImpl::IsReadOnly() {
194 base::AutoLock lock_scope(lock_);
195 return read_only_;
196 }
197
GetURL()198 CefString CefRequestImpl::GetURL() {
199 base::AutoLock lock_scope(lock_);
200 return url_.spec();
201 }
202
SetURL(const CefString & url)203 void CefRequestImpl::SetURL(const CefString& url) {
204 base::AutoLock lock_scope(lock_);
205 CHECK_READONLY_RETURN_VOID();
206 const GURL& new_url = GURL(url.ToString());
207 if (url_ != new_url) {
208 Changed(kChangedUrl);
209 url_ = new_url;
210 }
211 }
212
GetMethod()213 CefString CefRequestImpl::GetMethod() {
214 base::AutoLock lock_scope(lock_);
215 return method_;
216 }
217
SetMethod(const CefString & method)218 void CefRequestImpl::SetMethod(const CefString& method) {
219 base::AutoLock lock_scope(lock_);
220 CHECK_READONLY_RETURN_VOID();
221 const std::string& new_method = method;
222 if (method_ != new_method) {
223 Changed(kChangedMethod);
224 method_ = new_method;
225 }
226 }
227
SetReferrer(const CefString & referrer_url,ReferrerPolicy policy)228 void CefRequestImpl::SetReferrer(const CefString& referrer_url,
229 ReferrerPolicy policy) {
230 base::AutoLock lock_scope(lock_);
231 CHECK_READONLY_RETURN_VOID();
232
233 const auto& sanitized_referrer = content::Referrer::SanitizeForRequest(
234 url_, content::Referrer(GURL(referrer_url.ToString()),
235 NetReferrerPolicyToBlinkReferrerPolicy(policy)));
236 const auto sanitized_policy =
237 BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy);
238
239 if (referrer_url_ != sanitized_referrer.url ||
240 referrer_policy_ != sanitized_policy) {
241 Changed(kChangedReferrer);
242 referrer_url_ = sanitized_referrer.url;
243 referrer_policy_ = sanitized_policy;
244 }
245 }
246
GetReferrerURL()247 CefString CefRequestImpl::GetReferrerURL() {
248 base::AutoLock lock_scope(lock_);
249 return referrer_url_.spec();
250 }
251
GetReferrerPolicy()252 CefRequestImpl::ReferrerPolicy CefRequestImpl::GetReferrerPolicy() {
253 base::AutoLock lock_scope(lock_);
254 return referrer_policy_;
255 }
256
GetPostData()257 CefRefPtr<CefPostData> CefRequestImpl::GetPostData() {
258 base::AutoLock lock_scope(lock_);
259 return postdata_;
260 }
261
SetPostData(CefRefPtr<CefPostData> postData)262 void CefRequestImpl::SetPostData(CefRefPtr<CefPostData> postData) {
263 base::AutoLock lock_scope(lock_);
264 CHECK_READONLY_RETURN_VOID();
265 Changed(kChangedPostData);
266 postdata_ = postData;
267 }
268
GetHeaderMap(HeaderMap & headerMap)269 void CefRequestImpl::GetHeaderMap(HeaderMap& headerMap) {
270 base::AutoLock lock_scope(lock_);
271 headerMap = headermap_;
272 }
273
SetHeaderMap(const HeaderMap & headerMap)274 void CefRequestImpl::SetHeaderMap(const HeaderMap& headerMap) {
275 base::AutoLock lock_scope(lock_);
276 CHECK_READONLY_RETURN_VOID();
277 Changed(kChangedHeaderMap);
278 ::GetHeaderMap(headerMap, headermap_);
279 }
280
GetHeaderByName(const CefString & name)281 CefString CefRequestImpl::GetHeaderByName(const CefString& name) {
282 base::AutoLock lock_scope(lock_);
283
284 std::string nameLower = name;
285 HttpHeaderUtils::MakeASCIILower(&nameLower);
286
287 auto it = HttpHeaderUtils::FindHeaderInMap(nameLower, headermap_);
288 if (it != headermap_.end())
289 return it->second;
290
291 return CefString();
292 }
293
SetHeaderByName(const CefString & name,const CefString & value,bool overwrite)294 void CefRequestImpl::SetHeaderByName(const CefString& name,
295 const CefString& value,
296 bool overwrite) {
297 base::AutoLock lock_scope(lock_);
298 CHECK_READONLY_RETURN_VOID();
299
300 std::string nameLower = name;
301 HttpHeaderUtils::MakeASCIILower(&nameLower);
302
303 // Do not include Referer in the header map.
304 if (nameLower == kReferrerLowerCase)
305 return;
306
307 Changed(kChangedHeaderMap);
308
309 // There may be multiple values, so remove any first.
310 for (auto it = headermap_.begin(); it != headermap_.end();) {
311 if (base::EqualsCaseInsensitiveASCII(it->first.ToString(), nameLower)) {
312 if (!overwrite)
313 return;
314 it = headermap_.erase(it);
315 } else {
316 ++it;
317 }
318 }
319
320 headermap_.insert(std::make_pair(name, value));
321 }
322
Set(const CefString & url,const CefString & method,CefRefPtr<CefPostData> postData,const HeaderMap & headerMap)323 void CefRequestImpl::Set(const CefString& url,
324 const CefString& method,
325 CefRefPtr<CefPostData> postData,
326 const HeaderMap& headerMap) {
327 base::AutoLock lock_scope(lock_);
328 CHECK_READONLY_RETURN_VOID();
329 const GURL& new_url = GURL(url.ToString());
330 if (url_ != new_url) {
331 Changed(kChangedUrl);
332 url_ = new_url;
333 }
334 const std::string& new_method = method;
335 if (method_ != new_method) {
336 Changed(kChangedMethod);
337 method_ = new_method;
338 }
339 if (postdata_ != postData) {
340 Changed(kChangedPostData);
341 postdata_ = postData;
342 }
343 Changed(kChangedHeaderMap);
344 ::GetHeaderMap(headerMap, headermap_);
345 }
346
GetFlags()347 int CefRequestImpl::GetFlags() {
348 base::AutoLock lock_scope(lock_);
349 return flags_;
350 }
351
SetFlags(int flags)352 void CefRequestImpl::SetFlags(int flags) {
353 base::AutoLock lock_scope(lock_);
354 CHECK_READONLY_RETURN_VOID();
355 if (flags_ != flags) {
356 Changed(kChangedFlags);
357 flags_ = flags;
358 }
359 }
360
GetFirstPartyForCookies()361 CefString CefRequestImpl::GetFirstPartyForCookies() {
362 base::AutoLock lock_scope(lock_);
363 return site_for_cookies_.RepresentativeUrl().spec();
364 }
365
SetFirstPartyForCookies(const CefString & url)366 void CefRequestImpl::SetFirstPartyForCookies(const CefString& url) {
367 base::AutoLock lock_scope(lock_);
368 CHECK_READONLY_RETURN_VOID();
369 auto new_site = net::SiteForCookies::FromUrl(GURL(url.ToString()));
370 if (!new_site.IsEquivalent(site_for_cookies_)) {
371 Changed(kChangedSiteForCookies);
372 site_for_cookies_ = new_site;
373 }
374 }
375
GetResourceType()376 CefRequestImpl::ResourceType CefRequestImpl::GetResourceType() {
377 base::AutoLock lock_scope(lock_);
378 return resource_type_;
379 }
380
GetTransitionType()381 CefRequestImpl::TransitionType CefRequestImpl::GetTransitionType() {
382 base::AutoLock lock_scope(lock_);
383 return transition_type_;
384 }
385
GetIdentifier()386 uint64 CefRequestImpl::GetIdentifier() {
387 base::AutoLock lock_scope(lock_);
388 return identifier_;
389 }
390
Set(const network::ResourceRequest * request,uint64 identifier)391 void CefRequestImpl::Set(const network::ResourceRequest* request,
392 uint64 identifier) {
393 base::AutoLock lock_scope(lock_);
394 CHECK_READONLY_RETURN_VOID();
395
396 Reset();
397
398 url_ = request->url;
399 method_ = request->method;
400 identifier_ = identifier;
401
402 if (request->referrer.is_valid()) {
403 const auto& sanitized_referrer = content::Referrer::SanitizeForRequest(
404 request->url,
405 content::Referrer(
406 request->referrer,
407 NetReferrerPolicyToBlinkReferrerPolicy(
408 static_cast<cef_referrer_policy_t>(request->referrer_policy))));
409 referrer_url_ = sanitized_referrer.url;
410 referrer_policy_ =
411 BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy);
412 }
413
414 // Transfer request headers.
415 ::GetHeaderMap(request->headers, headermap_);
416
417 // Transfer post data, if any.
418 if (request->request_body) {
419 postdata_ = CefPostData::Create();
420 static_cast<CefPostDataImpl*>(postdata_.get())->Set(*request->request_body);
421 }
422
423 site_for_cookies_ = request->site_for_cookies;
424
425 resource_type_ = static_cast<cef_resource_type_t>(request->resource_type);
426 transition_type_ =
427 static_cast<cef_transition_type_t>(request->transition_type);
428 }
429
Get(network::ResourceRequest * request,bool changed_only) const430 void CefRequestImpl::Get(network::ResourceRequest* request,
431 bool changed_only) const {
432 base::AutoLock lock_scope(lock_);
433
434 if (ShouldSet(kChangedUrl, changed_only))
435 request->url = url_;
436
437 if (ShouldSet(kChangedMethod, changed_only))
438 request->method = method_;
439
440 if (ShouldSet(kChangedReferrer, changed_only)) {
441 request->referrer = referrer_url_;
442 request->referrer_policy =
443 static_cast<net::ReferrerPolicy>(referrer_policy_);
444 }
445
446 if (ShouldSet(kChangedHeaderMap, changed_only)) {
447 net::HttpRequestHeaders headers;
448 headers.AddHeadersFromString(HttpHeaderUtils::GenerateHeaders(headermap_));
449 request->headers.Swap(&headers);
450 }
451
452 if (ShouldSet(kChangedPostData, changed_only)) {
453 if (postdata_.get()) {
454 request->request_body =
455 static_cast<CefPostDataImpl*>(postdata_.get())->GetBody();
456 } else if (request->request_body) {
457 request->request_body = nullptr;
458 }
459 }
460
461 if (!site_for_cookies_.IsNull() &&
462 ShouldSet(kChangedSiteForCookies, changed_only)) {
463 request->site_for_cookies = site_for_cookies_;
464 }
465
466 if (ShouldSet(kChangedFlags, changed_only)) {
467 int flags = flags_;
468 if (!(flags & kURCachePolicyMask)) {
469 // Only consider the Cache-Control directives when a cache policy is not
470 // explicitly set on the request.
471 flags |= GetCacheControlHeaderPolicy(headermap_);
472 }
473
474 int net_flags = 0;
475
476 if (flags & UR_FLAG_SKIP_CACHE) {
477 net_flags |= net::LOAD_BYPASS_CACHE;
478 }
479 if (flags & UR_FLAG_ONLY_FROM_CACHE) {
480 net_flags |= net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION;
481 }
482 if (flags & UR_FLAG_DISABLE_CACHE) {
483 net_flags |= net::LOAD_DISABLE_CACHE;
484 }
485
486 if (!(flags & UR_FLAG_ALLOW_STORED_CREDENTIALS)) {
487 // This will disable all credentials including cookies, auth tokens, etc.
488 request->credentials_mode = network::mojom::CredentialsMode::kOmit;
489 }
490
491 request->load_flags = net_flags;
492 }
493 }
494
Set(const net::RedirectInfo & redirect_info)495 void CefRequestImpl::Set(const net::RedirectInfo& redirect_info) {
496 base::AutoLock lock_scope(lock_);
497 CHECK_READONLY_RETURN_VOID();
498
499 url_ = redirect_info.new_url;
500 method_ = redirect_info.new_method;
501 site_for_cookies_ = redirect_info.new_site_for_cookies;
502
503 const auto& sanitized_referrer = content::Referrer::SanitizeForRequest(
504 redirect_info.new_url,
505 content::Referrer(GURL(redirect_info.new_referrer),
506 NetReferrerPolicyToBlinkReferrerPolicy(
507 static_cast<cef_referrer_policy_t>(
508 redirect_info.new_referrer_policy))));
509 referrer_url_ = sanitized_referrer.url;
510 referrer_policy_ =
511 BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy);
512 }
513
Set(const net::HttpRequestHeaders & headers)514 void CefRequestImpl::Set(const net::HttpRequestHeaders& headers) {
515 base::AutoLock lock_scope(lock_);
516 CHECK_READONLY_RETURN_VOID();
517
518 ::GetHeaderMap(headers, headermap_);
519 }
520
Set(const navigation_interception::NavigationParams & params,bool is_main_frame)521 void CefRequestImpl::Set(
522 const navigation_interception::NavigationParams& params,
523 bool is_main_frame) {
524 base::AutoLock lock_scope(lock_);
525 CHECK_READONLY_RETURN_VOID();
526
527 Reset();
528
529 url_ = params.url();
530 method_ = params.is_post() ? "POST" : "GET";
531
532 const auto& sanitized_referrer =
533 content::Referrer::SanitizeForRequest(params.url(), params.referrer());
534 referrer_url_ = sanitized_referrer.url;
535 referrer_policy_ =
536 BlinkReferrerPolicyToNetReferrerPolicy(sanitized_referrer.policy);
537
538 resource_type_ = is_main_frame ? RT_MAIN_FRAME : RT_SUB_FRAME;
539 transition_type_ =
540 static_cast<cef_transition_type_t>(params.transition_type());
541 }
542
543 // static
Get(const cef::mojom::RequestParamsPtr & params,blink::WebURLRequest & request)544 void CefRequestImpl::Get(const cef::mojom::RequestParamsPtr& params,
545 blink::WebURLRequest& request) {
546 request.SetUrl(params->url);
547 request.SetRequestorOrigin(blink::WebSecurityOrigin::Create(params->url));
548 if (!params->method.empty())
549 request.SetHttpMethod(blink::WebString::FromASCII(params->method));
550
551 if (params->referrer && params->referrer->url.is_valid()) {
552 const blink::WebString& referrer =
553 blink::WebSecurityPolicy::GenerateReferrerHeader(
554 params->referrer->policy, params->url,
555 blink::WebString::FromUTF8(params->referrer->url.spec()));
556 if (!referrer.IsEmpty()) {
557 request.SetReferrerString(referrer);
558 request.SetReferrerPolicy(params->referrer->policy);
559 }
560 }
561
562 CefRequest::HeaderMap headerMap;
563 if (!params->headers.empty()) {
564 for (net::HttpUtil::HeadersIterator i(params->headers.begin(),
565 params->headers.end(), "\n\r");
566 i.GetNext();) {
567 request.AddHttpHeaderField(blink::WebString::FromUTF8(i.name()),
568 blink::WebString::FromUTF8(i.values()));
569 headerMap.insert(std::make_pair(i.name(), i.values()));
570 }
571 }
572
573 if (params->upload_data) {
574 const std::u16string& method = request.HttpMethod().Utf16();
575 if (method == u"GET" || method == u"HEAD") {
576 request.SetHttpMethod(blink::WebString::FromASCII("POST"));
577 }
578
579 // The comparison performed by httpHeaderField() is case insensitive.
580 if (request
581 .HttpHeaderField(blink::WebString::FromASCII(
582 net::HttpRequestHeaders::kContentType))
583 .length() == 0) {
584 request.SetHttpHeaderField(
585 blink::WebString::FromASCII(net::HttpRequestHeaders::kContentType),
586 blink::WebString::FromASCII(
587 net_service::kContentTypeApplicationFormURLEncoded));
588 }
589
590 request.SetHttpBody(
591 blink::GetWebHTTPBodyForRequestBody(*params->upload_data));
592 }
593
594 if (!params->site_for_cookies.IsNull())
595 request.SetSiteForCookies(params->site_for_cookies);
596
597 int flags = params->load_flags;
598 if (!(flags & kURCachePolicyMask)) {
599 // Only consider the Cache-Control directives when a cache policy is not
600 // explicitly set on the request.
601 flags |= GetCacheControlHeaderPolicy(headerMap);
602 }
603 request.SetCacheMode(GetFetchCacheMode(flags));
604
605 SETBOOLFLAG(request, params->load_flags, SetAllowStoredCredentials,
606 UR_FLAG_ALLOW_STORED_CREDENTIALS);
607 SETBOOLFLAG(request, params->load_flags, SetReportUploadProgress,
608 UR_FLAG_REPORT_UPLOAD_PROGRESS);
609 }
610
Get(cef::mojom::RequestParamsPtr & params) const611 void CefRequestImpl::Get(cef::mojom::RequestParamsPtr& params) const {
612 base::AutoLock lock_scope(lock_);
613
614 params->url = url_;
615 params->method = method_;
616
617 // Referrer policy will be applied later in the request pipeline.
618 params->referrer = blink::mojom::Referrer::New(
619 referrer_url_, NetReferrerPolicyToBlinkReferrerPolicy(referrer_policy_));
620
621 if (!headermap_.empty())
622 params->headers = HttpHeaderUtils::GenerateHeaders(headermap_);
623
624 if (postdata_) {
625 CefPostDataImpl* impl = static_cast<CefPostDataImpl*>(postdata_.get());
626 params->upload_data = impl->GetBody();
627 }
628
629 params->site_for_cookies = site_for_cookies_;
630 params->load_flags = flags_;
631 }
632
SetReadOnly(bool read_only)633 void CefRequestImpl::SetReadOnly(bool read_only) {
634 base::AutoLock lock_scope(lock_);
635 if (read_only_ == read_only)
636 return;
637
638 read_only_ = read_only;
639
640 if (postdata_.get())
641 static_cast<CefPostDataImpl*>(postdata_.get())->SetReadOnly(read_only);
642 }
643
SetTrackChanges(bool track_changes,bool backup_on_change)644 void CefRequestImpl::SetTrackChanges(bool track_changes,
645 bool backup_on_change) {
646 base::AutoLock lock_scope(lock_);
647 if (track_changes_ == track_changes)
648 return;
649
650 if (!track_changes && backup_on_change_)
651 backup_.reset();
652
653 track_changes_ = track_changes;
654 backup_on_change_ = track_changes ? backup_on_change : false;
655 changes_ = kChangedNone;
656
657 if (postdata_.get()) {
658 static_cast<CefPostDataImpl*>(postdata_.get())
659 ->SetTrackChanges(track_changes);
660 }
661 }
662
RevertChanges()663 void CefRequestImpl::RevertChanges() {
664 base::AutoLock lock_scope(lock_);
665 DCHECK(!read_only_);
666 DCHECK(track_changes_);
667 DCHECK(backup_on_change_);
668 if (!backup_)
669 return;
670
671 // Restore the original values if a backup exists.
672 if (backup_->backups_ & kChangedUrl)
673 url_ = backup_->url_;
674 if (backup_->backups_ & kChangedMethod)
675 method_ = backup_->method_;
676 if (backup_->backups_ & kChangedReferrer) {
677 referrer_url_ = backup_->referrer_url_;
678 referrer_policy_ = backup_->referrer_policy_;
679 }
680 if (backup_->backups_ & kChangedPostData)
681 postdata_ = backup_->postdata_;
682 if (backup_->backups_ & kChangedHeaderMap) {
683 DCHECK(backup_->headermap_);
684 headermap_.swap(*backup_->headermap_);
685 }
686 if (backup_->backups_ & kChangedFlags)
687 flags_ = backup_->flags_;
688 if (backup_->backups_ & kChangedSiteForCookies)
689 site_for_cookies_ = backup_->site_for_cookies_;
690
691 backup_.reset();
692 }
693
DiscardChanges()694 void CefRequestImpl::DiscardChanges() {
695 base::AutoLock lock_scope(lock_);
696 DCHECK(track_changes_);
697 DCHECK(backup_on_change_);
698 backup_.reset();
699 }
700
GetChanges() const701 uint8_t CefRequestImpl::GetChanges() const {
702 base::AutoLock lock_scope(lock_);
703
704 uint8_t changes = changes_;
705 if (postdata_.get() &&
706 static_cast<CefPostDataImpl*>(postdata_.get())->HasChanges()) {
707 changes |= kChangedPostData;
708 }
709 return changes;
710 }
711
712 // From content/child/web_url_loader_impl.cc
713 // static
714 network::mojom::ReferrerPolicy
NetReferrerPolicyToBlinkReferrerPolicy(cef_referrer_policy_t net_policy)715 CefRequestImpl::NetReferrerPolicyToBlinkReferrerPolicy(
716 cef_referrer_policy_t net_policy) {
717 switch (net_policy) {
718 case REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
719 return network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade;
720 case REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN:
721 return network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin;
722 case REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN:
723 return network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin;
724 case REFERRER_POLICY_NEVER_CLEAR_REFERRER:
725 return network::mojom::ReferrerPolicy::kAlways;
726 case REFERRER_POLICY_ORIGIN:
727 return network::mojom::ReferrerPolicy::kOrigin;
728 case REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN:
729 return network::mojom::ReferrerPolicy::kSameOrigin;
730 case REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
731 return network::mojom::ReferrerPolicy::kStrictOrigin;
732 case REFERRER_POLICY_NO_REFERRER:
733 return network::mojom::ReferrerPolicy::kNever;
734 }
735 NOTREACHED();
736 return network::mojom::ReferrerPolicy::kDefault;
737 }
738
739 // static
BlinkReferrerPolicyToNetReferrerPolicy(network::mojom::ReferrerPolicy blink_policy)740 cef_referrer_policy_t CefRequestImpl::BlinkReferrerPolicyToNetReferrerPolicy(
741 network::mojom::ReferrerPolicy blink_policy) {
742 switch (blink_policy) {
743 case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade:
744 return REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
745 case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin:
746 return REFERRER_POLICY_REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
747 case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin:
748 return REFERRER_POLICY_ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN;
749 case network::mojom::ReferrerPolicy::kAlways:
750 return REFERRER_POLICY_NEVER_CLEAR_REFERRER;
751 case network::mojom::ReferrerPolicy::kOrigin:
752 return REFERRER_POLICY_ORIGIN;
753 case network::mojom::ReferrerPolicy::kSameOrigin:
754 return REFERRER_POLICY_CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN;
755 case network::mojom::ReferrerPolicy::kStrictOrigin:
756 return REFERRER_POLICY_ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
757 case network::mojom::ReferrerPolicy::kNever:
758 return REFERRER_POLICY_NO_REFERRER;
759 case network::mojom::ReferrerPolicy::kDefault:
760 return REFERRER_POLICY_DEFAULT;
761 }
762 NOTREACHED();
763 return REFERRER_POLICY_DEFAULT;
764 }
765
Changed(uint8_t changes)766 void CefRequestImpl::Changed(uint8_t changes) {
767 lock_.AssertAcquired();
768 if (!track_changes_)
769 return;
770
771 if (backup_on_change_) {
772 if (!backup_)
773 backup_.reset(new Backup());
774
775 // Set the backup values if not already set.
776 if ((changes & kChangedUrl) && !(backup_->backups_ & kChangedUrl)) {
777 backup_->url_ = url_;
778 backup_->backups_ |= kChangedUrl;
779 }
780 if ((changes & kChangedMethod) && !(backup_->backups_ & kChangedMethod)) {
781 backup_->method_ = method_;
782 backup_->backups_ |= kChangedMethod;
783 }
784 if ((changes & kChangedReferrer) &&
785 !(backup_->backups_ & kChangedReferrer)) {
786 backup_->referrer_url_ = referrer_url_;
787 backup_->referrer_policy_ = referrer_policy_;
788 backup_->backups_ |= kChangedReferrer;
789 }
790 if ((changes & kChangedPostData) &&
791 !(backup_->backups_ & kChangedPostData)) {
792 backup_->postdata_ = postdata_;
793 backup_->backups_ |= kChangedPostData;
794 }
795 if ((changes & kChangedHeaderMap) &&
796 !(backup_->backups_ & kChangedHeaderMap)) {
797 backup_->headermap_.reset(new HeaderMap());
798 if (!headermap_.empty()) {
799 backup_->headermap_->insert(headermap_.begin(), headermap_.end());
800 }
801 backup_->backups_ |= kChangedHeaderMap;
802 }
803 if ((changes & kChangedFlags) && !(backup_->backups_ & kChangedFlags)) {
804 backup_->flags_ = flags_;
805 backup_->backups_ |= kChangedFlags;
806 }
807 if ((changes & kChangedSiteForCookies) &&
808 !(backup_->backups_ & kChangedSiteForCookies)) {
809 backup_->site_for_cookies_ = site_for_cookies_;
810 backup_->backups_ |= kChangedSiteForCookies;
811 }
812 }
813
814 changes_ |= changes;
815 }
816
ShouldSet(uint8_t changes,bool changed_only) const817 bool CefRequestImpl::ShouldSet(uint8_t changes, bool changed_only) const {
818 lock_.AssertAcquired();
819
820 // Always change if changes are not being tracked.
821 if (!track_changes_)
822 return true;
823
824 // Always change if changed-only was not requested.
825 if (!changed_only)
826 return true;
827
828 // Change if the |changes| bit flag has been set.
829 if ((changes_ & changes) == changes)
830 return true;
831
832 if ((changes & kChangedPostData) == kChangedPostData) {
833 // Change if the post data object was modified directly.
834 if (postdata_.get() &&
835 static_cast<CefPostDataImpl*>(postdata_.get())->HasChanges()) {
836 return true;
837 }
838 }
839
840 return false;
841 }
842
Reset()843 void CefRequestImpl::Reset() {
844 lock_.AssertAcquired();
845 DCHECK(!read_only_);
846
847 url_ = GURL();
848 method_ = "GET";
849 referrer_url_ = GURL();
850 referrer_policy_ = REFERRER_POLICY_DEFAULT;
851 postdata_ = nullptr;
852 headermap_.clear();
853 resource_type_ = RT_SUB_RESOURCE;
854 transition_type_ = TT_EXPLICIT;
855 identifier_ = 0U;
856 flags_ = UR_FLAG_NONE;
857 site_for_cookies_ = net::SiteForCookies();
858
859 changes_ = kChangedNone;
860 }
861
862 // CefPostData ----------------------------------------------------------------
863
864 // static
Create()865 CefRefPtr<CefPostData> CefPostData::Create() {
866 CefRefPtr<CefPostData> postdata(new CefPostDataImpl());
867 return postdata;
868 }
869
870 // CefPostDataImpl ------------------------------------------------------------
871
CefPostDataImpl()872 CefPostDataImpl::CefPostDataImpl()
873 : read_only_(false),
874 has_excluded_elements_(false),
875 track_changes_(false),
876 has_changes_(false) {}
877
IsReadOnly()878 bool CefPostDataImpl::IsReadOnly() {
879 base::AutoLock lock_scope(lock_);
880 return read_only_;
881 }
882
HasExcludedElements()883 bool CefPostDataImpl::HasExcludedElements() {
884 base::AutoLock lock_scope(lock_);
885 return has_excluded_elements_;
886 }
887
GetElementCount()888 size_t CefPostDataImpl::GetElementCount() {
889 base::AutoLock lock_scope(lock_);
890 return elements_.size();
891 }
892
GetElements(ElementVector & elements)893 void CefPostDataImpl::GetElements(ElementVector& elements) {
894 base::AutoLock lock_scope(lock_);
895 elements = elements_;
896 }
897
RemoveElement(CefRefPtr<CefPostDataElement> element)898 bool CefPostDataImpl::RemoveElement(CefRefPtr<CefPostDataElement> element) {
899 base::AutoLock lock_scope(lock_);
900 CHECK_READONLY_RETURN(false);
901
902 ElementVector::iterator it = elements_.begin();
903 for (; it != elements_.end(); ++it) {
904 if (it->get() == element.get()) {
905 elements_.erase(it);
906 Changed();
907 return true;
908 }
909 }
910
911 return false;
912 }
913
AddElement(CefRefPtr<CefPostDataElement> element)914 bool CefPostDataImpl::AddElement(CefRefPtr<CefPostDataElement> element) {
915 bool found = false;
916
917 base::AutoLock lock_scope(lock_);
918 CHECK_READONLY_RETURN(false);
919
920 // check that the element isn't already in the list before adding
921 ElementVector::const_iterator it = elements_.begin();
922 for (; it != elements_.end(); ++it) {
923 if (it->get() == element.get()) {
924 found = true;
925 break;
926 }
927 }
928
929 if (!found) {
930 elements_.push_back(element);
931 Changed();
932 }
933
934 return !found;
935 }
936
RemoveElements()937 void CefPostDataImpl::RemoveElements() {
938 base::AutoLock lock_scope(lock_);
939 CHECK_READONLY_RETURN_VOID();
940 elements_.clear();
941 Changed();
942 }
943
Set(const network::ResourceRequestBody & body)944 void CefPostDataImpl::Set(const network::ResourceRequestBody& body) {
945 {
946 base::AutoLock lock_scope(lock_);
947 CHECK_READONLY_RETURN_VOID();
948 }
949
950 CefRefPtr<CefPostDataElement> postelem;
951
952 for (const auto& element : *body.elements()) {
953 postelem = CefPostDataElement::Create();
954 static_cast<CefPostDataElementImpl*>(postelem.get())->Set(element);
955 AddElement(postelem);
956 }
957 }
958
GetBody() const959 scoped_refptr<network::ResourceRequestBody> CefPostDataImpl::GetBody() const {
960 base::AutoLock lock_scope(lock_);
961
962 scoped_refptr<network::ResourceRequestBody> body =
963 new network::ResourceRequestBody();
964 for (const auto& element : elements_) {
965 static_cast<CefPostDataElementImpl*>(element.get())->Get(*body);
966 }
967 return body;
968 }
969
SetReadOnly(bool read_only)970 void CefPostDataImpl::SetReadOnly(bool read_only) {
971 base::AutoLock lock_scope(lock_);
972 if (read_only_ == read_only)
973 return;
974
975 read_only_ = read_only;
976
977 ElementVector::const_iterator it = elements_.begin();
978 for (; it != elements_.end(); ++it) {
979 static_cast<CefPostDataElementImpl*>(it->get())->SetReadOnly(read_only);
980 }
981 }
982
SetTrackChanges(bool track_changes)983 void CefPostDataImpl::SetTrackChanges(bool track_changes) {
984 base::AutoLock lock_scope(lock_);
985 if (track_changes_ == track_changes)
986 return;
987
988 track_changes_ = track_changes;
989 has_changes_ = false;
990
991 ElementVector::const_iterator it = elements_.begin();
992 for (; it != elements_.end(); ++it) {
993 static_cast<CefPostDataElementImpl*>(it->get())->SetTrackChanges(
994 track_changes);
995 }
996 }
997
HasChanges() const998 bool CefPostDataImpl::HasChanges() const {
999 base::AutoLock lock_scope(lock_);
1000 if (has_changes_)
1001 return true;
1002
1003 ElementVector::const_iterator it = elements_.begin();
1004 for (; it != elements_.end(); ++it) {
1005 if (static_cast<CefPostDataElementImpl*>(it->get())->HasChanges())
1006 return true;
1007 }
1008
1009 return false;
1010 }
1011
Changed()1012 void CefPostDataImpl::Changed() {
1013 lock_.AssertAcquired();
1014 if (track_changes_ && !has_changes_)
1015 has_changes_ = true;
1016 }
1017
1018 // CefPostDataElement ---------------------------------------------------------
1019
1020 // static
Create()1021 CefRefPtr<CefPostDataElement> CefPostDataElement::Create() {
1022 CefRefPtr<CefPostDataElement> element(new CefPostDataElementImpl());
1023 return element;
1024 }
1025
1026 // CefPostDataElementImpl -----------------------------------------------------
1027
CefPostDataElementImpl()1028 CefPostDataElementImpl::CefPostDataElementImpl()
1029 : type_(PDE_TYPE_EMPTY),
1030 read_only_(false),
1031 track_changes_(false),
1032 has_changes_(false) {
1033 memset(&data_, 0, sizeof(data_));
1034 }
1035
~CefPostDataElementImpl()1036 CefPostDataElementImpl::~CefPostDataElementImpl() {
1037 Cleanup();
1038 }
1039
IsReadOnly()1040 bool CefPostDataElementImpl::IsReadOnly() {
1041 base::AutoLock lock_scope(lock_);
1042 return read_only_;
1043 }
1044
SetToEmpty()1045 void CefPostDataElementImpl::SetToEmpty() {
1046 base::AutoLock lock_scope(lock_);
1047 CHECK_READONLY_RETURN_VOID();
1048
1049 Cleanup();
1050 Changed();
1051 }
1052
SetToFile(const CefString & fileName)1053 void CefPostDataElementImpl::SetToFile(const CefString& fileName) {
1054 base::AutoLock lock_scope(lock_);
1055 CHECK_READONLY_RETURN_VOID();
1056
1057 // Clear any data currently in the element
1058 Cleanup();
1059
1060 // Assign the new data
1061 type_ = PDE_TYPE_FILE;
1062 cef_string_copy(fileName.c_str(), fileName.length(), &data_.filename);
1063
1064 Changed();
1065 }
1066
SetToBytes(size_t size,const void * bytes)1067 void CefPostDataElementImpl::SetToBytes(size_t size, const void* bytes) {
1068 base::AutoLock lock_scope(lock_);
1069 CHECK_READONLY_RETURN_VOID();
1070
1071 // Clear any data currently in the element
1072 Cleanup();
1073
1074 // Assign the new data
1075 void* data = malloc(size);
1076 DCHECK(data != nullptr);
1077 if (data == nullptr)
1078 return;
1079
1080 memcpy(data, bytes, size);
1081
1082 type_ = PDE_TYPE_BYTES;
1083 data_.bytes.bytes = data;
1084 data_.bytes.size = size;
1085
1086 Changed();
1087 }
1088
GetType()1089 CefPostDataElement::Type CefPostDataElementImpl::GetType() {
1090 base::AutoLock lock_scope(lock_);
1091 return type_;
1092 }
1093
GetFile()1094 CefString CefPostDataElementImpl::GetFile() {
1095 base::AutoLock lock_scope(lock_);
1096 DCHECK(type_ == PDE_TYPE_FILE);
1097 CefString filename;
1098 if (type_ == PDE_TYPE_FILE)
1099 filename.FromString(data_.filename.str, data_.filename.length, false);
1100 return filename;
1101 }
1102
GetBytesCount()1103 size_t CefPostDataElementImpl::GetBytesCount() {
1104 base::AutoLock lock_scope(lock_);
1105 DCHECK(type_ == PDE_TYPE_BYTES);
1106 size_t size = 0;
1107 if (type_ == PDE_TYPE_BYTES)
1108 size = data_.bytes.size;
1109 return size;
1110 }
1111
GetBytes(size_t size,void * bytes)1112 size_t CefPostDataElementImpl::GetBytes(size_t size, void* bytes) {
1113 base::AutoLock lock_scope(lock_);
1114 DCHECK(type_ == PDE_TYPE_BYTES);
1115 size_t rv = 0;
1116 if (type_ == PDE_TYPE_BYTES) {
1117 rv = (size < data_.bytes.size ? size : data_.bytes.size);
1118 memcpy(bytes, data_.bytes.bytes, rv);
1119 }
1120 return rv;
1121 }
1122
Set(const network::DataElement & element)1123 void CefPostDataElementImpl::Set(const network::DataElement& element) {
1124 {
1125 base::AutoLock lock_scope(lock_);
1126 CHECK_READONLY_RETURN_VOID();
1127 }
1128
1129 if (element.type() == network::DataElement::Tag::kBytes) {
1130 const auto& bytes_element = element.As<network::DataElementBytes>();
1131 const auto& bytes = bytes_element.bytes();
1132 SetToBytes(bytes.size(), bytes.data());
1133 } else if (element.type() == network::DataElement::Tag::kFile) {
1134 const auto& file_element = element.As<network::DataElementFile>();
1135 SetToFile(file_element.path().value());
1136 }
1137 }
1138
Get(network::ResourceRequestBody & body) const1139 void CefPostDataElementImpl::Get(network::ResourceRequestBody& body) const {
1140 base::AutoLock lock_scope(lock_);
1141
1142 if (type_ == PDE_TYPE_BYTES) {
1143 body.AppendBytes(static_cast<char*>(data_.bytes.bytes), data_.bytes.size);
1144 } else if (type_ == PDE_TYPE_FILE) {
1145 base::FilePath path = base::FilePath(CefString(&data_.filename));
1146 body.AppendFileRange(path, 0, std::numeric_limits<uint64_t>::max(),
1147 base::Time());
1148 } else {
1149 NOTREACHED();
1150 }
1151 }
1152
SetReadOnly(bool read_only)1153 void CefPostDataElementImpl::SetReadOnly(bool read_only) {
1154 base::AutoLock lock_scope(lock_);
1155 if (read_only_ == read_only)
1156 return;
1157
1158 read_only_ = read_only;
1159 }
1160
SetTrackChanges(bool track_changes)1161 void CefPostDataElementImpl::SetTrackChanges(bool track_changes) {
1162 base::AutoLock lock_scope(lock_);
1163 if (track_changes_ == track_changes)
1164 return;
1165
1166 track_changes_ = track_changes;
1167 has_changes_ = false;
1168 }
1169
HasChanges() const1170 bool CefPostDataElementImpl::HasChanges() const {
1171 base::AutoLock lock_scope(lock_);
1172 return has_changes_;
1173 }
1174
Changed()1175 void CefPostDataElementImpl::Changed() {
1176 lock_.AssertAcquired();
1177 if (track_changes_ && !has_changes_)
1178 has_changes_ = true;
1179 }
1180
Cleanup()1181 void CefPostDataElementImpl::Cleanup() {
1182 if (type_ == PDE_TYPE_EMPTY)
1183 return;
1184
1185 if (type_ == PDE_TYPE_BYTES)
1186 free(data_.bytes.bytes);
1187 else if (type_ == PDE_TYPE_FILE)
1188 cef_string_clear(&data_.filename);
1189 type_ = PDE_TYPE_EMPTY;
1190 memset(&data_, 0, sizeof(data_));
1191 }
1192