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 edc90563dd9..0248737b012 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_pageshow(nsIDOMEventListener*,nsIDOMEvent*);
+static nsresult NSAPI handle_pagehide(nsIDOMEventListener*,nsIDOMEvent*);
static nsresult NSAPI handle_load(nsIDOMEventListener*,nsIDOMEvent*);
static nsresult NSAPI handle_beforeunload(nsIDOMEventListener*,nsIDOMEvent*);
static nsresult NSAPI handle_unload(nsIDOMEventListener*,nsIDOMEvent*);
@@ -75,6 +76,7 @@ static const struct {
{ EVENTID_FOCUS, 0, EVENTLISTENER_VTBL(handle_focus) },
{ EVENTID_KEYPRESS, BUBBLES, EVENTLISTENER_VTBL(handle_keypress) },
{ EVENTID_PAGESHOW, OVERRIDE, EVENTLISTENER_VTBL(handle_pageshow), },
+ { EVENTID_PAGEHIDE, OVERRIDE, EVENTLISTENER_VTBL(handle_pagehide), },
{ EVENTID_LOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_load), },
{ EVENTID_BEFOREUNLOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_beforeunload), },
{ EVENTID_UNLOAD, OVERRIDE, EVENTLISTENER_VTBL(handle_unload) },
@@ -234,6 +236,26 @@ static nsresult NSAPI handle_pageshow(nsIDOMEventListener *iface, nsIDOMEvent *n
return NS_OK;
}
+static nsresult NSAPI handle_pagehide(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->dom_document || doc->document_mode < COMPAT_MODE_IE11 || doc->unload_sent)
+ return NS_OK;
+
+ hres = create_document_event(doc, EVENTID_PAGEHIDE, &event);
+ if(SUCCEEDED(hres)) {
+ dispatch_event(&window->event_target, event);
+ IDOMEvent_Release(&event->IDOMEvent_iface);
+ }
+
+ return NS_OK;
+}
+
static void handle_docobj_load(HTMLDocumentObj *doc)
{
IOleCommandTarget *olecmd = NULL;
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js
index 0669073771c..33d60f7e835 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 4e7238e119c..643993bc475 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);
@@ -1458,6 +1460,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)
{
@@ -1465,18 +1478,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;
}
@@ -2585,6 +2614,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);
@@ -2595,9 +2626,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 3fca80a1b37..063f7afab9d 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) {
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);