Add release logging to help debug issues related to service workers
[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 <runtime/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: %u", 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 %u to %u. 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, false, false));
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)
99         return Exception { InvalidStateError };
100
101     if (state() == State::Redundant)
102         return Exception { InvalidStateError, ASCIILiteral("Service Worker state is redundant") };
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 message = SerializedScriptValue::create(*execState, messageValue, WTFMove(transfer), ports, SerializationContext::WorkerPostMessage);
111     if (message.hasException())
112         return message.releaseException();
113
114     // Disentangle the port in preparation for sending it to the remote context.
115     auto channelsOrException = MessagePort::disentanglePorts(WTFMove(ports));
116     if (channelsOrException.hasException())
117         return channelsOrException.releaseException();
118
119     // FIXME: Support sending the channels.
120     auto channels = channelsOrException.releaseReturnValue();
121     if (!channels.isEmpty()) {
122         WORKER_RELEASE_LOG_ERROR_IF_ALLOWED("postMessage: Passing MessagePort objects to postMessage is not yet supported");
123         return Exception { NotSupportedError, ASCIILiteral("Passing MessagePort objects to postMessage is not yet supported") };
124     }
125
126     ServiceWorkerOrClientIdentifier sourceIdentifier;
127     if (is<ServiceWorkerGlobalScope>(context))
128         sourceIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
129     else {
130         auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(context.sessionID());
131         sourceIdentifier = ServiceWorkerClientIdentifier { connection.serverConnectionIdentifier(), downcast<Document>(context).identifier() };
132     }
133
134     callOnMainThread([sessionID = context.sessionID(), destinationIdentifier = identifier(), message = WTFMove(message), sourceIdentifier = WTFMove(sourceIdentifier)]() mutable {
135         auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(sessionID);
136         connection.postMessageToServiceWorker(destinationIdentifier, message.releaseReturnValue(), sourceIdentifier);
137     });
138     return { };
139 }
140
141 EventTargetInterface ServiceWorker::eventTargetInterface() const
142 {
143     return ServiceWorkerEventTargetInterfaceType;
144 }
145
146 ScriptExecutionContext* ServiceWorker::scriptExecutionContext() const
147 {
148     return ContextDestructionObserver::scriptExecutionContext();
149 }
150
151 const char* ServiceWorker::activeDOMObjectName() const
152 {
153     return "ServiceWorker";
154 }
155
156 bool ServiceWorker::canSuspendForDocumentSuspension() const
157 {
158     // FIXME: We should do better as this prevents the page from entering PageCache when there is a Service Worker.
159     return !hasPendingActivity();
160 }
161
162 void ServiceWorker::stop()
163 {
164     m_isStopped = true;
165     removeAllEventListeners();
166     scriptExecutionContext()->unregisterServiceWorker(*this);
167     updatePendingActivityForEventDispatch();
168 }
169
170 void ServiceWorker::updatePendingActivityForEventDispatch()
171 {
172     // ServiceWorkers can dispatch events until they become redundant or they are stopped.
173     if (m_isStopped || state() == State::Redundant) {
174         m_pendingActivityForEventDispatch = nullptr;
175         return;
176     }
177     if (m_pendingActivityForEventDispatch)
178         return;
179     m_pendingActivityForEventDispatch = makePendingActivity(*this);
180 }
181
182 bool ServiceWorker::isAlwaysOnLoggingAllowed() const
183 {
184     auto* context = scriptExecutionContext();
185     if (!context)
186         return false;
187
188     auto* container = context->serviceWorkerContainer();
189     if (!container)
190         return false;
191
192     return container->isAlwaysOnLoggingAllowed();
193 }
194
195 } // namespace WebCore
196
197 #endif // ENABLE(SERVICE_WORKER)