2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009, 2011 Google Inc. All Rights Reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
32 #include "WorkerContext.h"
34 #include "AbstractDatabase.h"
35 #include "ActiveDOMObject.h"
37 #include "DatabaseCallback.h"
38 #include "DatabaseSync.h"
39 #include "DatabaseTracker.h"
42 #include "DOMWindow.h"
43 #include "ErrorEvent.h"
45 #include "EventException.h"
46 #include "InspectorInstrumentation.h"
48 #include "MessagePort.h"
49 #include "NotImplemented.h"
50 #include "ScheduledAction.h"
51 #include "ScriptCallStack.h"
52 #include "ScriptSourceCode.h"
53 #include "ScriptValue.h"
54 #include "SecurityOrigin.h"
55 #include "WorkerInspectorController.h"
56 #include "WorkerLocation.h"
57 #include "WorkerNavigator.h"
58 #include "WorkerObjectProxy.h"
59 #include "WorkerScriptLoader.h"
60 #include "WorkerThread.h"
61 #include "WorkerThreadableLoader.h"
62 #include "XMLHttpRequestException.h"
63 #include <wtf/RefPtr.h>
64 #include <wtf/UnusedParam.h>
66 #if ENABLE(NOTIFICATIONS)
67 #include "NotificationCenter.h"
70 #if ENABLE(FILE_SYSTEM)
71 #include "AsyncFileSystem.h"
72 #include "DirectoryEntrySync.h"
73 #include "DOMFileSystem.h"
74 #include "DOMFileSystemBase.h"
75 #include "DOMFileSystemSync.h"
76 #include "ErrorCallback.h"
77 #include "FileEntrySync.h"
78 #include "FileError.h"
79 #include "FileException.h"
80 #include "FileSystemCallback.h"
81 #include "FileSystemCallbacks.h"
82 #include "LocalFileSystem.h"
83 #include "SyncCallbackHelper.h"
88 class CloseWorkerContextTask : public ScriptExecutionContext::Task {
90 static PassOwnPtr<CloseWorkerContextTask> create()
92 return adoptPtr(new CloseWorkerContextTask);
95 virtual void performTask(ScriptExecutionContext *context)
97 ASSERT(context->isWorkerContext());
98 WorkerContext* workerContext = static_cast<WorkerContext*>(context);
99 // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
100 workerContext->thread()->workerReportingProxy().workerContextClosed();
103 virtual bool isCleanupTask() const { return true; }
106 WorkerContext::WorkerContext(const KURL& url, const String& userAgent, WorkerThread* thread)
108 , m_userAgent(userAgent)
109 , m_script(adoptPtr(new WorkerScriptController(this)))
111 #if ENABLE(INSPECTOR)
112 , m_workerInspectorController(adoptPtr(new WorkerInspectorController(this)))
116 setSecurityOrigin(SecurityOrigin::create(url));
119 WorkerContext::~WorkerContext()
121 ASSERT(currentThread() == thread()->threadID());
122 #if ENABLE(NOTIFICATIONS)
123 m_notifications.clear();
126 // Make sure we have no observers.
127 notifyObserversOfStop();
129 // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this.
130 thread()->workerReportingProxy().workerContextDestroyed();
133 ScriptExecutionContext* WorkerContext::scriptExecutionContext() const
135 return const_cast<WorkerContext*>(this);
138 const KURL& WorkerContext::virtualURL() const
143 KURL WorkerContext::virtualCompleteURL(const String& url) const
145 return completeURL(url);
148 KURL WorkerContext::completeURL(const String& url) const
150 // Always return a null URL when passed a null string.
151 // FIXME: Should we change the KURL constructor to have this behavior?
154 // Always use UTF-8 in Workers.
155 return KURL(m_url, url);
158 String WorkerContext::userAgent(const KURL&) const
163 WorkerLocation* WorkerContext::location() const
166 m_location = WorkerLocation::create(m_url);
167 return m_location.get();
170 void WorkerContext::close()
175 // Let current script run to completion but prevent future script evaluations.
176 // After m_closing is set, all the tasks in the queue continue to be fetched but only
177 // tasks with isCleanupTask()==true will be executed.
179 postTask(CloseWorkerContextTask::create());
182 WorkerNavigator* WorkerContext::navigator() const
185 m_navigator = WorkerNavigator::create(m_userAgent);
186 return m_navigator.get();
189 bool WorkerContext::hasPendingActivity() const
191 ActiveDOMObjectsMap& activeObjects = activeDOMObjects();
192 ActiveDOMObjectsMap::const_iterator activeObjectsEnd = activeObjects.end();
193 for (ActiveDOMObjectsMap::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
194 if (iter->first->hasPendingActivity())
198 HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();
199 for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {
200 if ((*iter)->hasPendingActivity())
207 void WorkerContext::postTask(PassOwnPtr<Task> task)
209 thread()->runLoop().postTask(task);
212 int WorkerContext::setTimeout(PassOwnPtr<ScheduledAction> action, int timeout)
214 return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
217 void WorkerContext::clearTimeout(int timeoutId)
219 DOMTimer::removeById(scriptExecutionContext(), timeoutId);
222 int WorkerContext::setInterval(PassOwnPtr<ScheduledAction> action, int timeout)
224 return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
227 void WorkerContext::clearInterval(int timeoutId)
229 DOMTimer::removeById(scriptExecutionContext(), timeoutId);
232 void WorkerContext::importScripts(const Vector<String>& urls, ExceptionCode& ec)
235 Vector<String>::const_iterator urlsEnd = urls.end();
236 Vector<KURL> completedURLs;
237 for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
238 const KURL& url = scriptExecutionContext()->completeURL(*it);
239 if (!url.isValid()) {
243 completedURLs.append(url);
245 Vector<KURL>::const_iterator end = completedURLs.end();
247 for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
248 RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create(ResourceRequestBase::TargetIsScript));
249 scriptLoader->loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests);
251 // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
252 if (scriptLoader->failed()) {
253 ec = XMLHttpRequestException::NETWORK_ERR;
257 InspectorInstrumentation::scriptImported(scriptExecutionContext(), scriptLoader->identifier(), scriptLoader->script());
259 ScriptValue exception;
260 m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &exception);
261 if (!exception.hasNoValue()) {
262 m_script->setException(exception);
268 EventTarget* WorkerContext::errorEventTarget()
273 void WorkerContext::logExceptionToConsole(const String& errorMessage, int lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack>)
275 thread()->workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, sourceURL);
278 void WorkerContext::addMessage(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL, PassRefPtr<ScriptCallStack>)
280 thread()->workerReportingProxy().postConsoleMessageToWorkerObject(source, type, level, message, lineNumber, sourceURL);
283 #if ENABLE(NOTIFICATIONS)
284 NotificationCenter* WorkerContext::webkitNotifications() const
286 if (!m_notifications)
287 m_notifications = NotificationCenter::create(scriptExecutionContext(), m_thread->getNotificationPresenter());
288 return m_notifications.get();
293 PassRefPtr<Database> WorkerContext::openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
295 if (!securityOrigin()->canAccessDatabase() || !AbstractDatabase::isAvailable()) {
300 return Database::openDatabase(this, name, version, displayName, estimatedSize, creationCallback, ec);
303 void WorkerContext::databaseExceededQuota(const String&)
305 #if !PLATFORM(CHROMIUM)
306 // FIXME: This needs a real implementation; this is a temporary solution for testing.
307 const unsigned long long defaultQuota = 5 * 1024 * 1024;
308 DatabaseTracker::tracker().setQuota(securityOrigin(), defaultQuota);
312 PassRefPtr<DatabaseSync> WorkerContext::openDatabaseSync(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
314 if (!securityOrigin()->canAccessDatabase() || !AbstractDatabase::isAvailable()) {
319 return DatabaseSync::openDatabaseSync(this, name, version, displayName, estimatedSize, creationCallback, ec);
323 bool WorkerContext::isContextThread() const
325 return currentThread() == thread()->threadID();
328 bool WorkerContext::isJSExecutionForbidden() const
330 return m_script->isExecutionForbidden();
333 EventTargetData* WorkerContext::eventTargetData()
335 return &m_eventTargetData;
338 EventTargetData* WorkerContext::ensureEventTargetData()
340 return &m_eventTargetData;
344 DOMURL* WorkerContext::webkitURL() const
347 m_domURL = DOMURL::create(this->scriptExecutionContext());
348 return m_domURL.get();
352 #if ENABLE(FILE_SYSTEM)
353 void WorkerContext::webkitRequestFileSystem(int type, long long size, PassRefPtr<FileSystemCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
355 if (!AsyncFileSystem::isAvailable() || !securityOrigin()->canAccessFileSystem()) {
356 DOMFileSystem::scheduleCallback(this, errorCallback, FileError::create(FileError::SECURITY_ERR));
360 AsyncFileSystem::Type fileSystemType = static_cast<AsyncFileSystem::Type>(type);
361 if (fileSystemType != AsyncFileSystem::Temporary && fileSystemType != AsyncFileSystem::Persistent && fileSystemType != AsyncFileSystem::External) {
362 DOMFileSystem::scheduleCallback(this, errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
366 LocalFileSystem::localFileSystem().requestFileSystem(this, fileSystemType, size, FileSystemCallbacks::create(successCallback, errorCallback, this), false);
369 PassRefPtr<DOMFileSystemSync> WorkerContext::webkitRequestFileSystemSync(int type, long long size, ExceptionCode& ec)
372 if (!AsyncFileSystem::isAvailable() || !securityOrigin()->canAccessFileSystem()) {
373 ec = FileException::SECURITY_ERR;
377 AsyncFileSystem::Type fileSystemType = static_cast<AsyncFileSystem::Type>(type);
378 if (fileSystemType != AsyncFileSystem::Temporary && fileSystemType != AsyncFileSystem::Persistent && fileSystemType != AsyncFileSystem::External) {
379 ec = FileException::INVALID_MODIFICATION_ERR;
383 FileSystemSyncCallbackHelper helper;
384 LocalFileSystem::localFileSystem().requestFileSystem(this, fileSystemType, size, FileSystemCallbacks::create(helper.successCallback(), helper.errorCallback(), this), true);
385 return helper.getResult(ec);
388 void WorkerContext::webkitResolveLocalFileSystemURL(const String& url, PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
390 KURL completedURL = completeURL(url);
391 if (!AsyncFileSystem::isAvailable() || !securityOrigin()->canAccessFileSystem() || !securityOrigin()->canRequest(completedURL)) {
392 DOMFileSystem::scheduleCallback(this, errorCallback, FileError::create(FileError::SECURITY_ERR));
396 AsyncFileSystem::Type type;
398 if (!completedURL.isValid() || !DOMFileSystemBase::crackFileSystemURL(completedURL, type, filePath)) {
399 DOMFileSystem::scheduleCallback(this, errorCallback, FileError::create(FileError::ENCODING_ERR));
403 LocalFileSystem::localFileSystem().readFileSystem(this, type, ResolveURICallbacks::create(successCallback, errorCallback, this, filePath));
406 PassRefPtr<EntrySync> WorkerContext::webkitResolveLocalFileSystemSyncURL(const String& url, ExceptionCode& ec)
409 KURL completedURL = completeURL(url);
410 if (!AsyncFileSystem::isAvailable() || !securityOrigin()->canAccessFileSystem() || !securityOrigin()->canRequest(completedURL)) {
411 ec = FileException::SECURITY_ERR;
415 AsyncFileSystem::Type type;
417 if (!completedURL.isValid() || !DOMFileSystemBase::crackFileSystemURL(completedURL, type, filePath)) {
418 ec = FileException::ENCODING_ERR;
422 FileSystemSyncCallbackHelper readFileSystemHelper;
423 LocalFileSystem::localFileSystem().readFileSystem(this, type, FileSystemCallbacks::create(readFileSystemHelper.successCallback(), readFileSystemHelper.errorCallback(), this), true);
424 RefPtr<DOMFileSystemSync> fileSystem = readFileSystemHelper.getResult(ec);
428 RefPtr<EntrySync> entry = fileSystem->root()->getDirectory(filePath, 0, ec);
429 if (ec == FileException::TYPE_MISMATCH_ERR)
430 return fileSystem->root()->getFile(filePath, 0, ec);
432 return entry.release();
435 COMPILE_ASSERT(static_cast<int>(WorkerContext::TEMPORARY) == static_cast<int>(AsyncFileSystem::Temporary), enum_mismatch);
436 COMPILE_ASSERT(static_cast<int>(WorkerContext::PERSISTENT) == static_cast<int>(AsyncFileSystem::Persistent), enum_mismatch);
437 COMPILE_ASSERT(static_cast<int>(WorkerContext::EXTERNAL) == static_cast<int>(AsyncFileSystem::External), enum_mismatch);
440 WorkerContext::Observer::Observer(WorkerContext* context)
443 ASSERT(m_context && m_context->isContextThread());
444 m_context->registerObserver(this);
447 WorkerContext::Observer::~Observer()
451 ASSERT(m_context->isContextThread());
452 m_context->unregisterObserver(this);
455 void WorkerContext::Observer::stopObserving()
459 ASSERT(m_context->isContextThread());
460 m_context->unregisterObserver(this);
464 void WorkerContext::registerObserver(Observer* observer)
467 m_workerObservers.add(observer);
470 void WorkerContext::unregisterObserver(Observer* observer)
473 m_workerObservers.remove(observer);
476 void WorkerContext::notifyObserversOfStop()
478 HashSet<Observer*>::iterator iter = m_workerObservers.begin();
479 while (iter != m_workerObservers.end()) {
480 WorkerContext::Observer* observer = *iter;
481 observer->stopObserving();
482 observer->notifyStop();
483 iter = m_workerObservers.begin();
487 } // namespace WebCore
489 #endif // ENABLE(WORKERS)