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