diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c
index 254b8642767..ad016b903f5 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 4579617f9c7..fefc74908ca 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 cf5f674dc1f..d3f682c3feb 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 6f417a530b3..1432b2c9bc3 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;
- FIXME("(%p)\n", This);
+ static const WCHAR readystatechangeW[] = {'r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};
+
+ 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 = {