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 #include "config.h"
     31 #include "modules/serviceworkers/ServiceWorkerContainer.h"
     32 
     33 #include "bindings/v8/CallbackPromiseAdapter.h"
     34 #include "bindings/v8/ScriptPromise.h"
     35 #include "bindings/v8/ScriptPromiseResolverWithContext.h"
     36 #include "bindings/v8/ScriptState.h"
     37 #include "bindings/v8/SerializedScriptValue.h"
     38 #include "core/dom/DOMException.h"
     39 #include "core/dom/Document.h"
     40 #include "core/dom/ExceptionCode.h"
     41 #include "core/dom/ExecutionContext.h"
     42 #include "core/dom/MessagePort.h"
     43 #include "core/events/MessageEvent.h"
     44 #include "modules/serviceworkers/RegistrationOptionList.h"
     45 #include "modules/serviceworkers/ServiceWorker.h"
     46 #include "modules/serviceworkers/ServiceWorkerContainerClient.h"
     47 #include "modules/serviceworkers/ServiceWorkerError.h"
     48 #include "platform/RuntimeEnabledFeatures.h"
     49 #include "public/platform/WebServiceWorker.h"
     50 #include "public/platform/WebServiceWorkerProvider.h"
     51 #include "public/platform/WebString.h"
     52 #include "public/platform/WebURL.h"
     53 #include <v8.h>
     54 
     55 using blink::WebServiceWorker;
     56 using blink::WebServiceWorkerProvider;
     57 
     58 namespace WebCore {
     59 
     60 PassRefPtr<ServiceWorkerContainer> ServiceWorkerContainer::create(ExecutionContext* executionContext)
     61 {
     62     return adoptRef(new ServiceWorkerContainer(executionContext));
     63 }
     64 
     65 ServiceWorkerContainer::~ServiceWorkerContainer()
     66 {
     67     ASSERT(!m_provider);
     68 }
     69 
     70 void ServiceWorkerContainer::detachClient()
     71 {
     72     if (m_provider) {
     73         m_provider->setClient(0);
     74         m_provider = 0;
     75     }
     76 }
     77 
     78 ScriptPromise ServiceWorkerContainer::registerServiceWorker(ScriptState* scriptState, const String& url, const Dictionary& dictionary)
     79 {
     80     RegistrationOptionList options(dictionary);
     81     ASSERT(RuntimeEnabledFeatures::serviceWorkerEnabled());
     82     RefPtr<ScriptPromiseResolverWithContext> resolver = ScriptPromiseResolverWithContext::create(scriptState);
     83     ScriptPromise promise = resolver->promise();
     84 
     85     if (!m_provider) {
     86         resolver->reject(DOMException::create(InvalidStateError, "No associated provider is available"));
     87         return promise;
     88     }
     89 
     90     ExecutionContext* executionContext = scriptState->executionContext();
     91     RefPtr<SecurityOrigin> documentOrigin = executionContext->securityOrigin();
     92     KURL patternURL = executionContext->completeURL(options.scope);
     93     patternURL.removeFragmentIdentifier();
     94     if (!documentOrigin->canRequest(patternURL)) {
     95         resolver->reject(DOMException::create(SecurityError, "Can only register for patterns in the document's origin."));
     96         return promise;
     97     }
     98 
     99     KURL scriptURL = executionContext->completeURL(url);
    100     scriptURL.removeFragmentIdentifier();
    101     if (!documentOrigin->canRequest(scriptURL)) {
    102         resolver->reject(DOMException::create(SecurityError, "Script must be in document's origin."));
    103         return promise;
    104     }
    105 
    106     m_provider->registerServiceWorker(patternURL, scriptURL, new CallbackPromiseAdapter<ServiceWorker, ServiceWorkerError>(resolver));
    107     return promise;
    108 }
    109 
    110 class UndefinedValue {
    111 public:
    112     typedef WebServiceWorker WebType;
    113     static V8UndefinedType from(ScriptPromiseResolverWithContext* resolver, WebServiceWorker* worker)
    114     {
    115         ASSERT(!worker); // Anything passed here will be leaked.
    116         return V8UndefinedType();
    117     }
    118 
    119 private:
    120     UndefinedValue();
    121 };
    122 
    123 ScriptPromise ServiceWorkerContainer::unregisterServiceWorker(ScriptState* scriptState, const String& pattern)
    124 {
    125     ASSERT(RuntimeEnabledFeatures::serviceWorkerEnabled());
    126     RefPtr<ScriptPromiseResolverWithContext> resolver = ScriptPromiseResolverWithContext::create(scriptState);
    127     ScriptPromise promise = resolver->promise();
    128 
    129     if (!m_provider) {
    130         resolver->reject(DOMException::create(InvalidStateError, "No associated provider is available"));
    131         return promise;
    132     }
    133 
    134     RefPtr<SecurityOrigin> documentOrigin = scriptState->executionContext()->securityOrigin();
    135     KURL patternURL = scriptState->executionContext()->completeURL(pattern);
    136     patternURL.removeFragmentIdentifier();
    137     if (!pattern.isEmpty() && !documentOrigin->canRequest(patternURL)) {
    138         resolver->reject(DOMException::create(SecurityError, "Can only unregister for patterns in the document's origin."));
    139         return promise;
    140     }
    141 
    142     m_provider->unregisterServiceWorker(patternURL, new CallbackPromiseAdapter<UndefinedValue, ServiceWorkerError>(resolver));
    143     return promise;
    144 }
    145 
    146 ScriptPromise ServiceWorkerContainer::ready(ScriptState* scriptState)
    147 {
    148     if (m_controller.get()) {
    149         RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
    150         ScriptPromise promise = resolver->promise();
    151         resolver->resolve(m_controller.get());
    152         return promise;
    153     }
    154     // FIXME: Elaborate the implementation when the "waiting" property
    155     // or replace() is implemented.
    156     return ScriptPromise();
    157 }
    158 
    159 // If the WebServiceWorker is up for adoption (does not have a
    160 // WebServiceWorkerProxy owner), rejects the adoption by deleting the
    161 // WebServiceWorker.
    162 static void deleteIfNoExistingOwner(blink::WebServiceWorker* serviceWorker)
    163 {
    164     if (serviceWorker && !serviceWorker->proxy())
    165         delete serviceWorker;
    166 }
    167 
    168 void ServiceWorkerContainer::setActive(blink::WebServiceWorker* serviceWorker)
    169 {
    170     if (!executionContext()) {
    171         deleteIfNoExistingOwner(serviceWorker);
    172         return;
    173     }
    174     m_active = ServiceWorker::from(executionContext(), serviceWorker);
    175 }
    176 
    177 void ServiceWorkerContainer::setController(blink::WebServiceWorker* serviceWorker)
    178 {
    179     if (!executionContext()) {
    180         deleteIfNoExistingOwner(serviceWorker);
    181         return;
    182     }
    183     m_controller = ServiceWorker::from(executionContext(), serviceWorker);
    184 }
    185 
    186 void ServiceWorkerContainer::setInstalling(blink::WebServiceWorker* serviceWorker)
    187 {
    188     if (!executionContext()) {
    189         deleteIfNoExistingOwner(serviceWorker);
    190         return;
    191     }
    192     m_installing = ServiceWorker::from(executionContext(), serviceWorker);
    193 }
    194 
    195 void ServiceWorkerContainer::setWaiting(blink::WebServiceWorker* serviceWorker)
    196 {
    197     if (!executionContext()) {
    198         deleteIfNoExistingOwner(serviceWorker);
    199         return;
    200     }
    201     m_waiting = ServiceWorker::from(executionContext(), serviceWorker);
    202 }
    203 
    204 void ServiceWorkerContainer::dispatchMessageEvent(const blink::WebString& message, const blink::WebMessagePortChannelArray& webChannels)
    205 {
    206     if (!executionContext() || !executionContext()->executingWindow())
    207         return;
    208 
    209     OwnPtr<MessagePortArray> ports = MessagePort::toMessagePortArray(executionContext(), webChannels);
    210     RefPtr<SerializedScriptValue> value = SerializedScriptValue::createFromWire(message);
    211     executionContext()->executingWindow()->dispatchEvent(MessageEvent::create(ports.release(), value));
    212 }
    213 
    214 ServiceWorkerContainer::ServiceWorkerContainer(ExecutionContext* executionContext)
    215     : ContextLifecycleObserver(executionContext)
    216     , m_provider(0)
    217 {
    218     ScriptWrappable::init(this);
    219 
    220     if (!executionContext)
    221         return;
    222 
    223     if (ServiceWorkerContainerClient* client = ServiceWorkerContainerClient::from(executionContext)) {
    224         m_provider = client->provider();
    225         if (m_provider)
    226             m_provider->setClient(this);
    227     }
    228 }
    229 
    230 } // namespace WebCore
    231