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