|  | // Copyright 2014 The Chromium 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 "content/renderer/loader/web_url_loader_impl.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/auto_reset.h" | 
|  | #include "base/bind.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/feature_list.h" | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/optional.h" | 
|  | #include "base/sequence_checker.h" | 
|  | #include "base/single_thread_task_runner.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/string_util.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "base/time/time.h" | 
|  | #include "build/build_config.h" | 
|  | #include "content/child/child_thread_impl.h" | 
|  | #include "content/common/content_constants_internal.h" | 
|  | #include "content/common/service_worker/service_worker_types.h" | 
|  | #include "content/public/common/content_features.h" | 
|  | #include "content/public/common/navigation_policy.h" | 
|  | #include "content/public/common/origin_util.h" | 
|  | #include "content/public/common/previews_state.h" | 
|  | #include "content/public/common/referrer.h" | 
|  | #include "content/public/renderer/request_peer.h" | 
|  | #include "content/renderer/loader/request_extra_data.h" | 
|  | #include "content/renderer/loader/resource_dispatcher.h" | 
|  | #include "content/renderer/loader/sync_load_response.h" | 
|  | #include "content/renderer/loader/web_url_request_util.h" | 
|  | #include "net/base/data_url.h" | 
|  | #include "net/base/filename_util.h" | 
|  | #include "net/base/host_port_pair.h" | 
|  | #include "net/base/ip_endpoint.h" | 
|  | #include "net/base/load_flags.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "net/cert/cert_status_flags.h" | 
|  | #include "net/cert/ct_sct_to_string.h" | 
|  | #include "net/cert/x509_certificate.h" | 
|  | #include "net/cert/x509_util.h" | 
|  | #include "net/http/http_response_headers.h" | 
|  | #include "net/ssl/ssl_cipher_suite_names.h" | 
|  | #include "net/ssl/ssl_connection_status_flags.h" | 
|  | #include "net/ssl/ssl_info.h" | 
|  | #include "net/url_request/url_request_data_job.h" | 
|  | #include "services/network/loader_util.h" | 
|  | #include "services/network/public/cpp/resource_request.h" | 
|  | #include "services/network/public/mojom/request_context_frame_type.mojom.h" | 
|  | #include "services/network/public/mojom/url_loader.mojom.h" | 
|  | #include "third_party/blink/public/common/features.h" | 
|  | #include "third_party/blink/public/common/mime_util/mime_util.h" | 
|  | #include "third_party/blink/public/platform/file_path_conversion.h" | 
|  | #include "third_party/blink/public/platform/interface_provider.h" | 
|  | #include "third_party/blink/public/platform/platform.h" | 
|  | #include "third_party/blink/public/platform/web_http_load_info.h" | 
|  | #include "third_party/blink/public/platform/web_security_origin.h" | 
|  | #include "third_party/blink/public/platform/web_security_style.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_load_timing.h" | 
|  | #include "third_party/blink/public/platform/web_url_loader_client.h" | 
|  | #include "third_party/blink/public/platform/web_url_request.h" | 
|  | #include "third_party/blink/public/platform/web_url_response.h" | 
|  | #include "third_party/blink/public/web/web_security_policy.h" | 
|  | #include "third_party/boringssl/src/include/openssl/ssl.h" | 
|  |  | 
|  | using base::Time; | 
|  | using base::TimeTicks; | 
|  | using blink::WebData; | 
|  | using blink::WebHTTPBody; | 
|  | using blink::WebHTTPHeaderVisitor; | 
|  | using blink::WebHTTPLoadInfo; | 
|  | using blink::WebSecurityPolicy; | 
|  | using blink::WebString; | 
|  | using blink::WebURL; | 
|  | using blink::WebURLError; | 
|  | using blink::WebURLLoadTiming; | 
|  | using blink::WebURLLoader; | 
|  | using blink::WebURLLoaderClient; | 
|  | using blink::WebURLRequest; | 
|  | using blink::WebURLResponse; | 
|  | using blink::scheduler::WebResourceLoadingTaskRunnerHandle; | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | // Utilities ------------------------------------------------------------------ | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | constexpr char kStylesheetAcceptHeader[] = "text/css,*/*;q=0.1"; | 
|  | constexpr char kImageAcceptHeader[] = "image/webp,image/apng,image/*,*/*;q=0.8"; | 
|  |  | 
|  | using HeadersVector = network::HttpRawRequestResponseInfo::HeadersVector; | 
|  |  | 
|  | // TODO(estark): Figure out a way for the embedder to provide the | 
|  | // security style for a resource. Ideally, the logic for assigning | 
|  | // per-resource security styles should live in the same place as the | 
|  | // logic for assigning per-page security styles (which lives in the | 
|  | // embedder). It would also be nice for the embedder to have the chance | 
|  | // to control the per-resource security style beyond the simple logic | 
|  | // here. (For example, the embedder might want to mark certain resources | 
|  | // differently if they use SHA1 signatures.) https://crbug.com/648326 | 
|  | blink::WebSecurityStyle GetSecurityStyleForResource( | 
|  | const GURL& url, | 
|  | net::CertStatus cert_status) { | 
|  | if (!url.SchemeIsCryptographic()) | 
|  | return blink::kWebSecurityStyleNeutral; | 
|  |  | 
|  | // Minor errors don't lower the security style to | 
|  | // WebSecurityStyleAuthenticationBroken. | 
|  | if (net::IsCertStatusError(cert_status) && | 
|  | !net::IsCertStatusMinorError(cert_status)) { | 
|  | return blink::kWebSecurityStyleInsecure; | 
|  | } | 
|  |  | 
|  | return blink::kWebSecurityStyleSecure; | 
|  | } | 
|  |  | 
|  | // Converts timing data from |load_timing| to the format used by WebKit. | 
|  | void PopulateURLLoadTiming(const net::LoadTimingInfo& load_timing, | 
|  | WebURLLoadTiming* url_timing) { | 
|  | DCHECK(!load_timing.request_start.is_null()); | 
|  |  | 
|  | url_timing->Initialize(); | 
|  | url_timing->SetRequestTime(load_timing.request_start); | 
|  | url_timing->SetProxyStart(load_timing.proxy_resolve_start); | 
|  | url_timing->SetProxyEnd(load_timing.proxy_resolve_end); | 
|  | url_timing->SetDNSStart(load_timing.connect_timing.dns_start); | 
|  | url_timing->SetDNSEnd(load_timing.connect_timing.dns_end); | 
|  | url_timing->SetConnectStart(load_timing.connect_timing.connect_start); | 
|  | url_timing->SetConnectEnd(load_timing.connect_timing.connect_end); | 
|  | url_timing->SetSSLStart(load_timing.connect_timing.ssl_start); | 
|  | url_timing->SetSSLEnd(load_timing.connect_timing.ssl_end); | 
|  | url_timing->SetSendStart(load_timing.send_start); | 
|  | url_timing->SetSendEnd(load_timing.send_end); | 
|  | url_timing->SetReceiveHeadersStart(load_timing.receive_headers_start); | 
|  | url_timing->SetReceiveHeadersEnd(load_timing.receive_headers_end); | 
|  | url_timing->SetPushStart(load_timing.push_start); | 
|  | url_timing->SetPushEnd(load_timing.push_end); | 
|  | } | 
|  |  | 
|  | // This is complementary to ConvertNetPriorityToWebKitPriority, defined in | 
|  | // service_worker_context_client.cc. | 
|  | net::RequestPriority ConvertWebKitPriorityToNetPriority( | 
|  | const WebURLRequest::Priority& priority) { | 
|  | switch (priority) { | 
|  | case WebURLRequest::Priority::kVeryHigh: | 
|  | return net::HIGHEST; | 
|  |  | 
|  | case WebURLRequest::Priority::kHigh: | 
|  | return net::MEDIUM; | 
|  |  | 
|  | case WebURLRequest::Priority::kMedium: | 
|  | return net::LOW; | 
|  |  | 
|  | case WebURLRequest::Priority::kLow: | 
|  | return net::LOWEST; | 
|  |  | 
|  | case WebURLRequest::Priority::kVeryLow: | 
|  | return net::IDLE; | 
|  |  | 
|  | case WebURLRequest::Priority::kUnresolved: | 
|  | default: | 
|  | NOTREACHED(); | 
|  | return net::LOW; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Convert a net::SignedCertificateTimestampAndStatus object to a | 
|  | // blink::WebURLResponse::SignedCertificateTimestamp object. | 
|  | blink::WebURLResponse::SignedCertificateTimestamp NetSCTToBlinkSCT( | 
|  | const net::SignedCertificateTimestampAndStatus& sct_and_status) { | 
|  | return blink::WebURLResponse::SignedCertificateTimestamp( | 
|  | WebString::FromASCII(net::ct::StatusToString(sct_and_status.status)), | 
|  | WebString::FromASCII(net::ct::OriginToString(sct_and_status.sct->origin)), | 
|  | WebString::FromUTF8(sct_and_status.sct->log_description), | 
|  | WebString::FromASCII( | 
|  | base::HexEncode(sct_and_status.sct->log_id.c_str(), | 
|  | sct_and_status.sct->log_id.length())), | 
|  | sct_and_status.sct->timestamp.ToJavaTime(), | 
|  | WebString::FromASCII(net::ct::HashAlgorithmToString( | 
|  | sct_and_status.sct->signature.hash_algorithm)), | 
|  | WebString::FromASCII(net::ct::SignatureAlgorithmToString( | 
|  | sct_and_status.sct->signature.signature_algorithm)), | 
|  | WebString::FromASCII(base::HexEncode( | 
|  | sct_and_status.sct->signature.signature_data.c_str(), | 
|  | sct_and_status.sct->signature.signature_data.length()))); | 
|  | } | 
|  |  | 
|  | WebString CryptoBufferAsWebString(const CRYPTO_BUFFER* buffer) { | 
|  | base::StringPiece sp = net::x509_util::CryptoBufferAsStringPiece(buffer); | 
|  | return blink::WebString::FromLatin1( | 
|  | reinterpret_cast<const blink::WebLChar*>(sp.begin()), sp.size()); | 
|  | } | 
|  |  | 
|  | void SetSecurityStyleAndDetails(const GURL& url, | 
|  | const network::ResourceResponseInfo& info, | 
|  | WebURLResponse* response, | 
|  | bool report_security_info) { | 
|  | if (!report_security_info) { | 
|  | response->SetSecurityStyle(blink::kWebSecurityStyleUnknown); | 
|  | return; | 
|  | } | 
|  | if (!url.SchemeIsCryptographic()) { | 
|  | // Some origins are considered secure even though they're not cryptographic, | 
|  | // so treat them as secure in the UI. | 
|  | if (IsOriginSecure(url)) | 
|  | response->SetSecurityStyle(blink::kWebSecurityStyleSecure); | 
|  | else | 
|  | response->SetSecurityStyle(blink::kWebSecurityStyleInsecure); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The resource loader does not provide a guarantee that requests always have | 
|  | // security info (such as a certificate) attached. Use WebSecurityStyleUnknown | 
|  | // in this case where there isn't enough information to be useful. | 
|  | if (!info.ssl_info.has_value()) { | 
|  | response->SetSecurityStyle(blink::kWebSecurityStyleUnknown); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const net::SSLInfo& ssl_info = *info.ssl_info; | 
|  |  | 
|  | const char* protocol = ""; | 
|  | const char* key_exchange = ""; | 
|  | const char* cipher = ""; | 
|  | const char* mac = ""; | 
|  | const char* key_exchange_group = ""; | 
|  |  | 
|  | if (ssl_info.connection_status) { | 
|  | int ssl_version = | 
|  | net::SSLConnectionStatusToVersion(ssl_info.connection_status); | 
|  | net::SSLVersionToString(&protocol, ssl_version); | 
|  |  | 
|  | bool is_aead; | 
|  | bool is_tls13; | 
|  | uint16_t cipher_suite = | 
|  | net::SSLConnectionStatusToCipherSuite(ssl_info.connection_status); | 
|  | net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead, | 
|  | &is_tls13, cipher_suite); | 
|  | if (key_exchange == nullptr) { | 
|  | DCHECK(is_tls13); | 
|  | key_exchange = ""; | 
|  | } | 
|  |  | 
|  | if (mac == nullptr) { | 
|  | DCHECK(is_aead); | 
|  | mac = ""; | 
|  | } | 
|  |  | 
|  | if (ssl_info.key_exchange_group != 0) { | 
|  | // Historically the field was named 'curve' rather than 'group'. | 
|  | key_exchange_group = SSL_get_curve_name(ssl_info.key_exchange_group); | 
|  | if (!key_exchange_group) { | 
|  | NOTREACHED(); | 
|  | key_exchange_group = ""; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | response->SetSecurityStyle( | 
|  | GetSecurityStyleForResource(url, info.cert_status)); | 
|  |  | 
|  | blink::WebURLResponse::SignedCertificateTimestampList sct_list( | 
|  | ssl_info.signed_certificate_timestamps.size()); | 
|  |  | 
|  | for (size_t i = 0; i < sct_list.size(); ++i) | 
|  | sct_list[i] = NetSCTToBlinkSCT(ssl_info.signed_certificate_timestamps[i]); | 
|  |  | 
|  | if (!ssl_info.cert) { | 
|  | NOTREACHED(); | 
|  | response->SetSecurityStyle(blink::kWebSecurityStyleUnknown); | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::vector<std::string> san_dns; | 
|  | std::vector<std::string> san_ip; | 
|  | ssl_info.cert->GetSubjectAltName(&san_dns, &san_ip); | 
|  | blink::WebVector<blink::WebString> web_san(san_dns.size() + san_ip.size()); | 
|  | std::transform( | 
|  | san_dns.begin(), san_dns.end(), web_san.begin(), | 
|  | [](const std::string& h) { return blink::WebString::FromLatin1(h); }); | 
|  | std::transform(san_ip.begin(), san_ip.end(), web_san.begin() + san_dns.size(), | 
|  | [](const std::string& h) { | 
|  | net::IPAddress ip(reinterpret_cast<const uint8_t*>(h.data()), | 
|  | h.size()); | 
|  | return blink::WebString::FromLatin1(ip.ToString()); | 
|  | }); | 
|  |  | 
|  | blink::WebVector<blink::WebString> web_cert; | 
|  | web_cert.reserve(ssl_info.cert->intermediate_buffers().size() + 1); | 
|  | web_cert.emplace_back(CryptoBufferAsWebString(ssl_info.cert->cert_buffer())); | 
|  | for (const auto& cert : ssl_info.cert->intermediate_buffers()) | 
|  | web_cert.emplace_back(CryptoBufferAsWebString(cert.get())); | 
|  |  | 
|  | blink::WebURLResponse::WebSecurityDetails webSecurityDetails( | 
|  | WebString::FromASCII(protocol), WebString::FromASCII(key_exchange), | 
|  | WebString::FromASCII(key_exchange_group), WebString::FromASCII(cipher), | 
|  | WebString::FromASCII(mac), | 
|  | WebString::FromUTF8(ssl_info.cert->subject().common_name), web_san, | 
|  | WebString::FromUTF8(ssl_info.cert->issuer().common_name), | 
|  | ssl_info.cert->valid_start().ToDoubleT(), | 
|  | ssl_info.cert->valid_expiry().ToDoubleT(), web_cert, sct_list); | 
|  |  | 
|  | response->SetSecurityDetails(webSecurityDetails); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | WebURLLoaderFactoryImpl::WebURLLoaderFactoryImpl( | 
|  | base::WeakPtr<ResourceDispatcher> resource_dispatcher, | 
|  | scoped_refptr<network::SharedURLLoaderFactory> loader_factory) | 
|  | : resource_dispatcher_(std::move(resource_dispatcher)), | 
|  | loader_factory_(std::move(loader_factory)) {} | 
|  |  | 
|  | WebURLLoaderFactoryImpl::~WebURLLoaderFactoryImpl() = default; | 
|  |  | 
|  | std::unique_ptr<WebURLLoaderFactoryImpl> | 
|  | WebURLLoaderFactoryImpl::CreateTestOnlyFactory() { | 
|  | return std::make_unique<WebURLLoaderFactoryImpl>(nullptr, nullptr); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<blink::WebURLLoader> WebURLLoaderFactoryImpl::CreateURLLoader( | 
|  | const blink::WebURLRequest& request, | 
|  | std::unique_ptr<WebResourceLoadingTaskRunnerHandle> task_runner_handle) { | 
|  | if (!loader_factory_) { | 
|  | // In some tests like RenderViewTests loader_factory_ is not available. | 
|  | // These tests can still use data URLs to bypass the ResourceDispatcher. | 
|  | if (!task_runner_handle) { | 
|  | // TODO(altimin): base::ThreadTaskRunnerHandle::Get is deprecated in | 
|  | // the renderer. Fix this for frame and non-frame clients. | 
|  | task_runner_handle = | 
|  | WebResourceLoadingTaskRunnerHandle::CreateUnprioritized( | 
|  | base::ThreadTaskRunnerHandle::Get()); | 
|  | } | 
|  |  | 
|  | return std::make_unique<WebURLLoaderImpl>(resource_dispatcher_.get(), | 
|  | std::move(task_runner_handle), | 
|  | nullptr /* factory */); | 
|  | } | 
|  |  | 
|  | DCHECK(task_runner_handle); | 
|  | DCHECK(resource_dispatcher_); | 
|  | return std::make_unique<WebURLLoaderImpl>(resource_dispatcher_.get(), | 
|  | std::move(task_runner_handle), | 
|  | loader_factory_); | 
|  | } | 
|  |  | 
|  | // This inner class exists since the WebURLLoader may be deleted while inside a | 
|  | // call to WebURLLoaderClient.  Refcounting is to keep the context from being | 
|  | // deleted if it may have work to do after calling into the client. | 
|  | class WebURLLoaderImpl::Context : public base::RefCounted<Context> { | 
|  | public: | 
|  | Context( | 
|  | WebURLLoaderImpl* loader, | 
|  | ResourceDispatcher* resource_dispatcher, | 
|  | std::unique_ptr<WebResourceLoadingTaskRunnerHandle> task_runner_handle, | 
|  | scoped_refptr<network::SharedURLLoaderFactory> factory, | 
|  | mojom::KeepAliveHandlePtr keep_alive_handle); | 
|  |  | 
|  | ResourceDispatcher* resource_dispatcher() { return resource_dispatcher_; } | 
|  | int request_id() const { return request_id_; } | 
|  | WebURLLoaderClient* client() const { return client_; } | 
|  | void set_client(WebURLLoaderClient* client) { client_ = client; } | 
|  | scoped_refptr<base::SingleThreadTaskRunner> task_runner() { | 
|  | return task_runner_; | 
|  | } | 
|  |  | 
|  | void Cancel(); | 
|  | void SetDefersLoading(bool value); | 
|  | void DidChangePriority(WebURLRequest::Priority new_priority, | 
|  | int intra_priority_value); | 
|  | void Start(const WebURLRequest& request, | 
|  | SyncLoadResponse* sync_load_response); | 
|  |  | 
|  | void OnUploadProgress(uint64_t position, uint64_t size); | 
|  | bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, | 
|  | const network::ResourceResponseInfo& info); | 
|  | void OnReceivedResponse(const network::ResourceResponseInfo& info); | 
|  | void OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body); | 
|  | void OnTransferSizeUpdated(int transfer_size_diff); | 
|  | void OnReceivedCachedMetadata(const char* data, int len); | 
|  | void OnCompletedRequest(const network::URLLoaderCompletionStatus& status); | 
|  |  | 
|  | private: | 
|  | friend class base::RefCounted<Context>; | 
|  | // The maximal number of bytes consumed in a task. When there are more bytes | 
|  | // in the data pipe, they will be consumed in following tasks. Setting a too | 
|  | // small number will generate ton of tasks but setting a too large number will | 
|  | // lead to thread janks. Also, some clients cannot handle too large chunks | 
|  | // (512k for example). | 
|  | static constexpr uint32_t kMaxNumConsumedBytesInTask = 64 * 1024; | 
|  |  | 
|  | ~Context(); | 
|  |  | 
|  | // Called when the body data stream is detached from the reader side. | 
|  | void CancelBodyStreaming(); | 
|  |  | 
|  | void OnBodyAvailable(MojoResult, const mojo::HandleSignalsState&); | 
|  | void OnBodyHasBeenRead(uint32_t read_bytes); | 
|  |  | 
|  | static net::NetworkTrafficAnnotationTag GetTrafficAnnotationTag( | 
|  | const blink::WebURLRequest& request); | 
|  |  | 
|  | WebURLLoaderImpl* loader_; | 
|  |  | 
|  | WebURL url_; | 
|  | // Controls SetSecurityStyleAndDetails() in PopulateURLResponse(). Initially | 
|  | // set to WebURLRequest::ReportRawHeaders() in Start() and gets updated in | 
|  | // WillFollowRedirect() (by the InspectorNetworkAgent) while the new | 
|  | // ReportRawHeaders() value won't be propagated to the browser process. | 
|  | // | 
|  | // TODO(tyoshino): Investigate whether it's worth propagating the new value. | 
|  | bool report_raw_headers_; | 
|  |  | 
|  | // ResponseLoadViaDataPipe: Consume the data in Context when it's set to | 
|  | // false. | 
|  | bool pass_response_pipe_to_client_ = false; | 
|  |  | 
|  | WebURLLoaderClient* client_; | 
|  | ResourceDispatcher* resource_dispatcher_; | 
|  | std::unique_ptr<WebResourceLoadingTaskRunnerHandle> task_runner_handle_; | 
|  | scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 
|  | mojom::KeepAliveHandlePtr keep_alive_handle_; | 
|  | enum DeferState { NOT_DEFERRING, SHOULD_DEFER }; | 
|  | DeferState defers_loading_; | 
|  | int request_id_; | 
|  | bool in_two_phase_read_ = false; | 
|  | bool is_in_on_body_available_ = false; | 
|  |  | 
|  | base::Optional<network::URLLoaderCompletionStatus> completion_status_; | 
|  |  | 
|  | scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; | 
|  | }; | 
|  |  | 
|  | // A thin wrapper class for Context to ensure its lifetime while it is | 
|  | // handling IPC messages coming from ResourceDispatcher. Owns one ref to | 
|  | // Context and held by ResourceDispatcher. | 
|  | class WebURLLoaderImpl::RequestPeerImpl : public RequestPeer { | 
|  | public: | 
|  | // If |discard_body| is false this doesn't propagate the received data | 
|  | // to the context. | 
|  | explicit RequestPeerImpl(Context* context, bool discard_body = false); | 
|  |  | 
|  | // RequestPeer methods: | 
|  | void OnUploadProgress(uint64_t position, uint64_t size) override; | 
|  | bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, | 
|  | const network::ResourceResponseInfo& info) override; | 
|  | void OnReceivedResponse(const network::ResourceResponseInfo& info) override; | 
|  | void OnStartLoadingResponseBody( | 
|  | mojo::ScopedDataPipeConsumerHandle body) override; | 
|  | void OnTransferSizeUpdated(int transfer_size_diff) override; | 
|  | void OnReceivedCachedMetadata(const char* data, int len) override; | 
|  | void OnCompletedRequest( | 
|  | const network::URLLoaderCompletionStatus& status) override; | 
|  | scoped_refptr<base::TaskRunner> GetTaskRunner() override { | 
|  | return context_->task_runner(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | scoped_refptr<Context> context_; | 
|  | const bool discard_body_; | 
|  | DISALLOW_COPY_AND_ASSIGN(RequestPeerImpl); | 
|  | }; | 
|  |  | 
|  | // A sink peer that doesn't forward the data. | 
|  | class WebURLLoaderImpl::SinkPeer : public RequestPeer { | 
|  | public: | 
|  | explicit SinkPeer(Context* context) | 
|  | : context_(context), | 
|  | body_watcher_(FROM_HERE, | 
|  | mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC, | 
|  | context->task_runner()) {} | 
|  |  | 
|  | // RequestPeer implementation: | 
|  | void OnUploadProgress(uint64_t position, uint64_t size) override {} | 
|  | bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, | 
|  | const network::ResourceResponseInfo& info) override { | 
|  | return true; | 
|  | } | 
|  | void OnReceivedResponse(const network::ResourceResponseInfo& info) override {} | 
|  | void OnStartLoadingResponseBody( | 
|  | mojo::ScopedDataPipeConsumerHandle body) override { | 
|  | body_handle_ = std::move(body); | 
|  | body_watcher_.Watch( | 
|  | body_handle_.get(), | 
|  | MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, | 
|  | base::BindRepeating(&SinkPeer::OnBodyAvailable, | 
|  | base::Unretained(this))); | 
|  | } | 
|  | void OnTransferSizeUpdated(int transfer_size_diff) override {} | 
|  | void OnReceivedCachedMetadata(const char* data, int len) override {} | 
|  | void OnCompletedRequest( | 
|  | const network::URLLoaderCompletionStatus& status) override { | 
|  | body_handle_.reset(); | 
|  | body_watcher_.Cancel(); | 
|  | context_->resource_dispatcher()->Cancel(context_->request_id(), | 
|  | context_->task_runner()); | 
|  | } | 
|  | scoped_refptr<base::TaskRunner> GetTaskRunner() override { | 
|  | return context_->task_runner(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | void OnBodyAvailable(MojoResult, const mojo::HandleSignalsState&) { | 
|  | while (true) { | 
|  | const void* buffer = nullptr; | 
|  | uint32_t available = 0; | 
|  | MojoResult rv = body_handle_->BeginReadData(&buffer, &available, | 
|  | MOJO_READ_DATA_FLAG_NONE); | 
|  | if (rv == MOJO_RESULT_SHOULD_WAIT) { | 
|  | return; | 
|  | } | 
|  | if (rv != MOJO_RESULT_OK) { | 
|  | break; | 
|  | } | 
|  | rv = body_handle_->EndReadData(available); | 
|  | if (rv != MOJO_RESULT_OK) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | body_handle_.reset(); | 
|  | body_watcher_.Cancel(); | 
|  | } | 
|  |  | 
|  | scoped_refptr<Context> context_; | 
|  | mojo::ScopedDataPipeConsumerHandle body_handle_; | 
|  | mojo::SimpleWatcher body_watcher_; | 
|  | DISALLOW_COPY_AND_ASSIGN(SinkPeer); | 
|  | }; | 
|  |  | 
|  | // WebURLLoaderImpl::Context -------------------------------------------------- | 
|  |  | 
|  | // static | 
|  | constexpr uint32_t WebURLLoaderImpl::Context::kMaxNumConsumedBytesInTask; | 
|  |  | 
|  | WebURLLoaderImpl::Context::Context( | 
|  | WebURLLoaderImpl* loader, | 
|  | ResourceDispatcher* resource_dispatcher, | 
|  | std::unique_ptr<WebResourceLoadingTaskRunnerHandle> task_runner_handle, | 
|  | scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, | 
|  | mojom::KeepAliveHandlePtr keep_alive_handle_ptr) | 
|  | : loader_(loader), | 
|  | report_raw_headers_(false), | 
|  | client_(nullptr), | 
|  | resource_dispatcher_(resource_dispatcher), | 
|  | task_runner_handle_(std::move(task_runner_handle)), | 
|  | task_runner_(task_runner_handle_->GetTaskRunner()), | 
|  | keep_alive_handle_(std::move(keep_alive_handle_ptr)), | 
|  | defers_loading_(NOT_DEFERRING), | 
|  | request_id_(-1), | 
|  | url_loader_factory_(std::move(url_loader_factory)) { | 
|  | DCHECK(url_loader_factory_ || !resource_dispatcher); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::Cancel() { | 
|  | TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoaderImpl::Context::Cancel", this, | 
|  | TRACE_EVENT_FLAG_FLOW_IN); | 
|  | if (resource_dispatcher_ && // NULL in unittest. | 
|  | request_id_ != -1) { | 
|  | resource_dispatcher_->Cancel(request_id_, task_runner_); | 
|  | request_id_ = -1; | 
|  | } | 
|  |  | 
|  | // Do not make any further calls to the client. | 
|  | client_ = nullptr; | 
|  | loader_ = nullptr; | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::SetDefersLoading(bool value) { | 
|  | if (request_id_ != -1) | 
|  | resource_dispatcher_->SetDefersLoading(request_id_, value); | 
|  | if (value && defers_loading_ == NOT_DEFERRING) { | 
|  | defers_loading_ = SHOULD_DEFER; | 
|  | } else if (!value && defers_loading_ != NOT_DEFERRING) { | 
|  | defers_loading_ = NOT_DEFERRING; | 
|  | } | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::DidChangePriority( | 
|  | WebURLRequest::Priority new_priority, int intra_priority_value) { | 
|  | if (request_id_ != -1) { | 
|  | net::RequestPriority net_priority = | 
|  | ConvertWebKitPriorityToNetPriority(new_priority); | 
|  | resource_dispatcher_->DidChangePriority(request_id_, net_priority, | 
|  | intra_priority_value); | 
|  | task_runner_handle_->DidChangeRequestPriority(net_priority); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::Start(const WebURLRequest& request, | 
|  | SyncLoadResponse* sync_load_response) { | 
|  | DCHECK(request_id_ == -1); | 
|  |  | 
|  | // Notify Blink's scheduler with the initial resource fetch priority. | 
|  | task_runner_handle_->DidChangeRequestPriority( | 
|  | ConvertWebKitPriorityToNetPriority(request.GetPriority())); | 
|  |  | 
|  | url_ = request.Url(); | 
|  | report_raw_headers_ = request.ReportRawHeaders(); | 
|  | pass_response_pipe_to_client_ = request.PassResponsePipeToClient(); | 
|  |  | 
|  | std::unique_ptr<NavigationResponseOverrideParameters> response_override; | 
|  | if (request.GetExtraData()) { | 
|  | RequestExtraData* extra_data = | 
|  | static_cast<RequestExtraData*>(request.GetExtraData()); | 
|  | response_override = extra_data->TakeNavigationResponseOverrideOwnership(); | 
|  | } | 
|  |  | 
|  | // TODO(domfarolino): Retrieve the referrer in the form of a referrer member | 
|  | // instead of the header field. See https://crbug.com/850813. | 
|  | GURL referrer_url( | 
|  | request.HttpHeaderField(WebString::FromASCII("Referer")).Latin1()); | 
|  | const std::string& method = request.HttpMethod().Latin1(); | 
|  |  | 
|  | // TODO(brettw) this should take parameter encoding into account when | 
|  | // creating the GURLs. | 
|  |  | 
|  | // TODO(horo): Check credentials flag is unset when credentials mode is omit. | 
|  | //             Check credentials flag is set when credentials mode is include. | 
|  |  | 
|  | std::unique_ptr<network::ResourceRequest> resource_request( | 
|  | new network::ResourceRequest); | 
|  |  | 
|  | resource_request->method = method; | 
|  | resource_request->url = url_; | 
|  | resource_request->site_for_cookies = request.SiteForCookies(); | 
|  | resource_request->top_frame_origin = request.TopFrameOrigin(); | 
|  | resource_request->upgrade_if_insecure = request.UpgradeIfInsecure(); | 
|  | resource_request->is_revalidating = request.IsRevalidating(); | 
|  | if (!request.RequestorOrigin().IsNull()) { | 
|  | if (request.RequestorOrigin().ToString() == "null") { | 
|  | // "file:" origin is treated like an opaque unique origin when | 
|  | // allow-file-access-from-files is not specified. Such origin is not | 
|  | // opaque (i.e., IsOpaque() returns false) but still serializes to | 
|  | // "null". | 
|  | resource_request->request_initiator = url::Origin(); | 
|  | } else { | 
|  | resource_request->request_initiator = request.RequestorOrigin(); | 
|  | } | 
|  | } | 
|  | resource_request->referrer = referrer_url; | 
|  |  | 
|  | resource_request->referrer_policy = | 
|  | Referrer::ReferrerPolicyForUrlRequest(request.GetReferrerPolicy()); | 
|  | resource_request->resource_type = | 
|  | static_cast<int>(WebURLRequestToResourceType(request)); | 
|  |  | 
|  | resource_request->headers = GetWebURLRequestHeaders(request); | 
|  | if (resource_request->resource_type == | 
|  | static_cast<int>(ResourceType::kStylesheet)) { | 
|  | resource_request->headers.SetHeader(network::kAcceptHeader, | 
|  | kStylesheetAcceptHeader); | 
|  | } else if (resource_request->resource_type == | 
|  | static_cast<int>(ResourceType::kFavicon) || | 
|  | resource_request->resource_type == | 
|  | static_cast<int>(ResourceType::kImage)) { | 
|  | resource_request->headers.SetHeader(network::kAcceptHeader, | 
|  | kImageAcceptHeader); | 
|  | } else { | 
|  | // Calling SetHeaderIfMissing() instead of SetHeader() because JS can | 
|  | // manually set an accept header on an XHR. | 
|  | resource_request->headers.SetHeaderIfMissing(network::kAcceptHeader, | 
|  | network::kDefaultAcceptHeader); | 
|  | } | 
|  | // Set X-Requested-With header to cors_exempt_headers rather than headers to | 
|  | // be exempted from CORS checks. | 
|  | if (!request.GetRequestedWithHeader().IsEmpty()) { | 
|  | resource_request->cors_exempt_headers.SetHeader( | 
|  | kCorsExemptRequestedWithHeaderName, | 
|  | WebString(request.GetRequestedWithHeader()).Utf8()); | 
|  | } | 
|  | // Set Purpose header to cors_exempt_headers rather than headers to be | 
|  | // exempted from CORS checks. | 
|  | if (!request.GetPurposeHeader().IsEmpty()) { | 
|  | resource_request->cors_exempt_headers.SetHeader( | 
|  | kCorsExemptPurposeHeaderName, | 
|  | WebString(request.GetPurposeHeader()).Utf8()); | 
|  | } | 
|  |  | 
|  | if (resource_request->resource_type == | 
|  | static_cast<int>(ResourceType::kPrefetch) || | 
|  | resource_request->resource_type == | 
|  | static_cast<int>(ResourceType::kFavicon)) { | 
|  | resource_request->do_not_prompt_for_login = true; | 
|  | } | 
|  |  | 
|  | resource_request->load_flags = request.GetLoadFlagsForWebUrlRequest(); | 
|  |  | 
|  | // |plugin_child_id| only needs to be non-zero if the request originates | 
|  | // outside the render process, so we can use requestorProcessID even | 
|  | // for requests from in-process plugins. | 
|  | resource_request->plugin_child_id = request.GetPluginChildID(); | 
|  | resource_request->priority = | 
|  | ConvertWebKitPriorityToNetPriority(request.GetPriority()); | 
|  | resource_request->appcache_host_id = request.AppCacheHostID(); | 
|  | resource_request->should_reset_appcache = request.ShouldResetAppCache(); | 
|  | resource_request->is_external_request = request.IsExternalRequest(); | 
|  | resource_request->cors_preflight_policy = request.GetCorsPreflightPolicy(); | 
|  | resource_request->skip_service_worker = request.GetSkipServiceWorker(); | 
|  | resource_request->fetch_request_mode = request.GetFetchRequestMode(); | 
|  | resource_request->fetch_credentials_mode = request.GetFetchCredentialsMode(); | 
|  | resource_request->fetch_redirect_mode = request.GetFetchRedirectMode(); | 
|  | resource_request->fetch_integrity = | 
|  | GetFetchIntegrityForWebURLRequest(request); | 
|  | resource_request->fetch_request_context_type = | 
|  | static_cast<int>(GetRequestContextTypeForWebURLRequest(request)); | 
|  |  | 
|  | resource_request->fetch_frame_type = | 
|  | network::mojom::RequestContextFrameType::kNone; | 
|  | resource_request->request_body = | 
|  | GetRequestBodyForWebURLRequest(request).get(); | 
|  | resource_request->keepalive = request.GetKeepalive(); | 
|  | resource_request->has_user_gesture = request.HasUserGesture(); | 
|  | resource_request->enable_load_timing = true; | 
|  | resource_request->enable_upload_progress = request.ReportUploadProgress(); | 
|  | GURL gurl(url_); | 
|  | if (request.GetRequestContext() == | 
|  | blink::mojom::RequestContextType::XML_HTTP_REQUEST && | 
|  | (gurl.has_username() || gurl.has_password())) { | 
|  | resource_request->do_not_prompt_for_login = true; | 
|  | } | 
|  | resource_request->report_raw_headers = request.ReportRawHeaders(); | 
|  | // TODO(ryansturm): Remove resource_request->previews_state once it is no | 
|  | // longer used in a network delegate. https://crbug.com/842233 | 
|  | resource_request->previews_state = | 
|  | static_cast<int>(request.GetPreviewsState()); | 
|  | resource_request->throttling_profile_id = request.GetDevToolsToken(); | 
|  |  | 
|  | if (base::UnguessableToken window_id = request.GetFetchWindowId()) | 
|  | resource_request->fetch_window_id = base::make_optional(window_id); | 
|  |  | 
|  | if (request.GetDevToolsId().has_value()) { | 
|  | resource_request->devtools_request_id = | 
|  | request.GetDevToolsId().value().Ascii(); | 
|  | } | 
|  |  | 
|  | // The network request has already been made by the browser. The renderer | 
|  | // should bind the URLLoaderClientEndpoints stored in |response_override| to | 
|  | // an implementation of a URLLoaderClient to get the response body. | 
|  | if (response_override) { | 
|  | DCHECK(!sync_load_response); | 
|  | } | 
|  |  | 
|  | RequestExtraData empty_extra_data; | 
|  | RequestExtraData* extra_data; | 
|  | if (request.GetExtraData()) | 
|  | extra_data = static_cast<RequestExtraData*>(request.GetExtraData()); | 
|  | else | 
|  | extra_data = &empty_extra_data; | 
|  | extra_data->CopyToResourceRequest(resource_request.get()); | 
|  |  | 
|  | std::unique_ptr<RequestPeer> peer; | 
|  | if (request.IsDownloadToNetworkCacheOnly()) { | 
|  | peer = std::make_unique<SinkPeer>(this); | 
|  | } else { | 
|  | const bool discard_body = (resource_request->resource_type == | 
|  | static_cast<int>(ResourceType::kPrefetch)); | 
|  | peer = | 
|  | std::make_unique<WebURLLoaderImpl::RequestPeerImpl>(this, discard_body); | 
|  | } | 
|  |  | 
|  | auto throttles = extra_data->TakeURLLoaderThrottles(); | 
|  | // The frame request blocker is only for a frame's subresources. | 
|  | if (extra_data->frame_request_blocker() && | 
|  | !IsResourceTypeFrame( | 
|  | static_cast<ResourceType>(resource_request->resource_type))) { | 
|  | auto throttle = | 
|  | extra_data->frame_request_blocker()->GetThrottleIfRequestsBlocked(); | 
|  | if (throttle) | 
|  | throttles.push_back(std::move(throttle)); | 
|  | } | 
|  |  | 
|  | if (sync_load_response) { | 
|  | DCHECK(defers_loading_ == NOT_DEFERRING); | 
|  |  | 
|  | blink::mojom::BlobRegistryPtrInfo download_to_blob_registry; | 
|  | if (request.PassResponsePipeToClient()) { | 
|  | blink::Platform::Current()->GetInterfaceProvider()->GetInterface( | 
|  | MakeRequest(&download_to_blob_registry)); | 
|  | } | 
|  | TimeTicks start_time = TimeTicks::Now(); | 
|  | resource_dispatcher_->StartSync( | 
|  | std::move(resource_request), request.RequestorID(), | 
|  | GetTrafficAnnotationTag(request), sync_load_response, | 
|  | url_loader_factory_, std::move(throttles), request.TimeoutInterval(), | 
|  | std::move(download_to_blob_registry), std::move(peer)); | 
|  | base::TimeDelta delta = TimeTicks::Now() - start_time; | 
|  | UMA_HISTOGRAM_MEDIUM_TIMES("WebURLLoader.SyncResourceRequestDuration", | 
|  | delta); | 
|  | return; | 
|  | } | 
|  |  | 
|  | TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoaderImpl::Context::Start", this, | 
|  | TRACE_EVENT_FLAG_FLOW_OUT); | 
|  | request_id_ = resource_dispatcher_->StartAsync( | 
|  | std::move(resource_request), request.RequestorID(), task_runner_, | 
|  | GetTrafficAnnotationTag(request), false /* is_sync */, | 
|  | request.PassResponsePipeToClient(), std::move(peer), url_loader_factory_, | 
|  | std::move(throttles), std::move(response_override)); | 
|  |  | 
|  | if (defers_loading_ != NOT_DEFERRING) | 
|  | resource_dispatcher_->SetDefersLoading(request_id_, true); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::OnUploadProgress(uint64_t position, | 
|  | uint64_t size) { | 
|  | if (client_) | 
|  | client_->DidSendData(position, size); | 
|  | } | 
|  |  | 
|  | bool WebURLLoaderImpl::Context::OnReceivedRedirect( | 
|  | const net::RedirectInfo& redirect_info, | 
|  | const network::ResourceResponseInfo& info) { | 
|  | if (!client_) | 
|  | return false; | 
|  |  | 
|  | TRACE_EVENT_WITH_FLOW0( | 
|  | "loading", "WebURLLoaderImpl::Context::OnReceivedRedirect", | 
|  | this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); | 
|  |  | 
|  | WebURLResponse response; | 
|  | PopulateURLResponse(url_, info, &response, report_raw_headers_, request_id_); | 
|  |  | 
|  | url_ = WebURL(redirect_info.new_url); | 
|  | return client_->WillFollowRedirect( | 
|  | url_, redirect_info.new_site_for_cookies, | 
|  | redirect_info.new_top_frame_origin, | 
|  | WebString::FromUTF8(redirect_info.new_referrer), | 
|  | Referrer::NetReferrerPolicyToBlinkReferrerPolicy( | 
|  | redirect_info.new_referrer_policy), | 
|  | WebString::FromUTF8(redirect_info.new_method), response, | 
|  | report_raw_headers_); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::OnReceivedResponse( | 
|  | const network::ResourceResponseInfo& info) { | 
|  | if (!client_) | 
|  | return; | 
|  |  | 
|  | TRACE_EVENT_WITH_FLOW0( | 
|  | "loading", "WebURLLoaderImpl::Context::OnReceivedResponse", | 
|  | this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); | 
|  |  | 
|  | WebURLResponse response; | 
|  | PopulateURLResponse(url_, info, &response, report_raw_headers_, request_id_); | 
|  |  | 
|  | client_->DidReceiveResponse(response); | 
|  |  | 
|  | // DidReceiveResponse() may have triggered a cancel, causing the |client_| to | 
|  | // go away. | 
|  | if (!client_) | 
|  | return; | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::OnStartLoadingResponseBody( | 
|  | mojo::ScopedDataPipeConsumerHandle body) { | 
|  | if (client_) | 
|  | client_->DidStartLoadingResponseBody(std::move(body)); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::OnTransferSizeUpdated(int transfer_size_diff) { | 
|  | client_->DidReceiveTransferSizeUpdate(transfer_size_diff); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::OnReceivedCachedMetadata( | 
|  | const char* data, int len) { | 
|  | if (!client_) | 
|  | return; | 
|  | TRACE_EVENT_WITH_FLOW0( | 
|  | "loading", "WebURLLoaderImpl::Context::OnReceivedCachedMetadata", | 
|  | this, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); | 
|  | client_->DidReceiveCachedMetadata(data, len); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::OnCompletedRequest( | 
|  | const network::URLLoaderCompletionStatus& status) { | 
|  | int64_t total_transfer_size = status.encoded_data_length; | 
|  | int64_t encoded_body_size = status.encoded_body_length; | 
|  |  | 
|  | if (client_) { | 
|  | TRACE_EVENT_WITH_FLOW0("loading", | 
|  | "WebURLLoaderImpl::Context::OnCompletedRequest", | 
|  | this, TRACE_EVENT_FLAG_FLOW_IN); | 
|  |  | 
|  | if (status.error_code != net::OK) { | 
|  | client_->DidFail(PopulateURLError(status, url_), total_transfer_size, | 
|  | encoded_body_size, status.decoded_body_length); | 
|  | } else { | 
|  | client_->DidFinishLoading(status.completion_time, total_transfer_size, | 
|  | encoded_body_size, status.decoded_body_length, | 
|  | status.should_report_corb_blocking, | 
|  | status.cors_preflight_timing_info); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | WebURLLoaderImpl::Context::~Context() { | 
|  | // We must be already cancelled at this point. | 
|  | DCHECK_LT(request_id_, 0); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Context::CancelBodyStreaming() { | 
|  | scoped_refptr<Context> protect(this); | 
|  |  | 
|  | if (client_) { | 
|  | // TODO(yhirano): Set |stale_copy_in_cache| appropriately if possible. | 
|  | client_->DidFail(WebURLError(net::ERR_ABORTED, url_), | 
|  | WebURLLoaderClient::kUnknownEncodedDataLength, 0, 0); | 
|  | } | 
|  |  | 
|  | // Notify the browser process that the request is canceled. | 
|  | Cancel(); | 
|  | } | 
|  |  | 
|  | // WebURLLoaderImpl::RequestPeerImpl ------------------------------------------ | 
|  |  | 
|  | WebURLLoaderImpl::RequestPeerImpl::RequestPeerImpl(Context* context, | 
|  | bool discard_body) | 
|  | : context_(context), discard_body_(discard_body) {} | 
|  |  | 
|  | void WebURLLoaderImpl::RequestPeerImpl::OnUploadProgress(uint64_t position, | 
|  | uint64_t size) { | 
|  | context_->OnUploadProgress(position, size); | 
|  | } | 
|  |  | 
|  | bool WebURLLoaderImpl::RequestPeerImpl::OnReceivedRedirect( | 
|  | const net::RedirectInfo& redirect_info, | 
|  | const network::ResourceResponseInfo& info) { | 
|  | return context_->OnReceivedRedirect(redirect_info, info); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::RequestPeerImpl::OnReceivedResponse( | 
|  | const network::ResourceResponseInfo& info) { | 
|  | context_->OnReceivedResponse(info); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::RequestPeerImpl::OnStartLoadingResponseBody( | 
|  | mojo::ScopedDataPipeConsumerHandle body) { | 
|  | context_->OnStartLoadingResponseBody(std::move(body)); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::RequestPeerImpl::OnTransferSizeUpdated( | 
|  | int transfer_size_diff) { | 
|  | context_->OnTransferSizeUpdated(transfer_size_diff); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::RequestPeerImpl::OnReceivedCachedMetadata( | 
|  | const char* data, | 
|  | int len) { | 
|  | if (discard_body_) | 
|  | return; | 
|  | context_->OnReceivedCachedMetadata(data, len); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::RequestPeerImpl::OnCompletedRequest( | 
|  | const network::URLLoaderCompletionStatus& status) { | 
|  | context_->OnCompletedRequest(status); | 
|  | } | 
|  |  | 
|  | // WebURLLoaderImpl ----------------------------------------------------------- | 
|  |  | 
|  | WebURLLoaderImpl::WebURLLoaderImpl( | 
|  | ResourceDispatcher* resource_dispatcher, | 
|  | std::unique_ptr<WebResourceLoadingTaskRunnerHandle> task_runner_handle, | 
|  | scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) | 
|  | : WebURLLoaderImpl(resource_dispatcher, | 
|  | std::move(task_runner_handle), | 
|  | std::move(url_loader_factory), | 
|  | nullptr) {} | 
|  |  | 
|  | WebURLLoaderImpl::WebURLLoaderImpl( | 
|  | ResourceDispatcher* resource_dispatcher, | 
|  | std::unique_ptr<WebResourceLoadingTaskRunnerHandle> task_runner_handle, | 
|  | scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, | 
|  | mojom::KeepAliveHandlePtr keep_alive_handle) | 
|  | : context_(new Context(this, | 
|  | resource_dispatcher, | 
|  | std::move(task_runner_handle), | 
|  | std::move(url_loader_factory), | 
|  | std::move(keep_alive_handle))) {} | 
|  |  | 
|  | WebURLLoaderImpl::~WebURLLoaderImpl() { | 
|  | Cancel(); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::PopulateURLResponse( | 
|  | const WebURL& url, | 
|  | const network::ResourceResponseInfo& info, | 
|  | WebURLResponse* response, | 
|  | bool report_security_info, | 
|  | int request_id) { | 
|  | response->SetCurrentRequestUrl(url); | 
|  | response->SetResponseTime(info.response_time); | 
|  | response->SetMimeType(WebString::FromUTF8(info.mime_type)); | 
|  | response->SetTextEncodingName(WebString::FromUTF8(info.charset)); | 
|  | response->SetExpectedContentLength(info.content_length); | 
|  | response->SetHasMajorCertificateErrors( | 
|  | net::IsCertStatusError(info.cert_status) && | 
|  | !net::IsCertStatusMinorError(info.cert_status)); | 
|  | response->SetCTPolicyCompliance(info.ct_policy_compliance); | 
|  | response->SetIsLegacyTLSVersion(info.is_legacy_tls_version); | 
|  | response->SetAppCacheID(info.appcache_id); | 
|  | response->SetAppCacheManifestURL(info.appcache_manifest_url); | 
|  | response->SetWasCached(!info.load_timing.request_start_time.is_null() && | 
|  | info.response_time < | 
|  | info.load_timing.request_start_time); | 
|  | response->SetRemoteIPAddress(WebString::FromUTF8( | 
|  | net::HostPortPair::FromIPEndPoint(info.remote_endpoint).HostForURL())); | 
|  | response->SetRemotePort(info.remote_endpoint.port()); | 
|  | response->SetConnectionID(info.load_timing.socket_log_id); | 
|  | response->SetConnectionReused(info.load_timing.socket_reused); | 
|  | response->SetWasFetchedViaSPDY(info.was_fetched_via_spdy); | 
|  | response->SetWasFetchedViaServiceWorker(info.was_fetched_via_service_worker); | 
|  | response->SetWasFallbackRequiredByServiceWorker( | 
|  | info.was_fallback_required_by_service_worker); | 
|  | response->SetType(info.response_type); | 
|  | response->SetUrlListViaServiceWorker(info.url_list_via_service_worker); | 
|  | response->SetCacheStorageCacheName( | 
|  | info.is_in_cache_storage | 
|  | ? blink::WebString::FromUTF8(info.cache_storage_cache_name) | 
|  | : blink::WebString()); | 
|  | blink::WebVector<blink::WebString> cors_exposed_header_names( | 
|  | info.cors_exposed_header_names.size()); | 
|  | std::transform( | 
|  | info.cors_exposed_header_names.begin(), | 
|  | info.cors_exposed_header_names.end(), cors_exposed_header_names.begin(), | 
|  | [](const std::string& h) { return blink::WebString::FromLatin1(h); }); | 
|  | response->SetCorsExposedHeaderNames(cors_exposed_header_names); | 
|  | response->SetDidServiceWorkerNavigationPreload( | 
|  | info.did_service_worker_navigation_preload); | 
|  | response->SetEncodedDataLength(info.encoded_data_length); | 
|  | response->SetAlpnNegotiatedProtocol( | 
|  | WebString::FromUTF8(info.alpn_negotiated_protocol)); | 
|  | response->SetConnectionInfo(info.connection_info); | 
|  | response->SetAsyncRevalidationRequested(info.async_revalidation_requested); | 
|  | response->SetNetworkAccessed(info.network_accessed); | 
|  | response->SetRequestId(request_id); | 
|  | response->SetIsSignedExchangeInnerResponse( | 
|  | info.is_signed_exchange_inner_response); | 
|  |  | 
|  | SetSecurityStyleAndDetails(url, info, response, report_security_info); | 
|  |  | 
|  | // If there's no received headers end time, don't set load timing.  This is | 
|  | // the case for non-HTTP requests, requests that don't go over the wire, and | 
|  | // certain error cases. | 
|  | if (!info.load_timing.receive_headers_end.is_null()) { | 
|  | WebURLLoadTiming timing; | 
|  | PopulateURLLoadTiming(info.load_timing, &timing); | 
|  | timing.SetWorkerStart(info.service_worker_start_time); | 
|  | timing.SetWorkerReady(info.service_worker_ready_time); | 
|  | response->SetLoadTiming(timing); | 
|  | } | 
|  |  | 
|  | if (info.raw_request_response_info.get()) { | 
|  | WebHTTPLoadInfo load_info; | 
|  |  | 
|  | load_info.SetHTTPStatusCode( | 
|  | info.raw_request_response_info->http_status_code); | 
|  | load_info.SetHTTPStatusText(WebString::FromLatin1( | 
|  | info.raw_request_response_info->http_status_text)); | 
|  |  | 
|  | load_info.SetRequestHeadersText(WebString::FromLatin1( | 
|  | info.raw_request_response_info->request_headers_text)); | 
|  | load_info.SetResponseHeadersText(WebString::FromLatin1( | 
|  | info.raw_request_response_info->response_headers_text)); | 
|  | const HeadersVector& request_headers = | 
|  | info.raw_request_response_info->request_headers; | 
|  | for (auto it = request_headers.begin(); it != request_headers.end(); ++it) { | 
|  | load_info.AddRequestHeader(WebString::FromLatin1(it->first), | 
|  | WebString::FromLatin1(it->second)); | 
|  | } | 
|  | const HeadersVector& response_headers = | 
|  | info.raw_request_response_info->response_headers; | 
|  | for (auto it = response_headers.begin(); it != response_headers.end(); | 
|  | ++it) { | 
|  | load_info.AddResponseHeader(WebString::FromLatin1(it->first), | 
|  | WebString::FromLatin1(it->second)); | 
|  | } | 
|  | response->SetHTTPLoadInfo(load_info); | 
|  | } | 
|  |  | 
|  | const net::HttpResponseHeaders* headers = info.headers.get(); | 
|  | if (!headers) | 
|  | return; | 
|  |  | 
|  | WebURLResponse::HTTPVersion version = WebURLResponse::kHTTPVersionUnknown; | 
|  | if (headers->GetHttpVersion() == net::HttpVersion(0, 9)) | 
|  | version = WebURLResponse::kHTTPVersion_0_9; | 
|  | else if (headers->GetHttpVersion() == net::HttpVersion(1, 0)) | 
|  | version = WebURLResponse::kHTTPVersion_1_0; | 
|  | else if (headers->GetHttpVersion() == net::HttpVersion(1, 1)) | 
|  | version = WebURLResponse::kHTTPVersion_1_1; | 
|  | else if (headers->GetHttpVersion() == net::HttpVersion(2, 0)) | 
|  | version = WebURLResponse::kHTTPVersion_2_0; | 
|  | response->SetHttpVersion(version); | 
|  | response->SetHttpStatusCode(headers->response_code()); | 
|  | response->SetHttpStatusText(WebString::FromLatin1(headers->GetStatusText())); | 
|  |  | 
|  | // Build up the header map. | 
|  | size_t iter = 0; | 
|  | std::string name; | 
|  | std::string value; | 
|  | while (headers->EnumerateHeaderLines(&iter, &name, &value)) { | 
|  | response->AddHttpHeaderField(WebString::FromLatin1(name), | 
|  | WebString::FromLatin1(value)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | WebURLError WebURLLoaderImpl::PopulateURLError( | 
|  | const network::URLLoaderCompletionStatus& status, | 
|  | const GURL& url) { | 
|  | DCHECK_NE(net::OK, status.error_code); | 
|  | const WebURLError::HasCopyInCache has_copy_in_cache = | 
|  | status.exists_in_cache ? WebURLError::HasCopyInCache::kTrue | 
|  | : WebURLError::HasCopyInCache::kFalse; | 
|  | if (status.cors_error_status) | 
|  | return WebURLError(*status.cors_error_status, has_copy_in_cache, url); | 
|  | return WebURLError(status.error_code, status.extended_error_code, | 
|  | has_copy_in_cache, | 
|  | WebURLError::IsWebSecurityViolation::kFalse, url); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::LoadSynchronously( | 
|  | const WebURLRequest& request, | 
|  | WebURLLoaderClient* client, | 
|  | WebURLResponse& response, | 
|  | base::Optional<WebURLError>& error, | 
|  | WebData& data, | 
|  | int64_t& encoded_data_length, | 
|  | int64_t& encoded_body_length, | 
|  | blink::WebBlobInfo& downloaded_blob) { | 
|  | TRACE_EVENT0("loading", "WebURLLoaderImpl::loadSynchronously"); | 
|  | SyncLoadResponse sync_load_response; | 
|  |  | 
|  | DCHECK(!context_->client()); | 
|  | context_->set_client(client); | 
|  | context_->Start(request, &sync_load_response); | 
|  |  | 
|  | const GURL& final_url = sync_load_response.url; | 
|  |  | 
|  | // TODO(tc): For file loads, we may want to include a more descriptive | 
|  | // status code or status text. | 
|  | const int error_code = sync_load_response.error_code; | 
|  | if (error_code != net::OK) { | 
|  | if (sync_load_response.cors_error) { | 
|  | error = WebURLError(*sync_load_response.cors_error, | 
|  | WebURLError::HasCopyInCache::kFalse, final_url); | 
|  | } else { | 
|  | // SyncResourceHandler returns ERR_ABORTED for CORS redirect errors, | 
|  | // so we treat the error as a web security violation. | 
|  | const WebURLError::IsWebSecurityViolation is_web_security_violation = | 
|  | error_code == net::ERR_ABORTED | 
|  | ? WebURLError::IsWebSecurityViolation::kTrue | 
|  | : WebURLError::IsWebSecurityViolation::kFalse; | 
|  | error = WebURLError(error_code, sync_load_response.extended_error_code, | 
|  | WebURLError::HasCopyInCache::kFalse, | 
|  | is_web_security_violation, final_url); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | PopulateURLResponse(final_url, sync_load_response.info, &response, | 
|  | request.ReportRawHeaders(), context_->request_id()); | 
|  | encoded_data_length = sync_load_response.info.encoded_data_length; | 
|  | encoded_body_length = sync_load_response.info.encoded_body_length; | 
|  | if (sync_load_response.downloaded_blob) { | 
|  | downloaded_blob = blink::WebBlobInfo( | 
|  | WebString::FromLatin1(sync_load_response.downloaded_blob->uuid), | 
|  | WebString::FromLatin1(sync_load_response.downloaded_blob->content_type), | 
|  | sync_load_response.downloaded_blob->size, | 
|  | sync_load_response.downloaded_blob->blob.PassHandle()); | 
|  | } | 
|  |  | 
|  | data.Assign(sync_load_response.data.data(), sync_load_response.data.size()); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::LoadAsynchronously(const WebURLRequest& request, | 
|  | WebURLLoaderClient* client) { | 
|  | TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoaderImpl::loadAsynchronously", | 
|  | this, TRACE_EVENT_FLAG_FLOW_OUT); | 
|  | DCHECK(!context_->client()); | 
|  |  | 
|  | context_->set_client(client); | 
|  | context_->Start(request, nullptr); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::Cancel() { | 
|  | context_->Cancel(); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::SetDefersLoading(bool value) { | 
|  | context_->SetDefersLoading(value); | 
|  | } | 
|  |  | 
|  | void WebURLLoaderImpl::DidChangePriority(WebURLRequest::Priority new_priority, | 
|  | int intra_priority_value) { | 
|  | context_->DidChangePriority(new_priority, intra_priority_value); | 
|  | } | 
|  |  | 
|  | scoped_refptr<base::SingleThreadTaskRunner> WebURLLoaderImpl::GetTaskRunner() { | 
|  | return context_->task_runner(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | // We have this function at the bottom of this file because it confuses | 
|  | // syntax highliting. | 
|  | net::NetworkTrafficAnnotationTag | 
|  | WebURLLoaderImpl::Context::GetTrafficAnnotationTag( | 
|  | const blink::WebURLRequest& request) { | 
|  | switch (request.GetRequestContext()) { | 
|  | case blink::mojom::RequestContextType::UNSPECIFIED: | 
|  | case blink::mojom::RequestContextType::AUDIO: | 
|  | case blink::mojom::RequestContextType::BEACON: | 
|  | case blink::mojom::RequestContextType::CSP_REPORT: | 
|  | case blink::mojom::RequestContextType::DOWNLOAD: | 
|  | case blink::mojom::RequestContextType::EVENT_SOURCE: | 
|  | case blink::mojom::RequestContextType::FETCH: | 
|  | case blink::mojom::RequestContextType::FONT: | 
|  | case blink::mojom::RequestContextType::FORM: | 
|  | case blink::mojom::RequestContextType::FRAME: | 
|  | case blink::mojom::RequestContextType::HYPERLINK: | 
|  | case blink::mojom::RequestContextType::IFRAME: | 
|  | case blink::mojom::RequestContextType::IMAGE: | 
|  | case blink::mojom::RequestContextType::IMAGE_SET: | 
|  | case blink::mojom::RequestContextType::IMPORT: | 
|  | case blink::mojom::RequestContextType::INTERNAL: | 
|  | case blink::mojom::RequestContextType::LOCATION: | 
|  | case blink::mojom::RequestContextType::MANIFEST: | 
|  | case blink::mojom::RequestContextType::PING: | 
|  | case blink::mojom::RequestContextType::PREFETCH: | 
|  | case blink::mojom::RequestContextType::SCRIPT: | 
|  | case blink::mojom::RequestContextType::SERVICE_WORKER: | 
|  | case blink::mojom::RequestContextType::SHARED_WORKER: | 
|  | case blink::mojom::RequestContextType::SUBRESOURCE: | 
|  | case blink::mojom::RequestContextType::STYLE: | 
|  | case blink::mojom::RequestContextType::TRACK: | 
|  | case blink::mojom::RequestContextType::VIDEO: | 
|  | case blink::mojom::RequestContextType::WORKER: | 
|  | case blink::mojom::RequestContextType::XML_HTTP_REQUEST: | 
|  | case blink::mojom::RequestContextType::XSLT: | 
|  | return net::DefineNetworkTrafficAnnotation("blink_resource_loader", R"( | 
|  | semantics { | 
|  | sender: "Blink Resource Loader" | 
|  | description: | 
|  | "Blink-initiated request, which includes all resources for " | 
|  | "normal page loads, chrome URLs, and downloads." | 
|  | trigger: | 
|  | "The user navigates to a URL or downloads a file. Also when a " | 
|  | "webpage, ServiceWorker, or chrome:// uses any network communication." | 
|  | data: "Anything the initiator wants to send." | 
|  | destination: OTHER | 
|  | } | 
|  | policy { | 
|  | cookies_allowed: YES | 
|  | cookies_store: "user" | 
|  | setting: "These requests cannot be disabled in settings." | 
|  | policy_exception_justification: | 
|  | "Not implemented. Without these requests, Chrome will be unable " | 
|  | "to load any webpage." | 
|  | })"); | 
|  |  | 
|  | case blink::mojom::RequestContextType::EMBED: | 
|  | case blink::mojom::RequestContextType::OBJECT: | 
|  | case blink::mojom::RequestContextType::PLUGIN: | 
|  | return net::DefineNetworkTrafficAnnotation( | 
|  | "blink_extension_resource_loader", R"( | 
|  | semantics { | 
|  | sender: "Blink Resource Loader" | 
|  | description: | 
|  | "Blink-initiated request for resources required for NaCl instances " | 
|  | "tagged with <embed> or <object>, or installed extensions." | 
|  | trigger: | 
|  | "An extension or NaCl instance may initiate a request at any time, " | 
|  | "even in the background." | 
|  | data: "Anything the initiator wants to send." | 
|  | destination: OTHER | 
|  | } | 
|  | policy { | 
|  | cookies_allowed: YES | 
|  | cookies_store: "user" | 
|  | setting: | 
|  | "These requests cannot be disabled in settings, but they are " | 
|  | "sent only if user installs extensions." | 
|  | chrome_policy { | 
|  | ExtensionInstallBlacklist { | 
|  | ExtensionInstallBlacklist: { | 
|  | entries: '*' | 
|  | } | 
|  | } | 
|  | } | 
|  | })"); | 
|  |  | 
|  | case blink::mojom::RequestContextType::FAVICON: | 
|  | return net::DefineNetworkTrafficAnnotation("favicon_loader", R"( | 
|  | semantics { | 
|  | sender: "Blink Resource Loader" | 
|  | description: | 
|  | "Chrome sends a request to download favicon for a URL." | 
|  | trigger: | 
|  | "Navigating to a URL." | 
|  | data: "None." | 
|  | destination: WEBSITE | 
|  | } | 
|  | policy { | 
|  | cookies_allowed: YES | 
|  | cookies_store: "user" | 
|  | setting: "These requests cannot be disabled in settings." | 
|  | policy_exception_justification: | 
|  | "Not implemented." | 
|  | })"); | 
|  | } | 
|  |  | 
|  | return net::NetworkTrafficAnnotationTag::NotReached(); | 
|  | } | 
|  |  | 
|  | }  // namespace content |