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.
-- v4: mshtml: Implement pagehide event. mshtml: Implement unload event. mshtml: Dispatch beforeunload event to the window. mshtml: Store the nsevent listener descs in a table. mshtml: Implement pageshow event. mshtml: Add visibilitychange event stub. mshtml: Dispatch Gecko events to the window if there's no node. mshtml: Do not register Gecko load event handler twice. mshtml: Allow EVENT_BIND_TO_TARGET to bind to the window.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index f2c862c5b0a..e669afb7483 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -3683,10 +3683,7 @@ HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, nsIDOMNode *nsnode, ev
if(event_info[eid].flags & EVENT_DEFAULTLISTENER) { nsnode = NULL; - }else if(event_info[eid].flags & EVENT_BIND_TO_TARGET) { - if(!nsnode) - nsnode = doc->node.nsnode; - }else { + }else if(!(event_info[eid].flags & EVENT_BIND_TO_TARGET)) { return S_OK; }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/nsevents.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index a8e3d503921..65de8c8c2e3 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -482,4 +482,7 @@ void init_nsevents(HTMLDocumentNode *doc) init_event(target, L"load", &listener->load_listener.nsIDOMEventListener_iface, TRUE);
nsIDOMEventTarget_Release(target); + + /* handle_load already takes care of dispatching the load event */ + doc->event_vector[EVENTID_LOAD] = TRUE; }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
The window itself is not a DOM node, so this is perfectly normal to fail here. In fact, we weren't dispatching any gecko events sent to the window.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/nsevents.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index 65de8c8c2e3..17904963188 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -287,6 +287,7 @@ static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent * nsEventListener *This = impl_from_nsIDOMEventListener(iface); HTMLDocumentNode *doc = This->This->doc; nsIDOMEventTarget *event_target; + EventTarget *target; nsIDOMNode *nsnode; HTMLDOMNode *node; DOMEvent *event; @@ -309,18 +310,22 @@ static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent * nsres = nsIDOMEventTarget_QueryInterface(event_target, &IID_nsIDOMNode, (void**)&nsnode); nsIDOMEventTarget_Release(event_target); if(NS_FAILED(nsres)) { - ERR("Could not get nsIDOMNode: %08lx\n", nsres); - return NS_OK; + if(!doc->window) + return S_OK; + target = &doc->window->event_target; + node = NULL; + }else { + hres = get_node(nsnode, TRUE, &node); + nsIDOMNode_Release(nsnode); + if(FAILED(hres)) + return NS_OK; + target = &node->event_target; }
- hres = get_node(nsnode, TRUE, &node); - nsIDOMNode_Release(nsnode); - if(FAILED(hres)) - return NS_OK; - hres = create_event_from_nsevent(nsevent, dispex_compat_mode(&doc->node.event_target.dispex), &event); if(FAILED(hres)) { - node_release(node); + if(node) + node_release(node); return NS_OK; }
@@ -330,15 +335,16 @@ static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent *
hres = create_document_event(doc, event->event_id == EVENTID_FOCUS ? EVENTID_FOCUSIN : EVENTID_FOCUSOUT, &focus_event); if(SUCCEEDED(hres)) { - dispatch_event(&node->event_target, focus_event); + dispatch_event(target, focus_event); IDOMEvent_Release(&focus_event->IDOMEvent_iface); } }
- dispatch_event(&node->event_target, event); + dispatch_event(target, event);
IDOMEvent_Release(&event->IDOMEvent_iface); - node_release(node); + if(node) + node_release(node); return NS_OK; }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 2 + dlls/mshtml/htmlevent.h | 1 + dlls/mshtml/tests/blank_ie10.html | 3 + dlls/mshtml/tests/documentmode.js | 5 ++ dlls/mshtml/tests/events.c | 145 +++++++++++++++++++++++++----- dlls/mshtml/tests/rsrc.rc | 3 + 6 files changed, 136 insertions(+), 23 deletions(-) create mode 100644 dlls/mshtml/tests/blank_ie10.html
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index e669afb7483..3f02f455bbf 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_FIXME | 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/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..6a2ea792b1d 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,57 @@ 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 { + /* FIXME: currently not implemented in Wine, so we can't wait for it */ + BOOL *expect = broken(1) ? &called_visibilitychange : NULL; + + SET_EXPECT(visibilitychange); + ShowWindow(container_hwnd, SW_MINIMIZE); + pump_msgs(expect); + todo_wine + CHECK_CALLED(visibilitychange); + + container_minimize = FALSE; + SET_EXPECT(visibilitychange); + ShowWindow(container_hwnd, SW_RESTORE); + pump_msgs(expect); + todo_wine + CHECK_CALLED(visibilitychange); + } + + navigate(doc, document_mode < 10 ? L"blank_ie10.html" : L"blank.html"); + + if(document_mode >= 9) + add_event_listener((IUnknown*)doc, L"visibilitychange", (IDispatch*)&onvisibilitychange_obj, VARIANT_TRUE); + + if(!winetest_interactive) { + ShowWindow(container_hwnd, SW_HIDE); + pump_msgs(NULL); + } +} + static void test_submit(IHTMLDocument2 *doc) { IHTMLElement *elem, *submit; @@ -5091,6 +5168,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 +5273,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 +5296,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 +5593,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 3f02f455bbf..c8fca5d4936 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 e3971258b13..7e7dd358c8d 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 17904963188..fea86480be0 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
Rather than hardcoding them individually, so that it's all in one place when adding new ones.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/nsevents.c | 79 +++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 32 deletions(-)
diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index fea86480be0..0416fd32545 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -37,16 +37,45 @@
WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
+#define EVENTLISTENER_VTBL(handler) \ + { \ + nsDOMEventListener_QueryInterface, \ + nsDOMEventListener_AddRef, \ + nsDOMEventListener_Release, \ + handler, \ + } +static nsresult NSAPI nsDOMEventListener_QueryInterface(nsIDOMEventListener*,nsIIDRef,void**); +static nsrefcnt NSAPI nsDOMEventListener_AddRef(nsIDOMEventListener*); +static nsrefcnt NSAPI nsDOMEventListener_Release(nsIDOMEventListener*); + typedef struct { nsIDOMEventListener nsIDOMEventListener_iface; nsDocumentEventListener *This; } nsEventListener;
+static nsresult NSAPI handle_blur(nsIDOMEventListener*,nsIDOMEvent*); +static nsresult NSAPI handle_focus(nsIDOMEventListener*,nsIDOMEvent*); +static nsresult NSAPI handle_keypress(nsIDOMEventListener*,nsIDOMEvent*); +static nsresult NSAPI handle_load(nsIDOMEventListener*,nsIDOMEvent*); + +enum doc_event_listener_flags { + BUBBLES = 0x0001, + OVERRIDE = 0x0002, +}; + +static const struct { + eventid_t id; + enum doc_event_listener_flags flags; + nsIDOMEventListenerVtbl vtbl; +} doc_event_listeners[] = { + { EVENTID_BLUR, 0, EVENTLISTENER_VTBL(handle_blur) }, + { EVENTID_FOCUS, 0, EVENTLISTENER_VTBL(handle_focus) }, + { EVENTID_KEYPRESS, BUBBLES, EVENTLISTENER_VTBL(handle_keypress) }, + { EVENTID_LOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_load), }, +}; + struct nsDocumentEventListener { - nsEventListener blur_listener; - nsEventListener focus_listener; - nsEventListener keypress_listener; - nsEventListener load_listener; + nsEventListener listener[ARRAY_SIZE(doc_event_listeners)]; nsEventListener htmlevent_listener;
LONG ref; @@ -356,18 +385,6 @@ static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent * return NS_OK; }
-#define EVENTLISTENER_VTBL(handler) \ - { \ - nsDOMEventListener_QueryInterface, \ - nsDOMEventListener_AddRef, \ - nsDOMEventListener_Release, \ - handler, \ - } - -static const nsIDOMEventListenerVtbl blur_vtbl = EVENTLISTENER_VTBL(handle_blur); -static const nsIDOMEventListenerVtbl focus_vtbl = EVENTLISTENER_VTBL(handle_focus); -static const nsIDOMEventListenerVtbl keypress_vtbl = EVENTLISTENER_VTBL(handle_keypress); -static const nsIDOMEventListenerVtbl load_vtbl = EVENTLISTENER_VTBL(handle_load); static const nsIDOMEventListenerVtbl htmlevent_vtbl = EVENTLISTENER_VTBL(handle_htmlevent);
static void init_event(nsIDOMEventTarget *target, const PRUnichar *type, @@ -448,16 +465,15 @@ void detach_nsevent(HTMLDocumentNode *doc, const WCHAR *type) void release_nsevents(HTMLDocumentNode *doc) { nsDocumentEventListener *listener = doc->nsevent_listener; + unsigned i;
TRACE("%p %p\n", doc, doc->nsevent_listener);
if(!listener) return;
- detach_nslistener(doc, L"blur", &listener->blur_listener, TRUE); - detach_nslistener(doc, L"focus", &listener->focus_listener, TRUE); - detach_nslistener(doc, L"keypress", &listener->keypress_listener, FALSE); - detach_nslistener(doc, L"load", &listener->load_listener, TRUE); + for(i = 0; i < ARRAY_SIZE(doc_event_listeners); i++) + detach_nslistener(doc, get_event_name(doc_event_listeners[i].id), &listener->listener[i], !(doc_event_listeners[i].flags & BUBBLES));
listener->doc = NULL; release_listener(listener); @@ -468,6 +484,7 @@ void init_nsevents(HTMLDocumentNode *doc) { nsDocumentEventListener *listener; nsIDOMEventTarget *target; + unsigned i;
listener = heap_alloc(sizeof(nsDocumentEventListener)); if(!listener) @@ -478,11 +495,9 @@ void init_nsevents(HTMLDocumentNode *doc) listener->ref = 1; listener->doc = doc;
- init_listener(&listener->blur_listener, listener, &blur_vtbl); - init_listener(&listener->focus_listener, listener, &focus_vtbl); - init_listener(&listener->keypress_listener, listener, &keypress_vtbl); - init_listener(&listener->load_listener, listener, &load_vtbl); - init_listener(&listener->htmlevent_listener, listener, &htmlevent_vtbl); + for(i = 0; i < ARRAY_SIZE(doc_event_listeners); i++) + init_listener(&listener->listener[i], listener, &doc_event_listeners[i].vtbl); + init_listener(&listener->htmlevent_listener, listener, &htmlevent_vtbl);
doc->nsevent_listener = listener;
@@ -490,13 +505,13 @@ void init_nsevents(HTMLDocumentNode *doc) if(!target) return;
- init_event(target, L"blur", &listener->blur_listener.nsIDOMEventListener_iface, TRUE); - init_event(target, L"focus", &listener->focus_listener.nsIDOMEventListener_iface, TRUE); - init_event(target, L"keypress", &listener->keypress_listener.nsIDOMEventListener_iface, FALSE); - init_event(target, L"load", &listener->load_listener.nsIDOMEventListener_iface, TRUE); + for(i = 0; i < ARRAY_SIZE(doc_event_listeners); i++) { + init_event(target, get_event_name(doc_event_listeners[i].id), &listener->listener[i].nsIDOMEventListener_iface, + !(doc_event_listeners[i].flags & BUBBLES));
- nsIDOMEventTarget_Release(target); + if(doc_event_listeners[i].flags & OVERRIDE) + doc->event_vector[doc_event_listeners[i].id] = TRUE; + }
- /* handle_load already takes care of dispatching the load event */ - doc->event_vector[EVENTID_LOAD] = TRUE; + nsIDOMEventTarget_Release(target); }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/nsevents.c | 23 +++++++ dlls/mshtml/tests/documentmode.js | 5 ++ dlls/mshtml/tests/events.c | 103 ++++++++++++++++++++++++++++++ dlls/mshtml/tests/iframe.html | 4 ++ dlls/mshtml/tests/rsrc.rc | 3 + 5 files changed, 138 insertions(+) create mode 100644 dlls/mshtml/tests/iframe.html
diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index 0416fd32545..0863464305c 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -57,6 +57,7 @@ static nsresult NSAPI handle_blur(nsIDOMEventListener*,nsIDOMEvent*); static nsresult NSAPI handle_focus(nsIDOMEventListener*,nsIDOMEvent*); static nsresult NSAPI handle_keypress(nsIDOMEventListener*,nsIDOMEvent*); static nsresult NSAPI handle_load(nsIDOMEventListener*,nsIDOMEvent*); +static nsresult NSAPI handle_beforeunload(nsIDOMEventListener*,nsIDOMEvent*);
enum doc_event_listener_flags { BUBBLES = 0x0001, @@ -72,6 +73,7 @@ static const struct { { EVENTID_FOCUS, 0, EVENTLISTENER_VTBL(handle_focus) }, { EVENTID_KEYPRESS, BUBBLES, EVENTLISTENER_VTBL(handle_keypress) }, { EVENTID_LOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_load), }, + { EVENTID_BEFOREUNLOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_beforeunload), }, };
struct nsDocumentEventListener { @@ -319,6 +321,27 @@ static nsresult NSAPI handle_load(nsIDOMEventListener *iface, nsIDOMEvent *event return NS_OK; }
+static nsresult NSAPI handle_beforeunload(nsIDOMEventListener *iface, nsIDOMEvent *nsevent) +{ + nsEventListener *This = impl_from_nsIDOMEventListener(iface); + HTMLDocumentNode *doc = This->This->doc; + HTMLInnerWindow *window; + DOMEvent *event; + HRESULT hres; + + if(!doc || !(window = doc->window)) + return NS_OK; + + /* Gecko dispatches this to the document, but IE dispatches it to the window */ + hres = create_event_from_nsevent(nsevent, dispex_compat_mode(&doc->node.event_target.dispex), &event); + if(SUCCEEDED(hres)) { + dispatch_event(&window->event_target, event); + IDOMEvent_Release(&event->IDOMEvent_iface); + } + + return NS_OK; +} + static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent *nsevent) { nsEventListener *This = impl_from_nsIDOMEventListener(iface); diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 0ae5df94b3a..333590c6113 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -20,6 +20,8 @@ var compat_version; var tests = [];
var pageshow_fired = false; +window.onbeforeunload = function() { ok(false, "beforeunload fired"); }; + if(window.addEventListener) { window.addEventListener("pageshow", function(e) { pageshow_fired = true; @@ -32,6 +34,9 @@ if(window.addEventListener) { }, true);
document.addEventListener("visibilitychange", function() { ok(false, "visibilitychange fired"); }); + document.addEventListener("beforeunload", function() { ok(false, "beforeunload fired on document"); }); +}else { + document.attachEvent("onbeforeunload", function() { ok(false, "beforeunload fired on document"); }); }
sync_test("page transition events", function() { diff --git a/dlls/mshtml/tests/events.c b/dlls/mshtml/tests/events.c index 6a2ea792b1d..2f2ee583d18 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(onbeforeunload); +DEFINE_EXPECT(iframe_onbeforeunload); 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 onbeforeunload(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(onbeforeunload); + test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); + return S_OK; +} + +EVENT_HANDLER_FUNC_OBJ(onbeforeunload); + +static HRESULT WINAPI iframe_onbeforeunload(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(iframe_onbeforeunload); + ok(called_onbeforeunload, "beforeunload 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_onbeforeunload); + static HRESULT WINAPI nocall(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) { @@ -2464,6 +2487,85 @@ static void test_visibilitychange(IHTMLDocument2 *doc) } }
+static void test_unload_event(IHTMLDocument2 *doc) +{ + IHTMLDocument2 *child_doc; + IHTMLFrameBase2 *iframe; + IHTMLDocument6 *doc6; + IHTMLElement2 *elem; + IHTMLWindow2 *child; + HRESULT hres; + VARIANT v; + BSTR bstr; + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch*)&onbeforeunload_obj; + hres = IHTMLWindow2_put_onbeforeunload(window, v); + ok(hres == S_OK, "put_onbeforeunload failed: %08lx\n", hres); + + V_VT(&v) = VT_EMPTY; + hres = IHTMLWindow2_get_onbeforeunload(window, &v); + ok(hres == S_OK, "get_onbeforeunload failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(onbeforeunload) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) == (IDispatch*)&onbeforeunload_obj, "V_DISPATCH(onbeforeunload) = %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); + + hres = IHTMLWindow2_get_document(child, &child_doc); + ok(hres == S_OK, "get_document failed: %08lx\n", hres); + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch*)&iframe_onbeforeunload_obj; + hres = IHTMLWindow2_put_onbeforeunload(child, v); + ok(hres == S_OK, "put_onbeforeunload failed: %08lx\n", hres); + + V_VT(&v) = VT_EMPTY; + hres = IHTMLWindow2_get_onbeforeunload(child, &v); + ok(hres == S_OK, "get_onbeforeunload failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(onbeforeunload) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) == (IDispatch*)&iframe_onbeforeunload_obj, "V_DISPATCH(onbeforeunload) = %p\n", V_DISPATCH(&v)); + + add_event_listener((IUnknown*)doc, L"beforeunload", (IDispatch*)&nocall_obj, VARIANT_TRUE); + add_event_listener((IUnknown*)child_doc, L"beforeunload", (IDispatch*)&nocall_obj, VARIANT_TRUE); + IHTMLDocument2_Release(child_doc); + IHTMLWindow2_Release(child); + + SET_EXPECT(onbeforeunload); + SET_EXPECT(iframe_onbeforeunload); + navigate(doc, L"blank.html"); + CHECK_CALLED(iframe_onbeforeunload); + CHECK_CALLED(onbeforeunload); + + V_VT(&v) = VT_EMPTY; + hres = IHTMLWindow2_get_onbeforeunload(window, &v); + ok(hres == S_OK, "get_onbeforeunload failed: %08lx\n", hres); + ok(V_VT(&v) == VT_NULL, "V_VT(onbeforeunload) = %d\n", V_VT(&v)); + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch*)&onbeforeunload_obj; + hres = IHTMLWindow2_put_onbeforeunload(window, v); + ok(hres == S_OK, "put_onbeforeunload failed: %08lx\n", hres); + + V_VT(&v) = VT_EMPTY; + hres = IHTMLWindow2_get_onbeforeunload(window, &v); + ok(hres == S_OK, "get_onbeforeunload failed: %08lx\n", hres); + ok(V_VT(&v) == VT_DISPATCH, "V_VT(onbeforeunload) = %d\n", V_VT(&v)); + ok(V_DISPATCH(&v) == (IDispatch*)&onbeforeunload_obj, "V_DISPATCH(onbeforeunload) = %p\n", V_DISPATCH(&v)); +} + static void test_submit(IHTMLDocument2 *doc) { IHTMLElement *elem, *submit; @@ -5595,6 +5697,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 */
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 | 3 +- dlls/mshtml/nsevents.c | 23 +++++++++ dlls/mshtml/tests/documentmode.js | 12 +++++ dlls/mshtml/tests/events.c | 78 +++++++++++++++++++++++++++++++ dlls/mshtml/tests/script.c | 22 +++++++++ dlls/mshtml/view.c | 33 +++++++++++++ 7 files changed, 171 insertions(+), 2 deletions(-)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index c8fca5d4936..7b82d823bd9 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}, + EVENT_BIND_TO_TARGET}, {L"visibilitychange", EVENT_TYPE_EVENT, DISPID_EVPROP_VISIBILITYCHANGE, EVENT_FIXME | EVENT_BUBBLES},
diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 7e7dd358c8d..d9e2d93d220 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; diff --git a/dlls/mshtml/nsevents.c b/dlls/mshtml/nsevents.c index 0863464305c..f6c326b771c 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -58,6 +58,7 @@ static nsresult NSAPI handle_focus(nsIDOMEventListener*,nsIDOMEvent*); static nsresult NSAPI handle_keypress(nsIDOMEventListener*,nsIDOMEvent*); static nsresult NSAPI handle_load(nsIDOMEventListener*,nsIDOMEvent*); static nsresult NSAPI handle_beforeunload(nsIDOMEventListener*,nsIDOMEvent*); +static nsresult NSAPI handle_unload(nsIDOMEventListener*,nsIDOMEvent*);
enum doc_event_listener_flags { BUBBLES = 0x0001, @@ -74,6 +75,7 @@ static const struct { { EVENTID_KEYPRESS, BUBBLES, EVENTLISTENER_VTBL(handle_keypress) }, { EVENTID_LOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_load), }, { EVENTID_BEFOREUNLOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_beforeunload), }, + { EVENTID_UNLOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_unload) }, };
struct nsDocumentEventListener { @@ -342,6 +344,27 @@ static nsresult NSAPI handle_beforeunload(nsIDOMEventListener *iface, nsIDOMEven return NS_OK; }
+static nsresult NSAPI handle_unload(nsIDOMEventListener *iface, nsIDOMEvent *nsevent) +{ + nsEventListener *This = impl_from_nsIDOMEventListener(iface); + HTMLDocumentNode *doc = This->This->doc; + HTMLInnerWindow *window; + DOMEvent *event; + HRESULT hres; + + if(!doc || !(window = doc->window) || doc->unload_sent) + return NS_OK; + doc->unload_sent = TRUE; + + hres = create_event_from_nsevent(nsevent, dispex_compat_mode(&doc->node.event_target.dispex), &event); + if(SUCCEEDED(hres)) { + dispatch_event(&window->event_target, event); + IDOMEvent_Release(&event->IDOMEvent_iface); + } + + return NS_OK; +} + static nsresult NSAPI handle_htmlevent(nsIDOMEventListener *iface, nsIDOMEvent *nsevent) { nsEventListener *This = impl_from_nsIDOMEventListener(iface); diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 333590c6113..3020cbf1b62 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -20,7 +20,12 @@ var compat_version; var tests = [];
var pageshow_fired = false; +document.doc_unload_events_called = false; window.onbeforeunload = function() { ok(false, "beforeunload fired"); }; +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) { @@ -35,8 +40,10 @@ if(window.addEventListener) {
document.addEventListener("visibilitychange", function() { ok(false, "visibilitychange fired"); }); document.addEventListener("beforeunload", function() { ok(false, "beforeunload fired on document"); }); + document.addEventListener("unload", function() { ok(false, "unload fired on document"); }); }else { document.attachEvent("onbeforeunload", function() { ok(false, "beforeunload fired on document"); }); + document.attachEvent("onunload", function() { ok(false, "unload fired on document"); }); }
sync_test("page transition events", function() { @@ -44,6 +51,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 2f2ee583d18..8378cd2e16a 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -100,6 +100,8 @@ DEFINE_EXPECT(iframe_onload); DEFINE_EXPECT(visibilitychange); DEFINE_EXPECT(onbeforeunload); DEFINE_EXPECT(iframe_onbeforeunload); +DEFINE_EXPECT(onunload); +DEFINE_EXPECT(iframe_onunload); DEFINE_EXPECT(doc1_onstorage); DEFINE_EXPECT(doc1_onstoragecommit); DEFINE_EXPECT(window1_onstorage); @@ -1449,12 +1451,39 @@ static HRESULT WINAPI iframe_onbeforeunload(IDispatchEx *iface, DISPID id, LCID { CHECK_EXPECT(iframe_onbeforeunload); ok(called_onbeforeunload, "beforeunload not fired on parent window before iframe\n"); + ok(!called_onunload, "unload fired on parent window before beforeunload on iframe\n"); + ok(!called_iframe_onunload, "unload fired before beforeunload on iframe\n"); test_event_args(NULL, id, wFlags, pdp, pvarRes, pei, pspCaller); return S_OK; }
EVENT_HANDLER_FUNC_OBJ(iframe_onbeforeunload);
+static HRESULT WINAPI onunload(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + CHECK_EXPECT(onunload); + if(expect_iframe_onunload) { + ok(called_onbeforeunload, "beforeunload not fired before unload\n"); + ok(called_iframe_onbeforeunload, "beforeunload not fired on iframe 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_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) { @@ -2498,6 +2527,17 @@ static void test_unload_event(IHTMLDocument2 *doc) 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)); + V_VT(&v) = VT_DISPATCH; V_DISPATCH(&v) = (IDispatch*)&onbeforeunload_obj; hres = IHTMLWindow2_put_onbeforeunload(window, v); @@ -2527,6 +2567,17 @@ static void test_unload_event(IHTMLDocument2 *doc) hres = IHTMLWindow2_get_document(child, &child_doc); ok(hres == S_OK, "get_document failed: %08lx\n", hres);
+ 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)); + V_VT(&v) = VT_DISPATCH; V_DISPATCH(&v) = (IDispatch*)&iframe_onbeforeunload_obj; hres = IHTMLWindow2_put_onbeforeunload(child, v); @@ -2540,12 +2591,18 @@ static void test_unload_event(IHTMLDocument2 *doc)
add_event_listener((IUnknown*)doc, L"beforeunload", (IDispatch*)&nocall_obj, VARIANT_TRUE); add_event_listener((IUnknown*)child_doc, L"beforeunload", (IDispatch*)&nocall_obj, VARIANT_TRUE); + add_event_listener((IUnknown*)doc, L"unload", (IDispatch*)&nocall_obj, VARIANT_TRUE); + add_event_listener((IUnknown*)child_doc, L"unload", (IDispatch*)&nocall_obj, VARIANT_TRUE); IHTMLDocument2_Release(child_doc); IHTMLWindow2_Release(child);
SET_EXPECT(onbeforeunload); SET_EXPECT(iframe_onbeforeunload); + SET_EXPECT(onunload); + SET_EXPECT(iframe_onunload); navigate(doc, L"blank.html"); + CHECK_CALLED(iframe_onunload); + CHECK_CALLED(onunload); CHECK_CALLED(iframe_onbeforeunload); CHECK_CALLED(onbeforeunload);
@@ -2554,6 +2611,17 @@ static void test_unload_event(IHTMLDocument2 *doc) ok(hres == S_OK, "get_onbeforeunload failed: %08lx\n", hres); ok(V_VT(&v) == VT_NULL, "V_VT(onbeforeunload) = %d\n", V_VT(&v));
+ 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)); + V_VT(&v) = VT_DISPATCH; V_DISPATCH(&v) = (IDispatch*)&onbeforeunload_obj; hres = IHTMLWindow2_put_onbeforeunload(window, v); @@ -2564,6 +2632,16 @@ static void test_unload_event(IHTMLDocument2 *doc) ok(hres == S_OK, "get_onbeforeunload failed: %08lx\n", hres); ok(V_VT(&v) == VT_DISPATCH, "V_VT(onbeforeunload) = %d\n", V_VT(&v)); ok(V_DISPATCH(&v) == (IDispatch*)&onbeforeunload_obj, "V_DISPATCH(onbeforeunload) = %p\n", V_DISPATCH(&v)); + + IOleDocumentView_Show(view, FALSE); + + SET_EXPECT(onunload); + IOleDocumentView_CloseView(view, 0); + CHECK_CALLED(onunload); + + IOleDocumentView_SetInPlaceSite(view, NULL); + IOleDocumentView_Release(view); + view = NULL; }
static void test_submit(IHTMLDocument2 *doc) 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); }
diff --git a/dlls/mshtml/view.c b/dlls/mshtml/view.c index 04b35e43039..b54b1d6967b 100644 --- a/dlls/mshtml/view.c +++ b/dlls/mshtml/view.c @@ -31,6 +31,7 @@ #include "wine/debug.h"
#include "mshtml_private.h" +#include "htmlevent.h"
WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
@@ -407,6 +408,37 @@ HRESULT call_set_active_object(IOleInPlaceUIWindow *window, IOleInPlaceActiveObj return IOleInPlaceUIWindow_SetActiveObject(window, act_obj, act_obj ? html_documentW : NULL); }
+static void send_unload_events_impl(HTMLInnerWindow *window) +{ + HTMLOuterWindow *child; + DOMEvent *event; + HRESULT hres; + + if(!window) + return; + + if(window->doc) { + window->doc->unload_sent = TRUE; + + 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); +} + +static void send_unload_events(HTMLDocumentObj *doc) +{ + if(!doc->doc_node || !doc->window || !doc->doc_node->content_ready || doc->doc_node->unload_sent) + return; + + send_unload_events_impl(doc->window->base.inner_window); +} + /********************************************************** * IOleDocumentView implementation */ @@ -670,6 +702,7 @@ static HRESULT WINAPI OleDocumentView_CloseView(IOleDocumentView *iface, DWORD d if(dwReserved) WARN("dwReserved = %ld\n", dwReserved);
+ send_unload_events(This); IOleDocumentView_Show(iface, FALSE); return S_OK; }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/htmlevent.c | 2 ++ dlls/mshtml/htmlevent.h | 1 + dlls/mshtml/nsevents.c | 14 +++++++++++- dlls/mshtml/tests/documentmode.js | 17 +++++++++++++- dlls/mshtml/tests/events.c | 37 ++++++++++++++++++++++++++++++- dlls/mshtml/view.c | 10 +++++++++ 6 files changed, 78 insertions(+), 3 deletions(-)
diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 7b82d823bd9..01a385130b1 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/nsevents.c b/dlls/mshtml/nsevents.c index f6c326b771c..e9ea8a51967 100644 --- a/dlls/mshtml/nsevents.c +++ b/dlls/mshtml/nsevents.c @@ -348,6 +348,7 @@ static nsresult NSAPI handle_unload(nsIDOMEventListener *iface, nsIDOMEvent *nse { nsEventListener *This = impl_from_nsIDOMEventListener(iface); HTMLDocumentNode *doc = This->This->doc; + compat_mode_t compat_mode; HTMLInnerWindow *window; DOMEvent *event; HRESULT hres; @@ -355,8 +356,19 @@ static nsresult NSAPI handle_unload(nsIDOMEventListener *iface, nsIDOMEvent *nse if(!doc || !(window = doc->window) || doc->unload_sent) return NS_OK; doc->unload_sent = TRUE; + compat_mode = dispex_compat_mode(&doc->node.event_target.dispex);
- hres = create_event_from_nsevent(nsevent, dispex_compat_mode(&doc->node.event_target.dispex), &event); + /* Native sends pagehide events prior to unload on the same window + before it moves on to the next window, so they're interleaved. */ + if(compat_mode >= COMPAT_MODE_IE11) { + hres = create_document_event(doc, EVENTID_PAGEHIDE, &event); + if(SUCCEEDED(hres)) { + dispatch_event(&window->event_target, event); + IDOMEvent_Release(&event->IDOMEvent_iface); + } + } + + hres = create_event_from_nsevent(nsevent, compat_mode, &event); if(SUCCEEDED(hres)) { dispatch_event(&window->event_target, event); IDOMEvent_Release(&event->IDOMEvent_iface); diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 3020cbf1b62..bb96b8b686c 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -19,12 +19,16 @@ var compat_version; var tests = [];
-var pageshow_fired = false; +var pageshow_fired = false, pagehide_fired = false; document.doc_unload_events_called = false; window.onbeforeunload = function() { ok(false, "beforeunload fired"); }; 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) { @@ -38,6 +42,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("beforeunload", function() { ok(false, "beforeunload fired on document"); }); document.addEventListener("unload", function() { ok(false, "unload fired on document"); }); @@ -51,6 +65,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 8378cd2e16a..4ea2f834827 100644 --- a/dlls/mshtml/tests/events.c +++ b/dlls/mshtml/tests/events.c @@ -101,7 +101,9 @@ DEFINE_EXPECT(visibilitychange); DEFINE_EXPECT(onbeforeunload); DEFINE_EXPECT(iframe_onbeforeunload); 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); @@ -1459,6 +1461,17 @@ static HRESULT WINAPI iframe_onbeforeunload(IDispatchEx *iface, DISPID id, LCID
EVENT_HANDLER_FUNC_OBJ(iframe_onbeforeunload);
+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) { @@ -1466,18 +1479,34 @@ static HRESULT WINAPI onunload(IDispatchEx *iface, DISPID id, LCID lcid, WORD wF if(expect_iframe_onunload) { ok(called_onbeforeunload, "beforeunload not fired before unload\n"); ok(called_iframe_onbeforeunload, "beforeunload not fired on iframe before unload\n"); - } + ok(called_pagehide, "pagehide not fired before unload\n"); + }else + ok(!called_pagehide, "pagehide fired before unload in quirks mode\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; } @@ -2589,6 +2618,8 @@ static void test_unload_event(IHTMLDocument2 *doc) ok(V_VT(&v) == VT_DISPATCH, "V_VT(onbeforeunload) = %d\n", V_VT(&v)); ok(V_DISPATCH(&v) == (IDispatch*)&iframe_onbeforeunload_obj, "V_DISPATCH(onbeforeunload) = %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); add_event_listener((IUnknown*)doc, L"beforeunload", (IDispatch*)&nocall_obj, VARIANT_TRUE); add_event_listener((IUnknown*)child_doc, L"beforeunload", (IDispatch*)&nocall_obj, VARIANT_TRUE); add_event_listener((IUnknown*)doc, L"unload", (IDispatch*)&nocall_obj, VARIANT_TRUE); @@ -2599,9 +2630,13 @@ static void test_unload_event(IHTMLDocument2 *doc) SET_EXPECT(onbeforeunload); SET_EXPECT(iframe_onbeforeunload); 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); CHECK_CALLED(iframe_onbeforeunload); CHECK_CALLED(onbeforeunload); diff --git a/dlls/mshtml/view.c b/dlls/mshtml/view.c index b54b1d6967b..72d2e3f99db 100644 --- a/dlls/mshtml/view.c +++ b/dlls/mshtml/view.c @@ -420,6 +420,16 @@ static void send_unload_events_impl(HTMLInnerWindow *window) if(window->doc) { window->doc->unload_sent = TRUE;
+ /* Native sends pagehide events prior to 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);
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=126311
Your paranoid android.
=== w10pro64_en_AE_u8 (testbot log) ===
WineRunTask.pl:error: The previous 1 run(s) terminated abnormally
=== w10pro64_ja (64 bit report) ===
mshtml: htmldoc.c:351: Test failed: expected Exec_SETTITLE htmldoc.c:2938: Test failed: unexpected call Exec_SETTITLE
There's no XPCOM interface exposed by it, which would require changing wine-gecko and frankly, it complicates it for no reason (dispatching it is simple because we already handle the load event).
AFAICS the only reason you need XPCOM is to guess event type from nsIDOMEvent, but you wouldn't need that if you use a dedicated event listener. You could then just call proper constructor directly, for example.
Jacek Caban (@jacek) commented about dlls/mshtml/nsevents.c:
nsres = nsIDOMEventTarget_QueryInterface(event_target, &IID_nsIDOMNode, (void**)&nsnode); nsIDOMEventTarget_Release(event_target); if(NS_FAILED(nsres)) {
ERR("Could not get nsIDOMNode: %08lx\n", nsres);
return NS_OK;
if(!doc->window)
return S_OK;
target = &doc->window->event_target;
Grabbing window reference here would be reasonable and would allow the rest of the code to unconditionally release it.
Jacek Caban (@jacek) commented about dlls/mshtml/htmlevent.c:
L"MouseEvent", L"Event", /* FIXME */ L"UIEvent",
- L"Event", /* FIXME */
Is there any reason not to use the right string, especially since you already wrote a test?
On Wed Nov 16 16:35:45 2022 +0000, Gabriel Ivăncescu wrote:
I sent a version that keeps tests but has a check for native to have proper waits then (there's still todo_wine, so when it gets implemented, we'll know to remove that FIXME part as well). Now it's just a stub. It's slightly ugly but IMO much better to have the tests in to prevent confusion whether these events are actually needed by apps or not. I know it would've helped me.
container_minimize makes it even more ugly. Could we at least skip that part if tests are ignored on Wine anyway?
I make use of Gecko unload event now, but I also have to send it manually in CloseView. The difference is important too: CloseView does *not* send any beforeunload events, but it does send pagehide.
Sure, it was the generic case that was more concerning (and broken from frames, I think) anyway.
On Fri Nov 18 01:17:00 2022 +0000, Jacek Caban wrote:
Is there any reason not to use the right string, especially since you already wrote a test?
Unfortunately we can't, because gecko doesn't support it in `createEvent` yet, so `create_document_event` would fail. See wine-gecko!10.
I added more events there so I could be done with this problem, if you have any others in mind that aren't covered yet, let me know—there might be more IE-specific event types though but those don't seem to have equivalent in gecko, I basically looked at the webidl filenames in gecko, so those will require more work if done.