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; }