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

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 shimazu@ 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.
(...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
falken 2017/05/24 08:24:18 Add quote: "Upon fulfillment or rejection of f, q
leonhsl(Using Gerrit) 2017/05/25 00:16:40 Done.
71 // At this time point the microtask A running resolve/reject function of
72 // this promise has already been queued, in order to allow microtask A to
73 // call waitUntil, we enqueue another microtask B to delay the promise
74 // settled notification to |observer_|, thus A will run before B so A can
75 // call waitUntil well, but any other microtask C possibly enqueued by A
76 // will run after B so C maybe can't call waitUntil if there has no any
77 // extend lifetime promise at that time.
67 if (resolve_type_ == kRejected) { 78 if (resolve_type_ == kRejected) {
68 observer_->OnPromiseRejected(); 79 Microtask::EnqueueMicrotask(
80 WTF::Bind(&WaitUntilObserver::OnPromiseRejected,
81 WrapPersistent(observer_.Get())));
69 value = 82 value =
70 ScriptPromise::Reject(value.GetScriptState(), value).GetScriptValue(); 83 ScriptPromise::Reject(value.GetScriptState(), value).GetScriptValue();
71 } else { 84 } else {
72 observer_->OnPromiseFulfilled(); 85 Microtask::EnqueueMicrotask(
86 WTF::Bind(&WaitUntilObserver::OnPromiseFulfilled,
87 WrapPersistent(observer_.Get())));
73 } 88 }
74 observer_ = nullptr; 89 observer_ = nullptr;
75 return value; 90 return value;
76 } 91 }
77 92
78 Member<WaitUntilObserver> observer_; 93 Member<WaitUntilObserver> observer_;
79 ResolveType resolve_type_; 94 ResolveType resolve_type_;
80 }; 95 };
81 96
82 WaitUntilObserver* WaitUntilObserver::Create(ExecutionContext* context, 97 WaitUntilObserver* WaitUntilObserver::Create(ExecutionContext* context,
83 EventType type, 98 EventType type,
84 int event_id) { 99 int event_id) {
85 return new WaitUntilObserver(context, type, event_id); 100 return new WaitUntilObserver(context, type, event_id);
86 } 101 }
87 102
88 void WaitUntilObserver::WillDispatchEvent() { 103 void WaitUntilObserver::WillDispatchEvent() {
89 event_dispatch_time_ = WTF::CurrentTime(); 104 event_dispatch_time_ = WTF::CurrentTime();
90 // When handling a notificationclick or paymentrequest event, we want to 105 // When handling a notificationclick or paymentrequest event, we want to
91 // allow one window to be focused or opened. These calls are allowed between 106 // allow one window to be focused or opened. These calls are allowed between
92 // the call to willDispatchEvent() and the last call to 107 // the call to willDispatchEvent() and the last call to
93 // decrementPendingActivity(). If waitUntil() isn't called, that means 108 // DecrementPendingPromise(). If waitUntil() isn't called, that means
94 // between willDispatchEvent() and didDispatchEvent(). 109 // between willDispatchEvent() and didDispatchEvent().
95 if (type_ == kNotificationClick || type_ == kPaymentRequest) 110 if (type_ == kNotificationClick || type_ == kPaymentRequest)
96 execution_context_->AllowWindowInteraction(); 111 execution_context_->AllowWindowInteraction();
97 112
98 IncrementPendingActivity(); 113 DCHECK_EQ(EventDispatchState::kInitial, event_dispatch_state_);
114 event_dispatch_state_ = EventDispatchState::kDispatching;
99 } 115 }
100 116
101 void WaitUntilObserver::DidDispatchEvent(bool event_dispatch_failed) { 117 void WaitUntilObserver::DidDispatchEvent(bool event_dispatch_failed) {
102 event_dispatch_state_ = event_dispatch_failed 118 event_dispatch_state_ = event_dispatch_failed
103 ? EventDispatchState::kFailed 119 ? EventDispatchState::kFailed
104 : EventDispatchState::kCompleted; 120 : EventDispatchState::kDispatched;
105 DecrementPendingActivity(); 121 MaybeCompleteEvent();
106 } 122 }
107 123
108 void WaitUntilObserver::WaitUntil(ScriptState* script_state, 124 void WaitUntilObserver::WaitUntil(ScriptState* script_state,
109 ScriptPromise script_promise, 125 ScriptPromise script_promise,
110 ExceptionState& exception_state) { 126 ExceptionState& exception_state) {
111 if (event_dispatch_state_ != EventDispatchState::kInitial) { 127 if (pending_promise_ == 0) {
112 exception_state.ThrowDOMException(kInvalidStateError, 128 switch (event_dispatch_state_) {
113 "The event handler is already finished."); 129 case EventDispatchState::kInitial:
114 return; 130 NOTREACHED();
131 return;
132 case EventDispatchState::kDispatching:
133 if (!v8::MicrotasksScope::IsRunningMicrotasks(
134 script_state->GetIsolate())) {
135 break;
136 }
137 // Fall through:
138 // Although the event handler is not finished yet, it's running microtask,
falken 2017/05/24 08:24:18 I'd like this explanation to better explain what y
leonhsl(Using Gerrit) 2017/05/25 00:16:40 Done.
139 // and no any extend lifetime promises are outstanding, in such case we
140 // should also throw.
141 case EventDispatchState::kDispatched:
142 case EventDispatchState::kFailed:
143 exception_state.ThrowDOMException(
144 kInvalidStateError,
145 "The event handler is already finished "
146 "and no any extend lifetime promises are "
147 "outstanding.");
148 return;
149 }
115 } 150 }
116 151
117 if (!execution_context_) 152 if (!execution_context_)
118 return; 153 return;
119 154
120 // When handling a notificationclick event, we want to allow one window to 155 // When handling a notificationclick event, we want to allow one window to
121 // be focused or opened. See comments in ::willDispatchEvent(). When 156 // be focused or opened. See comments in ::willDispatchEvent(). When
122 // waitUntil() is being used, opening or closing a window must happen in a 157 // waitUntil() is being used, opening or closing a window must happen in a
123 // timeframe specified by windowInteractionTimeout(), otherwise the calls 158 // timeframe specified by windowInteractionTimeout(), otherwise the calls
124 // will fail. 159 // will fail.
125 if (type_ == kNotificationClick) 160 if (type_ == kNotificationClick)
126 consume_window_interaction_timer_.StartOneShot(WindowInteractionTimeout(), 161 consume_window_interaction_timer_.StartOneShot(WindowInteractionTimeout(),
127 BLINK_FROM_HERE); 162 BLINK_FROM_HERE);
128 163
129 IncrementPendingActivity(); 164 IncrementPendingPromise();
130 script_promise.Then(ThenFunction::CreateFunction(script_state, this, 165 script_promise.Then(ThenFunction::CreateFunction(script_state, this,
131 ThenFunction::kFulfilled), 166 ThenFunction::kFulfilled),
132 ThenFunction::CreateFunction(script_state, this, 167 ThenFunction::CreateFunction(script_state, this,
133 ThenFunction::kRejected)); 168 ThenFunction::kRejected));
134 } 169 }
135 170
136 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context, 171 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context,
137 EventType type, 172 EventType type,
138 int event_id) 173 int event_id)
139 : execution_context_(context), 174 : execution_context_(context),
140 type_(type), 175 type_(type),
141 event_id_(event_id), 176 event_id_(event_id),
142 consume_window_interaction_timer_( 177 consume_window_interaction_timer_(
143 Platform::Current()->CurrentThread()->GetWebTaskRunner(), 178 Platform::Current()->CurrentThread()->GetWebTaskRunner(),
144 this, 179 this,
145 &WaitUntilObserver::ConsumeWindowInteraction) {} 180 &WaitUntilObserver::ConsumeWindowInteraction) {}
146 181
147 void WaitUntilObserver::OnPromiseFulfilled() { 182 void WaitUntilObserver::OnPromiseFulfilled() {
148 DecrementPendingActivity(); 183 DecrementPendingPromise();
149 } 184 }
150 185
151 void WaitUntilObserver::OnPromiseRejected() { 186 void WaitUntilObserver::OnPromiseRejected() {
152 has_rejected_promise_ = true; 187 has_rejected_promise_ = true;
153 DecrementPendingActivity(); 188 DecrementPendingPromise();
154 } 189 }
155 190
156 void WaitUntilObserver::IncrementPendingActivity() { 191 void WaitUntilObserver::IncrementPendingPromise() {
157 ++pending_activity_; 192 ++pending_promise_;
158 } 193 }
159 194
160 void WaitUntilObserver::DecrementPendingActivity() { 195 void WaitUntilObserver::DecrementPendingPromise() {
161 DCHECK_GT(pending_activity_, 0); 196 DCHECK_GT(pending_promise_, 0);
162 if (!execution_context_ || 197 --pending_promise_;
163 (event_dispatch_state_ != EventDispatchState::kFailed && 198 MaybeCompleteEvent();
164 --pending_activity_)) 199 }
200
201 void WaitUntilObserver::MaybeCompleteEvent() {
202 if (!execution_context_)
165 return; 203 return;
166 204
205 switch (event_dispatch_state_) {
206 case EventDispatchState::kInitial:
207 NOTREACHED();
208 return;
209 case EventDispatchState::kDispatching:
210 // Still dispatching, do not complete the event.
211 return;
212 case EventDispatchState::kDispatched:
falken 2017/05/24 08:24:18 // Still waiting for a promise, do not complete th
leonhsl(Using Gerrit) 2017/05/25 00:16:40 Done.
213 if (pending_promise_ != 0)
214 return;
falken 2017/05/24 08:24:18 // Dispatch finished and there are no pending prom
leonhsl(Using Gerrit) 2017/05/25 00:16:40 Done.
215 break;
216 case EventDispatchState::kFailed:
217 // Dispatch had some error, complete the event immediatelly.
218 break;
219 }
220
167 ServiceWorkerGlobalScopeClient* client = 221 ServiceWorkerGlobalScopeClient* client =
168 ServiceWorkerGlobalScopeClient::From(execution_context_); 222 ServiceWorkerGlobalScopeClient::From(execution_context_);
169 WebServiceWorkerEventResult result = 223 WebServiceWorkerEventResult result =
170 (event_dispatch_state_ == EventDispatchState::kFailed || 224 (event_dispatch_state_ == EventDispatchState::kFailed ||
171 has_rejected_promise_) 225 has_rejected_promise_)
172 ? kWebServiceWorkerEventResultRejected 226 ? kWebServiceWorkerEventResultRejected
173 : kWebServiceWorkerEventResultCompleted; 227 : kWebServiceWorkerEventResultCompleted;
174 switch (type_) { 228 switch (type_) {
175 case kActivate: 229 case kActivate:
176 client->DidHandleActivateEvent(event_id_, result, event_dispatch_time_); 230 client->DidHandleActivateEvent(event_id_, result, event_dispatch_time_);
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 if (!execution_context_) 283 if (!execution_context_)
230 return; 284 return;
231 execution_context_->ConsumeWindowInteraction(); 285 execution_context_->ConsumeWindowInteraction();
232 } 286 }
233 287
234 DEFINE_TRACE(WaitUntilObserver) { 288 DEFINE_TRACE(WaitUntilObserver) {
235 visitor->Trace(execution_context_); 289 visitor->Trace(execution_context_);
236 } 290 }
237 291
238 } // namespace blink 292 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698