From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 2 +- dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/nsiface.idl | 10 +++ dlls/mshtml/omnavigator.c | 147 ++++++++++++++++++++++++++++++++++- dlls/mshtml/tests/es5.js | 63 ++++++++++++++- dlls/mshtml/tests/script.c | 22 ++++++ 6 files changed, 240 insertions(+), 5 deletions(-)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index d978b626be6..92daa60ff11 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -3337,7 +3337,7 @@ HRESULT create_storage_event(HTMLDocumentNode *doc, BSTR key, BSTR old_value, BS return S_OK; }
-static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv) +HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv) { IDispatchEx *dispex; EXCEPINFO ei; diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index e2e10f91de1..8a14d2ee47f 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1055,6 +1055,7 @@ void init_node_cc(void) DECLSPEC_HIDDEN;
HRESULT nsuri_to_url(LPCWSTR,BOOL,BSTR*) DECLSPEC_HIDDEN;
+HRESULT call_disp_func(IDispatch*,DISPPARAMS*,VARIANT*) DECLSPEC_HIDDEN; void call_property_onchanged(ConnectionPointContainer*,DISPID) DECLSPEC_HIDDEN; HRESULT call_set_active_object(IOleInPlaceUIWindow*,IOleInPlaceActiveObject*) DECLSPEC_HIDDEN;
diff --git a/dlls/mshtml/nsiface.idl b/dlls/mshtml/nsiface.idl index 8ad5e871e21..5991e0de45b 100644 --- a/dlls/mshtml/nsiface.idl +++ b/dlls/mshtml/nsiface.idl @@ -923,6 +923,16 @@ interface nsIDOMMediaQueryList : nsISupports nsresult SetListener(nsIDOMMediaQueryListListener *listener); }
+[ + object, + uuid(279a5cbd-5c15-475d-847b-e0de1624eb77), + local +] +interface nsIDOMMediaQueryListListener : nsISupports +{ + nsresult HandleChange(nsIDOMMediaQueryList *mql); +} + [ object, uuid(450cf0ba-de90-4f86-85bf-e10cc8b8713f), diff --git a/dlls/mshtml/omnavigator.c b/dlls/mshtml/omnavigator.c index c2a580ceeb3..b95d2d74e93 100644 --- a/dlls/mshtml/omnavigator.c +++ b/dlls/mshtml/omnavigator.c @@ -2651,11 +2651,25 @@ void create_console(compat_mode_t compat_mode, IWineMSHTMLConsole **ret) *ret = &obj->IWineMSHTMLConsole_iface; }
+struct media_query_list_listener { + struct list entry; + IDispatch *function; +}; + +struct media_query_list_callback; struct media_query_list { DispatchEx dispex; IWineMSHTMLMediaQueryList IWineMSHTMLMediaQueryList_iface; LONG ref; nsIDOMMediaQueryList *nsquerylist; + struct media_query_list_callback *callback; + struct list listeners; +}; + +struct media_query_list_callback { + nsIDOMMediaQueryListListener nsIDOMMediaQueryListListener_iface; + struct media_query_list *media_query_list; + LONG ref; };
static inline struct media_query_list *impl_from_IWineMSHTMLMediaQueryList(IWineMSHTMLMediaQueryList *iface) @@ -2696,10 +2710,17 @@ static ULONG WINAPI media_query_list_Release(IWineMSHTMLMediaQueryList *iface) { struct media_query_list *media_query_list = impl_from_IWineMSHTMLMediaQueryList(iface); LONG ref = InterlockedDecrement(&media_query_list->ref); + struct media_query_list_listener *listener, *listener2;
TRACE("(%p) ref=%ld\n", media_query_list, ref);
if(!ref) { + media_query_list->callback->media_query_list = NULL; + LIST_FOR_EACH_ENTRY_SAFE(listener, listener2, &media_query_list->listeners, struct media_query_list_listener, entry) { + IDispatch_Release(listener->function); + free(listener); + } + nsIDOMMediaQueryListListener_Release(&media_query_list->callback->nsIDOMMediaQueryListListener_iface); nsIDOMMediaQueryList_Release(media_query_list->nsquerylist); release_dispex(&media_query_list->dispex); free(media_query_list); @@ -2773,10 +2794,24 @@ static HRESULT WINAPI media_query_list_get_matches(IWineMSHTMLMediaQueryList *if static HRESULT WINAPI media_query_list_addListener(IWineMSHTMLMediaQueryList *iface, VARIANT *listener) { struct media_query_list *media_query_list = impl_from_IWineMSHTMLMediaQueryList(iface); + struct media_query_list_listener *entry;
- FIXME("(%p)->(%s)\n", media_query_list, debugstr_variant(listener)); + TRACE("(%p)->(%s)\n", media_query_list, debugstr_variant(listener));
- return E_NOTIMPL; + if(V_VT(listener) != VT_DISPATCH || !V_DISPATCH(listener)) + return S_OK; + + LIST_FOR_EACH_ENTRY(entry, &media_query_list->listeners, struct media_query_list_listener, entry) + if(entry->function == V_DISPATCH(listener)) + return S_OK; + + if(!(entry = malloc(sizeof(*entry)))) + return E_OUTOFMEMORY; + entry->function = V_DISPATCH(listener); + IDispatch_AddRef(V_DISPATCH(listener)); + + list_add_tail(&media_query_list->listeners, &entry->entry); + return S_OK; }
static HRESULT WINAPI media_query_list_removeListener(IWineMSHTMLMediaQueryList *iface, VARIANT *listener) @@ -2802,6 +2837,101 @@ static const IWineMSHTMLMediaQueryListVtbl media_query_list_vtbl = { media_query_list_removeListener };
+static inline struct media_query_list_callback *impl_from_nsIDOMMediaQueryListListener(nsIDOMMediaQueryListListener *iface) +{ + return CONTAINING_RECORD(iface, struct media_query_list_callback, nsIDOMMediaQueryListListener_iface); +} + +static nsresult NSAPI media_query_list_callback_QueryInterface(nsIDOMMediaQueryListListener *iface, + nsIIDRef riid, void **result) +{ + struct media_query_list_callback *callback = impl_from_nsIDOMMediaQueryListListener(iface); + + if(IsEqualGUID(&IID_nsISupports, riid) || IsEqualGUID(&IID_nsIDOMMediaQueryListListener, riid)) { + *result = &callback->nsIDOMMediaQueryListListener_iface; + }else { + *result = NULL; + return NS_NOINTERFACE; + } + + nsIDOMMediaQueryListListener_AddRef(&callback->nsIDOMMediaQueryListListener_iface); + return NS_OK; +} + +static nsrefcnt NSAPI media_query_list_callback_AddRef(nsIDOMMediaQueryListListener *iface) +{ + struct media_query_list_callback *callback = impl_from_nsIDOMMediaQueryListListener(iface); + LONG ref = InterlockedIncrement(&callback->ref); + + TRACE("(%p) ref=%ld\n", callback, ref); + + return ref; +} + +static nsrefcnt NSAPI media_query_list_callback_Release(nsIDOMMediaQueryListListener *iface) +{ + struct media_query_list_callback *callback = impl_from_nsIDOMMediaQueryListListener(iface); + LONG ref = InterlockedDecrement(&callback->ref); + + TRACE("(%p) ref=%ld\n", callback, ref); + + if(!ref) + free(callback); + return ref; +} + +static nsresult NSAPI media_query_list_callback_HandleChange(nsIDOMMediaQueryListListener *iface, nsIDOMMediaQueryList *mql) +{ + struct media_query_list_callback *callback = impl_from_nsIDOMMediaQueryListListener(iface); + IDispatch *listener_funcs_buf[4], **listener_funcs = listener_funcs_buf; + struct media_query_list *media_query_list = callback->media_query_list; + struct media_query_list_listener *listener; + unsigned cnt, i = 0; + VARIANT args[1], v; + HRESULT hres; + + if(!media_query_list) + return NS_OK; + + cnt = list_count(&media_query_list->listeners); + if(cnt > ARRAY_SIZE(listener_funcs_buf) && !(listener_funcs = malloc(cnt * sizeof(*listener_funcs)))) + return NS_ERROR_OUT_OF_MEMORY; + + LIST_FOR_EACH_ENTRY(listener, &media_query_list->listeners, struct media_query_list_listener, entry) { + listener_funcs[i] = listener->function; + IDispatch_AddRef(listener_funcs[i++]); + } + + for(i = 0; i < cnt; i++) { + DISPPARAMS dp = { args, NULL, 1, 0 }; + + V_VT(args) = VT_DISPATCH; + V_DISPATCH(args) = (IDispatch*)&media_query_list->dispex.IDispatchEx_iface; + V_VT(&v) = VT_EMPTY; + + TRACE("%p >>>\n", media_query_list); + hres = call_disp_func(listener_funcs[i], &dp, &v); + if(hres == S_OK) { + TRACE("%p <<< %s\n", media_query_list, debugstr_variant(&v)); + VariantClear(&v); + }else { + WARN("%p <<< %08lx\n", media_query_list, hres); + } + IDispatch_Release(listener_funcs[i]); + } + + if(listener_funcs != listener_funcs_buf) + free(listener_funcs); + return NS_OK; +} + +static const nsIDOMMediaQueryListListenerVtbl media_query_list_callback_vtbl = { + media_query_list_callback_QueryInterface, + media_query_list_callback_AddRef, + media_query_list_callback_Release, + media_query_list_callback_HandleChange +}; + static const tid_t media_query_list_iface_tids[] = { IWineMSHTMLMediaQueryList_tid, 0 @@ -2826,10 +2956,19 @@ HRESULT create_media_query_list(HTMLWindow *window, BSTR media_query, IDispatch if(!(media_query_list = malloc(sizeof(*media_query_list)))) return E_OUTOFMEMORY;
+ if(!(media_query_list->callback = malloc(sizeof(*media_query_list->callback)))) { + free(media_query_list); + return E_OUTOFMEMORY; + } + media_query_list->callback->nsIDOMMediaQueryListListener_iface.lpVtbl = &media_query_list_callback_vtbl; + media_query_list->callback->media_query_list = media_query_list; + media_query_list->callback->ref = 1; + nsAString_InitDepend(&nsstr, media_query); nsres = nsIDOMWindow_MatchMedia(window->outer_window->nswindow, &nsstr, &nsunk); nsAString_Finish(&nsstr); if(NS_FAILED(nsres)) { + free(media_query_list->callback); free(media_query_list); return map_nsresult(nsres); } @@ -2837,8 +2976,12 @@ HRESULT create_media_query_list(HTMLWindow *window, BSTR media_query, IDispatch assert(NS_SUCCEEDED(nsres)); nsISupports_Release(nsunk);
+ nsres = nsIDOMMediaQueryList_SetListener(media_query_list->nsquerylist, &media_query_list->callback->nsIDOMMediaQueryListListener_iface); + assert(NS_SUCCEEDED(nsres)); + media_query_list->IWineMSHTMLMediaQueryList_iface.lpVtbl = &media_query_list_vtbl; media_query_list->ref = 1; + list_init(&media_query_list->listeners); init_dispatch(&media_query_list->dispex, (IUnknown*)&media_query_list->IWineMSHTMLMediaQueryList_iface, &media_query_list_dispex, dispex_compat_mode(&window->inner_window->event_target.dispex));
diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index b2f63c77c83..8bd8d8e7d06 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -2071,8 +2071,8 @@ sync_test("console", function() { ok(except, "console.timeLog: expected exception"); });
-sync_test("matchMedia", function() { - var i, r, mql; +async_test("matchMedia", function() { + var i, r, mql, expect, event_fired, event2_fired;
try { mql = window.matchMedia(""); @@ -2094,6 +2094,65 @@ sync_test("matchMedia", function() { } mql = window.matchMedia("(max-width: 1000px)"); ok(mql.matches === true, "(max-width: 1000px) does not match"); + mql = window.matchMedia("(max-width: 50px)"); + ok(mql.matches === false, "(max-width: 50px) matches"); + + ok(!("addEventListener" in mql), "addEventListener in MediaQueryList"); + ok(!("removeEventListener" in mql), "removeEventListener in MediaQueryList"); + r = mql.addListener(null); + ok(r === undefined, "addListener with null returned " + r); + r = mql.addListener("function() { ok(false, 'string handler called'); }"); + ok(r === undefined, "addListener with string returned " + r); + + var handler = function(e) { + ok(this === window, "handler this = " + this); + ok(e === mql, "handler argument = " + e); + event_fired = true; + ok(event2_fired !== true, "second handler fired before first"); + } + var handler2 = function(e) { + ok(this === window, "handler2 this = " + this); + ok(e === mql, "handler2 argument = " + e); + event2_fired = true; + } + var tests = [ + [ 20, 20, function() { + var r = mql.addListener(handler); + ok(r === undefined, "addListener with function returned " + r); + }], + [ 120, 120, function() { + ok(event_fired === true, "event not fired after changing from 20x20 to 120x120 view"); + mql.addListener(null); + mql.addListener("function() { ok(false, 'second string handler called'); }"); + mql.addListener(handler2); + }], + [ 30, 30, function() { + ok(event_fired === true, "event not fired after changing from 120x120 to 30x30 view"); + ok(event2_fired === true, "event not fired from second handler after changing from 120x120 to 30x30 view"); + }], + [ 300, 300, function() { + }] + ]; + + function test() { + tests[i][2](); + if(++i >= tests.length) { + next_test(); + return; + } + expect = !expect; + event_fired = event2_fired = false; + external.setViewSize(tests[i][0], tests[i][1]); + window.setTimeout(check); + } + + // async dispatch once even after change confirmed, to ensure that any possible listeners are dispatched first (or not) + function check() { window.setTimeout(mql.matches === expect ? test : check); } + + i = 0; + expect = !mql.matches; + external.setViewSize(tests[i][0], tests[i][1]); + window.setTimeout(check); });
sync_test("initProgressEvent", function() { diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index cd27a05e397..2dc68f1456e 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -170,6 +170,7 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_TEST_VARS 0x30000B #define DISPID_EXTERNAL_TESTHOSTCTX 0x30000C #define DISPID_EXTERNAL_GETMIMETYPE 0x30000D +#define DISPID_EXTERNAL_SETVIEWSIZE 0x30000E
static const GUID CLSID_TestScript[] = { {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}, @@ -897,6 +898,10 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_GETMIMETYPE; return S_OK; } + if(!lstrcmpW(bstrName, L"setViewSize")) { + *pid = DISPID_EXTERNAL_SETVIEWSIZE; + return S_OK; + }
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -1158,6 +1163,23 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID V_VT(pvarRes) = V_BSTR(pvarRes) ? VT_BSTR : VT_NULL; return S_OK;
+ case DISPID_EXTERNAL_SETVIEWSIZE: { + RECT rect = { 0 }; + + ok(pdp != NULL, "pdp == NULL\n"); + ok(pdp->rgvarg != NULL, "rgvarg == NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(pdp->cArgs == 2, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pei != NULL, "pei == NULL\n"); + ok(V_VT(&pdp->rgvarg[1]) == VT_I4, "width VT = %d\n", V_VT(&pdp->rgvarg[1])); + ok(V_VT(&pdp->rgvarg[0]) == VT_I4, "height VT = %d\n", V_VT(&pdp->rgvarg[0])); + + rect.right = V_I4(&pdp->rgvarg[1]); + rect.bottom = V_I4(&pdp->rgvarg[0]); + return IOleDocumentView_SetRect(view, &rect); + } + default: ok(0, "unexpected call\n"); return E_NOTIMPL;