#include "config.h"
#include "kjs_events.h"
#include "Clipboard.h"
#include "ClipboardEvent.h"
#include "Document.h"
#include "Event.h"
#include "EventNames.h"
#include "Frame.h"
#include "HTMLImageElement.h"
#include "HTMLNames.h"
#include "JSEvent.h"
#include "JSKeyboardEvent.h"
#include "JSMouseEvent.h"
#include "JSMutationEvent.h"
#include "JSOverflowEvent.h"
#include "JSWheelEvent.h"
#include "KeyboardEvent.h"
#include "MouseEvent.h"
#include "MutationEvent.h"
#include "OverflowEvent.h"
#include "UIEvent.h"
#include "WheelEvent.h"
#include "kjs_proxy.h"
#include "kjs_window.h"
#include "kjs_events.lut.h"
using namespace WebCore;
using namespace EventNames;
using namespace HTMLNames;
namespace KJS {
JSAbstractEventListener::JSAbstractEventListener(bool _html)
: html(_html)
{
}
void JSAbstractEventListener::handleEvent(Event* ele, bool isWindowEvent)
{
#ifdef KJS_DEBUGGER
if (KJSDebugWin::instance() && KJSDebugWin::instance()->inSession())
return;
#endif
Event *event = ele;
JSObject* listener = listenerObj();
if (!listener)
return;
Window* window = windowObj();
Frame *frame = window->frame();
if (!frame)
return;
KJSProxy* proxy = frame->jScript();
if (!proxy)
return;
JSLock lock;
ScriptInterpreter* interpreter = proxy->interpreter();
ExecState* exec = interpreter->globalExec();
JSValue* handleEventFuncValue = listener->get(exec, "handleEvent");
JSObject* handleEventFunc = 0;
if (handleEventFuncValue->isObject()) {
handleEventFunc = static_cast<JSObject*>(handleEventFuncValue);
if (!handleEventFunc->implementsCall())
handleEventFunc = 0;
}
if (handleEventFunc || listener->implementsCall()) {
ref();
List args;
args.append(toJS(exec, event));
window->setCurrentEvent(event);
interpreter->setCurrentEvent(event);
JSValue* retval;
if (handleEventFunc) {
interpreter->startTimeoutCheck();
retval = handleEventFunc->call(exec, listener, args);
} else {
JSObject* thisObj;
if (isWindowEvent)
thisObj = window;
else
thisObj = static_cast<JSObject*>(toJS(exec, event->currentTarget()));
interpreter->startTimeoutCheck();
retval = listener->call(exec, thisObj, args);
}
interpreter->stopTimeoutCheck();
window->setCurrentEvent(0);
interpreter->setCurrentEvent(0);
if (exec->hadException()) {
JSObject* exception = exec->exception()->toObject(exec);
String message = exception->get(exec, messagePropertyName)->toString(exec);
int lineNumber = exception->get(exec, "line")->toInt32(exec);
String sourceURL = exception->get(exec, "sourceURL")->toString(exec);
if (Interpreter::shouldPrintExceptions())
printf("(event handler):%s\n", message.deprecatedString().utf8().data());
frame->addMessageToConsole(message, lineNumber, sourceURL);
exec->clearException();
} else {
if (!retval->isUndefinedOrNull() && event->storesResultAsString())
event->storeResult(retval->toString(exec));
if (html) {
bool retvalbool;
if (retval->getBoolean(retvalbool) && !retvalbool)
event->preventDefault();
}
}
Document::updateDocumentsRendering();
deref();
}
}
bool JSAbstractEventListener::isHTMLEventListener() const
{
return html;
}
JSUnprotectedEventListener::JSUnprotectedEventListener(JSObject* _listener, Window* _win, bool _html)
: JSAbstractEventListener(_html)
, listener(_listener)
, win(_win)
{
if (_listener) {
Window::UnprotectedListenersMap& listeners = _html
? _win->jsUnprotectedHTMLEventListeners : _win->jsUnprotectedEventListeners;
listeners.set(_listener, this);
}
}
JSUnprotectedEventListener::~JSUnprotectedEventListener()
{
if (listener && win) {
Window::UnprotectedListenersMap& listeners = isHTMLEventListener()
? win->jsUnprotectedHTMLEventListeners : win->jsUnprotectedEventListeners;
listeners.remove(listener);
}
}
JSObject* JSUnprotectedEventListener::listenerObj() const
{
return listener;
}
Window* JSUnprotectedEventListener::windowObj() const
{
return win;
}
void JSUnprotectedEventListener::clearWindowObj()
{
win = 0;
}
void JSUnprotectedEventListener::mark()
{
if (listener && !listener->marked())
listener->mark();
}
#ifndef NDEBUG
#ifndef LOG_CHANNEL_PREFIX
#define LOG_CHANNEL_PREFIX Log
#endif
WTFLogChannel LogWebCoreEventListenerLeaks = { 0x00000000, "", WTFLogChannelOn };
struct EventListenerCounter {
static unsigned count;
~EventListenerCounter()
{
if (count)
LOG(WebCoreEventListenerLeaks, "LEAK: %u EventListeners\n", count);
}
};
unsigned EventListenerCounter::count = 0;
static EventListenerCounter eventListenerCounter;
#endif
JSEventListener::JSEventListener(JSObject* _listener, Window* _win, bool _html)
: JSAbstractEventListener(_html)
, listener(_listener)
, win(_win)
{
if (_listener) {
Window::ListenersMap& listeners = _html
? _win->jsHTMLEventListeners : _win->jsEventListeners;
listeners.set(_listener, this);
}
#ifndef NDEBUG
++eventListenerCounter.count;
#endif
}
JSEventListener::~JSEventListener()
{
if (listener && win) {
Window::ListenersMap& listeners = isHTMLEventListener()
? win->jsHTMLEventListeners : win->jsEventListeners;
listeners.remove(listener);
}
#ifndef NDEBUG
--eventListenerCounter.count;
#endif
}
JSObject* JSEventListener::listenerObj() const
{
return listener;
}
Window* JSEventListener::windowObj() const
{
return win;
}
void JSEventListener::clearWindowObj()
{
win = 0;
}
JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& code, Window* win, Node* node, int lineno)
: JSEventListener(0, win, true)
, m_functionName(functionName)
, code(code)
, parsed(false)
, lineNumber(lineno)
, originalNode(node)
{
}
JSObject* JSLazyEventListener::listenerObj() const
{
parseCode();
return listener;
}
JSValue* JSLazyEventListener::eventParameterName() const
{
static ProtectedPtr<JSValue> eventString = jsString("event");
return eventString.get();
}
void JSLazyEventListener::parseCode() const
{
if (parsed)
return;
parsed = true;
Frame *frame = windowObj()->frame();
KJSProxy *proxy = 0;
if (frame)
proxy = frame->jScript();
if (proxy) {
ScriptInterpreter* interpreter = proxy->interpreter();
ExecState* exec = interpreter->globalExec();
JSLock lock;
JSObject* constr = interpreter->builtinFunction();
List args;
UString sourceURL(frame->url().url());
args.append(eventParameterName());
args.append(jsString(code));
listener = constr->construct(exec, args, m_functionName, sourceURL, lineNumber);
if (exec->hadException()) {
exec->clearException();
listener = 0;
} else if (originalNode) {
ScopeChain scope = listener->scope();
JSValue* thisObj = toJS(exec, originalNode);
if (thisObj->isObject()) {
static_cast<DOMEventTargetNode*>(thisObj)->pushEventHandlerScope(exec, scope);
listener->setScope(scope);
}
}
}
m_functionName = String();
code = String();
if (listener) {
Window::ListenersMap& listeners = isHTMLEventListener()
? windowObj()->jsHTMLEventListeners : windowObj()->jsEventListeners;
listeners.set(listener, const_cast<JSLazyEventListener*>(this));
}
}
JSValue* getNodeEventListener(EventTargetNode* n, const AtomicString& eventType)
{
if (JSAbstractEventListener* listener = static_cast<JSAbstractEventListener*>(n->getHTMLEventListener(eventType)))
if (JSValue* obj = listener->listenerObj())
return obj;
return jsNull();
}
const ClassInfo DOMEvent::info = { "Event", 0, &DOMEventTable, 0 };
KJS_IMPLEMENT_PROTOFUNC(DOMEventProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMEvent", DOMEventProto, DOMEventProtoFunc)
DOMEvent::DOMEvent(ExecState *exec, Event *e)
: m_impl(e), clipboard(0)
{
setPrototype(DOMEventProto::self(exec));
}
DOMEvent::~DOMEvent()
{
ScriptInterpreter::forgetDOMObject(m_impl.get());
}
void DOMEvent::mark()
{
DOMObject::mark();
if (clipboard && !clipboard->marked())
clipboard->mark();
}
bool DOMEvent::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticValueSlot<DOMEvent, DOMObject>(exec, &DOMEventTable, this, propertyName, slot);
}
JSValue *DOMEvent::getValueProperty(ExecState *exec, int token) const
{
Event &event = *m_impl;
switch (token) {
case Type:
return jsString(event.type());
case Target:
case SrcElement:
return toJS(exec, event.target());
case CurrentTarget:
return toJS(exec, event.currentTarget());
case EventPhase:
return jsNumber(event.eventPhase());
case Bubbles:
return jsBoolean(event.bubbles());
case CancelBubble:
return jsBoolean(event.getCancelBubble());
case ReturnValue:
return jsBoolean(!event.defaultPrevented());
case Cancelable:
return jsBoolean(event.cancelable());
case TimeStamp:
return jsNumber(event.timeStamp());
case ClipboardData:
{
if (event.isClipboardEvent()) {
ClipboardEvent *impl = static_cast<ClipboardEvent *>(&event);
if (!clipboard)
clipboard = new Clipboard(exec, impl->clipboard());
return clipboard;
} else
return jsUndefined();
}
case DataTransfer:
{
if (event.isDragEvent()) {
MouseEvent *impl = static_cast<MouseEvent *>(&event);
if (!clipboard)
clipboard = new Clipboard(exec, impl->clipboard());
return clipboard;
} else
return jsUndefined();
}
default:
return 0;
}
}
void DOMEvent::put(ExecState *exec, const Identifier &propertyName,
JSValue *value, int attr)
{
lookupPut<DOMEvent, DOMObject>(exec, propertyName, value, attr, &DOMEventTable, this);
}
void DOMEvent::putValueProperty(ExecState *exec, int token, JSValue *value, int)
{
Event &event = *m_impl;
switch (token) {
case ReturnValue:
event.setDefaultPrevented(!value->toBoolean(exec));
break;
case CancelBubble:
event.setCancelBubble(value->toBoolean(exec));
break;
default:
break;
}
}
JSValue *DOMEventProtoFunc::callAsFunction(ExecState *exec, JSObject * thisObj, const List &args)
{
if (!thisObj->inherits(&DOMEvent::info))
return throwError(exec, TypeError);
Event &event = *static_cast<DOMEvent *>( thisObj )->impl();
switch (id) {
case DOMEvent::StopPropagation:
event.stopPropagation();
return jsUndefined();
case DOMEvent::PreventDefault:
event.preventDefault();
return jsUndefined();
case DOMEvent::InitEvent:
event.initEvent(AtomicString(args[0]->toString(exec)), args[1]->toBoolean(exec), args[2]->toBoolean(exec));
return jsUndefined();
};
return jsUndefined();
}
JSValue *toJS(ExecState *exec, Event *e)
{
if (!e)
return jsNull();
ScriptInterpreter* interp = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
JSLock lock;
DOMObject *ret = interp->getDOMObject(e);
if (!ret) {
if (e->isKeyboardEvent())
ret = new JSKeyboardEvent(exec, static_cast<KeyboardEvent *>(e));
else if (e->isMouseEvent())
ret = new JSMouseEvent(exec, static_cast<MouseEvent *>(e));
else if (e->isWheelEvent())
ret = new JSWheelEvent(exec, static_cast<WheelEvent *>(e));
else if (e->isUIEvent())
ret = new JSUIEvent(exec, static_cast<UIEvent *>(e));
else if (e->isMutationEvent())
ret = new JSMutationEvent(exec, static_cast<MutationEvent *>(e));
else if (e->isOverflowEvent())
ret = new JSOverflowEvent(exec, static_cast<OverflowEvent*>(e));
else
ret = new JSEvent(exec, e);
interp->putDOMObject(e, ret);
}
return ret;
}
Event *toEvent(JSValue *val)
{
if (!val || !val->isObject(&DOMEvent::info))
return 0;
return static_cast<DOMEvent *>(val)->impl();
}
const ClassInfo Clipboard::info = { "Clipboard", 0, &ClipboardTable, 0 };
KJS_DEFINE_PROTOTYPE(ClipboardProto)
KJS_IMPLEMENT_PROTOFUNC(ClipboardProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("Clipboard", ClipboardProto, ClipboardProtoFunc)
Clipboard::Clipboard(ExecState *exec, WebCore::Clipboard *cb)
: clipboard(cb)
{
setPrototype(ClipboardProto::self(exec));
}
bool Clipboard::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticValueSlot<Clipboard, DOMObject>(exec, &ClipboardTable, this, propertyName, slot);
}
JSValue *Clipboard::getValueProperty(ExecState *exec, int token) const
{
switch (token) {
case DropEffect:
assert(clipboard->isForDragging() || clipboard->dropEffect().isNull());
return jsStringOrUndefined(clipboard->dropEffect());
case EffectAllowed:
assert(clipboard->isForDragging() || clipboard->effectAllowed().isNull());
return jsStringOrUndefined(clipboard->effectAllowed());
case Types:
{
DeprecatedStringList qTypes = clipboard->types();
if (qTypes.isEmpty())
return jsNull();
else {
List list;
for (DeprecatedStringList::Iterator it = qTypes.begin(); it != qTypes.end(); ++it) {
list.append(jsString(UString(*it)));
}
return exec->lexicalInterpreter()->builtinArray()->construct(exec, list);
}
}
default:
return NULL;
}
}
void Clipboard::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
{
lookupPut<Clipboard,DOMObject>(exec, propertyName, value, attr, &ClipboardTable, this );
}
void Clipboard::putValueProperty(ExecState *exec, int token, JSValue *value, int )
{
switch (token) {
case DropEffect:
if (clipboard->isForDragging())
clipboard->setDropEffect(value->toString(exec));
break;
case EffectAllowed:
if (clipboard->isForDragging())
clipboard->setEffectAllowed(value->toString(exec));
break;
}
}
JSValue *ClipboardProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
{
if (!thisObj->inherits(&Clipboard::info))
return throwError(exec, TypeError);
Clipboard *cb = static_cast<Clipboard *>(thisObj);
switch (id) {
case Clipboard::ClearData:
if (args.size() == 0) {
cb->clipboard->clearAllData();
return jsUndefined();
} else if (args.size() == 1) {
cb->clipboard->clearData(args[0]->toString(exec));
return jsUndefined();
} else
return throwError(exec, SyntaxError, "clearData: Invalid number of arguments");
case Clipboard::GetData:
{
if (args.size() == 1) {
bool success;
String result = cb->clipboard->getData(args[0]->toString(exec), success);
if (success)
return jsString(result);
else
return jsUndefined();
} else
return throwError(exec, SyntaxError, "getData: Invalid number of arguments");
}
case Clipboard::SetData:
if (args.size() == 2)
return jsBoolean(cb->clipboard->setData(args[0]->toString(exec), args[1]->toString(exec)));
else
return throwError(exec, SyntaxError, "setData: Invalid number of arguments");
case Clipboard::SetDragImage:
{
if (!cb->clipboard->isForDragging())
return jsUndefined();
if (args.size() != 3)
return throwError(exec, SyntaxError, "setDragImage: Invalid number of arguments");
int x = (int)args[1]->toNumber(exec);
int y = (int)args[2]->toNumber(exec);
Node *node = toNode(args[0]);
if (!node)
return throwError(exec, TypeError);
if (!node->isElementNode())
return throwError(exec, SyntaxError, "setDragImageFromElement: Invalid first argument");
if (static_cast<Element*>(node)->hasLocalName(imgTag) &&
!node->inDocument())
cb->clipboard->setDragImage(static_cast<HTMLImageElement*>(node)->cachedImage(), IntPoint(x, y));
else
cb->clipboard->setDragImageElement(node, IntPoint(x, y));
return jsUndefined();
}
}
return jsUndefined();
}
}