WebKit/chromium/src/SharedWorkerRepository.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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 "SharedWorkerRepository.h"
       
    36 
       
    37 #include "Event.h"
       
    38 #include "EventNames.h"
       
    39 #include "InspectorController.h"
       
    40 #include "MessagePortChannel.h"
       
    41 #include "PlatformMessagePortChannel.h"
       
    42 #include "ScriptExecutionContext.h"
       
    43 #include "SharedWorker.h"
       
    44 #include "WebFrameClient.h"
       
    45 #include "WebFrameImpl.h"
       
    46 #include "WebKit.h"
       
    47 #include "WebKitClient.h"
       
    48 #include "WebMessagePortChannel.h"
       
    49 #include "WebSharedWorker.h"
       
    50 #include "WebSharedWorkerRepository.h"
       
    51 #include "WebString.h"
       
    52 #include "WebURL.h"
       
    53 #include "WorkerScriptLoader.h"
       
    54 #include "WorkerScriptLoaderClient.h"
       
    55 
       
    56 namespace WebCore {
       
    57 
       
    58 class Document;
       
    59 using WebKit::WebFrameImpl;
       
    60 using WebKit::WebMessagePortChannel;
       
    61 using WebKit::WebSharedWorker;
       
    62 using WebKit::WebSharedWorkerRepository;
       
    63 
       
    64 // Callback class that keeps the SharedWorker and WebSharedWorker objects alive while loads are potentially happening, and also translates load errors into error events on the worker.
       
    65 class SharedWorkerScriptLoader : private WorkerScriptLoaderClient, private WebSharedWorker::ConnectListener {
       
    66 public:
       
    67     SharedWorkerScriptLoader(PassRefPtr<SharedWorker> worker, const KURL& url, const String& name, PassOwnPtr<MessagePortChannel> port, PassOwnPtr<WebSharedWorker> webWorker)
       
    68         : m_worker(worker)
       
    69         , m_url(url)
       
    70         , m_name(name)
       
    71         , m_webWorker(webWorker)
       
    72         , m_port(port)
       
    73         , m_scriptLoader(ResourceRequestBase::TargetIsSharedWorker)
       
    74         , m_loading(false)
       
    75         , m_responseAppCacheID(0)
       
    76     {
       
    77     }
       
    78 
       
    79     ~SharedWorkerScriptLoader();
       
    80     void load();
       
    81     static void stopAllLoadersForContext(ScriptExecutionContext*);
       
    82 
       
    83 private:
       
    84     // WorkerScriptLoaderClient callback
       
    85     virtual void didReceiveResponse(const ResourceResponse&);
       
    86     virtual void notifyFinished();
       
    87 
       
    88     virtual void connected();
       
    89 
       
    90     const ScriptExecutionContext* loadingContext() { return m_worker->scriptExecutionContext(); }
       
    91 
       
    92     void sendConnect();
       
    93 
       
    94     RefPtr<SharedWorker> m_worker;
       
    95     KURL m_url;
       
    96     String m_name;
       
    97     OwnPtr<WebSharedWorker> m_webWorker;
       
    98     OwnPtr<MessagePortChannel> m_port;
       
    99     WorkerScriptLoader m_scriptLoader;
       
   100     bool m_loading;
       
   101     long long m_responseAppCacheID;
       
   102 };
       
   103 
       
   104 static Vector<SharedWorkerScriptLoader*>& pendingLoaders()
       
   105 {
       
   106     AtomicallyInitializedStatic(Vector<SharedWorkerScriptLoader*>&, loaders = *new Vector<SharedWorkerScriptLoader*>);
       
   107     return loaders;
       
   108 }
       
   109 
       
   110 void SharedWorkerScriptLoader::stopAllLoadersForContext(ScriptExecutionContext* context)
       
   111 {
       
   112     // Walk our list of pending loaders and shutdown any that belong to this context.
       
   113     Vector<SharedWorkerScriptLoader*>& loaders = pendingLoaders();
       
   114     for (unsigned i = 0; i < loaders.size(); ) {
       
   115         SharedWorkerScriptLoader* loader = loaders[i];
       
   116         if (context == loader->loadingContext()) {
       
   117             loaders.remove(i);
       
   118             delete loader;
       
   119         } else
       
   120             i++;
       
   121     }
       
   122 }
       
   123 
       
   124 SharedWorkerScriptLoader::~SharedWorkerScriptLoader()
       
   125 {
       
   126     if (m_loading)
       
   127         m_worker->unsetPendingActivity(m_worker.get());
       
   128 }
       
   129 
       
   130 void SharedWorkerScriptLoader::load()
       
   131 {
       
   132     ASSERT(!m_loading);
       
   133     // If the shared worker is not yet running, load the script resource for it, otherwise just send it a connect event.
       
   134     if (m_webWorker->isStarted())
       
   135         sendConnect();
       
   136     else {
       
   137         m_scriptLoader.loadAsynchronously(m_worker->scriptExecutionContext(), m_url, DenyCrossOriginRequests, this);
       
   138         // Keep the worker + JS wrapper alive until the resource load is complete in case we need to dispatch an error event.
       
   139         m_worker->setPendingActivity(m_worker.get());
       
   140         m_loading = true;
       
   141     }
       
   142 }
       
   143 
       
   144 // Extracts a WebMessagePortChannel from a MessagePortChannel.
       
   145 static WebMessagePortChannel* getWebPort(PassOwnPtr<MessagePortChannel> port)
       
   146 {
       
   147     // Extract the WebMessagePortChannel to send to the worker.
       
   148     PlatformMessagePortChannel* platformChannel = port->channel();
       
   149     WebMessagePortChannel* webPort = platformChannel->webChannelRelease();
       
   150     webPort->setClient(0);
       
   151     return webPort;
       
   152 }
       
   153 
       
   154 void SharedWorkerScriptLoader::didReceiveResponse(const ResourceResponse& response)
       
   155 {
       
   156     m_responseAppCacheID = response.appCacheID();
       
   157 }
       
   158 
       
   159 void SharedWorkerScriptLoader::notifyFinished()
       
   160 {
       
   161     if (m_scriptLoader.failed()) {
       
   162         m_worker->dispatchEvent(Event::create(eventNames().errorEvent, false, true));
       
   163         delete this;
       
   164     } else {
       
   165 #if ENABLE(INSPECTOR)
       
   166         if (InspectorController* inspector = m_worker->scriptExecutionContext()->inspectorController())
       
   167             inspector->scriptImported(m_scriptLoader.identifier(), m_scriptLoader.script());
       
   168 #endif
       
   169         // Pass the script off to the worker, then send a connect event.
       
   170         m_webWorker->startWorkerContext(m_url, m_name, m_worker->scriptExecutionContext()->userAgent(m_url), m_scriptLoader.script(), m_responseAppCacheID);
       
   171         sendConnect();
       
   172     }
       
   173 }
       
   174 
       
   175 void SharedWorkerScriptLoader::sendConnect()
       
   176 {
       
   177     // Send the connect event off, and linger until it is done sending.
       
   178     m_webWorker->connect(getWebPort(m_port.release()), this);
       
   179 }
       
   180 
       
   181 void SharedWorkerScriptLoader::connected()
       
   182 {
       
   183     // Connect event has been sent, so free ourselves (this releases the SharedWorker so it can be freed as well if unreferenced).
       
   184     delete this;
       
   185 }
       
   186 
       
   187 bool SharedWorkerRepository::isAvailable()
       
   188 {
       
   189     // Allow the WebKitClient to determine if SharedWorkers are available.
       
   190     return WebKit::webKitClient()->sharedWorkerRepository();
       
   191 }
       
   192 
       
   193 static WebSharedWorkerRepository::DocumentID getId(void* document)
       
   194 {
       
   195     ASSERT(document);
       
   196     return reinterpret_cast<WebSharedWorkerRepository::DocumentID>(document);
       
   197 }
       
   198 
       
   199 void SharedWorkerRepository::connect(PassRefPtr<SharedWorker> worker, PassOwnPtr<MessagePortChannel> port, const KURL& url, const String& name, ExceptionCode& ec)
       
   200 {
       
   201     // This should not be callable unless there's a SharedWorkerRepository for
       
   202     // this context (since isAvailable() should have returned null).
       
   203     ASSERT(WebKit::webKitClient()->sharedWorkerRepository());
       
   204 
       
   205     // No nested workers (for now) - connect() should only be called from document context.
       
   206     ASSERT(worker->scriptExecutionContext()->isDocument());
       
   207     Document* document = static_cast<Document*>(worker->scriptExecutionContext());
       
   208     WebFrameImpl* webFrame = WebFrameImpl::fromFrame(document->frame());
       
   209     OwnPtr<WebSharedWorker> webWorker;
       
   210     webWorker = webFrame->client()->createSharedWorker(webFrame, url, name, getId(document));
       
   211 
       
   212     if (!webWorker) {
       
   213         // Existing worker does not match this url, so return an error back to the caller.
       
   214         ec = URL_MISMATCH_ERR;
       
   215         return;
       
   216     }
       
   217 
       
   218     WebKit::webKitClient()->sharedWorkerRepository()->addSharedWorker(
       
   219         webWorker.get(), getId(document));
       
   220 
       
   221     // The loader object manages its own lifecycle (and the lifecycles of the two worker objects).
       
   222     // It will free itself once loading is completed.
       
   223     SharedWorkerScriptLoader* loader = new SharedWorkerScriptLoader(worker, url, name, port, webWorker.release());
       
   224     loader->load();
       
   225 }
       
   226 
       
   227 void SharedWorkerRepository::documentDetached(Document* document)
       
   228 {
       
   229     WebSharedWorkerRepository* repo = WebKit::webKitClient()->sharedWorkerRepository();
       
   230     if (repo)
       
   231         repo->documentDetached(getId(document));
       
   232 
       
   233     // Stop the creation of any pending SharedWorkers for this context.
       
   234     // FIXME: Need a way to invoke this for WorkerContexts as well when we support for nested workers.
       
   235     SharedWorkerScriptLoader::stopAllLoadersForContext(document);
       
   236 }
       
   237 
       
   238 bool SharedWorkerRepository::hasSharedWorkers(Document* document)
       
   239 {
       
   240     WebSharedWorkerRepository* repo = WebKit::webKitClient()->sharedWorkerRepository();
       
   241     return repo && repo->hasSharedWorkers(getId(document));
       
   242 }
       
   243 
       
   244 
       
   245 
       
   246 } // namespace WebCore
       
   247 
       
   248 #endif // ENABLE(SHARED_WORKERS)