From: Jacek Caban Subject: mshtml: Added support for binding XHR events. Message-Id: <55940969.2050609@codeweavers.com> Date: Wed, 01 Jul 2015 17:38:17 +0200 --- dlls/mshtml/htmlevent.c | 6 +- dlls/mshtml/htmlevent.h | 3 + dlls/mshtml/tests/xmlhttprequest.c | 2 +- dlls/mshtml/xmlhttprequest.c | 135 ++++++++++++++++++++++++++++++++++++- 4 files changed, 140 insertions(+), 6 deletions(-) diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 254b864..ad016b9 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -256,7 +256,7 @@ static eventid_t attr_to_eid(LPCWSTR str) return EVENTID_LAST; } -typedef struct { +struct HTMLEventObj { DispatchEx dispex; IHTMLEventObj IHTMLEventObj_iface; @@ -268,7 +268,7 @@ typedef struct { VARIANT return_value; BOOL prevent_default; BOOL cancel_bubble; -} HTMLEventObj; +}; static inline HTMLEventObj *impl_from_IHTMLEventObj(IHTMLEventObj *iface) { @@ -966,7 +966,7 @@ static BOOL is_cp_event(cp_static_data_t *data, DISPID dispid) return FALSE; } -static void call_event_handlers(HTMLDocumentNode *doc, HTMLEventObj *event_obj, EventTarget *event_target, +void call_event_handlers(HTMLDocumentNode *doc, HTMLEventObj *event_obj, EventTarget *event_target, ConnectionPointContainer *cp_container, eventid_t eid, IDispatch *this_obj) { event_target_t *data = get_event_target_data(event_target, FALSE); diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index 4579617f..fefc749 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -67,6 +67,9 @@ 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; +typedef struct HTMLEventObj HTMLEventObj; +void call_event_handlers(HTMLDocumentNode*,HTMLEventObj*,EventTarget*,ConnectionPointContainer*,eventid_t,IDispatch*); + void init_nsevents(HTMLDocumentNode*) DECLSPEC_HIDDEN; void release_nsevents(HTMLDocumentNode*) DECLSPEC_HIDDEN; void add_nsevent_listener(HTMLDocumentNode*,nsIDOMNode*,LPCWSTR) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/tests/xmlhttprequest.c b/dlls/mshtml/tests/xmlhttprequest.c index cf5f674..d3f682c 100644 --- a/dlls/mshtml/tests/xmlhttprequest.c +++ b/dlls/mshtml/tests/xmlhttprequest.c @@ -504,7 +504,7 @@ static void test_async_xhr(IHTMLDocument2 *doc, const char *xml_url) SET_EXPECT(xmlhttprequest_onreadystatechange_opened); hres = IHTMLXMLHttpRequest_open(xhr, method, url, vbool, vempty, vempty); ok(hres == S_OK, "open failed: %08x\n", hres); - todo_wine CHECK_CALLED(xmlhttprequest_onreadystatechange_opened); + CHECK_CALLED(xmlhttprequest_onreadystatechange_opened); SysFreeString(method); SysFreeString(url); diff --git a/dlls/mshtml/xmlhttprequest.c b/dlls/mshtml/xmlhttprequest.c index 6f417a5..1432b2c 100644 --- a/dlls/mshtml/xmlhttprequest.c +++ b/dlls/mshtml/xmlhttprequest.c @@ -60,14 +60,117 @@ static HRESULT variant_to_nsastr(VARIANT var, nsAString *ret) } } -/* IHTMLXMLHttpRequest */ +typedef struct XMLHttpReqEventListener XMLHttpReqEventListener; + typedef struct { EventTarget event_target; IHTMLXMLHttpRequest IHTMLXMLHttpRequest_iface; LONG ref; nsIXMLHttpRequest *nsxhr; + XMLHttpReqEventListener *event_listener; } HTMLXMLHttpRequest; +struct XMLHttpReqEventListener { + nsIDOMEventListener nsIDOMEventListener_iface; + LONG ref; + HTMLXMLHttpRequest *xhr; +}; + +static void detach_xhr_event_listener(XMLHttpReqEventListener *event_listener) +{ + nsIDOMEventTarget *event_target; + nsAString str; + nsresult nsres; + + static const WCHAR readystatechangeW[] = + {'o','n','r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0}; + + nsres = nsIXMLHttpRequest_QueryInterface(event_listener->xhr->nsxhr, &IID_nsIDOMEventTarget, (void**)&event_target); + assert(nsres == NS_OK); + + nsAString_InitDepend(&str, readystatechangeW); + nsres = nsIDOMEventTarget_RemoveEventListener(event_target, &str, &event_listener->nsIDOMEventListener_iface, FALSE); + nsAString_Finish(&str); + nsIDOMEventTarget_Release(event_target); + + event_listener->xhr->event_listener = NULL; + event_listener->xhr = NULL; + nsIDOMEventListener_Release(&event_listener->nsIDOMEventListener_iface); +} + + +static inline XMLHttpReqEventListener *impl_from_nsIDOMEventListener(nsIDOMEventListener *iface) +{ + return CONTAINING_RECORD(iface, XMLHttpReqEventListener, nsIDOMEventListener_iface); +} + +static nsresult NSAPI XMLHttpReqEventListener_QueryInterface(nsIDOMEventListener *iface, + nsIIDRef riid, void **result) +{ + XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); + + if(IsEqualGUID(&IID_nsISupports, riid)) { + TRACE("(%p)->(IID_nsISupports, %p)\n", This, result); + *result = &This->nsIDOMEventListener_iface; + }else if(IsEqualGUID(&IID_nsIDOMEventListener, riid)) { + TRACE("(%p)->(IID_nsIDOMEventListener %p)\n", This, result); + *result = &This->nsIDOMEventListener_iface; + }else { + *result = NULL; + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result); + return NS_NOINTERFACE; + } + + nsIDOMEventListener_AddRef(&This->nsIDOMEventListener_iface); + return NS_OK; +} + +static nsrefcnt NSAPI XMLHttpReqEventListener_AddRef(nsIDOMEventListener *iface) +{ + XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static nsrefcnt NSAPI XMLHttpReqEventListener_Release(nsIDOMEventListener *iface) +{ + XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) { + assert(!This->xhr); + heap_free(This); + } + + return ref; +} + +static nsresult NSAPI XMLHttpReqEventListener_HandleEvent(nsIDOMEventListener *iface, nsIDOMEvent *event) +{ + XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); + + TRACE("(%p)\n", This); + + if(!This->xhr) + return NS_OK; + + call_event_handlers(NULL, NULL, &This->xhr->event_target, NULL, EVENTID_READYSTATECHANGE, + (IDispatch*)&This->xhr->IHTMLXMLHttpRequest_iface); + return NS_OK; +} + +static const nsIDOMEventListenerVtbl XMLHttpReqEventListenerVtbl = { + XMLHttpReqEventListener_QueryInterface, + XMLHttpReqEventListener_AddRef, + XMLHttpReqEventListener_Release, + XMLHttpReqEventListener_HandleEvent +}; + static inline HTMLXMLHttpRequest *impl_from_IHTMLXMLHttpRequest(IHTMLXMLHttpRequest *iface) { return CONTAINING_RECORD(iface, HTMLXMLHttpRequest, IHTMLXMLHttpRequest_iface); @@ -115,6 +218,8 @@ static ULONG WINAPI HTMLXMLHttpRequest_Release(IHTMLXMLHttpRequest *iface) TRACE("(%p) ref=%d\n", This, ref); if(!ref) { + if(This->event_listener) + detach_xhr_event_listener(This->event_listener); release_dispex(&This->event_target.dispex); nsIXMLHttpRequest_Release(This->nsxhr); heap_free(This); @@ -357,10 +462,36 @@ static inline HTMLXMLHttpRequest *impl_from_DispatchEx(DispatchEx *iface) static void HTMLXMLHttpRequest_bind_event(DispatchEx *dispex, int eid) { HTMLXMLHttpRequest *This = impl_from_DispatchEx(dispex); + nsIDOMEventTarget *nstarget; + nsAString type_str; + nsresult nsres; + + static const WCHAR readystatechangeW[] = {'r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0}; - FIXME("(%p)\n", This); + TRACE("(%p)\n", This); assert(eid == EVENTID_READYSTATECHANGE); + + if(This->event_listener) + return; + + This->event_listener = heap_alloc(sizeof(*This->event_listener)); + if(!This->event_listener) + return; + + This->event_listener->nsIDOMEventListener_iface.lpVtbl = &XMLHttpReqEventListenerVtbl; + This->event_listener->ref = 1; + This->event_listener->xhr = This; + + nsres = nsIXMLHttpRequest_QueryInterface(This->nsxhr, &IID_nsIDOMEventTarget, (void**)&nstarget); + assert(nsres == NS_OK); + + nsAString_InitDepend(&type_str, readystatechangeW); + nsres = nsIDOMEventTarget_AddEventListener(nstarget, &type_str, &This->event_listener->nsIDOMEventListener_iface, FALSE, TRUE, 2); + nsAString_Finish(&type_str); + nsIDOMEventTarget_Release(nstarget); + if(NS_FAILED(nsres)) + ERR("AddEventListener failed: %08x\n", nsres); } static dispex_static_data_vtbl_t HTMLXMLHttpRequest_dispex_vtbl = {