From 02994bdd108b6bcdea3f630a56133f754f04f5ec Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 24 Jul 2023 09:51:27 +0100 Subject: [PATCH] mshtml: Implement window.MutationObserver with MutationObserver stub. --- dlls/mshtml/htmlwindow.c | 33 ++- dlls/mshtml/mshtml_private.h | 6 +- dlls/mshtml/mshtml_private_iface.idl | 14 ++ dlls/mshtml/mutation.c | 293 +++++++++++++++++++++++++++ dlls/mshtml/tests/documentmode.js | 58 ++++++ 5 files changed, 402 insertions(+), 2 deletions(-) diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index c6072c7e9fa..5abe13c96e6 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -309,6 +309,9 @@ static void release_inner_window(HTMLInnerWindow *This) if(This->mon) IMoniker_Release(This->mon); + if(This->mutation_observer_ctor) + IDispatch_Release(This->mutation_observer_ctor); + free(This); } @@ -3330,6 +3333,26 @@ static HRESULT WINAPI window_private_get_console(IWineHTMLWindowPrivate *iface, return S_OK; } +static HRESULT WINAPI window_private_get_MutationObserver(IWineHTMLWindowPrivate *iface, + IDispatch **mutation_observer) +{ + HTMLWindow *This = impl_from_IWineHTMLWindowPrivateVtbl(iface); + HRESULT hres; + + TRACE("iface %p, mutation_observer %p.\n", iface, mutation_observer); + + if (!This->inner_window->mutation_observer_ctor) { + hres = create_mutation_observer_ctor(dispex_compat_mode(&This->inner_window->event_target.dispex), + &This->inner_window->mutation_observer_ctor); + if (FAILED(hres)) + return hres; + } + + IDispatch_AddRef(This->inner_window->mutation_observer_ctor); + *mutation_observer = This->inner_window->mutation_observer_ctor; + return S_OK; +} + static const IWineHTMLWindowPrivateVtbl WineHTMLWindowPrivateVtbl = { window_private_QueryInterface, window_private_AddRef, @@ -3343,6 +3366,7 @@ static const IWineHTMLWindowPrivateVtbl WineHTMLWindowPrivateVtbl = { window_private_get_console, window_private_matchMedia, window_private_postMessage, + window_private_get_MutationObserver }; static inline HTMLWindow *impl_from_IWineHTMLWindowCompatPrivateVtbl(IWineHTMLWindowCompatPrivate *iface) @@ -3975,12 +3999,19 @@ static void HTMLWindow_init_dispex_info(dispex_data_t *info, compat_mode_t compa {DISPID_UNKNOWN} }; + /* Hide props not available in IE10 */ + static const dispex_hook_t private_ie10_hooks[] = { + {DISPID_IWINEHTMLWINDOWPRIVATE_MUTATIONOBSERVER}, + {DISPID_UNKNOWN} + }; + if(compat_mode >= COMPAT_MODE_IE9) dispex_info_add_interface(info, IHTMLWindow7_tid, NULL); else dispex_info_add_interface(info, IWineHTMLWindowCompatPrivate_tid, NULL); if(compat_mode >= COMPAT_MODE_IE10) - dispex_info_add_interface(info, IWineHTMLWindowPrivate_tid, NULL); + dispex_info_add_interface(info, IWineHTMLWindowPrivate_tid, + compat_mode >= COMPAT_MODE_IE11 ? NULL : private_ie10_hooks); dispex_info_add_interface(info, IHTMLWindow5_tid, NULL); dispex_info_add_interface(info, IHTMLWindow4_tid, compat_mode >= COMPAT_MODE_IE11 ? window4_ie11_hooks : NULL); diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 9a606c4e501..803e1e38b4d 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -294,7 +294,8 @@ typedef struct EventTarget EventTarget; XIID(IWinePageTransitionEvent) \ XIID(IWineXMLHttpRequestPrivate) \ XIID(IWineMSHTMLConsole) \ - XIID(IWineMSHTMLMediaQueryList) + XIID(IWineMSHTMLMediaQueryList) \ + XIID(IWineMSHTMLMutationObserver) typedef enum { #define XIID(iface) iface ## _tid, @@ -636,6 +637,7 @@ struct HTMLInnerWindow { LONG task_magic; IMoniker *mon; + IDispatch *mutation_observer_ctor; nsChannelBSC *bscallback; struct list bindings; }; @@ -1496,3 +1498,5 @@ IInternetSecurityManager *get_security_manager(void); extern HINSTANCE hInst; void create_console(compat_mode_t compat_mode, IWineMSHTMLConsole **ret); HRESULT create_media_query_list(HTMLWindow *window, BSTR media_query, IDispatch **ret); + +HRESULT create_mutation_observer_ctor(compat_mode_t compat_mode, IDispatch **ret); diff --git a/dlls/mshtml/mshtml_private_iface.idl b/dlls/mshtml/mshtml_private_iface.idl index c0bb30fbbc8..46bae638fa5 100644 --- a/dlls/mshtml/mshtml_private_iface.idl +++ b/dlls/mshtml/mshtml_private_iface.idl @@ -76,6 +76,17 @@ interface IWineMSHTMLConsole : IDispatch HRESULT warn([in, optional] VARIANT *varargStart); } +[ + odl, + oleautomation, + dual, + hidden, + uuid(6ac5491e-1758-4b82-98a2-83e31a7c8871) +] +interface IWineMSHTMLMutationObserver : IDispatch +{ +} + [ odl, oleautomation, @@ -95,6 +106,7 @@ interface IWineMSHTMLMediaQueryList : IDispatch HRESULT removeListener([in] VARIANT *listener); } +const long DISPID_IWINEHTMLWINDOWPRIVATE_MUTATIONOBSERVER = 55; [ odl, oleautomation, @@ -114,6 +126,8 @@ interface IWineHTMLWindowPrivate : IDispatch HRESULT matchMedia([in] BSTR media_query, [retval, out] IDispatch **media_query_list); [id(54)] HRESULT postMessage([in] VARIANT msg, [in] BSTR targetOrigin, [in, optional] VARIANT transfer); + [propget, id(DISPID_IWINEHTMLWINDOWPRIVATE_MUTATIONOBSERVER)] + HRESULT MutationObserver([retval, out] IDispatch **observer_ctor); } [ diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index 83ab623aea8..09b2098bcb3 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -1076,3 +1076,296 @@ void init_mutation(nsIComponentManager *component_manager) if(NS_FAILED(nsres)) ERR("Could not create nsIContentUtils instance: %08lx\n", nsres); } + +struct mutation_observer { + IWineMSHTMLMutationObserver IWineMSHTMLMutationObserver_iface; + + LONG ref; + DispatchEx dispex; + IDispatch *callback; +}; + +static inline struct mutation_observer *impl_from_IWineMSHTMLMutationObserver(IWineMSHTMLMutationObserver *iface) +{ + return CONTAINING_RECORD(iface, struct mutation_observer, IWineMSHTMLMutationObserver_iface); +} + +static HRESULT WINAPI MutationObserver_QueryInterface(IWineMSHTMLMutationObserver *iface, REFIID riid, void **ppv) +{ + struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IWineMSHTMLMutationObserver, riid)) { + *ppv = &This->IWineMSHTMLMutationObserver_iface; + } else if(dispex_query_interface(&This->dispex, riid, ppv)) { + return *ppv ? S_OK : E_NOINTERFACE; + } else { + WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI MutationObserver_AddRef(IWineMSHTMLMutationObserver *iface) +{ + struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + return ref; +} + +static ULONG WINAPI MutationObserver_Release(IWineMSHTMLMutationObserver *iface) +{ + struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if(!ref) { + release_dispex(&This->dispex); + IDispatch_Release(This->callback); + free(This); + } + + return ref; +} + +static HRESULT WINAPI MutationObserver_GetTypeInfoCount(IWineMSHTMLMutationObserver *iface, UINT *pctinfo) +{ + struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface); + FIXME("(%p)->(%p)\n", This, pctinfo); + + return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo); +} + +static HRESULT WINAPI MutationObserver_GetTypeInfo(IWineMSHTMLMutationObserver *iface, UINT iTInfo, + LCID lcid, ITypeInfo **ppTInfo) +{ + struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface); + + return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo); +} + +static HRESULT WINAPI MutationObserver_GetIDsOfNames(IWineMSHTMLMutationObserver *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, + DISPID *rgDispId) +{ + struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface); + + return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames, lcid, + rgDispId); +} + +static HRESULT WINAPI MutationObserver_Invoke(IWineMSHTMLMutationObserver *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + struct mutation_observer *This = impl_from_IWineMSHTMLMutationObserver(iface); + + return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +static const IWineMSHTMLMutationObserverVtbl WineMSHTMLMutationObserverVtbl = { + MutationObserver_QueryInterface, + MutationObserver_AddRef, + MutationObserver_Release, + MutationObserver_GetTypeInfoCount, + MutationObserver_GetTypeInfo, + MutationObserver_GetIDsOfNames, + MutationObserver_Invoke, +}; + +static const tid_t mutation_observer_iface_tids[] = { + IWineMSHTMLMutationObserver_tid, + 0 +}; +static dispex_static_data_t mutation_observer_dispex = { + L"MutationObserver", + NULL, + IWineMSHTMLMutationObserver_tid, + mutation_observer_iface_tids +}; + +static HRESULT create_mutation_observer(compat_mode_t compat_mode, IDispatch *callback, + IWineMSHTMLMutationObserver **ret) +{ + struct mutation_observer *obj; + + TRACE("(compat_mode = %d, callback = %p, ret = %p)\n", compat_mode, callback, ret); + + obj = calloc(1, sizeof(*obj)); + if(!obj) + { + ERR("No memory.\n"); + return E_OUTOFMEMORY; + } + + obj->IWineMSHTMLMutationObserver_iface.lpVtbl = &WineMSHTMLMutationObserverVtbl; + obj->ref = 1; + init_dispatch(&obj->dispex, (IUnknown*)&obj->IWineMSHTMLMutationObserver_iface, + &mutation_observer_dispex, compat_mode); + + IDispatch_AddRef(callback); + obj->callback = callback; + *ret = &obj->IWineMSHTMLMutationObserver_iface; + return S_OK; +} + +struct mutation_observer_ctor { + IUnknown IUnknown_iface; + DispatchEx dispex; + + LONG ref; +}; + +static inline struct mutation_observer_ctor *mutation_observer_ctor_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct mutation_observer_ctor, IUnknown_iface); +} + +static inline struct mutation_observer_ctor *mutation_observer_ctor_from_DispatchEx(DispatchEx *iface) +{ + return CONTAINING_RECORD(iface, struct mutation_observer_ctor, dispex); +} + +static HRESULT WINAPI mutation_observer_ctor_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + struct mutation_observer_ctor *This = mutation_observer_ctor_from_IUnknown(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + + if(IsEqualGUID(&IID_IUnknown, riid)) { + *ppv = &This->IUnknown_iface; + }else if(dispex_query_interface(&This->dispex, riid, ppv)) { + return *ppv ? S_OK : E_NOINTERFACE; + }else { + WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI mutation_observer_ctor_AddRef(IUnknown *iface) +{ + struct mutation_observer_ctor *This = mutation_observer_ctor_from_IUnknown(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + return ref; +} + +static ULONG WINAPI mutation_observer_ctor_Release(IUnknown *iface) +{ + struct mutation_observer_ctor *This = mutation_observer_ctor_from_IUnknown(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + if(!ref) { + release_dispex(&This->dispex); + free(This); + } + + return ref; +} + +static const IUnknownVtbl mutation_observer_ctor_vtbl = { + mutation_observer_ctor_QueryInterface, + mutation_observer_ctor_AddRef, + mutation_observer_ctor_Release, +}; + +static HRESULT mutation_observer_ctor_value(DispatchEx *dispex, LCID lcid, + WORD flags, DISPPARAMS *params, VARIANT *res, EXCEPINFO *ei, + IServiceProvider *caller) +{ + struct mutation_observer_ctor *This = mutation_observer_ctor_from_DispatchEx(dispex); + VARIANT *callback; + IWineMSHTMLMutationObserver *mutation_observer; + HRESULT hres; + int argc = params->cArgs - params->cNamedArgs; + + TRACE("(%p)->(%lx %x %p %p %p %p)\n", This, lcid, flags, params, res, ei, caller); + + switch (flags) { + case DISPATCH_METHOD | DISPATCH_PROPERTYGET: + if (!res) + return E_INVALIDARG; + case DISPATCH_CONSTRUCT: + case DISPATCH_METHOD: + break; + default: + FIXME("flags %x is not supported\n", flags); + return E_NOTIMPL; + } + + if (argc < 1) + return E_UNEXPECTED; + + callback = params->rgvarg + (params->cArgs - 1); + if (V_VT(callback) != VT_DISPATCH) { + FIXME("Should return TypeMismatchError\n"); + return E_FAIL; + } + + if (!res) + return S_OK; + + hres = create_mutation_observer(dispex_compat_mode(&This->dispex), V_DISPATCH(callback), + &mutation_observer); + if (FAILED(hres)) + return hres; + + V_VT(res) = VT_DISPATCH; + V_DISPATCH(res) = (IDispatch*)mutation_observer; + + return S_OK; +} + +static dispex_static_data_vtbl_t mutation_observer_ctor_dispex_vtbl = { + .value = mutation_observer_ctor_value +}; + +static const tid_t mutation_observer_ctor_iface_tids[] = { + 0 +}; + +static dispex_static_data_t mutation_observer_ctor_dispex = { + L"Function", + &mutation_observer_ctor_dispex_vtbl, + NULL_tid, + mutation_observer_ctor_iface_tids +}; + +HRESULT create_mutation_observer_ctor(compat_mode_t compat_mode, IDispatch **ret) +{ + struct mutation_observer_ctor *obj; + + TRACE("(compat_mode = %d, ret = %p)\n", compat_mode, ret); + + obj = calloc(1, sizeof(*obj)); + if(!obj) + { + ERR("No memory.\n"); + return E_OUTOFMEMORY; + } + + obj->IUnknown_iface.lpVtbl = &mutation_observer_ctor_vtbl; + obj->ref = 1; + init_dispatch(&obj->dispex, (IUnknown*)&obj->IUnknown_iface, + &mutation_observer_ctor_dispex, compat_mode); + + *ret = (IDispatch *)&obj->dispex.IDispatchEx_iface; + return S_OK; +} diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 00a6eded3fe..35921ca565f 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -343,6 +343,9 @@ sync_test("builtin_toString", function() { test("console", window.console, "Console"); test("mediaQueryList", window.matchMedia("(hover:hover)"), "MediaQueryList"); } + if(v >= 11) { + test("MutationObserver", new window.MutationObserver(function() {}), "MutationObserver"); + } if(v >= 9) { document.body.innerHTML = ""; test("comment", document.body.firstChild, "Comment"); @@ -475,6 +478,7 @@ sync_test("window_props", function() { test_exposed("performance", true); test_exposed("console", v >= 10); test_exposed("matchMedia", v >= 10); + test_exposed("MutationObserver", v >= 11); }); sync_test("domimpl_props", function() { @@ -2853,6 +2857,60 @@ sync_test("__defineSetter__", function() { ok(x.setterVal === 9, "x.setterVal after setting bar = " + x.setterVal); }); +sync_test("MutationObserver", function() { + if (!window.MutationObserver) { + return; + } + + try { + window.MutationObserver(); + ok(false, "MutationObserver without args should fail"); + } catch(e) { + ok(e.number == 0xffff - 0x80000000, "MutationObserver without new threw exception " + e.number); + } + + try { + window.MutationObserver(42); + ok(false, "MutationObserver with non-function should fail"); + } catch(e) { + todo_wine. + ok(e.name == "TypeMismatchError", "MutationObserver with non-function arg threw exception " + e.name); + } + + try { + window.MutationObserver(function() {}); + } catch(e) { + ok(false, "MutationObserver without new threw exception " + e.number); + } + + try { + new window.MutationObserver(); + ok(false, "MutationObserver with no args should fail"); + } catch(e) { + ok(e.number == 0xffff - 0x80000000, "MutationObserver with no args threw exception " + e.number); + } + + try { + new window.MutationObserver(1); + ok(false, "MutationObserver with non-function arg should fail"); + } catch(e) { + todo_wine. + ok(e.name == "TypeMismatchError", "MutationObserver with non-function arg threw exception " + e.name); + } + + try { + new window.MutationObserver(function() {}); + } catch(e) { + ok(false, "MutationObserver threw exception " + e.number); + } + + try { + new window.MutationObserver(function() {}, 1); + } catch(e) { + ok(false, "MutationObserver with extra args threw exception " + e.number); + } +}) + async_test("postMessage", function() { var v = document.documentMode; var onmessage_called = false;