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 66 namespace WebCore { 67 68 class CloseWorkerGlobalScopeTask : public ScriptExecutionContext::Task { 69 public: 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 85 WorkerGlobalScope::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 101 WorkerGlobalScope::~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 112 void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType) 113 { 114 setContentSecurityPolicy(ContentSecurityPolicy::create(this)); 115 contentSecurityPolicy()->didReceiveHeader(policy, contentSecurityPolicyType); 116 } 117 118 ScriptExecutionContext* WorkerGlobalScope::scriptExecutionContext() const 119 { 120 return const_cast<WorkerGlobalScope*>(this); 121 } 122 123 const KURL& WorkerGlobalScope::virtualURL() const 124 { 125 return m_url; 126 } 127 128 KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const 129 { 130 return completeURL(url); 131 } 132 133 KURL 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 143 String WorkerGlobalScope::userAgent(const KURL&) const 144 { 145 return m_userAgent; 146 } 147 148 void WorkerGlobalScope::disableEval(const String& errorMessage) 149 { 150 m_script->disableEval(errorMessage); 151 } 152 153 WorkerLocation* WorkerGlobalScope::location() const 154 { 155 if (!m_location) 156 m_location = WorkerLocation::create(m_url); 157 return m_location.get(); 158 } 159 160 void 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 172 WorkerNavigator* WorkerGlobalScope::navigator() const 173 { 174 if (!m_navigator) 175 m_navigator = WorkerNavigator::create(m_userAgent); 176 return m_navigator.get(); 177 } 178 179 void WorkerGlobalScope::postTask(PassOwnPtr<Task> task) 180 { 181 thread()->runLoop().postTask(task); 182 } 183 184 void WorkerGlobalScope::clearInspector() 185 { 186 m_workerInspectorController.clear(); 187 } 188 189 void 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 226 EventTarget* WorkerGlobalScope::errorEventTarget() 227 { 228 return this; 229 } 230 231 void 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 236 void 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 247 void 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 257 void 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 266 bool WorkerGlobalScope::isContextThread() const 267 { 268 return thread()->isCurrentThread(); 269 } 270 271 bool WorkerGlobalScope::isJSExecutionForbidden() const 272 { 273 return m_script->isExecutionForbidden(); 274 } 275 276 EventTargetData* WorkerGlobalScope::eventTargetData() 277 { 278 return &m_eventTargetData; 279 } 280 281 EventTargetData* WorkerGlobalScope::ensureEventTargetData() 282 { 283 return &m_eventTargetData; 284 } 285 286 WorkerGlobalScope::Observer::Observer(WorkerGlobalScope* context) 287 : m_context(context) 288 { 289 ASSERT(m_context && m_context->isContextThread()); 290 m_context->registerObserver(this); 291 } 292 293 WorkerGlobalScope::Observer::~Observer() 294 { 295 if (!m_context) 296 return; 297 ASSERT(m_context->isContextThread()); 298 m_context->unregisterObserver(this); 299 } 300 301 void 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 310 void WorkerGlobalScope::registerObserver(Observer* observer) 311 { 312 ASSERT(observer); 313 m_workerObservers.add(observer); 314 } 315 316 void WorkerGlobalScope::unregisterObserver(Observer* observer) 317 { 318 ASSERT(observer); 319 m_workerObservers.remove(observer); 320 } 321 322 void 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 333 bool WorkerGlobalScope::idleNotification() 334 { 335 return script()->idleNotification(); 336 } 337 338 WorkerEventQueue* WorkerGlobalScope::eventQueue() const 339 { 340 return m_eventQueue.get(); 341 } 342 343 } // namespace WebCore 344