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/core/v8/CallbackPromiseAdapter.h" 34 #include "bindings/core/v8/ScriptPromise.h" 35 #include "bindings/core/v8/ScriptPromiseResolver.h" 36 #include "bindings/core/v8/ScriptState.h" 37 #include "bindings/core/v8/SerializedScriptValue.h" 38 #include "core/dom/DOMException.h" 39 #include "core/dom/ExceptionCode.h" 40 #include "core/dom/ExecutionContext.h" 41 #include "core/dom/MessagePort.h" 42 #include "core/events/MessageEvent.h" 43 #include "modules/serviceworkers/ServiceWorker.h" 44 #include "modules/serviceworkers/ServiceWorkerContainerClient.h" 45 #include "modules/serviceworkers/ServiceWorkerError.h" 46 #include "modules/serviceworkers/ServiceWorkerRegistration.h" 47 #include "platform/RuntimeEnabledFeatures.h" 48 #include "public/platform/WebServiceWorker.h" 49 #include "public/platform/WebServiceWorkerProvider.h" 50 #include "public/platform/WebServiceWorkerRegistration.h" 51 #include "public/platform/WebString.h" 52 #include "public/platform/WebURL.h" 53 54 namespace blink { 55 56 // This wraps CallbackPromiseAdapter and resolves the promise with undefined 57 // when nullptr is given to onSuccess. 58 class GetRegistrationCallback : public WebServiceWorkerProvider::WebServiceWorkerGetRegistrationCallbacks { 59 public: 60 explicit GetRegistrationCallback(PassRefPtr<ScriptPromiseResolver> resolver) 61 : m_resolver(resolver) 62 , m_adapter(m_resolver) { } 63 virtual ~GetRegistrationCallback() { } 64 virtual void onSuccess(WebServiceWorkerRegistration* registration) OVERRIDE 65 { 66 if (registration) 67 m_adapter.onSuccess(registration); 68 else if (m_resolver->executionContext() && !m_resolver->executionContext()->activeDOMObjectsAreStopped()) 69 m_resolver->resolve(); 70 } 71 virtual void onError(WebServiceWorkerError* error) OVERRIDE { m_adapter.onError(error); } 72 private: 73 RefPtr<ScriptPromiseResolver> m_resolver; 74 CallbackPromiseAdapter<ServiceWorkerRegistration, ServiceWorkerError> m_adapter; 75 WTF_MAKE_NONCOPYABLE(GetRegistrationCallback); 76 }; 77 78 ServiceWorkerContainer* ServiceWorkerContainer::create(ExecutionContext* executionContext) 79 { 80 return new ServiceWorkerContainer(executionContext); 81 } 82 83 ServiceWorkerContainer::~ServiceWorkerContainer() 84 { 85 ASSERT(!m_provider); 86 } 87 88 void ServiceWorkerContainer::willBeDetachedFromFrame() 89 { 90 if (m_provider) { 91 m_provider->setClient(0); 92 m_provider = nullptr; 93 } 94 } 95 96 void ServiceWorkerContainer::trace(Visitor* visitor) 97 { 98 visitor->trace(m_controller); 99 visitor->trace(m_readyRegistration); 100 visitor->trace(m_ready); 101 } 102 103 ScriptPromise ServiceWorkerContainer::registerServiceWorker(ScriptState* scriptState, const String& url, const RegistrationOptionList& options) 104 { 105 ASSERT(RuntimeEnabledFeatures::serviceWorkerEnabled()); 106 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState); 107 ScriptPromise promise = resolver->promise(); 108 109 if (!m_provider) { 110 resolver->reject(DOMException::create(InvalidStateError, "No associated provider is available")); 111 return promise; 112 } 113 114 // FIXME: This should use the container's execution context, not 115 // the callers. 116 ExecutionContext* executionContext = scriptState->executionContext(); 117 RefPtr<SecurityOrigin> documentOrigin = executionContext->securityOrigin(); 118 String errorMessage; 119 if (!documentOrigin->canAccessFeatureRequiringSecureOrigin(errorMessage)) { 120 resolver->reject(DOMException::create(NotSupportedError, errorMessage)); 121 return promise; 122 } 123 124 KURL patternURL = executionContext->completeURL(options.scope()); 125 patternURL.removeFragmentIdentifier(); 126 if (!documentOrigin->canRequest(patternURL)) { 127 resolver->reject(DOMException::create(SecurityError, "The scope must match the current origin.")); 128 return promise; 129 } 130 131 KURL scriptURL = executionContext->completeURL(url); 132 scriptURL.removeFragmentIdentifier(); 133 if (!documentOrigin->canRequest(scriptURL)) { 134 resolver->reject(DOMException::create(SecurityError, "The origin of the script must match the current origin.")); 135 return promise; 136 } 137 138 m_provider->registerServiceWorker(patternURL, scriptURL, new CallbackPromiseAdapter<ServiceWorkerRegistration, ServiceWorkerError>(resolver)); 139 140 return promise; 141 } 142 143 class BooleanValue { 144 public: 145 typedef bool WebType; 146 static bool take(ScriptPromiseResolver* resolver, WebType* boolean) 147 { 148 return *boolean; 149 } 150 static void dispose(WebType* boolean) { } 151 152 private: 153 BooleanValue(); 154 }; 155 156 ScriptPromise ServiceWorkerContainer::getRegistration(ScriptState* scriptState, const String& documentURL) 157 { 158 ASSERT(RuntimeEnabledFeatures::serviceWorkerEnabled()); 159 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState); 160 ScriptPromise promise = resolver->promise(); 161 162 // FIXME: This should use the container's execution context, not 163 // the callers. 164 ExecutionContext* executionContext = scriptState->executionContext(); 165 RefPtr<SecurityOrigin> documentOrigin = executionContext->securityOrigin(); 166 String errorMessage; 167 if (!documentOrigin->canAccessFeatureRequiringSecureOrigin(errorMessage)) { 168 resolver->reject(DOMException::create(NotSupportedError, errorMessage)); 169 return promise; 170 } 171 172 KURL completedURL = executionContext->completeURL(documentURL); 173 if (!documentOrigin->canRequest(completedURL)) { 174 resolver->reject(DOMException::create(SecurityError, "The documentURL must match the current origin.")); 175 return promise; 176 } 177 m_provider->getRegistration(completedURL, new GetRegistrationCallback(resolver)); 178 179 return promise; 180 } 181 182 ServiceWorkerContainer::ReadyProperty* ServiceWorkerContainer::createReadyProperty() 183 { 184 return new ReadyProperty(executionContext(), this, ReadyProperty::Ready); 185 } 186 187 ScriptPromise ServiceWorkerContainer::ready(ScriptState* callerState) 188 { 189 if (!executionContext()) 190 return ScriptPromise(); 191 192 if (!callerState->world().isMainWorld()) { 193 // FIXME: Support .ready from isolated worlds when 194 // ScriptPromiseProperty can vend Promises in isolated worlds. 195 return ScriptPromise::rejectWithDOMException(callerState, DOMException::create(NotSupportedError, "'ready' is only supported in pages.")); 196 } 197 198 return m_ready->promise(callerState->world()); 199 } 200 201 // If the WebServiceWorker is up for adoption (does not have a 202 // WebServiceWorkerProxy owner), rejects the adoption by deleting the 203 // WebServiceWorker. 204 static void deleteIfNoExistingOwner(WebServiceWorker* serviceWorker) 205 { 206 if (serviceWorker && !serviceWorker->proxy()) 207 delete serviceWorker; 208 } 209 210 static void deleteIfNoExistingOwner(WebServiceWorkerRegistration* registration) 211 { 212 if (registration && !registration->proxy()) 213 delete registration; 214 } 215 216 void ServiceWorkerContainer::setController(WebServiceWorker* serviceWorker) 217 { 218 if (!executionContext()) { 219 deleteIfNoExistingOwner(serviceWorker); 220 return; 221 } 222 m_controller = ServiceWorker::from(executionContext(), serviceWorker); 223 } 224 225 void ServiceWorkerContainer::setReadyRegistration(WebServiceWorkerRegistration* registration) 226 { 227 if (!executionContext()) { 228 deleteIfNoExistingOwner(registration); 229 return; 230 } 231 232 ServiceWorkerRegistration* readyRegistration = ServiceWorkerRegistration::from(executionContext(), registration); 233 ASSERT(readyRegistration->active()); 234 235 if (m_readyRegistration) { 236 ASSERT(m_readyRegistration == readyRegistration); 237 ASSERT(m_ready->state() == ReadyProperty::Resolved); 238 return; 239 } 240 241 m_readyRegistration = readyRegistration; 242 m_ready->resolve(readyRegistration); 243 } 244 245 void ServiceWorkerContainer::dispatchMessageEvent(const WebString& message, const WebMessagePortChannelArray& webChannels) 246 { 247 if (!executionContext() || !executionContext()->executingWindow()) 248 return; 249 250 OwnPtrWillBeRawPtr<MessagePortArray> ports = MessagePort::toMessagePortArray(executionContext(), webChannels); 251 RefPtr<SerializedScriptValue> value = SerializedScriptValue::createFromWire(message); 252 executionContext()->executingWindow()->dispatchEvent(MessageEvent::create(ports.release(), value)); 253 } 254 255 ServiceWorkerContainer::ServiceWorkerContainer(ExecutionContext* executionContext) 256 : ContextLifecycleObserver(executionContext) 257 , m_provider(0) 258 { 259 260 if (!executionContext) 261 return; 262 263 m_ready = createReadyProperty(); 264 265 if (ServiceWorkerContainerClient* client = ServiceWorkerContainerClient::from(executionContext)) { 266 m_provider = client->provider(); 267 if (m_provider) 268 m_provider->setClient(this); 269 } 270 } 271 272 } // namespace blink 273