void WorkerThread::stop() { // Mutex protection is necessary because stop() can be called before the context is fully created. MutexLocker lock(m_threadCreationMutex); // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever. if (m_workerGlobalScope) { m_workerGlobalScope->script()->scheduleExecutionTermination(); #if ENABLE(SQL_DATABASE) DatabaseManager::manager().interruptAllDatabasesForContext(m_workerGlobalScope.get()); #endif m_runLoop.postTaskAndTerminate({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext* context ) { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); #if ENABLE(SQL_DATABASE) // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below? DatabaseTaskSynchronizer cleanupSync; DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync); #endif workerGlobalScope->stopActiveDOMObjects(); workerGlobalScope->notifyObserversOfStop(); // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, // which become dangling once Heap is destroyed. workerGlobalScope->removeAllEventListeners(); #if ENABLE(SQL_DATABASE) // We wait for the database thread to clean up all its stuff so that we // can do more stringent leak checks as we exit. cleanupSync.waitForTaskCompletion(); #endif // Stick a shutdown command at the end of the queue, so that we deal // with all the cleanup tasks the databases post first. workerGlobalScope->postTask({ ScriptExecutionContext::Task::CleanupTask, [] (ScriptExecutionContext* context) { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. workerGlobalScope->clearScript(); } }); } }); return; } m_runLoop.terminate(); }
void InProcessWorkerObjectProxy::didCreateWorkerGlobalScope( WorkerOrWorkletGlobalScope* globalScope) { DCHECK(!m_workerGlobalScope); m_workerGlobalScope = toWorkerGlobalScope(globalScope); m_timer = wrapUnique(new Timer<InProcessWorkerObjectProxy>( this, &InProcessWorkerObjectProxy::checkPendingActivity)); }
void MessagePort::dispatchMessages() { // Because close() doesn't cancel any in flight calls to dispatchMessages() we // need to check if the port is still open before dispatch. if (m_closed) return; // Messages for contexts that are not fully active get dispatched too, but // JSAbstractEventListener::handleEvent() doesn't call handlers for these. // The HTML5 spec specifies that any messages sent to a document that is not // fully active should be dropped, so this behavior is OK. if (!started()) return; RefPtr<SerializedScriptValue> message; std::unique_ptr<MessagePortChannelArray> channels; while (tryGetMessage(message, channels)) { // close() in Worker onmessage handler should prevent next message from // dispatching. if (getExecutionContext()->isWorkerGlobalScope() && toWorkerGlobalScope(getExecutionContext())->isClosing()) return; MessagePortArray* ports = MessagePort::entanglePorts(*getExecutionContext(), std::move(channels)); Event* evt = MessageEvent::create(ports, message.release()); dispatchEvent(evt); } }
static ThrottlingController* from(ExecutionContext* context) { if (!context) return 0; if (context->isDocument()) { Document* document = toDocument(context); if (!document->frame()) return 0; ThrottlingController* controller = static_cast<ThrottlingController*>(WillBeHeapSupplement<LocalFrame>::from(document->frame(), supplementName())); if (controller) return controller; controller = new ThrottlingController(); WillBeHeapSupplement<LocalFrame>::provideTo(*document->frame(), supplementName(), adoptPtrWillBeNoop(controller)); return controller; } ASSERT(!isMainThread()); ASSERT(context->isWorkerGlobalScope()); WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); ThrottlingController* controller = static_cast<ThrottlingController*>(WillBeHeapSupplement<WorkerClients>::from(workerGlobalScope->clients(), supplementName())); if (controller) return controller; controller = new ThrottlingController(); WillBeHeapSupplement<WorkerClients>::provideTo(*workerGlobalScope->clients(), supplementName(), adoptPtrWillBeNoop(controller)); return controller; }
virtual void performTask(ScriptExecutionContext *context) { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); #if ENABLE(SQL_DATABASE) // FIXME: Should we stop the databases as part of stopActiveDOMObjects() below? DatabaseTaskSynchronizer cleanupSync; DatabaseManager::manager().stopDatabases(workerGlobalScope, &cleanupSync); #endif workerGlobalScope->stopActiveDOMObjects(); workerGlobalScope->notifyObserversOfStop(); // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, // which become dangling once Heap is destroyed. workerGlobalScope->removeAllEventListeners(); #if ENABLE(SQL_DATABASE) // We wait for the database thread to clean up all its stuff so that we // can do more stringent leak checks as we exit. cleanupSync.waitForTaskCompletion(); #endif // Stick a shutdown command at the end of the queue, so that we deal // with all the cleanup tasks the databases post first. workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create()); }
void V8WorkerGlobalScopeEventListener::handleEvent(ExecutionContext* context, Event* event) { if (!context) return; // The callback function on XMLHttpRequest can clear the event listener and destroys 'this' object. Keep a local reference to it. // See issue 889829. RefPtr<V8AbstractEventListener> protect(this); v8::Isolate* isolate = toIsolate(context); v8::HandleScope handleScope(isolate); WorkerScriptController* script = toWorkerGlobalScope(context)->script(); if (!script) return; v8::Handle<v8::Context> v8Context = script->context(); if (v8Context.IsEmpty()) return; // Enter the V8 context in which to perform the event handling. v8::Context::Scope scope(v8Context); // Get the V8 wrapper for the event object. v8::Handle<v8::Value> jsEvent = toV8(event, v8::Handle<v8::Object>(), isolate); invokeEventHandler(context, event, v8::Local<v8::Value>::New(isolate, jsEvent)); }
PassRefPtr<WebSocketChannel> WebSocketChannel::create(ExecutionContext* context, WebSocketChannelClient* client) { ASSERT(context); ASSERT(client); String sourceURL; unsigned lineNumber = 0; RefPtr<ScriptCallStack> callStack = createScriptCallStack(1, true); if (callStack && callStack->size()) { sourceURL = callStack->at(0).sourceURL(); lineNumber = callStack->at(0).lineNumber(); } if (context->isWorkerGlobalScope()) { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); WorkerRunLoop& runLoop = workerGlobalScope->thread()->runLoop(); String mode = webSocketChannelMode + String::number(runLoop.createUniqueId()); return WorkerThreadableWebSocketChannel::create(workerGlobalScope, client, mode, sourceURL, lineNumber); } Document* document = toDocument(context); Settings* settings = document->settings(); if (settings && settings->experimentalWebSocketEnabled()) { // FIXME: Create and return an "experimental" WebSocketChannel instead of a MainThreadWebSocketChannel. return MainThreadWebSocketChannel::create(document, client, sourceURL, lineNumber); } return MainThreadWebSocketChannel::create(document, client, sourceURL, lineNumber); }
void ScheduledAction::execute(ScriptExecutionContext* context) { if (context->isDocument()) execute(toDocument(context)); else execute(toWorkerGlobalScope(context)); }
double WorkerPerformance::now(ExecutionContext* context) const { ASSERT(context); ASSERT(context->isWorkerGlobalScope()); WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); return 1000.0 * (monotonicallyIncreasingTime() - workerGlobalScope->timeOrigin()); }
bool DatabaseObserver::canEstablishDatabase(ExecutionContext* executionContext, const String& name, const String& displayName, unsigned long estimatedSize) { ASSERT(executionContext->isContextThread()); ASSERT(executionContext->isDocument() || executionContext->isWorkerGlobalScope()); if (executionContext->isDocument()) { Document* document = toDocument(executionContext); WebFrameImpl* webFrame = WebFrameImpl::fromFrame(document->frame()); if (!webFrame) return false; WebViewImpl* webView = webFrame->viewImpl(); if (!webView) return false; if (webView->permissionClient()) return webView->permissionClient()->allowDatabase(webFrame, name, displayName, estimatedSize); } else { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(executionContext); WorkerPermissionClient* permissionClient = WorkerPermissionClient::from(workerGlobalScope); if (permissionClient->proxy()) return permissionClient->allowDatabase(name, displayName, estimatedSize); // FIXME: Deprecate this bridge code when PermissionClientProxy is // implemented by the embedder. WebWorkerBase* webWorker = static_cast<WebWorkerBase*>(workerGlobalScope->thread()->workerLoaderProxy().toWebWorkerBase()); WebView* view = webWorker->view(); if (!view) return false; return allowDatabaseForWorker(view->mainFrame(), name, displayName, estimatedSize); } return true; }
bool DatabaseObserver::canEstablishDatabase(ScriptExecutionContext* scriptExecutionContext, const String& name, const String& displayName, unsigned long estimatedSize) { ASSERT(scriptExecutionContext->isContextThread()); ASSERT(scriptExecutionContext->isDocument() || scriptExecutionContext->isWorkerGlobalScope()); if (scriptExecutionContext->isDocument()) { Document* document = toDocument(scriptExecutionContext); WebFrameImpl* webFrame = WebFrameImpl::fromFrame(document->frame()); if (!webFrame) return false; WebViewImpl* webView = webFrame->viewImpl(); if (!webView) return false; if (webView->permissionClient()) return webView->permissionClient()->allowDatabase(webFrame, name, displayName, estimatedSize); } else { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(scriptExecutionContext); WebWorkerBase* webWorker = static_cast<WebWorkerBase*>(workerGlobalScope->thread()->workerLoaderProxy().toWebWorkerBase()); WebView* view = webWorker->view(); if (!view) return false; return allowDatabaseForWorker(view->mainFrame(), name, displayName, estimatedSize); } return true; }
void ScriptExecutionContext::destroyedMessagePort(MessagePort& messagePort) { ASSERT((isDocument() && isMainThread()) || (isWorkerGlobalScope() && currentThread() == toWorkerGlobalScope(this)->thread().threadID())); m_messagePorts.remove(&messagePort); }
ScriptPromise ServiceWorkerClients::openWindow(ScriptState* scriptState, const String& url) { ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); ScriptPromise promise = resolver->promise(); ExecutionContext* context = scriptState->getExecutionContext(); KURL parsedUrl = KURL(toWorkerGlobalScope(context)->location()->url(), url); if (!parsedUrl.isValid()) { resolver->reject(V8ThrowException::createTypeError( scriptState->isolate(), "'" + url + "' is not a valid URL.")); return promise; } if (!context->getSecurityOrigin()->canDisplay(parsedUrl)) { resolver->reject(V8ThrowException::createTypeError( scriptState->isolate(), "'" + parsedUrl.elidedString() + "' cannot be opened.")); return promise; } if (!context->isWindowInteractionAllowed()) { resolver->reject(DOMException::create(InvalidAccessError, "Not allowed to open a window.")); return promise; } context->consumeWindowInteraction(); ServiceWorkerGlobalScopeClient::from(context)->openWindow( parsedUrl, WTF::makeUnique<NavigateClientCallback>(resolver)); return promise; }
void V8AbstractEventListener::invokeEventHandler(ExecutionContext* context, Event* event, v8::Local<v8::Value> jsEvent) { // If jsEvent is empty, attempt to set it as a hidden value would crash v8. if (jsEvent.IsEmpty()) return; v8::Local<v8::Context> v8Context = toV8Context(context, world()); if (v8Context.IsEmpty()) return; // We push the event being processed into the global object, so that it can be exposed by DOMWindow's bindings. v8::Handle<v8::String> eventSymbol = v8AtomicString(v8Context->GetIsolate(), "event"); v8::Local<v8::Value> returnValue; { // Catch exceptions thrown in the event handler so they do not propagate to javascript code that caused the event to fire. v8::TryCatch tryCatch; tryCatch.SetVerbose(true); // Save the old 'event' property so we can restore it later. v8::Local<v8::Value> savedEvent = getHiddenValue(v8Context->GetIsolate(), v8Context->Global(), eventSymbol); tryCatch.Reset(); // Make the event available in the global object, so DOMWindow can expose it. setHiddenValue(v8Context->GetIsolate(), v8Context->Global(), eventSymbol, jsEvent); tryCatch.Reset(); returnValue = callListenerFunction(context, jsEvent, event); if (tryCatch.HasCaught()) event->target()->uncaughtExceptionInEventHandler(); if (!tryCatch.CanContinue()) { // Result of TerminateExecution(). if (context->isWorkerGlobalScope()) toWorkerGlobalScope(context)->script()->forbidExecution(); return; } tryCatch.Reset(); // Restore the old event. This must be done for all exit paths through this method. if (savedEvent.IsEmpty()) setHiddenValue(v8Context->GetIsolate(), v8Context->Global(), eventSymbol, v8::Undefined(v8Context->GetIsolate())); else setHiddenValue(v8Context->GetIsolate(), v8Context->Global(), eventSymbol, savedEvent); tryCatch.Reset(); } ASSERT(!handleOutOfMemory() || returnValue.IsEmpty()); if (returnValue.IsEmpty()) return; if (!returnValue->IsNull() && !returnValue->IsUndefined() && event->isBeforeUnloadEvent()) { V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, stringReturnValue, returnValue); toBeforeUnloadEvent(event)->setReturnValue(stringReturnValue); } if (m_isAttribute && shouldPreventDefault(returnValue)) event->preventDefault(); }
WorkerMessagingProxy::~WorkerMessagingProxy() { ASSERT(!m_workerObject); ASSERT((m_executionContext->isDocument() && isMainThread()) || (m_executionContext->isWorkerGlobalScope() && toWorkerGlobalScope(m_executionContext.get())->thread()->isCurrentThread())); if (m_loaderProxy) m_loaderProxy->detachProvider(this); }
void ScriptExecutionContext::createdMessagePort(MessagePort* port) { ASSERT(port); ASSERT((isDocument() && isMainThread()) || (isWorkerGlobalScope() && currentThread() == toWorkerGlobalScope(this)->thread().threadID())); m_messagePorts.add(port); }
ImageBitmapFactories& ImageBitmapFactories::from(EventTarget& eventTarget) { if (LocalDOMWindow* window = eventTarget.toDOMWindow()) return fromInternal(*window); ASSERT(eventTarget.executionContext()->isWorkerGlobalScope()); return ImageBitmapFactories::fromInternal(*toWorkerGlobalScope(eventTarget.executionContext())); }
LocalFileSystem* LocalFileSystem::from(ExecutionContext& context) { if (context.isDocument()) { return static_cast<LocalFileSystem*>(WillBeHeapSupplement<LocalFrame>::from(toDocument(context).frame(), supplementName())); } ASSERT(context.isWorkerGlobalScope()); return static_cast<LocalFileSystem*>(WillBeHeapSupplement<WorkerClients>::from(toWorkerGlobalScope(context).clients(), supplementName())); }
void WorkerMessagingProxy::disconnectFromInspector() { m_pageInspector = 0; if (m_askedToTerminate) return; m_workerThread->runLoop().postTaskForMode([] (ScriptExecutionContext* context) { toWorkerGlobalScope(context)->workerInspectorController().disconnectFrontend(Inspector::InspectorDisconnectReason::InspectorDestroyed); }, WorkerDebuggerAgent::debuggerTaskMode); }
// This is called if the associated ExecutionContext is destructing while // we're still associated with it. That's our cue to disassociate and shutdown. // To do this, we stop the database and let everything shutdown naturally // because the database closing process may still make use of this context. // It is not safe to just delete the context here. void DatabaseContext::contextDestroyed() { RefPtrWillBeRawPtr<DatabaseContext> protector(this); stopDatabases(); if (executionContext()->isWorkerGlobalScope()) toWorkerGlobalScope(executionContext())->unregisterTerminationObserver(this); DatabaseManager::manager().unregisterDatabaseContext(this); ActiveDOMObject::contextDestroyed(); }
void MemoryCache::removeURLFromCache(ExecutionContext* context, const KURL& url) { if (context->isWorkerGlobalScope()) { WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); workerGlobalScope->thread()->workerLoaderProxy().postTaskToLoader(createCallbackTask(&removeURLFromCacheInternal, url)); return; } removeURLFromCacheInternal(context, url); }
void WebSharedWorkerImpl::connectTask(PassOwnPtr<WebMessagePortChannel> 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(channel); WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); ASSERT_WITH_SECURITY_IMPLICATION(workerGlobalScope->isSharedWorkerGlobalScope()); workerGlobalScope->dispatchEvent(createConnectEvent(port)); }
void ThreadableLoader::loadResourceSynchronously(ExecutionContext& context, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions) { if (context.isWorkerGlobalScope()) { WorkerThreadableLoader::loadResourceSynchronously(toWorkerGlobalScope(context), request, client, options, resourceLoaderOptions); return; } DocumentThreadableLoader::loadResourceSynchronously(toDocument(context), request, client, options, resourceLoaderOptions); }
bool ActiveDOMCallback::isScriptControllerTerminating() const { ExecutionContext* context = executionContext(); if (context && context->isWorkerGlobalScope()) { WorkerScriptController* scriptController = toWorkerGlobalScope(context)->script(); if (!scriptController || scriptController->isExecutionForbidden() || scriptController->isExecutionTerminating()) return true; } return false; }
IndexedDBClient* IndexedDBClient::from(ExecutionContext* context) { if (context->isDocument()) return static_cast<IndexedDBClient*>(Supplement<LocalFrame>::from( toDocument(*context).frame(), supplementName())); WorkerClients* clients = toWorkerGlobalScope(*context).clients(); ASSERT(clients); return static_cast<IndexedDBClient*>( Supplement<WorkerClients>::from(clients, supplementName())); }
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); SECURITY_DCHECK(workerGlobalScope->isSharedWorkerGlobalScope()); workerGlobalScope->dispatchEvent(createConnectEvent(port)); }
void WorkerMessagingProxy::connectToInspector(WorkerGlobalScopeProxy::PageInspector* pageInspector) { if (m_askedToTerminate) return; ASSERT(!m_pageInspector); m_pageInspector = pageInspector; m_workerThread->runLoop().postTaskForMode([] (ScriptExecutionContext* context) { toWorkerGlobalScope(context)->workerInspectorController().connectFrontend(); }, WorkerDebuggerAgent::debuggerTaskMode); }
void WorkerMessagingProxy::sendMessageToInspector(const String& message) { if (m_askedToTerminate) return; String messageCopy = message.isolatedCopy(); m_workerThread->runLoop().postTaskForMode([messageCopy] (ScriptExecutionContext* context) { toWorkerGlobalScope(context)->workerInspectorController().dispatchMessageFromFrontend(messageCopy); }, WorkerDebuggerAgent::debuggerTaskMode); WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(m_workerThread.get()); }
PassOwnPtr<ThreadableLoader> ThreadableLoader::create(ExecutionContext& context, ThreadableLoaderClient* client, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions) { ASSERT(client); if (context.isWorkerGlobalScope()) { return WorkerThreadableLoader::create(toWorkerGlobalScope(context), client, options, resourceLoaderOptions); } return DocumentThreadableLoader::create(toDocument(context), client, options, resourceLoaderOptions); }
bool LocalFileSystemClient::requestFileSystemAccessSync(ExecutionContext* context) { ASSERT(context); if (context->isDocument()) { ASSERT_NOT_REACHED(); return false; } ASSERT(context->isWorkerGlobalScope()); return WorkerPermissionClient::from(*toWorkerGlobalScope(context))->requestFileSystemAccessSync(); }