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