[PATCH 4/9] mshtml: Properly invoke event listeners in IE9+ mode.

Jacek Caban <[email protected]>
Newsgroups gmane.comp.emulators.wine.patches
Message-ID <[email protected]>
Signed-off-by: Jacek Caban <[email protected]>
---
 dlls/mshtml/htmlevent.c    | 55
++++++++++++++++++++++++++++++++++++++++++----
 dlls/mshtml/htmlevent.h    |  3 ++-
 dlls/mshtml/tests/events.c | 42 +++++++++++++++++++++++++++++++++--
 3 files changed, 93 insertions(+), 7 deletions(-)
0004-mshtml-Properly-invoke-event-listeners-in-IE9-mode.diff (text/x-patch, 9.2 KB)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c
index 8ca94eb4e6..88282c028a 100644
--- a/dlls/mshtml/htmlevent.c
+++ b/dlls/mshtml/htmlevent.c
@@ -1292,6 +1292,7 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
     const eventid_t eid = event->event_id;
     const listener_container_t *container = get_listener_container(event_target, eid, FALSE);
     const BOOL cancelable = event_info[eid].flags & EVENT_CANCELABLE;
+    const BOOL use_quirks = use_event_quirks(event_target);
     event_listener_t *listener, listeners_buf[8], *listeners = listeners_buf;
     unsigned listeners_cnt, listeners_size;
     ConnectionPointContainer *cp_container = NULL;
@@ -1299,9 +1300,10 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
     VARIANT v;
     HRESULT hres;
 
-    if(container && !list_empty(&container->listeners)) {
+    if(use_quirks && container && !list_empty(&container->listeners)
+       && event->phase != DEP_CAPTURING_PHASE) {
         listener = LIST_ENTRY(list_tail(&container->listeners), event_listener_t, entry);
-        if(listener->function && listener->type == LISTENER_TYPE_ONEVENT) {
+        if(listener && listener->function && listener->type == LISTENER_TYPE_ONEVENT) {
             DISPID named_arg = DISPID_THIS;
             VARIANTARG arg;
             DISPPARAMS dp = {&arg, &named_arg, 1, 1};
@@ -1337,6 +1339,16 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
         LIST_FOR_EACH_ENTRY(listener, &container->listeners, event_listener_t, entry) {
             if(!listener->function)
                 continue;
+            switch(listener->type) {
+            case LISTENER_TYPE_ONEVENT:
+                if(use_quirks || event->phase == DEP_CAPTURING_PHASE)
+                    continue;
+                break;
+            case LISTENER_TYPE_ATTACHED:
+                if(event->phase == DEP_CAPTURING_PHASE)
+                    continue;
+                break;
+            }
 
             if(listeners_cnt == listeners_size) {
                 event_listener_t *new_listeners;
@@ -1359,7 +1371,37 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
     }
 
     for(listener = listeners; listener < listeners + listeners_cnt; listener++) {
-        if(listener->type == LISTENER_TYPE_ATTACHED) {
+        if(listener->type != LISTENER_TYPE_ATTACHED) {
+            DISPID named_arg = DISPID_THIS;
+            VARIANTARG args[2];
+            DISPPARAMS dp = {args, &named_arg, 2, 1};
+
+            V_VT(args) = VT_DISPATCH;
+            V_DISPATCH(args) = (IDispatch*)&event_target->dispex.IDispatchEx_iface;
+            V_VT(args+1) = VT_DISPATCH;
+            V_DISPATCH(args+1) = event->in_fire_event
+                ? (IDispatch*)event->event_obj : (IDispatch*)&event->IDOMEvent_iface;
+            V_VT(&v) = VT_EMPTY;
+
+            TRACE("%s >>>\n", debugstr_w(event_info[event->event_id].name));
+            hres = call_disp_func(listener->function, &dp, &v);
+            if(hres == S_OK) {
+                TRACE("%s <<< %s\n", debugstr_w(event_info[event->event_id].name),
+                      debugstr_variant(&v));
+
+                if(cancelable) {
+                    if(V_VT(&v) == VT_BOOL) {
+                        if(!V_BOOL(&v))
+                            IDOMEvent_preventDefault(&event->IDOMEvent_iface);
+                    }else if(V_VT(&v) != VT_EMPTY) {
+                        FIXME("unhandled result %s\n", debugstr_variant(&v));
+                    }
+                }
+                VariantClear(&v);
+            }else {
+                WARN("%s <<< %08x\n", debugstr_w(event_info[event->event_id].name), hres);
+            }
+        }else {
             VARIANTARG arg;
             DISPPARAMS dp = {&arg, NULL, 1, 0};
 
@@ -1588,7 +1630,9 @@ HRESULT fire_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_va
 
     if(SUCCEEDED(hres)) {
         event_obj->event->event_obj = &event_obj->IHTMLEventObj_iface;
+        event_obj->event->in_fire_event++;
         dispatch_event(&node->event_target, event_obj->event);
+        event_obj->event->in_fire_event--;
         event_obj->event->event_obj = NULL;
     }
 
@@ -1826,7 +1870,10 @@ HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARI
 
     listener->type = LISTENER_TYPE_ATTACHED;
     IDispatch_AddRef(listener->function = disp);
-    list_add_head(&container->listeners, &listener->entry);
+    if(use_event_quirks(event_target))
+        list_add_head(&container->listeners, &listener->entry);
+    else
+        list_add_tail(&container->listeners, &listener->entry);
 
     *res = VARIANT_TRUE;
     return S_OK;
diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h
index 97d34af70a..eaf42eb859 100644
--- a/dlls/mshtml/htmlevent.h
+++ b/dlls/mshtml/htmlevent.h
@@ -66,10 +66,11 @@ typedef struct {
     EventTarget *target;
     BOOL prevent_default;
     BOOL stop_propagation;
-    USHORT phase;
+    DOM_EVENT_PHASE phase;
 
     IHTMLEventObj *event_obj;
     BOOL no_event_obj;
+    unsigned in_fire_event;
 } DOMEvent;
 
 void check_event_attr(HTMLDocumentNode*,nsIDOMHTMLElement*) DECLSPEC_HIDDEN;
diff --git a/dlls/mshtml/tests/events.c b/dlls/mshtml/tests/events.c
index ac7ae43a27..91ec4b6dea 100644
--- a/dlls/mshtml/tests/events.c
+++ b/dlls/mshtml/tests/events.c
@@ -93,6 +93,7 @@ static IHTMLWindow2 *window;
 static IOleDocumentView *view;
 static BOOL is_ie9plus;
 static int document_mode;
+static unsigned in_fire_event;
 
 typedef struct {
     LONG x;
@@ -358,7 +359,9 @@ static void _elem_fire_event(unsigned line, IUnknown *unk, const char *event, VA
 
     b = 100;
     str = a2bstr(event);
+    in_fire_event++;
     hres = IHTMLElement3_fireEvent(elem3, str, evobj, &b);
+    in_fire_event--;
     SysFreeString(str);
     ok_(__FILE__,line)(hres == S_OK, "fireEvent failed: %08x\n", hres);
     ok_(__FILE__,line)(b == VARIANT_TRUE, "fireEvent returned %x\n", b);
@@ -371,20 +374,51 @@ static void _test_event_args(unsigned line, const IID *dispiid, DISPID id, WORD
     ok_(__FILE__,line) (id == DISPID_VALUE, "id = %d\n", id);
     ok_(__FILE__,line) (wFlags == DISPATCH_METHOD, "wFlags = %x\n", wFlags);
     ok_(__FILE__,line) (pdp != NULL, "pdp == NULL\n");
-    todo_wine_if(document_mode >= 9)
     ok_(__FILE__,line) (pdp->cArgs == (document_mode < 9 ? 1 : 2), "pdp->cArgs = %d\n", pdp->cArgs);
     ok_(__FILE__,line) (pdp->cNamedArgs == 1, "pdp->cNamedArgs = %d\n", pdp->cNamedArgs);
     ok_(__FILE__,line) (pdp->rgdispidNamedArgs[0] == DISPID_THIS, "pdp->rgdispidNamedArgs[0] = %d\n",
                         pdp->rgdispidNamedArgs[0]);
     ok_(__FILE__,line) (V_VT(pdp->rgvarg) == VT_DISPATCH, "V_VT(rgvarg) = %d\n", V_VT(pdp->rgvarg));
     if(pdp->cArgs > 1)
-        ok_(__FILE__,line) (V_VT(pdp->rgvarg+1) == VT_DISPATCH, "V_VT(rgvarg) = %d\n", V_VT(pdp->rgvarg));
+        ok_(__FILE__,line) (V_VT(pdp->rgvarg+1) == VT_DISPATCH, "V_VT(rgvarg) = %d\n", V_VT(pdp->rgvarg+1));
     ok_(__FILE__,line) (pvarRes != NULL, "pvarRes == NULL\n");
     ok_(__FILE__,line) (pei != NULL, "pei == NULL");
     ok_(__FILE__,line) (!pspCaller, "pspCaller != NULL\n");
 
     if(dispiid)
         _test_disp(line, (IUnknown*)V_DISPATCH(pdp->rgvarg), dispiid);
+
+    if(pdp->cArgs > 1) {
+        IHTMLEventObj *window_event, *event_obj;
+        IDOMEvent *event;
+        HRESULT hres;
+
+        hres = IDispatch_QueryInterface(V_DISPATCH(pdp->rgvarg+1), &IID_IDOMEvent, (void**)&event);
+        if(in_fire_event)
+            ok(hres == E_NOINTERFACE, "QI(IID_IDOMEvent) returned %08x\n", hres);
+        else
+            ok(hres == S_OK, "Could not get IDOMEvent iface: %08x\n", hres);
+
+        hres = IDispatch_QueryInterface(V_DISPATCH(pdp->rgvarg+1), &IID_IHTMLEventObj, (void**)&event_obj);
+        if(in_fire_event)
+            ok(hres == S_OK, "Could not get IDOMEventObj iface: %08x\n", hres);
+        else
+            ok(hres == E_NOINTERFACE, "QI(IID_IHTMLEventObj) returned %08x\n", hres);
+
+        if(event)
+            IDOMEvent_Release(event);
+        if(event_obj)
+            IHTMLEventObj_Release(event_obj);
+
+        hres = IHTMLWindow2_get_event(window, &window_event);
+        ok(hres == S_OK, "get_event failed: %08x\n", hres);
+        if(window_event) {
+            todo_wine_if(in_fire_event)
+            ok(!iface_cmp((IUnknown*)V_DISPATCH(pdp->rgvarg+1), (IUnknown*)window_event),
+               "window_event != event arg\n");
+            IHTMLEventObj_Release(window_event);
+        }
+    }
 }
 
 #define test_attached_event_args(a,b,c,d,e) _test_attached_event_args(__LINE__,a,b,c,d,e)
@@ -666,6 +700,7 @@ static void _test_event_srcfilter(unsigned line, IHTMLEventObj *event)
 static void _test_event_obj(unsigned line, const char *type, const xy_test_t *xy)
 {
     IHTMLEventObj *event = _get_event_obj(line);
+    IDOMEvent *dom_event;
     VARIANT v;
     HRESULT hres;
 
@@ -699,6 +734,9 @@ static void _test_event_obj(unsigned line, const char *type, const xy_test_t *xy
     if(V_VT(&v) == VT_BOOL)
         ok_(__FILE__,line)(V_BOOL(&v) == VARIANT_TRUE, "V_BOOL(returnValue) = %x\n", V_BOOL(&v));
 
+    hres = IHTMLEventObj_QueryInterface(event, &IID_IDOMEvent, (void**)&dom_event);
+    ok(hres == E_NOINTERFACE, "Could not get IDOMEvent iface: %08x\n", hres);
+
     IHTMLEventObj_Release(event);
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.