ServiceWorkers API should reject promises when calling objects inside detached frames
[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 && !channels->isEmpty())
115         return Exception { NotSupportedError, ASCIILiteral("Passing MessagePort objects to postMessage is not yet supported") };
116
117     if (is<ServiceWorkerGlobalScope>(context)) {
118         auto sourceWorkerIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
119         callOnMainThread([sessionID = context.sessionID(), destinationIdentifier = identifier(), sourceWorkerIdentifier, message = WTFMove(message)]() mutable {
120             auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(sessionID);
121             connection.postMessageToServiceWorker(destinationIdentifier, message.releaseReturnValue(), sourceWorkerIdentifier);
122         });
123         return { };
124     }
125
126     auto sourceClientData = ServiceWorkerClientData::from(context);
127     auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(context.sessionID());
128     ServiceWorkerClientIdentifier sourceClientIdentifier { connection.serverConnectionIdentifier(), downcast<Document>(context).identifier() };
129     connection.postMessageToServiceWorker(identifier(), message.releaseReturnValue(), WTFMove(sourceClientIdentifier), WTFMove(sourceClientData));
130
131     return { };
132 }
133
134 EventTargetInterface ServiceWorker::eventTargetInterface() const
135 {
136     return ServiceWorkerEventTargetInterfaceType;
137 }
138
139 ScriptExecutionContext* ServiceWorker::scriptExecutionContext() const
140 {
141     return ContextDestructionObserver::scriptExecutionContext();
142 }
143
144 const char* ServiceWorker::activeDOMObjectName() const
145 {
146     return "ServiceWorker";
147 }
148
149 bool ServiceWorker::canSuspendForDocumentSuspension() const
150 {
151     // FIXME: We should do better as this prevents the page from entering PageCache when there is a Service Worker.
152     return !hasPendingActivity();
153 }
154
155 void ServiceWorker::stop()
156 {
157     m_isStopped = true;
158     removeAllEventListeners();
159     scriptExecutionContext()->unregisterServiceWorker(*this);
160     updatePendingActivityForEventDispatch();
161 }
162
163 void ServiceWorker::updatePendingActivityForEventDispatch()
164 {
165     // ServiceWorkers can dispatch events until they become redundant or they are stopped.
166     if (m_isStopped || state() == State::Redundant) {
167         m_pendingActivityForEventDispatch = nullptr;
168         return;
169     }
170     if (m_pendingActivityForEventDispatch)
171         return;
172     m_pendingActivityForEventDispatch = makePendingActivity(*this);
173 }
174
175 } // namespace WebCore
176
177 #endif // ENABLE(SERVICE_WORKER)