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 "core/dom/ScriptExecutionContext.h" 30 31 #include "core/dom/ContextLifecycleNotifier.h" 32 #include "core/dom/ErrorEvent.h" 33 #include "core/dom/EventTarget.h" 34 #include "core/dom/MessagePort.h" 35 #include "core/html/PublicURLManager.h" 36 #include "core/inspector/InspectorInstrumentation.h" 37 #include "core/inspector/ScriptCallStack.h" 38 #include "core/page/DOMTimer.h" 39 #include "core/workers/WorkerGlobalScope.h" 40 #include "core/workers/WorkerThread.h" 41 #include "modules/webdatabase/DatabaseContext.h" 42 #include "wtf/MainThread.h" 43 44 namespace WebCore { 45 46 class ProcessMessagesSoonTask : public ScriptExecutionContext::Task { 47 public: 48 static PassOwnPtr<ProcessMessagesSoonTask> create() 49 { 50 return adoptPtr(new ProcessMessagesSoonTask); 51 } 52 53 virtual void performTask(ScriptExecutionContext* context) 54 { 55 context->dispatchMessagePortEvents(); 56 } 57 }; 58 59 class ScriptExecutionContext::PendingException { 60 WTF_MAKE_NONCOPYABLE(PendingException); 61 public: 62 PendingException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, PassRefPtr<ScriptCallStack> callStack) 63 : m_errorMessage(errorMessage) 64 , m_lineNumber(lineNumber) 65 , m_columnNumber(columnNumber) 66 , m_sourceURL(sourceURL) 67 , m_callStack(callStack) 68 { 69 } 70 String m_errorMessage; 71 int m_lineNumber; 72 int m_columnNumber; 73 String m_sourceURL; 74 RefPtr<ScriptCallStack> m_callStack; 75 }; 76 77 void ScriptExecutionContext::AddConsoleMessageTask::performTask(ScriptExecutionContext* context) 78 { 79 context->addConsoleMessage(m_source, m_level, m_message); 80 } 81 82 ScriptExecutionContext::ScriptExecutionContext() 83 : m_circularSequentialID(0) 84 , m_inDispatchErrorEvent(false) 85 , m_activeDOMObjectsAreSuspended(false) 86 , m_reasonForSuspendingActiveDOMObjects(static_cast<ActiveDOMObject::ReasonForSuspension>(-1)) 87 , m_activeDOMObjectsAreStopped(false) 88 { 89 } 90 91 ScriptExecutionContext::~ScriptExecutionContext() 92 { 93 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); 94 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { 95 ASSERT((*iter)->scriptExecutionContext() == this); 96 (*iter)->contextDestroyed(); 97 } 98 } 99 100 void ScriptExecutionContext::processMessagePortMessagesSoon() 101 { 102 postTask(ProcessMessagesSoonTask::create()); 103 } 104 105 void ScriptExecutionContext::dispatchMessagePortEvents() 106 { 107 RefPtr<ScriptExecutionContext> protect(this); 108 109 // Make a frozen copy. 110 Vector<MessagePort*> ports; 111 copyToVector(m_messagePorts, ports); 112 113 unsigned portCount = ports.size(); 114 for (unsigned i = 0; i < portCount; ++i) { 115 MessagePort* port = ports[i]; 116 // The port may be destroyed, and another one created at the same address, but this is safe, as the worst that can happen 117 // as a result is that dispatchMessages() will be called needlessly. 118 if (m_messagePorts.contains(port) && port->started()) 119 port->dispatchMessages(); 120 } 121 } 122 123 void ScriptExecutionContext::createdMessagePort(MessagePort* port) 124 { 125 ASSERT(port); 126 ASSERT((isDocument() && isMainThread()) 127 || (isWorkerGlobalScope() && toWorkerGlobalScope(this)->thread()->isCurrentThread())); 128 129 m_messagePorts.add(port); 130 } 131 132 void ScriptExecutionContext::destroyedMessagePort(MessagePort* port) 133 { 134 ASSERT(port); 135 ASSERT((isDocument() && isMainThread()) 136 || (isWorkerGlobalScope() && toWorkerGlobalScope(this)->thread()->isCurrentThread())); 137 138 m_messagePorts.remove(port); 139 } 140 141 bool ScriptExecutionContext::canSuspendActiveDOMObjects() 142 { 143 return lifecycleNotifier()->canSuspendActiveDOMObjects(); 144 } 145 146 bool ScriptExecutionContext::hasPendingActivity() 147 { 148 if (lifecycleNotifier()->hasPendingActivity()) 149 return true; 150 151 HashSet<MessagePort*>::const_iterator messagePortsEnd = m_messagePorts.end(); 152 for (HashSet<MessagePort*>::const_iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { 153 if ((*iter)->hasPendingActivity()) 154 return true; 155 } 156 157 return false; 158 } 159 160 void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why) 161 { 162 lifecycleNotifier()->notifySuspendingActiveDOMObjects(why); 163 m_activeDOMObjectsAreSuspended = true; 164 m_reasonForSuspendingActiveDOMObjects = why; 165 } 166 167 void ScriptExecutionContext::resumeActiveDOMObjects() 168 { 169 m_activeDOMObjectsAreSuspended = false; 170 lifecycleNotifier()->notifyResumingActiveDOMObjects(); 171 } 172 173 void ScriptExecutionContext::stopActiveDOMObjects() 174 { 175 m_activeDOMObjectsAreStopped = true; 176 lifecycleNotifier()->notifyStoppingActiveDOMObjects(); 177 // Also close MessagePorts. If they were ActiveDOMObjects (they could be) then they could be stopped instead. 178 closeMessagePorts(); 179 } 180 181 void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject* object) 182 { 183 ASSERT(lifecycleNotifier()->contains(object)); 184 // Ensure all ActiveDOMObjects are suspended also newly created ones. 185 if (m_activeDOMObjectsAreSuspended) 186 object->suspend(m_reasonForSuspendingActiveDOMObjects); 187 } 188 189 void ScriptExecutionContext::closeMessagePorts() { 190 HashSet<MessagePort*>::iterator messagePortsEnd = m_messagePorts.end(); 191 for (HashSet<MessagePort*>::iterator iter = m_messagePorts.begin(); iter != messagePortsEnd; ++iter) { 192 ASSERT((*iter)->scriptExecutionContext() == this); 193 (*iter)->close(); 194 } 195 } 196 197 bool ScriptExecutionContext::shouldSanitizeScriptError(const String& sourceURL, AccessControlStatus corsStatus) 198 { 199 return !(securityOrigin()->canRequest(completeURL(sourceURL)) || corsStatus == SharableCrossOrigin); 200 } 201 202 void ScriptExecutionContext::reportException(PassRefPtr<ErrorEvent> event, PassRefPtr<ScriptCallStack> callStack, AccessControlStatus corsStatus) 203 { 204 RefPtr<ErrorEvent> errorEvent = event; 205 if (m_inDispatchErrorEvent) { 206 if (!m_pendingExceptions) 207 m_pendingExceptions = adoptPtr(new Vector<OwnPtr<PendingException> >()); 208 m_pendingExceptions->append(adoptPtr(new PendingException(errorEvent->message(), errorEvent->lineno(), errorEvent->colno(), errorEvent->filename(), callStack))); 209 return; 210 } 211 212 // First report the original exception and only then all the nested ones. 213 if (!dispatchErrorEvent(errorEvent, corsStatus)) 214 logExceptionToConsole(errorEvent->message(), errorEvent->filename(), errorEvent->lineno(), errorEvent->colno(), callStack); 215 216 if (!m_pendingExceptions) 217 return; 218 219 for (size_t i = 0; i < m_pendingExceptions->size(); i++) { 220 PendingException* e = m_pendingExceptions->at(i).get(); 221 logExceptionToConsole(e->m_errorMessage, e->m_sourceURL, e->m_lineNumber, e->m_columnNumber, e->m_callStack); 222 } 223 m_pendingExceptions.clear(); 224 } 225 226 void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, ScriptState* state, unsigned long requestIdentifier) 227 { 228 addMessage(source, level, message, sourceURL, lineNumber, 0, state, requestIdentifier); 229 } 230 231 bool ScriptExecutionContext::dispatchErrorEvent(PassRefPtr<ErrorEvent> event, AccessControlStatus corsStatus) 232 { 233 EventTarget* target = errorEventTarget(); 234 if (!target) 235 return false; 236 237 RefPtr<ErrorEvent> errorEvent = event; 238 if (shouldSanitizeScriptError(errorEvent->filename(), corsStatus)) 239 errorEvent = ErrorEvent::createSanitizedError(); 240 241 ASSERT(!m_inDispatchErrorEvent); 242 m_inDispatchErrorEvent = true; 243 target->dispatchEvent(errorEvent); 244 m_inDispatchErrorEvent = false; 245 return errorEvent->defaultPrevented(); 246 } 247 248 int ScriptExecutionContext::circularSequentialID() 249 { 250 ++m_circularSequentialID; 251 if (m_circularSequentialID <= 0) 252 m_circularSequentialID = 1; 253 return m_circularSequentialID; 254 } 255 256 int ScriptExecutionContext::installNewTimeout(PassOwnPtr<ScheduledAction> action, int timeout, bool singleShot) 257 { 258 int timeoutID; 259 while (true) { 260 timeoutID = circularSequentialID(); 261 if (!m_timeouts.contains(timeoutID)) 262 break; 263 } 264 TimeoutMap::AddResult result = m_timeouts.add(timeoutID, DOMTimer::create(this, action, timeout, singleShot, timeoutID)); 265 ASSERT(result.isNewEntry); 266 DOMTimer* timer = result.iterator->value.get(); 267 268 timer->suspendIfNeeded(); 269 270 return timer->timeoutID(); 271 } 272 273 void ScriptExecutionContext::removeTimeoutByID(int timeoutID) 274 { 275 if (timeoutID <= 0) 276 return; 277 m_timeouts.remove(timeoutID); 278 } 279 280 PublicURLManager& ScriptExecutionContext::publicURLManager() 281 { 282 if (!m_publicURLManager) 283 m_publicURLManager = PublicURLManager::create(this); 284 return *m_publicURLManager; 285 } 286 287 void ScriptExecutionContext::didChangeTimerAlignmentInterval() 288 { 289 for (TimeoutMap::iterator iter = m_timeouts.begin(); iter != m_timeouts.end(); ++iter) 290 iter->value->didChangeAlignmentInterval(); 291 } 292 293 double ScriptExecutionContext::timerAlignmentInterval() const 294 { 295 return DOMTimer::visiblePageAlignmentInterval(); 296 } 297 298 ContextLifecycleNotifier* ScriptExecutionContext::lifecycleNotifier() 299 { 300 return static_cast<ContextLifecycleNotifier*>(LifecycleContext::lifecycleNotifier()); 301 } 302 303 PassOwnPtr<LifecycleNotifier> ScriptExecutionContext::createLifecycleNotifier() 304 { 305 return ContextLifecycleNotifier::create(this); 306 } 307 308 ScriptExecutionContext::Task::~Task() 309 { 310 } 311 312 void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext) 313 { 314 m_databaseContext = databaseContext; 315 } 316 317 } // namespace WebCore 318