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