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