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