diff options
Diffstat (limited to 'Source/WebCore/workers/DefaultSharedWorkerRepository.cpp')
-rw-r--r-- | Source/WebCore/workers/DefaultSharedWorkerRepository.cpp | 452 |
1 files changed, 0 insertions, 452 deletions
diff --git a/Source/WebCore/workers/DefaultSharedWorkerRepository.cpp b/Source/WebCore/workers/DefaultSharedWorkerRepository.cpp deleted file mode 100644 index a64a4c90c..000000000 --- a/Source/WebCore/workers/DefaultSharedWorkerRepository.cpp +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(SHARED_WORKERS) - -#include "DefaultSharedWorkerRepository.h" - -#include "ActiveDOMObject.h" -#include "CrossThreadTask.h" -#include "Document.h" -#include "ExceptionCode.h" -#include "InspectorInstrumentation.h" -#include "MessageEvent.h" -#include "MessagePort.h" -#include "NotImplemented.h" -#include "PageGroup.h" -#include "PlatformStrategies.h" -#include "ScriptCallStack.h" -#include "SecurityOrigin.h" -#include "SecurityOriginHash.h" -#include "SharedWorker.h" -#include "SharedWorkerGlobalScope.h" -#include "SharedWorkerRepository.h" -#include "SharedWorkerStrategy.h" -#include "SharedWorkerThread.h" -#include "WorkerLoaderProxy.h" -#include "WorkerReportingProxy.h" -#include "WorkerScriptLoader.h" -#include "WorkerScriptLoaderClient.h" -#include <mutex> -#include <wtf/HashSet.h> -#include <wtf/Threading.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class SharedWorkerProxy : public ThreadSafeRefCounted<SharedWorkerProxy>, public WorkerLoaderProxy, public WorkerReportingProxy { -public: - static PassRefPtr<SharedWorkerProxy> create(const String& name, const URL& url, PassRefPtr<SecurityOrigin> origin) { return adoptRef(new SharedWorkerProxy(name, url, origin)); } - - void setThread(PassRefPtr<SharedWorkerThread> thread) { m_thread = thread; } - SharedWorkerThread* thread() { return m_thread.get(); } - bool isClosing() const { return m_closing; } - URL url() const - { - // Don't use m_url.copy() because it isn't a threadsafe method. - return URL(ParsedURLString, m_url.string().isolatedCopy()); - } - - String name() const { return m_name.isolatedCopy(); } - bool matches(const String& name, PassRefPtr<SecurityOrigin> origin, const URL& urlToMatch) const; - - // WorkerLoaderProxy - virtual void postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task>); - virtual bool postTaskForModeToWorkerGlobalScope(PassOwnPtr<ScriptExecutionContext::Task>, const String&); - - // WorkerReportingProxy - virtual void postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL); - virtual void postConsoleMessageToWorkerObject(MessageSource, MessageLevel, const String& message, int lineNumber, int columnNumber, const String& sourceURL); -#if ENABLE(INSPECTOR) - virtual void postMessageToPageInspector(const String&); -#endif - virtual void workerGlobalScopeClosed(); - virtual void workerGlobalScopeDestroyed(); - - // Updates the list of the worker's documents, per section 4.5 of the WebWorkers spec. - void addToWorkerDocuments(ScriptExecutionContext*); - - bool isInWorkerDocuments(Document* document) { return m_workerDocuments.contains(document); } - - // Removes a detached document from the list of worker's documents. May set the closing flag if this is the last document in the list. - void documentDetached(Document*); - - GroupSettings* groupSettings() const; // Page GroupSettings used by worker thread. - -private: - SharedWorkerProxy(const String& name, const URL&, PassRefPtr<SecurityOrigin>); - void close(); - - bool m_closing; - String m_name; - URL m_url; - // The thread is freed when the proxy is destroyed, so we need to make sure that the proxy stays around until the SharedWorkerGlobalScope exits. - RefPtr<SharedWorkerThread> m_thread; - RefPtr<SecurityOrigin> m_origin; - HashSet<Document*> m_workerDocuments; - // Ensures exclusive access to the worker documents. Must not grab any other locks (such as the DefaultSharedWorkerRepository lock) while holding this one. - Mutex m_workerDocumentsLock; -}; - -SharedWorkerProxy::SharedWorkerProxy(const String& name, const URL& url, PassRefPtr<SecurityOrigin> origin) - : m_closing(false) - , m_name(name.isolatedCopy()) - , m_url(url.copy()) - , m_origin(origin) -{ - // We should be the sole owner of the SecurityOrigin, as we will free it on another thread. - ASSERT(m_origin->hasOneRef()); -} - -bool SharedWorkerProxy::matches(const String& name, PassRefPtr<SecurityOrigin> origin, const URL& urlToMatch) const -{ - // If the origins don't match, or the names don't match, then this is not the proxy we are looking for. - if (!origin->equal(m_origin.get())) - return false; - - // If the names are both empty, compares the URLs instead per the Web Workers spec. - if (name.isEmpty() && m_name.isEmpty()) - return urlToMatch == url(); - - return name == m_name; -} - -void SharedWorkerProxy::postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task> task) -{ - MutexLocker lock(m_workerDocumentsLock); - - if (isClosing()) - return; - - // If we aren't closing, then we must have at least one document. - ASSERT(m_workerDocuments.size()); - - // Just pick an arbitrary active document from the HashSet and pass load requests to it. - // FIXME: Do we need to deal with the case where the user closes the document mid-load, via a shadow document or some other solution? - Document* document = *(m_workerDocuments.begin()); - document->postTask(task); -} - -bool SharedWorkerProxy::postTaskForModeToWorkerGlobalScope(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode) -{ - if (isClosing()) - return false; - ASSERT(m_thread); - m_thread->runLoop().postTaskForMode(task, mode); - return true; -} - -GroupSettings* SharedWorkerProxy::groupSettings() const -{ - if (isClosing()) - return 0; - ASSERT(m_workerDocuments.size()); - // Just pick the first active document, and use the groupsettings of that page. - Document* document = *(m_workerDocuments.begin()); - if (document->page()) - return &document->page()->group().groupSettings(); - - return 0; -} - -static void postExceptionTask(ScriptExecutionContext* context, const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL) -{ - context->reportException(errorMessage, lineNumber, columnNumber, sourceURL, 0); -} - -void SharedWorkerProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL) -{ - MutexLocker lock(m_workerDocumentsLock); - for (HashSet<Document*>::iterator iter = m_workerDocuments.begin(); iter != m_workerDocuments.end(); ++iter) - (*iter)->postTask(createCallbackTask(&postExceptionTask, errorMessage, lineNumber, columnNumber, sourceURL)); -} - -static void postConsoleMessageTask(ScriptExecutionContext* document, MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber) -{ - document->addConsoleMessage(source, level, message, sourceURL, lineNumber, columnNumber); -} - -void SharedWorkerProxy::postConsoleMessageToWorkerObject(MessageSource source, MessageLevel level, const String& message, int lineNumber, int columnNumber, const String& sourceURL) -{ - MutexLocker lock(m_workerDocumentsLock); - for (HashSet<Document*>::iterator iter = m_workerDocuments.begin(); iter != m_workerDocuments.end(); ++iter) - (*iter)->postTask(createCallbackTask(&postConsoleMessageTask, source, level, message, sourceURL, lineNumber, columnNumber)); -} - -#if ENABLE(INSPECTOR) -void SharedWorkerProxy::postMessageToPageInspector(const String&) -{ - notImplemented(); -} -#endif - -void SharedWorkerProxy::workerGlobalScopeClosed() -{ - if (isClosing()) - return; - close(); -} - -void SharedWorkerProxy::workerGlobalScopeDestroyed() -{ - // The proxy may be freed by this call, so do not reference it any further. - DefaultSharedWorkerRepository::instance().removeProxy(this); -} - -void SharedWorkerProxy::addToWorkerDocuments(ScriptExecutionContext* context) -{ - // Nested workers are not yet supported, so passed-in context should always be a Document. - ASSERT_WITH_SECURITY_IMPLICATION(context->isDocument()); - ASSERT(!isClosing()); - MutexLocker lock(m_workerDocumentsLock); - Document* document = static_cast<Document*>(context); - m_workerDocuments.add(document); -} - -void SharedWorkerProxy::documentDetached(Document* document) -{ - if (isClosing()) - return; - // Remove the document from our set (if it's there) and if that was the last document in the set, mark the proxy as closed. - MutexLocker lock(m_workerDocumentsLock); - m_workerDocuments.remove(document); - if (!m_workerDocuments.size()) - close(); -} - -void SharedWorkerProxy::close() -{ - ASSERT(!isClosing()); - m_closing = true; - // Stop the worker thread - the proxy will stay around until we get workerThreadExited() notification. - if (m_thread) - m_thread->stop(); -} - -class SharedWorkerConnectTask : public ScriptExecutionContext::Task { -public: - static PassOwnPtr<SharedWorkerConnectTask> create(PassOwnPtr<MessagePortChannel> channel) - { - return adoptPtr(new SharedWorkerConnectTask(channel)); - } - -private: - SharedWorkerConnectTask(PassOwnPtr<MessagePortChannel> channel) - : m_channel(channel) - { - } - - virtual void performTask(ScriptExecutionContext* scriptContext) - { - RefPtr<MessagePort> port = MessagePort::create(*scriptContext); - port->entangle(m_channel.release()); - ASSERT_WITH_SECURITY_IMPLICATION(scriptContext->isWorkerGlobalScope()); - WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(scriptContext); - // Since close() stops the thread event loop, this should not ever get called while closing. - ASSERT(!workerGlobalScope->isClosing()); - ASSERT_WITH_SECURITY_IMPLICATION(workerGlobalScope->isSharedWorkerGlobalScope()); - workerGlobalScope->dispatchEvent(createConnectEvent(port)); - } - - OwnPtr<MessagePortChannel> m_channel; -}; - -// Loads the script on behalf of a worker. -class SharedWorkerScriptLoader : public RefCounted<SharedWorkerScriptLoader>, private WorkerScriptLoaderClient { -public: - SharedWorkerScriptLoader(PassRefPtr<SharedWorker>, PassOwnPtr<MessagePortChannel>, PassRefPtr<SharedWorkerProxy>); - void load(const URL&); - -private: - // WorkerScriptLoaderClient callbacks - virtual void didReceiveResponse(unsigned long identifier, const ResourceResponse&); - virtual void notifyFinished(); - - RefPtr<SharedWorker> m_worker; - OwnPtr<MessagePortChannel> m_port; - RefPtr<SharedWorkerProxy> m_proxy; - RefPtr<WorkerScriptLoader> m_scriptLoader; -}; - -SharedWorkerScriptLoader::SharedWorkerScriptLoader(PassRefPtr<SharedWorker> worker, PassOwnPtr<MessagePortChannel> port, PassRefPtr<SharedWorkerProxy> proxy) - : m_worker(worker) - , m_port(port) - , m_proxy(proxy) -{ -} - -void SharedWorkerScriptLoader::load(const URL& url) -{ - // Stay alive (and keep the SharedWorker and JS wrapper alive) until the load finishes. - this->ref(); - m_worker->setPendingActivity(m_worker.get()); - - // Mark this object as active for the duration of the load. - m_scriptLoader = WorkerScriptLoader::create(); - m_scriptLoader->loadAsynchronously(m_worker->scriptExecutionContext(), url, DenyCrossOriginRequests, this); -} - -void SharedWorkerScriptLoader::didReceiveResponse(unsigned long identifier, const ResourceResponse&) -{ - InspectorInstrumentation::didReceiveScriptResponse(m_worker->scriptExecutionContext(), identifier); -} - -void SharedWorkerScriptLoader::notifyFinished() -{ - // FIXME: This method is not guaranteed to be invoked if we are loading from WorkerGlobalScope (see comment for WorkerScriptLoaderClient::notifyFinished()). - // We need to address this before supporting nested workers. - - // Hand off the just-loaded code to the repository to start up the worker thread. - if (m_scriptLoader->failed()) - m_worker->dispatchEvent(Event::create(eventNames().errorEvent, false, true)); - else { - InspectorInstrumentation::scriptImported(m_worker->scriptExecutionContext(), m_scriptLoader->identifier(), m_scriptLoader->script()); - DefaultSharedWorkerRepository::instance().workerScriptLoaded(*m_proxy, m_worker->scriptExecutionContext()->userAgent(m_scriptLoader->url()), - m_scriptLoader->script(), m_port.release(), - m_worker->scriptExecutionContext()->contentSecurityPolicy()->deprecatedHeader(), - m_worker->scriptExecutionContext()->contentSecurityPolicy()->deprecatedHeaderType()); - } - m_worker->unsetPendingActivity(m_worker.get()); - this->deref(); // This frees this object - must be the last action in this function. -} - -DefaultSharedWorkerRepository& DefaultSharedWorkerRepository::instance() -{ - static std::once_flag onceFlag; - static DefaultSharedWorkerRepository* instance; - std::call_once(onceFlag, []{ - instance = new DefaultSharedWorkerRepository; - }); - - return *instance; -} - -bool DefaultSharedWorkerRepository::isAvailable() -{ - return platformStrategies()->sharedWorkerStrategy()->isAvailable(); -} - -void DefaultSharedWorkerRepository::workerScriptLoaded(SharedWorkerProxy& proxy, const String& userAgent, const String& workerScript, PassOwnPtr<MessagePortChannel> port, const String& contentSecurityPolicy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType) -{ - MutexLocker lock(m_lock); - if (proxy.isClosing()) - return; - - // Another loader may have already started up a thread for this proxy - if so, just send a connect to the pre-existing thread. - if (!proxy.thread()) { - RefPtr<SharedWorkerThread> thread = SharedWorkerThread::create(proxy.name(), proxy.url(), userAgent, proxy.groupSettings(), workerScript, proxy, proxy, DontPauseWorkerGlobalScopeOnStart, contentSecurityPolicy, contentSecurityPolicyType); - proxy.setThread(thread); - thread->start(); - } - proxy.thread()->runLoop().postTask(SharedWorkerConnectTask::create(port)); -} - -bool DefaultSharedWorkerRepository::hasSharedWorkers(Document* document) -{ - MutexLocker lock(m_lock); - for (unsigned i = 0; i < m_proxies.size(); i++) { - if (m_proxies[i]->isInWorkerDocuments(document)) - return true; - } - return false; -} - -void DefaultSharedWorkerRepository::removeProxy(SharedWorkerProxy* proxy) -{ - MutexLocker lock(m_lock); - for (unsigned i = 0; i < m_proxies.size(); i++) { - if (proxy == m_proxies[i].get()) { - m_proxies.remove(i); - return; - } - } -} - -void DefaultSharedWorkerRepository::documentDetached(Document* document) -{ - MutexLocker lock(m_lock); - for (unsigned i = 0; i < m_proxies.size(); i++) - m_proxies[i]->documentDetached(document); -} - -void DefaultSharedWorkerRepository::connectToWorker(PassRefPtr<SharedWorker> worker, PassOwnPtr<MessagePortChannel> port, const URL& url, const String& name, ExceptionCode& ec) -{ - MutexLocker lock(m_lock); - ASSERT(worker->scriptExecutionContext()->securityOrigin()->canAccess(SecurityOrigin::create(url).get())); - // Fetch a proxy corresponding to this SharedWorker. - RefPtr<SharedWorkerProxy> proxy = getProxy(name, url); - - // FIXME: Why is this done even if we are raising an exception below? - proxy->addToWorkerDocuments(worker->scriptExecutionContext()); - - if (proxy->url() != url) { - // Proxy already existed under alternate URL - return an error. - ec = URL_MISMATCH_ERR; - return; - } - // If proxy is already running, just connect to it - otherwise, kick off a loader to load the script. - if (proxy->thread()) - proxy->thread()->runLoop().postTask(SharedWorkerConnectTask::create(port)); - else { - RefPtr<SharedWorkerScriptLoader> loader = adoptRef(new SharedWorkerScriptLoader(worker, port, proxy.release())); - loader->load(url); - } -} - -// Creates a new SharedWorkerProxy or returns an existing one from the repository. Must only be called while the repository mutex is held. -PassRefPtr<SharedWorkerProxy> DefaultSharedWorkerRepository::getProxy(const String& name, const URL& url) -{ - // Look for an existing worker, and create one if it doesn't exist. - // Items in the cache are freed on another thread, so do a threadsafe copy of the URL before creating the origin, - // to make sure no references to external strings linger. - RefPtr<SecurityOrigin> origin = SecurityOrigin::create(URL(ParsedURLString, url.string().isolatedCopy())); - for (unsigned i = 0; i < m_proxies.size(); i++) { - if (!m_proxies[i]->isClosing() && m_proxies[i]->matches(name, origin, url)) - return m_proxies[i]; - } - // Proxy is not in the repository currently - create a new one. - RefPtr<SharedWorkerProxy> proxy = SharedWorkerProxy::create(name, url, origin.release()); - m_proxies.append(proxy); - return proxy.release(); -} - -DefaultSharedWorkerRepository::DefaultSharedWorkerRepository() -{ -} - -DefaultSharedWorkerRepository::~DefaultSharedWorkerRepository() -{ -} - -} // namespace WebCore - -#endif // ENABLE(SHARED_WORKERS) |