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