Move URL from WebCore to WTF
[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 "Logging.h"
34 #include "MessagePort.h"
35 #include "SWClientConnection.h"
36 #include "ScriptExecutionContext.h"
37 #include "SerializedScriptValue.h"
38 #include "ServiceWorkerClientData.h"
39 #include "ServiceWorkerGlobalScope.h"
40 #include "ServiceWorkerProvider.h"
41 #include <JavaScriptCore/JSCJSValueInlines.h>
42 #include <wtf/NeverDestroyed.h>
43
44 #define WORKER_RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), ServiceWorker, "%p - ServiceWorker::" fmt, this, ##__VA_ARGS__)
45 #define WORKER_RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), ServiceWorker, "%p - ServiceWorker::" fmt, this, ##__VA_ARGS__)
46
47 namespace WebCore {
48
49 Ref<ServiceWorker> ServiceWorker::getOrCreate(ScriptExecutionContext& context, ServiceWorkerData&& data)
50 {
51     if (auto existingServiceWorker = context.serviceWorker(data.identifier))
52         return *existingServiceWorker;
53     return adoptRef(*new ServiceWorker(context, WTFMove(data)));
54 }
55
56 ServiceWorker::ServiceWorker(ScriptExecutionContext& context, ServiceWorkerData&& data)
57     : ActiveDOMObject(&context)
58     , m_data(WTFMove(data))
59 {
60     suspendIfNeeded();
61
62     context.registerServiceWorker(*this);
63
64     relaxAdoptionRequirement();
65     updatePendingActivityForEventDispatch();
66
67     WORKER_RELEASE_LOG_IF_ALLOWED("ServiceWorker: ID: %llu, state: %hhu", identifier().toUInt64(), m_data.state);
68 }
69
70 ServiceWorker::~ServiceWorker()
71 {
72     if (auto* context = scriptExecutionContext())
73         context->unregisterServiceWorker(*this);
74 }
75
76 void ServiceWorker::scheduleTaskToUpdateState(State state)
77 {
78     auto* context = scriptExecutionContext();
79     if (!context)
80         return;
81
82     context->postTask([this, protectedThis = makeRef(*this), state](ScriptExecutionContext&) {
83         ASSERT(this->state() != state);
84
85         WORKER_RELEASE_LOG_IF_ALLOWED("scheduleTaskToUpdateState: Updating service worker %llu state from %hhu to %hhu. Registration ID: %llu", identifier().toUInt64(), m_data.state, state, registrationIdentifier().toUInt64());
86         m_data.state = state;
87         if (state != State::Installing && !m_isStopped) {
88             ASSERT(m_pendingActivityForEventDispatch);
89             dispatchEvent(Event::create(eventNames().statechangeEvent, Event::CanBubble::No, Event::IsCancelable::No));
90         }
91
92         updatePendingActivityForEventDispatch();
93     });
94 }
95
96 ExceptionOr<void> ServiceWorker::postMessage(ScriptExecutionContext& context, JSC::JSValue messageValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer)
97 {
98     if (m_isStopped || !context.sessionID().isValid())
99         return Exception { InvalidStateError };
100
101     if (state() == State::Redundant)
102         return Exception { InvalidStateError, "Service Worker state is redundant"_s };
103
104     // FIXME: Invoke Run Service Worker algorithm with serviceWorker as the argument.
105
106     auto* execState = context.execState();
107     ASSERT(execState);
108
109     Vector<RefPtr<MessagePort>> ports;
110     auto messageData = SerializedScriptValue::create(*execState, messageValue, WTFMove(transfer), ports, SerializationContext::WorkerPostMessage);
111     if (messageData.hasException())
112         return messageData.releaseException();
113
114     // Disentangle the port in preparation for sending it to the remote context.
115     auto portsOrException = MessagePort::disentanglePorts(WTFMove(ports));
116     if (portsOrException.hasException())
117         return portsOrException.releaseException();
118
119     ServiceWorkerOrClientIdentifier sourceIdentifier;
120     if (is<ServiceWorkerGlobalScope>(context))
121         sourceIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
122     else {
123         auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(context.sessionID());
124         sourceIdentifier = ServiceWorkerClientIdentifier { connection.serverConnectionIdentifier(), downcast<Document>(context).identifier() };
125     }
126
127     MessageWithMessagePorts message = { messageData.releaseReturnValue(), portsOrException.releaseReturnValue() };
128     callOnMainThread([sessionID = context.sessionID(), destinationIdentifier = identifier(), message = WTFMove(message), sourceIdentifier = WTFMove(sourceIdentifier)]() mutable {
129         auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(sessionID);
130         connection.postMessageToServiceWorker(destinationIdentifier, WTFMove(message), sourceIdentifier);
131     });
132     return { };
133 }
134
135 EventTargetInterface ServiceWorker::eventTargetInterface() const
136 {
137     return ServiceWorkerEventTargetInterfaceType;
138 }
139
140 ScriptExecutionContext* ServiceWorker::scriptExecutionContext() const
141 {
142     return ContextDestructionObserver::scriptExecutionContext();
143 }
144
145 const char* ServiceWorker::activeDOMObjectName() const
146 {
147     return "ServiceWorker";
148 }
149
150 bool ServiceWorker::canSuspendForDocumentSuspension() const
151 {
152     // FIXME: We should do better as this prevents the page from entering PageCache when there is a Service Worker.
153     return !hasPendingActivity();
154 }
155
156 void ServiceWorker::stop()
157 {
158     m_isStopped = true;
159     removeAllEventListeners();
160     scriptExecutionContext()->unregisterServiceWorker(*this);
161     updatePendingActivityForEventDispatch();
162 }
163
164 void ServiceWorker::updatePendingActivityForEventDispatch()
165 {
166     // ServiceWorkers can dispatch events until they become redundant or they are stopped.
167     if (m_isStopped || state() == State::Redundant) {
168         m_pendingActivityForEventDispatch = nullptr;
169         return;
170     }
171     if (m_pendingActivityForEventDispatch)
172         return;
173     m_pendingActivityForEventDispatch = makePendingActivity(*this);
174 }
175
176 bool ServiceWorker::isAlwaysOnLoggingAllowed() const
177 {
178     auto* context = scriptExecutionContext();
179     if (!context)
180         return false;
181
182     auto* container = context->serviceWorkerContainer();
183     if (!container)
184         return false;
185
186     return container->isAlwaysOnLoggingAllowed();
187 }
188
189 } // namespace WebCore
190
191 #endif // ENABLE(SERVICE_WORKER)