From: Gabriel Ivăncescu gabrielopcode@gmail.com
It gets fired when the window is minimized, but since we can't subclass the parent window (which is not ours), an internal timer is used that checks the minimized state 30 times per second.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 2 + dlls/mshtml/htmlevent.h | 1 + dlls/mshtml/mshtml_private.h | 2 + dlls/mshtml/mutation.c | 7 +- dlls/mshtml/task.c | 122 +++++++++++++++++++++++++- dlls/mshtml/tests/blank_ie10.html | 3 + dlls/mshtml/tests/documentmode.js | 5 ++ dlls/mshtml/tests/events.c | 137 +++++++++++++++++++++++++----- dlls/mshtml/tests/rsrc.rc | 3 + 9 files changed, 257 insertions(+), 25 deletions(-) create mode 100644 dlls/mshtml/tests/blank_ie10.html
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index f2c862c5b0a..b0fadd71c77 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -206,6 +206,8 @@ static const event_info_t event_info[] = { EVENT_BIND_TO_TARGET}, {L"unload", EVENT_TYPE_UIEVENT, DISPID_EVMETH_ONUNLOAD, EVENT_FIXME}, + {L"visibilitychange", EVENT_TYPE_EVENT, DISPID_EVPROP_VISIBILITYCHANGE, + EVENT_BUBBLES},
/* EVENTID_LAST special entry */ {NULL, EVENT_TYPE_EVENT, 0, 0} diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index f78c4a0f188..97baf9eed7b 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -63,6 +63,7 @@ typedef enum { EVENTID_SUBMIT, EVENTID_TIMEOUT, EVENTID_UNLOAD, + EVENTID_VISIBILITYCHANGE, EVENTID_LAST } eventid_t;
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index e3971258b13..ccf67562aad 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -1278,8 +1278,10 @@ enum timer_type { TIMER_TIMEOUT, TIMER_INTERVAL, TIMER_ANIMATION_FRAME, + TIMER_INTERNAL_POLL, };
+HRESULT set_internal_poll_timer(HTMLInnerWindow*) DECLSPEC_HIDDEN; HRESULT set_task_timer(HTMLInnerWindow*,LONG,enum timer_type,IDispatch*,LONG*) DECLSPEC_HIDDEN; HRESULT clear_task_timer(HTMLInnerWindow*,DWORD) DECLSPEC_HIDDEN; HRESULT clear_animation_timer(HTMLInnerWindow*,DWORD) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index 7166d74cf0c..dc9e2932844 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -383,7 +383,12 @@ compat_mode_t lock_document_mode(HTMLDocumentNode *doc) { TRACE("%p: %d\n", doc, doc->document_mode);
- doc->document_mode_locked = TRUE; + if(!doc->document_mode_locked) { + doc->document_mode_locked = TRUE; + + if(doc->document_mode >= COMPAT_MODE_IE10 && doc->window) + set_internal_poll_timer(doc->window); + } return doc->document_mode; }
diff --git a/dlls/mshtml/task.c b/dlls/mshtml/task.c index 28564f7b8d8..6889b1bcdeb 100644 --- a/dlls/mshtml/task.c +++ b/dlls/mshtml/task.c @@ -29,6 +29,7 @@ #include "wine/debug.h"
#include "mshtml_private.h" +#include "htmlevent.h"
WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
@@ -208,7 +209,7 @@ HRESULT clear_task_timer(HTMLInnerWindow *window, DWORD id)
LIST_FOR_EACH_ENTRY(iter, &thread_data->timer_list, task_timer_t, entry) { if(iter->id == id && iter->window == window) { - if(iter->type != TIMER_ANIMATION_FRAME) + if(iter->type == TIMER_TIMEOUT || iter->type == TIMER_INTERVAL) release_task_timer(thread_data->thread_hwnd, iter); return S_OK; } @@ -244,6 +245,7 @@ static const char *debugstr_timer_type(enum timer_type type) case TIMER_TIMEOUT: return "timeout"; case TIMER_INTERVAL: return "interval"; case TIMER_ANIMATION_FRAME: return "animation-frame"; + case TIMER_INTERNAL_POLL: return "internal-poll"; DEFAULT_UNREACHABLE; } } @@ -432,3 +434,121 @@ ULONGLONG get_time_stamp(void) GetSystemTimeAsFileTime(&time); return (((ULONGLONG)time.dwHighDateTime << 32) + time.dwLowDateTime) / 10000 - time_epoch; } + +struct poll_timer { + IDispatch IDispatch_iface; + LONG ref; + + BOOL was_minimized; + HTMLInnerWindow *window; +}; + +static inline struct poll_timer *poll_timer_from_IDispatch(IDispatch *iface) +{ + return CONTAINING_RECORD(iface, struct poll_timer, IDispatch_iface); +} + +static HRESULT WINAPI poll_timer_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{ + struct poll_timer *This = poll_timer_from_IDispatch(iface); + + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IDispatch, riid)) + *ppv = &This->IDispatch_iface; + else { + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI poll_timer_AddRef(IDispatch *iface) +{ + struct poll_timer *This = poll_timer_from_IDispatch(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI poll_timer_Release(IDispatch *iface) +{ + struct poll_timer *This = poll_timer_from_IDispatch(iface); + ULONG ref = InterlockedDecrement(&This->ref); + if(!ref) + heap_free(This); + return ref; +} + +static HRESULT WINAPI poll_timer_Invoke(IDispatch *iface, DISPID dispIdMember, REFIID riid, LCID lcid, + WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + struct poll_timer *This = poll_timer_from_IDispatch(iface); + HTMLOuterWindow *window = This->window->base.outer_window; + HTMLDocumentNode *doc = This->window->doc; + DOMEvent *event; + HRESULT hres; + + assert(dispIdMember == DISPID_VALUE); + + if(doc && window && window->browser && window->browser->doc) { + BOOL minimized = IsIconic(GetAncestor(window->browser->doc->hwnd, GA_ROOT)); + + if(This->was_minimized != minimized) { + This->was_minimized = minimized; + hres = create_document_event(doc, EVENTID_VISIBILITYCHANGE, &event); + if(SUCCEEDED(hres)) { + dispatch_event(&doc->node.event_target, event); + IDOMEvent_Release(&event->IDOMEvent_iface); + } + } + } + return S_OK; +} + +static const IDispatchVtbl poll_timer_vtbl = { + poll_timer_QueryInterface, + poll_timer_AddRef, + poll_timer_Release, + NULL, + NULL, + NULL, + poll_timer_Invoke +}; + +HRESULT set_internal_poll_timer(HTMLInnerWindow *window) +{ + struct poll_timer *poll_timer_disp; + thread_data_t *thread_data; + task_timer_t *timer; + const DWORD msec = 33; + + thread_data = get_thread_data(TRUE); + if(!thread_data) + return E_OUTOFMEMORY; + + timer = heap_alloc(sizeof(task_timer_t)); + if(!timer) + return E_OUTOFMEMORY; + + poll_timer_disp = heap_alloc(sizeof(*poll_timer_disp)); + if(!poll_timer_disp) { + heap_free(timer); + return E_OUTOFMEMORY; + } + + timer->window = window; + timer->time = GetTickCount() + msec; + timer->interval = msec; + timer->type = TIMER_INTERNAL_POLL; + timer->disp = &poll_timer_disp->IDispatch_iface; + list_init(&timer->entry); + + poll_timer_disp->IDispatch_iface.lpVtbl = &poll_timer_vtbl; + poll_timer_disp->ref = 1; + poll_timer_disp->was_minimized = IsIconic(GetAncestor(window->base.outer_window->browser->doc->hwnd, GA_ROOT)); + poll_timer_disp->window = window; + + if(queue_timer(thread_data, timer)) + SetTimer(thread_data->thread_hwnd, TIMER_ID, msec, NULL); + + return S_OK; +} diff --git a/dlls/mshtml/tests/blank_ie10.html b/dlls/mshtml/tests/blank_ie10.html new file mode 100644 index 00000000000..64e6dfaa172 --- /dev/null +++ b/dlls/mshtml/tests/blank_ie10.html @@ -0,0 +1,3 @@ +<html> +<head><meta http-equiv="x-ua-compatible" content="IE=10" /></head> +</html> diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 5da89349d6d..7d26add1cfc 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -19,6 +19,11 @@ var compat_version; var tests = [];
+if(window.addEventListener) { + document.addEventListener("visibilitychange", function() { ok(false, "visibilitychange fired"); }); +} + + sync_test("builtin_toString", function() { var tags = [ [ "abbr", "Phrase" ], diff --git a/dlls/mshtml/tests/events.c b/dlls/mshtml/tests/events.c index dcd7160a67d..558b166d466 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -97,6 +97,7 @@ DEFINE_EXPECT(submit_onclick_attached_check_cancel); DEFINE_EXPECT(submit_onclick_setret); DEFINE_EXPECT(elem2_cp_onclick); DEFINE_EXPECT(iframe_onload); +DEFINE_EXPECT(visibilitychange); DEFINE_EXPECT(doc1_onstorage); DEFINE_EXPECT(doc1_onstoragecommit); DEFINE_EXPECT(window1_onstorage); @@ -105,6 +106,7 @@ DEFINE_EXPECT(doc2_onstoragecommit); DEFINE_EXPECT(window2_onstorage);
static HWND container_hwnd = NULL; +static BOOL container_minimize; static IHTMLWindow2 *window; static IOleDocumentView *view; static BOOL is_ie9plus; @@ -1406,6 +1408,30 @@ static HRESULT WINAPI iframe_onreadystatechange(IDispatchEx *iface, DISPID id, L
EVENT_HANDLER_FUNC_OBJ(iframe_onreadystatechange);
+static HRESULT WINAPI onvisibilitychange(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + IDispatchEx *dispex; + HRESULT hres; + BSTR bstr; + + CHECK_EXPECT(visibilitychange); + test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); + + hres = IDispatch_QueryInterface(V_DISPATCH(&pdp->rgvarg[1]), &IID_IDispatchEx, (void**)&dispex); + ok(hres == S_OK, "Could not get IDispatchEx: %08lx\n", hres); + + bstr = SysAllocString(L"toString"); + hres = IDispatchEx_GetDispID(dispex, bstr, 0, &id); + todo_wine + ok(hres == S_OK, "GetDispID("toString") failed: %08lx\n", hres); + SysFreeString(bstr); + + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(onvisibilitychange); + static HRESULT WINAPI nocall(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) { @@ -2387,6 +2413,49 @@ static void test_focus(IHTMLDocument2 *doc) IHTMLElement4_Release(div); }
+static void test_visibilitychange(IHTMLDocument2 *doc) +{ + if(!winetest_interactive) { + ShowWindow(container_hwnd, SW_SHOW); + pump_msgs(NULL); + } + add_event_listener((IUnknown*)doc, L"visibilitychange", (IDispatch*)&onvisibilitychange_obj, VARIANT_TRUE); + + ShowWindow(container_hwnd, SW_HIDE); + pump_msgs(NULL); + + ShowWindow(container_hwnd, SW_SHOW); + pump_msgs(NULL); + + container_minimize = TRUE; + if(document_mode < 10) { + ShowWindow(container_hwnd, SW_MINIMIZE); + pump_msgs(NULL); + + container_minimize = FALSE; + ShowWindow(container_hwnd, SW_RESTORE); + pump_msgs(NULL); + }else { + SET_EXPECT(visibilitychange); + ShowWindow(container_hwnd, SW_MINIMIZE); + pump_msgs(&called_visibilitychange); + CHECK_CALLED(visibilitychange); + + container_minimize = FALSE; + SET_EXPECT(visibilitychange); + ShowWindow(container_hwnd, SW_RESTORE); + pump_msgs(&called_visibilitychange); + CHECK_CALLED(visibilitychange); + } + + navigate(doc, document_mode < 10 ? L"blank_ie10.html" : L"blank.html"); + + if(!winetest_interactive) { + ShowWindow(container_hwnd, SW_HIDE); + pump_msgs(NULL); + } +} + static void test_submit(IHTMLDocument2 *doc) { IHTMLElement *elem, *submit; @@ -5091,6 +5160,48 @@ static IHTMLDocument2 *create_document_with_origin(const char *str) return doc; }
+static LRESULT WINAPI wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) { + case WM_SIZE: + if(winetest_interactive) + break; + + /* Sometimes minimizing/restoring isn't reliable due to differences in WMs, + so force it here when pumping messages if it's not what we expected... */ + switch(wParam) { + case SIZE_MINIMIZED: + if(!container_minimize) + ShowWindow(hwnd, SW_RESTORE); + break; + case SIZE_RESTORED: + case SIZE_MAXIMIZED: + if(container_minimize) + ShowWindow(hwnd, SW_MINIMIZE); + break; + } + break; + } + return DefWindowProcA(hwnd, msg, wParam, lParam); +} + +static HWND create_container_window(void) +{ + static const CHAR szHTMLDocumentTest[] = "HTMLDocumentTest"; + static WNDCLASSEXA wndclass = { + sizeof(WNDCLASSEXA), + 0, + wnd_proc, + 0, 0, NULL, NULL, NULL, NULL, NULL, + szHTMLDocumentTest, + NULL + }; + + RegisterClassExA(&wndclass); + return CreateWindowA(szHTMLDocumentTest, szHTMLDocumentTest, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + 300, 300, NULL, NULL, NULL, NULL); +}
typedef void (*testfunc_t)(IHTMLDocument2*);
@@ -5154,6 +5265,7 @@ static void run_test_impl(const char *str, const WCHAR *res, testfunc_t test) ok(hres == S_OK, "get_parentWindow failed: %08lx\n", hres); ok(window != NULL, "window == NULL\n");
+ ok((WNDPROC)GetWindowLongPtrA(container_hwnd, GWLP_WNDPROC) == wnd_proc, "container_hwnd is subclassed\n"); test(doc);
IHTMLWindow2_Release(window); @@ -5176,29 +5288,6 @@ static void run_test_from_res(const WCHAR *res, testfunc_t test) return run_test_impl(NULL, res, test); }
-static LRESULT WINAPI wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - return DefWindowProcA(hwnd, msg, wParam, lParam); -} - -static HWND create_container_window(void) -{ - static const CHAR szHTMLDocumentTest[] = "HTMLDocumentTest"; - static WNDCLASSEXA wndclass = { - sizeof(WNDCLASSEXA), - 0, - wnd_proc, - 0, 0, NULL, NULL, NULL, NULL, NULL, - szHTMLDocumentTest, - NULL - }; - - RegisterClassExA(&wndclass); - return CreateWindowA(szHTMLDocumentTest, szHTMLDocumentTest, - WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, - 300, 300, NULL, NULL, NULL, NULL); -} - static void test_empty_document(void) { HRESULT hres; @@ -5496,6 +5585,8 @@ START_TEST(events) if(is_ie9plus) { run_test_from_res(L"doc_with_prop.html", test_doc_obj); run_test_from_res(L"doc_with_prop_ie9.html", test_doc_obj); + run_test_from_res(L"doc_with_prop_ie9.html", test_visibilitychange); + run_test_from_res(L"blank_ie10.html", test_visibilitychange); run_test(empty_doc_ie9_str, test_create_event); }
diff --git a/dlls/mshtml/tests/rsrc.rc b/dlls/mshtml/tests/rsrc.rc index 9bff32a78e9..01b2838be8b 100644 --- a/dlls/mshtml/tests/rsrc.rc +++ b/dlls/mshtml/tests/rsrc.rc @@ -73,6 +73,9 @@ blank2.html HTML "blank.html" /* @makedep: blank.html */ 123 HTML "blank.html"
+/* @makedep: blank_ie10.html */ +blank_ie10.html HTML "blank_ie10.html" + /* @makedep: doc_with_prop.html */ doc_with_prop.html HTML "doc_with_prop.html"