Exemple #1
0
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));
}
Exemple #3
0
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);
  }
}
Exemple #4
0
    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;
    }
Exemple #5
0
    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));
}
Exemple #7
0
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);
}
Exemple #8
0
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);
}
Exemple #13
0
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;
}
Exemple #14
0
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();
}
Exemple #21
0
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;
}
Exemple #25
0
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();
}