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

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: Address comments from falken@ Created 3 years, 6 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
« no previous file with comments | « third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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"
16 #include "public/platform/Platform.h" 17 #include "public/platform/Platform.h"
17 #include "public/platform/modules/serviceworker/WebServiceWorkerEventResult.h" 18 #include "public/platform/modules/serviceworker/WebServiceWorkerEventResult.h"
18 #include "v8/include/v8.h" 19 #include "v8/include/v8.h"
19 20
20 namespace blink { 21 namespace blink {
21 22
22 namespace { 23 namespace {
23 24
24 // Timeout before a service worker that was given window interaction 25 // Timeout before a service worker that was given window interaction
25 // permission loses them. The unit is seconds. 26 // permission loses them. The unit is seconds.
26 const unsigned kWindowInteractionTimeout = 10; 27 const unsigned kWindowInteractionTimeout = 10;
27 const unsigned kWindowInteractionTimeoutForTest = 1; 28 const unsigned kWindowInteractionTimeoutForTest = 1;
28 29
29 unsigned WindowInteractionTimeout() { 30 unsigned WindowInteractionTimeout() {
30 return LayoutTestSupport::IsRunningLayoutTest() 31 return LayoutTestSupport::IsRunningLayoutTest()
31 ? kWindowInteractionTimeoutForTest 32 ? kWindowInteractionTimeoutForTest
32 : kWindowInteractionTimeout; 33 : kWindowInteractionTimeout;
33 } 34 }
34 35
35 } // anonymous namespace 36 } // anonymous namespace
36 37
37 class WaitUntilObserver::ThenFunction final : public ScriptFunction { 38 class WaitUntilObserver::ThenFunction final : public ScriptFunction {
38 public: 39 public:
39 enum ResolveType { 40 enum ResolveType {
40 kFulfilled, 41 kFulfilled,
41 kRejected, 42 kRejected,
42 }; 43 };
43 44
44 static v8::Local<v8::Function> CreateFunction(ScriptState* script_state, 45 static v8::Local<v8::Function> CreateFunction(
45 WaitUntilObserver* observer, 46 ScriptState* script_state,
46 ResolveType type) { 47 WaitUntilObserver* observer,
47 ThenFunction* self = new ThenFunction(script_state, observer, type); 48 ResolveType type,
49 std::unique_ptr<PromiseSettledCallback> callback) {
50 ThenFunction* self =
51 new ThenFunction(script_state, observer, type, std::move(callback));
48 return self->BindToV8Function(); 52 return self->BindToV8Function();
49 } 53 }
50 54
51 DEFINE_INLINE_VIRTUAL_TRACE() { 55 DEFINE_INLINE_VIRTUAL_TRACE() {
52 visitor->Trace(observer_); 56 visitor->Trace(observer_);
53 ScriptFunction::Trace(visitor); 57 ScriptFunction::Trace(visitor);
54 } 58 }
55 59
56 private: 60 private:
57 ThenFunction(ScriptState* script_state, 61 ThenFunction(ScriptState* script_state,
58 WaitUntilObserver* observer, 62 WaitUntilObserver* observer,
59 ResolveType type) 63 ResolveType type,
64 std::unique_ptr<PromiseSettledCallback> callback)
60 : ScriptFunction(script_state), 65 : ScriptFunction(script_state),
61 observer_(observer), 66 observer_(observer),
62 resolve_type_(type) {} 67 resolve_type_(type),
68 callback_(std::move(callback)) {}
63 69
64 ScriptValue Call(ScriptValue value) override { 70 ScriptValue Call(ScriptValue value) override {
65 DCHECK(observer_); 71 DCHECK(observer_);
66 DCHECK(resolve_type_ == kFulfilled || resolve_type_ == kRejected); 72 DCHECK(resolve_type_ == kFulfilled || resolve_type_ == kRejected);
73 if (callback_)
74 (*callback_)(value);
75 // According from step 4 of ExtendableEvent::waitUntil() in spec:
76 // https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
77 // "Upon fulfillment or rejection of f, queue a microtask to run these
78 // substeps: Decrement the pending promises count by one."
79
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 // DecrementPendingPromiseCount(). 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 extend lifetime promises are "
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 IncrementPendingPromiseCount();
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 DecrementPendingPromiseCount();
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 DecrementPendingPromiseCount();
154 } 208 }
155 209
156 void WaitUntilObserver::IncrementPendingActivity() { 210 void WaitUntilObserver::IncrementPendingPromiseCount() {
157 ++pending_activity_; 211 ++pending_promises_;
158 } 212 }
159 213
160 void WaitUntilObserver::DecrementPendingActivity() { 214 void WaitUntilObserver::DecrementPendingPromiseCount() {
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
« no previous file with comments | « third_party/WebKit/Source/modules/serviceworkers/WaitUntilObserver.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698