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