Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(619)

Side by Side Diff: third_party/WebKit/Source/modules/serviceworkers/RespondWithObserver.cpp

Issue 2715663002: ServiceWorker: Factor out FetchEvent related logics from RespondWithObserver. (Closed)
Patch Set: RespondWithObserver Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "modules/serviceworkers/RespondWithObserver.h" 5 #include "modules/serviceworkers/RespondWithObserver.h"
6 6
7 #include <v8.h>
8
7 #include "bindings/core/v8/ScriptFunction.h" 9 #include "bindings/core/v8/ScriptFunction.h"
8 #include "bindings/core/v8/ScriptPromise.h" 10 #include "bindings/core/v8/ScriptPromise.h"
9 #include "bindings/core/v8/ScriptValue.h" 11 #include "bindings/core/v8/ScriptValue.h"
10 #include "bindings/core/v8/V8Binding.h" 12 #include "bindings/core/v8/V8Binding.h"
11 #include "bindings/modules/v8/V8Response.h"
12 #include "core/dom/ExceptionCode.h"
13 #include "core/dom/ExecutionContext.h" 13 #include "core/dom/ExecutionContext.h"
14 #include "core/inspector/ConsoleMessage.h" 14 #include "modules/serviceworkers/WaitUntilObserver.h"
15 #include "core/streams/Stream.h"
16 #include "modules/fetch/BodyStreamBuffer.h"
17 #include "modules/fetch/BytesConsumer.h"
18 #include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h"
19 #include "platform/RuntimeEnabledFeatures.h"
20 #include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h" 15 #include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h"
21 #include "v8/include/v8.h"
22 #include "wtf/Assertions.h"
23 #include "wtf/RefPtr.h"
24 16
25 namespace blink { 17 namespace blink {
26 namespace {
27
28 // Returns the error message to let the developer know about the reason of the
29 // unusual failures.
30 const String getMessageForResponseError(WebServiceWorkerResponseError error,
31 const KURL& requestURL) {
32 String errorMessage = "The FetchEvent for \"" + requestURL.getString() +
33 "\" resulted in a network error response: ";
34 switch (error) {
35 case WebServiceWorkerResponseErrorPromiseRejected:
36 errorMessage = errorMessage + "the promise was rejected.";
37 break;
38 case WebServiceWorkerResponseErrorDefaultPrevented:
39 errorMessage =
40 errorMessage +
41 "preventDefault() was called without calling respondWith().";
42 break;
43 case WebServiceWorkerResponseErrorNoV8Instance:
44 errorMessage =
45 errorMessage +
46 "an object that was not a Response was passed to respondWith().";
47 break;
48 case WebServiceWorkerResponseErrorResponseTypeError:
49 errorMessage = errorMessage +
50 "the promise was resolved with an error response object.";
51 break;
52 case WebServiceWorkerResponseErrorResponseTypeOpaque:
53 errorMessage = errorMessage +
54 "an \"opaque\" response was used for a request whose type "
55 "is not no-cors";
56 break;
57 case WebServiceWorkerResponseErrorResponseTypeNotBasicOrDefault:
58 ASSERT_NOT_REACHED();
59 break;
60 case WebServiceWorkerResponseErrorBodyUsed:
61 errorMessage = errorMessage +
62 "a Response whose \"bodyUsed\" is \"true\" cannot be used "
63 "to respond to a request.";
64 break;
65 case WebServiceWorkerResponseErrorResponseTypeOpaqueForClientRequest:
66 errorMessage = errorMessage +
67 "an \"opaque\" response was used for a client request.";
68 break;
69 case WebServiceWorkerResponseErrorResponseTypeOpaqueRedirect:
70 errorMessage = errorMessage +
71 "an \"opaqueredirect\" type response was used for a "
72 "request whose redirect mode is not \"manual\".";
73 break;
74 case WebServiceWorkerResponseErrorBodyLocked:
75 errorMessage = errorMessage +
76 "a Response whose \"body\" is locked cannot be used to "
77 "respond to a request.";
78 break;
79 case WebServiceWorkerResponseErrorNoForeignFetchResponse:
80 errorMessage = errorMessage +
81 "an object that was not a ForeignFetchResponse was passed "
82 "to respondWith().";
83 break;
84 case WebServiceWorkerResponseErrorForeignFetchHeadersWithoutOrigin:
85 errorMessage =
86 errorMessage +
87 "headers were specified for a response without an explicit origin.";
88 break;
89 case WebServiceWorkerResponseErrorForeignFetchMismatchedOrigin:
90 errorMessage =
91 errorMessage + "origin in response does not match origin of request.";
92 break;
93 case WebServiceWorkerResponseErrorRedirectedResponseForNotFollowRequest:
94 errorMessage = errorMessage +
95 "a redirected response was used for a request whose "
96 "redirect mode is not \"follow\".";
97 break;
98 case WebServiceWorkerResponseErrorUnknown:
99 default:
100 errorMessage = errorMessage + "an unexpected error occurred.";
101 break;
102 }
103 return errorMessage;
104 }
105
106 const String getErrorMessageForRedirectedResponseForNavigationRequest(
107 const KURL& requestURL,
108 const Vector<KURL>& responseURLList) {
109 String errorMessage =
110 "In Chrome 59, the navigation to \"" + requestURL.getString() + "\" " +
111 "will result in a network error, because FetchEvent.respondWith() was " +
112 "called with a redirected response. See https://crbug.com/658249. The " +
113 "url list of the response was: [\"" + responseURLList[0].getString() +
114 "\"";
115 for (size_t i = 1; i < responseURLList.size(); ++i) {
116 errorMessage =
117 errorMessage + ", \"" + responseURLList[i].getString() + "\"";
118 }
119 return errorMessage + "]";
120 }
121
122 bool isNavigationRequest(WebURLRequest::FrameType frameType) {
123 return frameType != WebURLRequest::FrameTypeNone;
124 }
125
126 bool isClientRequest(WebURLRequest::FrameType frameType,
127 WebURLRequest::RequestContext requestContext) {
128 return isNavigationRequest(frameType) ||
129 requestContext == WebURLRequest::RequestContextSharedWorker ||
130 requestContext == WebURLRequest::RequestContextWorker;
131 }
132
133 class NoopLoaderClient final
134 : public GarbageCollectedFinalized<NoopLoaderClient>,
135 public FetchDataLoader::Client {
136 WTF_MAKE_NONCOPYABLE(NoopLoaderClient);
137 USING_GARBAGE_COLLECTED_MIXIN(NoopLoaderClient);
138
139 public:
140 NoopLoaderClient() = default;
141 void didFetchDataLoadedStream() override {}
142 void didFetchDataLoadFailed() override {}
143 DEFINE_INLINE_TRACE() { FetchDataLoader::Client::trace(visitor); }
144 };
145
146 } // namespace
147 18
148 class RespondWithObserver::ThenFunction final : public ScriptFunction { 19 class RespondWithObserver::ThenFunction final : public ScriptFunction {
149 public: 20 public:
150 enum ResolveType { 21 enum ResolveType {
151 Fulfilled, 22 Fulfilled,
152 Rejected, 23 Rejected,
153 }; 24 };
154 25
155 static v8::Local<v8::Function> createFunction(ScriptState* scriptState, 26 static v8::Local<v8::Function> createFunction(ScriptState* scriptState,
156 RespondWithObserver* observer, 27 RespondWithObserver* observer,
(...skipping 29 matching lines...) Expand all
186 m_observer = nullptr; 57 m_observer = nullptr;
187 return value; 58 return value;
188 } 59 }
189 60
190 Member<RespondWithObserver> m_observer; 61 Member<RespondWithObserver> m_observer;
191 ResolveType m_resolveType; 62 ResolveType m_resolveType;
192 }; 63 };
193 64
194 RespondWithObserver::~RespondWithObserver() {} 65 RespondWithObserver::~RespondWithObserver() {}
195 66
196 RespondWithObserver* RespondWithObserver::create( 67 RespondWithObserver* RespondWithObserver::create(ExecutionContext* context,
197 ExecutionContext* context, 68 int eventID,
198 int fetchEventID, 69 WaitUntilObserver* observer) {
199 const KURL& requestURL, 70 return new RespondWithObserver(context, eventID, observer);
200 WebURLRequest::FetchRequestMode requestMode,
201 WebURLRequest::FetchRedirectMode redirectMode,
202 WebURLRequest::FrameType frameType,
203 WebURLRequest::RequestContext requestContext,
204 WaitUntilObserver* observer) {
205 return new RespondWithObserver(context, fetchEventID, requestURL, requestMode,
206 redirectMode, frameType, requestContext,
207 observer);
208 } 71 }
209 72
210 void RespondWithObserver::contextDestroyed(ExecutionContext*) { 73 void RespondWithObserver::contextDestroyed(ExecutionContext*) {
211 if (m_observer) { 74 if (m_observer) {
212 DCHECK_EQ(Pending, m_state); 75 DCHECK_EQ(Pending, m_state);
213 m_observer.clear(); 76 m_observer.clear();
214 } 77 }
215 m_state = Done; 78 m_state = Done;
216 } 79 }
217 80
218 void RespondWithObserver::willDispatchEvent() { 81 void RespondWithObserver::willDispatchEvent() {
219 m_eventDispatchTime = WTF::currentTime(); 82 m_eventDispatchTime = WTF::currentTime();
220 } 83 }
221 84
222 void RespondWithObserver::didDispatchEvent(DispatchEventResult dispatchResult) { 85 void RespondWithObserver::didDispatchEvent(DispatchEventResult dispatchResult) {
nhiroki 2017/03/08 01:49:04 How about removing line 86-95 like this? void Res
zino 2017/03/10 17:57:17 Anyway done. The didDispatchEvent() was removed in
223 ASSERT(getExecutionContext()); 86 ASSERT(getExecutionContext());
224 if (m_state != Initial) 87 if (m_state != Initial)
225 return; 88 return;
226 89
227 if (dispatchResult != DispatchEventResult::NotCanceled) { 90 if (dispatchResult != DispatchEventResult::NotCanceled) {
228 m_observer->incrementPendingActivity(); 91 m_observer->incrementPendingActivity();
229 responseWasRejected(WebServiceWorkerResponseErrorDefaultPrevented); 92 responseWasRejected(WebServiceWorkerResponseErrorDefaultPrevented);
230 return; 93 return;
231 } 94 }
232 95
233 ServiceWorkerGlobalScopeClient::from(getExecutionContext())
shimazu 2017/03/08 01:39:03 Could you add a virtual method to notify no respon
falken 2017/03/09 04:59:00 I like something like this. See also my comment be
zino 2017/03/10 17:57:17 Done.
234 ->respondToFetchEvent(m_fetchEventID, m_eventDispatchTime);
235 m_state = Done; 96 m_state = Done;
236 m_observer.clear(); 97 m_observer.clear();
237 } 98 }
238 99
239 void RespondWithObserver::respondWith(ScriptState* scriptState, 100 void RespondWithObserver::respondWith(ScriptState* scriptState,
240 ScriptPromise scriptPromise, 101 ScriptPromise scriptPromise,
241 ExceptionState& exceptionState) { 102 ExceptionState& exceptionState) {
242 if (m_state != Initial) { 103 if (m_state != Initial) {
243 exceptionState.throwDOMException( 104 exceptionState.throwDOMException(
244 InvalidStateError, "The fetch event has already been responded to."); 105 InvalidStateError, "The event has already been responded to.");
245 return; 106 return;
246 } 107 }
247 108
248 m_state = Pending; 109 m_state = Pending;
249 m_observer->incrementPendingActivity(); 110 m_observer->incrementPendingActivity();
250 scriptPromise.then( 111 scriptPromise.then(
251 ThenFunction::createFunction(scriptState, this, ThenFunction::Fulfilled), 112 ThenFunction::createFunction(scriptState, this, ThenFunction::Fulfilled),
252 ThenFunction::createFunction(scriptState, this, ThenFunction::Rejected)); 113 ThenFunction::createFunction(scriptState, this, ThenFunction::Rejected));
253 } 114 }
254 115
255 void RespondWithObserver::responseWasRejected( 116 void RespondWithObserver::responseWasRejected(
256 WebServiceWorkerResponseError error) { 117 WebServiceWorkerResponseError error) {
257 ASSERT(getExecutionContext());
258 getExecutionContext()->addConsoleMessage(
259 ConsoleMessage::create(JSMessageSource, WarningMessageLevel,
260 getMessageForResponseError(error, m_requestURL)));
261
262 // The default value of WebServiceWorkerResponse's status is 0, which maps
263 // to a network error.
264 WebServiceWorkerResponse webResponse;
265 webResponse.setError(error);
266 ServiceWorkerGlobalScopeClient::from(getExecutionContext())
267 ->respondToFetchEvent(m_fetchEventID, webResponse, m_eventDispatchTime);
268 m_state = Done; 118 m_state = Done;
269 m_observer->decrementPendingActivity(); 119 m_observer->decrementPendingActivity();
270 m_observer.clear(); 120 m_observer.clear();
falken 2017/03/09 04:59:00 It seems unfortunate that the subclass has to reme
zino 2017/03/10 17:57:17 Done.
271 } 121 }
272 122
273 void RespondWithObserver::responseWasFulfilled(const ScriptValue& value) { 123 void RespondWithObserver::responseWasFulfilled(const ScriptValue& value) {
274 ASSERT(getExecutionContext());
275 if (!V8Response::hasInstance(value.v8Value(),
276 toIsolate(getExecutionContext()))) {
277 responseWasRejected(WebServiceWorkerResponseErrorNoV8Instance);
278 return;
279 }
280 Response* response = V8Response::toImplWithTypeCheck(
281 toIsolate(getExecutionContext()), value.v8Value());
282 // "If one of the following conditions is true, return a network error:
283 // - |response|'s type is |error|.
284 // - |request|'s mode is not |no-cors| and response's type is |opaque|.
285 // - |request| is a client request and |response|'s type is neither
286 // |basic| nor |default|."
287 const FetchResponseData::Type responseType = response->response()->getType();
288 if (responseType == FetchResponseData::ErrorType) {
289 responseWasRejected(WebServiceWorkerResponseErrorResponseTypeError);
290 return;
291 }
292 if (responseType == FetchResponseData::OpaqueType) {
293 if (m_requestMode != WebURLRequest::FetchRequestModeNoCORS) {
294 responseWasRejected(WebServiceWorkerResponseErrorResponseTypeOpaque);
295 return;
296 }
297
298 // The request mode of client requests should be "same-origin" but it is
299 // not explicitly stated in the spec yet. So we need to check here.
300 // FIXME: Set the request mode of client requests to "same-origin" and
301 // remove this check when the spec will be updated.
302 // Spec issue: https://github.com/whatwg/fetch/issues/101
303 if (isClientRequest(m_frameType, m_requestContext)) {
304 responseWasRejected(
305 WebServiceWorkerResponseErrorResponseTypeOpaqueForClientRequest);
306 return;
307 }
308 }
309 if (m_redirectMode != WebURLRequest::FetchRedirectModeManual &&
310 responseType == FetchResponseData::OpaqueRedirectType) {
311 responseWasRejected(
312 WebServiceWorkerResponseErrorResponseTypeOpaqueRedirect);
313 return;
314 }
315 if (m_redirectMode != WebURLRequest::FetchRedirectModeFollow &&
316 response->redirected()) {
317 if (!isNavigationRequest(m_frameType)) {
318 responseWasRejected(
319 WebServiceWorkerResponseErrorRedirectedResponseForNotFollowRequest);
320 return;
321 }
322 // TODO(horo): We should just reject even if the request was a navigation.
323 // Currently we measure the impact of the restriction with the use counter
324 // in DocumentLoader.
325 getExecutionContext()->addConsoleMessage(ConsoleMessage::create(
326 JSMessageSource, ErrorMessageLevel,
327 getErrorMessageForRedirectedResponseForNavigationRequest(
328 m_requestURL, response->internalURLList())));
329 }
330 if (response->isBodyLocked()) {
331 responseWasRejected(WebServiceWorkerResponseErrorBodyLocked);
332 return;
333 }
334 if (response->bodyUsed()) {
335 responseWasRejected(WebServiceWorkerResponseErrorBodyUsed);
336 return;
337 }
338
339 WebServiceWorkerResponse webResponse;
340 response->populateWebServiceWorkerResponse(webResponse);
341 BodyStreamBuffer* buffer = response->internalBodyBuffer();
342 if (buffer) {
343 RefPtr<BlobDataHandle> blobDataHandle = buffer->drainAsBlobDataHandle(
344 BytesConsumer::BlobSizePolicy::AllowBlobWithInvalidSize);
345 if (blobDataHandle) {
346 webResponse.setBlobDataHandle(blobDataHandle);
347 } else {
348 Stream* outStream = Stream::create(getExecutionContext(), "");
349 webResponse.setStreamURL(outStream->url());
350 buffer->startLoading(FetchDataLoader::createLoaderAsStream(outStream),
351 new NoopLoaderClient);
352 }
353 }
354 ServiceWorkerGlobalScopeClient::from(getExecutionContext())
355 ->respondToFetchEvent(m_fetchEventID, webResponse, m_eventDispatchTime);
356 m_state = Done; 124 m_state = Done;
357 m_observer->decrementPendingActivity(); 125 m_observer->decrementPendingActivity();
358 m_observer.clear(); 126 m_observer.clear();
359 } 127 }
360 128
361 RespondWithObserver::RespondWithObserver( 129 RespondWithObserver::RespondWithObserver(ExecutionContext* context,
362 ExecutionContext* context, 130 int eventID,
363 int fetchEventID, 131 WaitUntilObserver* observer)
364 const KURL& requestURL,
365 WebURLRequest::FetchRequestMode requestMode,
366 WebURLRequest::FetchRedirectMode redirectMode,
367 WebURLRequest::FrameType frameType,
368 WebURLRequest::RequestContext requestContext,
369 WaitUntilObserver* observer)
370 : ContextLifecycleObserver(context), 132 : ContextLifecycleObserver(context),
371 m_fetchEventID(fetchEventID), 133 m_eventID(eventID),
372 m_requestURL(requestURL),
373 m_requestMode(requestMode),
374 m_redirectMode(redirectMode),
375 m_frameType(frameType),
376 m_requestContext(requestContext),
377 m_state(Initial), 134 m_state(Initial),
378 m_observer(observer) {} 135 m_observer(observer) {}
379 136
380 DEFINE_TRACE(RespondWithObserver) { 137 DEFINE_TRACE(RespondWithObserver) {
381 visitor->trace(m_observer); 138 visitor->trace(m_observer);
382 ContextLifecycleObserver::trace(visitor); 139 ContextLifecycleObserver::trace(visitor);
383 } 140 }
384 141
385 } // namespace blink 142 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698