| 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 |