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