Reviewed by Richard.
[WebKit-https.git] / WebCore / khtml / ecma / kjs_events.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
5  *  Copyright (C) 2003 Apple Computer, Inc.
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 #include "khtml_part.h"
22 #include "kjs_window.h"
23 #include "kjs_events.h"
24 #include "kjs_events.lut.h"
25 #include "kjs_views.h"
26 #include "kjs_proxy.h"
27 #include "xml/dom_nodeimpl.h"
28 #include "xml/dom_docimpl.h"
29 #include "xml/dom2_eventsimpl.h"
30 #include "rendering/render_object.h"
31 #include "misc/loader.h"
32
33 #include <kdebug.h>
34
35 using namespace KJS;
36
37 using DOM::KeyboardEvent;
38 using DOM::EventImpl;
39 using DOM::NodeImpl;
40
41 // -------------------------------------------------------------------------
42
43 JSAbstractEventListener::JSAbstractEventListener(bool _html)
44   : html(_html)
45 {
46 }
47
48 JSAbstractEventListener::~JSAbstractEventListener()
49 {
50 }
51
52 void JSAbstractEventListener::handleEvent(DOM::Event &evt, bool isWindowEvent)
53 {
54 #ifdef KJS_DEBUGGER
55   if (KJSDebugWin::instance() && KJSDebugWin::instance()->inSession())
56     return;
57 #endif
58   Object listener = listenerObj();
59   Object win = windowObj();
60
61   KHTMLPart *part = static_cast<Window*>(win.imp())->part();
62   KJSProxy *proxy = 0;
63   if (part)
64       proxy = KJSProxy::proxy( part );
65
66   if (proxy && listener.implementsCall()) {
67     ref();
68
69     KJS::ScriptInterpreter *interpreter = static_cast<KJS::ScriptInterpreter *>(proxy->interpreter());
70     ExecState *exec = interpreter->globalExec();
71
72     List args;
73     args.append(getDOMEvent(exec,evt));
74
75     Window *window = static_cast<Window*>(win.imp());
76     // Set the event we're handling in the Window object
77     window->setCurrentEvent( &evt );
78     // ... and in the interpreter
79     interpreter->setCurrentEvent( &evt );
80
81     Object thisObj;
82     if (isWindowEvent) {
83         thisObj = win;
84     } else {
85         KJS::Interpreter::lock();
86         thisObj = Object::dynamicCast(getDOMNode(exec,evt.currentTarget()));
87         KJS::Interpreter::unlock();
88     }
89
90     KJS::Interpreter::lock();
91     Value retval = listener.call(exec, thisObj, args);
92     KJS::Interpreter::unlock();
93
94     window->setCurrentEvent( 0 );
95     interpreter->setCurrentEvent( 0 );
96 #if APPLE_CHANGES
97     if ( exec->hadException() ) {
98         KJS::Interpreter::lock();
99         char *message = exec->exception().toObject(exec).get(exec, messagePropertyName).toString(exec).ascii();
100         int lineNumber =  exec->exception().toObject(exec).get(exec, "line").toInt32(exec);
101         QString sourceURL;
102         {
103           // put this in a block to make sure UString is deallocated inside the lock
104           UString uSourceURL = exec->exception().toObject(exec).get(exec, "sourceURL").toString(exec);
105           sourceURL = uSourceURL.qstring();
106         }
107         KJS::Interpreter::unlock();
108         if (Interpreter::shouldPrintExceptions()) {
109             printf("(event handler):%s\n", message);
110         }
111         KWQ(part)->addMessageToConsole(message, lineNumber, sourceURL);
112         exec->clearException();
113     }
114 #else
115     if ( exec->hadException() )
116         exec->clearException();
117 #endif
118
119     else if (html)
120     {
121         QVariant ret = ValueToVariant(exec, retval);
122         if (ret.type() == QVariant::Bool && ret.toBool() == false)
123             evt.preventDefault();
124     }
125     DOM::DocumentImpl::updateDocumentsRendering();
126     deref();
127   }
128 }
129
130 DOM::DOMString JSAbstractEventListener::eventListenerType()
131 {
132     if (html)
133         return "_khtml_HTMLEventListener";
134     else
135         return "_khtml_JSEventListener";
136 }
137
138 // -------------------------------------------------------------------------
139
140 JSUnprotectedEventListener::JSUnprotectedEventListener(Object _listener, const Object &_win, bool _html)
141   : JSAbstractEventListener(_html)
142   , listener(_listener)
143   , win(_win)
144 {
145     if (_listener.imp()) {
146       static_cast<Window*>(win.imp())->jsUnprotectedEventListeners.insert(_listener.imp(), this);
147     }
148 }
149
150 JSUnprotectedEventListener::~JSUnprotectedEventListener()
151 {
152     if (listener.imp()) {
153       static_cast<Window*>(win.imp())->jsUnprotectedEventListeners.remove(listener.imp());
154     }
155 }
156
157 Object JSUnprotectedEventListener::listenerObj() const
158
159     return listener; 
160 }
161
162 Object JSUnprotectedEventListener::windowObj() const
163 {
164     return win;
165 }
166
167 void JSUnprotectedEventListener::mark()
168 {
169   ObjectImp *listenerImp = listener.imp();
170   if (listenerImp && !listenerImp->marked())
171     listenerImp->mark();
172 }
173
174 // -------------------------------------------------------------------------
175
176 JSEventListener::JSEventListener(Object _listener, const Object &_win, bool _html)
177   : JSAbstractEventListener(_html)
178   , listener(_listener)
179   , win(_win)
180 {
181     //fprintf(stderr,"JSEventListener::JSEventListener this=%p listener=%p\n",this,listener.imp());
182     if (_listener.imp()) {
183       static_cast<Window*>(win.imp())->jsEventListeners.insert(_listener.imp(), this);
184     }
185 }
186
187 JSEventListener::~JSEventListener()
188 {
189     if (listener.imp()) {
190       static_cast<Window*>(win.imp())->jsEventListeners.remove(listener.imp());
191     }
192     //fprintf(stderr,"JSEventListener::~JSEventListener this=%p listener=%p\n",this,listener.imp());
193 }
194
195 Object JSEventListener::listenerObj() const
196
197     return listener; 
198 }
199
200 Object JSEventListener::windowObj() const
201 {
202     return win;
203 }
204
205 // -------------------------------------------------------------------------
206
207 JSLazyEventListener::JSLazyEventListener(QString _code, const Object &_win, NodeImpl *_originalNode, int lineno)
208   : JSEventListener(Object(), _win, true),
209     code(_code),
210     parsed(false)
211 {
212     lineNumber = lineno;
213
214     // We don't retain the original node, because we assume it
215     // will stay alive as long as this handler object is around
216     // and we need to avoid a reference cycle. If JS transfers
217     // this handler to another node, parseCode will be called and
218     // then originalNode is no longer needed.
219     
220     originalNode = _originalNode;
221 }
222
223 void JSLazyEventListener::handleEvent(DOM::Event &evt, bool isWindowEvent)
224 {
225   parseCode();
226   if (!listener.isNull()) { 
227     JSEventListener::handleEvent(evt, isWindowEvent);
228   }
229 }
230
231
232 Object JSLazyEventListener::listenerObj() const
233 {
234   parseCode();
235   return listener;
236 }
237
238 void JSLazyEventListener::parseCode() const
239 {
240   if (!parsed) {
241     KHTMLPart *part = static_cast<Window*>(win.imp())->part();
242     KJSProxy *proxy = 0L;
243     if (part)
244       proxy = KJSProxy::proxy( part );
245
246     if (proxy) {
247       KJS::ScriptInterpreter *interpreter = static_cast<KJS::ScriptInterpreter *>(proxy->interpreter());
248       ExecState *exec = interpreter->globalExec();
249
250       KJS::Interpreter::lock();
251       //KJS::Constructor constr(KJS::Global::current().get("Function").imp());
252       KJS::Object constr = interpreter->builtinFunction();
253       KJS::List args;
254
255       static ProtectedValue eventString = KJS::String("event");
256       UString sourceURL(part->m_url.url());
257       args.append(eventString);
258       args.append(KJS::String(code));
259       listener = constr.construct(exec, args, sourceURL, lineNumber); // ### is globalExec ok ?
260
261       KJS::Interpreter::unlock();
262
263       if (exec->hadException()) {
264         exec->clearException();
265
266         // failed to parse, so let's just make this listener a no-op
267         listener = Object();
268       } else if (originalNode) {
269         // Add the event's home element to the scope
270         // (and the document, and the form - see KJS::HTMLElement::eventHandlerScope)
271         ScopeChain scope = listener.scope();
272         
273         KJS::Interpreter::lock();
274         Object thisObj = Object::dynamicCast(getDOMNode(exec, originalNode));
275         KJS::Interpreter::unlock();
276         
277         if (!thisObj.isNull()) {
278           KJS::Interpreter::lock();
279           static_cast<DOMNode*>(thisObj.imp())->pushEventHandlerScope(exec, scope);
280           KJS::Interpreter::unlock();
281           
282           listener.setScope(scope);
283         }
284       }
285     }
286
287     // no more need to keep the unparsed code around
288     code = QString();
289     
290     if (!listener.isNull()) {
291       static_cast<Window*>(win.imp())->jsEventListeners.insert(listener.imp(), 
292                                                                (KJS::JSEventListener *)(this));
293     }
294     
295     parsed = true;
296   }
297 }
298
299 Value KJS::getNodeEventListener(DOM::Node n, int eventId)
300 {
301     DOM::EventListener *listener = n.handle()->getHTMLEventListener(eventId);
302     JSEventListener *jsListener = static_cast<JSEventListener*>(listener);
303     if ( jsListener && jsListener->listenerObjImp() )
304         return jsListener->listenerObj();
305     else
306         return Null();
307 }
308
309
310
311 // -------------------------------------------------------------------------
312
313 const ClassInfo EventConstructor::info = { "EventConstructor", 0, &EventConstructorTable, 0 };
314 /*
315 @begin EventConstructorTable 3
316   CAPTURING_PHASE       DOM::Event::CAPTURING_PHASE     DontDelete|ReadOnly
317   AT_TARGET             DOM::Event::AT_TARGET           DontDelete|ReadOnly
318   BUBBLING_PHASE        DOM::Event::BUBBLING_PHASE      DontDelete|ReadOnly
319 # Reverse-engineered from Netscape
320   MOUSEDOWN             1                               DontDelete|ReadOnly
321   MOUSEUP               2                               DontDelete|ReadOnly
322   MOUSEOVER             4                               DontDelete|ReadOnly
323   MOUSEOUT              8                               DontDelete|ReadOnly
324   MOUSEMOVE             16                              DontDelete|ReadOnly
325   MOUSEDRAG             32                              DontDelete|ReadOnly
326   CLICK                 64                              DontDelete|ReadOnly
327   DBLCLICK              128                             DontDelete|ReadOnly
328   KEYDOWN               256                             DontDelete|ReadOnly
329   KEYUP                 512                             DontDelete|ReadOnly
330   KEYPRESS              1024                            DontDelete|ReadOnly
331   DRAGDROP              2048                            DontDelete|ReadOnly
332   FOCUS                 4096                            DontDelete|ReadOnly
333   BLUR                  8192                            DontDelete|ReadOnly
334   SELECT                16384                           DontDelete|ReadOnly
335   CHANGE                32768                           DontDelete|ReadOnly
336 @end
337 */
338
339 Value EventConstructor::tryGet(ExecState *exec, const Identifier &p) const
340 {
341   return DOMObjectLookupGetValue<EventConstructor, DOMObject>(exec,p,&EventConstructorTable,this);
342 }
343
344 Value EventConstructor::getValueProperty(ExecState *, int token) const
345 {
346   // We use the token as the value to return directly
347   return Number(token);
348 }
349
350 Value KJS::getEventConstructor(ExecState *exec)
351 {
352   return cacheGlobalObject<EventConstructor>(exec, "[[event.constructor]]");
353 }
354
355 // -------------------------------------------------------------------------
356
357 const ClassInfo DOMEvent::info = { "Event", 0, &DOMEventTable, 0 };
358 /*
359 @begin DOMEventTable 12
360   type          DOMEvent::Type          DontDelete|ReadOnly
361   target        DOMEvent::Target        DontDelete|ReadOnly
362   currentTarget DOMEvent::CurrentTarget DontDelete|ReadOnly
363   srcElement    DOMEvent::SrcElement    DontDelete|ReadOnly
364   eventPhase    DOMEvent::EventPhase    DontDelete|ReadOnly
365   bubbles       DOMEvent::Bubbles       DontDelete|ReadOnly
366   cancelable    DOMEvent::Cancelable    DontDelete|ReadOnly
367   timeStamp     DOMEvent::TimeStamp     DontDelete|ReadOnly
368   returnValue   DOMEvent::ReturnValue   DontDelete
369   cancelBubble  DOMEvent::CancelBubble  DontDelete
370   dataTransfer  DOMMouseEvent::DataTransfer DontDelete|ReadOnly
371   clipboardData  DOMEvent::ClipboardData  DontDelete|ReadOnly
372 @end
373 @begin DOMEventProtoTable 3
374   stopPropagation       DOMEvent::StopPropagation       DontDelete|Function 0
375   preventDefault        DOMEvent::PreventDefault        DontDelete|Function 0
376   initEvent             DOMEvent::InitEvent             DontDelete|Function 3
377 @end
378 */
379 DEFINE_PROTOTYPE("DOMEvent", DOMEventProto)
380 IMPLEMENT_PROTOFUNC(DOMEventProtoFunc)
381 IMPLEMENT_PROTOTYPE(DOMEventProto, DOMEventProtoFunc)
382
383 DOMEvent::DOMEvent(ExecState *exec, DOM::Event e)
384   : DOMObject(DOMEventProto::self(exec)), event(e), clipboard(0) { }
385
386 DOMEvent::~DOMEvent()
387 {
388   ScriptInterpreter::forgetDOMObject(event.handle());
389 }
390
391 // pass marks through to JS objects we hold during garbage collection
392 void DOMMouseEvent::mark()
393 {
394     ObjectImp::mark();
395     if (clipboard && !clipboard->marked())
396         clipboard->mark();
397 }
398
399 Value DOMEvent::tryGet(ExecState *exec, const Identifier &p) const
400 {
401 #ifdef KJS_VERBOSE
402   kdDebug() << "KJS::DOMEvent::tryGet " << p.qstring() << endl;
403 #endif
404   return DOMObjectLookupGetValue<DOMEvent,DOMObject>(exec, p, &DOMEventTable, this );
405 }
406
407 Value DOMEvent::getValueProperty(ExecState *exec, int token) const
408 {
409   switch (token) {
410   case Type:
411     return String(event.type());
412   case Target:
413   case SrcElement: /*MSIE extension - "the object that fired the event"*/
414     return getDOMNode(exec,event.target());
415   case CurrentTarget:
416     return getDOMNode(exec,event.currentTarget());
417   case EventPhase:
418     return Number((unsigned int)event.eventPhase());
419   case Bubbles:
420     return Boolean(event.bubbles());
421   case CancelBubble:
422     return Boolean(event.getCancelBubble());
423   case ReturnValue:
424     return Boolean(!event.defaultPrevented());
425   case Cancelable:
426     return Boolean(event.cancelable());
427   case TimeStamp:
428     return Number((long unsigned int)event.timeStamp()); // ### long long ?
429   case ClipboardData:
430   {
431     DOM::EventImpl *ei = event.handle();
432     if (ei->isClipboardEvent()) {
433       DOM::ClipboardEventImpl *impl = static_cast<DOM::ClipboardEventImpl *>(event.handle());
434       if (!clipboard) {
435         clipboard = new Clipboard(exec, impl->clipboard());
436       }
437       return Object(clipboard);
438     } else {
439       return Undefined();
440     }
441   }
442   case DataTransfer:
443   {
444     DOM::EventImpl *ei = event.handle();
445     if (ei->isDragEvent()) {
446       DOM::MouseEventImpl *impl = static_cast<DOM::MouseEventImpl *>(event.handle());
447       if (!clipboard) {
448         clipboard = new Clipboard(exec, impl->clipboard());
449       }
450       return Object(clipboard);
451     } else {
452       return Undefined();
453     }
454   }
455   default:
456     kdWarning() << "Unhandled token in DOMEvent::getValueProperty : " << token << endl;
457     return Value();
458   }
459 }
460
461 void DOMEvent::tryPut(ExecState *exec, const Identifier &propertyName,
462                       const Value& value, int attr)
463 {
464   DOMObjectLookupPut<DOMEvent, DOMObject>(exec, propertyName, value, attr,
465                                           &DOMEventTable, this);
466 }
467
468 void DOMEvent::putValue(ExecState *exec, int token, const Value& value, int)
469 {
470   switch (token) {
471   case ReturnValue:
472     event.setDefaultPrevented(!value.toBoolean(exec));
473     break;
474   case CancelBubble:
475     event.setCancelBubble(value.toBoolean(exec));
476     break;
477   default:
478     break;
479   }
480 }
481
482 Value DOMEventProtoFunc::tryCall(ExecState *exec, Object & thisObj, const List &args)
483 {
484   if (!thisObj.inherits(&KJS::DOMEvent::info)) {
485     Object err = Error::create(exec,TypeError);
486     exec->setException(err);
487     return err;
488   }
489   DOM::Event event = static_cast<DOMEvent *>( thisObj.imp() )->toEvent();
490   switch (id) {
491     case DOMEvent::StopPropagation:
492       event.stopPropagation();
493     case DOMEvent::PreventDefault:
494       event.preventDefault();
495       return Undefined();
496     case DOMEvent::InitEvent:
497       event.initEvent(args[0].toString(exec).string(),args[1].toBoolean(exec),args[2].toBoolean(exec));
498       return Undefined();
499   };
500   return Undefined();
501 }
502
503 Value KJS::getDOMEvent(ExecState *exec, DOM::Event e)
504 {
505   DOM::EventImpl *ei = e.handle();
506   if (!ei)
507     return Null();
508   ScriptInterpreter* interp = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
509
510   KJS::Interpreter::lock();
511
512   DOMObject *ret = interp->getDOMObject(ei);
513   if (!ret) {
514     if (ei->isKeyboardEvent())
515       ret = new DOMKeyboardEvent(exec, e);
516     else if (ei->isMouseEvent())
517       ret = new DOMMouseEvent(exec, e);
518     else if (ei->isUIEvent())
519       ret = new DOMUIEvent(exec, e);
520     else if (ei->isMutationEvent())
521       ret = new DOMMutationEvent(exec, e);
522     else
523       ret = new DOMEvent(exec, e);
524
525     interp->putDOMObject(ei, ret);
526   }
527
528   KJS::Interpreter::unlock();
529
530   return Value(ret);
531 }
532
533 DOM::Event KJS::toEvent(const Value& val)
534 {
535   Object obj = Object::dynamicCast(val);
536   if (obj.isNull() || !obj.inherits(&DOMEvent::info))
537     return DOM::Event();
538
539   const DOMEvent *dobj = static_cast<const DOMEvent*>(obj.imp());
540   return dobj->toEvent();
541 }
542
543 // -------------------------------------------------------------------------
544
545
546 const ClassInfo EventExceptionConstructor::info = { "EventExceptionConstructor", 0, &EventExceptionConstructorTable, 0 };
547 /*
548 @begin EventExceptionConstructorTable 1
549   UNSPECIFIED_EVENT_TYPE_ERR    DOM::EventException::UNSPECIFIED_EVENT_TYPE_ERR DontDelete|ReadOnly
550 @end
551 */
552 Value EventExceptionConstructor::tryGet(ExecState *exec, const Identifier &p) const
553 {
554   return DOMObjectLookupGetValue<EventExceptionConstructor, DOMObject>(exec,p,&EventExceptionConstructorTable,this);
555 }
556
557 Value EventExceptionConstructor::getValueProperty(ExecState *, int token) const
558 {
559   // We use the token as the value to return directly
560   return Number(token);
561 }
562
563 Value KJS::getEventExceptionConstructor(ExecState *exec)
564 {
565   return cacheGlobalObject<EventExceptionConstructor>(exec, "[[eventException.constructor]]");
566 }
567
568 // -------------------------------------------------------------------------
569
570 const ClassInfo DOMUIEvent::info = { "UIEvent", &DOMEvent::info, &DOMUIEventTable, 0 };
571 /*
572 @begin DOMUIEventTable 8
573   view          DOMUIEvent::View        DontDelete|ReadOnly
574   detail        DOMUIEvent::Detail      DontDelete|ReadOnly
575   keyCode       DOMUIEvent::KeyCode     DontDelete|ReadOnly
576   charCode      DOMUIEvent::CharCode    DontDelete|ReadOnly
577   layerX        DOMUIEvent::LayerX      DontDelete|ReadOnly
578   layerY        DOMUIEvent::LayerY      DontDelete|ReadOnly
579   pageX         DOMUIEvent::PageX       DontDelete|ReadOnly
580   pageY         DOMUIEvent::PageY       DontDelete|ReadOnly
581   which         DOMUIEvent::Which       DontDelete|ReadOnly
582 @end
583 @begin DOMUIEventProtoTable 1
584   initUIEvent   DOMUIEvent::InitUIEvent DontDelete|Function 5
585 @end
586 */
587 DEFINE_PROTOTYPE("DOMUIEvent",DOMUIEventProto)
588 IMPLEMENT_PROTOFUNC(DOMUIEventProtoFunc)
589 IMPLEMENT_PROTOTYPE_WITH_PARENT(DOMUIEventProto,DOMUIEventProtoFunc,DOMEventProto)
590
591 DOMUIEvent::~DOMUIEvent()
592 {
593 }
594
595 Value DOMUIEvent::tryGet(ExecState *exec, const Identifier &p) const
596 {
597   return DOMObjectLookupGetValue<DOMUIEvent,DOMEvent>(exec,p,&DOMUIEventTable,this);
598 }
599
600 Value DOMUIEvent::getValueProperty(ExecState *exec, int token) const
601 {
602   switch (token) {
603   case View:
604     return getDOMAbstractView(exec,static_cast<DOM::UIEvent>(event).view());
605   case Detail:
606     return Number(static_cast<DOM::UIEvent>(event).detail());
607   case KeyCode:
608     return Number(static_cast<DOM::UIEvent>(event).keyCode());
609   case CharCode:
610     return Number(static_cast<DOM::UIEvent>(event).charCode());
611   case LayerX:
612     return Number(static_cast<DOM::UIEvent>(event).layerX());
613   case LayerY:
614     return Number(static_cast<DOM::UIEvent>(event).layerY());
615   case PageX:
616     return Number(static_cast<DOM::UIEvent>(event).pageX());
617   case PageY:
618     return Number(static_cast<DOM::UIEvent>(event).pageY());
619   case Which:
620     return Number(static_cast<DOM::UIEvent>(event).which());
621   default:
622     kdWarning() << "Unhandled token in DOMUIEvent::getValueProperty : " << token << endl;
623     return Undefined();
624   }
625 }
626
627 Value DOMUIEventProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
628 {
629   if (!thisObj.inherits(&KJS::DOMUIEvent::info)) {
630     Object err = Error::create(exec,TypeError);
631     exec->setException(err);
632     return err;
633   }
634   DOM::UIEvent uiEvent = static_cast<DOMUIEvent *>(thisObj.imp())->toUIEvent();
635   switch (id) {
636     case DOMUIEvent::InitUIEvent: {
637       DOM::AbstractView v = toAbstractView(args[3]);
638       static_cast<DOM::UIEvent>(uiEvent).initUIEvent(args[0].toString(exec).string(),
639                                                      args[1].toBoolean(exec),
640                                                      args[2].toBoolean(exec),
641                                                      v,
642                                                      args[4].toInt32(exec));
643       }
644       return Undefined();
645   }
646   return Undefined();
647 }
648
649 // -------------------------------------------------------------------------
650
651 const ClassInfo DOMMouseEvent::info = { "MouseEvent", &DOMUIEvent::info, &DOMMouseEventTable, 0 };
652
653 /*
654 @begin DOMMouseEventTable 16
655   screenX       DOMMouseEvent::ScreenX  DontDelete|ReadOnly
656   screenY       DOMMouseEvent::ScreenY  DontDelete|ReadOnly
657   clientX       DOMMouseEvent::ClientX  DontDelete|ReadOnly
658   x             DOMMouseEvent::X        DontDelete|ReadOnly
659   clientY       DOMMouseEvent::ClientY  DontDelete|ReadOnly
660   y             DOMMouseEvent::Y        DontDelete|ReadOnly
661   offsetX       DOMMouseEvent::OffsetX  DontDelete|ReadOnly
662   offsetY       DOMMouseEvent::OffsetY  DontDelete|ReadOnly
663   ctrlKey       DOMMouseEvent::CtrlKey  DontDelete|ReadOnly
664   shiftKey      DOMMouseEvent::ShiftKey DontDelete|ReadOnly
665   altKey        DOMMouseEvent::AltKey   DontDelete|ReadOnly
666   metaKey       DOMMouseEvent::MetaKey  DontDelete|ReadOnly
667   button        DOMMouseEvent::Button   DontDelete|ReadOnly
668   relatedTarget DOMMouseEvent::RelatedTarget DontDelete|ReadOnly
669   fromElement   DOMMouseEvent::FromElement DontDelete|ReadOnly
670   toElement     DOMMouseEvent::ToElement        DontDelete|ReadOnly
671 @end
672 @begin DOMMouseEventProtoTable 1
673   initMouseEvent        DOMMouseEvent::InitMouseEvent   DontDelete|Function 15
674 @end
675 */
676 DEFINE_PROTOTYPE("DOMMouseEvent",DOMMouseEventProto)
677 IMPLEMENT_PROTOFUNC(DOMMouseEventProtoFunc)
678 IMPLEMENT_PROTOTYPE_WITH_PARENT(DOMMouseEventProto,DOMMouseEventProtoFunc,DOMUIEventProto)
679
680 DOMMouseEvent::~DOMMouseEvent()
681 {
682 }
683
684 Value DOMMouseEvent::tryGet(ExecState *exec, const Identifier &p) const
685 {
686 #ifdef KJS_VERBOSE
687   kdDebug(6070) << "DOMMouseEvent::tryGet " << p.qstring() << endl;
688 #endif
689   return DOMObjectLookupGetValue<DOMMouseEvent,DOMUIEvent>(exec,p,&DOMMouseEventTable,this);
690 }
691
692 Value DOMMouseEvent::getValueProperty(ExecState *exec, int token) const
693 {
694   switch (token) {
695   case ScreenX:
696     return Number(static_cast<DOM::MouseEvent>(event).screenX());
697   case ScreenY:
698     return Number(static_cast<DOM::MouseEvent>(event).screenY());
699   case ClientX:
700   case X:
701     return Number(static_cast<DOM::MouseEvent>(event).clientX());
702   case ClientY:
703   case Y:
704     return Number(static_cast<DOM::MouseEvent>(event).clientY());
705   case OffsetX:
706   case OffsetY: // MSIE extension
707   {
708     DOM::Node node = event.target();
709     node.handle()->getDocument()->updateRendering();
710     khtml::RenderObject *rend = node.handle() ? node.handle()->renderer() : 0L;
711     int x = static_cast<DOM::MouseEvent>(event).clientX();
712     int y = static_cast<DOM::MouseEvent>(event).clientY();
713     if ( rend ) {
714       int xPos, yPos;
715       if ( rend->absolutePosition( xPos, yPos ) ) {
716         kdDebug() << "DOMMouseEvent::getValueProperty rend=" << rend << "  xPos=" << xPos << "  yPos=" << yPos << endl;
717         x -= xPos;
718         y -= yPos;
719       }
720     }
721     return Number( token == OffsetX ? x : y );
722   }
723   case CtrlKey:
724     return Boolean(static_cast<DOM::MouseEvent>(event).ctrlKey());
725   case ShiftKey:
726     return Boolean(static_cast<DOM::MouseEvent>(event).shiftKey());
727   case AltKey:
728     return Boolean(static_cast<DOM::MouseEvent>(event).altKey());
729   case MetaKey:
730     return Boolean(static_cast<DOM::MouseEvent>(event).metaKey());
731   case Button:
732   {
733     // Tricky. The DOM (and khtml) use 0 for LMB, 1 for MMB and 2 for RMB
734     // but MSIE uses 1=LMB, 2=RMB, 4=MMB, as a bitfield
735     int domButton = static_cast<DOM::MouseEvent>(event).button();
736     int button = domButton==0 ? 1 : domButton==1 ? 4 : domButton==2 ? 2 : 0;
737     return Number( (unsigned int)button );
738   }
739   case ToElement:
740     // MSIE extension - "the object toward which the user is moving the mouse pointer"
741     if (event.handle()->id() == DOM::EventImpl::MOUSEOUT_EVENT)
742       return getDOMNode(exec,static_cast<DOM::MouseEvent>(event).relatedTarget());
743     return getDOMNode(exec,static_cast<DOM::MouseEvent>(event).target());
744   case FromElement:
745     // MSIE extension - "object from which activation
746     // or the mouse pointer is exiting during the event" (huh?)
747     if (event.handle()->id() == DOM::EventImpl::MOUSEOUT_EVENT)
748       return getDOMNode(exec,static_cast<DOM::MouseEvent>(event).target());
749     /* fall through */
750   case RelatedTarget:
751     return getDOMNode(exec,static_cast<DOM::MouseEvent>(event).relatedTarget());
752   default:
753     kdWarning() << "Unhandled token in DOMMouseEvent::getValueProperty : " << token << endl;
754     return Value();
755   }
756 }
757
758 Value DOMMouseEventProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
759 {
760   if (!thisObj.inherits(&KJS::DOMMouseEvent::info)) {
761     Object err = Error::create(exec,TypeError);
762     exec->setException(err);
763     return err;
764   }
765   DOM::MouseEvent mouseEvent = static_cast<DOMMouseEvent *>(thisObj.imp())->toMouseEvent();
766   switch (id) {
767     case DOMMouseEvent::InitMouseEvent:
768       mouseEvent.initMouseEvent(args[0].toString(exec).string(), // typeArg
769                                 args[1].toBoolean(exec), // canBubbleArg
770                                 args[2].toBoolean(exec), // cancelableArg
771                                 toAbstractView(args[3]), // viewArg
772                                 args[4].toInt32(exec), // detailArg
773                                 args[5].toInt32(exec), // screenXArg
774                                 args[6].toInt32(exec), // screenYArg
775                                 args[7].toInt32(exec), // clientXArg
776                                 args[8].toInt32(exec), // clientYArg
777                                 args[9].toBoolean(exec), // ctrlKeyArg
778                                 args[10].toBoolean(exec), // altKeyArg
779                                 args[11].toBoolean(exec), // shiftKeyArg
780                                 args[12].toBoolean(exec), // metaKeyArg
781                                 args[13].toInt32(exec), // buttonArg
782                                 toNode(args[14])); // relatedTargetArg
783       return Undefined();
784   }
785   return Undefined();
786 }
787
788 // -------------------------------------------------------------------------
789
790 const ClassInfo DOMKeyboardEvent::info = { "KeyboardEvent", &DOMUIEvent::info, &DOMKeyboardEventTable, 0 };
791
792 /*
793 @begin DOMKeyboardEventTable 5
794   keyIdentifier DOMKeyboardEvent::KeyIdentifier DontDelete|ReadOnly
795   keyLocation   DOMKeyboardEvent::KeyLocation   DontDelete|ReadOnly
796   ctrlKey       DOMKeyboardEvent::CtrlKey       DontDelete|ReadOnly
797   shiftKey      DOMKeyboardEvent::ShiftKey      DontDelete|ReadOnly
798   altKey        DOMKeyboardEvent::AltKey        DontDelete|ReadOnly
799   metaKey       DOMKeyboardEvent::MetaKey       DontDelete|ReadOnly
800   altGraphKey   DOMKeyboardEvent::AltGraphKey   DontDelete|ReadOnly
801 @end
802 @begin DOMKeyboardEventProtoTable 1
803   initKeyboardEvent     DOMKeyboardEvent::InitKeyboardEvent     DontDelete|Function 11
804 @end
805 */
806 DEFINE_PROTOTYPE("DOMKeyboardEvent", DOMKeyboardEventProto)
807 IMPLEMENT_PROTOFUNC(DOMKeyboardEventProtoFunc)
808 IMPLEMENT_PROTOTYPE_WITH_PARENT(DOMKeyboardEventProto, DOMKeyboardEventProtoFunc, DOMUIEventProto)
809
810 DOMKeyboardEvent::~DOMKeyboardEvent()
811 {
812 }
813
814 const ClassInfo* DOMKeyboardEvent::classInfo() const
815 {
816     return &info;
817 }
818
819 Value DOMKeyboardEvent::tryGet(ExecState *exec, const Identifier &p) const
820 {
821 #ifdef KJS_VERBOSE
822   kdDebug(6070) << "DOMKeyboardEvent::tryGet " << p.qstring() << endl;
823 #endif
824   return DOMObjectLookupGetValue<DOMKeyboardEvent, DOMUIEvent>(exec, p, &DOMKeyboardEventTable, this);
825 }
826
827 Value DOMKeyboardEvent::getValueProperty(ExecState *exec, int token) const
828 {
829   switch (token) {
830   case KeyIdentifier:
831     return String(static_cast<KeyboardEvent>(event).keyIdentifier());
832   case KeyLocation:
833     return Number(static_cast<KeyboardEvent>(event).keyLocation());
834   case CtrlKey:
835     return Boolean(static_cast<KeyboardEvent>(event).ctrlKey());
836   case ShiftKey:
837     return Boolean(static_cast<KeyboardEvent>(event).shiftKey());
838   case AltKey:
839     return Boolean(static_cast<KeyboardEvent>(event).altKey());
840   case MetaKey:
841     return Boolean(static_cast<KeyboardEvent>(event).metaKey());
842   case AltGraphKey:
843     return Boolean(static_cast<KeyboardEvent>(event).altGraphKey());
844   default:
845     kdWarning() << "Unhandled token in DOMKeyboardEvent::getValueProperty : " << token << endl;
846     return Value();
847   }
848 }
849
850 Value DOMKeyboardEventProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
851 {
852   if (!thisObj.inherits(&DOMKeyboardEvent::info)) {
853     Object err = Error::create(exec,TypeError);
854     exec->setException(err);
855     return err;
856   }
857   KeyboardEvent event = static_cast<DOMKeyboardEvent *>(thisObj.imp())->toKeyboardEvent();
858   switch (id) {
859     case DOMKeyboardEvent::InitKeyboardEvent:
860       event.initKeyboardEvent(args[0].toString(exec).string(), // typeArg
861                               args[1].toBoolean(exec), // canBubbleArg
862                               args[2].toBoolean(exec), // cancelableArg
863                               toAbstractView(args[3]), // viewArg
864                               args[4].toString(exec).string(), // keyIdentifier
865                               args[5].toInt32(exec), // keyLocationArg
866                               args[6].toBoolean(exec), // ctrlKeyArg
867                               args[7].toBoolean(exec), // altKeyArg
868                               args[8].toBoolean(exec), // shiftKeyArg
869                               args[9].toBoolean(exec), // metaKeyArg
870                               args[10].toBoolean(exec)); // altGraphKeyArg
871       return Undefined();
872   }
873   return Undefined();
874 }
875
876 // -------------------------------------------------------------------------
877
878 const ClassInfo MutationEventConstructor::info = { "MutationEventConstructor", 0, &MutationEventConstructorTable, 0 };
879 /*
880 @begin MutationEventConstructorTable 3
881   MODIFICATION  DOM::MutationEvent::MODIFICATION        DontDelete|ReadOnly
882   ADDITION      DOM::MutationEvent::ADDITION            DontDelete|ReadOnly
883   REMOVAL       DOM::MutationEvent::REMOVAL             DontDelete|ReadOnly
884 @end
885 */
886 Value MutationEventConstructor::tryGet(ExecState *exec, const Identifier &p) const
887 {
888   return DOMObjectLookupGetValue<MutationEventConstructor,DOMObject>(exec,p,&MutationEventConstructorTable,this);
889 }
890
891 Value MutationEventConstructor::getValueProperty(ExecState *, int token) const
892 {
893   // We use the token as the value to return directly
894   return Number(token);
895 }
896
897 Value KJS::getMutationEventConstructor(ExecState *exec)
898 {
899   return cacheGlobalObject<MutationEventConstructor>(exec, "[[mutationEvent.constructor]]");
900 }
901
902 // -------------------------------------------------------------------------
903
904 const ClassInfo DOMMutationEvent::info = { "MutationEvent", &DOMEvent::info, &DOMMutationEventTable, 0 };
905 /*
906 @begin DOMMutationEventTable 5
907   relatedNode   DOMMutationEvent::RelatedNode   DontDelete|ReadOnly
908   prevValue     DOMMutationEvent::PrevValue     DontDelete|ReadOnly
909   newValue      DOMMutationEvent::NewValue      DontDelete|ReadOnly
910   attrName      DOMMutationEvent::AttrName      DontDelete|ReadOnly
911   attrChange    DOMMutationEvent::AttrChange    DontDelete|ReadOnly
912 @end
913 @begin DOMMutationEventProtoTable 1
914   initMutationEvent     DOMMutationEvent::InitMutationEvent     DontDelete|Function 8
915 @end
916 */
917 DEFINE_PROTOTYPE("DOMMutationEvent",DOMMutationEventProto)
918 IMPLEMENT_PROTOFUNC(DOMMutationEventProtoFunc)
919 IMPLEMENT_PROTOTYPE_WITH_PARENT(DOMMutationEventProto,DOMMutationEventProtoFunc,DOMEventProto)
920
921 DOMMutationEvent::~DOMMutationEvent()
922 {
923 }
924
925 Value DOMMutationEvent::tryGet(ExecState *exec, const Identifier &p) const
926 {
927   return DOMObjectLookupGetValue<DOMMutationEvent,DOMEvent>(exec,p,&DOMMutationEventTable,this);
928 }
929
930 Value DOMMutationEvent::getValueProperty(ExecState *exec, int token) const
931 {
932   switch (token) {
933   case RelatedNode:
934     return getDOMNode(exec,static_cast<DOM::MutationEvent>(event).relatedNode());
935   case PrevValue:
936     return String(static_cast<DOM::MutationEvent>(event).prevValue());
937   case NewValue:
938     return String(static_cast<DOM::MutationEvent>(event).newValue());
939   case AttrName:
940     return String(static_cast<DOM::MutationEvent>(event).attrName());
941   case AttrChange:
942     return Number((unsigned int)static_cast<DOM::MutationEvent>(event).attrChange());
943   default:
944     kdWarning() << "Unhandled token in DOMMutationEvent::getValueProperty : " << token << endl;
945     return Value();
946   }
947 }
948
949 Value DOMMutationEventProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
950 {
951   if (!thisObj.inherits(&KJS::DOMMutationEvent::info)) {
952     Object err = Error::create(exec,TypeError);
953     exec->setException(err);
954     return err;
955   }
956   DOM::MutationEvent mutationEvent = static_cast<DOMMutationEvent *>(thisObj.imp())->toMutationEvent();
957   switch (id) {
958     case DOMMutationEvent::InitMutationEvent:
959       mutationEvent.initMutationEvent(args[0].toString(exec).string(), // typeArg,
960                                       args[1].toBoolean(exec), // canBubbleArg
961                                       args[2].toBoolean(exec), // cancelableArg
962                                       toNode(args[3]), // relatedNodeArg
963                                       args[4].toString(exec).string(), // prevValueArg
964                                       args[5].toString(exec).string(), // newValueArg
965                                       args[6].toString(exec).string(), // attrNameArg
966                                       args[7].toInt32(exec)); // attrChangeArg
967       return Undefined();
968   }
969   return Undefined();
970 }
971
972 // -------------------------------------------------------------------------
973
974 const ClassInfo Clipboard::info = { "Clipboard", 0, &ClipboardTable, 0 };
975
976 /* Source for ClipboardTable. Use "make hashtables" to regenerate.
977 @begin ClipboardTable 3
978   dropEffect    Clipboard::DropEffect   DontDelete
979   effectAllowed Clipboard::EffectAllowed        DontDelete
980   types         Clipboard::Types        DontDelete|ReadOnly
981 @end
982 @begin ClipboardProtoTable 4
983   clearData     Clipboard::ClearData    DontDelete|Function 0
984   getData       Clipboard::GetData      DontDelete|Function 1
985   setData       Clipboard::SetData      DontDelete|Function 2
986   setDragImage  Clipboard::SetDragImage DontDelete|Function 3
987 @end
988 */
989
990 DEFINE_PROTOTYPE("Clipboard", ClipboardProto)
991 IMPLEMENT_PROTOFUNC(ClipboardProtoFunc)
992 IMPLEMENT_PROTOTYPE(ClipboardProto, ClipboardProtoFunc)
993
994 Clipboard::Clipboard(ExecState *exec, DOM::ClipboardImpl *cb)
995 : DOMObject(ClipboardProto::self(exec)), clipboard(cb)
996 {
997     if (clipboard)
998         clipboard->ref();
999 }
1000
1001 Clipboard::~Clipboard()
1002 {
1003     if (clipboard)
1004         clipboard->deref();
1005 }
1006
1007 static Value stringOrUndefined(const DOM::DOMString &str)
1008 {
1009     if (str.isNull()) {
1010         return Undefined();
1011     } else {
1012         return String(str);
1013     }
1014 }
1015
1016 Value Clipboard::tryGet(ExecState *exec, const Identifier &propertyName) const
1017 {
1018     return DOMObjectLookupGetValue<Clipboard,DOMObject>(exec, propertyName, &ClipboardTable, this);
1019 }
1020
1021 Value Clipboard::getValueProperty(ExecState *exec, int token) const
1022 {
1023     switch (token) {
1024         case DropEffect:
1025             assert(clipboard->isForDragging() || clipboard->dropEffect().isNull());
1026             return stringOrUndefined(clipboard->dropEffect());
1027         case EffectAllowed:
1028             assert(clipboard->isForDragging() || clipboard->effectAllowed().isNull());
1029             return stringOrUndefined(clipboard->effectAllowed());
1030         case Types:
1031         {
1032             QStringList qTypes = clipboard->types();
1033             if (qTypes.isEmpty()) {
1034                 return Null(); 
1035             } else {
1036                 List list;
1037                 for (QStringList::Iterator it = qTypes.begin(); it != qTypes.end(); ++it) {
1038                     list.append(String(UString(*it)));
1039                 }
1040                 return exec->lexicalInterpreter()->builtinArray().construct(exec, list);
1041             }
1042         }
1043         default:
1044             kdWarning() << "Clipboard::getValueProperty unhandled token " << token << endl;
1045             return Value();
1046     }
1047 }
1048
1049 void Clipboard::tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr)
1050 {
1051     DOMObjectLookupPut<Clipboard,DOMObject>(exec, propertyName, value, attr, &ClipboardTable, this );
1052 }
1053
1054 void Clipboard::putValue(ExecState *exec, int token, const Value& value, int /*attr*/)
1055 {
1056     switch (token) {
1057         case DropEffect:
1058             // can never set this when not for dragging, thus getting always returns NULL string
1059             if (clipboard->isForDragging())
1060                 clipboard->setDropEffect(value.toString(exec).string());
1061             break;
1062         case EffectAllowed:
1063             // can never set this when not for dragging, thus getting always returns NULL string
1064             if (clipboard->isForDragging())
1065                 clipboard->setEffectAllowed(value.toString(exec).string());
1066             break;
1067         default:
1068             kdWarning() << "Clipboard::putValue unhandled token " << token << endl;
1069     }
1070 }
1071
1072 Value ClipboardProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
1073 {
1074     if (!thisObj.inherits(&KJS::Clipboard::info)) {
1075         Object err = Error::create(exec,TypeError);
1076         exec->setException(err);
1077         return err;
1078     }
1079
1080     Clipboard *cb = static_cast<Clipboard *>(thisObj.imp());
1081     switch (id) {
1082         case Clipboard::ClearData:
1083             if (args.size() == 0) {
1084                 cb->clipboard->clearAllData();
1085                 return Undefined();
1086             } else if (args.size() == 1) {
1087                 cb->clipboard->clearData(args[0].toString(exec).string());
1088                 return Undefined();
1089             } else {
1090                 Object err = Error::create(exec,SyntaxError,"clearData: Invalid number of arguments");
1091                 exec->setException(err);
1092                 return err;
1093             }
1094         case Clipboard::GetData:
1095         {
1096             if (args.size() == 1) {
1097                 bool success;
1098                 DOM::DOMString result = cb->clipboard->getData(args[0].toString(exec).string(), success);
1099                 if (success) {
1100                     return String(result);
1101                 } else {
1102                     return Undefined();
1103                 }
1104             } else {
1105                 Object err = Error::create(exec,SyntaxError,"getData: Invalid number of arguments");
1106                 exec->setException(err);
1107                 return err;
1108             }
1109         }
1110         case Clipboard::SetData:
1111             if (args.size() == 2) {
1112                 return Boolean(cb->clipboard->setData(args[0].toString(exec).string(), args[1].toString(exec).string()));
1113             } else {
1114                 Object err = Error::create(exec,SyntaxError,"setData: Invalid number of arguments");
1115                 exec->setException(err);
1116                 return err;
1117             }
1118         case Clipboard::SetDragImage:
1119         {
1120             if (!cb->clipboard->isForDragging()) {
1121                 return Undefined();
1122             }
1123
1124             if (args.size() != 3) {
1125                 Object err = Error::create(exec, SyntaxError,"setDragImage: Invalid number of arguments");
1126                 exec->setException(err);
1127                 return err;
1128             }
1129
1130             int x = (int)args[1].toNumber(exec);
1131             int y = (int)args[2].toNumber(exec);
1132
1133             // See if they passed us a node
1134             DOM::Node node = toNode(args[0]);
1135             if (!node.isNull()) {
1136                 if (node.nodeType() == DOM::Node::ELEMENT_NODE) {
1137                     cb->clipboard->setDragImageElement(node, QPoint(x,y));                    
1138                     return Undefined();
1139                 } else {
1140                     Object err = Error::create(exec, SyntaxError,"setDragImageFromElement: Invalid first argument");
1141                     exec->setException(err);
1142                     return err;
1143                 }
1144             }
1145
1146             // See if they passed us an Image object
1147             ObjectImp *o = static_cast<ObjectImp*>(args[0].imp());
1148             if (o->inherits(&Image::info)) {
1149                 Image *JSImage = static_cast<Image*>(o);
1150                 cb->clipboard->setDragImage(JSImage->image()->pixmap(), QPoint(x,y));                
1151                 return Undefined();
1152             } else {
1153                 Object err = Error::create(exec,TypeError);
1154                 exec->setException(err);
1155                 return err;
1156             }
1157         }
1158     }
1159     return Undefined();
1160 }
1161