Exemplo n.º 1
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();
}
void V8AbstractEventListener::invokeEventHandler(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;

    ASSERT(!scriptState()->contextIsEmpty());
    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 = V8HiddenValue::getHiddenValue(isolate(), scriptState()->context()->Global(), V8HiddenValue::event(isolate()));
        tryCatch.Reset();

        // Make the event available in the global object, so LocalDOMWindow can expose it.
        V8HiddenValue::setHiddenValue(isolate(), scriptState()->context()->Global(), V8HiddenValue::event(isolate()), jsEvent);
        tryCatch.Reset();

        returnValue = callListenerFunction(jsEvent, event);
        if (tryCatch.HasCaught())
            event->target()->uncaughtExceptionInEventHandler();

        if (!tryCatch.CanContinue()) { // Result of TerminateExecution().
            if (scriptState()->executionContext()->isWorkerGlobalScope())
                toWorkerGlobalScope(scriptState()->executionContext())->script()->forbidExecution();
            return;
        }
        tryCatch.Reset();

        // Restore the old event. This must be done for all exit paths through this method.
        if (savedEvent.IsEmpty())
            V8HiddenValue::setHiddenValue(isolate(), scriptState()->context()->Global(), V8HiddenValue::event(isolate()), v8::Undefined(isolate()));
        else
            V8HiddenValue::setHiddenValue(isolate(), scriptState()->context()->Global(), V8HiddenValue::event(isolate()), savedEvent);
        tryCatch.Reset();
    }

    if (returnValue.IsEmpty())
        return;

    if (m_isAttribute && !returnValue->IsNull() && !returnValue->IsUndefined() && event->isBeforeUnloadEvent()) {
        TOSTRING_VOID(V8StringResource<>, stringReturnValue, returnValue);
        toBeforeUnloadEvent(event)->setReturnValue(stringReturnValue);
    }

    if (m_isAttribute && shouldPreventDefault(returnValue))
        event->preventDefault();
}
Exemplo n.º 3
0
void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event)
{
    ASSERT(scriptExecutionContext);
    if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden())
        return;

    JSLockHolder lock(scriptExecutionContext->vm());

    JSObject* jsFunction = this->jsFunction(scriptExecutionContext);
    if (!jsFunction)
        return;

    JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, *m_isolatedWorld);
    if (!globalObject)
        return;

    if (scriptExecutionContext->isDocument()) {
        JSDOMWindow* window = jsCast<JSDOMWindow*>(globalObject);
        if (!window->impl().isCurrentlyDisplayedInFrame())
            return;
        // FIXME: Is this check needed for other contexts?
        ScriptController& script = window->impl().frame()->script();
        if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused())
            return;
    }

    ExecState* exec = globalObject->globalExec();
    JSValue handleEventFunction = jsFunction;

    CallData callData;
    CallType callType = getCallData(handleEventFunction, callData);
    // If jsFunction is not actually a function, see if it implements the EventListener interface and use that
    if (callType == CallTypeNone) {
        handleEventFunction = jsFunction->get(exec, Identifier(exec, "handleEvent"));
        callType = getCallData(handleEventFunction, callData);
    }

    if (callType != CallTypeNone) {
        Ref<JSEventListener> protect(*this);

        MarkedArgumentBuffer args;
        args.append(toJS(exec, globalObject, event));

        Event* savedEvent = globalObject->currentEvent();
        globalObject->setCurrentEvent(event);

        VM& vm = globalObject->vm();
        VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject);

        InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData);

        JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction;
        JSValue retval = scriptExecutionContext->isDocument()
            ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, thisValue, args)
            : JSC::call(exec, handleEventFunction, callType, callData, thisValue, args);

        InspectorInstrumentation::didCallFunction(cookie);

        globalObject->setCurrentEvent(savedEvent);

        if (scriptExecutionContext->isWorkerGlobalScope()) {
            bool terminatorCausedException = (exec->hadException() && isTerminatedExecutionException(exec->exception()));
            if (terminatorCausedException || vm.watchdog.didFire())
                static_cast<WorkerGlobalScope*>(scriptExecutionContext)->script()->forbidExecution();
        }

        if (exec->hadException()) {
            event->target()->uncaughtExceptionInEventHandler();
            reportCurrentException(exec);
        } else {
            if (!retval.isUndefinedOrNull() && event->isBeforeUnloadEvent())
                toBeforeUnloadEvent(event)->setReturnValue(retval.toString(exec)->value(exec));
            if (m_isAttribute) {
                if (retval.isFalse())
                    event->preventDefault();
            }
        }
    }
}