[PATCH v2 4/4] mshtml: Implement ontimeout event for XMLHttpRequest.

Gabriel Ivăncescu gabrielopcode at gmail.com
Wed May 25 11:13:16 CDT 2022


Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
 dlls/mshtml/htmlevent.c      | 142 +++++++++++++++++++++++++++++++++++
 dlls/mshtml/htmlevent.h      |   1 +
 dlls/mshtml/mshtml_private.h |   2 +
 dlls/mshtml/tests/script.c   |  69 ++++++++++++-----
 dlls/mshtml/tests/xhr.js     |  35 ++++++++-
 dlls/mshtml/xmlhttprequest.c |   9 ++-
 6 files changed, 236 insertions(+), 22 deletions(-)

diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c
index 92fa489..49392bf 100644
--- a/dlls/mshtml/htmlevent.c
+++ b/dlls/mshtml/htmlevent.c
@@ -67,6 +67,7 @@ typedef enum {
     EVENT_TYPE_FOCUS,
     EVENT_TYPE_DRAG,
     EVENT_TYPE_MESSAGE,
+    EVENT_TYPE_PROGRESS,
     EVENT_TYPE_CLIPBOARD
 } event_type_t;
 
@@ -78,6 +79,7 @@ static const WCHAR *event_types[] = {
     L"Event", /* FIXME */
     L"Event", /* FIXME */
     L"Event", /* FIXME */
+    L"ProgressEvent",
     L"Event"  /* FIXME */
 };
 
@@ -185,6 +187,8 @@ static const event_info_t event_info[] = {
         EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
     {L"submit",            EVENT_TYPE_EVENT,     DISPID_EVMETH_ONSUBMIT,
         EVENT_DEFAULTLISTENER | EVENT_HASDEFAULTHANDLERS | EVENT_BUBBLES | EVENT_CANCELABLE},
+    {L"timeout",           EVENT_TYPE_PROGRESS,  DISPID_EVPROP_TIMEOUT,
+        EVENT_BIND_TO_TARGET},
     {L"unload",            EVENT_TYPE_UIEVENT,   DISPID_EVMETH_ONUNLOAD,
         EVENT_FIXME}
 };
@@ -2286,6 +2290,122 @@ static void DOMMessageEvent_destroy(DOMEvent *event)
     heap_free(message_event->data);
 }
 
+typedef struct {
+    DOMEvent event;
+    IDOMProgressEvent IDOMProgressEvent_iface;
+} DOMProgressEvent;
+
+static inline DOMProgressEvent *impl_from_IDOMProgressEvent(IDOMProgressEvent *iface)
+{
+    return CONTAINING_RECORD(iface, DOMProgressEvent, IDOMProgressEvent_iface);
+}
+
+static HRESULT WINAPI DOMProgressEvent_QueryInterface(IDOMProgressEvent *iface, REFIID riid, void **ppv)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    return IDOMEvent_QueryInterface(&This->event.IDOMEvent_iface, riid, ppv);
+}
+
+static ULONG WINAPI DOMProgressEvent_AddRef(IDOMProgressEvent *iface)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    return IDOMEvent_AddRef(&This->event.IDOMEvent_iface);
+}
+
+static ULONG WINAPI DOMProgressEvent_Release(IDOMProgressEvent *iface)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    return IDOMEvent_Release(&This->event.IDOMEvent_iface);
+}
+
+static HRESULT WINAPI DOMProgressEvent_GetTypeInfoCount(IDOMProgressEvent *iface, UINT *pctinfo)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    return IDispatchEx_GetTypeInfoCount(&This->event.dispex.IDispatchEx_iface, pctinfo);
+}
+
+static HRESULT WINAPI DOMProgressEvent_GetTypeInfo(IDOMProgressEvent *iface, UINT iTInfo,
+                                                   LCID lcid, ITypeInfo **ppTInfo)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    return IDispatchEx_GetTypeInfo(&This->event.dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
+}
+
+static HRESULT WINAPI DOMProgressEvent_GetIDsOfNames(IDOMProgressEvent *iface, REFIID riid,
+        LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    return IDispatchEx_GetIDsOfNames(&This->event.dispex.IDispatchEx_iface, riid, rgszNames, cNames,
+            lcid, rgDispId);
+}
+
+static HRESULT WINAPI DOMProgressEvent_Invoke(IDOMProgressEvent *iface, DISPID dispIdMember,
+        REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
+        EXCEPINFO *pExcepInfo, UINT *puArgErr)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    return IDispatchEx_Invoke(&This->event.dispex.IDispatchEx_iface, dispIdMember, riid, lcid,
+            wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+}
+
+static HRESULT WINAPI DOMProgressEvent_get_lengthComputable(IDOMProgressEvent *iface, VARIANT_BOOL *p)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    FIXME("(%p)->(%p)\n", This, p);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DOMProgressEvent_get_loaded(IDOMProgressEvent *iface, ULONGLONG *p)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    FIXME("(%p)->(%p)\n", This, p);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DOMProgressEvent_get_total(IDOMProgressEvent *iface, ULONGLONG *p)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    FIXME("(%p)->(%p)\n", This, p);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DOMProgressEvent_initProgressEvent(IDOMProgressEvent *iface, BSTR type, VARIANT_BOOL can_bubble,
+                                                         VARIANT_BOOL cancelable, VARIANT_BOOL lengthComputable,
+                                                         ULONGLONG loaded, ULONGLONG total)
+{
+    DOMProgressEvent *This = impl_from_IDOMProgressEvent(iface);
+    FIXME("(%p)->(%s %x %x %x %s %s)\n", This, debugstr_w(type), can_bubble, cancelable, lengthComputable,
+          wine_dbgstr_longlong(loaded), wine_dbgstr_longlong(total));
+    return E_NOTIMPL;
+}
+
+static const IDOMProgressEventVtbl DOMProgressEventVtbl = {
+    DOMProgressEvent_QueryInterface,
+    DOMProgressEvent_AddRef,
+    DOMProgressEvent_Release,
+    DOMProgressEvent_GetTypeInfoCount,
+    DOMProgressEvent_GetTypeInfo,
+    DOMProgressEvent_GetIDsOfNames,
+    DOMProgressEvent_Invoke,
+    DOMProgressEvent_get_lengthComputable,
+    DOMProgressEvent_get_loaded,
+    DOMProgressEvent_get_total,
+    DOMProgressEvent_initProgressEvent
+};
+
+static DOMProgressEvent *DOMProgressEvent_from_DOMEvent(DOMEvent *event)
+{
+    return CONTAINING_RECORD(event, DOMProgressEvent, event);
+}
+
+static void *DOMProgressEvent_query_interface(DOMEvent *event, REFIID riid)
+{
+    DOMProgressEvent *This = DOMProgressEvent_from_DOMEvent(event);
+    if(IsEqualGUID(&IID_IDOMProgressEvent, riid))
+        return &This->IDOMProgressEvent_iface;
+    return NULL;
+}
+
 static const tid_t DOMEvent_iface_tids[] = {
     IDOMEvent_tid,
     0
@@ -2365,6 +2485,19 @@ dispex_static_data_t DOMMessageEvent_dispex = {
     DOMMessageEvent_iface_tids
 };
 
+static const tid_t DOMProgressEvent_iface_tids[] = {
+    IDOMEvent_tid,
+    IDOMProgressEvent_tid,
+    0
+};
+
+dispex_static_data_t DOMProgressEvent_dispex = {
+    L"ProgressEvent",
+    NULL,
+    DispDOMProgressEvent_tid,
+    DOMProgressEvent_iface_tids
+};
+
 static BOOL check_event_iface(nsIDOMEvent *event, REFIID riid)
 {
     nsISupports *iface;
@@ -2404,6 +2537,15 @@ static DOMEvent *alloc_event(nsIDOMEvent *nsevent, compat_mode_t compat_mode, ev
         message_event->event.destroy = DOMMessageEvent_destroy;
         event = &message_event->event;
         dispex_data = &DOMMessageEvent_dispex;
+    }else if(event_info[event_id].type == EVENT_TYPE_PROGRESS && compat_mode >= COMPAT_MODE_IE10) {
+        DOMProgressEvent *progress_event = heap_alloc_zero(sizeof(*progress_event));
+        if(!progress_event)
+            return NULL;
+
+        progress_event->IDOMProgressEvent_iface.lpVtbl = &DOMProgressEventVtbl;
+        progress_event->event.query_interface = DOMProgressEvent_query_interface;
+        event = &progress_event->event;
+        dispex_data = &DOMProgressEvent_dispex;
     }else {
         event = heap_alloc_zero(sizeof(*event));
         if(!event)
diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h
index d49b06d..58dbc43 100644
--- a/dlls/mshtml/htmlevent.h
+++ b/dlls/mshtml/htmlevent.h
@@ -56,6 +56,7 @@ typedef enum {
     EVENTID_SELECTIONCHANGE,
     EVENTID_SELECTSTART,
     EVENTID_SUBMIT,
+    EVENTID_TIMEOUT,
     EVENTID_UNLOAD,
     EVENTID_LAST
 } eventid_t;
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h
index 5574272..ad1fca0 100644
--- a/dlls/mshtml/mshtml_private.h
+++ b/dlls/mshtml/mshtml_private.h
@@ -89,6 +89,7 @@ typedef struct EventTarget EventTarget;
     XDIID(DispDOMKeyboardEvent) \
     XDIID(DispDOMMessageEvent) \
     XDIID(DispDOMMouseEvent) \
+    XDIID(DispDOMProgressEvent) \
     XDIID(DispDOMUIEvent) \
     XDIID(DispHTMLAnchorElement) \
     XDIID(DispHTMLAreaElement) \
@@ -150,6 +151,7 @@ typedef struct EventTarget EventTarget;
     XIID(IDOMKeyboardEvent) \
     XIID(IDOMMessageEvent) \
     XIID(IDOMMouseEvent) \
+    XIID(IDOMProgressEvent) \
     XIID(IDOMUIEvent) \
     XIID(IDocumentEvent) \
     XIID(IDocumentRange) \
diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c
index da31c4d..401e130 100644
--- a/dlls/mshtml/tests/script.c
+++ b/dlls/mshtml/tests/script.c
@@ -3046,11 +3046,23 @@ typedef struct {
     IStream *stream;
     char *data;
     ULONG size;
+    LONG delay;
     char *ptr;
 
     IUri *uri;
 } ProtocolHandler;
 
+static DWORD WINAPI delay_proc(void *arg)
+{
+    PROTOCOLDATA protocol_data = {PI_FORCE_ASYNC};
+    ProtocolHandler *protocol_handler = arg;
+
+    Sleep(protocol_handler->delay);
+    protocol_handler->delay = -1;
+    IInternetProtocolSink_Switch(protocol_handler->sink, &protocol_data);
+    return 0;
+}
+
 static void report_data(ProtocolHandler *This)
 {
     IServiceProvider *service_provider;
@@ -3061,27 +3073,36 @@ static void report_data(ProtocolHandler *This)
 
     static const WCHAR emptyW[] = {0};
 
-    hres = IInternetProtocolSink_QueryInterface(This->sink, &IID_IServiceProvider, (void**)&service_provider);
-    ok(hres == S_OK, "Could not get IServiceProvider iface: %08lx\n", hres);
+    if(This->delay >= 0) {
+        hres = IInternetProtocolSink_QueryInterface(This->sink, &IID_IServiceProvider, (void**)&service_provider);
+        ok(hres == S_OK, "Could not get IServiceProvider iface: %08lx\n", hres);
 
-    hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, &IID_IHttpNegotiate, (void**)&http_negotiate);
-    IServiceProvider_Release(service_provider);
-    ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08lx\n", hres);
+        hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, &IID_IHttpNegotiate, (void**)&http_negotiate);
+        IServiceProvider_Release(service_provider);
+        ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08lx\n", hres);
 
-    hres = IUri_GetDisplayUri(This->uri, &url);
-    ok(hres == S_OK, "Failed to get display uri: %08lx\n", hres);
-    hres = IHttpNegotiate_BeginningTransaction(http_negotiate, url, emptyW, 0, &addl_headers);
-    ok(hres == S_OK, "BeginningTransaction failed: %08lx\n", hres);
-    SysFreeString(url);
+        hres = IUri_GetDisplayUri(This->uri, &url);
+        ok(hres == S_OK, "Failed to get display uri: %08lx\n", hres);
+        hres = IHttpNegotiate_BeginningTransaction(http_negotiate, url, emptyW, 0, &addl_headers);
+        ok(hres == S_OK, "BeginningTransaction failed: %08lx\n", hres);
+        SysFreeString(url);
 
-    CoTaskMemFree(addl_headers);
+        CoTaskMemFree(addl_headers);
 
-    headers = SysAllocString(L"HTTP/1.1 200 OK\r\n\r\n");
-    hres = IHttpNegotiate_OnResponse(http_negotiate, 200, headers, NULL, NULL);
-    ok(hres == S_OK, "OnResponse failed: %08lx\n", hres);
-    SysFreeString(headers);
+        headers = SysAllocString(L"HTTP/1.1 200 OK\r\n\r\n");
+        hres = IHttpNegotiate_OnResponse(http_negotiate, 200, headers, NULL, NULL);
+        ok(hres == S_OK, "OnResponse failed: %08lx\n", hres);
+        SysFreeString(headers);
 
-    IHttpNegotiate_Release(http_negotiate);
+        IHttpNegotiate_Release(http_negotiate);
+
+        if(This->delay) {
+            IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
+            QueueUserWorkItem(delay_proc, This, 0);
+            return;
+        }
+    }
+    This->delay = 0;
 
     hres = IInternetProtocolSink_ReportData(This->sink, BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION,
                                             This->size, This->size);
@@ -3250,6 +3271,12 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA
 
 static HRESULT WINAPI Protocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason, DWORD dwOptions)
 {
+    ProtocolHandler *This = impl_from_IInternetProtocolEx(iface);
+    if(This->delay > 0) {
+        ok(hrReason == E_ABORT, "Abort hrReason = %08lx\n", hrReason);
+        ok(dwOptions == 0, "Abort dwOptions = %lx\n", dwOptions);
+        return S_OK;
+    }
     trace("Abort(%08lx %lx)\n", hrReason, dwOptions);
     return E_NOTIMPL;
 }
@@ -3322,8 +3349,8 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
 {
     ProtocolHandler *This = impl_from_IInternetProtocolEx(iface);
     BOOL block = FALSE;
+    BSTR path, query;
     DWORD bindf;
-    BSTR path;
     HRSRC src;
     HRESULT hres;
 
@@ -3391,6 +3418,13 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
     if(FAILED(hres))
         return hres;
 
+    hres = IUri_GetQuery(uri, &query);
+    if(SUCCEEDED(hres)) {
+        if(!lstrcmpW(query, L"?delay"))
+            This->delay = 1000;
+        SysFreeString(query);
+    }
+
     IInternetProtocolSink_AddRef(This->sink = pOIProtSink);
     IUri_AddRef(This->uri = uri);
 
@@ -3723,6 +3757,7 @@ static void run_js_tests(void)
     init_protocol_handler();
 
     run_script_as_http_with_mode("xhr.js", NULL, "9");
+    run_script_as_http_with_mode("xhr.js", NULL, "10");
     run_script_as_http_with_mode("xhr.js", NULL, "11");
     run_script_as_http_with_mode("dom.js", NULL, "11");
     run_script_as_http_with_mode("es5.js", NULL, "11");
diff --git a/dlls/mshtml/tests/xhr.js b/dlls/mshtml/tests/xhr.js
index cb7e461..6228f73 100644
--- a/dlls/mshtml/tests/xhr.js
+++ b/dlls/mshtml/tests/xhr.js
@@ -28,6 +28,7 @@ function test_xhr() {
         if(complete_cnt++)
             next_test();
     }
+    xhr.ontimeout = function() { ok(false, "ontimeout called"); }
     var onload_func = xhr.onload = function() {
         ok(xhr.statusText === "OK", "statusText = " + xhr.statusText);
         if(complete_cnt++)
@@ -40,6 +41,38 @@ function test_xhr() {
     xhr.send("Testing...");
 }
 
+function test_timeout() {
+    var xhr = new XMLHttpRequest();
+    var v = document.documentMode;
+
+    xhr.onreadystatechange = function() {
+        if(xhr.readyState != 4)
+            return;
+        todo_wine_if(v < 10).
+        ok(v >= 10, "onreadystatechange called");
+    }
+    xhr.onload = function() { ok(false, "onload called"); }
+    xhr.ontimeout = function(e) {
+        var r = Object.prototype.toString.call(e);
+        todo_wine.
+        ok(r === ("[object " + (v < 10 ? "Event" : "ProgressEvent") + "]"), "Object.toString = " + r);
+        var props = [ "initProgressEvent", "lengthComputable", "loaded", "total" ];
+        for(r = 0; r < props.length; r++) {
+            if(v < 10)
+                ok(!(props[r] in e), props[r] + " is available");
+            else
+                ok(props[r] in e, props[r] + " not available");
+        }
+        next_test();
+    }
+
+    xhr.open("POST", "echo.php?delay", true);
+    xhr.setRequestHeader("X-Test", "True");
+    xhr.timeout = 10;
+    xhr.send("Timeout Test");
+}
+
 var tests = [
-    test_xhr
+    test_xhr,
+    test_timeout
 ];
diff --git a/dlls/mshtml/xmlhttprequest.c b/dlls/mshtml/xmlhttprequest.c
index c9f7149..966dc8d 100644
--- a/dlls/mshtml/xmlhttprequest.c
+++ b/dlls/mshtml/xmlhttprequest.c
@@ -97,6 +97,7 @@ static HRESULT return_nscstr(nsresult nsres, nsACString *nscstr, BSTR *p)
 static const eventid_t events[] = {
     EVENTID_READYSTATECHANGE,
     EVENTID_LOAD,
+    EVENTID_TIMEOUT,
 };
 
 typedef struct {
@@ -812,18 +813,18 @@ static HRESULT WINAPI HTMLXMLHttpRequest2_put_ontimeout(IHTMLXMLHttpRequest2 *if
 {
     HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest2(iface);
 
-    FIXME("(%p)->(%s)\n", This, debugstr_variant(&v));
+    TRACE("(%p)->(%s)\n", This, debugstr_variant(&v));
 
-    return E_NOTIMPL;
+    return set_event_handler(&This->event_target, EVENTID_TIMEOUT, &v);
 }
 
 static HRESULT WINAPI HTMLXMLHttpRequest2_get_ontimeout(IHTMLXMLHttpRequest2 *iface, VARIANT *p)
 {
     HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest2(iface);
 
-    FIXME("(%p)->(%p)\n", This, p);
+    TRACE("(%p)->(%p)\n", This, p);
 
-    return E_NOTIMPL;
+    return get_event_handler(&This->event_target, EVENTID_TIMEOUT, p);
 }
 
 static const IHTMLXMLHttpRequest2Vtbl HTMLXMLHttpRequest2Vtbl = {
-- 
2.34.1




More information about the wine-devel mailing list