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