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 63 namespace blink { 64 65 class CloseWorkerGlobalScopeTask : public ExecutionContextTask { 66 public: 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 82 WorkerGlobalScope::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 99 WorkerGlobalScope::~WorkerGlobalScope() 100 { 101 } 102 103 void 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 112 ExecutionContext* WorkerGlobalScope::executionContext() const 113 { 114 return const_cast<WorkerGlobalScope*>(this); 115 } 116 117 const KURL& WorkerGlobalScope::virtualURL() const 118 { 119 return m_url; 120 } 121 122 KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const 123 { 124 return completeURL(url); 125 } 126 127 KURL 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 137 String WorkerGlobalScope::userAgent(const KURL&) const 138 { 139 return m_userAgent; 140 } 141 142 void WorkerGlobalScope::disableEval(const String& errorMessage) 143 { 144 m_script->disableEval(errorMessage); 145 } 146 147 double WorkerGlobalScope::timerAlignmentInterval() const 148 { 149 return DOMTimer::visiblePageAlignmentInterval(); 150 } 151 152 WorkerLocation* WorkerGlobalScope::location() const 153 { 154 if (!m_location) 155 m_location = WorkerLocation::create(m_url); 156 return m_location.get(); 157 } 158 159 void 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 171 WorkerConsole* WorkerGlobalScope::console() 172 { 173 if (!m_console) 174 m_console = WorkerConsole::create(this); 175 return m_console.get(); 176 } 177 178 WorkerNavigator* WorkerGlobalScope::navigator() const 179 { 180 if (!m_navigator) 181 m_navigator = WorkerNavigator::create(m_userAgent); 182 return m_navigator.get(); 183 } 184 185 void WorkerGlobalScope::postTask(PassOwnPtr<ExecutionContextTask> task) 186 { 187 thread()->postTask(task); 188 } 189 190 // FIXME: Called twice, from WorkerThreadShutdownFinishTask and WorkerGlobalScope::dispose. 191 void WorkerGlobalScope::clearInspector() 192 { 193 if (!m_workerInspectorController) 194 return; 195 thread()->setWorkerInspectorController(nullptr); 196 m_workerInspectorController->dispose(); 197 m_workerInspectorController.clear(); 198 } 199 200 void 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 215 void 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 260 EventTarget* WorkerGlobalScope::errorEventTarget() 261 { 262 return this; 263 } 264 265 void 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 270 void WorkerGlobalScope::reportBlockedScriptExecutionToInspector(const String& directiveText) 271 { 272 InspectorInstrumentation::scriptExecutionBlockedByCSP(this, directiveText); 273 } 274 275 void 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 286 void WorkerGlobalScope::addMessageToWorkerConsole(PassRefPtrWillBeRawPtr<ConsoleMessage> consoleMessage) 287 { 288 ASSERT(isContextThread()); 289 m_messageStorage->reportMessage(consoleMessage); 290 } 291 292 bool WorkerGlobalScope::isContextThread() const 293 { 294 return thread()->isCurrentThread(); 295 } 296 297 bool WorkerGlobalScope::isJSExecutionForbidden() const 298 { 299 return m_script->isExecutionForbidden(); 300 } 301 302 bool WorkerGlobalScope::idleNotification() 303 { 304 return script()->idleNotification(); 305 } 306 307 WorkerEventQueue* WorkerGlobalScope::eventQueue() const 308 { 309 return m_eventQueue.get(); 310 } 311 312 void WorkerGlobalScope::countFeature(UseCounter::Feature) const 313 { 314 // FIXME: How should we count features for shared/service workers? 315 } 316 317 void WorkerGlobalScope::countDeprecation(UseCounter::Feature) const 318 { 319 // FIXME: How should we count features for shared/service workers? 320 } 321 322 ConsoleMessageStorage* WorkerGlobalScope::messageStorage() 323 { 324 return m_messageStorage.get(); 325 } 326 327 void 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