| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "web/WebSharedWorkerImpl.h" |
| |
| #include "core/dom/Document.h" |
| #include "core/dom/ExecutionContextTask.h" |
| #include "core/events/MessageEvent.h" |
| #include "core/html/HTMLFormElement.h" |
| #include "core/inspector/ConsoleMessage.h" |
| #include "core/inspector/InspectorInstrumentation.h" |
| #include "core/loader/FrameLoadRequest.h" |
| #include "core/loader/FrameLoader.h" |
| #include "core/page/Page.h" |
| #include "core/workers/ParentFrameTaskRunners.h" |
| #include "core/workers/SharedWorkerGlobalScope.h" |
| #include "core/workers/SharedWorkerThread.h" |
| #include "core/workers/WorkerClients.h" |
| #include "core/workers/WorkerGlobalScope.h" |
| #include "core/workers/WorkerInspectorProxy.h" |
| #include "core/workers/WorkerLoaderProxy.h" |
| #include "core/workers/WorkerScriptLoader.h" |
| #include "core/workers/WorkerThreadStartupData.h" |
| #include "platform/CrossThreadFunctional.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/heap/Handle.h" |
| #include "platform/network/ContentSecurityPolicyParsers.h" |
| #include "platform/network/ResourceResponse.h" |
| #include "platform/weborigin/KURL.h" |
| #include "platform/weborigin/SecurityOrigin.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebFileError.h" |
| #include "public/platform/WebMessagePortChannel.h" |
| #include "public/platform/WebString.h" |
| #include "public/platform/WebURL.h" |
| #include "public/platform/WebURLRequest.h" |
| #include "public/web/WebDevToolsAgent.h" |
| #include "public/web/WebFrame.h" |
| #include "public/web/WebSettings.h" |
| #include "public/web/WebView.h" |
| #include "public/web/WebWorkerContentSettingsClientProxy.h" |
| #include "public/web/modules/serviceworker/WebServiceWorkerNetworkProvider.h" |
| #include "web/IndexedDBClientImpl.h" |
| #include "web/LocalFileSystemClient.h" |
| #include "web/WebDataSourceImpl.h" |
| #include "web/WebLocalFrameImpl.h" |
| #include "web/WorkerContentSettingsClient.h" |
| #include "wtf/Functional.h" |
| #include "wtf/PtrUtil.h" |
| #include <memory> |
| |
| namespace blink { |
| |
| // TODO(toyoshim): Share implementation with WebEmbeddedWorkerImpl as much as |
| // possible. |
| |
| WebSharedWorkerImpl::WebSharedWorkerImpl(WebSharedWorkerClient* client) |
| : m_webView(nullptr), |
| m_mainFrame(nullptr), |
| m_askedToTerminate(false), |
| m_workerInspectorProxy(WorkerInspectorProxy::create()), |
| m_client(client), |
| m_pauseWorkerContextOnStart(false), |
| m_isPausedOnStart(false), |
| m_creationAddressSpace(WebAddressSpacePublic) {} |
| |
| WebSharedWorkerImpl::~WebSharedWorkerImpl() { |
| DCHECK(m_webView); |
| // Detach the client before closing the view to avoid getting called back. |
| m_mainFrame->setClient(0); |
| |
| m_webView->close(); |
| m_mainFrame->close(); |
| if (m_loaderProxy) |
| m_loaderProxy->detachProvider(this); |
| } |
| |
| void WebSharedWorkerImpl::terminateWorkerThread() { |
| if (m_askedToTerminate) |
| return; |
| m_askedToTerminate = true; |
| if (m_mainScriptLoader) { |
| m_mainScriptLoader->cancel(); |
| m_mainScriptLoader.clear(); |
| m_client->workerScriptLoadFailed(); |
| delete this; |
| return; |
| } |
| if (m_workerThread) |
| m_workerThread->terminate(); |
| m_workerInspectorProxy->workerThreadTerminated(); |
| } |
| |
| void WebSharedWorkerImpl::initializeLoader() { |
| // Create 'shadow page'. This page is never displayed, it is used to proxy the |
| // loading requests from the worker context to the rest of WebKit and Chromium |
| // infrastructure. |
| DCHECK(!m_webView); |
| m_webView = WebView::create(nullptr, WebPageVisibilityStateVisible); |
| // FIXME: http://crbug.com/363843. This needs to find a better way to |
| // not create graphics layers. |
| m_webView->settings()->setAcceleratedCompositingEnabled(false); |
| // FIXME: Settings information should be passed to the Worker process from |
| // Browser process when the worker is created (similar to |
| // RenderThread::OnCreateNewView). |
| m_mainFrame = toWebLocalFrameImpl( |
| WebLocalFrame::create(WebTreeScopeType::Document, this)); |
| m_webView->setMainFrame(m_mainFrame.get()); |
| m_mainFrame->setDevToolsAgentClient(this); |
| |
| // If we were asked to pause worker context on start and wait for debugger |
| // then it is the good time to do that. |
| m_client->workerReadyForInspection(); |
| if (m_pauseWorkerContextOnStart) { |
| m_isPausedOnStart = true; |
| return; |
| } |
| loadShadowPage(); |
| } |
| |
| WebApplicationCacheHost* WebSharedWorkerImpl::createApplicationCacheHost( |
| WebApplicationCacheHostClient* appcacheHostClient) { |
| return m_client->createApplicationCacheHost(appcacheHostClient); |
| } |
| |
| void WebSharedWorkerImpl::loadShadowPage() { |
| // Construct substitute data source for the 'shadow page'. We only need it |
| // to have same origin as the worker so the loading checks work correctly. |
| CString content(""); |
| RefPtr<SharedBuffer> buffer( |
| SharedBuffer::create(content.data(), content.length())); |
| m_mainFrame->frame()->loader().load( |
| FrameLoadRequest(0, ResourceRequest(m_url), |
| SubstituteData(buffer, "text/html", "UTF-8", KURL()))); |
| } |
| |
| void WebSharedWorkerImpl::willSendRequest(WebLocalFrame* frame, |
| WebURLRequest& request) { |
| if (m_networkProvider) |
| m_networkProvider->willSendRequest(frame->dataSource(), request); |
| } |
| |
| void WebSharedWorkerImpl::didFinishDocumentLoad(WebLocalFrame* frame) { |
| DCHECK(!m_loadingDocument); |
| DCHECK(!m_mainScriptLoader); |
| m_networkProvider = wrapUnique( |
| m_client->createServiceWorkerNetworkProvider(frame->dataSource())); |
| m_mainScriptLoader = WorkerScriptLoader::create(); |
| m_mainScriptLoader->setRequestContext( |
| WebURLRequest::RequestContextSharedWorker); |
| m_loadingDocument = toWebLocalFrameImpl(frame)->frame()->document(); |
| m_mainScriptLoader->loadAsynchronously( |
| *m_loadingDocument.get(), m_url, DenyCrossOriginRequests, |
| m_creationAddressSpace, |
| bind(&WebSharedWorkerImpl::didReceiveScriptLoaderResponse, |
| WTF::unretained(this)), |
| bind(&WebSharedWorkerImpl::onScriptLoaderFinished, |
| WTF::unretained(this))); |
| // Do nothing here since onScriptLoaderFinished() might have been already |
| // invoked and |this| might have been deleted at this point. |
| } |
| |
| bool WebSharedWorkerImpl::isControlledByServiceWorker( |
| WebDataSource& dataSource) { |
| return m_networkProvider && |
| m_networkProvider->isControlledByServiceWorker(dataSource); |
| } |
| |
| int64_t WebSharedWorkerImpl::serviceWorkerID(WebDataSource& dataSource) { |
| if (!m_networkProvider) |
| return -1; |
| return m_networkProvider->serviceWorkerID(dataSource); |
| } |
| |
| InterfaceProvider* WebSharedWorkerImpl::interfaceProvider() { |
| return Platform::current()->interfaceProvider(); |
| } |
| |
| void WebSharedWorkerImpl::sendProtocolMessage(int sessionId, |
| int callId, |
| const WebString& message, |
| const WebString& state) { |
| m_client->sendDevToolsMessage(sessionId, callId, message, state); |
| } |
| |
| void WebSharedWorkerImpl::resumeStartup() { |
| bool isPausedOnStart = m_isPausedOnStart; |
| m_isPausedOnStart = false; |
| if (isPausedOnStart) |
| loadShadowPage(); |
| } |
| |
| WebDevToolsAgentClient::WebKitClientMessageLoop* |
| WebSharedWorkerImpl::createClientMessageLoop() { |
| return m_client->createDevToolsMessageLoop(); |
| } |
| |
| // WorkerReportingProxy -------------------------------------------------------- |
| |
| void WebSharedWorkerImpl::reportException(const String& errorMessage, |
| std::unique_ptr<SourceLocation>, |
| int exceptionId) { |
| // Not suppported in SharedWorker. |
| } |
| |
| void WebSharedWorkerImpl::reportConsoleMessage(MessageSource, |
| MessageLevel, |
| const String& message, |
| SourceLocation*) { |
| // Not supported in SharedWorker. |
| } |
| |
| void WebSharedWorkerImpl::postMessageToPageInspector(const String& message) { |
| m_mainFrame->frame()->document()->postInspectorTask( |
| BLINK_FROM_HERE, |
| createCrossThreadTask( |
| &WebSharedWorkerImpl::postMessageToPageInspectorOnMainThread, |
| crossThreadUnretained(this), message)); |
| } |
| |
| void WebSharedWorkerImpl::postMessageToPageInspectorOnMainThread( |
| const String& message) { |
| m_workerInspectorProxy->dispatchMessageFromWorker(message); |
| } |
| |
| void WebSharedWorkerImpl::didCloseWorkerGlobalScope() { |
| Platform::current()->mainThread()->getWebTaskRunner()->postTask( |
| BLINK_FROM_HERE, |
| crossThreadBind( |
| &WebSharedWorkerImpl::didCloseWorkerGlobalScopeOnMainThread, |
| crossThreadUnretained(this))); |
| } |
| |
| void WebSharedWorkerImpl::didCloseWorkerGlobalScopeOnMainThread() { |
| m_client->workerContextClosed(); |
| |
| terminateWorkerThread(); |
| } |
| |
| void WebSharedWorkerImpl::didTerminateWorkerThread() { |
| Platform::current()->mainThread()->getWebTaskRunner()->postTask( |
| BLINK_FROM_HERE, |
| crossThreadBind( |
| &WebSharedWorkerImpl::didTerminateWorkerThreadOnMainThread, |
| crossThreadUnretained(this))); |
| } |
| |
| void WebSharedWorkerImpl::didTerminateWorkerThreadOnMainThread() { |
| m_client->workerContextDestroyed(); |
| // The lifetime of this proxy is controlled by the worker context. |
| delete this; |
| } |
| |
| // WorkerLoaderProxyProvider ------------------------------------------------- |
| |
| void WebSharedWorkerImpl::postTaskToLoader( |
| const WebTraceLocation& location, |
| std::unique_ptr<ExecutionContextTask> task) { |
| // TODO(hiroshige,yuryu): Make this not use ExecutionContextTask and |
| // consider using m_mainThreadTaskRunners->get(TaskType::Networking) |
| // instead. |
| m_mainFrame->frame()->document()->postTask(location, std::move(task)); |
| } |
| |
| void WebSharedWorkerImpl::postTaskToWorkerGlobalScope( |
| const WebTraceLocation& location, |
| std::unique_ptr<ExecutionContextTask> task) { |
| m_workerThread->postTask(location, std::move(task)); |
| } |
| |
| void WebSharedWorkerImpl::connect(WebMessagePortChannel* webChannel) { |
| workerThread()->postTask( |
| BLINK_FROM_HERE, |
| createCrossThreadTask( |
| &connectTask, passed(WebMessagePortChannelUniquePtr(webChannel)))); |
| } |
| |
| void WebSharedWorkerImpl::connectTask(WebMessagePortChannelUniquePtr channel, |
| ExecutionContext* context) { |
| // Wrap the passed-in channel in a MessagePort, and send it off via a connect |
| // event. |
| MessagePort* port = MessagePort::create(*context); |
| port->entangle(std::move(channel)); |
| WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); |
| ASSERT_WITH_SECURITY_IMPLICATION( |
| workerGlobalScope->isSharedWorkerGlobalScope()); |
| workerGlobalScope->dispatchEvent(createConnectEvent(port)); |
| } |
| |
| void WebSharedWorkerImpl::startWorkerContext( |
| const WebURL& url, |
| const WebString& name, |
| const WebString& contentSecurityPolicy, |
| WebContentSecurityPolicyType policyType, |
| WebAddressSpace creationAddressSpace) { |
| m_url = url; |
| m_name = name; |
| m_creationAddressSpace = creationAddressSpace; |
| initializeLoader(); |
| } |
| |
| void WebSharedWorkerImpl::didReceiveScriptLoaderResponse() { |
| InspectorInstrumentation::didReceiveScriptResponse( |
| m_loadingDocument.get(), m_mainScriptLoader->identifier()); |
| m_client->selectAppCacheID(m_mainScriptLoader->appCacheID()); |
| } |
| |
| void WebSharedWorkerImpl::onScriptLoaderFinished() { |
| DCHECK(m_loadingDocument); |
| DCHECK(m_mainScriptLoader); |
| if (m_askedToTerminate) |
| return; |
| if (m_mainScriptLoader->failed()) { |
| m_mainScriptLoader->cancel(); |
| m_client->workerScriptLoadFailed(); |
| |
| // The SharedWorker was unable to load the initial script, so |
| // shut it down right here. |
| delete this; |
| return; |
| } |
| |
| Document* document = m_mainFrame->frame()->document(); |
| // FIXME: this document's origin is pristine and without any extra privileges. |
| // (crbug.com/254993) |
| SecurityOrigin* starterOrigin = document->getSecurityOrigin(); |
| |
| WorkerClients* workerClients = WorkerClients::create(); |
| provideLocalFileSystemToWorker(workerClients, |
| LocalFileSystemClient::create()); |
| WebSecurityOrigin webSecurityOrigin(m_loadingDocument->getSecurityOrigin()); |
| provideContentSettingsClientToWorker( |
| workerClients, |
| wrapUnique( |
| m_client->createWorkerContentSettingsClientProxy(webSecurityOrigin))); |
| provideIndexedDBClientToWorker(workerClients, IndexedDBClientImpl::create()); |
| ContentSecurityPolicy* contentSecurityPolicy = |
| m_mainScriptLoader->releaseContentSecurityPolicy(); |
| WorkerThreadStartMode startMode = |
| m_workerInspectorProxy->workerStartMode(document); |
| std::unique_ptr<WorkerSettings> workerSettings = |
| wrapUnique(new WorkerSettings(document->settings())); |
| std::unique_ptr<WorkerThreadStartupData> startupData = |
| WorkerThreadStartupData::create( |
| m_url, m_loadingDocument->userAgent(), m_mainScriptLoader->script(), |
| nullptr, startMode, |
| contentSecurityPolicy ? contentSecurityPolicy->headers().get() |
| : nullptr, |
| m_mainScriptLoader->referrerPolicy(), starterOrigin, workerClients, |
| m_mainScriptLoader->responseAddressSpace(), |
| m_mainScriptLoader->originTrialTokens(), std::move(workerSettings)); |
| |
| // We have a dummy document here for loading but it doesn't really represent |
| // the document/frame of associated document(s) for this worker. Here we |
| // populate the task runners with null document not to confuse the frame |
| // scheduler (which will end up using the thread's default task runner). |
| m_mainThreadTaskRunners = ParentFrameTaskRunners::create(nullptr); |
| |
| m_loaderProxy = WorkerLoaderProxy::create(this); |
| m_workerThread = SharedWorkerThread::create(m_name, m_loaderProxy, *this); |
| InspectorInstrumentation::scriptImported(m_loadingDocument.get(), |
| m_mainScriptLoader->identifier(), |
| m_mainScriptLoader->script()); |
| m_mainScriptLoader.clear(); |
| |
| workerThread()->start(std::move(startupData)); |
| m_workerInspectorProxy->workerThreadCreated( |
| toDocument(m_loadingDocument.get()), workerThread(), m_url); |
| m_client->workerScriptLoaded(); |
| } |
| |
| void WebSharedWorkerImpl::terminateWorkerContext() { |
| terminateWorkerThread(); |
| } |
| |
| void WebSharedWorkerImpl::pauseWorkerContextOnStart() { |
| m_pauseWorkerContextOnStart = true; |
| } |
| |
| void WebSharedWorkerImpl::attachDevTools(const WebString& hostId, |
| int sessionId) { |
| WebDevToolsAgent* devtoolsAgent = m_mainFrame->devToolsAgent(); |
| if (devtoolsAgent) |
| devtoolsAgent->attach(hostId, sessionId); |
| } |
| |
| void WebSharedWorkerImpl::reattachDevTools(const WebString& hostId, |
| int sessionId, |
| const WebString& savedState) { |
| WebDevToolsAgent* devtoolsAgent = m_mainFrame->devToolsAgent(); |
| if (devtoolsAgent) |
| devtoolsAgent->reattach(hostId, sessionId, savedState); |
| resumeStartup(); |
| } |
| |
| void WebSharedWorkerImpl::detachDevTools() { |
| WebDevToolsAgent* devtoolsAgent = m_mainFrame->devToolsAgent(); |
| if (devtoolsAgent) |
| devtoolsAgent->detach(); |
| } |
| |
| void WebSharedWorkerImpl::dispatchDevToolsMessage(int sessionId, |
| int callId, |
| const WebString& method, |
| const WebString& message) { |
| if (m_askedToTerminate) |
| return; |
| WebDevToolsAgent* devtoolsAgent = m_mainFrame->devToolsAgent(); |
| if (devtoolsAgent) |
| devtoolsAgent->dispatchOnInspectorBackend(sessionId, callId, method, |
| message); |
| } |
| |
| WebSharedWorker* WebSharedWorker::create(WebSharedWorkerClient* client) { |
| return new WebSharedWorkerImpl(client); |
| } |
| |
| } // namespace blink |