• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 
33 #if ENABLE(SHARED_WORKERS)
34 
35 #include "DefaultSharedWorkerRepository.h"
36 
37 #include "ActiveDOMObject.h"
38 #include "MessagePort.h"
39 #include "NotImplemented.h"
40 #include "PlatformString.h"
41 #include "SecurityOrigin.h"
42 #include "SecurityOriginHash.h"
43 #include "SharedWorker.h"
44 #include "SharedWorkerContext.h"
45 #include "SharedWorkerThread.h"
46 #include "WorkerLoaderProxy.h"
47 #include "WorkerScriptLoader.h"
48 #include "WorkerScriptLoaderClient.h"
49 
50 #include <wtf/Threading.h>
51 
52 namespace WebCore {
53 
54 class SharedWorkerProxy : public ThreadSafeShared<SharedWorkerProxy>, public WorkerLoaderProxy {
55 public:
create(const String & name,const KURL & url)56     static PassRefPtr<SharedWorkerProxy> create(const String& name, const KURL& url) { return adoptRef(new SharedWorkerProxy(name, url)); }
57 
setThread(PassRefPtr<SharedWorkerThread> thread)58     void setThread(PassRefPtr<SharedWorkerThread> thread) { m_thread = thread; }
thread()59     SharedWorkerThread* thread() { return m_thread.get(); }
closing() const60     bool closing() const { return m_closing; }
url() const61     KURL url() const { return m_url.copy(); }
name() const62     String name() const { return m_name.copy(); }
63 
64     // WorkerLoaderProxy
65     // FIXME: Implement WorkerLoaderProxy APIs by proxying to an active document.
postTaskToLoader(PassRefPtr<ScriptExecutionContext::Task>)66     virtual void postTaskToLoader(PassRefPtr<ScriptExecutionContext::Task>) { notImplemented(); }
postTaskForModeToWorkerContext(PassRefPtr<ScriptExecutionContext::Task>,const String &)67     virtual void postTaskForModeToWorkerContext(PassRefPtr<ScriptExecutionContext::Task>, const String&) { notImplemented(); }
68 
69     // Updates the list of the worker's documents, per section 4.5 of the WebWorkers spec.
70     void addToWorkerDocuments(ScriptExecutionContext*);
71 private:
72     SharedWorkerProxy(const String& name, const KURL&);
73     bool m_closing;
74     String m_name;
75     KURL m_url;
76     RefPtr<SharedWorkerThread> m_thread;
77 };
78 
SharedWorkerProxy(const String & name,const KURL & url)79 SharedWorkerProxy::SharedWorkerProxy(const String& name, const KURL& url)
80     : m_closing(false)
81     , m_name(name.copy())
82     , m_url(url.copy())
83 {
84 }
85 
addToWorkerDocuments(ScriptExecutionContext * context)86 void SharedWorkerProxy::addToWorkerDocuments(ScriptExecutionContext* context)
87 {
88     // Nested workers are not yet supported, so passed-in context should always be a Document.
89     ASSERT(context->isDocument());
90     // FIXME: track referring documents so we can shutdown the thread when the last one exits and remove the proxy from the cache.
91 }
92 
93 class SharedWorkerConnectTask : public ScriptExecutionContext::Task {
94 public:
create(PassOwnPtr<MessagePortChannel> channel)95     static PassRefPtr<SharedWorkerConnectTask> create(PassOwnPtr<MessagePortChannel> channel)
96     {
97         return adoptRef(new SharedWorkerConnectTask(channel));
98     }
99 
100 private:
SharedWorkerConnectTask(PassOwnPtr<MessagePortChannel> channel)101     SharedWorkerConnectTask(PassOwnPtr<MessagePortChannel> channel)
102         : m_channel(channel)
103     {
104     }
105 
performTask(ScriptExecutionContext * scriptContext)106     virtual void performTask(ScriptExecutionContext* scriptContext)
107     {
108         RefPtr<MessagePort> port = MessagePort::create(*scriptContext);
109         port->entangle(m_channel.release());
110         ASSERT(scriptContext->isWorkerContext());
111         WorkerContext* workerContext = static_cast<WorkerContext*>(scriptContext);
112         ASSERT(workerContext->isSharedWorkerContext());
113         workerContext->toSharedWorkerContext()->dispatchConnect(port);
114     }
115 
116     OwnPtr<MessagePortChannel> m_channel;
117 };
118 
119 // Loads the script on behalf of a worker.
120 class SharedWorkerScriptLoader : public RefCounted<SharedWorkerScriptLoader>, public ActiveDOMObject, private WorkerScriptLoaderClient {
121 public:
122     SharedWorkerScriptLoader(PassRefPtr<SharedWorker>, PassOwnPtr<MessagePortChannel>, PassRefPtr<SharedWorkerProxy>);
123     void load(const KURL&);
124 
125 private:
126     // WorkerScriptLoaderClient callback
127     virtual void notifyFinished();
128 
129     RefPtr<SharedWorker> m_worker;
130     OwnPtr<MessagePortChannel> m_port;
131     RefPtr<SharedWorkerProxy> m_proxy;
132     OwnPtr<WorkerScriptLoader> m_scriptLoader;
133 };
134 
SharedWorkerScriptLoader(PassRefPtr<SharedWorker> worker,PassOwnPtr<MessagePortChannel> port,PassRefPtr<SharedWorkerProxy> proxy)135 SharedWorkerScriptLoader::SharedWorkerScriptLoader(PassRefPtr<SharedWorker> worker, PassOwnPtr<MessagePortChannel> port, PassRefPtr<SharedWorkerProxy> proxy)
136     : ActiveDOMObject(worker->scriptExecutionContext(), this)
137     , m_worker(worker)
138     , m_port(port)
139     , m_proxy(proxy)
140 {
141 }
142 
load(const KURL & url)143 void SharedWorkerScriptLoader::load(const KURL& url)
144 {
145     // Mark this object as active for the duration of the load.
146     ASSERT(!hasPendingActivity());
147     m_scriptLoader = new WorkerScriptLoader();
148     m_scriptLoader->loadAsynchronously(scriptExecutionContext(), url, DenyCrossOriginRedirect, this);
149 
150     // Stay alive until the load finishes.
151     setPendingActivity(this);
152 }
153 
notifyFinished()154 void SharedWorkerScriptLoader::notifyFinished()
155 {
156     // Hand off the just-loaded code to the repository to start up the worker thread.
157     if (m_scriptLoader->failed())
158         m_worker->dispatchLoadErrorEvent();
159     else
160         DefaultSharedWorkerRepository::instance().workerScriptLoaded(*m_proxy, scriptExecutionContext()->userAgent(m_scriptLoader->url()), m_scriptLoader->script(), m_port.release());
161 
162     // This frees this object - must be the last action in this function.
163     unsetPendingActivity(this);
164 }
165 
instance()166 DefaultSharedWorkerRepository& DefaultSharedWorkerRepository::instance()
167 {
168     AtomicallyInitializedStatic(DefaultSharedWorkerRepository*, instance = new DefaultSharedWorkerRepository);
169     return *instance;
170 }
171 
workerScriptLoaded(SharedWorkerProxy & proxy,const String & userAgent,const String & workerScript,PassOwnPtr<MessagePortChannel> port)172 void DefaultSharedWorkerRepository::workerScriptLoaded(SharedWorkerProxy& proxy, const String& userAgent, const String& workerScript, PassOwnPtr<MessagePortChannel> port)
173 {
174     MutexLocker lock(m_lock);
175     if (proxy.closing())
176         return;
177 
178     // Another loader may have already started up a thread for this proxy - if so, just send a connect to the pre-existing thread.
179     if (!proxy.thread()) {
180         RefPtr<SharedWorkerThread> thread = SharedWorkerThread::create(proxy.name(), proxy.url(), userAgent, workerScript, proxy);
181         proxy.setThread(thread);
182         thread->start();
183     }
184     proxy.thread()->runLoop().postTask(SharedWorkerConnectTask::create(port));
185 }
186 
connect(PassRefPtr<SharedWorker> worker,PassOwnPtr<MessagePortChannel> port,const KURL & url,const String & name,ExceptionCode & ec)187 void SharedWorkerRepository::connect(PassRefPtr<SharedWorker> worker, PassOwnPtr<MessagePortChannel> port, const KURL& url, const String& name, ExceptionCode& ec)
188 {
189     DefaultSharedWorkerRepository::instance().connectToWorker(worker, port, url, name, ec);
190 }
191 
connectToWorker(PassRefPtr<SharedWorker> worker,PassOwnPtr<MessagePortChannel> port,const KURL & url,const String & name,ExceptionCode & ec)192 void DefaultSharedWorkerRepository::connectToWorker(PassRefPtr<SharedWorker> worker, PassOwnPtr<MessagePortChannel> port, const KURL& url, const String& name, ExceptionCode& ec)
193 {
194     MutexLocker lock(m_lock);
195     ASSERT(worker->scriptExecutionContext()->securityOrigin()->canAccess(SecurityOrigin::create(url).get()));
196     // Fetch a proxy corresponding to this SharedWorker.
197     RefPtr<SharedWorkerProxy> proxy = getProxy(name, url);
198     proxy->addToWorkerDocuments(worker->scriptExecutionContext());
199     if (proxy->url() != url) {
200         // Proxy already existed under alternate URL - return an error.
201         ec = URL_MISMATCH_ERR;
202         return;
203     }
204     // If proxy is already running, just connect to it - otherwise, kick off a loader to load the script.
205     if (proxy->thread())
206         proxy->thread()->runLoop().postTask(SharedWorkerConnectTask::create(port));
207     else {
208         RefPtr<SharedWorkerScriptLoader> loader = adoptRef(new SharedWorkerScriptLoader(worker, port.release(), proxy.release()));
209         loader->load(url);
210     }
211 }
212 
213 // Creates a new SharedWorkerProxy or returns an existing one from the repository. Must only be called while the repository mutex is held.
getProxy(const String & name,const KURL & url)214 PassRefPtr<SharedWorkerProxy> DefaultSharedWorkerRepository::getProxy(const String& name, const KURL& url)
215 {
216     // Look for an existing worker, and create one if it doesn't exist.
217     // Items in the cache are freed on another thread, so copy the URL before creating the origin, to make sure no references to external strings linger.
218     RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url.copy());
219     SharedWorkerNameMap* nameMap = m_cache.get(origin);
220     if (!nameMap) {
221         nameMap = new SharedWorkerNameMap();
222         m_cache.set(origin, nameMap);
223     }
224 
225     RefPtr<SharedWorkerProxy> proxy = nameMap->get(name);
226     if (!proxy.get()) {
227         proxy = SharedWorkerProxy::create(name, url);
228         nameMap->set(proxy->name(), proxy);
229     }
230     return proxy;
231 }
232 
DefaultSharedWorkerRepository()233 DefaultSharedWorkerRepository::DefaultSharedWorkerRepository()
234 {
235 }
236 
~DefaultSharedWorkerRepository()237 DefaultSharedWorkerRepository::~DefaultSharedWorkerRepository()
238 {
239 }
240 
241 } // namespace WebCore
242 
243 #endif // ENABLE(SHARED_WORKERS)
244