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/v8/ExceptionState.h"
35 #include "bindings/v8/ScriptPromiseResolverWithContext.h"
36 #include "bindings/v8/ScriptState.h"
37 #include "core/dom/MessagePort.h"
38 #include "modules/EventTargetModules.h"
39 #include "platform/NotImplemented.h"
40 #include "public/platform/WebMessagePortChannel.h"
41 #include "public/platform/WebServiceWorkerState.h"
42 #include "public/platform/WebString.h"
43 #include <v8.h>
44
45 namespace WebCore {
46
47 class ServiceWorker::ThenFunction FINAL : public ScriptFunction {
48 public:
create(PassRefPtr<ServiceWorker> observer)49 static PassOwnPtr<ScriptFunction> create(PassRefPtr<ServiceWorker> observer)
50 {
51 ExecutionContext* executionContext = observer->executionContext();
52 return adoptPtr(new ThenFunction(toIsolate(executionContext), observer));
53 }
54 private:
ThenFunction(v8::Isolate * isolate,PassRefPtr<ServiceWorker> observer)55 ThenFunction(v8::Isolate* isolate, PassRefPtr<ServiceWorker> observer)
56 : ScriptFunction(isolate)
57 , m_observer(observer)
58 {
59 }
60
call(ScriptValue value)61 virtual ScriptValue call(ScriptValue value) OVERRIDE
62 {
63 m_observer->onPromiseResolved();
64 return value;
65 }
66
67 RefPtr<ServiceWorker> m_observer;
68 };
69
interfaceName() const70 const AtomicString& ServiceWorker::interfaceName() const
71 {
72 return EventTargetNames::ServiceWorker;
73 }
74
postMessage(PassRefPtr<SerializedScriptValue> message,const MessagePortArray * ports,ExceptionState & exceptionState)75 void ServiceWorker::postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, ExceptionState& exceptionState)
76 {
77 // Disentangle the port in preparation for sending it to the remote context.
78 OwnPtr<MessagePortChannelArray> channels = MessagePort::disentanglePorts(ports, exceptionState);
79 if (exceptionState.hadException())
80 return;
81
82 blink::WebString messageString = message->toWireString();
83 OwnPtr<blink::WebMessagePortChannelArray> webChannels = MessagePort::toWebMessagePortChannelArray(channels.release());
84 m_outerWorker->postMessage(messageString, webChannels.leakPtr());
85 }
86
isReady()87 bool ServiceWorker::isReady()
88 {
89 return m_proxyState == Ready;
90 }
91
dispatchStateChangeEvent()92 void ServiceWorker::dispatchStateChangeEvent()
93 {
94 ASSERT(isReady());
95 this->dispatchEvent(Event::create(EventTypeNames::statechange));
96 }
97
scope() const98 String ServiceWorker::scope() const
99 {
100 return m_outerWorker->scope().string();
101 }
102
url() const103 String ServiceWorker::url() const
104 {
105 return m_outerWorker->url().string();
106 }
107
state() const108 const AtomicString& ServiceWorker::state() const
109 {
110 DEFINE_STATIC_LOCAL(AtomicString, unknown, ("unknown", AtomicString::ConstructFromLiteral));
111 DEFINE_STATIC_LOCAL(AtomicString, parsed, ("parsed", AtomicString::ConstructFromLiteral));
112 DEFINE_STATIC_LOCAL(AtomicString, installing, ("installing", AtomicString::ConstructFromLiteral));
113 DEFINE_STATIC_LOCAL(AtomicString, installed, ("installed", AtomicString::ConstructFromLiteral));
114 DEFINE_STATIC_LOCAL(AtomicString, activating, ("activating", AtomicString::ConstructFromLiteral));
115 DEFINE_STATIC_LOCAL(AtomicString, active, ("active", AtomicString::ConstructFromLiteral));
116 DEFINE_STATIC_LOCAL(AtomicString, deactivated, ("deactivated", AtomicString::ConstructFromLiteral));
117
118 switch (m_outerWorker->state()) {
119 case blink::WebServiceWorkerStateUnknown:
120 // The web platform should never see this internal state
121 ASSERT_NOT_REACHED();
122 return unknown;
123 case blink::WebServiceWorkerStateParsed:
124 return parsed;
125 case blink::WebServiceWorkerStateInstalling:
126 return installing;
127 case blink::WebServiceWorkerStateInstalled:
128 return installed;
129 case blink::WebServiceWorkerStateActivating:
130 return activating;
131 case blink::WebServiceWorkerStateActive:
132 return active;
133 case blink::WebServiceWorkerStateDeactivated:
134 return deactivated;
135 default:
136 ASSERT_NOT_REACHED();
137 return nullAtom;
138 }
139 }
140
from(ExecutionContext * executionContext,WebType * worker)141 PassRefPtr<ServiceWorker> ServiceWorker::from(ExecutionContext* executionContext, WebType* worker)
142 {
143 if (!worker)
144 return PassRefPtr<ServiceWorker>();
145
146 blink::WebServiceWorkerProxy* proxy = worker->proxy();
147 ServiceWorker* existingServiceWorker = proxy ? proxy->unwrap() : 0;
148 if (existingServiceWorker) {
149 ASSERT(existingServiceWorker->executionContext() == executionContext);
150 return existingServiceWorker;
151 }
152
153 return create(executionContext, adoptPtr(worker));
154 }
155
from(ScriptPromiseResolverWithContext * resolver,WebType * worker)156 PassRefPtr<ServiceWorker> ServiceWorker::from(ScriptPromiseResolverWithContext* resolver, WebType* worker)
157 {
158 RefPtr<ServiceWorker> serviceWorker = ServiceWorker::from(resolver->scriptState()->executionContext(), worker);
159 ScriptState::Scope scope(resolver->scriptState());
160 serviceWorker->waitOnPromise(resolver->promise());
161 return serviceWorker;
162 }
163
setProxyState(ProxyState state)164 void ServiceWorker::setProxyState(ProxyState state)
165 {
166 if (m_proxyState == state)
167 return;
168 switch (m_proxyState) {
169 case Initial:
170 ASSERT(state == RegisterPromisePending || state == ContextStopped);
171 break;
172 case RegisterPromisePending:
173 ASSERT(state == Ready || state == ContextStopped);
174 break;
175 case Ready:
176 ASSERT(state == ContextStopped);
177 break;
178 case ContextStopped:
179 ASSERT_NOT_REACHED();
180 break;
181 }
182
183 ProxyState oldState = m_proxyState;
184 m_proxyState = state;
185 if (oldState == Ready || state == Ready)
186 m_outerWorker->proxyReadyChanged();
187 }
188
onPromiseResolved()189 void ServiceWorker::onPromiseResolved()
190 {
191 if (m_proxyState == ContextStopped)
192 return;
193 setProxyState(Ready);
194 }
195
waitOnPromise(ScriptPromise promise)196 void ServiceWorker::waitOnPromise(ScriptPromise promise)
197 {
198 if (promise.isEmpty()) {
199 // The document was detached during registration. The state doesn't really
200 // matter since this ServiceWorker will immediately die.
201 setProxyState(ContextStopped);
202 return;
203 }
204 setProxyState(RegisterPromisePending);
205 promise.then(ThenFunction::create(this));
206 }
207
hasPendingActivity() const208 bool ServiceWorker::hasPendingActivity() const
209 {
210 if (AbstractWorker::hasPendingActivity())
211 return true;
212 if (m_proxyState == ContextStopped)
213 return false;
214 return m_outerWorker->state() != blink::WebServiceWorkerStateDeactivated;
215 }
216
stop()217 void ServiceWorker::stop()
218 {
219 setProxyState(ContextStopped);
220 }
221
create(ExecutionContext * executionContext,PassOwnPtr<blink::WebServiceWorker> outerWorker)222 PassRefPtr<ServiceWorker> ServiceWorker::create(ExecutionContext* executionContext, PassOwnPtr<blink::WebServiceWorker> outerWorker)
223 {
224 RefPtr<ServiceWorker> worker = adoptRef(new ServiceWorker(executionContext, outerWorker));
225 worker->suspendIfNeeded();
226 return worker.release();
227 }
228
ServiceWorker(ExecutionContext * executionContext,PassOwnPtr<blink::WebServiceWorker> worker)229 ServiceWorker::ServiceWorker(ExecutionContext* executionContext, PassOwnPtr<blink::WebServiceWorker> worker)
230 : AbstractWorker(executionContext)
231 , WebServiceWorkerProxy(this)
232 , m_outerWorker(worker)
233 , m_proxyState(Initial)
234 {
235 ScriptWrappable::init(this);
236 ASSERT(m_outerWorker);
237 m_outerWorker->setProxy(this);
238 }
239
240 } // namespace WebCore
241