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

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: Part of 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"
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.
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 ThenFunction(ScriptState* script_state, 59 ThenFunction(ScriptState* script_state,
58 WaitUntilObserver* observer, 60 WaitUntilObserver* observer,
59 ResolveType type) 61 ResolveType type)
60 : ScriptFunction(script_state), 62 : ScriptFunction(script_state),
61 observer_(observer), 63 observer_(observer),
62 resolve_type_(type) {} 64 resolve_type_(type) {}
63 65
64 ScriptValue Call(ScriptValue value) override { 66 ScriptValue Call(ScriptValue value) override {
65 DCHECK(observer_); 67 DCHECK(observer_);
66 DCHECK(resolve_type_ == kFulfilled || resolve_type_ == kRejected); 68 DCHECK(resolve_type_ == kFulfilled || resolve_type_ == kRejected);
69 // According from step 4 of ExtendableEvent::waitUntil() in spec:
70 // https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
71 // "Upon fulfillment or rejection of f, queue a microtask to run these
72 // substeps: Decrement the pending promises count by one."
73 // At this time point the microtask A running resolve/reject function of
74 // this promise has already been queued, in order to allow microtask A to
75 // call waitUntil, we enqueue another microtask B to delay the promise
76 // settled notification to |observer_|, thus A will run before B so A can
77 // call waitUntil well, but any other microtask C possibly enqueued by A
78 // will run after B so C maybe can't call waitUntil if there has no any
79 // extend lifetime promise at that time.
67 if (resolve_type_ == kRejected) { 80 if (resolve_type_ == kRejected) {
68 observer_->OnPromiseRejected(); 81 Microtask::EnqueueMicrotask(
82 WTF::Bind(&WaitUntilObserver::OnPromiseRejected,
83 WrapPersistent(observer_.Get())));
69 value = 84 value =
70 ScriptPromise::Reject(value.GetScriptState(), value).GetScriptValue(); 85 ScriptPromise::Reject(value.GetScriptState(), value).GetScriptValue();
71 } else { 86 } else {
72 observer_->OnPromiseFulfilled(); 87 Microtask::EnqueueMicrotask(
88 WTF::Bind(&WaitUntilObserver::OnPromiseFulfilled,
89 WrapPersistent(observer_.Get())));
73 } 90 }
74 observer_ = nullptr; 91 observer_ = nullptr;
75 return value; 92 return value;
76 } 93 }
77 94
78 Member<WaitUntilObserver> observer_; 95 Member<WaitUntilObserver> observer_;
79 ResolveType resolve_type_; 96 ResolveType resolve_type_;
80 }; 97 };
81 98
82 WaitUntilObserver* WaitUntilObserver::Create(ExecutionContext* context, 99 WaitUntilObserver* WaitUntilObserver::Create(ExecutionContext* context,
83 EventType type, 100 EventType type,
84 int event_id) { 101 int event_id) {
85 return new WaitUntilObserver(context, type, event_id); 102 return new WaitUntilObserver(context, type, event_id);
86 } 103 }
87 104
88 void WaitUntilObserver::WillDispatchEvent() { 105 void WaitUntilObserver::WillDispatchEvent() {
89 event_dispatch_time_ = WTF::CurrentTime(); 106 event_dispatch_time_ = WTF::CurrentTime();
90 // When handling a notificationclick or paymentrequest event, we want to 107 // When handling a notificationclick or paymentrequest event, we want to
91 // allow one window to be focused or opened. These calls are allowed between 108 // allow one window to be focused or opened. These calls are allowed between
92 // the call to willDispatchEvent() and the last call to 109 // the call to willDispatchEvent() and the last call to
93 // decrementPendingActivity(). If waitUntil() isn't called, that means 110 // DecrementPendingPromise(). If waitUntil() isn't called, that means
94 // between willDispatchEvent() and didDispatchEvent(). 111 // between willDispatchEvent() and didDispatchEvent().
95 if (type_ == kNotificationClick || type_ == kPaymentRequest) 112 if (type_ == kNotificationClick || type_ == kPaymentRequest)
96 execution_context_->AllowWindowInteraction(); 113 execution_context_->AllowWindowInteraction();
97 114
98 IncrementPendingActivity(); 115 DCHECK_EQ(EventDispatchState::kInitial, event_dispatch_state_);
116 event_dispatch_state_ = EventDispatchState::kDispatching;
99 } 117 }
100 118
101 void WaitUntilObserver::DidDispatchEvent(bool event_dispatch_failed) { 119 void WaitUntilObserver::DidDispatchEvent(bool event_dispatch_failed) {
102 event_dispatch_state_ = event_dispatch_failed 120 event_dispatch_state_ = event_dispatch_failed
103 ? EventDispatchState::kFailed 121 ? EventDispatchState::kFailed
104 : EventDispatchState::kCompleted; 122 : EventDispatchState::kDispatched;
105 DecrementPendingActivity(); 123 MaybeCompleteEvent();
106 } 124 }
107 125
108 void WaitUntilObserver::WaitUntil(ScriptState* script_state, 126 void WaitUntilObserver::WaitUntil(ScriptState* script_state,
109 ScriptPromise script_promise, 127 ScriptPromise script_promise,
110 ExceptionState& exception_state) { 128 ExceptionState& exception_state) {
111 if (event_dispatch_state_ != EventDispatchState::kInitial) { 129 if (pending_promises_ == 0) {
112 exception_state.ThrowDOMException(kInvalidStateError, 130 switch (event_dispatch_state_) {
113 "The event handler is already finished."); 131 case EventDispatchState::kInitial:
114 return; 132 NOTREACHED();
133 return;
134 case EventDispatchState::kDispatching:
135 if (!v8::MicrotasksScope::IsRunningMicrotasks(
136 script_state->GetIsolate())) {
137 break;
138 }
139 // Fall through:
140 // didDispatchEvent() is called after both the event handler
141 // execution finished and microtasks queued by the event handler execution
142 // finished, it's hard to get the presise time point between the 2
kinuko 2017/05/25 01:45:02 nit: presise -> precise
leonhsl(Using Gerrit) 2017/05/25 06:43:55 Done.
143 // execution phases.
144 // So even in EventDispatchState::kDispatching state at this time point,
145 // running microtask indicates that event handler execution has actually
146 // finished, in such case if there has no any extend lifetime promises are
kinuko 2017/05/25 01:45:03 has -> have? 'if there aren't any outstanding lif
leonhsl(Using Gerrit) 2017/05/25 06:43:55 Done. Yeah 'extend lifetime promises' is a spec te
147 // outstanding, we should throw here.
148 case EventDispatchState::kDispatched:
149 case EventDispatchState::kFailed:
150 exception_state.ThrowDOMException(
151 kInvalidStateError,
152 "The event handler is already finished "
153 "and no any extend lifetime promises are "
154 "outstanding.");
155 return;
156 }
115 } 157 }
116 158
117 if (!execution_context_) 159 if (!execution_context_)
118 return; 160 return;
119 161
120 // When handling a notificationclick event, we want to allow one window to 162 // When handling a notificationclick event, we want to allow one window to
121 // be focused or opened. See comments in ::willDispatchEvent(). When 163 // be focused or opened. See comments in ::willDispatchEvent(). When
122 // waitUntil() is being used, opening or closing a window must happen in a 164 // waitUntil() is being used, opening or closing a window must happen in a
123 // timeframe specified by windowInteractionTimeout(), otherwise the calls 165 // timeframe specified by windowInteractionTimeout(), otherwise the calls
124 // will fail. 166 // will fail.
125 if (type_ == kNotificationClick) 167 if (type_ == kNotificationClick)
126 consume_window_interaction_timer_.StartOneShot(WindowInteractionTimeout(), 168 consume_window_interaction_timer_.StartOneShot(WindowInteractionTimeout(),
127 BLINK_FROM_HERE); 169 BLINK_FROM_HERE);
128 170
129 IncrementPendingActivity(); 171 IncrementPendingPromise();
130 script_promise.Then(ThenFunction::CreateFunction(script_state, this, 172 script_promise.Then(ThenFunction::CreateFunction(script_state, this,
131 ThenFunction::kFulfilled), 173 ThenFunction::kFulfilled),
132 ThenFunction::CreateFunction(script_state, this, 174 ThenFunction::CreateFunction(script_state, this,
133 ThenFunction::kRejected)); 175 ThenFunction::kRejected));
134 } 176 }
135 177
136 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context, 178 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context,
137 EventType type, 179 EventType type,
138 int event_id) 180 int event_id)
139 : execution_context_(context), 181 : execution_context_(context),
140 type_(type), 182 type_(type),
141 event_id_(event_id), 183 event_id_(event_id),
142 consume_window_interaction_timer_( 184 consume_window_interaction_timer_(
143 Platform::Current()->CurrentThread()->GetWebTaskRunner(), 185 Platform::Current()->CurrentThread()->GetWebTaskRunner(),
144 this, 186 this,
145 &WaitUntilObserver::ConsumeWindowInteraction) {} 187 &WaitUntilObserver::ConsumeWindowInteraction) {}
146 188
147 void WaitUntilObserver::OnPromiseFulfilled() { 189 void WaitUntilObserver::OnPromiseFulfilled() {
148 DecrementPendingActivity(); 190 DecrementPendingPromise();
149 } 191 }
150 192
151 void WaitUntilObserver::OnPromiseRejected() { 193 void WaitUntilObserver::OnPromiseRejected() {
152 has_rejected_promise_ = true; 194 has_rejected_promise_ = true;
153 DecrementPendingActivity(); 195 DecrementPendingPromise();
154 } 196 }
155 197
156 void WaitUntilObserver::IncrementPendingActivity() { 198 void WaitUntilObserver::IncrementPendingPromise() {
157 ++pending_activity_; 199 ++pending_promises_;
158 } 200 }
159 201
160 void WaitUntilObserver::DecrementPendingActivity() { 202 void WaitUntilObserver::DecrementPendingPromise() {
161 DCHECK_GT(pending_activity_, 0); 203 DCHECK_GT(pending_promises_, 0);
162 if (!execution_context_ || 204 --pending_promises_;
163 (event_dispatch_state_ != EventDispatchState::kFailed && 205 MaybeCompleteEvent();
164 --pending_activity_)) 206 }
207
208 void WaitUntilObserver::MaybeCompleteEvent() {
209 if (!execution_context_)
165 return; 210 return;
166 211
212 switch (event_dispatch_state_) {
213 case EventDispatchState::kInitial:
214 NOTREACHED();
215 return;
216 case EventDispatchState::kDispatching:
217 // Still dispatching, do not complete the event.
218 return;
219 case EventDispatchState::kDispatched:
220 // Still waiting for a promise, do not complete the event.
221 if (pending_promises_ != 0)
222 return;
223 // Dispatch finished and there are no pending promises, complete the
224 // event.
225 break;
226 case EventDispatchState::kFailed:
227 // Dispatch had some error, complete the event immediatelly.
228 break;
229 }
230
167 ServiceWorkerGlobalScopeClient* client = 231 ServiceWorkerGlobalScopeClient* client =
168 ServiceWorkerGlobalScopeClient::From(execution_context_); 232 ServiceWorkerGlobalScopeClient::From(execution_context_);
169 WebServiceWorkerEventResult result = 233 WebServiceWorkerEventResult result =
170 (event_dispatch_state_ == EventDispatchState::kFailed || 234 (event_dispatch_state_ == EventDispatchState::kFailed ||
171 has_rejected_promise_) 235 has_rejected_promise_)
172 ? kWebServiceWorkerEventResultRejected 236 ? kWebServiceWorkerEventResultRejected
173 : kWebServiceWorkerEventResultCompleted; 237 : kWebServiceWorkerEventResultCompleted;
174 switch (type_) { 238 switch (type_) {
175 case kActivate: 239 case kActivate:
176 client->DidHandleActivateEvent(event_id_, result, event_dispatch_time_); 240 client->DidHandleActivateEvent(event_id_, result, event_dispatch_time_);
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 if (!execution_context_) 293 if (!execution_context_)
230 return; 294 return;
231 execution_context_->ConsumeWindowInteraction(); 295 execution_context_->ConsumeWindowInteraction();
232 } 296 }
233 297
234 DEFINE_TRACE(WaitUntilObserver) { 298 DEFINE_TRACE(WaitUntilObserver) {
235 visitor->Trace(execution_context_); 299 visitor->Trace(execution_context_);
236 } 300 }
237 301
238 } // namespace blink 302 } // 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