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