1/* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * Copyright (C) 2009, 2011 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/workers/WorkerGlobalScope.h" 30 31#include "bindings/v8/ExceptionState.h" 32#include "bindings/v8/ScheduledAction.h" 33#include "bindings/v8/ScriptSourceCode.h" 34#include "bindings/v8/ScriptValue.h" 35#include "core/dom/ActiveDOMObject.h" 36#include "core/dom/ContextLifecycleNotifier.h" 37#include "core/dom/ErrorEvent.h" 38#include "core/dom/Event.h" 39#include "core/dom/ExceptionCode.h" 40#include "core/dom/MessagePort.h" 41#include "core/html/DOMURL.h" 42#include "core/inspector/InspectorConsoleInstrumentation.h" 43#include "core/inspector/ScriptCallStack.h" 44#include "core/inspector/WorkerInspectorController.h" 45#include "core/loader/WorkerThreadableLoader.h" 46#include "core/page/ContentSecurityPolicy.h" 47#include "core/page/DOMWindow.h" 48#include "core/page/WorkerNavigator.h" 49#include "core/platform/NotImplemented.h" 50#include "core/workers/WorkerClients.h" 51#include "core/workers/WorkerLocation.h" 52#include "core/workers/WorkerObjectProxy.h" 53#include "core/workers/WorkerScriptLoader.h" 54#include "core/workers/WorkerThread.h" 55#include "weborigin/KURL.h" 56#include "weborigin/SecurityOrigin.h" 57#include "wtf/RefPtr.h" 58#include "wtf/UnusedParam.h" 59 60#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS) 61#include "modules/notifications/NotificationCenter.h" 62#endif 63 64 65 66namespace WebCore { 67 68class CloseWorkerGlobalScopeTask : public ScriptExecutionContext::Task { 69public: 70 static PassOwnPtr<CloseWorkerGlobalScopeTask> create() 71 { 72 return adoptPtr(new CloseWorkerGlobalScopeTask); 73 } 74 75 virtual void performTask(ScriptExecutionContext *context) 76 { 77 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); 78 // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop(). 79 workerGlobalScope->thread()->workerReportingProxy().workerGlobalScopeClosed(); 80 } 81 82 virtual bool isCleanupTask() const { return true; } 83}; 84 85WorkerGlobalScope::WorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread* thread, double timeOrigin, PassOwnPtr<WorkerClients> workerClients) 86 : m_url(url) 87 , m_userAgent(userAgent) 88 , m_script(adoptPtr(new WorkerScriptController(this))) 89 , m_thread(thread) 90 , m_workerInspectorController(adoptPtr(new WorkerInspectorController(this))) 91 , m_closing(false) 92 , m_eventQueue(WorkerEventQueue::create(this)) 93 , m_timeOrigin(timeOrigin) 94 , m_workerClients(workerClients) 95{ 96 ScriptWrappable::init(this); 97 setSecurityOrigin(SecurityOrigin::create(url)); 98 m_workerClients->reattachThread(); 99} 100 101WorkerGlobalScope::~WorkerGlobalScope() 102{ 103 ASSERT(thread()->isCurrentThread()); 104 105 // Make sure we have no observers. 106 notifyObserversOfStop(); 107 108 // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this. 109 thread()->workerReportingProxy().workerGlobalScopeDestroyed(); 110} 111 112void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType) 113{ 114 setContentSecurityPolicy(ContentSecurityPolicy::create(this)); 115 contentSecurityPolicy()->didReceiveHeader(policy, contentSecurityPolicyType); 116} 117 118ScriptExecutionContext* WorkerGlobalScope::scriptExecutionContext() const 119{ 120 return const_cast<WorkerGlobalScope*>(this); 121} 122 123const KURL& WorkerGlobalScope::virtualURL() const 124{ 125 return m_url; 126} 127 128KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const 129{ 130 return completeURL(url); 131} 132 133KURL WorkerGlobalScope::completeURL(const String& url) const 134{ 135 // Always return a null URL when passed a null string. 136 // FIXME: Should we change the KURL constructor to have this behavior? 137 if (url.isNull()) 138 return KURL(); 139 // Always use UTF-8 in Workers. 140 return KURL(m_url, url); 141} 142 143String WorkerGlobalScope::userAgent(const KURL&) const 144{ 145 return m_userAgent; 146} 147 148void WorkerGlobalScope::disableEval(const String& errorMessage) 149{ 150 m_script->disableEval(errorMessage); 151} 152 153WorkerLocation* WorkerGlobalScope::location() const 154{ 155 if (!m_location) 156 m_location = WorkerLocation::create(m_url); 157 return m_location.get(); 158} 159 160void WorkerGlobalScope::close() 161{ 162 if (m_closing) 163 return; 164 165 // Let current script run to completion but prevent future script evaluations. 166 // After m_closing is set, all the tasks in the queue continue to be fetched but only 167 // tasks with isCleanupTask()==true will be executed. 168 m_closing = true; 169 postTask(CloseWorkerGlobalScopeTask::create()); 170} 171 172WorkerNavigator* WorkerGlobalScope::navigator() const 173{ 174 if (!m_navigator) 175 m_navigator = WorkerNavigator::create(m_userAgent); 176 return m_navigator.get(); 177} 178 179void WorkerGlobalScope::postTask(PassOwnPtr<Task> task) 180{ 181 thread()->runLoop().postTask(task); 182} 183 184void WorkerGlobalScope::clearInspector() 185{ 186 m_workerInspectorController.clear(); 187} 188 189void WorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionState& es) 190{ 191 ASSERT(contentSecurityPolicy()); 192 Vector<String>::const_iterator urlsEnd = urls.end(); 193 Vector<KURL> completedURLs; 194 for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) { 195 const KURL& url = scriptExecutionContext()->completeURL(*it); 196 if (!url.isValid()) { 197 es.throwDOMException(SyntaxError); 198 return; 199 } 200 completedURLs.append(url); 201 } 202 Vector<KURL>::const_iterator end = completedURLs.end(); 203 204 for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) { 205 RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create()); 206 scriptLoader->setTargetType(ResourceRequest::TargetIsScript); 207 scriptLoader->loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests); 208 209 // If the fetching attempt failed, throw a NetworkError exception and abort all these steps. 210 if (scriptLoader->failed()) { 211 es.throwDOMException(NetworkError); 212 return; 213 } 214 215 InspectorInstrumentation::scriptImported(scriptExecutionContext(), scriptLoader->identifier(), scriptLoader->script()); 216 217 RefPtr<ErrorEvent> errorEvent; 218 m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &errorEvent); 219 if (errorEvent) { 220 m_script->rethrowExceptionFromImportedScript(errorEvent.release()); 221 return; 222 } 223 } 224} 225 226EventTarget* WorkerGlobalScope::errorEventTarget() 227{ 228 return this; 229} 230 231void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtr<ScriptCallStack>) 232{ 233 thread()->workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, columnNumber, sourceURL); 234} 235 236void WorkerGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier) 237{ 238 if (!isContextThread()) { 239 postTask(AddConsoleMessageTask::create(source, level, message)); 240 return; 241 } 242 thread()->workerReportingProxy().postConsoleMessageToWorkerObject(source, level, message, 0, String()); 243 244 addMessageToWorkerConsole(source, level, message, String(), 0, 0, 0, requestIdentifier); 245} 246 247void WorkerGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, PassRefPtr<ScriptCallStack> callStack, ScriptState* state, unsigned long requestIdentifier) 248{ 249 if (!isContextThread()) { 250 postTask(AddConsoleMessageTask::create(source, level, message)); 251 return; 252 } 253 thread()->workerReportingProxy().postConsoleMessageToWorkerObject(source, level, message, lineNumber, sourceURL); 254 addMessageToWorkerConsole(source, level, message, sourceURL, lineNumber, callStack, state, requestIdentifier); 255} 256 257void WorkerGlobalScope::addMessageToWorkerConsole(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, PassRefPtr<ScriptCallStack> callStack, ScriptState* state, unsigned long requestIdentifier) 258{ 259 ASSERT(isContextThread()); 260 if (callStack) 261 InspectorInstrumentation::addMessageToConsole(this, source, LogMessageType, level, message, callStack, requestIdentifier); 262 else 263 InspectorInstrumentation::addMessageToConsole(this, source, LogMessageType, level, message, sourceURL, lineNumber, 0, state, requestIdentifier); 264} 265 266bool WorkerGlobalScope::isContextThread() const 267{ 268 return thread()->isCurrentThread(); 269} 270 271bool WorkerGlobalScope::isJSExecutionForbidden() const 272{ 273 return m_script->isExecutionForbidden(); 274} 275 276EventTargetData* WorkerGlobalScope::eventTargetData() 277{ 278 return &m_eventTargetData; 279} 280 281EventTargetData* WorkerGlobalScope::ensureEventTargetData() 282{ 283 return &m_eventTargetData; 284} 285 286WorkerGlobalScope::Observer::Observer(WorkerGlobalScope* context) 287 : m_context(context) 288{ 289 ASSERT(m_context && m_context->isContextThread()); 290 m_context->registerObserver(this); 291} 292 293WorkerGlobalScope::Observer::~Observer() 294{ 295 if (!m_context) 296 return; 297 ASSERT(m_context->isContextThread()); 298 m_context->unregisterObserver(this); 299} 300 301void WorkerGlobalScope::Observer::stopObserving() 302{ 303 if (!m_context) 304 return; 305 ASSERT(m_context->isContextThread()); 306 m_context->unregisterObserver(this); 307 m_context = 0; 308} 309 310void WorkerGlobalScope::registerObserver(Observer* observer) 311{ 312 ASSERT(observer); 313 m_workerObservers.add(observer); 314} 315 316void WorkerGlobalScope::unregisterObserver(Observer* observer) 317{ 318 ASSERT(observer); 319 m_workerObservers.remove(observer); 320} 321 322void WorkerGlobalScope::notifyObserversOfStop() 323{ 324 HashSet<Observer*>::iterator iter = m_workerObservers.begin(); 325 while (iter != m_workerObservers.end()) { 326 WorkerGlobalScope::Observer* observer = *iter; 327 observer->stopObserving(); 328 observer->notifyStop(); 329 iter = m_workerObservers.begin(); 330 } 331} 332 333bool WorkerGlobalScope::idleNotification() 334{ 335 return script()->idleNotification(); 336} 337 338WorkerEventQueue* WorkerGlobalScope::eventQueue() const 339{ 340 return m_eventQueue.get(); 341} 342 343} // namespace WebCore 344