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

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

Issue 2877543003: [ServiceWorker] Allow waitUntil to be called multiple times asynchronously (Closed)
Patch Set: Let RespondWithObserver call WaitUntilObserver::WaitUntil to add extend lifetime promise Created 3 years, 7 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/WaitUntilObserver.h" 5 #include "modules/serviceworkers/WaitUntilObserver.h"
6 6
7 #include "bindings/core/v8/ScriptFunction.h" 7 #include "bindings/core/v8/ScriptFunction.h"
8 #include "bindings/core/v8/ScriptPromise.h" 8 #include "bindings/core/v8/ScriptPromise.h"
9 #include "bindings/core/v8/ScriptValue.h" 9 #include "bindings/core/v8/ScriptValue.h"
10 #include "bindings/core/v8/V8BindingForCore.h" 10 #include "bindings/core/v8/V8BindingForCore.h"
11 #include "core/dom/ExceptionCode.h" 11 #include "core/dom/ExceptionCode.h"
12 #include "core/dom/ExecutionContext.h" 12 #include "core/dom/ExecutionContext.h"
13 #include "modules/serviceworkers/ServiceWorkerGlobalScope.h" 13 #include "modules/serviceworkers/ServiceWorkerGlobalScope.h"
14 #include "platform/LayoutTestSupport.h" 14 #include "platform/LayoutTestSupport.h"
15 #include "platform/bindings/Microtask.h"
15 #include "platform/wtf/Assertions.h" 16 #include "platform/wtf/Assertions.h"
17 #include "platform/wtf/Functional.h"
16 #include "public/platform/Platform.h" 18 #include "public/platform/Platform.h"
17 #include "public/platform/modules/serviceworker/WebServiceWorkerEventResult.h" 19 #include "public/platform/modules/serviceworker/WebServiceWorkerEventResult.h"
18 #include "v8/include/v8.h" 20 #include "v8/include/v8.h"
19 21
20 namespace blink { 22 namespace blink {
21 23
22 namespace { 24 namespace {
23 25
24 // Timeout before a service worker that was given window interaction 26 // Timeout before a service worker that was given window interaction
25 // permission loses them. The unit is seconds. 27 // permission loses them. The unit is seconds.
26 const unsigned kWindowInteractionTimeout = 10; 28 const unsigned kWindowInteractionTimeout = 10;
27 const unsigned kWindowInteractionTimeoutForTest = 1; 29 const unsigned kWindowInteractionTimeoutForTest = 1;
28 30
29 unsigned WindowInteractionTimeout() { 31 unsigned WindowInteractionTimeout() {
30 return LayoutTestSupport::IsRunningLayoutTest() 32 return LayoutTestSupport::IsRunningLayoutTest()
31 ? kWindowInteractionTimeoutForTest 33 ? kWindowInteractionTimeoutForTest
32 : kWindowInteractionTimeout; 34 : kWindowInteractionTimeout;
33 } 35 }
34 36
35 } // anonymous namespace 37 } // anonymous namespace
36 38
37 class WaitUntilObserver::ThenFunction final : public ScriptFunction { 39 class WaitUntilObserver::ThenFunction final : public ScriptFunction {
38 public: 40 public:
39 enum ResolveType { 41 enum ResolveType {
40 kFulfilled, 42 kFulfilled,
41 kRejected, 43 kRejected,
42 }; 44 };
43 45
44 static v8::Local<v8::Function> CreateFunction(ScriptState* script_state, 46 static v8::Local<v8::Function> CreateFunction(
45 WaitUntilObserver* observer, 47 ScriptState* script_state,
46 ResolveType type) { 48 WaitUntilObserver* observer,
47 ThenFunction* self = new ThenFunction(script_state, observer, type); 49 ResolveType type,
50 std::unique_ptr<PromiseSettledCallback> callback) {
51 ThenFunction* self =
52 new ThenFunction(script_state, observer, type, std::move(callback));
48 return self->BindToV8Function(); 53 return self->BindToV8Function();
49 } 54 }
50 55
51 DEFINE_INLINE_VIRTUAL_TRACE() { 56 DEFINE_INLINE_VIRTUAL_TRACE() {
52 visitor->Trace(observer_); 57 visitor->Trace(observer_);
53 ScriptFunction::Trace(visitor); 58 ScriptFunction::Trace(visitor);
54 } 59 }
55 60
56 private: 61 private:
57 ThenFunction(ScriptState* script_state, 62 ThenFunction(ScriptState* script_state,
58 WaitUntilObserver* observer, 63 WaitUntilObserver* observer,
59 ResolveType type) 64 ResolveType type,
65 std::unique_ptr<PromiseSettledCallback> callback)
60 : ScriptFunction(script_state), 66 : ScriptFunction(script_state),
61 observer_(observer), 67 observer_(observer),
62 resolve_type_(type) {} 68 resolve_type_(type),
69 callback_(std::move(callback)) {}
63 70
64 ScriptValue Call(ScriptValue value) override { 71 ScriptValue Call(ScriptValue value) override {
65 DCHECK(observer_); 72 DCHECK(observer_);
66 DCHECK(resolve_type_ == kFulfilled || resolve_type_ == kRejected); 73 DCHECK(resolve_type_ == kFulfilled || resolve_type_ == kRejected);
74 if (callback_)
75 (*callback_)(value);
76 // According from step 4 of ExtendableEvent::waitUntil() in spec:
77 // https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
78 // "Upon fulfillment or rejection of f, queue a microtask to run these
79 // substeps: Decrement the pending promises count by one."
falken 2017/05/25 07:23:24 add an empty // line after this for easy reading
leonhsl(Using Gerrit) 2017/05/25 10:11:36 Done.
80 // At this time point the microtask A running resolve/reject function of
81 // this promise has already been queued, in order to allow microtask A to
82 // call waitUntil, we enqueue another microtask B to delay the promise
83 // settled notification to |observer_|, thus A will run before B so A can
84 // call waitUntil well, but any other microtask C possibly enqueued by A
85 // will run after B so C maybe can't call waitUntil if there has no any
86 // extend lifetime promise at that time.
67 if (resolve_type_ == kRejected) { 87 if (resolve_type_ == kRejected) {
68 observer_->OnPromiseRejected(); 88 Microtask::EnqueueMicrotask(
89 WTF::Bind(&WaitUntilObserver::OnPromiseRejected,
90 WrapPersistent(observer_.Get())));
69 value = 91 value =
70 ScriptPromise::Reject(value.GetScriptState(), value).GetScriptValue(); 92 ScriptPromise::Reject(value.GetScriptState(), value).GetScriptValue();
71 } else { 93 } else {
72 observer_->OnPromiseFulfilled(); 94 Microtask::EnqueueMicrotask(
95 WTF::Bind(&WaitUntilObserver::OnPromiseFulfilled,
96 WrapPersistent(observer_.Get())));
73 } 97 }
74 observer_ = nullptr; 98 observer_ = nullptr;
75 return value; 99 return value;
76 } 100 }
77 101
78 Member<WaitUntilObserver> observer_; 102 Member<WaitUntilObserver> observer_;
79 ResolveType resolve_type_; 103 ResolveType resolve_type_;
104 std::unique_ptr<PromiseSettledCallback> callback_;
80 }; 105 };
81 106
82 WaitUntilObserver* WaitUntilObserver::Create(ExecutionContext* context, 107 WaitUntilObserver* WaitUntilObserver::Create(ExecutionContext* context,
83 EventType type, 108 EventType type,
84 int event_id) { 109 int event_id) {
85 return new WaitUntilObserver(context, type, event_id); 110 return new WaitUntilObserver(context, type, event_id);
86 } 111 }
87 112
88 void WaitUntilObserver::WillDispatchEvent() { 113 void WaitUntilObserver::WillDispatchEvent() {
89 event_dispatch_time_ = WTF::CurrentTime(); 114 event_dispatch_time_ = WTF::CurrentTime();
90 // When handling a notificationclick or paymentrequest event, we want to 115 // When handling a notificationclick or paymentrequest event, we want to
91 // allow one window to be focused or opened. These calls are allowed between 116 // allow one window to be focused or opened. These calls are allowed between
92 // the call to willDispatchEvent() and the last call to 117 // the call to willDispatchEvent() and the last call to
93 // decrementPendingActivity(). If waitUntil() isn't called, that means 118 // DecrementPendingPromise(). If waitUntil() isn't called, that means
94 // between willDispatchEvent() and didDispatchEvent(). 119 // between willDispatchEvent() and didDispatchEvent().
95 if (type_ == kNotificationClick || type_ == kPaymentRequest) 120 if (type_ == kNotificationClick || type_ == kPaymentRequest)
96 execution_context_->AllowWindowInteraction(); 121 execution_context_->AllowWindowInteraction();
97 122
98 IncrementPendingActivity(); 123 DCHECK_EQ(EventDispatchState::kInitial, event_dispatch_state_);
124 event_dispatch_state_ = EventDispatchState::kDispatching;
99 } 125 }
100 126
101 void WaitUntilObserver::DidDispatchEvent(bool event_dispatch_failed) { 127 void WaitUntilObserver::DidDispatchEvent(bool event_dispatch_failed) {
102 event_dispatch_state_ = event_dispatch_failed 128 event_dispatch_state_ = event_dispatch_failed
103 ? EventDispatchState::kFailed 129 ? EventDispatchState::kFailed
104 : EventDispatchState::kCompleted; 130 : EventDispatchState::kDispatched;
105 DecrementPendingActivity(); 131 MaybeCompleteEvent();
106 } 132 }
107 133
108 void WaitUntilObserver::WaitUntil(ScriptState* script_state, 134 void WaitUntilObserver::WaitUntil(
109 ScriptPromise script_promise, 135 ScriptState* script_state,
110 ExceptionState& exception_state) { 136 ScriptPromise script_promise,
111 if (event_dispatch_state_ != EventDispatchState::kInitial) { 137 ExceptionState& exception_state,
112 exception_state.ThrowDOMException(kInvalidStateError, 138 std::unique_ptr<PromiseSettledCallback> on_promise_fulfilled,
113 "The event handler is already finished."); 139 std::unique_ptr<PromiseSettledCallback> on_promise_rejected) {
114 return; 140 if (pending_promises_ == 0) {
141 switch (event_dispatch_state_) {
142 case EventDispatchState::kInitial:
143 NOTREACHED();
144 return;
145 case EventDispatchState::kDispatching:
146 if (!v8::MicrotasksScope::IsRunningMicrotasks(
147 script_state->GetIsolate())) {
148 break;
149 }
150 // Fall through:
151 // didDispatchEvent() is called after both the event handler
152 // execution finished and microtasks queued by the event handler execution
153 // finished, it's hard to get the precise time point between the 2
154 // execution phases.
155 // So even in EventDispatchState::kDispatching state at this time point,
156 // running microtask indicates that event handler execution has actually
157 // finished, in such case if there aren't any outstanding extend lifetime
158 // promises, we should throw here.
159 case EventDispatchState::kDispatched:
160 case EventDispatchState::kFailed:
161 exception_state.ThrowDOMException(
162 kInvalidStateError,
163 "The event handler is already finished "
164 "and no any extend lifetime promises are "
falken 2017/05/25 07:23:23 s/no any/no/
leonhsl(Using Gerrit) 2017/05/25 10:11:36 Done.
165 "outstanding.");
166 return;
167 }
115 } 168 }
116 169
117 if (!execution_context_) 170 if (!execution_context_)
118 return; 171 return;
119 172
120 // When handling a notificationclick event, we want to allow one window to 173 // When handling a notificationclick event, we want to allow one window to
121 // be focused or opened. See comments in ::willDispatchEvent(). When 174 // be focused or opened. See comments in ::willDispatchEvent(). When
122 // waitUntil() is being used, opening or closing a window must happen in a 175 // waitUntil() is being used, opening or closing a window must happen in a
123 // timeframe specified by windowInteractionTimeout(), otherwise the calls 176 // timeframe specified by windowInteractionTimeout(), otherwise the calls
124 // will fail. 177 // will fail.
125 if (type_ == kNotificationClick) 178 if (type_ == kNotificationClick)
126 consume_window_interaction_timer_.StartOneShot(WindowInteractionTimeout(), 179 consume_window_interaction_timer_.StartOneShot(WindowInteractionTimeout(),
127 BLINK_FROM_HERE); 180 BLINK_FROM_HERE);
128 181
129 IncrementPendingActivity(); 182 IncrementPendingPromise();
130 script_promise.Then(ThenFunction::CreateFunction(script_state, this, 183 script_promise.Then(
131 ThenFunction::kFulfilled), 184 ThenFunction::CreateFunction(script_state, this, ThenFunction::kFulfilled,
132 ThenFunction::CreateFunction(script_state, this, 185 std::move(on_promise_fulfilled)),
133 ThenFunction::kRejected)); 186 ThenFunction::CreateFunction(script_state, this, ThenFunction::kRejected,
187 std::move(on_promise_rejected)));
134 } 188 }
135 189
136 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context, 190 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context,
137 EventType type, 191 EventType type,
138 int event_id) 192 int event_id)
139 : execution_context_(context), 193 : execution_context_(context),
140 type_(type), 194 type_(type),
141 event_id_(event_id), 195 event_id_(event_id),
142 consume_window_interaction_timer_( 196 consume_window_interaction_timer_(
143 Platform::Current()->CurrentThread()->GetWebTaskRunner(), 197 Platform::Current()->CurrentThread()->GetWebTaskRunner(),
144 this, 198 this,
145 &WaitUntilObserver::ConsumeWindowInteraction) {} 199 &WaitUntilObserver::ConsumeWindowInteraction) {}
146 200
147 void WaitUntilObserver::OnPromiseFulfilled() { 201 void WaitUntilObserver::OnPromiseFulfilled() {
148 DecrementPendingActivity(); 202 DecrementPendingPromise();
149 } 203 }
150 204
151 void WaitUntilObserver::OnPromiseRejected() { 205 void WaitUntilObserver::OnPromiseRejected() {
152 has_rejected_promise_ = true; 206 has_rejected_promise_ = true;
153 DecrementPendingActivity(); 207 DecrementPendingPromise();
154 } 208 }
155 209
156 void WaitUntilObserver::IncrementPendingActivity() { 210 void WaitUntilObserver::IncrementPendingPromise() {
157 ++pending_activity_; 211 ++pending_promises_;
158 } 212 }
159 213
160 void WaitUntilObserver::DecrementPendingActivity() { 214 void WaitUntilObserver::DecrementPendingPromise() {
161 DCHECK_GT(pending_activity_, 0); 215 DCHECK_GT(pending_promises_, 0);
162 if (!execution_context_ || 216 --pending_promises_;
163 (event_dispatch_state_ != EventDispatchState::kFailed && 217 MaybeCompleteEvent();
164 --pending_activity_)) 218 }
219
220 void WaitUntilObserver::MaybeCompleteEvent() {
221 if (!execution_context_)
165 return; 222 return;
166 223
224 switch (event_dispatch_state_) {
225 case EventDispatchState::kInitial:
226 NOTREACHED();
227 return;
228 case EventDispatchState::kDispatching:
229 // Still dispatching, do not complete the event.
230 return;
231 case EventDispatchState::kDispatched:
232 // Still waiting for a promise, do not complete the event.
233 if (pending_promises_ != 0)
234 return;
235 // Dispatch finished and there are no pending promises, complete the
236 // event.
237 break;
238 case EventDispatchState::kFailed:
239 // Dispatch had some error, complete the event immediatelly.
240 break;
241 }
242
167 ServiceWorkerGlobalScopeClient* client = 243 ServiceWorkerGlobalScopeClient* client =
168 ServiceWorkerGlobalScopeClient::From(execution_context_); 244 ServiceWorkerGlobalScopeClient::From(execution_context_);
169 WebServiceWorkerEventResult result = 245 WebServiceWorkerEventResult result =
170 (event_dispatch_state_ == EventDispatchState::kFailed || 246 (event_dispatch_state_ == EventDispatchState::kFailed ||
171 has_rejected_promise_) 247 has_rejected_promise_)
172 ? kWebServiceWorkerEventResultRejected 248 ? kWebServiceWorkerEventResultRejected
173 : kWebServiceWorkerEventResultCompleted; 249 : kWebServiceWorkerEventResultCompleted;
174 switch (type_) { 250 switch (type_) {
175 case kActivate: 251 case kActivate:
176 client->DidHandleActivateEvent(event_id_, result, event_dispatch_time_); 252 client->DidHandleActivateEvent(event_id_, result, event_dispatch_time_);
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 if (!execution_context_) 305 if (!execution_context_)
230 return; 306 return;
231 execution_context_->ConsumeWindowInteraction(); 307 execution_context_->ConsumeWindowInteraction();
232 } 308 }
233 309
234 DEFINE_TRACE(WaitUntilObserver) { 310 DEFINE_TRACE(WaitUntilObserver) {
235 visitor->Trace(execution_context_); 311 visitor->Trace(execution_context_);
236 } 312 }
237 313
238 } // namespace blink 314 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698