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