1 /* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * Copyright (C) 2009 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 30 #if ENABLE(WORKERS) 31 32 #include "WorkerContext.h" 33 34 #include "ActiveDOMObject.h" 35 #include "Database.h" 36 #include "DOMTimer.h" 37 #include "DOMWindow.h" 38 #include "Event.h" 39 #include "EventException.h" 40 #include "MessagePort.h" 41 #include "NotImplemented.h" 42 #include "ScriptSourceCode.h" 43 #include "ScriptValue.h" 44 #include "SecurityOrigin.h" 45 #include "WorkerLocation.h" 46 #include "WorkerNavigator.h" 47 #include "WorkerObjectProxy.h" 48 #include "WorkerScriptLoader.h" 49 #include "WorkerThread.h" 50 #include "WorkerThreadableLoader.h" 51 #include "XMLHttpRequestException.h" 52 #include <wtf/RefPtr.h> 53 #include <wtf/UnusedParam.h> 54 55 #if ENABLE(NOTIFICATIONS) 56 #include "NotificationCenter.h" 57 #endif 58 59 namespace WebCore { 60 61 WorkerContext::WorkerContext(const KURL& url, const String& userAgent, WorkerThread* thread) 62 : m_url(url) 63 , m_userAgent(userAgent) 64 , m_script(new WorkerScriptController(this)) 65 , m_thread(thread) 66 , m_closing(false) 67 { 68 setSecurityOrigin(SecurityOrigin::create(url)); 69 } 70 71 WorkerContext::~WorkerContext() 72 { 73 ASSERT(currentThread() == thread()->threadID()); 74 #if ENABLE(NOTIFICATIONS) 75 m_notifications.clear(); 76 #endif 77 // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this. 78 thread()->workerReportingProxy().workerContextDestroyed(); 79 } 80 81 ScriptExecutionContext* WorkerContext::scriptExecutionContext() const 82 { 83 return const_cast<WorkerContext*>(this); 84 } 85 86 const KURL& WorkerContext::virtualURL() const 87 { 88 return m_url; 89 } 90 91 KURL WorkerContext::virtualCompleteURL(const String& url) const 92 { 93 return completeURL(url); 94 } 95 96 KURL WorkerContext::completeURL(const String& url) const 97 { 98 // Always return a null URL when passed a null string. 99 // FIXME: Should we change the KURL constructor to have this behavior? 100 if (url.isNull()) 101 return KURL(); 102 // Always use UTF-8 in Workers. 103 return KURL(m_url, url); 104 } 105 106 String WorkerContext::userAgent(const KURL&) const 107 { 108 return m_userAgent; 109 } 110 111 WorkerLocation* WorkerContext::location() const 112 { 113 if (!m_location) 114 m_location = WorkerLocation::create(m_url); 115 return m_location.get(); 116 } 117 118 void WorkerContext::close() 119 { 120 if (m_closing) 121 return; 122 123 m_closing = true; 124 // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop(). 125 thread()->workerReportingProxy().workerContextClosed(); 126 } 127 128 WorkerNavigator* WorkerContext::navigator() const 129 { 130 if (!m_navigator) 131 m_navigator = WorkerNavigator::create(m_userAgent); 132 return m_navigator.get(); 133 } 134 135 bool WorkerContext::hasPendingActivity() const 136 { 137 ActiveDOMObjectsMap& activeObjects = activeDOMObjects(); 138 ActiveDOMObjectsMap::const_iterator activeObjectsEnd = activeObjects.end(); 139 for (ActiveDOMObjectsMap::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) { 140 if (iter->first->hasPendingActivity()) 141 return true; 142 } 143 144 // Keep the worker active as long as there is a MessagePort with pending activity or that is remotely entangled. 145 HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end(); 146 for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) { 147 if ((*iter)->hasPendingActivity() || ((*iter)->isEntangled() && !(*iter)->locallyEntangledPort())) 148 return true; 149 } 150 151 return false; 152 } 153 154 void WorkerContext::resourceRetrievedByXMLHttpRequest(unsigned long, const ScriptString&) 155 { 156 // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175 157 notImplemented(); 158 } 159 160 void WorkerContext::scriptImported(unsigned long, const String&) 161 { 162 // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175 163 notImplemented(); 164 } 165 166 void WorkerContext::postTask(PassOwnPtr<Task> task) 167 { 168 thread()->runLoop().postTask(task); 169 } 170 171 int WorkerContext::setTimeout(ScheduledAction* action, int timeout) 172 { 173 return DOMTimer::install(scriptExecutionContext(), action, timeout, true); 174 } 175 176 void WorkerContext::clearTimeout(int timeoutId) 177 { 178 DOMTimer::removeById(scriptExecutionContext(), timeoutId); 179 } 180 181 int WorkerContext::setInterval(ScheduledAction* action, int timeout) 182 { 183 return DOMTimer::install(scriptExecutionContext(), action, timeout, false); 184 } 185 186 void WorkerContext::clearInterval(int timeoutId) 187 { 188 DOMTimer::removeById(scriptExecutionContext(), timeoutId); 189 } 190 191 void WorkerContext::importScripts(const Vector<String>& urls, const String& callerURL, int callerLine, ExceptionCode& ec) 192 { 193 #if !ENABLE(INSPECTOR) 194 UNUSED_PARAM(callerURL); 195 UNUSED_PARAM(callerLine); 196 #endif 197 ec = 0; 198 Vector<String>::const_iterator urlsEnd = urls.end(); 199 Vector<KURL> completedURLs; 200 for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) { 201 const KURL& url = scriptExecutionContext()->completeURL(*it); 202 if (!url.isValid()) { 203 ec = SYNTAX_ERR; 204 return; 205 } 206 completedURLs.append(url); 207 } 208 Vector<KURL>::const_iterator end = completedURLs.end(); 209 210 for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) { 211 WorkerScriptLoader scriptLoader; 212 scriptLoader.loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests); 213 214 // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps. 215 if (scriptLoader.failed()) { 216 ec = XMLHttpRequestException::NETWORK_ERR; 217 return; 218 } 219 220 scriptExecutionContext()->scriptImported(scriptLoader.identifier(), scriptLoader.script()); 221 #if ENABLE(INSPECTOR) 222 scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageType, LogMessageLevel, "Worker script imported: \"" + *it + "\".", callerLine, callerURL); 223 #endif 224 225 ScriptValue exception; 226 m_script->evaluate(ScriptSourceCode(scriptLoader.script(), *it), &exception); 227 if (!exception.hasNoValue()) { 228 m_script->setException(exception); 229 return; 230 } 231 } 232 } 233 234 void WorkerContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL) 235 { 236 bool errorHandled = false; 237 if (onerror()) 238 errorHandled = onerror()->reportError(this, errorMessage, sourceURL, lineNumber); 239 240 if (!errorHandled) 241 thread()->workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, sourceURL); 242 } 243 244 void WorkerContext::addMessage(MessageDestination destination, MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL) 245 { 246 thread()->workerReportingProxy().postConsoleMessageToWorkerObject(destination, source, type, level, message, lineNumber, sourceURL); 247 } 248 249 #if ENABLE(NOTIFICATIONS) 250 NotificationCenter* WorkerContext::webkitNotifications() const 251 { 252 if (!m_notifications) 253 m_notifications = NotificationCenter::create(scriptExecutionContext(), m_thread->getNotificationPresenter()); 254 return m_notifications.get(); 255 } 256 #endif 257 258 #if ENABLE(DATABASE) 259 PassRefPtr<Database> WorkerContext::openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, ExceptionCode& ec) 260 { 261 if (!securityOrigin()->canAccessDatabase()) { 262 ec = SECURITY_ERR; 263 return 0; 264 } 265 266 ASSERT(Database::isAvailable()); 267 if (!Database::isAvailable()) 268 return 0; 269 270 return Database::openDatabase(this, name, version, displayName, estimatedSize, ec); 271 } 272 #endif 273 274 bool WorkerContext::isContextThread() const 275 { 276 return currentThread() == thread()->threadID(); 277 } 278 279 EventTargetData* WorkerContext::eventTargetData() 280 { 281 return &m_eventTargetData; 282 } 283 284 EventTargetData* WorkerContext::ensureEventTargetData() 285 { 286 return &m_eventTargetData; 287 } 288 289 } // namespace WebCore 290 291 #endif // ENABLE(WORKERS) 292