e9f8cb042200ee494ea06a743d1e062928d72a66
[WebKit-https.git] / Source / WebCore / workers / service / ServiceWorker.cpp
1 /*
2  * Copyright (C) 2017 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ServiceWorker.h"
28
29 #if ENABLE(SERVICE_WORKER)
30
31 #include "Document.h"
32 #include "EventNames.h"
33 #include "MessagePort.h"
34 #include "SWClientConnection.h"
35 #include "ScriptExecutionContext.h"
36 #include "SerializedScriptValue.h"
37 #include "ServiceWorkerClientData.h"
38 #include "ServiceWorkerGlobalScope.h"
39 #include "ServiceWorkerProvider.h"
40 #include <runtime/JSCJSValueInlines.h>
41 #include <wtf/NeverDestroyed.h>
42
43 namespace WebCore {
44
45 Ref<ServiceWorker> ServiceWorker::getOrCreate(ScriptExecutionContext& context, ServiceWorkerData&& data)
46 {
47     if (auto existingServiceWorker = context.serviceWorker(data.identifier))
48         return *existingServiceWorker;
49     return adoptRef(*new ServiceWorker(context, WTFMove(data)));
50 }
51
52 ServiceWorker::ServiceWorker(ScriptExecutionContext& context, ServiceWorkerData&& data)
53     : ActiveDOMObject(&context)
54     , m_data(WTFMove(data))
55 {
56     suspendIfNeeded();
57
58     context.registerServiceWorker(*this);
59
60     relaxAdoptionRequirement();
61     updatePendingActivityForEventDispatch();
62 }
63
64 ServiceWorker::~ServiceWorker()
65 {
66     if (auto* context = scriptExecutionContext())
67         context->unregisterServiceWorker(*this);
68 }
69
70 void ServiceWorker::scheduleTaskToUpdateState(State state)
71 {
72     auto* context = scriptExecutionContext();
73     if (!context)
74         return;
75
76     context->postTask([this, protectedThis = makeRef(*this), state](ScriptExecutionContext&) {
77         ASSERT(this->state() != state);
78
79         m_data.state = state;
80         if (state != State::Installing && !m_isStopped) {
81             ASSERT(m_pendingActivityForEventDispatch);
82             dispatchEvent(Event::create(eventNames().statechangeEvent, false, false));
83         }
84
85         updatePendingActivityForEventDispatch();
86     });
87 }
88
89 ExceptionOr<void> ServiceWorker::postMessage(ScriptExecutionContext& context, JSC::JSValue messageValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer)
90 {
91     if (m_isStopped)
92         return Exception { InvalidStateError };
93
94     if (state() == State::Redundant)
95         return Exception { InvalidStateError, ASCIILiteral("Service Worker state is redundant") };
96
97     // FIXME: Invoke Run Service Worker algorithm with serviceWorker as the argument.
98
99     auto* execState = context.execState();
100     ASSERT(execState);
101
102     Vector<RefPtr<MessagePort>> ports;
103     auto message = SerializedScriptValue::create(*execState, messageValue, WTFMove(transfer), ports, SerializationContext::WorkerPostMessage);
104     if (message.hasException())
105         return message.releaseException();
106
107     // Disentangle the port in preparation for sending it to the remote context.
108     auto channelsOrException = MessagePort::disentanglePorts(WTFMove(ports));
109     if (channelsOrException.hasException())
110         return channelsOrException.releaseException();
111
112     // FIXME: Support sending the channels.
113     auto channels = channelsOrException.releaseReturnValue();
114     if (!channels.isEmpty())
115         return Exception { NotSupportedError, ASCIILiteral("Passing MessagePort objects to postMessage is not yet supported") };
116
117     ServiceWorkerOrClientIdentifier sourceIdentifier;
118     if (is<ServiceWorkerGlobalScope>(context))
119         sourceIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
120     else {
121         auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(context.sessionID());
122         sourceIdentifier = ServiceWorkerClientIdentifier { connection.serverConnectionIdentifier(), downcast<Document>(context).identifier() };
123     }
124
125     callOnMainThread([sessionID = context.sessionID(), destinationIdentifier = identifier(), message = WTFMove(message), sourceIdentifier = WTFMove(sourceIdentifier)]() mutable {
126         auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(sessionID);
127         connection.postMessageToServiceWorker(destinationIdentifier, message.releaseReturnValue(), sourceIdentifier);
128     });
129     return { };
130 }
131
132 EventTargetInterface ServiceWorker::eventTargetInterface() const
133 {
134     return ServiceWorkerEventTargetInterfaceType;
135 }
136
137 ScriptExecutionContext* ServiceWorker::scriptExecutionContext() const
138 {
139     return ContextDestructionObserver::scriptExecutionContext();
140 }
141
142 const char* ServiceWorker::activeDOMObjectName() const
143 {
144     return "ServiceWorker";
145 }
146
147 bool ServiceWorker::canSuspendForDocumentSuspension() const
148 {
149     // FIXME: We should do better as this prevents the page from entering PageCache when there is a Service Worker.
150     return !hasPendingActivity();
151 }
152
153 void ServiceWorker::stop()
154 {
155     m_isStopped = true;
156     removeAllEventListeners();
157     scriptExecutionContext()->unregisterServiceWorker(*this);
158     updatePendingActivityForEventDispatch();
159 }
160
161 void ServiceWorker::updatePendingActivityForEventDispatch()
162 {
163     // ServiceWorkers can dispatch events until they become redundant or they are stopped.
164     if (m_isStopped || state() == State::Redundant) {
165         m_pendingActivityForEventDispatch = nullptr;
166         return;
167     }
168     if (m_pendingActivityForEventDispatch)
169         return;
170     m_pendingActivityForEventDispatch = makePendingActivity(*this);
171 }
172
173 } // namespace WebCore
174
175 #endif // ENABLE(SERVICE_WORKER)