1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/service_worker/navigation_preload_request.h"
#include <utility>
#include "base/task/thread_pool.h"
#include "net/http/http_response_headers.h"
#include "services/network/public/cpp/content_decoding_interceptor.h"
#include "services/network/public/cpp/record_ontransfersizeupdate_utils.h"
#include "services/network/public/mojom/early_hints.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom-blink.h"
#include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h"
namespace blink {
// static
std::unique_ptr<WebNavigationPreloadRequest>
WebNavigationPreloadRequest::Create(
WebServiceWorkerContextClient* owner,
int fetch_event_id,
const WebURL& url,
mojo::PendingReceiver<network::mojom::URLLoaderClient>
preload_url_loader_client_receiver) {
return std::make_unique<NavigationPreloadRequest>(
owner, fetch_event_id, url,
std::move(preload_url_loader_client_receiver));
}
NavigationPreloadRequest::NavigationPreloadRequest(
WebServiceWorkerContextClient* owner,
int fetch_event_id,
const WebURL& url,
mojo::PendingReceiver<network::mojom::URLLoaderClient>
preload_url_loader_client_receiver)
: owner_(owner),
fetch_event_id_(fetch_event_id),
url_(url),
receiver_(this, std::move(preload_url_loader_client_receiver)) {}
NavigationPreloadRequest::~NavigationPreloadRequest() = default;
void NavigationPreloadRequest::OnReceiveEarlyHints(
network::mojom::EarlyHintsPtr early_hints) {}
void NavigationPreloadRequest::OnReceiveResponse(
network::mojom::URLResponseHeadPtr response_head,
mojo::ScopedDataPipeConsumerHandle body,
std::optional<mojo_base::BigBuffer> cached_metadata) {
DCHECK(!response_);
if (!response_head->client_side_content_decoding_types.empty()) {
auto endpoints = network::mojom::URLLoaderClientEndpoints::New(
mojo::PendingRemote<network::mojom::URLLoader>(), receiver_.Unbind());
// Attempt to create the data pipe needed for content decoding.
auto data_pipe_pair =
network::ContentDecodingInterceptor::CreateDataPipePair(
network::ContentDecodingInterceptor::ClientType::
kNavigationPreload);
if (!data_pipe_pair) {
// If pipe creation fails, report an error and stop processing.
// This will cause the navigation preload fetch to fail.
OnComplete(
network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
return;
}
// If pipe creation succeeds, intercept the response to set up decoding.
network::ContentDecodingInterceptor::Intercept(
response_head->client_side_content_decoding_types, endpoints, body,
std::move(*data_pipe_pair),
base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_BLOCKING}));
decoder_loader_ = std::move(endpoints->url_loader);
receiver_.Bind(std::move(endpoints->url_loader_client));
}
response_ = std::make_unique<WebURLResponse>();
// TODO(horo): Set report_security_info to true when DevTools is attached.
const bool report_security_info = false;
*response_ = WebURLResponse::Create(
url_, *response_head, report_security_info, -1 /* request_id */);
body_ = std::move(body);
MaybeReportResponseToOwner();
}
void NavigationPreloadRequest::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr response_head) {
DCHECK(!response_);
DCHECK(net::HttpResponseHeaders::IsRedirectResponseCode(
response_head->headers->response_code()));
response_ = std::make_unique<WebURLResponse>();
*response_ = WebURLResponse::Create(url_, *response_head,
false /* report_security_info */,
-1 /* request_id */);
owner_->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
mojo::ScopedDataPipeConsumerHandle());
// This will delete |this|.
owner_->OnNavigationPreloadComplete(
fetch_event_id_, response_head->response_start,
response_head->encoded_data_length, 0 /* encoded_body_length */,
0 /* decoded_body_length */);
}
void NavigationPreloadRequest::OnUploadProgress(
int64_t current_position,
int64_t total_size,
OnUploadProgressCallback ack_callback) {
NOTREACHED();
}
void NavigationPreloadRequest::OnTransferSizeUpdated(
int32_t transfer_size_diff) {
network::RecordOnTransferSizeUpdatedUMA(
network::OnTransferSizeUpdatedFrom::kNavigationPreloadRequest);
}
void NavigationPreloadRequest::OnComplete(
const network::URLLoaderCompletionStatus& status) {
decoder_loader_.reset();
if (status.error_code != net::OK) {
WebString message;
WebServiceWorkerError::Mode error_mode = WebServiceWorkerError::Mode::kNone;
if (status.error_code == net::ERR_ABORTED) {
message =
"The service worker navigation preload request was cancelled "
"before 'preloadResponse' settled. If you intend to use "
"'preloadResponse', use waitUntil() or respondWith() to wait for "
"the promise to settle.";
error_mode = WebServiceWorkerError::Mode::kShownInConsole;
} else {
message =
"The service worker navigation preload request failed due to a "
"network error. This may have been an actual network error, or "
"caused by the browser simulating offline to see if the page works "
"offline: see https://w3c.github.io/manifest/#installability-signals";
}
// This will delete |this|.
ReportErrorToOwner(message, error_mode);
return;
}
if (response_) {
// When the response body from the server is empty, OnComplete() is called
// without OnStartLoadingResponseBody().
DCHECK(!body_.is_valid());
owner_->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
mojo::ScopedDataPipeConsumerHandle());
}
// This will delete |this|.
owner_->OnNavigationPreloadComplete(
fetch_event_id_, status.completion_time, status.encoded_data_length,
status.encoded_body_length, status.decoded_body_length);
}
void NavigationPreloadRequest::MaybeReportResponseToOwner() {
if (!response_ || !body_.is_valid())
return;
owner_->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
std::move(body_));
}
void NavigationPreloadRequest::ReportErrorToOwner(
const WebString& message,
WebServiceWorkerError::Mode error_mode) {
// This will delete |this|.
owner_->OnNavigationPreloadError(
fetch_event_id_,
std::make_unique<WebServiceWorkerError>(
mojom::blink::ServiceWorkerErrorType::kNetwork, message, error_mode));
}
} // namespace blink
|