DefaultSharedWorkerRepository.cpp [plain text]
#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 "PlatformString.h"
#include "ScriptCallStack.h"
#include "SecurityOrigin.h"
#include "SecurityOriginHash.h"
#include "SharedWorker.h"
#include "SharedWorkerContext.h"
#include "SharedWorkerRepository.h"
#include "SharedWorkerThread.h"
#include "WorkerLoaderProxy.h"
#include "WorkerReportingProxy.h"
#include "WorkerScriptLoader.h"
#include "WorkerScriptLoaderClient.h"
#include <wtf/HashSet.h>
#include <wtf/Threading.h>
namespace WebCore {
class SharedWorkerProxy : public ThreadSafeRefCounted<SharedWorkerProxy>, public WorkerLoaderProxy, public WorkerReportingProxy {
public:
static PassRefPtr<SharedWorkerProxy> create(const String& name, const KURL& 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; }
KURL url() const
{
return KURL(ParsedURLString, m_url.string().isolatedCopy());
}
String name() const { return m_name.isolatedCopy(); }
bool matches(const String& name, PassRefPtr<SecurityOrigin> origin, const KURL& urlToMatch) const;
virtual void postTaskToLoader(PassOwnPtr<ScriptExecutionContext::Task>);
virtual bool postTaskForModeToWorkerContext(PassOwnPtr<ScriptExecutionContext::Task>, const String&);
virtual void postExceptionToWorkerObject(const String& errorMessage, int lineNumber, const String& sourceURL);
virtual void postConsoleMessageToWorkerObject(MessageSource, MessageType, MessageLevel, const String& message, int lineNumber, const String& sourceURL);
#if ENABLE(INSPECTOR)
virtual void postMessageToPageInspector(const String&);
virtual void updateInspectorStateCookie(const String&);
#endif
virtual void workerContextClosed();
virtual void workerContextDestroyed();
void addToWorkerDocuments(ScriptExecutionContext*);
bool isInWorkerDocuments(Document* document) { return m_workerDocuments.contains(document); }
void documentDetached(Document*);
private:
SharedWorkerProxy(const String& name, const KURL&, PassRefPtr<SecurityOrigin>);
void close();
bool m_closing;
String m_name;
KURL m_url;
RefPtr<SharedWorkerThread> m_thread;
RefPtr<SecurityOrigin> m_origin;
HashSet<Document*> m_workerDocuments;
Mutex m_workerDocumentsLock;
};
SharedWorkerProxy::SharedWorkerProxy(const String& name, const KURL& url, PassRefPtr<SecurityOrigin> origin)
: m_closing(false)
, m_name(name.isolatedCopy())
, m_url(url.copy())
, m_origin(origin)
{
ASSERT(m_origin->hasOneRef());
}
bool SharedWorkerProxy::matches(const String& name, PassRefPtr<SecurityOrigin> origin, const KURL& urlToMatch) const
{
if (!origin->equal(m_origin.get()))
return false;
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;
ASSERT(m_workerDocuments.size());
Document* document = *(m_workerDocuments.begin());
document->postTask(task);
}
bool SharedWorkerProxy::postTaskForModeToWorkerContext(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
{
if (isClosing())
return false;
ASSERT(m_thread);
m_thread->runLoop().postTaskForMode(task, mode);
return true;
}
static void postExceptionTask(ScriptExecutionContext* context, const String& errorMessage, int lineNumber, const String& sourceURL)
{
context->reportException(errorMessage, lineNumber, sourceURL, 0);
}
void SharedWorkerProxy::postExceptionToWorkerObject(const String& errorMessage, int lineNumber, 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, sourceURL));
}
static void postConsoleMessageTask(ScriptExecutionContext* document, MessageSource source, MessageType type, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber)
{
document->addConsoleMessage(source, type, level, message, sourceURL, lineNumber);
}
void SharedWorkerProxy::postConsoleMessageToWorkerObject(MessageSource source, MessageType type, MessageLevel level, const String& message, int lineNumber, 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, type, level, message, sourceURL, lineNumber));
}
#if ENABLE(INSPECTOR)
void SharedWorkerProxy::postMessageToPageInspector(const String&)
{
notImplemented();
}
void SharedWorkerProxy::updateInspectorStateCookie(const String&)
{
notImplemented();
}
#endif
void SharedWorkerProxy::workerContextClosed()
{
if (isClosing())
return;
close();
}
void SharedWorkerProxy::workerContextDestroyed()
{
DefaultSharedWorkerRepository::instance().removeProxy(this);
}
void SharedWorkerProxy::addToWorkerDocuments(ScriptExecutionContext* context)
{
ASSERT(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;
MutexLocker lock(m_workerDocumentsLock);
m_workerDocuments.remove(document);
if (!m_workerDocuments.size())
close();
}
void SharedWorkerProxy::close()
{
ASSERT(!isClosing());
m_closing = true;
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(scriptContext->isWorkerContext());
WorkerContext* workerContext = static_cast<WorkerContext*>(scriptContext);
ASSERT(!workerContext->isClosing());
ASSERT(workerContext->isSharedWorkerContext());
workerContext->dispatchEvent(createConnectEvent(port));
}
OwnPtr<MessagePortChannel> m_channel;
};
class SharedWorkerScriptLoader : public RefCounted<SharedWorkerScriptLoader>, private WorkerScriptLoaderClient {
public:
SharedWorkerScriptLoader(PassRefPtr<SharedWorker>, PassOwnPtr<MessagePortChannel>, PassRefPtr<SharedWorkerProxy>);
void load(const KURL&);
private:
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 KURL& url)
{
this->ref();
m_worker->setPendingActivity(m_worker.get());
m_scriptLoader = WorkerScriptLoader::create();
#if PLATFORM(CHROMIUM) || PLATFORM(BLACKBERRY)
m_scriptLoader->setTargetType(ResourceRequest::TargetIsSharedWorker);
#endif
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()
{
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()->header(),
m_worker->scriptExecutionContext()->contentSecurityPolicy()->headerType());
}
m_worker->unsetPendingActivity(m_worker.get());
this->deref(); }
DefaultSharedWorkerRepository& DefaultSharedWorkerRepository::instance()
{
AtomicallyInitializedStatic(DefaultSharedWorkerRepository*, instance = new DefaultSharedWorkerRepository);
return *instance;
}
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;
if (!proxy.thread()) {
RefPtr<SharedWorkerThread> thread = SharedWorkerThread::create(proxy.name(), proxy.url(), userAgent, workerScript, proxy, proxy, DontPauseWorkerContextOnStart, contentSecurityPolicy, contentSecurityPolicyType);
proxy.setThread(thread);
thread->start();
}
proxy.thread()->runLoop().postTask(SharedWorkerConnectTask::create(port));
}
bool SharedWorkerRepository::isAvailable()
{
return true;
}
void SharedWorkerRepository::connect(PassRefPtr<SharedWorker> worker, PassOwnPtr<MessagePortChannel> port, const KURL& url, const String& name, ExceptionCode& ec)
{
DefaultSharedWorkerRepository::instance().connectToWorker(worker, port, url, name, ec);
}
void SharedWorkerRepository::documentDetached(Document* document)
{
DefaultSharedWorkerRepository::instance().documentDetached(document);
}
bool SharedWorkerRepository::hasSharedWorkers(Document* document)
{
return DefaultSharedWorkerRepository::instance().hasSharedWorkers(document);
}
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 KURL& url, const String& name, ExceptionCode& ec)
{
MutexLocker lock(m_lock);
ASSERT(worker->scriptExecutionContext()->securityOrigin()->canAccess(SecurityOrigin::create(url).get()));
RefPtr<SharedWorkerProxy> proxy = getProxy(name, url);
proxy->addToWorkerDocuments(worker->scriptExecutionContext());
if (proxy->url() != url) {
ec = URL_MISMATCH_ERR;
return;
}
if (proxy->thread())
proxy->thread()->runLoop().postTask(SharedWorkerConnectTask::create(port));
else {
RefPtr<SharedWorkerScriptLoader> loader = adoptRef(new SharedWorkerScriptLoader(worker, port, proxy.release()));
loader->load(url);
}
}
PassRefPtr<SharedWorkerProxy> DefaultSharedWorkerRepository::getProxy(const String& name, const KURL& url)
{
RefPtr<SecurityOrigin> origin = SecurityOrigin::create(KURL(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];
}
RefPtr<SharedWorkerProxy> proxy = SharedWorkerProxy::create(name, url, origin.release());
m_proxies.append(proxy);
return proxy.release();
}
DefaultSharedWorkerRepository::DefaultSharedWorkerRepository()
{
}
DefaultSharedWorkerRepository::~DefaultSharedWorkerRepository()
{
}
}
#endif // ENABLE(SHARED_WORKERS)