1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <utility>
6
7 #include "third_party/blink/renderer/modules/service_worker/fetch_event.h"
8
9 #include "base/memory/scoped_refptr.h"
10 #include "third_party/blink/public/mojom/timing/performance_mark_or_measure.mojom-blink.h"
11 #include "third_party/blink/public/mojom/timing/worker_timing_container.mojom-blink.h"
12 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
13 #include "third_party/blink/public/platform/web_url_response.h"
14 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
15 #include "third_party/blink/renderer/core/dom/abort_signal.h"
16 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
17 #include "third_party/blink/renderer/core/fetch/request.h"
18 #include "third_party/blink/renderer/core/fetch/response.h"
19 #include "third_party/blink/renderer/core/timing/performance_mark.h"
20 #include "third_party/blink/renderer/core/timing/performance_measure.h"
21 #include "third_party/blink/renderer/core/timing/worker_global_scope_performance.h"
22 #include "third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.h"
23 #include "third_party/blink/renderer/modules/service_worker/service_worker_error.h"
24 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
25 #include "third_party/blink/renderer/platform/bindings/script_state.h"
26 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
27 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
28 #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
29 #include "third_party/blink/renderer/platform/network/http_names.h"
30 #include "third_party/blink/renderer/platform/network/network_utils.h"
31
32 namespace blink {
33
Create(ScriptState * script_state,const AtomicString & type,const FetchEventInit * initializer)34 FetchEvent* FetchEvent::Create(ScriptState* script_state,
35 const AtomicString& type,
36 const FetchEventInit* initializer) {
37 return MakeGarbageCollected<FetchEvent>(script_state, type, initializer,
38 nullptr, nullptr, mojo::NullRemote(),
39 false);
40 }
41
request() const42 Request* FetchEvent::request() const {
43 return request_;
44 }
45
clientId() const46 String FetchEvent::clientId() const {
47 return client_id_;
48 }
49
resultingClientId() const50 String FetchEvent::resultingClientId() const {
51 return resulting_client_id_;
52 }
53
isReload() const54 bool FetchEvent::isReload() const {
55 UseCounter::Count(GetExecutionContext(), WebFeature::kFetchEventIsReload);
56 return is_reload_;
57 }
58
respondWith(ScriptState * script_state,ScriptPromise script_promise,ExceptionState & exception_state)59 void FetchEvent::respondWith(ScriptState* script_state,
60 ScriptPromise script_promise,
61 ExceptionState& exception_state) {
62 stopImmediatePropagation();
63 if (observer_)
64 observer_->RespondWith(script_state, script_promise, exception_state);
65 }
66
preloadResponse(ScriptState * script_state)67 ScriptPromise FetchEvent::preloadResponse(ScriptState* script_state) {
68 return preload_response_property_->Promise(script_state->World());
69 }
70
handled(ScriptState * script_state)71 ScriptPromise FetchEvent::handled(ScriptState* script_state) {
72 return handled_property_->Promise(script_state->World());
73 }
74
ResolveHandledPromise()75 void FetchEvent::ResolveHandledPromise() {
76 handled_property_->ResolveWithUndefined();
77 }
78
RejectHandledPromise(const String & error_message)79 void FetchEvent::RejectHandledPromise(const String& error_message) {
80 handled_property_->Reject(ServiceWorkerError::GetException(
81 nullptr, mojom::blink::ServiceWorkerErrorType::kNetwork, error_message));
82 }
83
InterfaceName() const84 const AtomicString& FetchEvent::InterfaceName() const {
85 return event_interface_names::kFetchEvent;
86 }
87
HasPendingActivity() const88 bool FetchEvent::HasPendingActivity() const {
89 // Prevent V8 from garbage collecting the wrapper object while waiting for the
90 // preload response. This is in order to keep the resolver of preloadResponse
91 // Promise alive. Note that |preload_response_property_| can be nullptr as
92 // GC can run while running the FetchEvent constructor, before the member is
93 // set. If it isn't set we treat it as a pending state.
94 return !preload_response_property_ ||
95 preload_response_property_->GetState() ==
96 PreloadResponseProperty::kPending;
97 }
98
FetchEvent(ScriptState * script_state,const AtomicString & type,const FetchEventInit * initializer,FetchRespondWithObserver * respond_with_observer,WaitUntilObserver * wait_until_observer,mojo::PendingRemote<mojom::blink::WorkerTimingContainer> worker_timing_remote,bool navigation_preload_sent)99 FetchEvent::FetchEvent(ScriptState* script_state,
100 const AtomicString& type,
101 const FetchEventInit* initializer,
102 FetchRespondWithObserver* respond_with_observer,
103 WaitUntilObserver* wait_until_observer,
104 mojo::PendingRemote<mojom::blink::WorkerTimingContainer>
105 worker_timing_remote,
106 bool navigation_preload_sent)
107 : ExtendableEvent(type, initializer, wait_until_observer),
108 ExecutionContextClient(ExecutionContext::From(script_state)),
109 observer_(respond_with_observer),
110 preload_response_property_(MakeGarbageCollected<PreloadResponseProperty>(
111 ExecutionContext::From(script_state))),
112 handled_property_(
113 MakeGarbageCollected<ScriptPromiseProperty<ToV8UndefinedGenerator,
114 Member<DOMException>>>(
115 ExecutionContext::From(script_state))),
116 worker_timing_remote_(ExecutionContext::From(script_state)) {
117 worker_timing_remote_.Bind(std::move(worker_timing_remote),
118 ExecutionContext::From(script_state)
119 ->GetTaskRunner(TaskType::kNetworking));
120 if (!navigation_preload_sent)
121 preload_response_property_->ResolveWithUndefined();
122
123 client_id_ = initializer->clientId();
124 resulting_client_id_ = initializer->resultingClientId();
125 is_reload_ = initializer->isReload();
126 request_ = initializer->request();
127 }
128
129 FetchEvent::~FetchEvent() = default;
130
OnNavigationPreloadResponse(ScriptState * script_state,std::unique_ptr<WebURLResponse> response,mojo::ScopedDataPipeConsumerHandle data_pipe)131 void FetchEvent::OnNavigationPreloadResponse(
132 ScriptState* script_state,
133 std::unique_ptr<WebURLResponse> response,
134 mojo::ScopedDataPipeConsumerHandle data_pipe) {
135 if (!script_state->ContextIsValid())
136 return;
137 DCHECK(preload_response_property_);
138 DCHECK(!preload_response_);
139 ScriptState::Scope scope(script_state);
140 preload_response_ = std::move(response);
141 DataPipeBytesConsumer* bytes_consumer = nullptr;
142 if (data_pipe.is_valid()) {
143 DataPipeBytesConsumer::CompletionNotifier* completion_notifier = nullptr;
144 bytes_consumer = MakeGarbageCollected<DataPipeBytesConsumer>(
145 ExecutionContext::From(script_state)
146 ->GetTaskRunner(TaskType::kNetworking),
147 std::move(data_pipe), &completion_notifier);
148 body_completion_notifier_ = completion_notifier;
149 }
150 // TODO(ricea): Verify that this response can't be aborted from JS.
151 FetchResponseData* response_data =
152 bytes_consumer
153 ? FetchResponseData::CreateWithBuffer(BodyStreamBuffer::Create(
154 script_state, bytes_consumer,
155 MakeGarbageCollected<AbortSignal>(
156 ExecutionContext::From(script_state))))
157 : FetchResponseData::Create();
158 Vector<KURL> url_list(1);
159 url_list[0] = preload_response_->CurrentRequestUrl();
160
161 response_data->InitFromResourceResponse(
162 url_list, http_names::kGET, network::mojom::CredentialsMode::kInclude,
163 FetchRequestData::kBasicTainting,
164 preload_response_->ToResourceResponse());
165
166 FetchResponseData* tainted_response =
167 network_utils::IsRedirectResponseCode(preload_response_->HttpStatusCode())
168 ? response_data->CreateOpaqueRedirectFilteredResponse()
169 : response_data->CreateBasicFilteredResponse();
170 preload_response_property_->Resolve(
171 Response::Create(ExecutionContext::From(script_state), tainted_response));
172 }
173
OnNavigationPreloadError(ScriptState * script_state,std::unique_ptr<WebServiceWorkerError> error)174 void FetchEvent::OnNavigationPreloadError(
175 ScriptState* script_state,
176 std::unique_ptr<WebServiceWorkerError> error) {
177 if (!script_state->ContextIsValid())
178 return;
179 if (body_completion_notifier_) {
180 body_completion_notifier_->SignalError(BytesConsumer::Error());
181 body_completion_notifier_ = nullptr;
182 }
183 DCHECK(preload_response_property_);
184 if (preload_response_property_->GetState() !=
185 PreloadResponseProperty::kPending) {
186 return;
187 }
188 preload_response_property_->Reject(
189 ServiceWorkerError::Take(nullptr, *error.get()));
190 }
191
OnNavigationPreloadComplete(WorkerGlobalScope * worker_global_scope,base::TimeTicks completion_time,int64_t encoded_data_length,int64_t encoded_body_length,int64_t decoded_body_length)192 void FetchEvent::OnNavigationPreloadComplete(
193 WorkerGlobalScope* worker_global_scope,
194 base::TimeTicks completion_time,
195 int64_t encoded_data_length,
196 int64_t encoded_body_length,
197 int64_t decoded_body_length) {
198 DCHECK(preload_response_);
199 if (body_completion_notifier_) {
200 body_completion_notifier_->SignalComplete();
201 body_completion_notifier_ = nullptr;
202 }
203 std::unique_ptr<WebURLResponse> response = std::move(preload_response_);
204 ResourceResponse resource_response = response->ToResourceResponse();
205 resource_response.SetEncodedDataLength(encoded_data_length);
206 resource_response.SetEncodedBodyLength(encoded_body_length);
207 resource_response.SetDecodedBodyLength(decoded_body_length);
208
209 ResourceLoadTiming* timing = resource_response.GetResourceLoadTiming();
210 // |timing| can be null, see https://crbug.com/817691.
211 base::TimeTicks request_time =
212 timing ? timing->RequestTime() : base::TimeTicks();
213 // According to the Resource Timing spec, the initiator type of
214 // navigation preload request is "navigation".
215 scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
216 "navigation", request_time, request_->GetRequestContextType(),
217 request_->GetRequestDestination());
218 info->SetNegativeAllowed(true);
219 info->SetLoadResponseEnd(completion_time);
220 info->SetInitialURL(request_->url());
221 info->SetFinalResponse(resource_response);
222 info->AddFinalTransferSize(encoded_data_length == -1 ? 0
223 : encoded_data_length);
224 WorkerGlobalScopePerformance::performance(*worker_global_scope)
225 ->GenerateAndAddResourceTiming(*info);
226 }
227
addPerformanceEntry(PerformanceMark * performance_mark)228 void FetchEvent::addPerformanceEntry(PerformanceMark* performance_mark) {
229 if (worker_timing_remote_.is_bound()) {
230 auto mojo_performance_mark =
231 performance_mark->ToMojoPerformanceMarkOrMeasure();
232 worker_timing_remote_->AddPerformanceEntry(
233 std::move(mojo_performance_mark));
234 }
235 }
236
addPerformanceEntry(PerformanceMeasure * performance_measure)237 void FetchEvent::addPerformanceEntry(PerformanceMeasure* performance_measure) {
238 if (worker_timing_remote_.is_bound()) {
239 auto mojo_performance_measure =
240 performance_measure->ToMojoPerformanceMarkOrMeasure();
241 worker_timing_remote_->AddPerformanceEntry(
242 std::move(mojo_performance_measure));
243 }
244 }
245
Trace(Visitor * visitor) const246 void FetchEvent::Trace(Visitor* visitor) const {
247 visitor->Trace(observer_);
248 visitor->Trace(request_);
249 visitor->Trace(preload_response_property_);
250 visitor->Trace(body_completion_notifier_);
251 visitor->Trace(handled_property_);
252 visitor->Trace(worker_timing_remote_);
253 ExtendableEvent::Trace(visitor);
254 ExecutionContextClient::Trace(visitor);
255 }
256
257 } // namespace blink
258