OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |