Jacek Caban : mshtml: Register load, error and abort events directly in event target.

Alexandre Julliard julliard at winehq.org
Thu Feb 22 15:07:48 CST 2018


Module: wine
Branch: master
Commit: d7c94cc7d8d9fcba996eab4747ade2cdb023fef9
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=d7c94cc7d8d9fcba996eab4747ade2cdb023fef9

Author: Jacek Caban <jacek at codeweavers.com>
Date:   Thu Feb 22 19:47:26 2018 +0100

mshtml: Register load, error and abort events directly in event target.

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/mshtml/htmldoc.c       |  2 +-
 dlls/mshtml/htmlelem.c      | 11 +---------
 dlls/mshtml/htmlevent.c     | 53 +++++++++++++++++++++++++++++----------------
 dlls/mshtml/htmlevent.h     |  2 +-
 dlls/mshtml/htmlwindow.c    |  2 +-
 dlls/mshtml/tests/events.js | 40 ++++++++++++++++++++++++++++++++++
 6 files changed, 78 insertions(+), 32 deletions(-)

diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c
index 4473b49..1f458fc 100644
--- a/dlls/mshtml/htmldoc.c
+++ b/dlls/mshtml/htmldoc.c
@@ -5048,7 +5048,7 @@ static nsISupports *HTMLDocumentNode_get_gecko_target(DispatchEx *dispex)
 static void HTMLDocumentNode_bind_event(DispatchEx *dispex, eventid_t eid)
 {
     HTMLDocumentNode *This = impl_from_DispatchEx(dispex);
-    ensure_doc_nsevent_handler(This, eid);
+    ensure_doc_nsevent_handler(This, This->node.nsnode, eid);
 }
 
 static EventTarget *HTMLDocumentNode_get_parent_event_target(DispatchEx *dispex)
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c
index 6379101..8a4e164 100644
--- a/dlls/mshtml/htmlelem.c
+++ b/dlls/mshtml/htmlelem.c
@@ -5439,16 +5439,7 @@ static nsISupports *HTMLElement_get_gecko_target(DispatchEx *dispex)
 static void HTMLElement_bind_event(DispatchEx *dispex, eventid_t eid)
 {
     HTMLElement *This = impl_from_DispatchEx(dispex);
-
-    static const WCHAR loadW[] = {'l','o','a','d',0};
-
-    switch(eid) {
-    case EVENTID_LOAD:
-        add_nsevent_listener(This->node.doc, This->node.nsnode, loadW);
-        return;
-    default:
-        ensure_doc_nsevent_handler(This->node.doc, eid);
-    }
+    ensure_doc_nsevent_handler(This->node.doc, This->node.nsnode, eid);
 }
 
 static HRESULT HTMLElement_handle_event_default(DispatchEx *dispex, eventid_t eid, nsIDOMEvent *nsevent, BOOL *prevent_default)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c
index 6773a53..3e0dfa5 100644
--- a/dlls/mshtml/htmlevent.c
+++ b/dlls/mshtml/htmlevent.c
@@ -131,11 +131,17 @@ typedef struct {
     DWORD flags;
 } event_info_t;
 
+/* Use Gecko default listener (it's registered on window object for DOM nodes). */
 #define EVENT_DEFAULTLISTENER    0x0001
-#define EVENT_BUBBLES            0x0002
-#define EVENT_BIND_TO_BODY       0x0008
-#define EVENT_CANCELABLE         0x0010
+/* Register Gecko listener on target itself (unlike EVENT_DEFAULTLISTENER). */
+#define EVENT_BIND_TO_TARGET     0x0002
+/* Event bubbles by default (unless explicitly specified otherwise). */
+#define EVENT_BUBBLES            0x0004
+/* Event is cancelable by default (unless explicitly specified otherwise). */
+#define EVENT_CANCELABLE         0x0008
+/* Event may have default handler (so we always have to register Gecko listener). */
 #define EVENT_HASDEFAULTHANDLERS 0x0020
+/* Ecent is not supported properly, print FIXME message when it's used. */
 #define EVENT_FIXME              0x0040
 
 /* mouse event flags for fromElement and toElement implementation */
@@ -144,7 +150,7 @@ typedef struct {
 
 static const event_info_t event_info[] = {
     {abortW,             EVENT_TYPE_EVENT,     DISPID_EVMETH_ONABORT,
-        EVENT_BIND_TO_BODY},
+        EVENT_BIND_TO_TARGET},
     {beforeactivateW,    EVENT_TYPE_EVENT,     DISPID_EVMETH_ONBEFOREACTIVATE,
         EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
     {beforeunloadW,      EVENT_TYPE_EVENT,     DISPID_EVMETH_ONBEFOREUNLOAD,
@@ -168,7 +174,7 @@ static const event_info_t event_info[] = {
     {dragstartW,         EVENT_TYPE_DRAG,      DISPID_EVMETH_ONDRAGSTART,
         EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
     {errorW,             EVENT_TYPE_EVENT,     DISPID_EVMETH_ONERROR,
-        EVENT_BIND_TO_BODY},
+        EVENT_BIND_TO_TARGET},
     {focusW,             EVENT_TYPE_FOCUS,     DISPID_EVMETH_ONFOCUS,
         EVENT_DEFAULTLISTENER},
     {focusinW,           EVENT_TYPE_FOCUS,     DISPID_EVMETH_ONFOCUSIN,
@@ -184,7 +190,7 @@ static const event_info_t event_info[] = {
     {keyupW,             EVENT_TYPE_KEYBOARD,  DISPID_EVMETH_ONKEYUP,
         EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
     {loadW,              EVENT_TYPE_UIEVENT,   DISPID_EVMETH_ONLOAD,
-        EVENT_BIND_TO_BODY},
+        EVENT_BIND_TO_TARGET},
     {messageW,           EVENT_TYPE_MESSAGE,   DISPID_EVMETH_ONMESSAGE,
         0},
     {mousedownW,         EVENT_TYPE_MOUSE,     DISPID_EVMETH_ONMOUSEDOWN,
@@ -2618,6 +2624,14 @@ static HRESULT dispatch_event_object(EventTarget *event_target, DOMEvent *event,
 void dispatch_event(EventTarget *event_target, DOMEvent *event)
 {
     dispatch_event_object(event_target, event, DISPATCH_BOTH, NULL);
+
+    /*
+     * We may have registered multiple Gecko listeners for the same event type,
+     * but we already dispatched event to all relevant targets. Stop event
+     * propagation here to avoid events being dispatched multiple times.
+     */
+    if(event->event_id != EVENTID_LAST && (event_info[event->event_id].flags & EVENT_BIND_TO_TARGET))
+        nsIDOMEvent_StopPropagation(event->nsevent);
 }
 
 HRESULT fire_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_var, VARIANT_BOOL *cancelled)
@@ -2679,10 +2693,8 @@ HRESULT fire_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_va
     return S_OK;
 }
 
-HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, eventid_t eid)
+HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, nsIDOMNode *nsnode, eventid_t eid)
 {
-    nsIDOMNode *nsnode = NULL;
-
     TRACE("%s\n", debugstr_w(event_info[eid].name));
 
     if(!doc->nsdoc)
@@ -2701,19 +2713,22 @@ HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, eventid_t eid)
         break;
     }
 
-    if(doc->event_vector[eid] || !(event_info[eid].flags & (EVENT_DEFAULTLISTENER|EVENT_BIND_TO_BODY)))
+    if(event_info[eid].flags & EVENT_DEFAULTLISTENER) {
+        nsnode = NULL;
+    }else if(event_info[eid].flags & EVENT_BIND_TO_TARGET) {
+        if(!nsnode)
+            nsnode = doc->node.nsnode;
+    }else {
         return S_OK;
+    }
 
-    if(event_info[eid].flags & EVENT_BIND_TO_BODY) {
-        nsnode = doc->node.nsnode;
-        nsIDOMNode_AddRef(nsnode);
+    if(!nsnode || nsnode == doc->node.nsnode) {
+        if(doc->event_vector[eid])
+            return S_OK;
+        doc->event_vector[eid] = TRUE;
     }
 
-    doc->event_vector[eid] = TRUE;
     add_nsevent_listener(doc, nsnode, event_info[eid].name);
-
-    if(nsnode)
-        nsIDOMNode_Release(nsnode);
     return S_OK;
 }
 
@@ -2949,7 +2964,7 @@ void update_doc_cp_events(HTMLDocumentNode *doc, cp_static_data_t *cp)
 
     for(i=0; i < EVENTID_LAST; i++) {
         if((event_info[i].flags & EVENT_DEFAULTLISTENER) && is_cp_event(cp, event_info[i].dispid))
-            ensure_doc_nsevent_handler(doc, i);
+            ensure_doc_nsevent_handler(doc, NULL, i);
     }
 }
 
@@ -3046,7 +3061,7 @@ HRESULT doc_init_events(HTMLDocumentNode *doc)
 
     for(i=0; i < EVENTID_LAST; i++) {
         if(event_info[i].flags & EVENT_HASDEFAULTHANDLERS) {
-            hres = ensure_doc_nsevent_handler(doc, i);
+            hres = ensure_doc_nsevent_handler(doc, NULL, i);
             if(FAILED(hres))
                 return hres;
         }
diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h
index 39bcf01..4d1267e 100644
--- a/dlls/mshtml/htmlevent.h
+++ b/dlls/mshtml/htmlevent.h
@@ -98,7 +98,7 @@ HRESULT doc_init_events(HTMLDocumentNode*) DECLSPEC_HIDDEN;
 void detach_events(HTMLDocumentNode *doc) DECLSPEC_HIDDEN;
 HRESULT create_event_obj(IHTMLEventObj**) DECLSPEC_HIDDEN;
 void bind_target_event(HTMLDocumentNode*,EventTarget*,const WCHAR*,IDispatch*) DECLSPEC_HIDDEN;
-HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode*,eventid_t) DECLSPEC_HIDDEN;
+HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode*,nsIDOMNode*,eventid_t) DECLSPEC_HIDDEN;
 
 void dispatch_event(EventTarget*,DOMEvent*) DECLSPEC_HIDDEN;
 
diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c
index 2ef62cc..80cc7ec 100644
--- a/dlls/mshtml/htmlwindow.c
+++ b/dlls/mshtml/htmlwindow.c
@@ -3033,7 +3033,7 @@ static nsISupports *HTMLWindow_get_gecko_target(DispatchEx *dispex)
 static void HTMLWindow_bind_event(DispatchEx *dispex, eventid_t eid)
 {
     HTMLInnerWindow *This = impl_from_DispatchEx(dispex);
-    ensure_doc_nsevent_handler(This->doc, eid);
+    ensure_doc_nsevent_handler(This->doc, NULL, eid);
 }
 
 static void HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode)
diff --git a/dlls/mshtml/tests/events.js b/dlls/mshtml/tests/events.js
index 7db440a..108afe2 100644
--- a/dlls/mshtml/tests/events.js
+++ b/dlls/mshtml/tests/events.js
@@ -743,6 +743,44 @@ function test_keyboard_event() {
     next_test();
 }
 
+function test_error_event() {
+    document.body.innerHTML = '<div><img></img></div>';
+    var div = document.body.firstChild;
+    var img = div.firstChild;
+    var calls = "";
+
+    function record_call(msg) {
+        return function() { calls += msg + "," };
+    }
+    var win_onerror = record_call("window.onerror");
+    var doc_onerror = record_call("doc.onerror");
+    var body_onerror = record_call("body.onerror");
+
+    window.addEventListener("error", win_onerror, true);
+    document.addEventListener("error", doc_onerror, true);
+    document.body.addEventListener("error", body_onerror, true);
+    div.addEventListener("error", record_call("div.onerror"), true);
+
+    div.addEventListener("error", function() {
+        ok(calls === "window.onerror,doc.onerror,body.onerror,div.onerror,", "calls = " + calls);
+
+        window.removeEventListener("error", win_onerror, true);
+        document.removeEventListener("error", doc_onerror, true);
+        document.body.removeEventListener("error", body_onerror, true);
+        next_test();
+    }, true);
+
+    img.src = "about:blank";
+}
+
+function test_detached_img_error_event() {
+    var img = new Image();
+    img.onerror = function() {
+        next_test();
+    }
+    img.src = "about:blank";
+}
+
 var tests = [
     test_content_loaded,
     test_add_remove_listener,
@@ -758,6 +796,8 @@ var tests = [
     test_ui_event,
     test_mouse_event,
     test_keyboard_event,
+    test_error_event,
+    test_detached_img_error_event,
     test_time_stamp,
     test_listener_order
 ];




More information about the wine-cvs mailing list