1/*
2 * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2012 Google Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28#pragma once
29
30#include "ActiveDOMObject.h"
31#include "DOMTimer.h"
32#include "SecurityContext.h"
33#include "ServiceWorkerTypes.h"
34#include <JavaScriptCore/ConsoleTypes.h>
35#include <JavaScriptCore/HandleTypes.h>
36#include <wtf/CrossThreadTask.h>
37#include <wtf/Function.h>
38#include <wtf/HashSet.h>
39#include <wtf/ObjectIdentifier.h>
40#include <wtf/text/WTFString.h>
41
42namespace JSC {
43class Exception;
44class ExecState;
45class JSPromise;
46class VM;
47template<typename> class Strong;
48}
49
50namespace Inspector {
51class ConsoleMessage;
52class ScriptCallStack;
53}
54
55namespace PAL {
56class SessionID;
57}
58
59namespace WebCore {
60
61class CachedScript;
62class DatabaseContext;
63class EventQueue;
64class EventTarget;
65class MessagePort;
66class PublicURLManager;
67class RejectedPromiseTracker;
68class ResourceRequest;
69class SecurityOrigin;
70class SocketProvider;
71
72#if ENABLE(SERVICE_WORKER)
73class ServiceWorker;
74class ServiceWorkerContainer;
75#endif
76
77namespace IDBClient {
78class IDBConnectionProxy;
79}
80
81enum ScriptExecutionContextIdentifierType { };
82using ScriptExecutionContextIdentifier = ObjectIdentifier<ScriptExecutionContextIdentifierType>;
83
84class ScriptExecutionContext : public SecurityContext {
85public:
86 ScriptExecutionContext();
87 virtual ~ScriptExecutionContext();
88
89 virtual bool isDocument() const { return false; }
90 virtual bool isWorkerGlobalScope() const { return false; }
91 virtual bool isWorkletGlobalScope() const { return false; }
92
93 virtual bool isContextThread() const { return true; }
94 virtual bool isJSExecutionForbidden() const = 0;
95
96 virtual const URL& url() const = 0;
97 virtual URL completeURL(const String& url) const = 0;
98 virtual PAL::SessionID sessionID() const = 0;
99
100 virtual String userAgent(const URL&) const = 0;
101
102 virtual void disableEval(const String& errorMessage) = 0;
103 virtual void disableWebAssembly(const String& errorMessage) = 0;
104
105#if ENABLE(INDEXED_DATABASE)
106 virtual IDBClient::IDBConnectionProxy* idbConnectionProxy() = 0;
107#endif
108 virtual SocketProvider* socketProvider() = 0;
109
110 virtual String resourceRequestIdentifier() const { return String(); };
111
112 bool canIncludeErrorDetails(CachedScript*, const String& sourceURL);
113 void reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception*, RefPtr<Inspector::ScriptCallStack>&&, CachedScript* = nullptr);
114 void reportUnhandledPromiseRejection(JSC::ExecState&, JSC::JSPromise&, RefPtr<Inspector::ScriptCallStack>&&);
115
116 virtual void addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&&) = 0;
117
118 // The following addConsoleMessage functions are deprecated.
119 // Callers should try to create the ConsoleMessage themselves.
120 void addConsoleMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0);
121 virtual void addConsoleMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0) = 0;
122
123 virtual SecurityOrigin& topOrigin() const = 0;
124 virtual String origin() const = 0;
125
126 virtual bool shouldBypassMainWorldContentSecurityPolicy() const { return false; }
127
128 PublicURLManager& publicURLManager();
129
130 // Active objects are not garbage collected even if inaccessible, e.g. because their activity may result in callbacks being invoked.
131 WEBCORE_EXPORT bool canSuspendActiveDOMObjectsForDocumentSuspension(Vector<ActiveDOMObject*>* unsuspendableObjects = nullptr);
132
133 // Active objects can be asked to suspend even if canSuspendActiveDOMObjectsForDocumentSuspension() returns 'false' -
134 // step-by-step JS debugging is one example.
135 virtual void suspendActiveDOMObjects(ReasonForSuspension);
136 virtual void resumeActiveDOMObjects(ReasonForSuspension);
137 virtual void stopActiveDOMObjects();
138
139 bool activeDOMObjectsAreSuspended() const { return m_activeDOMObjectsAreSuspended; }
140 bool activeDOMObjectsAreStopped() const { return m_activeDOMObjectsAreStopped; }
141
142 // Called from the constructor and destructors of ActiveDOMObject.
143 void didCreateActiveDOMObject(ActiveDOMObject&);
144 void willDestroyActiveDOMObject(ActiveDOMObject&);
145
146 // Called after the construction of an ActiveDOMObject to synchronize suspend state.
147 void suspendActiveDOMObjectIfNeeded(ActiveDOMObject&);
148
149 void didCreateDestructionObserver(ContextDestructionObserver&);
150 void willDestroyDestructionObserver(ContextDestructionObserver&);
151
152 // MessagePort is conceptually a kind of ActiveDOMObject, but it needs to be tracked separately for message dispatch.
153 void processMessageWithMessagePortsSoon();
154 void dispatchMessagePortEvents();
155 void createdMessagePort(MessagePort&);
156 void destroyedMessagePort(MessagePort&);
157
158 virtual void didLoadResourceSynchronously();
159
160 void ref() { refScriptExecutionContext(); }
161 void deref() { derefScriptExecutionContext(); }
162
163 class Task {
164 WTF_MAKE_FAST_ALLOCATED;
165 public:
166 enum CleanupTaskTag { CleanupTask };
167
168 template<typename T, typename = typename std::enable_if<!std::is_base_of<Task, T>::value && std::is_convertible<T, WTF::Function<void (ScriptExecutionContext&)>>::value>::type>
169 Task(T task)
170 : m_task(WTFMove(task))
171 , m_isCleanupTask(false)
172 {
173 }
174
175 Task(WTF::Function<void ()>&& task)
176 : m_task([task = WTFMove(task)](ScriptExecutionContext&) { task(); })
177 , m_isCleanupTask(false)
178 {
179 }
180
181 template<typename T, typename = typename std::enable_if<std::is_convertible<T, WTF::Function<void (ScriptExecutionContext&)>>::value>::type>
182 Task(CleanupTaskTag, T task)
183 : m_task(WTFMove(task))
184 , m_isCleanupTask(true)
185 {
186 }
187
188 void performTask(ScriptExecutionContext& context) { m_task(context); }
189 bool isCleanupTask() const { return m_isCleanupTask; }
190
191 protected:
192 WTF::Function<void (ScriptExecutionContext&)> m_task;
193 bool m_isCleanupTask;
194 };
195
196 virtual void postTask(Task&&) = 0; // Executes the task on context's thread asynchronously.
197
198 template<typename... Arguments>
199 void postCrossThreadTask(Arguments&&... arguments)
200 {
201 postTask([crossThreadTask = createCrossThreadTask(arguments...)](ScriptExecutionContext&) mutable {
202 crossThreadTask.performTask();
203 });
204 }
205
206 // Gets the next id in a circular sequence from 1 to 2^31-1.
207 int circularSequentialID();
208
209 bool addTimeout(int timeoutId, DOMTimer& timer) { return m_timeouts.add(timeoutId, &timer).isNewEntry; }
210 void removeTimeout(int timeoutId) { m_timeouts.remove(timeoutId); }
211 DOMTimer* findTimeout(int timeoutId) { return m_timeouts.get(timeoutId); }
212
213 WEBCORE_EXPORT JSC::VM& vm();
214
215 void adjustMinimumDOMTimerInterval(Seconds oldMinimumTimerInterval);
216 virtual Seconds minimumDOMTimerInterval() const;
217
218 void didChangeTimerAlignmentInterval();
219 virtual Seconds domTimerAlignmentInterval(bool hasReachedMaxNestingLevel) const;
220
221 virtual EventQueue& eventQueue() const = 0;
222 virtual EventTarget* errorEventTarget() = 0;
223
224 DatabaseContext* databaseContext() { return m_databaseContext.get(); }
225 void setDatabaseContext(DatabaseContext*);
226
227#if ENABLE(WEB_CRYPTO)
228 virtual bool wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) = 0;
229 virtual bool unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) = 0;
230#endif
231
232 int timerNestingLevel() const { return m_timerNestingLevel; }
233 void setTimerNestingLevel(int timerNestingLevel) { m_timerNestingLevel = timerNestingLevel; }
234
235 RejectedPromiseTracker& ensureRejectedPromiseTracker()
236 {
237 if (m_rejectedPromiseTracker)
238 return *m_rejectedPromiseTracker.get();
239 return ensureRejectedPromiseTrackerSlow();
240 }
241
242 WEBCORE_EXPORT JSC::ExecState* execState();
243
244 WEBCORE_EXPORT String domainForCachePartition() const;
245 void setDomainForCachePartition(String&& domain) { m_domainForCachePartition = WTFMove(domain); }
246
247 bool allowsMediaDevices() const;
248 bool hasServiceWorkerScheme() const;
249#if ENABLE(SERVICE_WORKER)
250 ServiceWorker* activeServiceWorker() const;
251 void setActiveServiceWorker(RefPtr<ServiceWorker>&&);
252
253 void registerServiceWorker(ServiceWorker&);
254 void unregisterServiceWorker(ServiceWorker&);
255 ServiceWorker* serviceWorker(ServiceWorkerIdentifier identifier) { return m_serviceWorkers.get(identifier); }
256
257 ServiceWorkerContainer* serviceWorkerContainer();
258
259 WEBCORE_EXPORT static bool postTaskTo(const DocumentOrWorkerIdentifier&, WTF::Function<void(ScriptExecutionContext&)>&&);
260#endif
261 WEBCORE_EXPORT static bool postTaskTo(ScriptExecutionContextIdentifier, Task&&);
262
263 ScriptExecutionContextIdentifier contextIdentifier() const;
264
265protected:
266 class AddConsoleMessageTask : public Task {
267 public:
268 AddConsoleMessageTask(std::unique_ptr<Inspector::ConsoleMessage>&& consoleMessage)
269 : Task([&consoleMessage](ScriptExecutionContext& context) {
270 context.addConsoleMessage(WTFMove(consoleMessage));
271 })
272 {
273 }
274
275 AddConsoleMessageTask(MessageSource source, MessageLevel level, const String& message)
276 : Task([source, level, message = message.isolatedCopy()](ScriptExecutionContext& context) {
277 context.addConsoleMessage(source, level, message);
278 })
279 {
280 }
281 };
282
283 ReasonForSuspension reasonForSuspendingActiveDOMObjects() const { return m_reasonForSuspendingActiveDOMObjects; }
284
285 bool hasPendingActivity() const;
286 void removeFromContextsMap();
287 void removeRejectedPromiseTracker();
288
289private:
290 // The following addMessage function is deprecated.
291 // Callers should try to create the ConsoleMessage themselves.
292 virtual void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<Inspector::ScriptCallStack>&&, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0) = 0;
293 virtual void logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, RefPtr<Inspector::ScriptCallStack>&&) = 0;
294 bool dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception*, CachedScript*);
295
296 virtual void refScriptExecutionContext() = 0;
297 virtual void derefScriptExecutionContext() = 0;
298
299 enum class ShouldContinue { No, Yes };
300 void forEachActiveDOMObject(const Function<ShouldContinue(ActiveDOMObject&)>&) const;
301
302 RejectedPromiseTracker& ensureRejectedPromiseTrackerSlow();
303
304 void checkConsistency() const;
305
306 HashSet<MessagePort*> m_messagePorts;
307 HashSet<ContextDestructionObserver*> m_destructionObservers;
308 HashSet<ActiveDOMObject*> m_activeDOMObjects;
309
310 HashMap<int, RefPtr<DOMTimer>> m_timeouts;
311
312 struct PendingException;
313 std::unique_ptr<Vector<std::unique_ptr<PendingException>>> m_pendingExceptions;
314 std::unique_ptr<RejectedPromiseTracker> m_rejectedPromiseTracker;
315
316 ReasonForSuspension m_reasonForSuspendingActiveDOMObjects { static_cast<ReasonForSuspension>(-1) };
317
318 std::unique_ptr<PublicURLManager> m_publicURLManager;
319
320 RefPtr<DatabaseContext> m_databaseContext;
321
322 int m_circularSequentialID { 0 };
323 int m_timerNestingLevel { 0 };
324
325 bool m_activeDOMObjectsAreSuspended { false };
326 bool m_activeDOMObjectsAreStopped { false };
327 bool m_inDispatchErrorEvent { false };
328 mutable bool m_activeDOMObjectAdditionForbidden { false };
329 bool m_willprocessMessageWithMessagePortsSoon { false };
330
331#if !ASSERT_DISABLED
332 bool m_inScriptExecutionContextDestructor { false };
333#endif
334
335#if ENABLE(SERVICE_WORKER)
336 RefPtr<ServiceWorker> m_activeServiceWorker;
337 HashMap<ServiceWorkerIdentifier, ServiceWorker*> m_serviceWorkers;
338#endif
339
340 String m_domainForCachePartition;
341 mutable ScriptExecutionContextIdentifier m_contextIdentifier;
342};
343
344} // namespace WebCore
345