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