Home | History | Annotate | Download | only in serviceworkers
      1 /*
      2  * Copyright (C) 2013 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "ServiceWorker.h"
     33 
     34 #include "bindings/core/v8/ExceptionState.h"
     35 #include "bindings/core/v8/ScriptPromiseResolver.h"
     36 #include "bindings/core/v8/ScriptState.h"
     37 #include "core/dom/MessagePort.h"
     38 #include "core/events/Event.h"
     39 #include "modules/EventTargetModules.h"
     40 #include "platform/NotImplemented.h"
     41 #include "public/platform/WebMessagePortChannel.h"
     42 #include "public/platform/WebServiceWorkerState.h"
     43 #include "public/platform/WebString.h"
     44 #include <v8.h>
     45 
     46 namespace blink {
     47 
     48 class ServiceWorker::ThenFunction FINAL : public ScriptFunction {
     49 public:
     50     static v8::Handle<v8::Function> createFunction(ScriptState* scriptState, PassRefPtrWillBeRawPtr<ServiceWorker> observer)
     51     {
     52         ThenFunction* self = new ThenFunction(scriptState, observer);
     53         return self->bindToV8Function();
     54     }
     55 
     56     virtual void trace(Visitor* visitor) OVERRIDE
     57     {
     58         visitor->trace(m_observer);
     59         ScriptFunction::trace(visitor);
     60     }
     61 
     62 private:
     63     ThenFunction(ScriptState* scriptState, PassRefPtrWillBeRawPtr<ServiceWorker> observer)
     64         : ScriptFunction(scriptState)
     65         , m_observer(observer)
     66     {
     67     }
     68 
     69     virtual ScriptValue call(ScriptValue value) OVERRIDE
     70     {
     71         m_observer->onPromiseResolved();
     72         return value;
     73     }
     74 
     75     RefPtrWillBeMember<ServiceWorker> m_observer;
     76 };
     77 
     78 const AtomicString& ServiceWorker::interfaceName() const
     79 {
     80     return EventTargetNames::ServiceWorker;
     81 }
     82 
     83 void ServiceWorker::postMessage(ExecutionContext*, PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, ExceptionState& exceptionState)
     84 {
     85     // Disentangle the port in preparation for sending it to the remote context.
     86     OwnPtr<MessagePortChannelArray> channels = MessagePort::disentanglePorts(ports, exceptionState);
     87     if (exceptionState.hadException())
     88         return;
     89     if (m_outerWorker->state() == WebServiceWorkerStateRedundant) {
     90         exceptionState.throwDOMException(InvalidStateError, "ServiceWorker is in redundant state.");
     91         return;
     92     }
     93 
     94     WebString messageString = message->toWireString();
     95     OwnPtr<WebMessagePortChannelArray> webChannels = MessagePort::toWebMessagePortChannelArray(channels.release());
     96     m_outerWorker->postMessage(messageString, webChannels.leakPtr());
     97 }
     98 
     99 void ServiceWorker::terminate(ExceptionState& exceptionState)
    100 {
    101     exceptionState.throwDOMException(InvalidAccessError, "Not supported.");
    102 }
    103 
    104 bool ServiceWorker::isReady()
    105 {
    106     return m_proxyState == Ready;
    107 }
    108 
    109 void ServiceWorker::dispatchStateChangeEvent()
    110 {
    111     ASSERT(isReady());
    112     this->dispatchEvent(Event::create(EventTypeNames::statechange));
    113 }
    114 
    115 String ServiceWorker::scriptURL() const
    116 {
    117     return m_outerWorker->url().string();
    118 }
    119 
    120 const AtomicString& ServiceWorker::state() const
    121 {
    122     DEFINE_STATIC_LOCAL(AtomicString, unknown, ("unknown", AtomicString::ConstructFromLiteral));
    123     DEFINE_STATIC_LOCAL(AtomicString, parsed, ("parsed", AtomicString::ConstructFromLiteral));
    124     DEFINE_STATIC_LOCAL(AtomicString, installing, ("installing", AtomicString::ConstructFromLiteral));
    125     DEFINE_STATIC_LOCAL(AtomicString, installed, ("installed", AtomicString::ConstructFromLiteral));
    126     DEFINE_STATIC_LOCAL(AtomicString, activating, ("activating", AtomicString::ConstructFromLiteral));
    127     DEFINE_STATIC_LOCAL(AtomicString, activated, ("activated", AtomicString::ConstructFromLiteral));
    128     DEFINE_STATIC_LOCAL(AtomicString, redundant, ("redundant", AtomicString::ConstructFromLiteral));
    129 
    130     switch (m_outerWorker->state()) {
    131     case WebServiceWorkerStateUnknown:
    132         // The web platform should never see this internal state
    133         ASSERT_NOT_REACHED();
    134         return unknown;
    135     case WebServiceWorkerStateParsed:
    136         return parsed;
    137     case WebServiceWorkerStateInstalling:
    138         return installing;
    139     case WebServiceWorkerStateInstalled:
    140         return installed;
    141     case WebServiceWorkerStateActivating:
    142         return activating;
    143     case WebServiceWorkerStateActivated:
    144         return activated;
    145     case WebServiceWorkerStateRedundant:
    146         return redundant;
    147     default:
    148         ASSERT_NOT_REACHED();
    149         return nullAtom;
    150     }
    151 }
    152 
    153 PassRefPtrWillBeRawPtr<ServiceWorker> ServiceWorker::from(ExecutionContext* executionContext, WebType* worker)
    154 {
    155     if (!worker)
    156         return nullptr;
    157 
    158     RefPtrWillBeRawPtr<ServiceWorker> serviceWorker = getOrCreate(executionContext, worker);
    159     if (serviceWorker->m_proxyState == Initial)
    160         serviceWorker->setProxyState(Ready);
    161     return serviceWorker.release();
    162 }
    163 
    164 PassRefPtrWillBeRawPtr<ServiceWorker> ServiceWorker::take(ScriptPromiseResolver* resolver, WebType* worker)
    165 {
    166     if (!worker)
    167         return nullptr;
    168 
    169     RefPtrWillBeRawPtr<ServiceWorker> serviceWorker = getOrCreate(resolver->scriptState()->executionContext(), worker);
    170     ScriptState::Scope scope(resolver->scriptState());
    171     if (serviceWorker->m_proxyState == Initial)
    172         serviceWorker->waitOnPromise(resolver);
    173     return serviceWorker;
    174 }
    175 
    176 void ServiceWorker::dispose(WebType* worker)
    177 {
    178     if (worker && !worker->proxy())
    179         delete worker;
    180 }
    181 
    182 void ServiceWorker::setProxyState(ProxyState state)
    183 {
    184     if (m_proxyState == state)
    185         return;
    186     switch (m_proxyState) {
    187     case Initial:
    188         ASSERT(state == RegisterPromisePending || state == Ready || state == ContextStopped);
    189         break;
    190     case RegisterPromisePending:
    191         ASSERT(state == Ready || state == ContextStopped);
    192         break;
    193     case Ready:
    194         ASSERT(state == ContextStopped);
    195         break;
    196     case ContextStopped:
    197         ASSERT_NOT_REACHED();
    198         break;
    199     }
    200 
    201     ProxyState oldState = m_proxyState;
    202     m_proxyState = state;
    203     if (oldState == Ready || state == Ready)
    204         m_outerWorker->proxyReadyChanged();
    205 }
    206 
    207 void ServiceWorker::onPromiseResolved()
    208 {
    209     if (m_proxyState == ContextStopped)
    210         return;
    211     setProxyState(Ready);
    212 }
    213 
    214 void ServiceWorker::waitOnPromise(ScriptPromiseResolver* resolver)
    215 {
    216     if (resolver->promise().isEmpty()) {
    217         // The document was detached during registration. The state doesn't really
    218         // matter since this ServiceWorker will immediately die.
    219         setProxyState(ContextStopped);
    220         return;
    221     }
    222     setProxyState(RegisterPromisePending);
    223     resolver->promise().then(ThenFunction::createFunction(resolver->scriptState(), this));
    224 }
    225 
    226 bool ServiceWorker::hasPendingActivity() const
    227 {
    228     if (AbstractWorker::hasPendingActivity())
    229         return true;
    230     if (m_proxyState == ContextStopped)
    231         return false;
    232     return m_outerWorker->state() != WebServiceWorkerStateRedundant;
    233 }
    234 
    235 void ServiceWorker::stop()
    236 {
    237     setProxyState(ContextStopped);
    238 }
    239 
    240 PassRefPtrWillBeRawPtr<ServiceWorker> ServiceWorker::getOrCreate(ExecutionContext* executionContext, WebType* outerWorker)
    241 {
    242     if (!outerWorker)
    243         return nullptr;
    244 
    245     WebServiceWorkerProxy* proxy = outerWorker->proxy();
    246     ServiceWorker* existingServiceWorker = proxy ? proxy->unwrap() : 0;
    247     if (existingServiceWorker) {
    248         ASSERT(existingServiceWorker->executionContext() == executionContext);
    249         return existingServiceWorker;
    250     }
    251 
    252     RefPtrWillBeRawPtr<ServiceWorker> worker = adoptRefWillBeNoop(new ServiceWorker(executionContext, adoptPtr(outerWorker)));
    253     worker->suspendIfNeeded();
    254     return worker.release();
    255 }
    256 
    257 ServiceWorker::ServiceWorker(ExecutionContext* executionContext, PassOwnPtr<WebServiceWorker> worker)
    258     : AbstractWorker(executionContext)
    259     , WebServiceWorkerProxy(this)
    260     , m_outerWorker(worker)
    261     , m_proxyState(Initial)
    262 {
    263     ASSERT(m_outerWorker);
    264     m_outerWorker->setProxy(this);
    265 }
    266 
    267 } // namespace blink
    268