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/core/v8/ExceptionState.h" 32#include "bindings/core/v8/ScheduledAction.h" 33#include "bindings/core/v8/ScriptSourceCode.h" 34#include "bindings/core/v8/ScriptValue.h" 35#include "core/dom/ActiveDOMObject.h" 36#include "core/dom/AddConsoleMessageTask.h" 37#include "core/dom/ContextLifecycleNotifier.h" 38#include "core/dom/DOMURL.h" 39#include "core/dom/ExceptionCode.h" 40#include "core/dom/MessagePort.h" 41#include "core/events/ErrorEvent.h" 42#include "core/events/Event.h" 43#include "core/inspector/ConsoleMessage.h" 44#include "core/inspector/ConsoleMessageStorage.h" 45#include "core/inspector/InspectorConsoleInstrumentation.h" 46#include "core/inspector/ScriptCallStack.h" 47#include "core/inspector/WorkerInspectorController.h" 48#include "core/loader/WorkerThreadableLoader.h" 49#include "core/frame/LocalDOMWindow.h" 50#include "core/workers/WorkerNavigator.h" 51#include "core/workers/WorkerClients.h" 52#include "core/workers/WorkerConsole.h" 53#include "core/workers/WorkerLocation.h" 54#include "core/workers/WorkerNavigator.h" 55#include "core/workers/WorkerReportingProxy.h" 56#include "core/workers/WorkerScriptLoader.h" 57#include "core/workers/WorkerThread.h" 58#include "platform/network/ContentSecurityPolicyParsers.h" 59#include "platform/weborigin/KURL.h" 60#include "platform/weborigin/SecurityOrigin.h" 61#include "public/platform/WebURLRequest.h" 62 63namespace blink { 64 65class CloseWorkerGlobalScopeTask : public ExecutionContextTask { 66public: 67 static PassOwnPtr<CloseWorkerGlobalScopeTask> create() 68 { 69 return adoptPtr(new CloseWorkerGlobalScopeTask); 70 } 71 72 virtual void performTask(ExecutionContext *context) 73 { 74 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); 75 // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop(). 76 workerGlobalScope->thread()->workerReportingProxy().workerGlobalScopeClosed(); 77 } 78 79 virtual bool isCleanupTask() const { return true; } 80}; 81 82WorkerGlobalScope::WorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread* thread, double timeOrigin, PassOwnPtrWillBeRawPtr<WorkerClients> workerClients) 83 : m_url(url) 84 , m_userAgent(userAgent) 85 , m_script(adoptPtr(new WorkerScriptController(*this))) 86 , m_thread(thread) 87 , m_workerInspectorController(adoptRefWillBeNoop(new WorkerInspectorController(this))) 88 , m_closing(false) 89 , m_eventQueue(WorkerEventQueue::create(this)) 90 , m_workerClients(workerClients) 91 , m_timeOrigin(timeOrigin) 92 , m_messageStorage(ConsoleMessageStorage::createForWorker(this)) 93{ 94 setSecurityOrigin(SecurityOrigin::create(url)); 95 m_workerClients->reattachThread(); 96 m_thread->setWorkerInspectorController(m_workerInspectorController.get()); 97} 98 99WorkerGlobalScope::~WorkerGlobalScope() 100{ 101} 102 103void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicyHeaderType contentSecurityPolicyType) 104{ 105 // FIXME: This doesn't match the CSP2 spec's Worker behavior (see https://w3c.github.io/webappsec/specs/content-security-policy/#processing-model-workers) 106 RefPtr<ContentSecurityPolicy> csp = ContentSecurityPolicy::create(); 107 csp->didReceiveHeader(policy, contentSecurityPolicyType, ContentSecurityPolicyHeaderSourceHTTP); 108 csp->bindToExecutionContext(executionContext()); 109 setContentSecurityPolicy(csp); 110} 111 112ExecutionContext* WorkerGlobalScope::executionContext() const 113{ 114 return const_cast<WorkerGlobalScope*>(this); 115} 116 117const KURL& WorkerGlobalScope::virtualURL() const 118{ 119 return m_url; 120} 121 122KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const 123{ 124 return completeURL(url); 125} 126 127KURL WorkerGlobalScope::completeURL(const String& url) const 128{ 129 // Always return a null URL when passed a null string. 130 // FIXME: Should we change the KURL constructor to have this behavior? 131 if (url.isNull()) 132 return KURL(); 133 // Always use UTF-8 in Workers. 134 return KURL(m_url, url); 135} 136 137String WorkerGlobalScope::userAgent(const KURL&) const 138{ 139 return m_userAgent; 140} 141 142void WorkerGlobalScope::disableEval(const String& errorMessage) 143{ 144 m_script->disableEval(errorMessage); 145} 146 147double WorkerGlobalScope::timerAlignmentInterval() const 148{ 149 return DOMTimer::visiblePageAlignmentInterval(); 150} 151 152WorkerLocation* WorkerGlobalScope::location() const 153{ 154 if (!m_location) 155 m_location = WorkerLocation::create(m_url); 156 return m_location.get(); 157} 158 159void WorkerGlobalScope::close() 160{ 161 if (m_closing) 162 return; 163 164 // Let current script run to completion but prevent future script evaluations. 165 // After m_closing is set, all the tasks in the queue continue to be fetched but only 166 // tasks with isCleanupTask()==true will be executed. 167 m_closing = true; 168 postTask(CloseWorkerGlobalScopeTask::create()); 169} 170 171WorkerConsole* WorkerGlobalScope::console() 172{ 173 if (!m_console) 174 m_console = WorkerConsole::create(this); 175 return m_console.get(); 176} 177 178WorkerNavigator* WorkerGlobalScope::navigator() const 179{ 180 if (!m_navigator) 181 m_navigator = WorkerNavigator::create(m_userAgent); 182 return m_navigator.get(); 183} 184 185void WorkerGlobalScope::postTask(PassOwnPtr<ExecutionContextTask> task) 186{ 187 thread()->postTask(task); 188} 189 190// FIXME: Called twice, from WorkerThreadShutdownFinishTask and WorkerGlobalScope::dispose. 191void WorkerGlobalScope::clearInspector() 192{ 193 if (!m_workerInspectorController) 194 return; 195 thread()->setWorkerInspectorController(nullptr); 196 m_workerInspectorController->dispose(); 197 m_workerInspectorController.clear(); 198} 199 200void WorkerGlobalScope::dispose() 201{ 202 ASSERT(thread()->isCurrentThread()); 203 204 m_eventQueue->close(); 205 clearScript(); 206 clearInspector(); 207 // We do not clear the thread field of the 208 // WorkerGlobalScope. Other objects keep the worker global scope 209 // alive because they need its thread field to check that work is 210 // being carried out on the right thread. We therefore cannot clear 211 // the thread field before all references to the worker global 212 // scope are gone. 213} 214 215void WorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionState& exceptionState) 216{ 217 ASSERT(contentSecurityPolicy()); 218 ASSERT(executionContext()); 219 220 ExecutionContext& executionContext = *this->executionContext(); 221 222 Vector<String>::const_iterator urlsEnd = urls.end(); 223 Vector<KURL> completedURLs; 224 for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) { 225 const KURL& url = executionContext.completeURL(*it); 226 if (!url.isValid()) { 227 exceptionState.throwDOMException(SyntaxError, "The URL '" + *it + "' is invalid."); 228 return; 229 } 230 if (!contentSecurityPolicy()->allowScriptFromSource(url)) { 231 exceptionState.throwDOMException(NetworkError, "The script at '" + url.elidedString() + "' failed to load."); 232 return; 233 } 234 completedURLs.append(url); 235 } 236 Vector<KURL>::const_iterator end = completedURLs.end(); 237 238 for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) { 239 RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create()); 240 scriptLoader->setRequestContext(blink::WebURLRequest::RequestContextScript); 241 scriptLoader->loadSynchronously(executionContext, *it, AllowCrossOriginRequests); 242 243 // If the fetching attempt failed, throw a NetworkError exception and abort all these steps. 244 if (scriptLoader->failed()) { 245 exceptionState.throwDOMException(NetworkError, "The script at '" + it->elidedString() + "' failed to load."); 246 return; 247 } 248 249 InspectorInstrumentation::scriptImported(&executionContext, scriptLoader->identifier(), scriptLoader->script()); 250 251 RefPtrWillBeRawPtr<ErrorEvent> errorEvent = nullptr; 252 m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &errorEvent); 253 if (errorEvent) { 254 m_script->rethrowExceptionFromImportedScript(errorEvent.release(), exceptionState); 255 return; 256 } 257 } 258} 259 260EventTarget* WorkerGlobalScope::errorEventTarget() 261{ 262 return this; 263} 264 265void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, int, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack>) 266{ 267 thread()->workerReportingProxy().reportException(errorMessage, lineNumber, columnNumber, sourceURL); 268} 269 270void WorkerGlobalScope::reportBlockedScriptExecutionToInspector(const String& directiveText) 271{ 272 InspectorInstrumentation::scriptExecutionBlockedByCSP(this, directiveText); 273} 274 275void WorkerGlobalScope::addConsoleMessage(PassRefPtrWillBeRawPtr<ConsoleMessage> prpConsoleMessage) 276{ 277 RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = prpConsoleMessage; 278 if (!isContextThread()) { 279 postTask(AddConsoleMessageTask::create(consoleMessage->source(), consoleMessage->level(), consoleMessage->message())); 280 return; 281 } 282 thread()->workerReportingProxy().reportConsoleMessage(consoleMessage); 283 addMessageToWorkerConsole(consoleMessage.release()); 284} 285 286void WorkerGlobalScope::addMessageToWorkerConsole(PassRefPtrWillBeRawPtr<ConsoleMessage> consoleMessage) 287{ 288 ASSERT(isContextThread()); 289 m_messageStorage->reportMessage(consoleMessage); 290} 291 292bool WorkerGlobalScope::isContextThread() const 293{ 294 return thread()->isCurrentThread(); 295} 296 297bool WorkerGlobalScope::isJSExecutionForbidden() const 298{ 299 return m_script->isExecutionForbidden(); 300} 301 302bool WorkerGlobalScope::idleNotification() 303{ 304 return script()->idleNotification(); 305} 306 307WorkerEventQueue* WorkerGlobalScope::eventQueue() const 308{ 309 return m_eventQueue.get(); 310} 311 312void WorkerGlobalScope::countFeature(UseCounter::Feature) const 313{ 314 // FIXME: How should we count features for shared/service workers? 315} 316 317void WorkerGlobalScope::countDeprecation(UseCounter::Feature) const 318{ 319 // FIXME: How should we count features for shared/service workers? 320} 321 322ConsoleMessageStorage* WorkerGlobalScope::messageStorage() 323{ 324 return m_messageStorage.get(); 325} 326 327void WorkerGlobalScope::trace(Visitor* visitor) 328{ 329#if ENABLE(OILPAN) 330 visitor->trace(m_console); 331 visitor->trace(m_location); 332 visitor->trace(m_navigator); 333 visitor->trace(m_workerInspectorController); 334 visitor->trace(m_eventQueue); 335 visitor->trace(m_workerClients); 336 visitor->trace(m_messageStorage); 337 HeapSupplementable<WorkerGlobalScope>::trace(visitor); 338#endif 339 ExecutionContext::trace(visitor); 340 EventTargetWithInlineData::trace(visitor); 341} 342 343} // namespace blink 344