Sync with trunk r63174.
[reactos.git] / dll / win32 / mshtml / htmlevent.c
1 /*
2 * Copyright 2008-2009 Jacek Caban for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "mshtml_private.h"
20
21 typedef struct {
22 IDispatch *handler_prop;
23 DWORD handler_cnt;
24 IDispatch *handlers[0];
25 } handler_vector_t;
26
27 struct event_target_t {
28 handler_vector_t *event_table[EVENTID_LAST];
29 };
30
31 static const WCHAR abortW[] = {'a','b','o','r','t',0};
32 static const WCHAR onabortW[] = {'o','n','a','b','o','r','t',0};
33
34 static const WCHAR beforeunloadW[] = {'b','e','f','o','r','e','u','n','l','o','a','d',0};
35 static const WCHAR onbeforeunloadW[] = {'o','n','b','e','f','o','r','e','u','n','l','o','a','d',0};
36
37 static const WCHAR blurW[] = {'b','l','u','r',0};
38 static const WCHAR onblurW[] = {'o','n','b','l','u','r',0};
39
40 static const WCHAR changeW[] = {'c','h','a','n','g','e',0};
41 static const WCHAR onchangeW[] = {'o','n','c','h','a','n','g','e',0};
42
43 static const WCHAR clickW[] = {'c','l','i','c','k',0};
44 static const WCHAR onclickW[] = {'o','n','c','l','i','c','k',0};
45
46 static const WCHAR contextmenuW[] = {'c','o','n','t','e','x','t','m','e','n','u',0};
47 static const WCHAR oncontextmenuW[] = {'o','n','c','o','n','t','e','x','t','m','e','n','u',0};
48
49 static const WCHAR dataavailableW[] = {'d','a','t','a','a','v','a','i','l','a','b','l','e',0};
50 static const WCHAR ondataavailableW[] = {'o','n','d','a','t','a','a','v','a','i','l','a','b','l','e',0};
51
52 static const WCHAR dblclickW[] = {'d','b','l','c','l','i','c','k',0};
53 static const WCHAR ondblclickW[] = {'o','n','d','b','l','c','l','i','c','k',0};
54
55 static const WCHAR dragW[] = {'d','r','a','g',0};
56 static const WCHAR ondragW[] = {'o','n','d','r','a','g',0};
57
58 static const WCHAR dragstartW[] = {'d','r','a','g','s','t','a','r','t',0};
59 static const WCHAR ondragstartW[] = {'o','n','d','r','a','g','s','t','a','r','t',0};
60
61 static const WCHAR errorW[] = {'e','r','r','o','r',0};
62 static const WCHAR onerrorW[] = {'o','n','e','r','r','o','r',0};
63
64 static const WCHAR focusW[] = {'f','o','c','u','s',0};
65 static const WCHAR onfocusW[] = {'o','n','f','o','c','u','s',0};
66
67 static const WCHAR helpW[] = {'h','e','l','p',0};
68 static const WCHAR onhelpW[] = {'o','n','h','e','l','p',0};
69
70 static const WCHAR keydownW[] = {'k','e','y','d','o','w','n',0};
71 static const WCHAR onkeydownW[] = {'o','n','k','e','y','d','o','w','n',0};
72
73 static const WCHAR keypressW[] = {'k','e','y','p','r','e','s','s',0};
74 static const WCHAR onkeypressW[] = {'o','n','k','e','y','p','r','e','s','s',0};
75
76 static const WCHAR keyupW[] = {'k','e','y','u','p',0};
77 static const WCHAR onkeyupW[] = {'o','n','k','e','y','u','p',0};
78
79 static const WCHAR loadW[] = {'l','o','a','d',0};
80 static const WCHAR onloadW[] = {'o','n','l','o','a','d',0};
81
82 static const WCHAR mousedownW[] = {'m','o','u','s','e','d','o','w','n',0};
83 static const WCHAR onmousedownW[] = {'o','n','m','o','u','s','e','d','o','w','n',0};
84
85 static const WCHAR mousemoveW[] = {'m','o','u','s','e','m','o','v','e',0};
86 static const WCHAR onmousemoveW[] = {'o','n','m','o','u','s','e','m','o','v','e',0};
87
88 static const WCHAR mouseoutW[] = {'m','o','u','s','e','o','u','t',0};
89 static const WCHAR onmouseoutW[] = {'o','n','m','o','u','s','e','o','u','t',0};
90
91 static const WCHAR mouseoverW[] = {'m','o','u','s','e','o','v','e','r',0};
92 static const WCHAR onmouseoverW[] = {'o','n','m','o','u','s','e','o','v','e','r',0};
93
94 static const WCHAR mouseupW[] = {'m','o','u','s','e','u','p',0};
95 static const WCHAR onmouseupW[] = {'o','n','m','o','u','s','e','u','p',0};
96
97 static const WCHAR pasteW[] = {'p','a','s','t','e',0};
98 static const WCHAR onpasteW[] = {'o','n','p','a','s','t','e',0};
99
100 static const WCHAR readystatechangeW[] = {'r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};
101 static const WCHAR onreadystatechangeW[] = {'o','n','r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};
102
103 static const WCHAR resizeW[] = {'r','e','s','i','z','e',0};
104 static const WCHAR onresizeW[] = {'o','n','r','e','s','i','z','e',0};
105
106 static const WCHAR scrollW[] = {'s','c','r','o','l','l',0};
107 static const WCHAR onscrollW[] = {'o','n','s','c','r','o','l','l',0};
108
109 static const WCHAR selectstartW[] = {'s','e','l','e','c','t','s','t','a','r','t',0};
110 static const WCHAR onselectstartW[] = {'o','n','s','e','l','e','c','t','s','t','a','r','t',0};
111
112 static const WCHAR submitW[] = {'s','u','b','m','i','t',0};
113 static const WCHAR onsubmitW[] = {'o','n','s','u','b','m','i','t',0};
114
115 static const WCHAR HTMLEventsW[] = {'H','T','M','L','E','v','e','n','t','s',0};
116 static const WCHAR KeyboardEventW[] = {'K','e','y','b','o','a','r','d','E','v','e','n','t',0};
117 static const WCHAR MouseEventW[] = {'M','o','u','s','e','E','v','e','n','t',0};
118
119 enum {
120 EVENTT_NONE,
121 EVENTT_HTML,
122 EVENTT_KEY,
123 EVENTT_MOUSE
124 };
125
126 static const WCHAR *event_types[] = {
127 NULL,
128 HTMLEventsW,
129 KeyboardEventW,
130 MouseEventW
131 };
132
133 typedef struct {
134 LPCWSTR name;
135 LPCWSTR attr_name;
136 DWORD type;
137 DISPID dispid;
138 DWORD flags;
139 } event_info_t;
140
141 #define EVENT_DEFAULTLISTENER 0x0001
142 #define EVENT_BUBBLE 0x0002
143 #define EVENT_FORWARDBODY 0x0004
144 #define EVENT_BIND_TO_BODY 0x0008
145 #define EVENT_CANCELABLE 0x0010
146 #define EVENT_HASDEFAULTHANDLERS 0x0020
147
148 static const event_info_t event_info[] = {
149 {abortW, onabortW, EVENTT_NONE, DISPID_EVMETH_ONABORT,
150 EVENT_BIND_TO_BODY},
151 {beforeunloadW, onbeforeunloadW, EVENTT_NONE, DISPID_EVMETH_ONBEFOREUNLOAD,
152 EVENT_DEFAULTLISTENER|EVENT_FORWARDBODY},
153 {blurW, onblurW, EVENTT_HTML, DISPID_EVMETH_ONBLUR,
154 EVENT_DEFAULTLISTENER},
155 {changeW, onchangeW, EVENTT_HTML, DISPID_EVMETH_ONCHANGE,
156 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
157 {clickW, onclickW, EVENTT_MOUSE, DISPID_EVMETH_ONCLICK,
158 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE|EVENT_HASDEFAULTHANDLERS},
159 {contextmenuW, oncontextmenuW, EVENTT_MOUSE, DISPID_EVMETH_ONCONTEXTMENU,
160 EVENT_BUBBLE|EVENT_CANCELABLE},
161 {dataavailableW, ondataavailableW, EVENTT_NONE, DISPID_EVMETH_ONDATAAVAILABLE,
162 EVENT_BUBBLE},
163 {dblclickW, ondblclickW, EVENTT_MOUSE, DISPID_EVMETH_ONDBLCLICK,
164 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE},
165 {dragW, ondragW, EVENTT_MOUSE, DISPID_EVMETH_ONDRAG,
166 EVENT_CANCELABLE},
167 {dragstartW, ondragstartW, EVENTT_MOUSE, DISPID_EVMETH_ONDRAGSTART,
168 EVENT_CANCELABLE},
169 {errorW, onerrorW, EVENTT_NONE, DISPID_EVMETH_ONERROR,
170 EVENT_BIND_TO_BODY},
171 {focusW, onfocusW, EVENTT_HTML, DISPID_EVMETH_ONFOCUS,
172 EVENT_DEFAULTLISTENER},
173 {helpW, onhelpW, EVENTT_KEY, DISPID_EVMETH_ONHELP,
174 EVENT_BUBBLE|EVENT_CANCELABLE},
175 {keydownW, onkeydownW, EVENTT_KEY, DISPID_EVMETH_ONKEYDOWN,
176 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_HASDEFAULTHANDLERS},
177 {keypressW, onkeypressW, EVENTT_KEY, DISPID_EVMETH_ONKEYPRESS,
178 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
179 {keyupW, onkeyupW, EVENTT_KEY, DISPID_EVMETH_ONKEYUP,
180 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
181 {loadW, onloadW, EVENTT_HTML, DISPID_EVMETH_ONLOAD,
182 EVENT_BIND_TO_BODY},
183 {mousedownW, onmousedownW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEDOWN,
184 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
185 {mousemoveW, onmousemoveW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEMOVE,
186 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
187 {mouseoutW, onmouseoutW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEOUT,
188 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
189 {mouseoverW, onmouseoverW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEOVER,
190 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
191 {mouseupW, onmouseupW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEUP,
192 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
193 {pasteW, onpasteW, EVENTT_NONE, DISPID_EVMETH_ONPASTE,
194 EVENT_CANCELABLE},
195 {readystatechangeW, onreadystatechangeW, EVENTT_NONE, DISPID_EVMETH_ONREADYSTATECHANGE,
196 0},
197 {resizeW, onresizeW, EVENTT_NONE, DISPID_EVMETH_ONRESIZE,
198 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
199 {scrollW, onscrollW, EVENTT_HTML, DISPID_EVMETH_ONSCROLL,
200 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
201 {selectstartW, onselectstartW, EVENTT_MOUSE, DISPID_EVMETH_ONSELECTSTART,
202 EVENT_CANCELABLE},
203 {submitW, onsubmitW, EVENTT_HTML, DISPID_EVMETH_ONSUBMIT,
204 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE}
205 };
206
207 eventid_t str_to_eid(LPCWSTR str)
208 {
209 int i;
210
211 for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
212 if(!strcmpW(event_info[i].name, str))
213 return i;
214 }
215
216 ERR("unknown type %s\n", debugstr_w(str));
217 return EVENTID_LAST;
218 }
219
220 static eventid_t attr_to_eid(LPCWSTR str)
221 {
222 int i;
223
224 for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
225 if(!strcmpW(event_info[i].attr_name, str))
226 return i;
227 }
228
229 return EVENTID_LAST;
230 }
231
232 typedef struct {
233 DispatchEx dispex;
234 IHTMLEventObj IHTMLEventObj_iface;
235
236 LONG ref;
237
238 HTMLDOMNode *target;
239 const event_info_t *type;
240 nsIDOMEvent *nsevent;
241 BOOL prevent_default;
242 BOOL cancel_bubble;
243 } HTMLEventObj;
244
245 static inline HTMLEventObj *impl_from_IHTMLEventObj(IHTMLEventObj *iface)
246 {
247 return CONTAINING_RECORD(iface, HTMLEventObj, IHTMLEventObj_iface);
248 }
249
250 static HRESULT WINAPI HTMLEventObj_QueryInterface(IHTMLEventObj *iface, REFIID riid, void **ppv)
251 {
252 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
253
254 *ppv = NULL;
255
256 if(IsEqualGUID(&IID_IUnknown, riid)) {
257 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
258 *ppv = &This->IHTMLEventObj_iface;
259 }else if(IsEqualGUID(&IID_IHTMLEventObj, riid)) {
260 TRACE("(%p)->(IID_IHTMLEventObj %p)\n", This, ppv);
261 *ppv = &This->IHTMLEventObj_iface;
262 }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
263 return *ppv ? S_OK : E_NOINTERFACE;
264 }
265
266 if(*ppv) {
267 IUnknown_AddRef((IUnknown*)*ppv);
268 return S_OK;
269 }
270
271 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
272 return E_NOINTERFACE;
273 }
274
275 static ULONG WINAPI HTMLEventObj_AddRef(IHTMLEventObj *iface)
276 {
277 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
278 LONG ref = InterlockedIncrement(&This->ref);
279
280 TRACE("(%p) ref=%d\n", This, ref);
281
282 return ref;
283 }
284
285 static ULONG WINAPI HTMLEventObj_Release(IHTMLEventObj *iface)
286 {
287 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
288 LONG ref = InterlockedDecrement(&This->ref);
289
290 TRACE("(%p) ref=%d\n", This, ref);
291
292 if(!ref) {
293 if(This->target)
294 IHTMLDOMNode_Release(&This->target->IHTMLDOMNode_iface);
295 if(This->nsevent)
296 nsIDOMEvent_Release(This->nsevent);
297 release_dispex(&This->dispex);
298 heap_free(This);
299 }
300
301 return ref;
302 }
303
304 static HRESULT WINAPI HTMLEventObj_GetTypeInfoCount(IHTMLEventObj *iface, UINT *pctinfo)
305 {
306 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
307 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
308 }
309
310 static HRESULT WINAPI HTMLEventObj_GetTypeInfo(IHTMLEventObj *iface, UINT iTInfo,
311 LCID lcid, ITypeInfo **ppTInfo)
312 {
313 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
314 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
315 }
316
317 static HRESULT WINAPI HTMLEventObj_GetIDsOfNames(IHTMLEventObj *iface, REFIID riid,
318 LPOLESTR *rgszNames, UINT cNames,
319 LCID lcid, DISPID *rgDispId)
320 {
321 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
322 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
323 lcid, rgDispId);
324 }
325
326 static HRESULT WINAPI HTMLEventObj_Invoke(IHTMLEventObj *iface, DISPID dispIdMember,
327 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
328 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
329 {
330 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
331 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid,
332 wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
333 }
334
335 static HRESULT WINAPI HTMLEventObj_get_srcElement(IHTMLEventObj *iface, IHTMLElement **p)
336 {
337 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
338
339 TRACE("(%p)->(%p)\n", This, p);
340
341 *p = NULL;
342 if(This->target)
343 IHTMLDOMNode_QueryInterface(&This->target->IHTMLDOMNode_iface, &IID_IHTMLElement, (void**)p);
344 return S_OK;
345 }
346
347 static HRESULT WINAPI HTMLEventObj_get_altKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
348 {
349 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
350 cpp_bool ret = FALSE;
351
352 TRACE("(%p)->(%p)\n", This, p);
353
354 if(This->nsevent) {
355 nsIDOMKeyEvent *key_event;
356 nsresult nsres;
357
358 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
359 if(NS_SUCCEEDED(nsres)) {
360 nsIDOMKeyEvent_GetAltKey(key_event, &ret);
361 nsIDOMKeyEvent_Release(key_event);
362 }else {
363 nsIDOMMouseEvent *mouse_event;
364
365 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
366 if(NS_SUCCEEDED(nsres)) {
367 nsIDOMMouseEvent_GetAltKey(mouse_event, &ret);
368 nsIDOMMouseEvent_Release(mouse_event);
369 }
370 }
371 }
372
373 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
374 return S_OK;
375 }
376
377 static HRESULT WINAPI HTMLEventObj_get_ctrlKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
378 {
379 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
380 cpp_bool ret = FALSE;
381
382 TRACE("(%p)->(%p)\n", This, p);
383
384 if(This->nsevent) {
385 nsIDOMKeyEvent *key_event;
386 nsresult nsres;
387
388 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
389 if(NS_SUCCEEDED(nsres)) {
390 nsIDOMKeyEvent_GetCtrlKey(key_event, &ret);
391 nsIDOMKeyEvent_Release(key_event);
392 }else {
393 nsIDOMMouseEvent *mouse_event;
394
395 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
396 if(NS_SUCCEEDED(nsres)) {
397 nsIDOMMouseEvent_GetCtrlKey(mouse_event, &ret);
398 nsIDOMMouseEvent_Release(mouse_event);
399 }
400 }
401 }
402
403 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
404 return S_OK;
405 }
406
407 static HRESULT WINAPI HTMLEventObj_get_shiftKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
408 {
409 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
410 cpp_bool ret = FALSE;
411
412 TRACE("(%p)->(%p)\n", This, p);
413
414 if(This->nsevent) {
415 nsIDOMKeyEvent *key_event;
416 nsresult nsres;
417
418 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
419 if(NS_SUCCEEDED(nsres)) {
420 nsIDOMKeyEvent_GetShiftKey(key_event, &ret);
421 nsIDOMKeyEvent_Release(key_event);
422 }else {
423 nsIDOMMouseEvent *mouse_event;
424
425 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
426 if(NS_SUCCEEDED(nsres)) {
427 nsIDOMMouseEvent_GetShiftKey(mouse_event, &ret);
428 nsIDOMMouseEvent_Release(mouse_event);
429 }
430 }
431 }
432
433 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
434 return S_OK;
435 }
436
437 static HRESULT WINAPI HTMLEventObj_put_returnValue(IHTMLEventObj *iface, VARIANT v)
438 {
439 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
440
441 TRACE("(%p)->(%s)\n", This, debugstr_variant(&v));
442
443 if(V_VT(&v) != VT_BOOL) {
444 FIXME("unsupported value %s\n", debugstr_variant(&v));
445 return DISP_E_BADVARTYPE;
446 }
447
448 if(!V_BOOL(&v))
449 This->prevent_default = TRUE;
450 return S_OK;
451 }
452
453 static HRESULT WINAPI HTMLEventObj_get_returnValue(IHTMLEventObj *iface, VARIANT *p)
454 {
455 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
456
457 FIXME("(%p)->(%p)\n", This, p);
458
459 V_VT(p) = VT_EMPTY;
460 return S_OK;
461 }
462
463 static HRESULT WINAPI HTMLEventObj_put_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL v)
464 {
465 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
466
467 TRACE("(%p)->(%x)\n", This, v);
468
469 This->cancel_bubble = !!v;
470 return S_OK;
471 }
472
473 static HRESULT WINAPI HTMLEventObj_get_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL *p)
474 {
475 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
476
477 TRACE("(%p)->(%p)\n", This, p);
478
479 *p = This->cancel_bubble ? VARIANT_TRUE : VARIANT_FALSE;
480 return S_OK;
481 }
482
483 static HRESULT WINAPI HTMLEventObj_get_fromElement(IHTMLEventObj *iface, IHTMLElement **p)
484 {
485 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
486
487 FIXME("(%p)->(%p)\n", This, p);
488
489 *p = NULL;
490 return S_OK;
491 }
492
493 static HRESULT WINAPI HTMLEventObj_get_toElement(IHTMLEventObj *iface, IHTMLElement **p)
494 {
495 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
496
497 FIXME("(%p)->(%p)\n", This, p);
498
499 *p = NULL;
500 return S_OK;
501 }
502
503 static HRESULT WINAPI HTMLEventObj_put_keyCode(IHTMLEventObj *iface, LONG v)
504 {
505 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
506 FIXME("(%p)->(%d)\n", This, v);
507 return E_NOTIMPL;
508 }
509
510 static HRESULT WINAPI HTMLEventObj_get_keyCode(IHTMLEventObj *iface, LONG *p)
511 {
512 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
513 UINT32 key_code = 0;
514
515 TRACE("(%p)->(%p)\n", This, p);
516
517 if(This->nsevent) {
518 nsIDOMKeyEvent *key_event;
519 nsresult nsres;
520
521 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
522 if(NS_SUCCEEDED(nsres)) {
523 nsIDOMKeyEvent_GetKeyCode(key_event, &key_code);
524 nsIDOMKeyEvent_Release(key_event);
525 }
526 }
527
528 *p = key_code;
529 return S_OK;
530 }
531
532 static HRESULT WINAPI HTMLEventObj_get_button(IHTMLEventObj *iface, LONG *p)
533 {
534 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
535 UINT16 button = 0;
536
537 TRACE("(%p)->(%p)\n", This, p);
538
539 if(This->nsevent) {
540 nsIDOMMouseEvent *mouse_event;
541 nsresult nsres;
542
543 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
544 if(NS_SUCCEEDED(nsres)) {
545 nsIDOMMouseEvent_GetButton(mouse_event, &button);
546 nsIDOMMouseEvent_Release(mouse_event);
547 }
548 }
549
550 *p = button;
551 return S_OK;
552 }
553
554 static HRESULT WINAPI HTMLEventObj_get_type(IHTMLEventObj *iface, BSTR *p)
555 {
556 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
557
558 TRACE("(%p)->(%p)\n", This, p);
559
560 if(!This->type) {
561 *p = NULL;
562 return S_OK;
563 }
564
565 *p = SysAllocString(This->type->name);
566 return *p ? S_OK : E_OUTOFMEMORY;
567 }
568
569 static HRESULT WINAPI HTMLEventObj_get_qualifier(IHTMLEventObj *iface, BSTR *p)
570 {
571 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
572
573 FIXME("(%p)->(%p)\n", This, p);
574
575 *p = NULL;
576 return S_OK;
577 }
578
579 static HRESULT WINAPI HTMLEventObj_get_reason(IHTMLEventObj *iface, LONG *p)
580 {
581 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
582
583 FIXME("(%p)->(%p)\n", This, p);
584
585 *p = 0;
586 return S_OK;
587 }
588
589 static HRESULT WINAPI HTMLEventObj_get_x(IHTMLEventObj *iface, LONG *p)
590 {
591 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
592 LONG x = 0;
593
594 TRACE("(%p)->(%p)\n", This, p);
595
596 if(This->nsevent) {
597 nsIDOMUIEvent *ui_event;
598 nsresult nsres;
599
600 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
601 if(NS_SUCCEEDED(nsres)) {
602 /* NOTE: pageX is not exactly right here. */
603 nsres = nsIDOMUIEvent_GetPageX(ui_event, &x);
604 assert(nsres == NS_OK);
605 nsIDOMUIEvent_Release(ui_event);
606 }
607 }
608
609 *p = x;
610 return S_OK;
611 }
612
613 static HRESULT WINAPI HTMLEventObj_get_y(IHTMLEventObj *iface, LONG *p)
614 {
615 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
616 LONG y = 0;
617
618 TRACE("(%p)->(%p)\n", This, p);
619
620 if(This->nsevent) {
621 nsIDOMUIEvent *ui_event;
622 nsresult nsres;
623
624 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
625 if(NS_SUCCEEDED(nsres)) {
626 /* NOTE: pageY is not exactly right here. */
627 nsres = nsIDOMUIEvent_GetPageY(ui_event, &y);
628 assert(nsres == NS_OK);
629 nsIDOMUIEvent_Release(ui_event);
630 }
631 }
632
633 *p = y;
634 return S_OK;
635 }
636
637 static HRESULT WINAPI HTMLEventObj_get_clientX(IHTMLEventObj *iface, LONG *p)
638 {
639 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
640 LONG x = 0;
641
642 TRACE("(%p)->(%p)\n", This, p);
643
644 if(This->nsevent) {
645 nsIDOMMouseEvent *mouse_event;
646 nsresult nsres;
647
648 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
649 if(NS_SUCCEEDED(nsres)) {
650 nsIDOMMouseEvent_GetClientX(mouse_event, &x);
651 nsIDOMMouseEvent_Release(mouse_event);
652 }
653 }
654
655 *p = x;
656 return S_OK;
657 }
658
659 static HRESULT WINAPI HTMLEventObj_get_clientY(IHTMLEventObj *iface, LONG *p)
660 {
661 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
662 LONG y = 0;
663
664 TRACE("(%p)->(%p)\n", This, p);
665
666 if(This->nsevent) {
667 nsIDOMMouseEvent *mouse_event;
668 nsresult nsres;
669
670 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
671 if(NS_SUCCEEDED(nsres)) {
672 nsIDOMMouseEvent_GetClientY(mouse_event, &y);
673 nsIDOMMouseEvent_Release(mouse_event);
674 }
675 }
676
677 *p = y;
678 return S_OK;
679 }
680
681 static HRESULT WINAPI HTMLEventObj_get_offsetX(IHTMLEventObj *iface, LONG *p)
682 {
683 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
684
685 FIXME("(%p)->(%p)\n", This, p);
686
687 *p = 0;
688 return S_OK;
689 }
690
691 static HRESULT WINAPI HTMLEventObj_get_offsetY(IHTMLEventObj *iface, LONG *p)
692 {
693 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
694
695 FIXME("(%p)->(%p)\n", This, p);
696
697 *p = 0;
698 return S_OK;
699 }
700
701 static HRESULT WINAPI HTMLEventObj_get_screenX(IHTMLEventObj *iface, LONG *p)
702 {
703 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
704 LONG x = 0;
705
706 TRACE("(%p)->(%p)\n", This, p);
707
708 if(This->nsevent) {
709 nsIDOMMouseEvent *mouse_event;
710 nsresult nsres;
711
712 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
713 if(NS_SUCCEEDED(nsres)) {
714 nsIDOMMouseEvent_GetScreenX(mouse_event, &x);
715 nsIDOMMouseEvent_Release(mouse_event);
716 }
717 }
718
719 *p = x;
720 return S_OK;
721 }
722
723 static HRESULT WINAPI HTMLEventObj_get_screenY(IHTMLEventObj *iface, LONG *p)
724 {
725 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
726 LONG y = 0;
727
728 TRACE("(%p)->(%p)\n", This, p);
729
730 if(This->nsevent) {
731 nsIDOMMouseEvent *mouse_event;
732 nsresult nsres;
733
734 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
735 if(NS_SUCCEEDED(nsres)) {
736 nsIDOMMouseEvent_GetScreenY(mouse_event, &y);
737 nsIDOMMouseEvent_Release(mouse_event);
738 }
739 }
740
741 *p = y;
742 return S_OK;
743 }
744
745 static HRESULT WINAPI HTMLEventObj_get_srcFilter(IHTMLEventObj *iface, IDispatch **p)
746 {
747 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
748
749 FIXME("(%p)->(%p)\n", This, p);
750
751 *p = NULL;
752 return S_OK;
753 }
754
755 static const IHTMLEventObjVtbl HTMLEventObjVtbl = {
756 HTMLEventObj_QueryInterface,
757 HTMLEventObj_AddRef,
758 HTMLEventObj_Release,
759 HTMLEventObj_GetTypeInfoCount,
760 HTMLEventObj_GetTypeInfo,
761 HTMLEventObj_GetIDsOfNames,
762 HTMLEventObj_Invoke,
763 HTMLEventObj_get_srcElement,
764 HTMLEventObj_get_altKey,
765 HTMLEventObj_get_ctrlKey,
766 HTMLEventObj_get_shiftKey,
767 HTMLEventObj_put_returnValue,
768 HTMLEventObj_get_returnValue,
769 HTMLEventObj_put_cancelBubble,
770 HTMLEventObj_get_cancelBubble,
771 HTMLEventObj_get_fromElement,
772 HTMLEventObj_get_toElement,
773 HTMLEventObj_put_keyCode,
774 HTMLEventObj_get_keyCode,
775 HTMLEventObj_get_button,
776 HTMLEventObj_get_type,
777 HTMLEventObj_get_qualifier,
778 HTMLEventObj_get_reason,
779 HTMLEventObj_get_x,
780 HTMLEventObj_get_y,
781 HTMLEventObj_get_clientX,
782 HTMLEventObj_get_clientY,
783 HTMLEventObj_get_offsetX,
784 HTMLEventObj_get_offsetY,
785 HTMLEventObj_get_screenX,
786 HTMLEventObj_get_screenY,
787 HTMLEventObj_get_srcFilter
788 };
789
790 static inline HTMLEventObj *unsafe_impl_from_IHTMLEventObj(IHTMLEventObj *iface)
791 {
792 return iface->lpVtbl == &HTMLEventObjVtbl ? impl_from_IHTMLEventObj(iface) : NULL;
793 }
794
795 static const tid_t HTMLEventObj_iface_tids[] = {
796 IHTMLEventObj_tid,
797 0
798 };
799
800 static dispex_static_data_t HTMLEventObj_dispex = {
801 NULL,
802 DispCEventObj_tid,
803 NULL,
804 HTMLEventObj_iface_tids
805 };
806
807 static HTMLEventObj *create_event(void)
808 {
809 HTMLEventObj *ret;
810
811 ret = heap_alloc_zero(sizeof(*ret));
812 if(!ret)
813 return NULL;
814
815 ret->IHTMLEventObj_iface.lpVtbl = &HTMLEventObjVtbl;
816 ret->ref = 1;
817
818 init_dispex(&ret->dispex, (IUnknown*)&ret->IHTMLEventObj_iface, &HTMLEventObj_dispex);
819
820 return ret;
821 }
822
823 static HRESULT set_event_info(HTMLEventObj *event, HTMLDOMNode *target, eventid_t eid, nsIDOMEvent *nsevent)
824 {
825 event->type = event_info+eid;
826 event->nsevent = nsevent;
827
828 if(nsevent) {
829 nsIDOMEvent_AddRef(nsevent);
830 }else if(event_types[event_info[eid].type]) {
831 nsAString type_str;
832 nsresult nsres;
833
834 nsAString_InitDepend(&type_str, event_types[event_info[eid].type]);
835 nsres = nsIDOMHTMLDocument_CreateEvent(target->doc->nsdoc, &type_str, &event->nsevent);
836 nsAString_Finish(&type_str);
837 if(NS_FAILED(nsres)) {
838 ERR("Could not create event: %08x\n", nsres);
839 return E_FAIL;
840 }
841 }
842
843 event->target = target;
844 if(target)
845 IHTMLDOMNode_AddRef(&target->IHTMLDOMNode_iface);
846 return S_OK;
847 }
848
849 HRESULT create_event_obj(IHTMLEventObj **ret)
850 {
851 HTMLEventObj *event;
852
853 event = create_event();
854 if(!event)
855 return E_OUTOFMEMORY;
856
857 *ret = &event->IHTMLEventObj_iface;
858 return S_OK;
859 }
860
861 static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv)
862 {
863 IDispatchEx *dispex;
864 EXCEPINFO ei;
865 HRESULT hres;
866
867 memset(&ei, 0, sizeof(ei));
868
869 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
870 if(SUCCEEDED(hres)) {
871 hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, dp, retv, &ei, NULL);
872 IDispatchEx_Release(dispex);
873 }else {
874 TRACE("Could not get IDispatchEx interface: %08x\n", hres);
875 hres = IDispatch_Invoke(disp, 0, &IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD,
876 dp, retv, &ei, NULL);
877 }
878
879 return hres;
880 }
881
882 static HRESULT call_cp_func(IDispatch *disp, DISPID dispid, HTMLEventObj *event_obj, VARIANT *retv)
883 {
884 DISPPARAMS dp = {NULL,NULL,0,0};
885 VARIANT event_arg;
886 ULONG argerr;
887 EXCEPINFO ei;
888
889 if(event_obj) {
890 V_VT(&event_arg) = VT_DISPATCH;
891 V_DISPATCH(&event_arg) = (IDispatch*)&event_obj->IHTMLEventObj_iface;
892 dp.rgvarg = &event_arg;
893 dp.cArgs = 1;
894 }
895
896 memset(&ei, 0, sizeof(ei));
897 return IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_METHOD, &dp, retv, &ei, &argerr);
898 }
899
900 static BOOL is_cp_event(cp_static_data_t *data, DISPID dispid)
901 {
902 int min, max, i;
903 HRESULT hres;
904
905 if(!data)
906 return FALSE;
907
908 if(!data->ids) {
909 hres = get_dispids(data->tid, &data->id_cnt, &data->ids);
910 if(FAILED(hres))
911 return FALSE;
912 }
913
914 min = 0;
915 max = data->id_cnt-1;
916 while(min <= max) {
917 i = (min+max)/2;
918 if(data->ids[i] == dispid)
919 return TRUE;
920
921 if(data->ids[i] < dispid)
922 min = i+1;
923 else
924 max = i-1;
925 }
926
927 return FALSE;
928 }
929
930 static void call_event_handlers(HTMLDocumentNode *doc, HTMLEventObj *event_obj, event_target_t *event_target,
931 ConnectionPointContainer *cp_container, eventid_t eid, IDispatch *this_obj)
932 {
933 const BOOL cancelable = event_info[eid].flags & EVENT_CANCELABLE;
934 handler_vector_t *handler_vector = NULL;
935 VARIANT v;
936 HRESULT hres;
937
938 if(event_target)
939 handler_vector = event_target->event_table[eid];
940
941 if(handler_vector && handler_vector->handler_prop) {
942 DISPID named_arg = DISPID_THIS;
943 VARIANTARG arg;
944 DISPPARAMS dp = {&arg, &named_arg, 1, 1};
945
946 V_VT(&arg) = VT_DISPATCH;
947 V_DISPATCH(&arg) = this_obj;
948 V_VT(&v) = VT_EMPTY;
949
950 TRACE("%s >>>\n", debugstr_w(event_info[eid].name));
951 hres = call_disp_func(handler_vector->handler_prop, &dp, &v);
952 if(hres == S_OK) {
953 TRACE("%s <<< %s\n", debugstr_w(event_info[eid].name), debugstr_variant(&v));
954
955 if(cancelable) {
956 if(V_VT(&v) == VT_BOOL) {
957 if(!V_BOOL(&v))
958 event_obj->prevent_default = TRUE;
959 }else if(V_VT(&v) != VT_EMPTY) {
960 FIXME("unhandled result %s\n", debugstr_variant(&v));
961 }
962 }
963 VariantClear(&v);
964 }else {
965 WARN("%s <<< %08x\n", debugstr_w(event_info[eid].name), hres);
966 }
967 }
968
969 if(handler_vector && handler_vector->handler_cnt) {
970 VARIANTARG arg;
971 DISPPARAMS dp = {&arg, NULL, 1, 0};
972 int i;
973
974 V_VT(&arg) = VT_DISPATCH;
975 V_DISPATCH(&arg) = (IDispatch*)event_obj;
976
977 i = handler_vector->handler_cnt;
978 while(i--) {
979 if(handler_vector->handlers[i]) {
980 V_VT(&v) = VT_EMPTY;
981
982 TRACE("%s [%d] >>>\n", debugstr_w(event_info[eid].name), i);
983 hres = call_disp_func(handler_vector->handlers[i], &dp, &v);
984 if(hres == S_OK) {
985 TRACE("%s [%d] <<<\n", debugstr_w(event_info[eid].name), i);
986
987 if(cancelable) {
988 if(V_VT(&v) == VT_BOOL) {
989 if(!V_BOOL(&v))
990 event_obj->prevent_default = TRUE;
991 }else if(V_VT(&v) != VT_EMPTY) {
992 FIXME("unhandled result %s\n", debugstr_variant(&v));
993 }
994 }
995 VariantClear(&v);
996 }else {
997 WARN("%s [%d] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
998 }
999 }
1000 }
1001 }
1002
1003 /*
1004 * NOTE: CP events may require doc_obj reference, which we don't own. We make sure that
1005 * it's safe to call event handler by checking nsevent_listener, which is NULL for
1006 * detached documents.
1007 */
1008 if(cp_container && cp_container->forward_container)
1009 cp_container = cp_container->forward_container;
1010 if(cp_container && cp_container->cps && doc->nsevent_listener) {
1011 ConnectionPoint *cp;
1012 unsigned i, j;
1013
1014 for(j=0; cp_container->cp_entries[j].riid; j++) {
1015 cp = cp_container->cps + j;
1016 if(!cp->sinks_size || !is_cp_event(cp->data, event_info[eid].dispid))
1017 continue;
1018
1019 for(i=0; doc->nsevent_listener && i < cp->sinks_size; i++) {
1020 if(!cp->sinks[i].disp)
1021 continue;
1022
1023 V_VT(&v) = VT_EMPTY;
1024
1025 TRACE("cp %s [%u] >>>\n", debugstr_w(event_info[eid].name), i);
1026 hres = call_cp_func(cp->sinks[i].disp, event_info[eid].dispid,
1027 cp->data->pass_event_arg ? event_obj : NULL, &v);
1028 if(hres == S_OK) {
1029 TRACE("cp %s [%u] <<<\n", debugstr_w(event_info[eid].name), i);
1030
1031 if(cancelable) {
1032 if(V_VT(&v) == VT_BOOL) {
1033 if(!V_BOOL(&v))
1034 event_obj->prevent_default = TRUE;
1035 }else if(V_VT(&v) != VT_EMPTY) {
1036 FIXME("unhandled result %s\n", debugstr_variant(&v));
1037 }
1038 }
1039 VariantClear(&v);
1040 }else {
1041 WARN("cp %s [%u] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
1042 }
1043 }
1044
1045 if(!doc->nsevent_listener)
1046 break;
1047 }
1048 }
1049 }
1050
1051 static void fire_event_obj(HTMLDocumentNode *doc, eventid_t eid, HTMLEventObj *event_obj,
1052 nsIDOMNode *target, IDispatch *script_this)
1053 {
1054 IHTMLEventObj *prev_event;
1055 nsIDOMNode *parent, *nsnode;
1056 BOOL prevent_default = FALSE;
1057 HTMLInnerWindow *window;
1058 HTMLDOMNode *node;
1059 UINT16 node_type;
1060 nsresult nsres;
1061 HRESULT hres;
1062
1063 TRACE("(%p) %s\n", doc, debugstr_w(event_info[eid].name));
1064
1065 window = doc->window;
1066 if(!window) {
1067 WARN("NULL window\n");
1068 return;
1069 }
1070
1071 htmldoc_addref(&doc->basedoc);
1072
1073 prev_event = window->event;
1074 window->event = event_obj ? &event_obj->IHTMLEventObj_iface : NULL;
1075
1076 nsIDOMNode_GetNodeType(target, &node_type);
1077 nsnode = target;
1078 nsIDOMNode_AddRef(nsnode);
1079
1080 switch(node_type) {
1081 case ELEMENT_NODE:
1082 do {
1083 hres = get_node(doc, nsnode, FALSE, &node);
1084 if(SUCCEEDED(hres) && node) {
1085 call_event_handlers(doc, event_obj, *get_node_event_target(node),
1086 node->cp_container, eid, script_this ? script_this : (IDispatch*)&node->IHTMLDOMNode_iface);
1087 node_release(node);
1088 }
1089
1090 if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1091 break;
1092
1093 nsIDOMNode_GetParentNode(nsnode, &parent);
1094 nsIDOMNode_Release(nsnode);
1095 nsnode = parent;
1096 if(!nsnode)
1097 break;
1098
1099 nsIDOMNode_GetNodeType(nsnode, &node_type);
1100 }while(node_type == ELEMENT_NODE);
1101
1102 if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1103 break;
1104
1105 case DOCUMENT_NODE:
1106 if(event_info[eid].flags & EVENT_FORWARDBODY) {
1107 nsIDOMHTMLElement *nsbody;
1108 nsresult nsres;
1109
1110 nsres = nsIDOMHTMLDocument_GetBody(doc->nsdoc, &nsbody);
1111 if(NS_SUCCEEDED(nsres) && nsbody) {
1112 hres = get_node(doc, (nsIDOMNode*)nsbody, FALSE, &node);
1113 if(SUCCEEDED(hres) && node) {
1114 call_event_handlers(doc, event_obj, *get_node_event_target(node),
1115 node->cp_container, eid, script_this ? script_this : (IDispatch*)&node->IHTMLDOMNode_iface);
1116 node_release(node);
1117 }
1118 nsIDOMHTMLElement_Release(nsbody);
1119 }else {
1120 ERR("Could not get body: %08x\n", nsres);
1121 }
1122 }
1123
1124 call_event_handlers(doc, event_obj, doc->node.event_target, &doc->basedoc.cp_container, eid,
1125 script_this ? script_this : (IDispatch*)&doc->basedoc.IHTMLDocument2_iface);
1126 break;
1127
1128 default:
1129 FIXME("unimplemented node type %d\n", node_type);
1130 }
1131
1132 if(nsnode)
1133 nsIDOMNode_Release(nsnode);
1134
1135 if(event_obj && event_obj->prevent_default)
1136 prevent_default = TRUE;
1137 window->event = prev_event;
1138
1139 if(!prevent_default && (event_info[eid].flags & EVENT_HASDEFAULTHANDLERS)) {
1140 nsIDOMNode_AddRef(target);
1141 nsnode = target;
1142
1143 do {
1144 hres = get_node(doc, nsnode, TRUE, &node);
1145 if(FAILED(hres))
1146 break;
1147
1148 if(node) {
1149 if(node->vtbl->handle_event)
1150 hres = node->vtbl->handle_event(node, eid, event_obj ? event_obj->nsevent : NULL, &prevent_default);
1151 node_release(node);
1152 if(FAILED(hres) || prevent_default || (event_obj && event_obj->cancel_bubble))
1153 break;
1154 }
1155
1156 nsres = nsIDOMNode_GetParentNode(nsnode, &parent);
1157 if(NS_FAILED(nsres))
1158 break;
1159
1160 nsIDOMNode_Release(nsnode);
1161 nsnode = parent;
1162 } while(nsnode);
1163
1164 if(nsnode)
1165 nsIDOMNode_Release(nsnode);
1166 }
1167
1168 if(prevent_default && event_obj && event_obj->nsevent) {
1169 TRACE("calling PreventDefault\n");
1170 nsIDOMEvent_PreventDefault(event_obj->nsevent);
1171 }
1172
1173 htmldoc_release(&doc->basedoc);
1174 }
1175
1176 void fire_event(HTMLDocumentNode *doc, eventid_t eid, BOOL set_event, nsIDOMNode *target, nsIDOMEvent *nsevent,
1177 IDispatch *script_this)
1178 {
1179 HTMLEventObj *event_obj = NULL;
1180 HTMLDOMNode *node;
1181 HRESULT hres;
1182
1183 if(set_event) {
1184 hres = get_node(doc, target, TRUE, &node);
1185 if(FAILED(hres))
1186 return;
1187
1188 event_obj = create_event();
1189 node_release(node);
1190 if(!event_obj)
1191 return;
1192
1193 hres = set_event_info(event_obj, node, eid, nsevent);
1194 if(FAILED(hres)) {
1195 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1196 return;
1197 }
1198 }
1199
1200 fire_event_obj(doc, eid, event_obj, target, script_this);
1201
1202 if(event_obj)
1203 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1204 }
1205
1206 HRESULT dispatch_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_var, VARIANT_BOOL *cancelled)
1207 {
1208 HTMLEventObj *event_obj = NULL;
1209 eventid_t eid;
1210 HRESULT hres;
1211
1212 eid = attr_to_eid(event_name);
1213 if(eid == EVENTID_LAST) {
1214 WARN("unknown event %s\n", debugstr_w(event_name));
1215 return E_INVALIDARG;
1216 }
1217
1218 if(event_var && V_VT(event_var) != VT_EMPTY && V_VT(event_var) != VT_ERROR) {
1219 if(V_VT(event_var) != VT_DISPATCH) {
1220 FIXME("event_var %s not supported\n", debugstr_variant(event_var));
1221 return E_NOTIMPL;
1222 }
1223
1224 if(V_DISPATCH(event_var)) {
1225 IHTMLEventObj *event_iface;
1226
1227 hres = IDispatch_QueryInterface(V_DISPATCH(event_var), &IID_IHTMLEventObj, (void**)&event_iface);
1228 if(FAILED(hres)) {
1229 FIXME("No IHTMLEventObj iface\n");
1230 return hres;
1231 }
1232
1233 event_obj = unsafe_impl_from_IHTMLEventObj(event_iface);
1234 if(!event_obj) {
1235 ERR("Not our IHTMLEventObj?\n");
1236 IHTMLEventObj_Release(event_iface);
1237 return E_FAIL;
1238 }
1239 }
1240 }
1241
1242 if(event_obj) {
1243 hres = set_event_info(event_obj, node, eid, NULL);
1244 if(SUCCEEDED(hres))
1245 fire_event_obj(node->doc, eid, event_obj, node->nsnode, NULL);
1246
1247 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1248 if(FAILED(hres))
1249 return hres;
1250 }else {
1251 if(!(event_info[eid].flags & EVENT_DEFAULTLISTENER)) {
1252 FIXME("not EVENT_DEFAULTEVENTHANDLER\n");
1253 return E_NOTIMPL;
1254 }
1255
1256 fire_event(node->doc, eid, TRUE, node->nsnode, NULL, NULL);
1257 }
1258
1259 *cancelled = VARIANT_TRUE; /* FIXME */
1260 return S_OK;
1261 }
1262
1263 HRESULT call_fire_event(HTMLDOMNode *node, eventid_t eid)
1264 {
1265 HRESULT hres;
1266
1267 if(node->vtbl->fire_event) {
1268 BOOL handled = FALSE;
1269
1270 hres = node->vtbl->fire_event(node, eid, &handled);
1271 if(handled)
1272 return hres;
1273 }
1274
1275 fire_event(node->doc, eid, TRUE, node->nsnode, NULL, NULL);
1276 return S_OK;
1277 }
1278
1279 static inline event_target_t *get_event_target(event_target_t **event_target_ptr)
1280 {
1281 if(!*event_target_ptr)
1282 *event_target_ptr = heap_alloc_zero(sizeof(event_target_t));
1283 return *event_target_ptr;
1284 }
1285
1286 static BOOL alloc_handler_vector(event_target_t *event_target, eventid_t eid, int cnt)
1287 {
1288 handler_vector_t *new_vector, *handler_vector = event_target->event_table[eid];
1289
1290 if(handler_vector) {
1291 if(cnt <= handler_vector->handler_cnt)
1292 return TRUE;
1293
1294 new_vector = heap_realloc_zero(handler_vector, sizeof(handler_vector_t) + sizeof(IDispatch*)*cnt);
1295 }else {
1296 new_vector = heap_alloc_zero(sizeof(handler_vector_t) + sizeof(IDispatch*)*cnt);
1297 }
1298
1299 if(!new_vector)
1300 return FALSE;
1301
1302 new_vector->handler_cnt = cnt;
1303 event_target->event_table[eid] = new_vector;
1304 return TRUE;
1305 }
1306
1307 static HRESULT ensure_nsevent_handler(HTMLDocumentNode *doc, event_target_t *event_target, eventid_t eid)
1308 {
1309 nsIDOMNode *nsnode = NULL;
1310
1311 TRACE("%s\n", debugstr_w(event_info[eid].name));
1312
1313 if(!doc->nsdoc || doc->event_vector[eid] || !(event_info[eid].flags & (EVENT_DEFAULTLISTENER|EVENT_BIND_TO_BODY)))
1314 return S_OK;
1315
1316 if(event_info[eid].flags & EVENT_BIND_TO_BODY) {
1317 nsIDOMHTMLElement *nsbody;
1318 nsresult nsres;
1319
1320 nsres = nsIDOMHTMLDocument_GetBody(doc->nsdoc, &nsbody);
1321 if(NS_SUCCEEDED(nsres) && nsbody) {
1322 nsnode = (nsIDOMNode*)nsbody;
1323 }else {
1324 ERR("GetBody failed: %08x\n", nsres);
1325 return E_UNEXPECTED;
1326 }
1327 }
1328
1329 doc->event_vector[eid] = TRUE;
1330 add_nsevent_listener(doc, nsnode, event_info[eid].name);
1331
1332 if(nsnode)
1333 nsIDOMNode_Release(nsnode);
1334 return S_OK;
1335 }
1336
1337 void detach_events(HTMLDocumentNode *doc)
1338 {
1339 if(doc->event_vector) {
1340 int i;
1341
1342 for(i=0; i < EVENTID_LAST; i++) {
1343 if(doc->event_vector[i]) {
1344 detach_nsevent(doc, event_info[i].name);
1345 doc->event_vector[i] = FALSE;
1346 }
1347 }
1348 }
1349
1350 release_nsevents(doc);
1351 }
1352
1353
1354 static HRESULT remove_event_handler(event_target_t **event_target, eventid_t eid)
1355 {
1356 if(*event_target && (*event_target)->event_table[eid] && (*event_target)->event_table[eid]->handler_prop) {
1357 IDispatch_Release((*event_target)->event_table[eid]->handler_prop);
1358 (*event_target)->event_table[eid]->handler_prop = NULL;
1359 }
1360
1361 return S_OK;
1362 }
1363
1364 static HRESULT set_event_handler_disp(event_target_t **event_target_ptr, HTMLDocumentNode *doc,
1365 eventid_t eid, IDispatch *disp)
1366 {
1367 event_target_t *event_target;
1368
1369 if(!disp)
1370 return remove_event_handler(event_target_ptr, eid);
1371
1372 event_target = get_event_target(event_target_ptr);
1373 if(!event_target)
1374 return E_OUTOFMEMORY;
1375
1376 if(!alloc_handler_vector(event_target, eid, 0))
1377 return E_OUTOFMEMORY;
1378
1379 if(event_target->event_table[eid]->handler_prop)
1380 IDispatch_Release(event_target->event_table[eid]->handler_prop);
1381
1382 event_target->event_table[eid]->handler_prop = disp;
1383 IDispatch_AddRef(disp);
1384
1385 return ensure_nsevent_handler(doc, event_target, eid);
1386 }
1387
1388 HRESULT set_event_handler(event_target_t **event_target, HTMLDocumentNode *doc, eventid_t eid, VARIANT *var)
1389 {
1390 switch(V_VT(var)) {
1391 case VT_NULL:
1392 return remove_event_handler(event_target, eid);
1393
1394 case VT_DISPATCH:
1395 return set_event_handler_disp(event_target, doc, eid, V_DISPATCH(var));
1396
1397 default:
1398 FIXME("not handler %s\n", debugstr_variant(var));
1399 /* fall through */
1400 case VT_EMPTY:
1401 return E_NOTIMPL;
1402 }
1403
1404 return S_OK;
1405 }
1406
1407 HRESULT get_event_handler(event_target_t **event_target, eventid_t eid, VARIANT *var)
1408 {
1409 if(*event_target && (*event_target)->event_table[eid] && (*event_target)->event_table[eid]->handler_prop) {
1410 V_VT(var) = VT_DISPATCH;
1411 V_DISPATCH(var) = (*event_target)->event_table[eid]->handler_prop;
1412 IDispatch_AddRef(V_DISPATCH(var));
1413 }else {
1414 V_VT(var) = VT_NULL;
1415 }
1416
1417 return S_OK;
1418 }
1419
1420 HRESULT attach_event(event_target_t **event_target_ptr, HTMLDocument *doc, BSTR name,
1421 IDispatch *disp, VARIANT_BOOL *res)
1422 {
1423 event_target_t *event_target;
1424 eventid_t eid;
1425 DWORD i = 0;
1426
1427 eid = attr_to_eid(name);
1428 if(eid == EVENTID_LAST) {
1429 WARN("Unknown event\n");
1430 *res = VARIANT_TRUE;
1431 return S_OK;
1432 }
1433
1434 event_target = get_event_target(event_target_ptr);
1435 if(!event_target)
1436 return E_OUTOFMEMORY;
1437
1438 if(event_target->event_table[eid]) {
1439 while(i < event_target->event_table[eid]->handler_cnt && event_target->event_table[eid]->handlers[i])
1440 i++;
1441 if(i == event_target->event_table[eid]->handler_cnt && !alloc_handler_vector(event_target, eid, i+1))
1442 return E_OUTOFMEMORY;
1443 }else if(!alloc_handler_vector(event_target, eid, i+1)) {
1444 return E_OUTOFMEMORY;
1445 }
1446
1447 IDispatch_AddRef(disp);
1448 event_target->event_table[eid]->handlers[i] = disp;
1449
1450 *res = VARIANT_TRUE;
1451 return ensure_nsevent_handler(doc->doc_node, event_target, eid);
1452 }
1453
1454 HRESULT detach_event(event_target_t *event_target, HTMLDocument *doc, BSTR name, IDispatch *disp)
1455 {
1456 eventid_t eid;
1457 DWORD i = 0;
1458
1459 if(!event_target)
1460 return S_OK;
1461
1462 eid = attr_to_eid(name);
1463 if(eid == EVENTID_LAST) {
1464 WARN("Unknown event\n");
1465 return S_OK;
1466 }
1467
1468 if(!event_target->event_table[eid])
1469 return S_OK;
1470
1471 while(i < event_target->event_table[eid]->handler_cnt) {
1472 if(event_target->event_table[eid]->handlers[i] == disp) {
1473 IDispatch_Release(event_target->event_table[eid]->handlers[i]);
1474 event_target->event_table[eid]->handlers[i] = NULL;
1475 }
1476 i++;
1477 }
1478
1479 return S_OK;
1480 }
1481
1482 void bind_node_event(HTMLDocumentNode *doc, event_target_t **event_target, HTMLDOMNode *node, const WCHAR *event, IDispatch *disp)
1483 {
1484 eventid_t eid;
1485
1486 TRACE("(%p %p %p %s %p)\n", doc, event_target, node, debugstr_w(event), disp);
1487
1488 eid = attr_to_eid(event);
1489 if(eid == EVENTID_LAST) {
1490 WARN("Unsupported event %s\n", debugstr_w(event));
1491 return;
1492 }
1493
1494 set_event_handler_disp(event_target, doc, eid, disp);
1495 }
1496
1497 void update_cp_events(HTMLInnerWindow *window, event_target_t **event_target_ptr, cp_static_data_t *cp)
1498 {
1499 event_target_t *event_target;
1500 int i;
1501
1502 event_target = get_event_target(event_target_ptr);
1503 if(!event_target)
1504 return; /* FIXME */
1505
1506 for(i=0; i < EVENTID_LAST; i++) {
1507 if((event_info[i].flags & EVENT_DEFAULTLISTENER) && is_cp_event(cp, event_info[i].dispid))
1508 ensure_nsevent_handler(window->doc, event_target, i);
1509 }
1510 }
1511
1512 void check_event_attr(HTMLDocumentNode *doc, nsIDOMElement *nselem)
1513 {
1514 const PRUnichar *attr_value;
1515 nsAString attr_name_str, attr_value_str;
1516 IDispatch *disp;
1517 HTMLDOMNode *node;
1518 int i;
1519 nsresult nsres;
1520 HRESULT hres;
1521
1522 nsAString_Init(&attr_value_str, NULL);
1523 nsAString_Init(&attr_name_str, NULL);
1524
1525 for(i=0; i < EVENTID_LAST; i++) {
1526 nsAString_SetData(&attr_name_str, event_info[i].attr_name);
1527 nsres = nsIDOMElement_GetAttribute(nselem, &attr_name_str, &attr_value_str);
1528 if(NS_SUCCEEDED(nsres)) {
1529 nsAString_GetData(&attr_value_str, &attr_value);
1530 if(!*attr_value)
1531 continue;
1532
1533 TRACE("%p.%s = %s\n", nselem, debugstr_w(event_info[i].attr_name), debugstr_w(attr_value));
1534
1535 disp = script_parse_event(doc->window, attr_value);
1536 if(disp) {
1537 hres = get_node(doc, (nsIDOMNode*)nselem, TRUE, &node);
1538 if(SUCCEEDED(hres)) {
1539 set_event_handler_disp(get_node_event_target(node), node->doc, i, disp);
1540 node_release(node);
1541 }
1542 IDispatch_Release(disp);
1543 }
1544 }
1545 }
1546
1547 nsAString_Finish(&attr_value_str);
1548 nsAString_Finish(&attr_name_str);
1549 }
1550
1551 HRESULT doc_init_events(HTMLDocumentNode *doc)
1552 {
1553 unsigned i;
1554 HRESULT hres;
1555
1556 doc->event_vector = heap_alloc_zero(EVENTID_LAST*sizeof(BOOL));
1557 if(!doc->event_vector)
1558 return E_OUTOFMEMORY;
1559
1560 init_nsevents(doc);
1561
1562 for(i=0; i < EVENTID_LAST; i++) {
1563 if(event_info[i].flags & EVENT_HASDEFAULTHANDLERS) {
1564 hres = ensure_nsevent_handler(doc, NULL, i);
1565 if(FAILED(hres))
1566 return hres;
1567 }
1568 }
1569
1570 return S_OK;
1571 }
1572
1573 void release_event_target(event_target_t *event_target)
1574 {
1575 int i;
1576 unsigned int j;
1577
1578 for(i=0; i < EVENTID_LAST; i++) {
1579 if(event_target->event_table[i]) {
1580 if(event_target->event_table[i]->handler_prop)
1581 IDispatch_Release(event_target->event_table[i]->handler_prop);
1582 for(j=0; j < event_target->event_table[i]->handler_cnt; j++)
1583 if(event_target->event_table[i]->handlers[j])
1584 IDispatch_Release(event_target->event_table[i]->handlers[j]);
1585 }
1586 }
1587
1588 heap_free(event_target);
1589 }