[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