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(); }
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(); } } } }