/* | |
* Copyright (C) 2001 Peter Kelly (pmk@post.com) | |
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All Rights Reserved. | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2 of the License, or (at your option) any later version. | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this library; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
*/ | |
#include "config.h" | |
#include "JSEventListener.h" | |
#include "Event.h" | |
#include "Frame.h" | |
#include "JSEvent.h" | |
#include "JSEventTarget.h" | |
#include <runtime/JSLock.h> | |
#include <wtf/RefCountedLeakCounter.h> | |
using namespace JSC; | |
namespace WebCore { | |
JSEventListener::JSEventListener(JSObject* function, JSObject* wrapper, bool isAttribute, DOMWrapperWorld* isolatedWorld) | |
: EventListener(JSEventListenerType) | |
, m_jsFunction(function) | |
, m_wrapper(wrapper) | |
, m_isAttribute(isAttribute) | |
, m_isolatedWorld(isolatedWorld) | |
{ | |
} | |
JSEventListener::~JSEventListener() | |
{ | |
} | |
JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext*) const | |
{ | |
ASSERT_NOT_REACHED(); | |
return 0; | |
} | |
void JSEventListener::markJSFunction(MarkStack& markStack) | |
{ | |
if (m_jsFunction) | |
markStack.append(m_jsFunction); | |
} | |
void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event) | |
{ | |
ASSERT(scriptExecutionContext); | |
if (!scriptExecutionContext) | |
return; | |
JSLock lock(SilenceAssertionsOnly); | |
JSObject* jsFunction = this->jsFunction(scriptExecutionContext); | |
if (!jsFunction) | |
return; | |
JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, m_isolatedWorld.get()); | |
if (!globalObject) | |
return; | |
if (scriptExecutionContext->isDocument()) { | |
JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); | |
Frame* frame = window->impl()->frame(); | |
if (!frame) | |
return; | |
// The window must still be active in its frame. See <https://bugs.webkit.org/show_bug.cgi?id=21921>. | |
// FIXME: A better fix for this may be to change DOMWindow::frame() to not return a frame the detached window used to be in. | |
if (frame->domWindow() != window->impl()) | |
return; | |
// FIXME: Is this check needed for other contexts? | |
ScriptController* script = frame->script(); | |
if (!script->canExecuteScripts(AboutToExecuteScript) || script->isPaused()) | |
return; | |
} | |
ExecState* exec = globalObject->globalExec(); | |
JSValue handleEventFunction = jsFunction->get(exec, Identifier(exec, "handleEvent")); | |
CallData callData; | |
CallType callType = handleEventFunction.getCallData(callData); | |
if (callType == CallTypeNone) { | |
handleEventFunction = JSValue(); | |
callType = jsFunction->getCallData(callData); | |
} | |
if (callType != CallTypeNone) { | |
ref(); | |
MarkedArgumentBuffer args; | |
args.append(toJS(exec, globalObject, event)); | |
Event* savedEvent = globalObject->currentEvent(); | |
globalObject->setCurrentEvent(event); | |
JSGlobalData* globalData = globalObject->globalData(); | |
DynamicGlobalObjectScope globalObjectScope(exec, globalData->dynamicGlobalObject ? globalData->dynamicGlobalObject : globalObject); | |
globalData->timeoutChecker.start(); | |
JSValue retval = handleEventFunction | |
? JSC::call(exec, handleEventFunction, callType, callData, jsFunction, args) | |
: JSC::call(exec, jsFunction, callType, callData, toJS(exec, globalObject, event->currentTarget()), args); | |
globalData->timeoutChecker.stop(); | |
globalObject->setCurrentEvent(savedEvent); | |
if (exec->hadException()) | |
reportCurrentException(exec); | |
else { | |
if (!retval.isUndefinedOrNull() && event->storesResultAsString()) | |
event->storeResult(retval.toString(exec)); | |
if (m_isAttribute) { | |
bool retvalbool; | |
if (retval.getBoolean(retvalbool) && !retvalbool) | |
event->preventDefault(); | |
} | |
} | |
deref(); | |
} | |
} | |
bool JSEventListener::reportError(ScriptExecutionContext* context, const String& message, const String& url, int lineNumber) | |
{ | |
JSLock lock(SilenceAssertionsOnly); | |
JSObject* jsFunction = this->jsFunction(context); | |
if (!jsFunction) | |
return false; | |
JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(context, m_isolatedWorld.get()); | |
ExecState* exec = globalObject->globalExec(); | |
CallData callData; | |
CallType callType = jsFunction->getCallData(callData); | |
if (callType == CallTypeNone) | |
return false; | |
MarkedArgumentBuffer args; | |
args.append(jsString(exec, message)); | |
args.append(jsString(exec, url)); | |
args.append(jsNumber(exec, lineNumber)); | |
JSGlobalData* globalData = globalObject->globalData(); | |
DynamicGlobalObjectScope globalObjectScope(exec, globalData->dynamicGlobalObject ? globalData->dynamicGlobalObject : globalObject); | |
JSValue thisValue = globalObject->toThisObject(exec); | |
globalData->timeoutChecker.start(); | |
JSValue returnValue = JSC::call(exec, jsFunction, callType, callData, thisValue, args); | |
globalData->timeoutChecker.stop(); | |
// If an error occurs while handling the script error, it should be bubbled up. | |
if (exec->hadException()) { | |
exec->clearException(); | |
return false; | |
} | |
bool bubbleEvent; | |
return returnValue.getBoolean(bubbleEvent) && !bubbleEvent; | |
} | |
bool JSEventListener::virtualisAttribute() const | |
{ | |
return m_isAttribute; | |
} | |
bool JSEventListener::operator==(const EventListener& listener) | |
{ | |
if (const JSEventListener* jsEventListener = JSEventListener::cast(&listener)) | |
return m_jsFunction == jsEventListener->m_jsFunction && m_isAttribute == jsEventListener->m_isAttribute; | |
return false; | |
} | |
} // namespace WebCore |