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 "web/WebSharedWorkerImpl.h"
     33 
     34 #include "core/dom/CrossThreadTask.h"
     35 #include "core/dom/Document.h"
     36 #include "core/events/MessageEvent.h"
     37 #include "core/html/HTMLFormElement.h"
     38 #include "core/inspector/ConsoleMessage.h"
     39 #include "core/inspector/InspectorInstrumentation.h"
     40 #include "core/inspector/WorkerDebuggerAgent.h"
     41 #include "core/inspector/WorkerInspectorController.h"
     42 #include "core/loader/FrameLoadRequest.h"
     43 #include "core/loader/FrameLoader.h"
     44 #include "core/page/Page.h"
     45 #include "core/workers/SharedWorkerGlobalScope.h"
     46 #include "core/workers/SharedWorkerThread.h"
     47 #include "core/workers/WorkerClients.h"
     48 #include "core/workers/WorkerGlobalScope.h"
     49 #include "core/workers/WorkerInspectorProxy.h"
     50 #include "core/workers/WorkerScriptLoader.h"
     51 #include "core/workers/WorkerThreadStartupData.h"
     52 #include "platform/RuntimeEnabledFeatures.h"
     53 #include "platform/heap/Handle.h"
     54 #include "platform/network/ContentSecurityPolicyParsers.h"
     55 #include "platform/network/ResourceResponse.h"
     56 #include "platform/weborigin/KURL.h"
     57 #include "platform/weborigin/SecurityOrigin.h"
     58 #include "public/platform/WebFileError.h"
     59 #include "public/platform/WebMessagePortChannel.h"
     60 #include "public/platform/WebString.h"
     61 #include "public/platform/WebURL.h"
     62 #include "public/platform/WebURLRequest.h"
     63 #include "public/web/WebFrame.h"
     64 #include "public/web/WebSettings.h"
     65 #include "public/web/WebView.h"
     66 #include "public/web/WebWorkerPermissionClientProxy.h"
     67 #include "web/LocalFileSystemClient.h"
     68 #include "web/WebDataSourceImpl.h"
     69 #include "web/WebLocalFrameImpl.h"
     70 #include "web/WorkerPermissionClient.h"
     71 #include "wtf/Functional.h"
     72 #include "wtf/MainThread.h"
     73 
     74 namespace blink {
     75 
     76 // A thin wrapper for one-off script loading.
     77 class WebSharedWorkerImpl::Loader : public WorkerScriptLoaderClient {
     78 public:
     79     static PassOwnPtr<Loader> create()
     80     {
     81         return adoptPtr(new Loader());
     82     }
     83 
     84     virtual ~Loader()
     85     {
     86         m_scriptLoader->setClient(0);
     87     }
     88 
     89     void load(ExecutionContext* loadingContext, const KURL& scriptURL, const Closure& receiveResponseCallback, const Closure& finishCallback)
     90     {
     91         ASSERT(loadingContext);
     92         m_receiveResponseCallback = receiveResponseCallback;
     93         m_finishCallback = finishCallback;
     94         m_scriptLoader->setRequestContext(WebURLRequest::RequestContextSharedWorker);
     95         m_scriptLoader->loadAsynchronously(
     96             *loadingContext, scriptURL, DenyCrossOriginRequests, this);
     97     }
     98 
     99     void didReceiveResponse(unsigned long identifier, const ResourceResponse& response) OVERRIDE
    100     {
    101         m_identifier = identifier;
    102         m_appCacheID = response.appCacheID();
    103         m_receiveResponseCallback();
    104     }
    105 
    106     virtual void notifyFinished() OVERRIDE
    107     {
    108         m_finishCallback();
    109     }
    110 
    111     void cancel()
    112     {
    113         m_scriptLoader->cancel();
    114     }
    115 
    116     bool failed() const { return m_scriptLoader->failed(); }
    117     const KURL& url() const { return m_scriptLoader->responseURL(); }
    118     String script() const { return m_scriptLoader->script(); }
    119     unsigned long identifier() const { return m_identifier; }
    120     long long appCacheID() const { return m_appCacheID; }
    121 
    122 private:
    123     Loader() : m_scriptLoader(WorkerScriptLoader::create()), m_identifier(0), m_appCacheID(0)
    124     {
    125     }
    126 
    127     RefPtr<WorkerScriptLoader> m_scriptLoader;
    128     unsigned long m_identifier;
    129     long long m_appCacheID;
    130     Closure m_receiveResponseCallback;
    131     Closure m_finishCallback;
    132 };
    133 
    134 // This function is called on the main thread to force to initialize some static
    135 // values used in WebKit before any worker thread is started. This is because in
    136 // our worker processs, we do not run any WebKit code in main thread and thus
    137 // when multiple workers try to start at the same time, we might hit crash due
    138 // to contention for initializing static values.
    139 static void initializeWebKitStaticValues()
    140 {
    141     static bool initialized = false;
    142     if (!initialized) {
    143         initialized = true;
    144         // Note that we have to pass a URL with valid protocol in order to follow
    145         // the path to do static value initializations.
    146         RefPtr<SecurityOrigin> origin =
    147             SecurityOrigin::create(KURL(ParsedURLString, "http://localhost"));
    148         origin.release();
    149     }
    150 }
    151 
    152 WebSharedWorkerImpl::WebSharedWorkerImpl(WebSharedWorkerClient* client)
    153     : m_webView(0)
    154     , m_mainFrame(0)
    155     , m_askedToTerminate(false)
    156     , m_workerInspectorProxy(WorkerInspectorProxy::create())
    157     , m_client(WeakReference<WebSharedWorkerClient>::create(client))
    158     , m_clientWeakPtr(WeakPtr<WebSharedWorkerClient>(m_client))
    159     , m_pauseWorkerContextOnStart(false)
    160     , m_attachDevToolsOnStart(false)
    161 {
    162     initializeWebKitStaticValues();
    163 }
    164 
    165 WebSharedWorkerImpl::~WebSharedWorkerImpl()
    166 {
    167     ASSERT(m_webView);
    168     // Detach the client before closing the view to avoid getting called back.
    169     toWebLocalFrameImpl(m_mainFrame)->setClient(0);
    170 
    171     m_webView->close();
    172     m_mainFrame->close();
    173 }
    174 
    175 void WebSharedWorkerImpl::stopWorkerThread()
    176 {
    177     if (m_askedToTerminate)
    178         return;
    179     m_askedToTerminate = true;
    180     if (m_mainScriptLoader) {
    181         m_mainScriptLoader->cancel();
    182         m_mainScriptLoader.clear();
    183         if (client())
    184             client()->workerScriptLoadFailed();
    185         delete this;
    186         return;
    187     }
    188     if (m_workerThread)
    189         m_workerThread->stop();
    190     m_workerInspectorProxy->workerThreadTerminated();
    191 }
    192 
    193 void WebSharedWorkerImpl::initializeLoader(const WebURL& url)
    194 {
    195     // Create 'shadow page'. This page is never displayed, it is used to proxy the
    196     // loading requests from the worker context to the rest of WebKit and Chromium
    197     // infrastructure.
    198     ASSERT(!m_webView);
    199     m_webView = WebView::create(0);
    200     m_webView->settings()->setOfflineWebApplicationCacheEnabled(RuntimeEnabledFeatures::applicationCacheEnabled());
    201     // FIXME: http://crbug.com/363843. This needs to find a better way to
    202     // not create graphics layers.
    203     m_webView->settings()->setAcceleratedCompositingEnabled(false);
    204     // FIXME: Settings information should be passed to the Worker process from Browser process when the worker
    205     // is created (similar to RenderThread::OnCreateNewView).
    206     m_mainFrame = WebLocalFrame::create(this);
    207     m_webView->setMainFrame(m_mainFrame);
    208 
    209     WebLocalFrameImpl* webFrame = toWebLocalFrameImpl(m_webView->mainFrame());
    210 
    211     // Construct substitute data source for the 'shadow page'. We only need it
    212     // to have same origin as the worker so the loading checks work correctly.
    213     CString content("");
    214     int length = static_cast<int>(content.length());
    215     RefPtr<SharedBuffer> buffer(SharedBuffer::create(content.data(), length));
    216     webFrame->frame()->loader().load(FrameLoadRequest(0, ResourceRequest(url), SubstituteData(buffer, "text/html", "UTF-8", KURL())));
    217 }
    218 
    219 WebApplicationCacheHost* WebSharedWorkerImpl::createApplicationCacheHost(WebLocalFrame*, WebApplicationCacheHostClient* appcacheHostClient)
    220 {
    221     if (client())
    222         return client()->createApplicationCacheHost(appcacheHostClient);
    223     return 0;
    224 }
    225 
    226 void WebSharedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame)
    227 {
    228     ASSERT(!m_loadingDocument);
    229     ASSERT(!m_mainScriptLoader);
    230     m_mainScriptLoader = Loader::create();
    231     m_loadingDocument = toWebLocalFrameImpl(frame)->frame()->document();
    232     m_mainScriptLoader->load(
    233         m_loadingDocument.get(),
    234         m_url,
    235         bind(&WebSharedWorkerImpl::didReceiveScriptLoaderResponse, this),
    236         bind(&WebSharedWorkerImpl::onScriptLoaderFinished, this));
    237 }
    238 
    239 // WorkerReportingProxy --------------------------------------------------------
    240 
    241 void WebSharedWorkerImpl::reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL)
    242 {
    243     // Not suppported in SharedWorker.
    244 }
    245 
    246 void WebSharedWorkerImpl::reportConsoleMessage(PassRefPtrWillBeRawPtr<ConsoleMessage>)
    247 {
    248     // Not supported in SharedWorker.
    249 }
    250 
    251 void WebSharedWorkerImpl::postMessageToPageInspector(const String& message)
    252 {
    253     // Note that we need to keep the closure creation on a separate line so
    254     // that the temporary created by isolatedCopy() will always be destroyed
    255     // before the copy in the closure is used on the main thread.
    256     const Closure& boundFunction = bind(&WebSharedWorkerClient::dispatchDevToolsMessage, m_clientWeakPtr, message.isolatedCopy());
    257     callOnMainThread(boundFunction);
    258     toWebLocalFrameImpl(m_mainFrame)->frame()->document()->postInspectorTask(createCrossThreadTask(&WebSharedWorkerImpl::postMessageToPageInspectorOnMainThread, this, message));
    259 }
    260 
    261 void WebSharedWorkerImpl::postMessageToPageInspectorOnMainThread(const String& message)
    262 {
    263     WorkerInspectorProxy::PageInspector* pageInspector = m_workerInspectorProxy->pageInspector();
    264     if (!pageInspector)
    265         return;
    266     pageInspector->dispatchMessageFromWorker(message);
    267 
    268 }
    269 
    270 void WebSharedWorkerImpl::updateInspectorStateCookie(const String& cookie)
    271 {
    272     // Note that we need to keep the closure creation on a separate line so
    273     // that the temporary created by isolatedCopy() will always be destroyed
    274     // before the copy in the closure is used on the main thread.
    275     const Closure& boundFunction = bind(&WebSharedWorkerClient::saveDevToolsAgentState, m_clientWeakPtr, cookie.isolatedCopy());
    276     callOnMainThread(boundFunction);
    277 }
    278 
    279 void WebSharedWorkerImpl::workerGlobalScopeClosed()
    280 {
    281     callOnMainThread(bind(&WebSharedWorkerImpl::workerGlobalScopeClosedOnMainThread, this));
    282 }
    283 
    284 void WebSharedWorkerImpl::workerGlobalScopeClosedOnMainThread()
    285 {
    286     if (client())
    287         client()->workerContextClosed();
    288 
    289     stopWorkerThread();
    290 }
    291 
    292 void WebSharedWorkerImpl::workerGlobalScopeStarted(WorkerGlobalScope*)
    293 {
    294 }
    295 
    296 void WebSharedWorkerImpl::workerThreadTerminated()
    297 {
    298     callOnMainThread(bind(&WebSharedWorkerImpl::workerThreadTerminatedOnMainThread, this));
    299 }
    300 
    301 void WebSharedWorkerImpl::workerThreadTerminatedOnMainThread()
    302 {
    303     if (client())
    304         client()->workerContextDestroyed();
    305     // The lifetime of this proxy is controlled by the worker context.
    306     delete this;
    307 }
    308 
    309 // WorkerLoaderProxy -----------------------------------------------------------
    310 
    311 void WebSharedWorkerImpl::postTaskToLoader(PassOwnPtr<ExecutionContextTask> task)
    312 {
    313     toWebLocalFrameImpl(m_mainFrame)->frame()->document()->postTask(task);
    314 }
    315 
    316 bool WebSharedWorkerImpl::postTaskToWorkerGlobalScope(PassOwnPtr<ExecutionContextTask> task)
    317 {
    318     m_workerThread->postTask(task);
    319     return true;
    320 }
    321 
    322 void WebSharedWorkerImpl::connect(WebMessagePortChannel* webChannel)
    323 {
    324     workerThread()->postTask(
    325         createCrossThreadTask(&connectTask, adoptPtr(webChannel)));
    326 }
    327 
    328 void WebSharedWorkerImpl::connectTask(ExecutionContext* context, PassOwnPtr<WebMessagePortChannel> channel)
    329 {
    330     // Wrap the passed-in channel in a MessagePort, and send it off via a connect event.
    331     RefPtrWillBeRawPtr<MessagePort> port = MessagePort::create(*context);
    332     port->entangle(channel);
    333     WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
    334     ASSERT_WITH_SECURITY_IMPLICATION(workerGlobalScope->isSharedWorkerGlobalScope());
    335     workerGlobalScope->dispatchEvent(createConnectEvent(port.release()));
    336 }
    337 
    338 void WebSharedWorkerImpl::startWorkerContext(const WebURL& url, const WebString& name, const WebString& contentSecurityPolicy, WebContentSecurityPolicyType policyType)
    339 {
    340     m_url = url;
    341     m_name = name;
    342     m_contentSecurityPolicy = contentSecurityPolicy;
    343     m_policyType = policyType;
    344     initializeLoader(url);
    345 }
    346 
    347 void WebSharedWorkerImpl::didReceiveScriptLoaderResponse()
    348 {
    349     InspectorInstrumentation::didReceiveScriptResponse(m_loadingDocument.get(), m_mainScriptLoader->identifier());
    350     if (client())
    351         client()->selectAppCacheID(m_mainScriptLoader->appCacheID());
    352 }
    353 
    354 static void connectToWorkerContextInspectorTask(ExecutionContext* context, bool)
    355 {
    356     toWorkerGlobalScope(context)->workerInspectorController()->connectFrontend();
    357 }
    358 
    359 void WebSharedWorkerImpl::onScriptLoaderFinished()
    360 {
    361     ASSERT(m_loadingDocument);
    362     ASSERT(m_mainScriptLoader);
    363     if (m_askedToTerminate)
    364         return;
    365     if (m_mainScriptLoader->failed()) {
    366         m_mainScriptLoader->cancel();
    367         if (client())
    368             client()->workerScriptLoadFailed();
    369 
    370         // The SharedWorker was unable to load the initial script, so
    371         // shut it down right here.
    372         delete this;
    373         return;
    374     }
    375     WorkerThreadStartMode startMode = m_pauseWorkerContextOnStart ? PauseWorkerGlobalScopeOnStart : DontPauseWorkerGlobalScopeOnStart;
    376     OwnPtrWillBeRawPtr<WorkerClients> workerClients = WorkerClients::create();
    377     provideLocalFileSystemToWorker(workerClients.get(), LocalFileSystemClient::create());
    378     WebSecurityOrigin webSecurityOrigin(m_loadingDocument->securityOrigin());
    379     providePermissionClientToWorker(workerClients.get(), adoptPtr(client()->createWorkerPermissionClientProxy(webSecurityOrigin)));
    380     OwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData = WorkerThreadStartupData::create(m_url, m_loadingDocument->userAgent(m_url), m_mainScriptLoader->script(), startMode, m_contentSecurityPolicy, static_cast<ContentSecurityPolicyHeaderType>(m_policyType), workerClients.release());
    381     setWorkerThread(SharedWorkerThread::create(m_name, *this, *this, startupData.release()));
    382     InspectorInstrumentation::scriptImported(m_loadingDocument.get(), m_mainScriptLoader->identifier(), m_mainScriptLoader->script());
    383     m_mainScriptLoader.clear();
    384 
    385     if (m_attachDevToolsOnStart)
    386         workerThread()->postDebuggerTask(createCrossThreadTask(connectToWorkerContextInspectorTask, true));
    387 
    388     workerThread()->start();
    389     m_workerInspectorProxy->workerThreadCreated(m_loadingDocument.get(), workerThread(), m_url);
    390     if (client()) {
    391         client()->workerScriptLoaded();
    392         client()->workerReadyForInspection();
    393     }
    394 }
    395 
    396 void WebSharedWorkerImpl::terminateWorkerContext()
    397 {
    398     stopWorkerThread();
    399 }
    400 
    401 void WebSharedWorkerImpl::clientDestroyed()
    402 {
    403     m_client.clear();
    404 }
    405 
    406 void WebSharedWorkerImpl::pauseWorkerContextOnStart()
    407 {
    408     m_pauseWorkerContextOnStart = true;
    409 }
    410 
    411 static void resumeWorkerContextTask(ExecutionContext* context, bool)
    412 {
    413     toWorkerGlobalScope(context)->workerInspectorController()->resume();
    414 }
    415 
    416 void WebSharedWorkerImpl::resumeWorkerContext()
    417 {
    418     m_pauseWorkerContextOnStart = false;
    419     if (workerThread())
    420         workerThread()->postDebuggerTask(createCrossThreadTask(resumeWorkerContextTask, true));
    421 }
    422 
    423 void WebSharedWorkerImpl::attachDevTools()
    424 {
    425     if (workerThread())
    426         workerThread()->postDebuggerTask(createCrossThreadTask(connectToWorkerContextInspectorTask, true));
    427     else
    428         m_attachDevToolsOnStart = true;
    429 }
    430 
    431 void WebSharedWorkerImpl::attachDevTools(const WebString& hostId)
    432 {
    433     attachDevTools();
    434 }
    435 
    436 static void reconnectToWorkerContextInspectorTask(ExecutionContext* context, const String& savedState)
    437 {
    438     WorkerInspectorController* ic = toWorkerGlobalScope(context)->workerInspectorController();
    439     ic->restoreInspectorStateFromCookie(savedState);
    440     ic->resume();
    441 }
    442 
    443 void WebSharedWorkerImpl::reattachDevTools(const WebString& savedState)
    444 {
    445     workerThread()->postDebuggerTask(createCrossThreadTask(reconnectToWorkerContextInspectorTask, String(savedState)));
    446 }
    447 
    448 void WebSharedWorkerImpl::reattachDevTools(const WebString& hostId, const WebString& savedState)
    449 {
    450     reattachDevTools(savedState);
    451 }
    452 
    453 static void disconnectFromWorkerContextInspectorTask(ExecutionContext* context, bool)
    454 {
    455     toWorkerGlobalScope(context)->workerInspectorController()->disconnectFrontend();
    456 }
    457 
    458 void WebSharedWorkerImpl::detachDevTools()
    459 {
    460     m_attachDevToolsOnStart = false;
    461     workerThread()->postDebuggerTask(createCrossThreadTask(disconnectFromWorkerContextInspectorTask, true));
    462 }
    463 
    464 static void dispatchOnInspectorBackendTask(ExecutionContext* context, const String& message)
    465 {
    466     toWorkerGlobalScope(context)->workerInspectorController()->dispatchMessageFromFrontend(message);
    467 }
    468 
    469 void WebSharedWorkerImpl::dispatchDevToolsMessage(const WebString& message)
    470 {
    471     if (m_askedToTerminate)
    472         return;
    473     workerThread()->postDebuggerTask(createCrossThreadTask(dispatchOnInspectorBackendTask, String(message)));
    474     workerThread()->interruptAndDispatchInspectorCommands();
    475 }
    476 
    477 WebSharedWorker* WebSharedWorker::create(WebSharedWorkerClient* client)
    478 {
    479     return new WebSharedWorkerImpl(client);
    480 }
    481 
    482 } // namespace blink
    483