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

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: Add EventDispatcherState::kDispatching 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
« 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 // 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 exception_state.ThrowDOMException(
136 kInvalidStateError,
137 "Although the event handler is not finished yet, it's running "
shimazu 2017/05/23 09:08:43 I think it's okay to have the same message with th
leonhsl(Using Gerrit) 2017/05/23 09:40:18 Done.
138 "microtask, and no any extend lifetime promises are "
139 "outstanding.");
140 return;
141 }
142 break;
143 case EventDispatchState::kDispatched:
144 case EventDispatchState::kFailed:
145 exception_state.ThrowDOMException(
146 kInvalidStateError,
147 "The event handler is already finished "
148 "and no any extend lifetime promises are "
149 "outstanding.");
150 return;
151 }
115 } 152 }
116 153
117 if (!execution_context_) 154 if (!execution_context_)
118 return; 155 return;
119 156
120 // When handling a notificationclick event, we want to allow one window to 157 // When handling a notificationclick event, we want to allow one window to
121 // be focused or opened. See comments in ::willDispatchEvent(). When 158 // be focused or opened. See comments in ::willDispatchEvent(). When
122 // waitUntil() is being used, opening or closing a window must happen in a 159 // waitUntil() is being used, opening or closing a window must happen in a
123 // timeframe specified by windowInteractionTimeout(), otherwise the calls 160 // timeframe specified by windowInteractionTimeout(), otherwise the calls
124 // will fail. 161 // will fail.
125 if (type_ == kNotificationClick) 162 if (type_ == kNotificationClick)
126 consume_window_interaction_timer_.StartOneShot(WindowInteractionTimeout(), 163 consume_window_interaction_timer_.StartOneShot(WindowInteractionTimeout(),
127 BLINK_FROM_HERE); 164 BLINK_FROM_HERE);
128 165
129 IncrementPendingActivity(); 166 IncrementPendingPromise();
130 script_promise.Then(ThenFunction::CreateFunction(script_state, this, 167 script_promise.Then(ThenFunction::CreateFunction(script_state, this,
131 ThenFunction::kFulfilled), 168 ThenFunction::kFulfilled),
132 ThenFunction::CreateFunction(script_state, this, 169 ThenFunction::CreateFunction(script_state, this,
133 ThenFunction::kRejected)); 170 ThenFunction::kRejected));
134 } 171 }
135 172
136 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context, 173 WaitUntilObserver::WaitUntilObserver(ExecutionContext* context,
137 EventType type, 174 EventType type,
138 int event_id) 175 int event_id)
139 : execution_context_(context), 176 : execution_context_(context),
140 type_(type), 177 type_(type),
141 event_id_(event_id), 178 event_id_(event_id),
142 consume_window_interaction_timer_( 179 consume_window_interaction_timer_(
143 Platform::Current()->CurrentThread()->GetWebTaskRunner(), 180 Platform::Current()->CurrentThread()->GetWebTaskRunner(),
144 this, 181 this,
145 &WaitUntilObserver::ConsumeWindowInteraction) {} 182 &WaitUntilObserver::ConsumeWindowInteraction) {}
146 183
147 void WaitUntilObserver::OnPromiseFulfilled() { 184 void WaitUntilObserver::OnPromiseFulfilled() {
148 DecrementPendingActivity(); 185 DecrementPendingPromise();
149 } 186 }
150 187
151 void WaitUntilObserver::OnPromiseRejected() { 188 void WaitUntilObserver::OnPromiseRejected() {
152 has_rejected_promise_ = true; 189 has_rejected_promise_ = true;
153 DecrementPendingActivity(); 190 DecrementPendingPromise();
154 } 191 }
155 192
156 void WaitUntilObserver::IncrementPendingActivity() { 193 void WaitUntilObserver::IncrementPendingPromise() {
157 ++pending_activity_; 194 ++pending_promise_;
158 } 195 }
159 196
160 void WaitUntilObserver::DecrementPendingActivity() { 197 void WaitUntilObserver::DecrementPendingPromise() {
161 DCHECK_GT(pending_activity_, 0); 198 DCHECK_GT(pending_promise_, 0);
162 if (!execution_context_ || 199 --pending_promise_;
163 (event_dispatch_state_ != EventDispatchState::kFailed && 200 MaybeCompleteEvent();
164 --pending_activity_)) 201 }
202
203 void WaitUntilObserver::MaybeCompleteEvent() {
204 if (!execution_context_)
165 return; 205 return;
166 206
207 switch (event_dispatch_state_) {
208 case EventDispatchState::kInitial:
209 NOTREACHED();
210 return;
211 case EventDispatchState::kDispatching:
212 // Still dispatching, do not complete the event.
213 return;
214 case EventDispatchState::kDispatched:
215 if (pending_promise_ != 0)
216 return;
217 break;
218 case EventDispatchState::kFailed:
219 // Dispatch had some error, complete the event immediatelly.
220 break;
221 }
222
167 ServiceWorkerGlobalScopeClient* client = 223 ServiceWorkerGlobalScopeClient* client =
168 ServiceWorkerGlobalScopeClient::From(execution_context_); 224 ServiceWorkerGlobalScopeClient::From(execution_context_);
169 WebServiceWorkerEventResult result = 225 WebServiceWorkerEventResult result =
170 (event_dispatch_state_ == EventDispatchState::kFailed || 226 (event_dispatch_state_ == EventDispatchState::kFailed ||
171 has_rejected_promise_) 227 has_rejected_promise_)
172 ? kWebServiceWorkerEventResultRejected 228 ? kWebServiceWorkerEventResultRejected
173 : kWebServiceWorkerEventResultCompleted; 229 : kWebServiceWorkerEventResultCompleted;
174 switch (type_) { 230 switch (type_) {
175 case kActivate: 231 case kActivate:
176 client->DidHandleActivateEvent(event_id_, result, event_dispatch_time_); 232 client->DidHandleActivateEvent(event_id_, result, event_dispatch_time_);
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
229 if (!execution_context_) 285 if (!execution_context_)
230 return; 286 return;
231 execution_context_->ConsumeWindowInteraction(); 287 execution_context_->ConsumeWindowInteraction();
232 } 288 }
233 289
234 DEFINE_TRACE(WaitUntilObserver) { 290 DEFINE_TRACE(WaitUntilObserver) {
235 visitor->Trace(execution_context_); 291 visitor->Trace(execution_context_);
236 } 292 }
237 293
238 } // namespace blink 294 } // 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