summaryrefslogtreecommitdiff
path: root/Source/WebCore/workers/DefaultSharedWorkerRepository.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/workers/DefaultSharedWorkerRepository.cpp')
-rw-r--r--Source/WebCore/workers/DefaultSharedWorkerRepository.cpp452
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)