htmlevent.c 51.1 KB
Newer Older
1
/*
2
 * Copyright 2008-2009 Jacek Caban for CodeWeavers
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
20
#include <assert.h>
21 22 23 24 25 26 27

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "ole2.h"
28
#include "mshtmdid.h"
29 30

#include "mshtml_private.h"
31
#include "htmlevent.h"
32
#include "htmlscript.h"
33 34 35 36 37

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(mshtml);

38 39 40 41 42 43
typedef struct {
    IDispatch *handler_prop;
    DWORD handler_cnt;
    IDispatch *handlers[0];
} handler_vector_t;

44
struct event_target_t {
45
    handler_vector_t *event_table[EVENTID_LAST];
46 47
};

48 49 50
static const WCHAR abortW[] = {'a','b','o','r','t',0};
static const WCHAR onabortW[] = {'o','n','a','b','o','r','t',0};

51 52 53
static const WCHAR beforeunloadW[] = {'b','e','f','o','r','e','u','n','l','o','a','d',0};
static const WCHAR onbeforeunloadW[] = {'o','n','b','e','f','o','r','e','u','n','l','o','a','d',0};

54 55 56
static const WCHAR blurW[] = {'b','l','u','r',0};
static const WCHAR onblurW[] = {'o','n','b','l','u','r',0};

57 58 59
static const WCHAR changeW[] = {'c','h','a','n','g','e',0};
static const WCHAR onchangeW[] = {'o','n','c','h','a','n','g','e',0};

60 61 62
static const WCHAR clickW[] = {'c','l','i','c','k',0};
static const WCHAR onclickW[] = {'o','n','c','l','i','c','k',0};

63 64 65
static const WCHAR contextmenuW[] = {'c','o','n','t','e','x','t','m','e','n','u',0};
static const WCHAR oncontextmenuW[] = {'o','n','c','o','n','t','e','x','t','m','e','n','u',0};

66 67 68
static const WCHAR dataavailableW[] = {'d','a','t','a','a','v','a','i','l','a','b','l','e',0};
static const WCHAR ondataavailableW[] = {'o','n','d','a','t','a','a','v','a','i','l','a','b','l','e',0};

69 70 71
static const WCHAR dblclickW[] = {'d','b','l','c','l','i','c','k',0};
static const WCHAR ondblclickW[] = {'o','n','d','b','l','c','l','i','c','k',0};

72 73 74
static const WCHAR dragW[] = {'d','r','a','g',0};
static const WCHAR ondragW[] = {'o','n','d','r','a','g',0};

75 76 77
static const WCHAR dragstartW[] = {'d','r','a','g','s','t','a','r','t',0};
static const WCHAR ondragstartW[] = {'o','n','d','r','a','g','s','t','a','r','t',0};

78 79 80
static const WCHAR errorW[] = {'e','r','r','o','r',0};
static const WCHAR onerrorW[] = {'o','n','e','r','r','o','r',0};

81 82 83
static const WCHAR focusW[] = {'f','o','c','u','s',0};
static const WCHAR onfocusW[] = {'o','n','f','o','c','u','s',0};

84 85 86
static const WCHAR focusinW[] = {'f','o','c','u','s','i','n',0};
static const WCHAR onfocusinW[] = {'o','n','f','o','c','u','s','i','n',0};

87 88 89
static const WCHAR focusoutW[] = {'f','o','c','u','s','o','u','t',0};
static const WCHAR onfocusoutW[] = {'o','n','f','o','c','u','s','o','u','t',0};

90 91 92
static const WCHAR helpW[] = {'h','e','l','p',0};
static const WCHAR onhelpW[] = {'o','n','h','e','l','p',0};

93 94 95
static const WCHAR keydownW[] = {'k','e','y','d','o','w','n',0};
static const WCHAR onkeydownW[] = {'o','n','k','e','y','d','o','w','n',0};

96 97 98
static const WCHAR keypressW[] = {'k','e','y','p','r','e','s','s',0};
static const WCHAR onkeypressW[] = {'o','n','k','e','y','p','r','e','s','s',0};

99 100 101
static const WCHAR keyupW[] = {'k','e','y','u','p',0};
static const WCHAR onkeyupW[] = {'o','n','k','e','y','u','p',0};

102 103 104
static const WCHAR loadW[] = {'l','o','a','d',0};
static const WCHAR onloadW[] = {'o','n','l','o','a','d',0};

105 106 107
static const WCHAR messageW[] = {'m','e','s','s','a','g','e',0};
static const WCHAR onmessageW[] = {'o','n','m','e','s','s','a','g','e',0};

108 109 110
static const WCHAR mousedownW[] = {'m','o','u','s','e','d','o','w','n',0};
static const WCHAR onmousedownW[] = {'o','n','m','o','u','s','e','d','o','w','n',0};

111 112 113
static const WCHAR mousemoveW[] = {'m','o','u','s','e','m','o','v','e',0};
static const WCHAR onmousemoveW[] = {'o','n','m','o','u','s','e','m','o','v','e',0};

114 115 116
static const WCHAR mouseoutW[] = {'m','o','u','s','e','o','u','t',0};
static const WCHAR onmouseoutW[] = {'o','n','m','o','u','s','e','o','u','t',0};

117 118 119
static const WCHAR mouseoverW[] = {'m','o','u','s','e','o','v','e','r',0};
static const WCHAR onmouseoverW[] = {'o','n','m','o','u','s','e','o','v','e','r',0};

120 121 122
static const WCHAR mouseupW[] = {'m','o','u','s','e','u','p',0};
static const WCHAR onmouseupW[] = {'o','n','m','o','u','s','e','u','p',0};

123 124 125
static const WCHAR mousewheelW[] = {'m','o','u','s','e','w','h','e','e','l',0};
static const WCHAR onmousewheelW[] = {'o','n','m','o','u','s','e','w','h','e','e','l',0};

126 127 128
static const WCHAR pasteW[] = {'p','a','s','t','e',0};
static const WCHAR onpasteW[] = {'o','n','p','a','s','t','e',0};

129 130 131
static const WCHAR readystatechangeW[] = {'r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};
static const WCHAR onreadystatechangeW[] = {'o','n','r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};

132 133 134
static const WCHAR resizeW[] = {'r','e','s','i','z','e',0};
static const WCHAR onresizeW[] = {'o','n','r','e','s','i','z','e',0};

135 136 137
static const WCHAR scrollW[] = {'s','c','r','o','l','l',0};
static const WCHAR onscrollW[] = {'o','n','s','c','r','o','l','l',0};

138 139 140
static const WCHAR selectstartW[] = {'s','e','l','e','c','t','s','t','a','r','t',0};
static const WCHAR onselectstartW[] = {'o','n','s','e','l','e','c','t','s','t','a','r','t',0};

141 142 143
static const WCHAR submitW[] = {'s','u','b','m','i','t',0};
static const WCHAR onsubmitW[] = {'o','n','s','u','b','m','i','t',0};

144 145 146
static const WCHAR unloadW[] = {'u','n','l','o','a','d',0};
static const WCHAR onunloadW[] = {'o','n','u','n','l','o','a','d',0};

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
static const WCHAR HTMLEventsW[] = {'H','T','M','L','E','v','e','n','t','s',0};
static const WCHAR KeyboardEventW[] = {'K','e','y','b','o','a','r','d','E','v','e','n','t',0};
static const WCHAR MouseEventW[] = {'M','o','u','s','e','E','v','e','n','t',0};

enum {
    EVENTT_NONE,
    EVENTT_HTML,
    EVENTT_KEY,
    EVENTT_MOUSE
};

static const WCHAR *event_types[] = {
    NULL,
    HTMLEventsW,
    KeyboardEventW,
    MouseEventW
};

165 166 167
typedef struct {
    LPCWSTR name;
    LPCWSTR attr_name;
168
    DWORD type;
169
    DISPID dispid;
170
    DWORD flags;
171 172
} event_info_t;

173
#define EVENT_DEFAULTLISTENER    0x0001
174
#define EVENT_BUBBLE             0x0002
175
#define EVENT_FORWARDBODY        0x0004
176
#define EVENT_BIND_TO_BODY       0x0008
177
#define EVENT_CANCELABLE         0x0010
178
#define EVENT_HASDEFAULTHANDLERS 0x0020
179
#define EVENT_FIXME              0x0040
180

181
static const event_info_t event_info[] = {
182
    {abortW,             onabortW,             EVENTT_NONE,   DISPID_EVMETH_ONABORT,
183
        EVENT_BIND_TO_BODY},
184 185 186 187 188 189 190
    {beforeunloadW,      onbeforeunloadW,      EVENTT_NONE,   DISPID_EVMETH_ONBEFOREUNLOAD,
        EVENT_DEFAULTLISTENER|EVENT_FORWARDBODY},
    {blurW,              onblurW,              EVENTT_HTML,   DISPID_EVMETH_ONBLUR,
        EVENT_DEFAULTLISTENER},
    {changeW,            onchangeW,            EVENTT_HTML,   DISPID_EVMETH_ONCHANGE,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
    {clickW,             onclickW,             EVENTT_MOUSE,  DISPID_EVMETH_ONCLICK,
191
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE|EVENT_HASDEFAULTHANDLERS},
192
    {contextmenuW,       oncontextmenuW,       EVENTT_MOUSE,  DISPID_EVMETH_ONCONTEXTMENU,
193
        EVENT_BUBBLE|EVENT_CANCELABLE},
194 195
    {dataavailableW,     ondataavailableW,     EVENTT_NONE,   DISPID_EVMETH_ONDATAAVAILABLE,
        EVENT_BUBBLE},
196
    {dblclickW,          ondblclickW,          EVENTT_MOUSE,  DISPID_EVMETH_ONDBLCLICK,
197
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE},
198
    {dragW,              ondragW,              EVENTT_MOUSE,  DISPID_EVMETH_ONDRAG,
199
        EVENT_FIXME|EVENT_CANCELABLE},
200
    {dragstartW,         ondragstartW,         EVENTT_MOUSE,  DISPID_EVMETH_ONDRAGSTART,
201
        EVENT_FIXME|EVENT_CANCELABLE},
202
    {errorW,             onerrorW,             EVENTT_NONE,   DISPID_EVMETH_ONERROR,
203
        EVENT_BIND_TO_BODY},
204 205
    {focusW,             onfocusW,             EVENTT_HTML,   DISPID_EVMETH_ONFOCUS,
        EVENT_DEFAULTLISTENER},
206 207
    {focusinW,           onfocusinW,           EVENTT_HTML,   DISPID_EVMETH_ONFOCUSIN,
        EVENT_BUBBLE},
208 209
    {focusoutW,          onfocusoutW,          EVENTT_HTML,   DISPID_EVMETH_ONFOCUSOUT,
        EVENT_BUBBLE},
210 211
    {helpW,              onhelpW,              EVENTT_KEY,    DISPID_EVMETH_ONHELP,
        EVENT_BUBBLE|EVENT_CANCELABLE},
212
    {keydownW,           onkeydownW,           EVENTT_KEY,    DISPID_EVMETH_ONKEYDOWN,
213
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_HASDEFAULTHANDLERS},
214 215
    {keypressW,          onkeypressW,          EVENTT_KEY,    DISPID_EVMETH_ONKEYPRESS,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
216 217 218
    {keyupW,             onkeyupW,             EVENTT_KEY,    DISPID_EVMETH_ONKEYUP,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
    {loadW,              onloadW,              EVENTT_HTML,   DISPID_EVMETH_ONLOAD,
219
        EVENT_BIND_TO_BODY},
220 221
    {messageW,           onmessageW,           EVENTT_NONE,   DISPID_EVMETH_ONMESSAGE,
        EVENT_FORWARDBODY /* FIXME: remove when we get the target right */ },
222
    {mousedownW,         onmousedownW,         EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEDOWN,
223
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE},
224 225
    {mousemoveW,         onmousemoveW,         EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEMOVE,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
226 227 228 229 230 231
    {mouseoutW,          onmouseoutW,          EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEOUT,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
    {mouseoverW,         onmouseoverW,         EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEOVER,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
    {mouseupW,           onmouseupW,           EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEUP,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
232
    {mousewheelW,        onmousewheelW,        EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEWHEEL,
233
        EVENT_FIXME},
234
    {pasteW,             onpasteW,             EVENTT_NONE,   DISPID_EVMETH_ONPASTE,
235
        EVENT_CANCELABLE},
236 237
    {readystatechangeW,  onreadystatechangeW,  EVENTT_NONE,   DISPID_EVMETH_ONREADYSTATECHANGE,
        0},
238 239
    {resizeW,            onresizeW,            EVENTT_NONE,   DISPID_EVMETH_ONRESIZE,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
240 241
    {scrollW,            onscrollW,            EVENTT_HTML,   DISPID_EVMETH_ONSCROLL,
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
242
    {selectstartW,       onselectstartW,       EVENTT_MOUSE,  DISPID_EVMETH_ONSELECTSTART,
243
        EVENT_CANCELABLE},
244
    {submitW,            onsubmitW,            EVENTT_HTML,   DISPID_EVMETH_ONSUBMIT,
245 246 247
        EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE|EVENT_HASDEFAULTHANDLERS},
    {unloadW,            onunloadW,            EVENTT_HTML,   DISPID_EVMETH_ONUNLOAD,
        EVENT_FIXME}
248 249
};

250 251 252 253 254 255 256 257 258 259 260 261 262
eventid_t str_to_eid(LPCWSTR str)
{
    int i;

    for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
        if(!strcmpW(event_info[i].name, str))
            return i;
    }

    ERR("unknown type %s\n", debugstr_w(str));
    return EVENTID_LAST;
}

263 264 265 266 267 268 269 270 271 272 273 274
static eventid_t attr_to_eid(LPCWSTR str)
{
    int i;

    for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
        if(!strcmpW(event_info[i].attr_name, str))
            return i;
    }

    return EVENTID_LAST;
}

275
struct HTMLEventObj {
276
    DispatchEx dispex;
277
    IHTMLEventObj IHTMLEventObj_iface;
278

279
    LONG ref;
280 281

    HTMLDOMNode *target;
282
    const event_info_t *type;
283
    nsIDOMEvent *nsevent;
284
    VARIANT return_value;
285
    BOOL prevent_default;
286
    BOOL cancel_bubble;
287
};
288

289 290 291 292
static inline HTMLEventObj *impl_from_IHTMLEventObj(IHTMLEventObj *iface)
{
    return CONTAINING_RECORD(iface, HTMLEventObj, IHTMLEventObj_iface);
}
293 294 295

static HRESULT WINAPI HTMLEventObj_QueryInterface(IHTMLEventObj *iface, REFIID riid, void **ppv)
{
296
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
297

298
    TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
299 300

    if(IsEqualGUID(&IID_IUnknown, riid)) {
301
        *ppv = &This->IHTMLEventObj_iface;
302
    }else if(IsEqualGUID(&IID_IHTMLEventObj, riid)) {
303
        *ppv = &This->IHTMLEventObj_iface;
304 305
    }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
        return *ppv ? S_OK : E_NOINTERFACE;
306 307 308 309
    }else {
        *ppv = NULL;
        WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
        return E_NOINTERFACE;
310 311
    }

312 313
    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
314 315 316 317
}

static ULONG WINAPI HTMLEventObj_AddRef(IHTMLEventObj *iface)
{
318
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
319 320 321 322 323 324 325 326 327
    LONG ref = InterlockedIncrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

    return ref;
}

static ULONG WINAPI HTMLEventObj_Release(IHTMLEventObj *iface)
{
328
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
329 330 331 332
    LONG ref = InterlockedDecrement(&This->ref);

    TRACE("(%p) ref=%d\n", This, ref);

333
    if(!ref) {
334 335
        if(This->target)
            IHTMLDOMNode_Release(&This->target->IHTMLDOMNode_iface);
336 337
        if(This->nsevent)
            nsIDOMEvent_Release(This->nsevent);
338
        release_dispex(&This->dispex);
339
        heap_free(This);
340
    }
341 342 343 344 345 346

    return ref;
}

static HRESULT WINAPI HTMLEventObj_GetTypeInfoCount(IHTMLEventObj *iface, UINT *pctinfo)
{
347
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
348
    return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
349 350 351 352 353
}

static HRESULT WINAPI HTMLEventObj_GetTypeInfo(IHTMLEventObj *iface, UINT iTInfo,
                                              LCID lcid, ITypeInfo **ppTInfo)
{
354
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
355
    return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
356 357 358 359 360 361
}

static HRESULT WINAPI HTMLEventObj_GetIDsOfNames(IHTMLEventObj *iface, REFIID riid,
                                                LPOLESTR *rgszNames, UINT cNames,
                                                LCID lcid, DISPID *rgDispId)
{
362
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
363 364
    return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
            lcid, rgDispId);
365 366 367 368 369 370
}

static HRESULT WINAPI HTMLEventObj_Invoke(IHTMLEventObj *iface, DISPID dispIdMember,
                            REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
                            VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
371
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
372
    return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid,
373
            wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
374 375 376 377
}

static HRESULT WINAPI HTMLEventObj_get_srcElement(IHTMLEventObj *iface, IHTMLElement **p)
{
378
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
379 380 381

    TRACE("(%p)->(%p)\n", This, p);

382 383 384 385
    *p = NULL;
    if(This->target)
        IHTMLDOMNode_QueryInterface(&This->target->IHTMLDOMNode_iface, &IID_IHTMLElement, (void**)p);
    return S_OK;
386 387 388 389
}

static HRESULT WINAPI HTMLEventObj_get_altKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
{
390
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
391
    cpp_bool ret = FALSE;
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMKeyEvent *key_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMKeyEvent_GetAltKey(key_event, &ret);
            nsIDOMKeyEvent_Release(key_event);
        }else {
            nsIDOMMouseEvent *mouse_event;

            nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
            if(NS_SUCCEEDED(nsres)) {
                nsIDOMMouseEvent_GetAltKey(mouse_event, &ret);
                nsIDOMMouseEvent_Release(mouse_event);
            }
        }
    }

    *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
    return S_OK;
416 417 418 419
}

static HRESULT WINAPI HTMLEventObj_get_ctrlKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
{
420
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
421
    cpp_bool ret = FALSE;
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMKeyEvent *key_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMKeyEvent_GetCtrlKey(key_event, &ret);
            nsIDOMKeyEvent_Release(key_event);
        }else {
            nsIDOMMouseEvent *mouse_event;

            nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
            if(NS_SUCCEEDED(nsres)) {
                nsIDOMMouseEvent_GetCtrlKey(mouse_event, &ret);
                nsIDOMMouseEvent_Release(mouse_event);
            }
        }
    }

    *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
    return S_OK;
446 447 448 449
}

static HRESULT WINAPI HTMLEventObj_get_shiftKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
{
450
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
451
    cpp_bool ret = FALSE;
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMKeyEvent *key_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMKeyEvent_GetShiftKey(key_event, &ret);
            nsIDOMKeyEvent_Release(key_event);
        }else {
            nsIDOMMouseEvent *mouse_event;

            nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
            if(NS_SUCCEEDED(nsres)) {
                nsIDOMMouseEvent_GetShiftKey(mouse_event, &ret);
                nsIDOMMouseEvent_Release(mouse_event);
            }
        }
    }

    *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
    return S_OK;
476 477 478 479
}

static HRESULT WINAPI HTMLEventObj_put_returnValue(IHTMLEventObj *iface, VARIANT v)
{
480
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
481 482 483 484

    TRACE("(%p)->(%s)\n", This, debugstr_variant(&v));

    if(V_VT(&v) != VT_BOOL) {
485
        FIXME("unsupported value %s\n", debugstr_variant(&v));
486 487 488
        return DISP_E_BADVARTYPE;
    }

489
    This->return_value = v;
490 491 492
    if(!V_BOOL(&v))
        This->prevent_default = TRUE;
    return S_OK;
493 494 495 496
}

static HRESULT WINAPI HTMLEventObj_get_returnValue(IHTMLEventObj *iface, VARIANT *p)
{
497
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
498

499
    TRACE("(%p)->(%p)\n", This, p);
500

501
    V_VT(p) = VT_EMPTY;
502
    return VariantCopy(p, &This->return_value);
503 504 505 506
}

static HRESULT WINAPI HTMLEventObj_put_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL v)
{
507
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
508 509 510 511 512

    TRACE("(%p)->(%x)\n", This, v);

    This->cancel_bubble = !!v;
    return S_OK;
513 514 515 516
}

static HRESULT WINAPI HTMLEventObj_get_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL *p)
{
517
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
518

519
    TRACE("(%p)->(%p)\n", This, p);
520

521
    *p = This->cancel_bubble ? VARIANT_TRUE : VARIANT_FALSE;
522
    return S_OK;
523 524 525 526
}

static HRESULT WINAPI HTMLEventObj_get_fromElement(IHTMLEventObj *iface, IHTMLElement **p)
{
527
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
528

529
    FIXME("(%p)->(%p)\n", This, p);
530 531 532

    *p = NULL;
    return S_OK;
533 534 535 536
}

static HRESULT WINAPI HTMLEventObj_get_toElement(IHTMLEventObj *iface, IHTMLElement **p)
{
537
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
538

539
    FIXME("(%p)->(%p)\n", This, p);
540 541 542

    *p = NULL;
    return S_OK;
543 544
}

545
static HRESULT WINAPI HTMLEventObj_put_keyCode(IHTMLEventObj *iface, LONG v)
546
{
547
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
548
    FIXME("(%p)->(%d)\n", This, v);
549 550 551
    return E_NOTIMPL;
}

552
static HRESULT WINAPI HTMLEventObj_get_keyCode(IHTMLEventObj *iface, LONG *p)
553
{
554
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
555
    UINT32 key_code = 0;
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMKeyEvent *key_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMKeyEvent_GetKeyCode(key_event, &key_code);
            nsIDOMKeyEvent_Release(key_event);
        }
    }

    *p = key_code;
    return S_OK;
572 573
}

574
static HRESULT WINAPI HTMLEventObj_get_button(IHTMLEventObj *iface, LONG *p)
575
{
576
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
577
    INT16 button = 0;
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMMouseEvent *mouse_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMMouseEvent_GetButton(mouse_event, &button);
            nsIDOMMouseEvent_Release(mouse_event);
        }
    }

    *p = button;
    return S_OK;
594 595 596 597
}

static HRESULT WINAPI HTMLEventObj_get_type(IHTMLEventObj *iface, BSTR *p)
{
598
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
599 600 601

    TRACE("(%p)->(%p)\n", This, p);

602 603 604 605 606
    if(!This->type) {
        *p = NULL;
        return S_OK;
    }

607 608
    *p = SysAllocString(This->type->name);
    return *p ? S_OK : E_OUTOFMEMORY;
609 610 611 612
}

static HRESULT WINAPI HTMLEventObj_get_qualifier(IHTMLEventObj *iface, BSTR *p)
{
613
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
614

615
    FIXME("(%p)->(%p)\n", This, p);
616 617 618

    *p = NULL;
    return S_OK;
619 620
}

621
static HRESULT WINAPI HTMLEventObj_get_reason(IHTMLEventObj *iface, LONG *p)
622
{
623
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
624

625
    FIXME("(%p)->(%p)\n", This, p);
626 627 628

    *p = 0;
    return S_OK;
629 630
}

631
static HRESULT WINAPI HTMLEventObj_get_x(IHTMLEventObj *iface, LONG *p)
632
{
633
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
634
    LONG x = 0;
635

636
    TRACE("(%p)->(%p)\n", This, p);
637

638 639 640 641 642 643 644 645 646 647 648 649 650 651
    if(This->nsevent) {
        nsIDOMUIEvent *ui_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
        if(NS_SUCCEEDED(nsres)) {
            /* NOTE: pageX is not exactly right here. */
            nsres = nsIDOMUIEvent_GetPageX(ui_event, &x);
            assert(nsres == NS_OK);
            nsIDOMUIEvent_Release(ui_event);
        }
    }

    *p = x;
652
    return S_OK;
653 654
}

655
static HRESULT WINAPI HTMLEventObj_get_y(IHTMLEventObj *iface, LONG *p)
656
{
657
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
658
    LONG y = 0;
659

660 661 662 663 664 665 666 667 668 669 670 671 672 673
    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMUIEvent *ui_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
        if(NS_SUCCEEDED(nsres)) {
            /* NOTE: pageY is not exactly right here. */
            nsres = nsIDOMUIEvent_GetPageY(ui_event, &y);
            assert(nsres == NS_OK);
            nsIDOMUIEvent_Release(ui_event);
        }
    }
674

675
    *p = y;
676
    return S_OK;
677 678
}

679
static HRESULT WINAPI HTMLEventObj_get_clientX(IHTMLEventObj *iface, LONG *p)
680
{
681
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
682
    LONG x = 0;
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMMouseEvent *mouse_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMMouseEvent_GetClientX(mouse_event, &x);
            nsIDOMMouseEvent_Release(mouse_event);
        }
    }

    *p = x;
    return S_OK;
699 700
}

701
static HRESULT WINAPI HTMLEventObj_get_clientY(IHTMLEventObj *iface, LONG *p)
702
{
703
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
704
    LONG y = 0;
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMMouseEvent *mouse_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMMouseEvent_GetClientY(mouse_event, &y);
            nsIDOMMouseEvent_Release(mouse_event);
        }
    }

    *p = y;
    return S_OK;
721 722
}

723
static HRESULT WINAPI HTMLEventObj_get_offsetX(IHTMLEventObj *iface, LONG *p)
724
{
725
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
726

727
    FIXME("(%p)->(%p)\n", This, p);
728 729 730

    *p = 0;
    return S_OK;
731 732
}

733
static HRESULT WINAPI HTMLEventObj_get_offsetY(IHTMLEventObj *iface, LONG *p)
734
{
735
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
736

737
    FIXME("(%p)->(%p)\n", This, p);
738 739 740

    *p = 0;
    return S_OK;
741 742
}

743
static HRESULT WINAPI HTMLEventObj_get_screenX(IHTMLEventObj *iface, LONG *p)
744
{
745
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
746
    LONG x = 0;
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMMouseEvent *mouse_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMMouseEvent_GetScreenX(mouse_event, &x);
            nsIDOMMouseEvent_Release(mouse_event);
        }
    }

    *p = x;
    return S_OK;
763 764
}

765
static HRESULT WINAPI HTMLEventObj_get_screenY(IHTMLEventObj *iface, LONG *p)
766
{
767
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
768
    LONG y = 0;
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784

    TRACE("(%p)->(%p)\n", This, p);

    if(This->nsevent) {
        nsIDOMMouseEvent *mouse_event;
        nsresult nsres;

        nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
        if(NS_SUCCEEDED(nsres)) {
            nsIDOMMouseEvent_GetScreenY(mouse_event, &y);
            nsIDOMMouseEvent_Release(mouse_event);
        }
    }

    *p = y;
    return S_OK;
785 786 787 788
}

static HRESULT WINAPI HTMLEventObj_get_srcFilter(IHTMLEventObj *iface, IDispatch **p)
{
789
    HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
790

791
    FIXME("(%p)->(%p)\n", This, p);
792 793 794

    *p = NULL;
    return S_OK;
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
}

static const IHTMLEventObjVtbl HTMLEventObjVtbl = {
    HTMLEventObj_QueryInterface,
    HTMLEventObj_AddRef,
    HTMLEventObj_Release,
    HTMLEventObj_GetTypeInfoCount,
    HTMLEventObj_GetTypeInfo,
    HTMLEventObj_GetIDsOfNames,
    HTMLEventObj_Invoke,
    HTMLEventObj_get_srcElement,
    HTMLEventObj_get_altKey,
    HTMLEventObj_get_ctrlKey,
    HTMLEventObj_get_shiftKey,
    HTMLEventObj_put_returnValue,
    HTMLEventObj_get_returnValue,
    HTMLEventObj_put_cancelBubble,
    HTMLEventObj_get_cancelBubble,
    HTMLEventObj_get_fromElement,
    HTMLEventObj_get_toElement,
    HTMLEventObj_put_keyCode,
    HTMLEventObj_get_keyCode,
    HTMLEventObj_get_button,
    HTMLEventObj_get_type,
    HTMLEventObj_get_qualifier,
    HTMLEventObj_get_reason,
    HTMLEventObj_get_x,
    HTMLEventObj_get_y,
    HTMLEventObj_get_clientX,
    HTMLEventObj_get_clientY,
    HTMLEventObj_get_offsetX,
    HTMLEventObj_get_offsetY,
    HTMLEventObj_get_screenX,
    HTMLEventObj_get_screenY,
    HTMLEventObj_get_srcFilter
};

832 833 834 835 836
static inline HTMLEventObj *unsafe_impl_from_IHTMLEventObj(IHTMLEventObj *iface)
{
    return iface->lpVtbl == &HTMLEventObjVtbl ? impl_from_IHTMLEventObj(iface) : NULL;
}

837 838 839 840 841 842 843 844 845 846 847
static const tid_t HTMLEventObj_iface_tids[] = {
    IHTMLEventObj_tid,
    0
};

static dispex_static_data_t HTMLEventObj_dispex = {
    NULL,
    DispCEventObj_tid,
    HTMLEventObj_iface_tids
};

848
static HTMLEventObj *create_event(void)
849 850 851
{
    HTMLEventObj *ret;

852
    ret = heap_alloc_zero(sizeof(*ret));
853 854 855
    if(!ret)
        return NULL;

856
    ret->IHTMLEventObj_iface.lpVtbl = &HTMLEventObjVtbl;
857 858
    ret->ref = 1;

859 860 861 862 863 864 865 866 867 868
    init_dispex(&ret->dispex, (IUnknown*)&ret->IHTMLEventObj_iface, &HTMLEventObj_dispex);

    return ret;
}

static HRESULT set_event_info(HTMLEventObj *event, HTMLDOMNode *target, eventid_t eid, nsIDOMEvent *nsevent)
{
    event->type = event_info+eid;
    event->nsevent = nsevent;

869
    if(nsevent) {
870
        nsIDOMEvent_AddRef(nsevent);
871
    }else if(event_types[event_info[eid].type]) {
872
        nsAString type_str;
873 874
        nsresult nsres;

875
        nsAString_InitDepend(&type_str, event_types[event_info[eid].type]);
876
        nsres = nsIDOMHTMLDocument_CreateEvent(target->doc->nsdoc, &type_str, &event->nsevent);
877
        nsAString_Finish(&type_str);
878 879
        if(NS_FAILED(nsres)) {
            ERR("Could not create event: %08x\n", nsres);
880
            return E_FAIL;
881 882 883
        }
    }

884
    event->target = target;
885 886
    if(target)
        IHTMLDOMNode_AddRef(&target->IHTMLDOMNode_iface);
887
    return S_OK;
888 889
}

890 891 892 893
HRESULT create_event_obj(IHTMLEventObj **ret)
{
    HTMLEventObj *event;

894
    event = create_event();
895 896 897 898 899 900 901
    if(!event)
        return E_OUTOFMEMORY;

    *ret = &event->IHTMLEventObj_iface;
    return S_OK;
}

902 903
static inline event_target_t *get_event_target_data(EventTarget *event_target, BOOL alloc)
{
904
    const dispex_static_data_vtbl_t *vtbl = dispex_get_vtbl(&event_target->dispex);
905 906
    event_target_t **ptr;

907 908
    ptr = vtbl && vtbl->get_event_target_ptr
        ? vtbl->get_event_target_ptr(&event_target->dispex)
909 910 911 912 913 914 915
        : &event_target->ptr;
    if(*ptr || !alloc)
        return *ptr;

    return *ptr = heap_alloc_zero(sizeof(event_target_t));
}

916
static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv)
917 918
{
    IDispatchEx *dispex;
919
    EXCEPINFO ei;
920 921 922 923 924 925
    HRESULT hres;

    memset(&ei, 0, sizeof(ei));

    hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
    if(SUCCEEDED(hres)) {
926
        hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, dp, retv, &ei, NULL);
927 928 929 930
        IDispatchEx_Release(dispex);
    }else {
        TRACE("Could not get IDispatchEx interface: %08x\n", hres);
        hres = IDispatch_Invoke(disp, 0, &IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD,
931
                dp, retv, &ei, NULL);
932 933 934 935 936
    }

    return hres;
}

937
static HRESULT call_cp_func(IDispatch *disp, DISPID dispid, HTMLEventObj *event_obj, VARIANT *retv)
938 939
{
    DISPPARAMS dp = {NULL,NULL,0,0};
940
    VARIANT event_arg;
941 942 943
    ULONG argerr;
    EXCEPINFO ei;

944 945 946 947 948 949 950
    if(event_obj) {
        V_VT(&event_arg) = VT_DISPATCH;
        V_DISPATCH(&event_arg) = (IDispatch*)&event_obj->IHTMLEventObj_iface;
        dp.rgvarg = &event_arg;
        dp.cArgs = 1;
    }

951
    memset(&ei, 0, sizeof(ei));
952
    return IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_METHOD, &dp, retv, &ei, &argerr);
953 954
}

955
static BOOL is_cp_event(cp_static_data_t *data, DISPID dispid)
956
{
957
    int min, max, i;
958 959 960 961 962 963 964 965
    HRESULT hres;

    if(!data)
        return FALSE;

    if(!data->ids) {
        hres = get_dispids(data->tid, &data->id_cnt, &data->ids);
        if(FAILED(hres))
966
            return FALSE;
967 968 969
    }

    min = 0;
970
    max = data->id_cnt-1;
971 972 973 974 975 976 977 978 979 980 981 982 983 984
    while(min <= max) {
        i = (min+max)/2;
        if(data->ids[i] == dispid)
            return TRUE;

        if(data->ids[i] < dispid)
            min = i+1;
        else
            max = i-1;
    }

    return FALSE;
}

985
void call_event_handlers(HTMLDocumentNode *doc, HTMLEventObj *event_obj, EventTarget *event_target,
986
        ConnectionPointContainer *cp_container, eventid_t eid, IDispatch *this_obj)
987
{
988
    event_target_t *data = get_event_target_data(event_target, FALSE);
989 990
    const BOOL cancelable = event_info[eid].flags & EVENT_CANCELABLE;
    VARIANT v;
991 992
    HRESULT hres;

993
    if(data && data->event_table[eid] && data->event_table[eid]->handler_prop) {
994 995 996 997 998 999
        DISPID named_arg = DISPID_THIS;
        VARIANTARG arg;
        DISPPARAMS dp = {&arg, &named_arg, 1, 1};

        V_VT(&arg) = VT_DISPATCH;
        V_DISPATCH(&arg) = this_obj;
1000
        V_VT(&v) = VT_EMPTY;
1001 1002

        TRACE("%s >>>\n", debugstr_w(event_info[eid].name));
1003
        hres = call_disp_func(data->event_table[eid]->handler_prop, &dp, &v);
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
        if(hres == S_OK) {
            TRACE("%s <<< %s\n", debugstr_w(event_info[eid].name), debugstr_variant(&v));

            if(cancelable) {
                if(V_VT(&v) == VT_BOOL) {
                    if(!V_BOOL(&v))
                        event_obj->prevent_default = TRUE;
                }else if(V_VT(&v) != VT_EMPTY) {
                    FIXME("unhandled result %s\n", debugstr_variant(&v));
                }
            }
            VariantClear(&v);
        }else {
1017
            WARN("%s <<< %08x\n", debugstr_w(event_info[eid].name), hres);
1018
        }
1019
    }
1020

1021
    if(data && data->event_table[eid] && data->event_table[eid]->handler_cnt) {
1022 1023
        VARIANTARG arg;
        DISPPARAMS dp = {&arg, NULL, 1, 0};
1024
        int i;
1025 1026 1027 1028

        V_VT(&arg) = VT_DISPATCH;
        V_DISPATCH(&arg) = (IDispatch*)event_obj;

1029
        i = data->event_table[eid]->handler_cnt;
1030
        while(i--) {
1031
            if(data->event_table[eid]->handlers[i]) {
1032 1033
                V_VT(&v) = VT_EMPTY;

1034
                TRACE("%s [%d] >>>\n", debugstr_w(event_info[eid].name), i);
1035
                hres = call_disp_func(data->event_table[eid]->handlers[i], &dp, &v);
1036
                if(hres == S_OK) {
1037
                    TRACE("%s [%d] <<<\n", debugstr_w(event_info[eid].name), i);
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048

                    if(cancelable) {
                        if(V_VT(&v) == VT_BOOL) {
                            if(!V_BOOL(&v))
                                event_obj->prevent_default = TRUE;
                        }else if(V_VT(&v) != VT_EMPTY) {
                            FIXME("unhandled result %s\n", debugstr_variant(&v));
                        }
                    }
                    VariantClear(&v);
                }else {
1049
                    WARN("%s [%d] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
1050
                }
1051 1052 1053
            }
        }
    }
1054

1055 1056 1057 1058 1059
    /*
     * NOTE: CP events may require doc_obj reference, which we don't own. We make sure that
     * it's safe to call event handler by checking nsevent_listener, which is NULL for
     * detached documents.
     */
1060 1061 1062
    if(cp_container && cp_container->forward_container)
        cp_container = cp_container->forward_container;
    if(cp_container && cp_container->cps && doc->nsevent_listener) {
1063
        ConnectionPoint *cp;
1064
        unsigned i, j;
1065

1066 1067 1068 1069
        for(j=0; cp_container->cp_entries[j].riid; j++) {
            cp = cp_container->cps + j;
            if(!cp->sinks_size || !is_cp_event(cp->data, event_info[eid].dispid))
                continue;
1070

1071 1072 1073
            for(i=0; doc->nsevent_listener && i < cp->sinks_size; i++) {
                if(!cp->sinks[i].disp)
                    continue;
1074

1075
                V_VT(&v) = VT_EMPTY;
1076

1077
                TRACE("cp %s [%u] >>>\n", debugstr_w(event_info[eid].name), i);
1078 1079
                hres = call_cp_func(cp->sinks[i].disp, event_info[eid].dispid,
                        cp->data->pass_event_arg ? event_obj : NULL, &v);
1080 1081
                if(hres == S_OK) {
                    TRACE("cp %s [%u] <<<\n", debugstr_w(event_info[eid].name), i);
1082

1083 1084 1085 1086 1087 1088
                    if(cancelable) {
                        if(V_VT(&v) == VT_BOOL) {
                            if(!V_BOOL(&v))
                                event_obj->prevent_default = TRUE;
                        }else if(V_VT(&v) != VT_EMPTY) {
                            FIXME("unhandled result %s\n", debugstr_variant(&v));
1089 1090
                        }
                    }
1091 1092 1093
                    VariantClear(&v);
                }else {
                    WARN("cp %s [%u] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
1094 1095
                }
            }
1096 1097 1098

            if(!doc->nsevent_listener)
                break;
1099 1100
        }
    }
1101 1102
}

1103
static void fire_event_obj(HTMLDocumentNode *doc, eventid_t eid, HTMLEventObj *event_obj,
1104
        HTMLDOMNode *target, IDispatch *script_this)
1105
{
1106
    IHTMLEventObj *prev_event;
1107
    nsIDOMNode *parent, *nsnode;
1108
    BOOL prevent_default = FALSE;
1109
    HTMLInnerWindow *window;
1110
    HTMLDOMNode *node;
1111
    UINT16 node_type;
1112
    nsresult nsres;
1113
    HRESULT hres;
1114

1115
    TRACE("(%p) %s\n", doc, debugstr_w(event_info[eid].name));
1116

1117 1118 1119 1120 1121 1122
    window = doc->window;
    if(!window) {
        WARN("NULL window\n");
        return;
    }

1123 1124
    htmldoc_addref(&doc->basedoc);

1125
    prev_event = window->event;
1126
    window->event = event_obj ? &event_obj->IHTMLEventObj_iface : NULL;
1127

1128 1129
    nsIDOMNode_GetNodeType(target->nsnode, &node_type);
    nsnode = target->nsnode;
1130 1131
    nsIDOMNode_AddRef(nsnode);

1132 1133 1134
    switch(node_type) {
    case ELEMENT_NODE:
        do {
1135
            hres = get_node(doc, nsnode, FALSE, &node);
1136
            if(SUCCEEDED(hres) && node) {
1137 1138
                call_event_handlers(doc, event_obj, &node->event_target, node->cp_container, eid,
                        script_this ? script_this : (IDispatch*)&node->IHTMLDOMNode_iface);
1139 1140
                node_release(node);
            }
1141

1142
            if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1143 1144 1145 1146 1147 1148 1149
                break;

            nsIDOMNode_GetParentNode(nsnode, &parent);
            nsIDOMNode_Release(nsnode);
            nsnode = parent;
            if(!nsnode)
                break;
1150

1151 1152
            nsIDOMNode_GetNodeType(nsnode, &node_type);
        }while(node_type == ELEMENT_NODE);
1153

1154
        if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1155
            break;
1156

1157
    case DOCUMENT_NODE:
1158 1159 1160 1161
        if(event_info[eid].flags & EVENT_FORWARDBODY) {
            nsIDOMHTMLElement *nsbody;
            nsresult nsres;

1162
            nsres = nsIDOMHTMLDocument_GetBody(doc->nsdoc, &nsbody);
1163
            if(NS_SUCCEEDED(nsres) && nsbody) {
1164
                hres = get_node(doc, (nsIDOMNode*)nsbody, FALSE, &node);
1165
                if(SUCCEEDED(hres) && node) {
1166 1167
                    call_event_handlers(doc, event_obj, &node->event_target, node->cp_container, eid,
                            script_this ? script_this : (IDispatch*)&node->IHTMLDOMNode_iface);
1168 1169
                    node_release(node);
                }
1170 1171 1172 1173 1174 1175
                nsIDOMHTMLElement_Release(nsbody);
            }else {
                ERR("Could not get body: %08x\n", nsres);
            }
        }

1176
        call_event_handlers(doc, event_obj, &doc->node.event_target, &doc->basedoc.cp_container, eid,
1177
                script_this ? script_this : (IDispatch*)&doc->basedoc.IHTMLDocument2_iface);
1178
        break;
1179

1180 1181
    default:
        FIXME("unimplemented node type %d\n", node_type);
1182 1183 1184 1185 1186
    }

    if(nsnode)
        nsIDOMNode_Release(nsnode);

1187 1188
    if(event_obj && event_obj->prevent_default)
        prevent_default = TRUE;
1189
    window->event = prev_event;
1190

1191
    if(!prevent_default && (event_info[eid].flags & EVENT_HASDEFAULTHANDLERS)) {
1192 1193
        nsnode = target->nsnode;
        nsIDOMNode_AddRef(nsnode);
1194 1195 1196 1197 1198 1199

        do {
            hres = get_node(doc, nsnode, TRUE, &node);
            if(FAILED(hres))
                break;

1200 1201
            if(node) {
                if(node->vtbl->handle_event)
1202
                    hres = node->vtbl->handle_event(node, eid, event_obj ? event_obj->nsevent : NULL, &prevent_default);
1203
                node_release(node);
1204
                if(FAILED(hres) || prevent_default || (event_obj && event_obj->cancel_bubble))
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
                    break;
            }

            nsres = nsIDOMNode_GetParentNode(nsnode, &parent);
            if(NS_FAILED(nsres))
                break;

            nsIDOMNode_Release(nsnode);
            nsnode = parent;
        } while(nsnode);

        if(nsnode)
            nsIDOMNode_Release(nsnode);
    }

1220
    if(prevent_default && event_obj && event_obj->nsevent) {
1221
        TRACE("calling PreventDefault\n");
1222
        nsIDOMEvent_PreventDefault(event_obj->nsevent);
1223
    }
1224 1225

    htmldoc_release(&doc->basedoc);
1226 1227
}

1228
void fire_event(HTMLDocumentNode *doc, eventid_t eid, BOOL set_event, HTMLDOMNode *target, nsIDOMEvent *nsevent,
1229
        IDispatch *script_this)
1230
{
1231 1232 1233 1234 1235 1236 1237 1238
    HTMLEventObj *event_obj = NULL;
    HRESULT hres;

    if(set_event) {
        event_obj = create_event();
        if(!event_obj)
            return;

1239
        hres = set_event_info(event_obj, target, eid, nsevent);
1240 1241 1242 1243 1244 1245
        if(FAILED(hres)) {
            IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
            return;
        }
    }

1246
    fire_event_obj(doc, eid, event_obj, target, script_this);
1247 1248 1249 1250 1251 1252 1253 1254

    if(event_obj)
        IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
}

HRESULT dispatch_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_var, VARIANT_BOOL *cancelled)
{
    HTMLEventObj *event_obj = NULL;
1255
    eventid_t eid;
1256
    HRESULT hres;
1257 1258 1259 1260 1261 1262 1263

    eid = attr_to_eid(event_name);
    if(eid == EVENTID_LAST) {
        WARN("unknown event %s\n", debugstr_w(event_name));
        return E_INVALIDARG;
    }

1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
    if(event_var && V_VT(event_var) != VT_EMPTY && V_VT(event_var) != VT_ERROR) {
        if(V_VT(event_var) != VT_DISPATCH) {
            FIXME("event_var %s not supported\n", debugstr_variant(event_var));
            return E_NOTIMPL;
        }

        if(V_DISPATCH(event_var)) {
            IHTMLEventObj *event_iface;

            hres = IDispatch_QueryInterface(V_DISPATCH(event_var), &IID_IHTMLEventObj, (void**)&event_iface);
            if(FAILED(hres)) {
                FIXME("No IHTMLEventObj iface\n");
                return hres;
            }
1278

1279 1280 1281 1282 1283 1284 1285
            event_obj = unsafe_impl_from_IHTMLEventObj(event_iface);
            if(!event_obj) {
                ERR("Not our IHTMLEventObj?\n");
                IHTMLEventObj_Release(event_iface);
                return E_FAIL;
            }
        }
1286 1287
    }

1288 1289 1290
    if(event_obj) {
        hres = set_event_info(event_obj, node, eid, NULL);
        if(SUCCEEDED(hres))
1291
            fire_event_obj(node->doc, eid, event_obj, node, NULL);
1292 1293 1294 1295 1296

        IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
        if(FAILED(hres))
            return hres;
    }else {
1297
        fire_event(node->doc, eid, TRUE, node, NULL, NULL);
1298
    }
1299 1300 1301 1302 1303

    *cancelled = VARIANT_TRUE; /* FIXME */
    return S_OK;
}

1304
HRESULT call_fire_event(HTMLDOMNode *node, eventid_t eid)
1305 1306 1307
{
    HRESULT hres;

1308
    if(node->vtbl->fire_event) {
1309 1310
        BOOL handled = FALSE;

1311
        hres = node->vtbl->fire_event(node, eid, &handled);
1312 1313 1314 1315
        if(handled)
            return hres;
    }

1316
    fire_event(node->doc, eid, TRUE, node, NULL, NULL);
1317 1318 1319
    return S_OK;
}

1320
static BOOL alloc_handler_vector(event_target_t *event_target, eventid_t eid, int cnt)
1321
{
1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
    handler_vector_t *new_vector, *handler_vector = event_target->event_table[eid];

    if(handler_vector) {
        if(cnt <= handler_vector->handler_cnt)
            return TRUE;

        new_vector = heap_realloc_zero(handler_vector, sizeof(handler_vector_t) + sizeof(IDispatch*)*cnt);
    }else {
        new_vector = heap_alloc_zero(sizeof(handler_vector_t) + sizeof(IDispatch*)*cnt);
    }

    if(!new_vector)
        return FALSE;
1335

1336 1337 1338 1339 1340
    new_vector->handler_cnt = cnt;
    event_target->event_table[eid] = new_vector;
    return TRUE;
}

1341
HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, eventid_t eid)
1342
{
1343
    nsIDOMNode *nsnode = NULL;
1344

1345
    TRACE("%s\n", debugstr_w(event_info[eid].name));
1346

1347 1348 1349 1350 1351 1352 1353 1354
    if(!doc->nsdoc)
        return S_OK;

    switch(eid) {
    case EVENTID_FOCUSIN:
        doc->event_vector[eid] = TRUE;
        eid = EVENTID_FOCUS;
        break;
1355 1356 1357 1358
    case EVENTID_FOCUSOUT:
        doc->event_vector[eid] = TRUE;
        eid = EVENTID_BLUR;
        break;
1359 1360 1361 1362 1363
    default:
        break;
    }

    if(doc->event_vector[eid] || !(event_info[eid].flags & (EVENT_DEFAULTLISTENER|EVENT_BIND_TO_BODY)))
1364 1365
        return S_OK;

1366
    if(event_info[eid].flags & EVENT_BIND_TO_BODY) {
1367 1368
        nsnode = doc->node.nsnode;
        nsIDOMNode_AddRef(nsnode);
1369 1370
    }

1371 1372 1373 1374 1375
    doc->event_vector[eid] = TRUE;
    add_nsevent_listener(doc, nsnode, event_info[eid].name);

    if(nsnode)
        nsIDOMNode_Release(nsnode);
1376 1377 1378
    return S_OK;
}

1379 1380
void detach_events(HTMLDocumentNode *doc)
{
1381 1382
    if(doc->event_vector) {
        int i;
1383

1384
        for(i=0; i < EVENTID_LAST; i++) {
1385
            if(doc->event_vector[i]) {
1386
                detach_nsevent(doc, event_info[i].name);
1387 1388
                doc->event_vector[i] = FALSE;
            }
1389
        }
1390
    }
1391 1392

    release_nsevents(doc);
1393 1394
}

1395
/* Caller should ensure that it's called only once for given event in the target. */
1396 1397
static void bind_event(EventTarget *event_target, eventid_t eid)
{
1398 1399 1400
    const dispex_static_data_vtbl_t *vtbl = dispex_get_vtbl(&event_target->dispex);
    if(vtbl->bind_event)
        vtbl->bind_event(&event_target->dispex, eid);
1401 1402 1403
    else
        FIXME("Unsupported event binding on target %p\n", event_target);
}
1404

1405
static void remove_event_handler(EventTarget *event_target, eventid_t eid)
1406
{
1407
    event_target_t *data;
1408 1409 1410
    VARIANT *store;
    HRESULT hres;

1411
    hres = dispex_get_dprop_ref(&event_target->dispex, event_info[eid].attr_name, FALSE, &store);
1412 1413 1414
    if(SUCCEEDED(hres))
        VariantClear(store);

1415 1416 1417 1418
    data = get_event_target_data(event_target, FALSE);
    if(data && data->event_table[eid] && data->event_table[eid]->handler_prop) {
        IDispatch_Release(data->event_table[eid]->handler_prop);
        data->event_table[eid]->handler_prop = NULL;
1419 1420 1421
    }
}

1422
static HRESULT set_event_handler_disp(EventTarget *event_target, eventid_t eid, IDispatch *disp)
1423
{
1424
    event_target_t *data;
1425

1426 1427 1428
    if(event_info[eid].flags & EVENT_FIXME)
        FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));

1429
    remove_event_handler(event_target, eid);
1430
    if(!disp)
1431
        return S_OK;
1432

1433
    data = get_event_target_data(event_target, TRUE);
1434
    if(!data)
1435 1436
        return E_OUTOFMEMORY;

1437 1438 1439 1440 1441 1442 1443 1444
    if(!data->event_table[eid]) {
        if(!alloc_handler_vector(data, eid, 0))
            return E_OUTOFMEMORY;

        bind_event(event_target, eid);
    }else if(data->event_table[eid]->handler_prop) {
        IDispatch_Release(data->event_table[eid]->handler_prop);
    }
1445

1446
    data->event_table[eid]->handler_prop = disp;
1447
    IDispatch_AddRef(disp);
1448
    return S_OK;
1449 1450
}

1451
HRESULT set_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
1452 1453
{
    switch(V_VT(var)) {
1454
    case VT_NULL:
1455
        remove_event_handler(event_target, eid);
1456
        return S_OK;
1457

1458
    case VT_DISPATCH:
1459
        return set_event_handler_disp(event_target, eid, V_DISPATCH(var));
1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470

    case VT_BSTR: {
        VARIANT *v;
        HRESULT hres;

        /*
         * Setting event handler to string is a rare case and we don't want to
         * complicate nor increase memory of event_target_t for that. Instead,
         * we store the value in DispatchEx, which can already handle custom
         * properties.
         */
1471
        remove_event_handler(event_target, eid);
1472

1473
        hres = dispex_get_dprop_ref(&event_target->dispex, event_info[eid].attr_name, TRUE, &v);
1474 1475 1476 1477 1478 1479 1480 1481 1482
        if(FAILED(hres))
            return hres;

        V_BSTR(v) = SysAllocString(V_BSTR(var));
        if(!V_BSTR(v))
            return E_OUTOFMEMORY;
        V_VT(v) = VT_BSTR;
        return S_OK;
    }
1483 1484

    default:
1485
        FIXME("not handler %s\n", debugstr_variant(var));
1486
        /* fall through */
1487
    case VT_EMPTY:
1488
        return E_NOTIMPL;
1489 1490
    }

1491
    return S_OK;
1492 1493
}

1494
HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
1495
{
1496
    event_target_t *data;
1497 1498 1499
    VARIANT *v;
    HRESULT hres;

1500
    hres = dispex_get_dprop_ref(&event_target->dispex, event_info[eid].attr_name, FALSE, &v);
1501 1502
    if(SUCCEEDED(hres) && V_VT(v) != VT_EMPTY) {
        V_VT(var) = VT_EMPTY;
1503
        return VariantCopy(var, v);
1504
    }
1505

1506 1507
    data = get_event_target_data(event_target, FALSE);
    if(data && data->event_table[eid] && data->event_table[eid]->handler_prop) {
1508
        V_VT(var) = VT_DISPATCH;
1509
        V_DISPATCH(var) = data->event_table[eid]->handler_prop;
1510 1511 1512 1513 1514 1515 1516 1517
        IDispatch_AddRef(V_DISPATCH(var));
    }else {
        V_VT(var) = VT_NULL;
    }

    return S_OK;
}

1518
HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARIANT_BOOL *res)
1519
{
1520
    event_target_t *data;
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530
    eventid_t eid;
    DWORD i = 0;

    eid = attr_to_eid(name);
    if(eid == EVENTID_LAST) {
        WARN("Unknown event\n");
        *res = VARIANT_TRUE;
        return S_OK;
    }

1531 1532 1533
    if(event_info[eid].flags & EVENT_FIXME)
        FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));

1534 1535
    data = get_event_target_data(event_target, TRUE);
    if(!data)
1536 1537
        return E_OUTOFMEMORY;

1538 1539
    if(data->event_table[eid]) {
        while(i < data->event_table[eid]->handler_cnt && data->event_table[eid]->handlers[i])
1540
            i++;
1541
        if(i == data->event_table[eid]->handler_cnt && !alloc_handler_vector(data, eid, i+1))
1542
            return E_OUTOFMEMORY;
1543 1544 1545
    }else if(alloc_handler_vector(data, eid, i+1)) {
        bind_event(event_target, eid);
    }else {
1546 1547 1548 1549
        return E_OUTOFMEMORY;
    }

    IDispatch_AddRef(disp);
1550
    data->event_table[eid]->handlers[i] = disp;
1551 1552

    *res = VARIANT_TRUE;
1553
    return S_OK;
1554 1555
}

1556
HRESULT detach_event(EventTarget *event_target, BSTR name, IDispatch *disp)
1557
{
1558
    event_target_t *data;
1559 1560 1561 1562 1563 1564 1565 1566 1567
    eventid_t eid;
    DWORD i = 0;

    eid = attr_to_eid(name);
    if(eid == EVENTID_LAST) {
        WARN("Unknown event\n");
        return S_OK;
    }

1568 1569 1570 1571 1572
    data = get_event_target_data(event_target, FALSE);
    if(!data)
        return S_OK;

    if(!data->event_table[eid])
1573 1574
        return S_OK;

1575 1576 1577 1578
    while(i < data->event_table[eid]->handler_cnt) {
        if(data->event_table[eid]->handlers[i] == disp) {
            IDispatch_Release(data->event_table[eid]->handlers[i]);
            data->event_table[eid]->handlers[i] = NULL;
1579 1580 1581 1582 1583 1584 1585
        }
        i++;
    }

    return S_OK;
}

1586
void bind_target_event(HTMLDocumentNode *doc, EventTarget *event_target, const WCHAR *event, IDispatch *disp)
1587 1588 1589
{
    eventid_t eid;

1590
    TRACE("(%p %p %s %p)\n", doc, event_target, debugstr_w(event), disp);
1591 1592 1593 1594 1595 1596 1597

    eid = attr_to_eid(event);
    if(eid == EVENTID_LAST) {
        WARN("Unsupported event %s\n", debugstr_w(event));
        return;
    }

1598
    set_event_handler_disp(event_target, eid, disp);
1599 1600
}

1601
void update_doc_cp_events(HTMLDocumentNode *doc, cp_static_data_t *cp)
1602 1603 1604 1605 1606
{
    int i;

    for(i=0; i < EVENTID_LAST; i++) {
        if((event_info[i].flags & EVENT_DEFAULTLISTENER) && is_cp_event(cp, event_info[i].dispid))
1607
            ensure_doc_nsevent_handler(doc, i);
1608 1609 1610
    }
}

1611
void check_event_attr(HTMLDocumentNode *doc, nsIDOMHTMLElement *nselem)
1612 1613
{
    const PRUnichar *attr_value;
1614
    nsAString attr_value_str;
1615 1616 1617 1618
    IDispatch *disp;
    HTMLDOMNode *node;
    int i;
    nsresult nsres;
1619
    HRESULT hres;
1620 1621

    for(i=0; i < EVENTID_LAST; i++) {
1622
        nsres = get_elem_attr_value(nselem, event_info[i].attr_name, &attr_value_str, &attr_value);
1623 1624 1625 1626 1627 1628
        if(NS_SUCCEEDED(nsres)) {
            if(!*attr_value)
                continue;

            TRACE("%p.%s = %s\n", nselem, debugstr_w(event_info[i].attr_name), debugstr_w(attr_value));

1629
            disp = script_parse_event(doc->window, attr_value);
1630
            if(disp) {
1631
                hres = get_node(doc, (nsIDOMNode*)nselem, TRUE, &node);
1632
                if(SUCCEEDED(hres)) {
1633
                    set_event_handler_disp(&node->event_target, i, disp);
1634 1635
                    node_release(node);
                }
1636
                IDispatch_Release(disp);
1637
            }
1638
            nsAString_Finish(&attr_value_str);
1639 1640 1641 1642
        }
    }
}

1643 1644
HRESULT doc_init_events(HTMLDocumentNode *doc)
{
1645 1646 1647
    unsigned i;
    HRESULT hres;

1648 1649 1650 1651 1652
    doc->event_vector = heap_alloc_zero(EVENTID_LAST*sizeof(BOOL));
    if(!doc->event_vector)
        return E_OUTOFMEMORY;

    init_nsevents(doc);
1653 1654

    for(i=0; i < EVENTID_LAST; i++) {
1655
        if(event_info[i].flags & EVENT_HASDEFAULTHANDLERS) {
1656
            hres = ensure_doc_nsevent_handler(doc, i);
1657 1658 1659 1660 1661
            if(FAILED(hres))
                return hres;
        }
    }

1662 1663 1664
    return S_OK;
}

1665 1666
void release_event_target(event_target_t *event_target)
{
1667 1668
    int i;
    unsigned int j;
1669 1670

    for(i=0; i < EVENTID_LAST; i++) {
1671 1672 1673 1674
        if(event_target->event_table[i]) {
            if(event_target->event_table[i]->handler_prop)
                IDispatch_Release(event_target->event_table[i]->handler_prop);
            for(j=0; j < event_target->event_table[i]->handler_cnt; j++)
1675 1676
                if(event_target->event_table[i]->handlers[j])
                    IDispatch_Release(event_target->event_table[i]->handlers[j]);
1677
        }
1678 1679 1680 1681
    }

    heap_free(event_target);
}