This implements some events related to document/window load and unload / visibility changes. The latter is related in that native partially implements it, and only sends it when it is minimized or restored from being minimized, not on navigation or anything else.
The main issue for me is the visibilitychange patch. Since we don't control the container hwnd, and it can even be in another process, there is no way to be notified cleanly when it changes its minimization state. We can't subclass it either, as the tests show, it's not subclassed.
I implemented it now using an internal timer that polls for that, since it was the least invasive and works in all cases, including other process hwnd. Hooks wouldn't be able to do that, and are also more invasive IMO. I'm hoping for better ideas, though.
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"
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 120 +++++++++++++++++++++++++++ dlls/mshtml/htmlevent.h | 1 + dlls/mshtml/mshtml_private.h | 1 + dlls/mshtml/mshtml_private_iface.idl | 14 ++++ dlls/mshtml/nsevents.c | 8 ++ dlls/mshtml/tests/documentmode.js | 17 ++++ 6 files changed, 161 insertions(+)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index b0fadd71c77..512a21ea700 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -68,6 +68,7 @@ typedef enum { EVENT_TYPE_MOUSE, EVENT_TYPE_FOCUS, EVENT_TYPE_UIEVENT, + EVENT_TYPE_PAGETRANSITION, EVENT_TYPE_MESSAGE, EVENT_TYPE_PROGRESS, EVENT_TYPE_STORAGE, @@ -82,6 +83,7 @@ static const WCHAR *event_types[] = { L"MouseEvent", L"Event", /* FIXME */ L"UIEvent", + L"Event", /* FIXME */ L"MessageEvent", L"ProgressEvent", L"StorageEvent", @@ -182,6 +184,8 @@ static const event_info_t event_info[] = { EVENT_FIXME}, {L"msthumbnailclick", EVENT_TYPE_MOUSE, DISPID_EVPROP_ONMSTHUMBNAILCLICK, EVENT_FIXME}, + {L"pageshow", EVENT_TYPE_PAGETRANSITION, DISPID_EVPROP_ONPAGESHOW, + 0}, {L"paste", EVENT_TYPE_CLIPBOARD, DISPID_EVMETH_ONPASTE, EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE}, {L"progress", EVENT_TYPE_PROGRESS, DISPID_EVPROP_PROGRESS, @@ -2209,6 +2213,98 @@ static void DOMKeyboardEvent_destroy(DOMEvent *event) nsIDOMKeyEvent_Release(This->nsevent); }
+typedef struct { + DOMEvent event; + IWinePageTransitionEvent IWinePageTransitionEvent_iface; +} DOMPageTransitionEvent; + +static inline DOMPageTransitionEvent *impl_from_IWinePageTransitionEvent(IWinePageTransitionEvent *iface) +{ + return CONTAINING_RECORD(iface, DOMPageTransitionEvent, IWinePageTransitionEvent_iface); +} + +static HRESULT WINAPI DOMPageTransitionEvent_QueryInterface(IWinePageTransitionEvent *iface, REFIID riid, void **ppv) +{ + DOMPageTransitionEvent *This = impl_from_IWinePageTransitionEvent(iface); + return IDOMEvent_QueryInterface(&This->event.IDOMEvent_iface, riid, ppv); +} + +static ULONG WINAPI DOMPageTransitionEvent_AddRef(IWinePageTransitionEvent *iface) +{ + DOMPageTransitionEvent *This = impl_from_IWinePageTransitionEvent(iface); + return IDOMEvent_AddRef(&This->event.IDOMEvent_iface); +} + +static ULONG WINAPI DOMPageTransitionEvent_Release(IWinePageTransitionEvent *iface) +{ + DOMPageTransitionEvent *This = impl_from_IWinePageTransitionEvent(iface); + return IDOMEvent_Release(&This->event.IDOMEvent_iface); +} + +static HRESULT WINAPI DOMPageTransitionEvent_GetTypeInfoCount(IWinePageTransitionEvent *iface, UINT *pctinfo) +{ + DOMPageTransitionEvent *This = impl_from_IWinePageTransitionEvent(iface); + return IDispatchEx_GetTypeInfoCount(&This->event.dispex.IDispatchEx_iface, pctinfo); +} + +static HRESULT WINAPI DOMPageTransitionEvent_GetTypeInfo(IWinePageTransitionEvent *iface, UINT iTInfo, + LCID lcid, ITypeInfo **ppTInfo) +{ + DOMPageTransitionEvent *This = impl_from_IWinePageTransitionEvent(iface); + return IDispatchEx_GetTypeInfo(&This->event.dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo); +} + +static HRESULT WINAPI DOMPageTransitionEvent_GetIDsOfNames(IWinePageTransitionEvent *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + DOMPageTransitionEvent *This = impl_from_IWinePageTransitionEvent(iface); + return IDispatchEx_GetIDsOfNames(&This->event.dispex.IDispatchEx_iface, riid, rgszNames, cNames, + lcid, rgDispId); +} + +static HRESULT WINAPI DOMPageTransitionEvent_Invoke(IWinePageTransitionEvent *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + DOMPageTransitionEvent *This = impl_from_IWinePageTransitionEvent(iface); + return IDispatchEx_Invoke(&This->event.dispex.IDispatchEx_iface, dispIdMember, riid, lcid, + wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +static HRESULT WINAPI DOMPageTransitionEvent_get_persisted(IWinePageTransitionEvent *iface, VARIANT_BOOL *p) +{ + DOMPageTransitionEvent *This = impl_from_IWinePageTransitionEvent(iface); + + FIXME("(%p)->(%p): always returning FALSE\n", This, p); + + *p = VARIANT_FALSE; + return S_OK; +} + +static const IWinePageTransitionEventVtbl DOMPageTransitionEventVtbl = { + DOMPageTransitionEvent_QueryInterface, + DOMPageTransitionEvent_AddRef, + DOMPageTransitionEvent_Release, + DOMPageTransitionEvent_GetTypeInfoCount, + DOMPageTransitionEvent_GetTypeInfo, + DOMPageTransitionEvent_GetIDsOfNames, + DOMPageTransitionEvent_Invoke, + DOMPageTransitionEvent_get_persisted +}; + +static DOMPageTransitionEvent *DOMPageTransitionEvent_from_DOMEvent(DOMEvent *event) +{ + return CONTAINING_RECORD(event, DOMPageTransitionEvent, event); +} + +static void *DOMPageTransitionEvent_query_interface(DOMEvent *event, REFIID riid) +{ + DOMPageTransitionEvent *page_transition_event = DOMPageTransitionEvent_from_DOMEvent(event); + if(IsEqualGUID(&IID_IWinePageTransitionEvent, riid)) + return &page_transition_event->IWinePageTransitionEvent_iface; + return NULL; +} + typedef struct { DOMEvent event; IDOMCustomEvent IDOMCustomEvent_iface; @@ -2837,6 +2933,20 @@ static dispex_static_data_t DOMKeyboardEvent_dispex = { DOMKeyboardEvent_iface_tids };
+static void DOMPageTransitionEvent_init_dispex_info(dispex_data_t *info, compat_mode_t mode) +{ + if(mode >= COMPAT_MODE_IE11) + dispex_info_add_interface(info, IWinePageTransitionEvent_tid, NULL); +} + +dispex_static_data_t DOMPageTransitionEvent_dispex = { + L"PageTransitionEvent", + NULL, + DispDOMEvent_tid, + DOMEvent_iface_tids, + DOMPageTransitionEvent_init_dispex_info +}; + static const tid_t DOMCustomEvent_iface_tids[] = { IDOMEvent_tid, IDOMCustomEvent_tid, @@ -2961,6 +3071,15 @@ static DOMEvent *keyboard_event_ctor(void *iface, nsIDOMEvent *nsevent, eventid_ return &keyboard_event->ui_event.event; }
+static DOMEvent *page_transition_event_ctor(void *iface, nsIDOMEvent *nsevent, eventid_t event_id, compat_mode_t compat_mode) +{ + DOMPageTransitionEvent *page_transition_event = event_ctor(sizeof(DOMCustomEvent), &DOMPageTransitionEvent_dispex, + DOMPageTransitionEvent_query_interface, NULL, nsevent, event_id, compat_mode); + if(!page_transition_event) return NULL; + page_transition_event->IWinePageTransitionEvent_iface.lpVtbl = &DOMPageTransitionEventVtbl; + return &page_transition_event->event; +} + static DOMEvent *custom_event_ctor(void *iface, nsIDOMEvent *nsevent, eventid_t event_id, compat_mode_t compat_mode) { DOMCustomEvent *custom_event = event_ctor(sizeof(DOMCustomEvent), &DOMCustomEvent_dispex, @@ -3015,6 +3134,7 @@ static const struct { [EVENT_TYPE_CLIPBOARD] = { NULL, generic_event_ctor }, [EVENT_TYPE_FOCUS] = { NULL, generic_event_ctor }, [EVENT_TYPE_DRAG] = { NULL, generic_event_ctor }, + [EVENT_TYPE_PAGETRANSITION] = { NULL, page_transition_event_ctor }, [EVENT_TYPE_CUSTOM] = { &IID_nsIDOMCustomEvent, custom_event_ctor }, [EVENT_TYPE_PROGRESS] = { &IID_nsIDOMProgressEvent, progress_event_ctor }, [EVENT_TYPE_MESSAGE] = { NULL, message_event_ctor }, diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index 97baf9eed7b..e49c47d6988 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -51,6 +51,7 @@ typedef enum { EVENTID_MOUSEUP, EVENTID_MOUSEWHEEL, EVENTID_MSTHUMBNAILCLICK, + EVENTID_PAGESHOW, EVENTID_PASTE, EVENTID_PROGRESS, EVENTID_READYSTATECHANGE, diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index ccf67562aad..dde80d733e0 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -288,6 +288,7 @@ typedef struct EventTarget EventTarget; XIID(IWineHTMLElementPrivate) \ XIID(IWineHTMLWindowPrivate) \ XIID(IWineHTMLWindowCompatPrivate) \ + XIID(IWinePageTransitionEvent) \ XIID(IWineXMLHttpRequestPrivate) \ XIID(IWineMSHTMLConsole) \ XIID(IWineMSHTMLMediaQueryList) diff --git a/dlls/mshtml/mshtml_private_iface.idl b/dlls/mshtml/mshtml_private_iface.idl index fc023473cc9..8e4a64bda39 100644 --- a/dlls/mshtml/mshtml_private_iface.idl +++ b/dlls/mshtml/mshtml_private_iface.idl @@ -162,6 +162,20 @@ interface IWineDOMTokenList : IDispatch HRESULT toString([retval, out] BSTR *String); }
+[ + odl, + oleautomation, + dual, + hidden, + uuid(25508c5d-6a54-6888-8f41-75ff3ae8706b) +] +interface IWinePageTransitionEvent : IDispatch +{ + [propget, id(1)] + HRESULT persisted([retval, out] VARIANT_BOOL *ret); +} + + const long DISPID_IWINEXMLHTTPREQUESTPRIVATE_RESPONSE = 1; const long DISPID_IWINEXMLHTTPREQUESTPRIVATE_RESPONSETYPE = 2; const long DISPID_IWINEXMLHTTPREQUESTPRIVATE_UPLOAD = 3; diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index a8e3d503921..863970a01de 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -274,6 +274,14 @@ static nsresult NSAPI handle_load(nsIDOMEventListener *iface, nsIDOMEvent *event dispatch_event(&doc->window->event_target, load_event); IDOMEvent_Release(&load_event->IDOMEvent_iface); } + + if(doc->dom_document && doc->document_mode >= COMPAT_MODE_IE11) { + hres = create_document_event(doc, EVENTID_PAGESHOW, &load_event); + if(SUCCEEDED(hres)) { + dispatch_event(&doc->window->event_target, load_event); + IDOMEvent_Release(&load_event->IDOMEvent_iface); + } + } }else { WARN("no window\n"); } diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 7d26add1cfc..0ae5df94b3a 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -19,10 +19,27 @@ var compat_version; var tests = [];
+var pageshow_fired = false; if(window.addEventListener) { + window.addEventListener("pageshow", function(e) { + pageshow_fired = true; + + var r = Object.prototype.toString.call(e); + todo_wine. + ok(r === "[object PageTransitionEvent]", "pageshow toString = " + r); + ok("persisted" in e, "'persisted' not in pageshow event"); + ok(document.readyState === "complete", "pageshow readyState = " + document.readyState); + }, true); + document.addEventListener("visibilitychange", function() { ok(false, "visibilitychange fired"); }); }
+sync_test("page transition events", function() { + if(document.documentMode < 11) + ok(pageshow_fired === false, "pageshow fired"); + else + ok(pageshow_fired === true, "pageshow not fired"); +});
sync_test("builtin_toString", function() { var tags = [
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmldoc.c | 30 +++++++++++ dlls/mshtml/htmlevent.c | 2 +- dlls/mshtml/mshtml_private.h | 4 +- dlls/mshtml/navigate.c | 8 ++- dlls/mshtml/oleobj.c | 3 ++ dlls/mshtml/tests/documentmode.js | 14 +++++ dlls/mshtml/tests/events.c | 85 +++++++++++++++++++++++++++++++ dlls/mshtml/tests/iframe.html | 4 ++ dlls/mshtml/tests/rsrc.rc | 3 ++ dlls/mshtml/tests/script.c | 22 ++++++++ 10 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 dlls/mshtml/tests/iframe.html
diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index 81960f53400..dccb0c4f028 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -148,6 +148,36 @@ UINT get_document_charset(HTMLDocumentNode *doc) return doc->charset = ret; }
+static void send_unload_events_impl(HTMLInnerWindow *window) +{ + HTMLOuterWindow *child; + DOMEvent *event; + HRESULT hres; + + if(!window) + return; + + if(window->doc) { + hres = create_document_event(window->doc, EVENTID_UNLOAD, &event); + if(SUCCEEDED(hres)) { + dispatch_event(&window->event_target, event); + IDOMEvent_Release(&event->IDOMEvent_iface); + } + } + + LIST_FOR_EACH_ENTRY(child, &window->children, HTMLOuterWindow, sibling_entry) + send_unload_events_impl(child->base.inner_window); +} + +void send_unload_events(HTMLDocumentNode *doc) +{ + if(!doc->content_ready || doc->unload_sent || !doc->window || !doc->outer_window || !is_main_content_window(doc->outer_window)) + return; + doc->unload_sent = TRUE; + + send_unload_events_impl(doc->window); +} + typedef struct { HTMLDOMNode node; IDOMDocumentType IDOMDocumentType_iface; diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 512a21ea700..a64fa6a67dd 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -209,7 +209,7 @@ static const event_info_t event_info[] = { {L"timeout", EVENT_TYPE_PROGRESS, DISPID_EVPROP_TIMEOUT, EVENT_BIND_TO_TARGET}, {L"unload", EVENT_TYPE_UIEVENT, DISPID_EVMETH_ONUNLOAD, - EVENT_FIXME}, + 0}, {L"visibilitychange", EVENT_TYPE_EVENT, DISPID_EVPROP_VISIBILITYCHANGE, EVENT_BUBBLES},
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index dde80d733e0..3a0e5421722 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -924,7 +924,8 @@ struct HTMLDocumentNode {
nsIDOMDocument *dom_document; nsIDOMHTMLDocument *html_document; - BOOL content_ready; + BOOL content_ready : 1; + BOOL unload_sent : 1;
IHTMLDOMImplementation *dom_implementation; IHTMLNamespaceCollection *namespaces; @@ -1010,6 +1011,7 @@ void hide_tooltip(HTMLDocumentObj*) DECLSPEC_HIDDEN; HRESULT get_client_disp_property(IOleClientSite*,DISPID,VARIANT*) DECLSPEC_HIDDEN;
UINT get_document_charset(HTMLDocumentNode*) DECLSPEC_HIDDEN; +void send_unload_events(HTMLDocumentNode*) DECLSPEC_HIDDEN;
HRESULT ProtocolFactory_Create(REFCLSID,REFIID,void**) DECLSPEC_HIDDEN;
diff --git a/dlls/mshtml/navigate.c b/dlls/mshtml/navigate.c index 5825e0f27c0..9e645caff70 100644 --- a/dlls/mshtml/navigate.c +++ b/dlls/mshtml/navigate.c @@ -1357,8 +1357,12 @@ static HRESULT nsChannelBSC_start_binding(BSCallback *bsc) { nsChannelBSC *This = nsChannelBSC_from_BSCallback(bsc);
- if(This->is_doc_channel) - This->bsc.window->base.outer_window->base.inner_window->doc->skip_mutation_notif = FALSE; + if(This->is_doc_channel) { + HTMLDocumentNode *cur_doc = This->bsc.window->base.outer_window->base.inner_window->doc; + + cur_doc->skip_mutation_notif = FALSE; + send_unload_events(cur_doc); + }
return S_OK; } diff --git a/dlls/mshtml/oleobj.c b/dlls/mshtml/oleobj.c index a8bd5751b49..b9f41c1469f 100644 --- a/dlls/mshtml/oleobj.c +++ b/dlls/mshtml/oleobj.c @@ -448,6 +448,9 @@ static HRESULT WINAPI DocObjOleObject_SetClientSite(IOleObject *iface, IOleClien if(pClientSite == This->client) return S_OK;
+ if(!pClientSite && This->doc_node) + send_unload_events(This->doc_node); + if(This->client) { IOleClientSite_Release(This->client); This->client = NULL; diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 0ae5df94b3a..b200dacd23e 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -20,6 +20,12 @@ var compat_version; var tests = [];
var pageshow_fired = false; +document.doc_unload_events_called = false; +window.onunload = function() { + document.doc_unload_events_called = true; + ok(document.readyState === "complete", "unload readyState = " + document.readyState); +}; + if(window.addEventListener) { window.addEventListener("pageshow", function(e) { pageshow_fired = true; @@ -32,6 +38,9 @@ if(window.addEventListener) { }, true);
document.addEventListener("visibilitychange", function() { ok(false, "visibilitychange fired"); }); + document.addEventListener("unload", function() { ok(false, "unload fired on document"); }); +}else { + document.attachEvent("onunload", function() { ok(false, "unload fired on document"); }); }
sync_test("page transition events", function() { @@ -39,6 +48,11 @@ sync_test("page transition events", function() { ok(pageshow_fired === false, "pageshow fired"); else ok(pageshow_fired === true, "pageshow not fired"); + + if(document.body.addEventListener) + document.body.addEventListener("unload", function() { ok(false, "unload fired on document.body"); }); + else + document.body.attachEvent("onunload", function() { ok(false, "unload fired on document.body"); }); });
sync_test("builtin_toString", function() { diff --git a/dlls/mshtml/tests/events.c b/dlls/mshtml/tests/events.c index 558b166d466..34d3098ff8d 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -98,6 +98,8 @@ DEFINE_EXPECT(submit_onclick_setret); DEFINE_EXPECT(elem2_cp_onclick); DEFINE_EXPECT(iframe_onload); DEFINE_EXPECT(visibilitychange); +DEFINE_EXPECT(onunload); +DEFINE_EXPECT(iframe_onunload); DEFINE_EXPECT(doc1_onstorage); DEFINE_EXPECT(doc1_onstoragecommit); DEFINE_EXPECT(window1_onstorage); @@ -1432,6 +1434,27 @@ static HRESULT WINAPI onvisibilitychange(IDispatchEx *iface, DISPID id, LCID lci
EVENT_HANDLER_FUNC_OBJ(onvisibilitychange);
+static HRESULT WINAPI onunload(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(onunload); + test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(onunload); + +static HRESULT WINAPI iframe_onunload(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(iframe_onunload); + ok(called_onunload, "unload not fired on parent window before iframe\n"); + test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(iframe_onunload); + static HRESULT WINAPI nocall(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) { @@ -2456,6 +2479,67 @@ static void test_visibilitychange(IHTMLDocument2 *doc) } }
+static void test_unload_event(IHTMLDocument2 *doc) +{ + IHTMLFrameBase2 *iframe; + IHTMLDocument6 *doc6; + IHTMLElement2 *elem; + IHTMLWindow2 *child; + HRESULT hres; + VARIANT v; + BSTR bstr; + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch*)&onunload_obj; + hres = IHTMLWindow2_put_onunload(window, v); + ok(hres == S_OK, "put_onunload failed: %08lx\n", hres); + + V_VT(&v) = VT_EMPTY; + hres = IHTMLWindow2_get_onunload(window, &v); + ok(hres == S_OK, "get_onunload failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(onunload) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) == (IDispatch*)&onunload_obj, "V_DISPATCH(onunload) = %p\n", V_DISPATCH(&v)); + + hres = IHTMLDocument2_QueryInterface(doc, &IID_IHTMLDocument6, (void**)&doc6); + ok(hres == S_OK, "Could not get IHTMLDocument6 iface: %08lx\n", hres); + bstr = SysAllocString(L"ifr"); + hres = IHTMLDocument6_getElementById(doc6, bstr, &elem); + ok(hres == S_OK, "getElementById failed: %08lx\n", hres); + IHTMLDocument6_Release(doc6); + SysFreeString(bstr); + + hres = IHTMLElement2_QueryInterface(elem, &IID_IHTMLFrameBase2, (void**)&iframe); + ok(hres == S_OK, "Could not get IHTMLFrameBase2 iface: %08lx\n", hres); + IHTMLElement2_Release(elem); + hres = IHTMLFrameBase2_get_contentWindow(iframe, &child); + ok(hres == S_OK, "get_contentWindow failed: %08lx\n", hres); + IHTMLFrameBase2_Release(iframe); + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch*)&iframe_onunload_obj; + hres = IHTMLWindow2_put_onunload(child, v); + ok(hres == S_OK, "put_onunload failed: %08lx\n", hres); + + V_VT(&v) = VT_EMPTY; + hres = IHTMLWindow2_get_onunload(child, &v); + ok(hres == S_OK, "get_onunload failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(onunload) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) == (IDispatch*)&iframe_onunload_obj, "V_DISPATCH(onunload) = %p\n", V_DISPATCH(&v)); + + IHTMLWindow2_Release(child); + + SET_EXPECT(onunload); + SET_EXPECT(iframe_onunload); + navigate(doc, L"blank.html"); + CHECK_CALLED(iframe_onunload); + CHECK_CALLED(onunload); + + V_VT(&v) = VT_EMPTY; + hres = IHTMLWindow2_get_onunload(window, &v); + ok(hres == S_OK, "get_onunload failed: %08lx\n", hres); + ok(V_VT(&v) == VT_NULL, "V_VT(onunload) = %d\n", V_VT(&v)); +} + static void test_submit(IHTMLDocument2 *doc) { IHTMLElement *elem, *submit; @@ -5587,6 +5671,7 @@ START_TEST(events) 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_from_res(L"iframe.html", test_unload_event); run_test(empty_doc_ie9_str, test_create_event); }
diff --git a/dlls/mshtml/tests/iframe.html b/dlls/mshtml/tests/iframe.html new file mode 100644 index 00000000000..ec08a0eb19a --- /dev/null +++ b/dlls/mshtml/tests/iframe.html @@ -0,0 +1,4 @@ +<html> +<head><meta http-equiv="x-ua-compatible" content="IE=11" /></head> +<body><iframe src="about:blank" id="ifr"></iframe></body> +</html> diff --git a/dlls/mshtml/tests/rsrc.rc b/dlls/mshtml/tests/rsrc.rc index 01b2838be8b..e60e37198de 100644 --- a/dlls/mshtml/tests/rsrc.rc +++ b/dlls/mshtml/tests/rsrc.rc @@ -82,6 +82,9 @@ doc_with_prop.html HTML "doc_with_prop.html" /* @makedep: doc_with_prop_ie9.html */ doc_with_prop_ie9.html HTML "doc_with_prop_ie9.html"
+/* @makedep: iframe.html */ +iframe.html HTML "iframe.html" + /* For res: protocol test: */
/* @makedep: jstest.html */ diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index e71f178e51a..d7d7e7361c3 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -3892,8 +3892,10 @@ static void test_simple_script(void)
static void run_from_moniker(IMoniker *mon) { + DISPID dispid = DISPID_UNKNOWN; IPersistMoniker *persist; IHTMLDocument2 *doc; + BSTR bstr; MSG msg; HRESULT hres;
@@ -3921,8 +3923,28 @@ static void run_from_moniker(IMoniker *mon)
CHECK_CALLED(external_success);
+ /* check prop set by events fired during document unload */ + bstr = SysAllocString(L"doc_unload_events_called"); + hres = IHTMLDocument2_GetIDsOfNames(doc, &IID_NULL, &bstr, 1, 0, &dispid); + SysFreeString(bstr); + if(hres == DISP_E_UNKNOWNNAME) + dispid = DISPID_UNKNOWN; + else + ok(hres == S_OK, "GetIDsOfNames failed %08lx\n", hres); + free_registered_streams(); set_client_site(doc, FALSE); + + if(dispid != DISPID_UNKNOWN) { + DISPPARAMS dp = { 0 }; + UINT argerr; + VARIANT v; + + hres = IHTMLDocument2_Invoke(doc, dispid, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dp, &v, NULL, &argerr); + ok(hres == S_OK, "Invoke failed %08lx\n", hres); + ok(V_VT(&v) == VT_BOOL, "V_VT(doc_unload_events_called) = %d\n", V_VT(&v)); + ok(V_BOOL(&v) == VARIANT_TRUE, "doc_unload_events_called is not true\n"); + } IHTMLDocument2_Release(doc); }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmldoc.c | 10 +++++++++ dlls/mshtml/htmlevent.c | 2 ++ dlls/mshtml/htmlevent.h | 1 + dlls/mshtml/tests/documentmode.js | 17 +++++++++++++++- dlls/mshtml/tests/events.c | 34 +++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index dccb0c4f028..2cb9f7e2c22 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -158,6 +158,16 @@ static void send_unload_events_impl(HTMLInnerWindow *window) return;
if(window->doc) { + /* Native sends pagehide events before unload on the same window + before it moves on to the next window, so they're interleaved. */ + if(window->doc->document_mode >= COMPAT_MODE_IE11) { + hres = create_document_event(window->doc, EVENTID_PAGEHIDE, &event); + if(SUCCEEDED(hres)) { + dispatch_event(&window->event_target, event); + IDOMEvent_Release(&event->IDOMEvent_iface); + } + } + hres = create_document_event(window->doc, EVENTID_UNLOAD, &event); if(SUCCEEDED(hres)) { dispatch_event(&window->event_target, event); diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index a64fa6a67dd..77d93902a42 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -184,6 +184,8 @@ static const event_info_t event_info[] = { EVENT_FIXME}, {L"msthumbnailclick", EVENT_TYPE_MOUSE, DISPID_EVPROP_ONMSTHUMBNAILCLICK, EVENT_FIXME}, + {L"pagehide", EVENT_TYPE_PAGETRANSITION, DISPID_EVPROP_ONPAGEHIDE, + 0}, {L"pageshow", EVENT_TYPE_PAGETRANSITION, DISPID_EVPROP_ONPAGESHOW, 0}, {L"paste", EVENT_TYPE_CLIPBOARD, DISPID_EVMETH_ONPASTE, diff --git a/dlls/mshtml/htmlevent.h b/dlls/mshtml/htmlevent.h index e49c47d6988..a6d1f734a40 100644 --- a/dlls/mshtml/htmlevent.h +++ b/dlls/mshtml/htmlevent.h @@ -51,6 +51,7 @@ typedef enum { EVENTID_MOUSEUP, EVENTID_MOUSEWHEEL, EVENTID_MSTHUMBNAILCLICK, + EVENTID_PAGEHIDE, EVENTID_PAGESHOW, EVENTID_PASTE, EVENTID_PROGRESS, diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index b200dacd23e..3d473219b9f 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -19,11 +19,15 @@ var compat_version; var tests = [];
-var pageshow_fired = false; +var pageshow_fired = false, pagehide_fired = false; document.doc_unload_events_called = false; window.onunload = function() { document.doc_unload_events_called = true; ok(document.readyState === "complete", "unload readyState = " + document.readyState); + if(document.documentMode < 11) + ok(pagehide_fired === false, "pagehide fired before unload"); + else + ok(pagehide_fired === true, "pagehide not fired before unload"); };
if(window.addEventListener) { @@ -37,6 +41,16 @@ if(window.addEventListener) { ok(document.readyState === "complete", "pageshow readyState = " + document.readyState); }, true);
+ window.addEventListener("pagehide", function(e) { + pagehide_fired = true; + ok(document.documentMode >= 11, "pagehide fired"); + + var r = Object.prototype.toString.call(e); + todo_wine. + ok(r === "[object PageTransitionEvent]", "pagehide toString = " + r); + ok("persisted" in e, "'persisted' not in pagehide event"); + }, true); + document.addEventListener("visibilitychange", function() { ok(false, "visibilitychange fired"); }); document.addEventListener("unload", function() { ok(false, "unload fired on document"); }); }else { @@ -48,6 +62,7 @@ sync_test("page transition events", function() { ok(pageshow_fired === false, "pageshow fired"); else ok(pageshow_fired === true, "pageshow not fired"); + ok(pagehide_fired === false, "pagehide fired");
if(document.body.addEventListener) document.body.addEventListener("unload", function() { ok(false, "unload fired on document.body"); }); diff --git a/dlls/mshtml/tests/events.c b/dlls/mshtml/tests/events.c index 34d3098ff8d..0603ff5af5c 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -99,7 +99,9 @@ DEFINE_EXPECT(elem2_cp_onclick); DEFINE_EXPECT(iframe_onload); DEFINE_EXPECT(visibilitychange); DEFINE_EXPECT(onunload); +DEFINE_EXPECT(pagehide); DEFINE_EXPECT(iframe_onunload); +DEFINE_EXPECT(iframe_pagehide); DEFINE_EXPECT(doc1_onstorage); DEFINE_EXPECT(doc1_onstoragecommit); DEFINE_EXPECT(window1_onstorage); @@ -1434,21 +1436,47 @@ static HRESULT WINAPI onvisibilitychange(IDispatchEx *iface, DISPID id, LCID lci
EVENT_HANDLER_FUNC_OBJ(onvisibilitychange);
+static HRESULT WINAPI pagehide(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(pagehide); + ok(!called_onunload, "unload fired before pagehide\n"); + test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(pagehide); + static HRESULT WINAPI onunload(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) { CHECK_EXPECT(onunload); + ok(called_pagehide, "pagehide not fired before unload\n"); test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); return S_OK; }
EVENT_HANDLER_FUNC_OBJ(onunload);
+static HRESULT WINAPI iframe_pagehide(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(iframe_pagehide); + ok(called_pagehide, "pagehide not fired on parent window before iframe\n"); + ok(called_onunload, "unload not fired on parent window before pagehide on iframe\n"); + ok(!called_iframe_onunload, "unload fired before pagehide on iframe\n"); + test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(iframe_pagehide); + static HRESULT WINAPI iframe_onunload(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) { CHECK_EXPECT(iframe_onunload); ok(called_onunload, "unload not fired on parent window before iframe\n"); + ok(called_iframe_pagehide, "pagehide not fired before unload on iframe\n"); test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); return S_OK; } @@ -2526,12 +2554,18 @@ static void test_unload_event(IHTMLDocument2 *doc) ok(V_VT(&v) == VT_DISPATCH, "V_VT(onunload) = %d\n", V_VT(&v)); ok(V_DISPATCH(&v) == (IDispatch*)&iframe_onunload_obj, "V_DISPATCH(onunload) = %p\n", V_DISPATCH(&v));
+ add_event_listener((IUnknown*)window, L"pagehide", (IDispatch*)&pagehide_obj, VARIANT_TRUE); + add_event_listener((IUnknown*)child, L"pagehide", (IDispatch*)&iframe_pagehide_obj, VARIANT_TRUE); IHTMLWindow2_Release(child);
SET_EXPECT(onunload); + SET_EXPECT(pagehide); SET_EXPECT(iframe_onunload); + SET_EXPECT(iframe_pagehide); navigate(doc, L"blank.html"); + CHECK_CALLED(iframe_pagehide); CHECK_CALLED(iframe_onunload); + CHECK_CALLED(pagehide); CHECK_CALLED(onunload);
V_VT(&v) = VT_EMPTY;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=126193
Your paranoid android.
=== debian11 (32 bit report) ===
Report validation errors: advapi32:security has no test summary line (early exit of the main process?) advapi32:security has unaccounted for todo messages
I implemented it now using an internal timer that polls for that, since it was the least invasive and works in all cases, including other process hwnd. Hooks wouldn't be able to do that, and are also more invasive IMO. I'm hoping for better ideas, though.
Yeah, I hope for a better solution as well and I don't have suggestion off top of my head. Is there is a real application that actually needs it (and for which a stub is not enough)?
On Wed Nov 16 14:38:34 2022 +0000, Jacek Caban wrote:
I implemented it now using an internal timer that polls for that,
since it was the least invasive and works in all cases, including other process hwnd. Hooks wouldn't be able to do that, and are also more invasive IMO. I'm hoping for better ideas, though. Yeah, I hope for a better solution as well and I don't have suggestion off top of my head. Is there is a real application that actually needs it (and for which a stub is not enough)?
Not aware of one specifically, but the tests are nice to have, and I don't know how to test it without actually implementing it (and sending the events). Is bailing out on Wine (in the tests) with a skip or FIXME good enough? (like we have in some js tests)
The reason for the tests is that this event gets registered by apps, and according to spec it should be sent during unload as well, not just minimize. However, native doesn't seem to do that, but without tests, I wouldn't have known this… hence why they're nice to have, IMO.
Any ideas here how to keep the tests?
BTW in private conversation, Conor also said in WinEvents could be used here, EVENT_SYSTEM_MINIMIZESTART and EVENT_SYSTEM_MINIMIZEEND, but they're not implemented in Wine so I can't right now, but that's probably the best way to implement it at some point.