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