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