2d74a381a6d74073328920a21f57a4a78b5dba80
[WebKit-https.git] / Source / WebCore / dom / ScriptExecutionContext.cpp
1 /*
2  * Copyright (C) 2008 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 COMPUTER, 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 COMPUTER, 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 #include "config.h"
29 #include "ScriptExecutionContext.h"
30
31 #include "CachedScript.h"
32 #include "DOMTimer.h"
33 #include "ErrorEvent.h"
34 #include "MessagePort.h"
35 #include "PublicURLManager.h"
36 #include "Settings.h"
37 #include "WorkerGlobalScope.h"
38 #include "WorkerThread.h"
39 #include <inspector/ScriptCallStack.h>
40 #include <wtf/MainThread.h>
41 #include <wtf/Ref.h>
42
43 // FIXME: This is a layering violation.
44 #include "JSDOMWindow.h"
45
46 #if PLATFORM(IOS)
47 #include "Document.h"
48 #endif
49
50 #if ENABLE(SQL_DATABASE)
51 #include "DatabaseContext.h"
52 #endif
53
54 using namespace Inspector;
55
56 namespace WebCore {
57
58 class ProcessMessagesSoonTask : public ScriptExecutionContext::Task {
59 public:
60     static PassOwnPtr<ProcessMessagesSoonTask> create()
61     {
62         return adoptPtr(new ProcessMessagesSoonTask);
63     }
64
65     virtual void performTask(ScriptExecutionContext* context) override
66     {
67         context->dispatchMessagePortEvents();
68     }
69 };
70
71 class ScriptExecutionContext::PendingException {
72     WTF_MAKE_NONCOPYABLE(PendingException);
73 public:
74     PendingException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
75         : m_errorMessage(errorMessage)
76         , m_lineNumber(lineNumber)
77         , m_columnNumber(columnNumber)
78         , m_sourceURL(sourceURL)
79         , m_callStack(callStack)
80     {
81     }
82     String m_errorMessage;
83     int m_lineNumber;
84     int m_columnNumber;
85     String m_sourceURL;
86     RefPtr<ScriptCallStack> m_callStack;
87 };
88
89 void ScriptExecutionContext::AddConsoleMessageTask::performTask(ScriptExecutionContext* context)
90 {
91     context->addConsoleMessage(m_source, m_level, m_message);
92 }
93
94 ScriptExecutionContext::ScriptExecutionContext()
95     : m_iteratingActiveDOMObjects(false)
96     , m_inDestructor(false)
97     , m_circularSequentialID(0)
98     , m_inDispatchErrorEvent(false)
99     , m_activeDOMObjectsAreSuspended(false)
100     , m_reasonForSuspendingActiveDOMObjects(static_cast<ActiveDOMObject::ReasonForSuspension>(-1))
101     , m_activeDOMObjectsAreStopped(false)
102 {
103 }
104
105 ScriptExecutionContext::~ScriptExecutionContext()
106 {
107     m_inDestructor = true;
108     for (HashSet<ContextDestructionObserver*>::iterator iter = m_destructionObservers.begin(); iter != m_destructionObservers.end(); iter = m_destructionObservers.begin()) {
109         ContextDestructionObserver* observer = *iter;
110         m_destructionObservers.remove(observer);
111         ASSERT(observer->scriptExecutionContext() == this);
112         observer->contextDestroyed();
113     }
114
115     HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
116     for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
117         ASSERT((*iter)->scriptExecutionContext() == this);
118         (*iter)->contextDestroyed();
119     }
120 }
121
122 void ScriptExecutionContext::processMessagePortMessagesSoon()
123 {
124     postTask(ProcessMessagesSoonTask::create());
125 }
126
127 void ScriptExecutionContext::dispatchMessagePortEvents()
128 {
129     Ref<ScriptExecutionContext> protect(*this);
130
131     // Make a frozen copy.
132     Vector<MessagePort*> ports;
133     copyToVector(m_messagePorts, ports);
134
135     unsigned portCount = ports.size();
136     for (unsigned i = 0; i < portCount; ++i) {
137         MessagePort* port = ports[i];
138         // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen
139         // as a result is that dispatchMessages() will be called needlessly.
140         if (m_messagePorts.contains(port) && port->started())
141             port->dispatchMessages();
142     }
143 }
144
145 void ScriptExecutionContext::createdMessagePort(MessagePort* port)
146 {
147     ASSERT(port);
148     ASSERT((isDocument() && isMainThread())
149         || (isWorkerGlobalScope() && currentThread() == toWorkerGlobalScope(this)->thread().threadID()));
150
151     m_messagePorts.add(port);
152 }
153
154 void ScriptExecutionContext::destroyedMessagePort(MessagePort* port)
155 {
156     ASSERT(port);
157     ASSERT((isDocument() && isMainThread())
158         || (isWorkerGlobalScope() && currentThread() == toWorkerGlobalScope(this)->thread().threadID()));
159
160     m_messagePorts.remove(port);
161 }
162
163 bool ScriptExecutionContext::canSuspendActiveDOMObjects()
164 {
165     // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS.
166     m_iteratingActiveDOMObjects = true;
167     ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end();
168     for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
169         ASSERT((*iter)->scriptExecutionContext() == this);
170         ASSERT((*iter)->suspendIfNeededCalled());
171         if (!(*iter)->canSuspend()) {
172             m_iteratingActiveDOMObjects = false;
173             return false;
174         }
175     }
176     m_iteratingActiveDOMObjects = false;
177     return true;
178 }
179
180 void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
181 {
182 #if PLATFORM(IOS)
183     if (m_activeDOMObjectsAreSuspended) {
184         ASSERT(m_reasonForSuspendingActiveDOMObjects == ActiveDOMObject::DocumentWillBePaused);
185         return;
186     }
187 #endif
188
189     // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS.
190     m_iteratingActiveDOMObjects = true;
191     ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end();
192     for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
193         ASSERT((*iter)->scriptExecutionContext() == this);
194         ASSERT((*iter)->suspendIfNeededCalled());
195         (*iter)->suspend(why);
196     }
197     m_iteratingActiveDOMObjects = false;
198     m_activeDOMObjectsAreSuspended = true;
199     m_reasonForSuspendingActiveDOMObjects = why;
200 }
201
202 void ScriptExecutionContext::resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
203 {
204     if (m_reasonForSuspendingActiveDOMObjects != why)
205         return;
206
207     m_activeDOMObjectsAreSuspended = false;
208     // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS.
209     m_iteratingActiveDOMObjects = true;
210     ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end();
211     for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
212         ASSERT((*iter)->scriptExecutionContext() == this);
213         ASSERT((*iter)->suspendIfNeededCalled());
214         (*iter)->resume();
215     }
216     m_iteratingActiveDOMObjects = false;
217 }
218
219 void ScriptExecutionContext::stopActiveDOMObjects()
220 {
221     if (m_activeDOMObjectsAreStopped)
222         return;
223     m_activeDOMObjectsAreStopped = true;
224     // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS.
225     m_iteratingActiveDOMObjects = true;
226     ActiveDOMObjectsSet::iterator activeObjectsEnd = m_activeDOMObjects.end();
227     for (ActiveDOMObjectsSet::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
228         ASSERT((*iter)->scriptExecutionContext() == this);
229         ASSERT((*iter)->suspendIfNeededCalled());
230         (*iter)->stop();
231     }
232     m_iteratingActiveDOMObjects = false;
233
234     // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead.
235     closeMessagePorts();
236 }
237
238 void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject* object)
239 {
240     ASSERT(m_activeDOMObjects.contains(object));
241     // Ensure all ActiveDOMObjects are suspended also newly created ones.
242     if (m_activeDOMObjectsAreSuspended)
243         object->suspend(m_reasonForSuspendingActiveDOMObjects);
244     if (m_activeDOMObjectsAreStopped)
245         object->stop();
246 }
247
248 void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject* object)
249 {
250     ASSERT(object);
251     ASSERT(!m_inDestructor);
252     if (m_iteratingActiveDOMObjects)
253         CRASH();
254     m_activeDOMObjects.add(object);
255 }
256
257 void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject* object)
258 {
259     ASSERT(object);
260     if (m_iteratingActiveDOMObjects)
261         CRASH();
262     m_activeDOMObjects.remove(object);
263 }
264
265 void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver* observer)
266 {
267     ASSERT(observer);
268     ASSERT(!m_inDestructor);
269     m_destructionObservers.add(observer);
270 }
271
272 void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver* observer)
273 {
274     ASSERT(observer);
275     m_destructionObservers.remove(observer);
276 }
277
278 void ScriptExecutionContext::closeMessagePorts() {
279     HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
280     for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
281         ASSERT((*iter)->scriptExecutionContext() == this);
282         (*iter)->close();
283     }
284 }
285
286 bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, CachedScript* cachedScript)
287 {
288     URL targetURL = completeURL(sourceURL);
289     if (securityOrigin()->canRequest(targetURL) || (cachedScript && cachedScript->passesAccessControlCheck(securityOrigin())))
290         return false;
291     errorMessage = "Script error.";
292     sourceURL = String();
293     lineNumber = 0;
294     columnNumber = 0;
295     return true;
296 }
297
298 void ScriptExecutionContext::reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack, CachedScript* cachedScript)
299 {
300     if (m_inDispatchErrorEvent) {
301         if (!m_pendingExceptions)
302             m_pendingExceptions = adoptPtr(new Vector<OwnPtr<PendingException>>());
303         m_pendingExceptions->append(adoptPtr(new PendingException(errorMessage, lineNumber, columnNumber, sourceURL, callStack)));
304         return;
305     }
306
307     // First report the original exception and only then all the nested ones.
308     if (!dispatchErrorEvent(errorMessage, lineNumber, columnNumber, sourceURL, cachedScript))
309         logExceptionToConsole(errorMessage, sourceURL, lineNumber, columnNumber, callStack);
310
311     if (!m_pendingExceptions)
312         return;
313
314     for (size_t i = 0; i < m_pendingExceptions->size(); i++) {
315         PendingException* e = m_pendingExceptions->at(i).get();
316         logExceptionToConsole(e->m_errorMessage, e->m_sourceURL, e->m_lineNumber, e->m_columnNumber, e->m_callStack);
317     }
318     m_pendingExceptions.clear();
319 }
320
321 void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* state, unsigned long requestIdentifier)
322 {
323     addMessage(source, level, message, sourceURL, lineNumber, columnNumber, 0, state, requestIdentifier);
324 }
325
326 bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, CachedScript* cachedScript)
327 {
328     EventTarget* target = errorEventTarget();
329     if (!target)
330         return false;
331
332 #if PLATFORM(IOS)
333     if (target == target->toDOMWindow() && isDocument()) {
334         Settings* settings = static_cast<Document*>(this)->settings();
335         if (settings && !settings->shouldDispatchJavaScriptWindowOnErrorEvents())
336             return false;
337     }
338 #endif
339
340     String message = errorMessage;
341     int line = lineNumber;
342     int column = columnNumber;
343     String sourceName = sourceURL;
344     sanitizeScriptError(message, line, column, sourceName, cachedScript);
345
346     ASSERT(!m_inDispatchErrorEvent);
347     m_inDispatchErrorEvent = true;
348     RefPtr<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line, column);
349     target->dispatchEvent(errorEvent);
350     m_inDispatchErrorEvent = false;
351     return errorEvent->defaultPrevented();
352 }
353
354 int ScriptExecutionContext::circularSequentialID()
355 {
356     ++m_circularSequentialID;
357     if (m_circularSequentialID <= 0)
358         m_circularSequentialID = 1;
359     return m_circularSequentialID;
360 }
361
362 #if ENABLE(BLOB)
363 PublicURLManager& ScriptExecutionContext::publicURLManager()
364 {
365     if (!m_publicURLManager)
366         m_publicURLManager = PublicURLManager::create(this);
367     return *m_publicURLManager;
368 }
369 #endif
370
371 void ScriptExecutionContext::adjustMinimumTimerInterval(double oldMinimumTimerInterval)
372 {
373     if (minimumTimerInterval() != oldMinimumTimerInterval) {
374         for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) {
375             DOMTimer* timer = iter->value;
376             timer->adjustMinimumTimerInterval(oldMinimumTimerInterval);
377         }
378     }
379 }
380
381 double ScriptExecutionContext::minimumTimerInterval() const
382 {
383     // The default implementation returns the DOMTimer's default
384     // minimum timer interval. FIXME: to make it work with dedicated
385     // workers, we will have to override it in the appropriate
386     // subclass, and provide a way to enumerate a Document's dedicated
387     // workers so we can update them all.
388     return Settings::defaultMinDOMTimerInterval();
389 }
390
391 void ScriptExecutionContext::didChangeTimerAlignmentInterval()
392 {
393     for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) {
394         DOMTimer* timer = iter->value;
395         timer->didChangeAlignmentInterval();
396     }
397 }
398
399 double ScriptExecutionContext::timerAlignmentInterval() const
400 {
401     return Settings::defaultDOMTimerAlignmentInterval();
402 }
403
404 ScriptExecutionContext::Task::~Task()
405 {
406 }
407
408 JSC::VM* ScriptExecutionContext::vm()
409 {
410      if (isDocument())
411         return JSDOMWindow::commonVM();
412
413     if (isWorkerGlobalScope())
414         return toWorkerGlobalScope(this)->script()->vm();
415
416     ASSERT_NOT_REACHED();
417     return 0;
418 }
419
420 #if ENABLE(SQL_DATABASE)
421 void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext)
422 {
423     ASSERT(!m_databaseContext);
424     m_databaseContext = databaseContext;
425 }
426 #endif
427
428 } // namespace WebCore