initial import
[vuplus_webkit] / Source / WebCore / dom / ScriptExecutionContext.cpp
1 /*
2  * Copyright (C) 2008 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 COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26
27 #include "config.h"
28 #include "ScriptExecutionContext.h"
29
30 #include "ActiveDOMObject.h"
31 #include "Blob.h"
32 #include "BlobURL.h"
33 #include "DOMTimer.h"
34 #include "DOMURL.h"
35 #include "Database.h"
36 #include "DatabaseTask.h"
37 #include "DatabaseThread.h"
38 #include "ErrorEvent.h"
39 #include "EventListener.h"
40 #include "EventTarget.h"
41 #include "FileThread.h"
42 #include "MediaStream.h"
43 #include "MediaStreamRegistry.h"
44 #include "MessagePort.h"
45 #include "ScriptCallStack.h"
46 #include "SecurityOrigin.h"
47 #include "Settings.h"
48 #include "ThreadableBlobRegistry.h"
49 #include "WorkerContext.h"
50 #include "WorkerThread.h"
51 #include <wtf/MainThread.h>
52 #include <wtf/PassRefPtr.h>
53 #include <wtf/Vector.h>
54
55 #if USE(JSC)
56 #include "JSDOMWindow.h"
57 #endif
58
59 namespace WebCore {
60
61 class ProcessMessagesSoonTask : public ScriptExecutionContext::Task {
62 public:
63     static PassOwnPtr<ProcessMessagesSoonTask> create()
64     {
65         return adoptPtr(new ProcessMessagesSoonTask);
66     }
67
68     virtual void performTask(ScriptExecutionContext* context)
69     {
70         context->dispatchMessagePortEvents();
71     }
72 };
73
74 class ScriptExecutionContext::PendingException {
75     WTF_MAKE_NONCOPYABLE(PendingException);
76 public:
77     PendingException(const String& errorMessage, int lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
78         : m_errorMessage(errorMessage)
79         , m_lineNumber(lineNumber)
80         , m_sourceURL(sourceURL)
81         , m_callStack(callStack)
82     {
83     }
84     String m_errorMessage;
85     int m_lineNumber;
86     String m_sourceURL;
87     RefPtr<ScriptCallStack> m_callStack;
88 };
89
90 ScriptExecutionContext::ScriptExecutionContext()
91     : m_iteratingActiveDOMObjects(false)
92     , m_inDestructor(false)
93     , m_inDispatchErrorEvent(false)
94 #if ENABLE(DATABASE)
95     , m_hasOpenDatabases(false)
96 #endif
97 {
98 }
99
100 ScriptExecutionContext::~ScriptExecutionContext()
101 {
102     m_inDestructor = true;
103     for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != m_activeDOMObjects.end(); iter = m_activeDOMObjects.begin()) {
104         ActiveDOMObject* object = iter->first;
105         m_activeDOMObjects.remove(iter);
106         ASSERT(object->scriptExecutionContext() == this);
107         object->contextDestroyed();
108     }
109
110     HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
111     for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
112         ASSERT((*iter)->scriptExecutionContext() == this);
113         (*iter)->contextDestroyed();
114     }
115 #if ENABLE(DATABASE)
116     if (m_databaseThread) {
117         ASSERT(m_databaseThread->terminationRequested());
118         m_databaseThread = 0;
119     }
120 #endif
121 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
122     if (m_fileThread) {
123         m_fileThread->stop();
124         m_fileThread = 0;
125     }
126 #endif
127
128 #if ENABLE(BLOB)
129     HashSet<String>::iterator publicBlobURLsEnd = m_publicBlobURLs.end();
130     for (HashSet<String>::iterator iter = m_publicBlobURLs.begin(); iter != publicBlobURLsEnd; ++iter)
131         ThreadableBlobRegistry::unregisterBlobURL(KURL(ParsedURLString, *iter));
132
133     HashSet<DOMURL*>::iterator domUrlsEnd = m_domUrls.end();
134     for (HashSet<DOMURL*>::iterator iter = m_domUrls.begin(); iter != domUrlsEnd; ++iter) {
135         ASSERT((*iter)->scriptExecutionContext() == this);
136         (*iter)->contextDestroyed();
137     }
138 #endif
139
140 #if ENABLE(MEDIA_STREAM)
141     HashSet<String>::iterator publicStreamURLsEnd = m_publicStreamURLs.end();
142     for (HashSet<String>::iterator iter = m_publicStreamURLs.begin(); iter != publicStreamURLsEnd; ++iter)
143         MediaStreamRegistry::registry().unregisterMediaStreamURL(KURL(ParsedURLString, *iter));
144 #endif
145 }
146
147 #if ENABLE(DATABASE)
148
149 DatabaseThread* ScriptExecutionContext::databaseThread()
150 {
151     if (!m_databaseThread && !m_hasOpenDatabases) {
152         // Create the database thread on first request - but not if at least one database was already opened,
153         // because in that case we already had a database thread and terminated it and should not create another.
154         m_databaseThread = DatabaseThread::create();
155         if (!m_databaseThread->start())
156             m_databaseThread = 0;
157     }
158
159     return m_databaseThread.get();
160 }
161
162 void ScriptExecutionContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync)
163 {
164     ASSERT(isContextThread());
165     if (m_databaseThread)
166         m_databaseThread->requestTermination(cleanupSync);
167     else if (cleanupSync)
168         cleanupSync->taskCompleted();
169 }
170
171 #endif
172
173 void ScriptExecutionContext::processMessagePortMessagesSoon()
174 {
175     postTask(ProcessMessagesSoonTask::create());
176 }
177
178 void ScriptExecutionContext::dispatchMessagePortEvents()
179 {
180     RefPtr<ScriptExecutionContext> protect(this);
181
182     // Make a frozen copy.
183     Vector<MessagePort*> ports;
184     copyToVector(m_messagePorts, ports);
185
186     unsigned portCount = ports.size();
187     for (unsigned i = 0; i < portCount; ++i) {
188         MessagePort* port = ports[i];
189         // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen
190         // as a result is that dispatchMessages() will be called needlessly.
191         if (m_messagePorts.contains(port) && port->started())
192             port->dispatchMessages();
193     }
194 }
195
196 void ScriptExecutionContext::createdMessagePort(MessagePort* port)
197 {
198     ASSERT(port);
199 #if ENABLE(WORKERS)
200     ASSERT((isDocument() && isMainThread())
201         || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
202 #endif
203
204     m_messagePorts.add(port);
205 }
206
207 void ScriptExecutionContext::destroyedMessagePort(MessagePort* port)
208 {
209     ASSERT(port);
210 #if ENABLE(WORKERS)
211     ASSERT((isDocument() && isMainThread())
212         || (isWorkerContext() && currentThread() == static_cast<WorkerContext*>(this)->thread()->threadID()));
213 #endif
214
215     m_messagePorts.remove(port);
216 }
217
218 #if ENABLE(BLOB)
219 void ScriptExecutionContext::createdDomUrl(DOMURL* url)
220 {
221     ASSERT(url);
222     m_domUrls.add(url);
223 }
224
225 void ScriptExecutionContext::destroyedDomUrl(DOMURL* url)
226 {
227     ASSERT(url);
228     m_domUrls.remove(url);
229 }
230 #endif
231
232 bool ScriptExecutionContext::canSuspendActiveDOMObjects()
233 {
234     // No protection against m_activeDOMObjects changing during iteration: canSuspend() shouldn't execute arbitrary JS.
235     m_iteratingActiveDOMObjects = true;
236     HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
237     for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
238         ASSERT(iter->first->scriptExecutionContext() == this);
239         if (!iter->first->canSuspend()) {
240             m_iteratingActiveDOMObjects = false;
241             return false;
242         }
243     }    
244     m_iteratingActiveDOMObjects = false;
245     return true;
246 }
247
248 void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
249 {
250     // No protection against m_activeDOMObjects changing during iteration: suspend() shouldn't execute arbitrary JS.
251     m_iteratingActiveDOMObjects = true;
252     HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
253     for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
254         ASSERT(iter->first->scriptExecutionContext() == this);
255         iter->first->suspend(why);
256     }
257     m_iteratingActiveDOMObjects = false;
258 }
259
260 void ScriptExecutionContext::resumeActiveDOMObjects()
261 {
262     // No protection against m_activeDOMObjects changing during iteration: resume() shouldn't execute arbitrary JS.
263     m_iteratingActiveDOMObjects = true;
264     HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
265     for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
266         ASSERT(iter->first->scriptExecutionContext() == this);
267         iter->first->resume();
268     }
269     m_iteratingActiveDOMObjects = false;
270 }
271
272 void ScriptExecutionContext::stopActiveDOMObjects()
273 {
274     // No protection against m_activeDOMObjects changing during iteration: stop() shouldn't execute arbitrary JS.
275     m_iteratingActiveDOMObjects = true;
276     HashMap<ActiveDOMObject*, void*>::iterator activeObjectsEnd = m_activeDOMObjects.end();
277     for (HashMap<ActiveDOMObject*, void*>::iterator iter = m_activeDOMObjects.begin(); iter != activeObjectsEnd; ++iter) {
278         ASSERT(iter->first->scriptExecutionContext() == this);
279         iter->first->stop();
280     }
281     m_iteratingActiveDOMObjects = false;
282
283     // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead.
284     closeMessagePorts();
285 }
286
287 void ScriptExecutionContext::createdActiveDOMObject(ActiveDOMObject* object, void* upcastPointer)
288 {
289     ASSERT(object);
290     ASSERT(upcastPointer);
291     ASSERT(!m_inDestructor);
292     if (m_iteratingActiveDOMObjects)
293         CRASH();
294     m_activeDOMObjects.add(object, upcastPointer);
295 }
296
297 void ScriptExecutionContext::destroyedActiveDOMObject(ActiveDOMObject* object)
298 {
299     ASSERT(object);
300     if (m_iteratingActiveDOMObjects)
301         CRASH();
302     m_activeDOMObjects.remove(object);
303 }
304
305 void ScriptExecutionContext::closeMessagePorts() {
306     HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end();
307     for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) {
308         ASSERT((*iter)->scriptExecutionContext() == this);
309         (*iter)->close();
310     }
311 }
312
313 void ScriptExecutionContext::setSecurityOrigin(PassRefPtr<SecurityOrigin> securityOrigin)
314 {
315     m_securityOrigin = securityOrigin;
316 }
317
318 bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, String& sourceURL)
319 {
320     KURL targetURL = completeURL(sourceURL);
321     if (securityOrigin()->canRequest(targetURL))
322         return false;
323     errorMessage = "Script error.";
324     sourceURL = String();
325     lineNumber = 0;
326     return true;
327 }
328
329 void ScriptExecutionContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack)
330 {
331     if (m_inDispatchErrorEvent) {
332         if (!m_pendingExceptions)
333             m_pendingExceptions = adoptPtr(new Vector<OwnPtr<PendingException> >());
334         m_pendingExceptions->append(adoptPtr(new PendingException(errorMessage, lineNumber, sourceURL, callStack)));
335         return;
336     }
337
338     // First report the original exception and only then all the nested ones.
339     if (!dispatchErrorEvent(errorMessage, lineNumber, sourceURL))
340         logExceptionToConsole(errorMessage, lineNumber, sourceURL, callStack);
341
342     if (!m_pendingExceptions)
343         return;
344
345     for (size_t i = 0; i < m_pendingExceptions->size(); i++) {
346         PendingException* e = m_pendingExceptions->at(i).get();
347         logExceptionToConsole(e->m_errorMessage, e->m_lineNumber, e->m_sourceURL, e->m_callStack);
348     }
349     m_pendingExceptions.clear();
350 }
351
352 bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, const String& sourceURL)
353 {
354     EventTarget* target = errorEventTarget();
355     if (!target)
356         return false;
357
358     String message = errorMessage;
359     int line = lineNumber;
360     String sourceName = sourceURL;
361     sanitizeScriptError(message, line, sourceName);
362
363     ASSERT(!m_inDispatchErrorEvent);
364     m_inDispatchErrorEvent = true;
365     RefPtr<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line);
366     target->dispatchEvent(errorEvent);
367     m_inDispatchErrorEvent = false;
368     return errorEvent->defaultPrevented();
369 }
370
371 void ScriptExecutionContext::addTimeout(int timeoutId, DOMTimer* timer)
372 {
373     ASSERT(!m_timeouts.contains(timeoutId));
374     m_timeouts.set(timeoutId, timer);
375 }
376
377 void ScriptExecutionContext::removeTimeout(int timeoutId)
378 {
379     m_timeouts.remove(timeoutId);
380 }
381
382 DOMTimer* ScriptExecutionContext::findTimeout(int timeoutId)
383 {
384     return m_timeouts.get(timeoutId);
385 }
386
387 #if ENABLE(BLOB)
388
389 #if ENABLE(MEDIA_STREAM)
390 KURL ScriptExecutionContext::createPublicBlobURL(MediaStream* stream)
391 {
392     if (!stream)
393         return KURL();
394
395     KURL publicURL = BlobURL::createPublicURL(securityOrigin());
396
397     // Since WebWorkers cannot obtain Stream objects, we should be on the main thread.
398     ASSERT(isMainThread());
399     MediaStreamRegistry::registry().registerMediaStreamURL(publicURL, stream);
400     m_publicStreamURLs.add(publicURL.string());
401     return publicURL;
402 }
403 #endif // ENABLE(MEDIA_STREAM)
404
405 KURL ScriptExecutionContext::createPublicBlobURL(Blob* blob)
406 {
407     if (!blob)
408         return KURL();
409     KURL publicURL = BlobURL::createPublicURL(securityOrigin());
410     if (publicURL.isEmpty())
411         return KURL();
412     ThreadableBlobRegistry::registerBlobURL(publicURL, blob->url());
413     m_publicBlobURLs.add(publicURL.string());
414     return publicURL;
415 }
416
417 void ScriptExecutionContext::revokePublicBlobURL(const KURL& url)
418 {
419     if (m_publicBlobURLs.contains(url.string())) {
420         ThreadableBlobRegistry::unregisterBlobURL(url);
421         m_publicBlobURLs.remove(url.string());
422     }
423 #if ENABLE(MEDIA_STREAM)
424     if (m_publicStreamURLs.contains(url.string())) {
425         // FIXME: make sure of this assertion below. Raise a spec question if required.
426         // Since WebWorkers cannot obtain Stream objects, we should be on the main thread.
427         ASSERT(isMainThread());
428         MediaStreamRegistry::registry().unregisterMediaStreamURL(url);
429         m_publicStreamURLs.remove(url.string());
430     }
431 #endif // ENABLE(MEDIA_STREAM)
432 }
433 #endif // ENABLE(BLOB)
434
435 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
436 FileThread* ScriptExecutionContext::fileThread()
437 {
438     if (!m_fileThread) {
439         m_fileThread = FileThread::create();
440         if (!m_fileThread->start())
441             m_fileThread = 0;
442     }
443     return m_fileThread.get();
444 }
445 #endif
446
447 void ScriptExecutionContext::adjustMinimumTimerInterval(double oldMinimumTimerInterval)
448 {
449     if (minimumTimerInterval() != oldMinimumTimerInterval) {
450         for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) {
451             DOMTimer* timer = iter->second;
452             timer->adjustMinimumTimerInterval(oldMinimumTimerInterval);
453         }
454     }
455 }
456
457 double ScriptExecutionContext::minimumTimerInterval() const
458 {
459     // The default implementation returns the DOMTimer's default
460     // minimum timer interval. FIXME: to make it work with dedicated
461     // workers, we will have to override it in the appropriate
462     // subclass, and provide a way to enumerate a Document's dedicated
463     // workers so we can update them all.
464     return Settings::defaultMinDOMTimerInterval();
465 }
466
467 ScriptExecutionContext::Task::~Task()
468 {
469 }
470
471 #if USE(JSC)
472 JSC::JSGlobalData* ScriptExecutionContext::globalData()
473 {
474      if (isDocument())
475         return JSDOMWindow::commonJSGlobalData();
476
477 #if ENABLE(WORKERS)
478     if (isWorkerContext())
479         return static_cast<WorkerContext*>(this)->script()->globalData();
480 #endif
481
482     ASSERT_NOT_REACHED();
483     return 0;
484 }
485 #endif
486
487 } // namespace WebCore