From 64ad19a8011d2c468ba07c1e78759d9046a24fbd Mon Sep 17 00:00:00 2001 From: "jst%mozilla.jstenback.com" Date: Wed, 11 Feb 2004 06:09:51 +0000 Subject: [PATCH] Fixing bug 68215. Adding support for the onbeforeunload event. r=peterv@propagandism.org, sr=brendan@mozilla.org --- content/base/src/nsDocumentViewer.cpp | 112 +++++++++- content/base/src/nsSyncLoadService.cpp | 22 ++ content/events/src/nsDOMEvent.cpp | 104 +++++----- content/events/src/nsDOMEvent.h | 1 + content/events/src/nsEventListenerManager.cpp | 194 +++++------------- content/events/src/nsEventListenerManager.h | 11 +- .../html/content/src/nsGenericHTMLElement.cpp | 1 + .../html/content/src/nsHTMLScriptElement.cpp | 6 +- content/html/document/src/nsHTMLDocument.cpp | 23 ++- content/shared/public/nsLayoutAtomList.h | 1 + content/xbl/src/nsXBLService.cpp | 1 + docshell/base/nsDocShell.cpp | 60 ++++-- docshell/base/nsDocShell.h | 26 +-- docshell/base/nsIContentViewer.idl | 1 + dom/public/coreEvents/nsIDOMLoadListener.h | 7 + dom/public/nsIScriptContext.h | 6 +- dom/resources/locale/dom.properties | 4 +- dom/src/base/nsDOMClassInfo.h | 1 + dom/src/base/nsGlobalWindow.cpp | 12 +- dom/src/base/nsJSEnvironment.cpp | 18 +- dom/src/base/nsJSEnvironment.h | 5 +- dom/src/events/nsJSEventListener.cpp | 44 +++- dom/src/jsurl/nsJSProtocolHandler.cpp | 36 +++- extensions/xmlextras/base/src/nsDOMParser.cpp | 6 + extensions/xmlextras/base/src/nsDOMParser.h | 1 + .../base/src/nsLoadListenerProxy.cpp | 12 ++ .../xmlextras/base/src/nsLoadListenerProxy.h | 1 + .../xmlextras/base/src/nsXMLHttpRequest.cpp | 6 + .../xmlextras/base/src/nsXMLHttpRequest.h | 1 + layout/base/nsDocumentViewer.cpp | 112 +++++++++- layout/base/nsLayoutAtomList.h | 1 + widget/public/nsGUIEvent.h | 61 +++--- xpfe/appshell/src/nsWebShellWindow.cpp | 37 ++-- xpfe/browser/resources/content/navigator.js | 11 +- .../resources/content/bindings/tabbrowser.xml | 15 +- 35 files changed, 630 insertions(+), 330 deletions(-) diff --git a/content/base/src/nsDocumentViewer.cpp b/content/base/src/nsDocumentViewer.cpp index 0dd9f615f9cbd..a966756573594 100644 --- a/content/base/src/nsDocumentViewer.cpp +++ b/content/base/src/nsDocumentViewer.cpp @@ -199,6 +199,10 @@ static const char sPrintOptionsContractID[] = "@mozilla.org/gfx/printset static NS_DEFINE_CID(kGalleyContextCID, NS_GALLEYCONTEXT_CID); +static const char kDOMStringBundleURL[] = + "chrome://communicator/locale/dom/dom.properties"; + + #ifdef NS_DEBUG #undef NOISY_VIEWER @@ -414,9 +418,11 @@ class DocumentViewerImpl : public nsIDocumentViewer, nsIWidget* mParentWidget; // purposely won't be ref counted + PRPackedBool mInPermitUnload; + #ifdef NS_PRINTING + PRPackedBool mClosingWhilePrinting; nsPrintEngine* mPrintEngine; - PRBool mClosingWhilePrinting; nsCOMPtr mDialogParentWin; #if NS_PRINT_PREVIEW // These data member support delayed printing when the document is loading @@ -961,6 +967,104 @@ DocumentViewerImpl::LoadComplete(nsresult aStatus) return rv; } +NS_IMETHODIMP +DocumentViewerImpl::PermitUnload(PRBool *aPermitUnload) +{ + *aPermitUnload = PR_TRUE; + + if (!mDocument || mInPermitUnload) { + return NS_OK; + } + + // First, get the script global object from the document... + nsIScriptGlobalObject *global = mDocument->GetScriptGlobalObject(); + + if (!global) { + // This is odd, but not fatal + NS_WARNING("nsIScriptGlobalObject not set for document!"); + return NS_OK; + } + + // Now, fire an BeforeUnload event to the document and see if it's ok + // to unload... + nsEventStatus status = nsEventStatus_eIgnore; + nsBeforePageUnloadEvent event(NS_BEFORE_PAGE_UNLOAD); + + // In evil cases we might be destroyed while handling the + // onbeforeunload event, don't let that happen. + nsRefPtr kungFuDeathGrip(this); + + mInPermitUnload = PR_TRUE; + nsresult rv = global->HandleDOMEvent(mPresContext, &event, nsnull, + NS_EVENT_FLAG_INIT, &status); + mInPermitUnload = PR_FALSE; + + if (NS_SUCCEEDED(rv) && event.flags & NS_EVENT_FLAG_NO_DEFAULT) { + // Ask the user if it's ok to unload the current page + + nsCOMPtr prompt(do_GetInterface(mContainer)); + + if (prompt) { + nsCOMPtr + stringService(do_GetService(NS_STRINGBUNDLE_CONTRACTID)); + NS_ENSURE_TRUE(stringService, NS_OK); + + nsCOMPtr bundle; + stringService->CreateBundle(kDOMStringBundleURL, getter_AddRefs(bundle)); + NS_ENSURE_TRUE(bundle, NS_OK); + + nsXPIDLString preMsg, postMsg; + nsresult rv; + rv = bundle->GetStringFromName(NS_LITERAL_STRING("OnBeforeUnloadPreMessage").get(), getter_Copies(preMsg)); + rv |= bundle->GetStringFromName(NS_LITERAL_STRING("OnBeforeUnloadPostMessage").get(), getter_Copies(postMsg)); + + // GetStringFromName can succeed, yet give NULL strings back. + if (NS_FAILED(rv) || !preMsg || !postMsg) { + NS_ERROR("Failed to get strings from dom.properties!"); + return NS_OK; + } + + // Limit the length of the text the page can inject into this + // dialogue to 1024 characters. + PRInt32 len = PR_MIN(event.text.Length(), 1024); + + nsAutoString msg(preMsg + NS_LITERAL_STRING("\n\n") + + StringHead(event.text, len) + + NS_LITERAL_STRING("\n\n") + postMsg); + + // This doesn't pass a title, which makes the title be + // "Confirm", is that ok, or do we want a localizable title for + // this dialogue? + if (NS_FAILED(prompt->Confirm(nsnull, msg.get(), aPermitUnload))) { + *aPermitUnload = PR_TRUE; + } + } + } + + nsCOMPtr docShellNode(do_QueryInterface(mContainer)); + + if (docShellNode) { + PRInt32 childCount; + docShellNode->GetChildCount(&childCount); + + for (PRInt32 i = 0; i < childCount && *aPermitUnload; ++i) { + nsCOMPtr item; + docShellNode->GetChildAt(i, getter_AddRefs(item)); + + nsCOMPtr docShell(do_QueryInterface(item)); + nsCOMPtr cv; + + docShell->GetContentViewer(getter_AddRefs(cv)); + + if (cv) { + cv->PermitUnload(aPermitUnload); + } + } + } + + return NS_OK; +} + NS_IMETHODIMP DocumentViewerImpl::Unload() { @@ -971,11 +1075,11 @@ DocumentViewerImpl::Unload() } // First, get the script global object from the document... - nsCOMPtr global = mDocument->GetScriptGlobalObject(); + nsIScriptGlobalObject *global = mDocument->GetScriptGlobalObject(); if (!global) { // Fail if no ScriptGlobalObject is available... - NS_ASSERTION(0, "nsIScriptGlobalObject not set for document!"); + NS_ERROR("nsIScriptGlobalObject not set for document!"); return NS_ERROR_NULL_POINTER; } @@ -1121,6 +1225,8 @@ DocumentViewerImpl::Destroy() mPresShell = nsnull; } + mContainer = nsnull; + return NS_OK; } diff --git a/content/base/src/nsSyncLoadService.cpp b/content/base/src/nsSyncLoadService.cpp index 530b5ca120392..bf92f4b742f4f 100644 --- a/content/base/src/nsSyncLoadService.cpp +++ b/content/base/src/nsSyncLoadService.cpp @@ -117,6 +117,7 @@ class nsSyncLoader : public nsIDOMLoadListener, // nsIDOMLoadListener NS_IMETHOD Load(nsIDOMEvent* aEvent); + NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent); NS_IMETHOD Unload(nsIDOMEvent* aEvent); NS_IMETHOD Abort(nsIDOMEvent* aEvent); NS_IMETHOD Error(nsIDOMEvent* aEvent); @@ -157,6 +158,7 @@ class txLoadListenerProxy : public nsIDOMLoadListener { // nsIDOMLoadListener NS_IMETHOD Load(nsIDOMEvent* aEvent); + NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent); NS_IMETHOD Unload(nsIDOMEvent* aEvent); NS_IMETHOD Abort(nsIDOMEvent* aEvent); NS_IMETHOD Error(nsIDOMEvent* aEvent); @@ -200,6 +202,18 @@ txLoadListenerProxy::Load(nsIDOMEvent* aEvent) return NS_OK; } +NS_IMETHODIMP +txLoadListenerProxy::BeforeUnload(nsIDOMEvent* aEvent) +{ + nsCOMPtr listener = do_QueryReferent(mParent); + + if (listener) { + return listener->BeforeUnload(aEvent); + } + + return NS_OK; +} + NS_IMETHODIMP txLoadListenerProxy::Unload(nsIDOMEvent* aEvent) { @@ -465,6 +479,14 @@ nsSyncLoader::Load(nsIDOMEvent* aEvent) return NS_OK; } +nsresult +nsSyncLoader::BeforeUnload(nsIDOMEvent* aEvent) +{ + // Like, whatever. + + return NS_OK; +} + nsresult nsSyncLoader::Unload(nsIDOMEvent* aEvent) { diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 67463d41584c0..ac37772a0859f 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -64,10 +64,10 @@ #include "nsIDOMMutationEvent.h" #include "nsIURI.h" -static const char* const mEventNames[] = { +static const char* const sEventNames[] = { "mousedown", "mouseup", "click", "dblclick", "mouseover", "mouseout", "mousemove", "contextmenu", "keydown", "keyup", "keypress", - "focus", "blur", "load", "unload", "abort", "error", + "focus", "blur", "load", "beforeunload", "unload", "abort", "error", "submit", "reset", "change", "select", "input", "paint" ,"text", "popupshowing", "popupshown", "popuphiding", "popuphidden", "close", "command", "broadcast", "commandupdate", "dragenter", "dragover", "dragexit", "dragdrop", "draggesture", "resize", @@ -1482,113 +1482,115 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType) case NS_MOUSE_LEFT_BUTTON_DOWN: case NS_MOUSE_MIDDLE_BUTTON_DOWN: case NS_MOUSE_RIGHT_BUTTON_DOWN: - return mEventNames[eDOMEvents_mousedown]; + return sEventNames[eDOMEvents_mousedown]; case NS_MOUSE_LEFT_BUTTON_UP: case NS_MOUSE_MIDDLE_BUTTON_UP: case NS_MOUSE_RIGHT_BUTTON_UP: - return mEventNames[eDOMEvents_mouseup]; + return sEventNames[eDOMEvents_mouseup]; case NS_MOUSE_LEFT_CLICK: case NS_MOUSE_MIDDLE_CLICK: case NS_MOUSE_RIGHT_CLICK: - return mEventNames[eDOMEvents_click]; + return sEventNames[eDOMEvents_click]; case NS_MOUSE_LEFT_DOUBLECLICK: case NS_MOUSE_MIDDLE_DOUBLECLICK: case NS_MOUSE_RIGHT_DOUBLECLICK: - return mEventNames[eDOMEvents_dblclick]; + return sEventNames[eDOMEvents_dblclick]; case NS_MOUSE_ENTER_SYNTH: - return mEventNames[eDOMEvents_mouseover]; + return sEventNames[eDOMEvents_mouseover]; case NS_MOUSE_EXIT_SYNTH: - return mEventNames[eDOMEvents_mouseout]; + return sEventNames[eDOMEvents_mouseout]; case NS_MOUSE_MOVE: - return mEventNames[eDOMEvents_mousemove]; + return sEventNames[eDOMEvents_mousemove]; case NS_KEY_UP: - return mEventNames[eDOMEvents_keyup]; + return sEventNames[eDOMEvents_keyup]; case NS_KEY_DOWN: - return mEventNames[eDOMEvents_keydown]; + return sEventNames[eDOMEvents_keydown]; case NS_KEY_PRESS: - return mEventNames[eDOMEvents_keypress]; + return sEventNames[eDOMEvents_keypress]; case NS_FOCUS_CONTENT: - return mEventNames[eDOMEvents_focus]; + return sEventNames[eDOMEvents_focus]; case NS_BLUR_CONTENT: - return mEventNames[eDOMEvents_blur]; + return sEventNames[eDOMEvents_blur]; case NS_XUL_CLOSE: - return mEventNames[eDOMEvents_close]; + return sEventNames[eDOMEvents_close]; case NS_PAGE_LOAD: case NS_IMAGE_LOAD: case NS_SCRIPT_LOAD: - return mEventNames[eDOMEvents_load]; + return sEventNames[eDOMEvents_load]; + case NS_BEFORE_PAGE_UNLOAD: + return sEventNames[eDOMEvents_beforeunload]; case NS_PAGE_UNLOAD: - return mEventNames[eDOMEvents_unload]; + return sEventNames[eDOMEvents_unload]; case NS_IMAGE_ABORT: - return mEventNames[eDOMEvents_abort]; + return sEventNames[eDOMEvents_abort]; case NS_IMAGE_ERROR: case NS_SCRIPT_ERROR: - return mEventNames[eDOMEvents_error]; + return sEventNames[eDOMEvents_error]; case NS_FORM_SUBMIT: - return mEventNames[eDOMEvents_submit]; + return sEventNames[eDOMEvents_submit]; case NS_FORM_RESET: - return mEventNames[eDOMEvents_reset]; + return sEventNames[eDOMEvents_reset]; case NS_FORM_CHANGE: - return mEventNames[eDOMEvents_change]; + return sEventNames[eDOMEvents_change]; case NS_FORM_SELECTED: - return mEventNames[eDOMEvents_select]; + return sEventNames[eDOMEvents_select]; case NS_FORM_INPUT: - return mEventNames[eDOMEvents_input]; + return sEventNames[eDOMEvents_input]; case NS_PAINT: - return mEventNames[eDOMEvents_paint]; + return sEventNames[eDOMEvents_paint]; case NS_RESIZE_EVENT: - return mEventNames[eDOMEvents_resize]; + return sEventNames[eDOMEvents_resize]; case NS_SCROLL_EVENT: - return mEventNames[eDOMEvents_scroll]; + return sEventNames[eDOMEvents_scroll]; case NS_TEXT_TEXT: - return mEventNames[eDOMEvents_text]; + return sEventNames[eDOMEvents_text]; case NS_XUL_POPUP_SHOWING: - return mEventNames[eDOMEvents_popupShowing]; + return sEventNames[eDOMEvents_popupShowing]; case NS_XUL_POPUP_SHOWN: - return mEventNames[eDOMEvents_popupShown]; + return sEventNames[eDOMEvents_popupShown]; case NS_XUL_POPUP_HIDING: - return mEventNames[eDOMEvents_popupHiding]; + return sEventNames[eDOMEvents_popupHiding]; case NS_XUL_POPUP_HIDDEN: - return mEventNames[eDOMEvents_popupHidden]; + return sEventNames[eDOMEvents_popupHidden]; case NS_XUL_COMMAND: - return mEventNames[eDOMEvents_command]; + return sEventNames[eDOMEvents_command]; case NS_XUL_BROADCAST: - return mEventNames[eDOMEvents_broadcast]; + return sEventNames[eDOMEvents_broadcast]; case NS_XUL_COMMAND_UPDATE: - return mEventNames[eDOMEvents_commandupdate]; + return sEventNames[eDOMEvents_commandupdate]; case NS_DRAGDROP_ENTER: - return mEventNames[eDOMEvents_dragenter]; + return sEventNames[eDOMEvents_dragenter]; case NS_DRAGDROP_OVER_SYNTH: - return mEventNames[eDOMEvents_dragover]; + return sEventNames[eDOMEvents_dragover]; case NS_DRAGDROP_EXIT_SYNTH: - return mEventNames[eDOMEvents_dragexit]; + return sEventNames[eDOMEvents_dragexit]; case NS_DRAGDROP_DROP: - return mEventNames[eDOMEvents_dragdrop]; + return sEventNames[eDOMEvents_dragdrop]; case NS_DRAGDROP_GESTURE: - return mEventNames[eDOMEvents_draggesture]; + return sEventNames[eDOMEvents_draggesture]; case NS_SCROLLPORT_OVERFLOW: - return mEventNames[eDOMEvents_overflow]; + return sEventNames[eDOMEvents_overflow]; case NS_SCROLLPORT_UNDERFLOW: - return mEventNames[eDOMEvents_underflow]; + return sEventNames[eDOMEvents_underflow]; case NS_SCROLLPORT_OVERFLOWCHANGED: - return mEventNames[eDOMEvents_overflowchanged]; + return sEventNames[eDOMEvents_overflowchanged]; case NS_MUTATION_SUBTREEMODIFIED: - return mEventNames[eDOMEvents_subtreemodified]; + return sEventNames[eDOMEvents_subtreemodified]; case NS_MUTATION_NODEINSERTED: - return mEventNames[eDOMEvents_nodeinserted]; + return sEventNames[eDOMEvents_nodeinserted]; case NS_MUTATION_NODEREMOVED: - return mEventNames[eDOMEvents_noderemoved]; + return sEventNames[eDOMEvents_noderemoved]; case NS_MUTATION_NODEREMOVEDFROMDOCUMENT: - return mEventNames[eDOMEvents_noderemovedfromdocument]; + return sEventNames[eDOMEvents_noderemovedfromdocument]; case NS_MUTATION_NODEINSERTEDINTODOCUMENT: - return mEventNames[eDOMEvents_nodeinsertedintodocument]; + return sEventNames[eDOMEvents_nodeinsertedintodocument]; case NS_MUTATION_ATTRMODIFIED: - return mEventNames[eDOMEvents_attrmodified]; + return sEventNames[eDOMEvents_attrmodified]; case NS_MUTATION_CHARACTERDATAMODIFIED: - return mEventNames[eDOMEvents_characterdatamodified]; + return sEventNames[eDOMEvents_characterdatamodified]; case NS_CONTEXTMENU: case NS_CONTEXTMENU_KEY: - return mEventNames[eDOMEvents_contextmenu]; + return sEventNames[eDOMEvents_contextmenu]; default: break; } diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index c4fe1a13875ac..f9b548519af5f 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -86,6 +86,7 @@ class nsDOMEvent : public nsIDOMKeyEvent, eDOMEvents_focus, eDOMEvents_blur, eDOMEvents_load, + eDOMEvents_beforeunload, eDOMEvents_unload, eDOMEvents_abort, eDOMEvents_error, diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index 787f3321e22ba..4f1c075847a7b 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -229,7 +229,8 @@ static const EventDispatchData sLoadEvents[] = { {NS_SCRIPT_LOAD, HANDLER(&nsIDOMLoadListener::Load), NS_EVENT_BITS_LOAD_LOAD}, {NS_PAGE_UNLOAD, HANDLER(&nsIDOMLoadListener::Unload),NS_EVENT_BITS_LOAD_UNLOAD}, {NS_IMAGE_ERROR, HANDLER(&nsIDOMLoadListener::Error), NS_EVENT_BITS_LOAD_ERROR}, - {NS_SCRIPT_ERROR,HANDLER(&nsIDOMLoadListener::Error), NS_EVENT_BITS_LOAD_ERROR} + {NS_SCRIPT_ERROR,HANDLER(&nsIDOMLoadListener::Error), NS_EVENT_BITS_LOAD_ERROR}, + {NS_BEFORE_PAGE_UNLOAD,HANDLER(&nsIDOMLoadListener::BeforeUnload), NS_EVENT_BITS_LOAD_BEFORE_UNLOAD} }; static const EventDispatchData sPaintEvents[] = { @@ -842,6 +843,10 @@ nsEventListenerManager::GetIdentifiersForType(nsIAtom* aType, *aArrayType = eEventArrayType_Load; *aFlags = NS_EVENT_BITS_LOAD_LOAD; } + else if (aType == nsLayoutAtoms::onbeforeunload) { + *aArrayType = eEventArrayType_Load; + *aFlags = NS_EVENT_BITS_LOAD_BEFORE_UNLOAD; + } else if (aType == nsLayoutAtoms::onunload) { *aArrayType = eEventArrayType_Load; *aFlags = NS_EVENT_BITS_LOAD_UNLOAD; @@ -1465,7 +1470,6 @@ nsresult nsEventListenerManager::HandleEvent(nsIPresContext* aPresContext, keys which cause window deletion, can destroy this object before we're ready. */ nsCOMPtr kungFuDeathGrip(this); - nsString empty; nsVoidArray *listeners = nsnull; if (aEvent->message == NS_CONTEXTMENU || aEvent->message == NS_CONTEXTMENU_KEY) { @@ -1476,6 +1480,7 @@ nsresult nsEventListenerManager::HandleEvent(nsIPresContext* aPresContext, } } + const EventTypeData* typeData = nsnull; const EventDispatchData* dispData = nsnull; @@ -1500,7 +1505,7 @@ nsresult nsEventListenerManager::HandleEvent(nsIPresContext* aPresContext, if (aEvent->eventStructType == NS_MUTATION_EVENT) ret = NS_NewDOMMutationEvent(aDOMEvent, aPresContext, aEvent); else - ret = NS_NewDOMUIEvent(aDOMEvent, aPresContext, empty, aEvent); + ret = NS_NewDOMUIEvent(aDOMEvent, aPresContext, EmptyString(), aEvent); } if (NS_SUCCEEDED(ret)) { @@ -1590,225 +1595,119 @@ nsresult nsEventListenerManager::FlipCaptureBit(PRInt32 aEventTypes, PRBool aInitCapture) { - EventArrayType arrayType; - nsListenerStruct *ls; + EventArrayType arrayType = eEventArrayType_None; + PRUint8 bits; if (aEventTypes & nsIDOMNSEvent::MOUSEDOWN) { arrayType = eEventArrayType_Mouse; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_MOUSE_MOUSEDOWN; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_MOUSE_MOUSEDOWN; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_MOUSE_MOUSEDOWN; } if (aEventTypes & nsIDOMNSEvent::MOUSEUP) { arrayType = eEventArrayType_Mouse; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_MOUSE_MOUSEUP; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_MOUSE_MOUSEUP; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_MOUSE_MOUSEUP; } if (aEventTypes & nsIDOMNSEvent::MOUSEOVER) { arrayType = eEventArrayType_Mouse; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_MOUSE_MOUSEOVER; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_MOUSE_MOUSEOVER; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_MOUSE_MOUSEOVER; } if (aEventTypes & nsIDOMNSEvent::MOUSEOUT) { arrayType = eEventArrayType_Mouse; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_MOUSE_MOUSEOUT; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_MOUSE_MOUSEOUT; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_MOUSE_MOUSEOUT; } if (aEventTypes & nsIDOMNSEvent::MOUSEMOVE) { arrayType = eEventArrayType_MouseMotion; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_MOUSEMOTION_MOUSEMOVE; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_MOUSEMOTION_MOUSEMOVE; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_MOUSEMOTION_MOUSEMOVE; } if (aEventTypes & nsIDOMNSEvent::CLICK) { arrayType = eEventArrayType_Mouse; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_MOUSE_CLICK; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_MOUSE_CLICK; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_MOUSE_CLICK; } if (aEventTypes & nsIDOMNSEvent::DBLCLICK) { arrayType = eEventArrayType_Mouse; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_MOUSE_DBLCLICK; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_MOUSE_DBLCLICK; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_MOUSE_DBLCLICK; } if (aEventTypes & nsIDOMNSEvent::KEYDOWN) { arrayType = eEventArrayType_Key; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_KEY_KEYDOWN; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_KEY_KEYDOWN; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_KEY_KEYDOWN; } if (aEventTypes & nsIDOMNSEvent::KEYUP) { arrayType = eEventArrayType_Key; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_KEY_KEYUP; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_KEY_KEYUP; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_KEY_KEYUP; } if (aEventTypes & nsIDOMNSEvent::KEYPRESS) { arrayType = eEventArrayType_Key; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_KEY_KEYPRESS; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_KEY_KEYPRESS; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_KEY_KEYPRESS; } if (aEventTypes & nsIDOMNSEvent::DRAGDROP) { arrayType = eEventArrayType_Drag; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_DRAG_ENTER; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_DRAG_ENTER; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_DRAG_ENTER; } /*if (aEventTypes & nsIDOMNSEvent::MOUSEDRAG) { arrayType = kIDOMMouseListenerarrayType; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_MOUSE_MOUSEDOWN; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_MOUSE_MOUSEDOWN; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_MOUSE_MOUSEDOWN; }*/ if (aEventTypes & nsIDOMNSEvent::FOCUS) { arrayType = eEventArrayType_Focus; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_FOCUS_FOCUS; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_FOCUS_FOCUS; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_FOCUS_FOCUS; } if (aEventTypes & nsIDOMNSEvent::BLUR) { arrayType = eEventArrayType_Focus; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_FOCUS_BLUR; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_FOCUS_BLUR; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_FOCUS_BLUR; } if (aEventTypes & nsIDOMNSEvent::SELECT) { arrayType = eEventArrayType_Form; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_FORM_SELECT; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_FORM_SELECT; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_FORM_SELECT; } if (aEventTypes & nsIDOMNSEvent::CHANGE) { arrayType = eEventArrayType_Form; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_FORM_CHANGE; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_FORM_CHANGE; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_FORM_CHANGE; } if (aEventTypes & nsIDOMNSEvent::RESET) { arrayType = eEventArrayType_Form; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_FORM_RESET; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_FORM_RESET; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_FORM_RESET; } if (aEventTypes & nsIDOMNSEvent::SUBMIT) { arrayType = eEventArrayType_Form; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_FORM_SUBMIT; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_FORM_SUBMIT; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_FORM_SUBMIT; } if (aEventTypes & nsIDOMNSEvent::LOAD) { arrayType = eEventArrayType_Load; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_LOAD_LOAD; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_LOAD_LOAD; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_LOAD_LOAD; } if (aEventTypes & nsIDOMNSEvent::UNLOAD) { arrayType = eEventArrayType_Load; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_LOAD_UNLOAD; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_LOAD_UNLOAD; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_LOAD_UNLOAD; } if (aEventTypes & nsIDOMNSEvent::ABORT) { arrayType = eEventArrayType_Load; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_LOAD_ABORT; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_LOAD_ABORT; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_LOAD_ABORT; } if (aEventTypes & nsIDOMNSEvent::ERROR) { arrayType = eEventArrayType_Load; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_LOAD_ERROR; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_LOAD_ERROR; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_LOAD_ERROR; } if (aEventTypes & nsIDOMNSEvent::RESIZE) { arrayType = eEventArrayType_Paint; - ls = FindJSEventListener(arrayType); - if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_PAINT_RESIZE; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_PAINT_RESIZE; - ls->mFlags |= NS_EVENT_FLAG_CAPTURE; - } + bits = NS_EVENT_BITS_PAINT_RESIZE; } if (aEventTypes & nsIDOMNSEvent::SCROLL) { arrayType = eEventArrayType_Scroll; - ls = FindJSEventListener(arrayType); + bits = NS_EVENT_BITS_PAINT_RESIZE; + } + + if (arrayType != eEventArrayType_None) { + nsListenerStruct *ls = FindJSEventListener(arrayType); + if (ls) { - if (aInitCapture) ls->mSubTypeCapture |= NS_EVENT_BITS_PAINT_RESIZE; - else ls->mSubTypeCapture &= ~NS_EVENT_BITS_PAINT_RESIZE; + if (aInitCapture) + ls->mSubTypeCapture |= bits; + else + ls->mSubTypeCapture &= ~bits; + ls->mFlags |= NS_EVENT_FLAG_CAPTURE; } } + return NS_OK; } @@ -2002,7 +1901,6 @@ nsEventListenerManager::FixContextMenuEvent(nsIPresContext* aPresContext, nsCOMPtr currentFocus; nsCOMPtr doc; nsIPresShell* shell = aPresContext->PresShell(); - nsString empty; if (aEvent->message == NS_CONTEXTMENU_KEY) { shell->GetDocument(getter_AddRefs(doc)); @@ -2027,7 +1925,7 @@ nsEventListenerManager::FixContextMenuEvent(nsIPresContext* aPresContext, // the client X/Y will be 0,0. We can make use of that if the widget is null. if (aEvent->message == NS_CONTEXTMENU_KEY) NS_IF_RELEASE(((nsGUIEvent*)aEvent)->widget); // nulls out widget - ret = NS_NewDOMUIEvent(aDOMEvent, aPresContext, empty, aEvent); + ret = NS_NewDOMUIEvent(aDOMEvent, aPresContext, EmptyString(), aEvent); } if (NS_SUCCEEDED(ret)) { diff --git a/content/events/src/nsEventListenerManager.h b/content/events/src/nsEventListenerManager.h index 470fb164dba79..70c24214cd9e8 100644 --- a/content/events/src/nsEventListenerManager.h +++ b/content/events/src/nsEventListenerManager.h @@ -292,11 +292,12 @@ class nsEventListenerManager : public nsIEventListenerManager, #define NS_EVENT_BITS_FORM_INPUT 0x10 //nsIDOMLoadListener -#define NS_EVENT_BITS_LOAD_NONE 0x00 -#define NS_EVENT_BITS_LOAD_LOAD 0x01 -#define NS_EVENT_BITS_LOAD_UNLOAD 0x02 -#define NS_EVENT_BITS_LOAD_ABORT 0x04 -#define NS_EVENT_BITS_LOAD_ERROR 0x08 +#define NS_EVENT_BITS_LOAD_NONE 0x00 +#define NS_EVENT_BITS_LOAD_LOAD 0x01 +#define NS_EVENT_BITS_LOAD_UNLOAD 0x02 +#define NS_EVENT_BITS_LOAD_ABORT 0x04 +#define NS_EVENT_BITS_LOAD_ERROR 0x08 +#define NS_EVENT_BITS_LOAD_BEFORE_UNLOAD 0x10 //nsIDOMXULListener #define NS_EVENT_BITS_XUL_NONE 0x00 diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 066816c9f2ed9..f934645576369 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -1809,6 +1809,7 @@ PRBool nsGenericHTMLElement::IsEventName(nsIAtom* aName) aName == nsLayoutAtoms::onmousemove || aName == nsLayoutAtoms::onload || aName == nsLayoutAtoms::onunload || + aName == nsLayoutAtoms::onbeforeunload || aName == nsLayoutAtoms::onabort || aName == nsLayoutAtoms::onerror || aName == nsLayoutAtoms::onfocus || diff --git a/content/html/content/src/nsHTMLScriptElement.cpp b/content/html/content/src/nsHTMLScriptElement.cpp index cea110859cde4..9ae264ed6fc5d 100644 --- a/content/html/content/src/nsHTMLScriptElement.cpp +++ b/content/html/content/src/nsHTMLScriptElement.cpp @@ -301,9 +301,9 @@ nsHTMLScriptEventHandler::Invoke(nsISupports *aTargetObject, } // Invoke the event handler script... - PRBool dummy; - return scriptContext->CallEventHandler(scriptObject, funcObject, aArgCount, - aArgs, &dummy); + jsval dummy; + return scriptContext->CallEventHandler(scriptObject, (JSObject *)funcObject, + aArgCount, (jsval *)aArgs, &dummy); } diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp index 19fb759372a7f..191c0a7bb6da9 100644 --- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -2020,15 +2020,28 @@ nsHTMLDocument::OpenCommon(nsIURI* aSourceURI) } nsCOMPtr docshell = do_QueryReferent(mDocumentContainer); + nsresult rv = NS_OK; // Stop current loads targeted at the window this document is in. if (mScriptGlobalObject && docshell) { + nsCOMPtr cv; + docshell->GetContentViewer(getter_AddRefs(cv)); + + if (cv) { + PRBool okToUnload; + rv = cv->PermitUnload(&okToUnload); + + if (NS_SUCCEEDED(rv) && !okToUnload) { + // We don't want to unload, so stop here, but don't throw an + // exception. + return NS_OK; + } + } + nsCOMPtr webnav(do_QueryInterface(docshell)); webnav->Stop(nsIWebNavigation::STOP_NETWORK); } - nsresult rv = NS_OK; - // The open occurred after the document finished loading. // So we reset the document and create a new one. nsCOMPtr channel; @@ -2242,7 +2255,11 @@ nsHTMLDocument::WriteCommon(const nsAString& aText, if (!mParser) { rv = Open(); - if (NS_FAILED(rv)) { + + // If Open() fails, or if it didn't create a parser (as it won't + // if the user chose to not discard the current document through + // onbeforeunload), don't write anything. + if (NS_FAILED(rv) || !mParser) { return rv; } } else if (IsXHTML()) { diff --git a/content/shared/public/nsLayoutAtomList.h b/content/shared/public/nsLayoutAtomList.h index e68994cec9f4b..e65382caa09bd 100644 --- a/content/shared/public/nsLayoutAtomList.h +++ b/content/shared/public/nsLayoutAtomList.h @@ -147,6 +147,7 @@ LAYOUT_ATOM(viewProperty, "ViewProperty") // Alphabetical list of event handler names LAYOUT_ATOM(onabort, "onabort") +LAYOUT_ATOM(onbeforeunload, "onbeforeunload") LAYOUT_ATOM(onblur, "onblur") LAYOUT_ATOM(onbroadcast, "onbroadcast") LAYOUT_ATOM(onchange, "onchange") diff --git a/content/xbl/src/nsXBLService.cpp b/content/xbl/src/nsXBLService.cpp index 9875e8e017972..b8b9f53cb5059 100644 --- a/content/xbl/src/nsXBLService.cpp +++ b/content/xbl/src/nsXBLService.cpp @@ -232,6 +232,7 @@ class nsXBLStreamListener : public nsIStreamListener, public nsIDOMLoadListener NS_DECL_NSIREQUESTOBSERVER NS_IMETHOD Load(nsIDOMEvent* aEvent); + NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent) { return NS_OK; }; NS_IMETHOD Unload(nsIDOMEvent* aEvent) { return NS_OK; }; NS_IMETHOD Abort(nsIDOMEvent* aEvent) { return NS_OK; }; NS_IMETHOD Error(nsIDOMEvent* aEvent) { return NS_OK; }; diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 0ec6f935fb4d4..b67accef1f6f8 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -228,6 +228,7 @@ nsDocShell::nsDocShell(): mAllowMetaRedirects(PR_TRUE), mAllowImages(PR_TRUE), mFocusDocFirst(PR_FALSE), + mHasFocus(PR_FALSE), mCreatingDocument(PR_FALSE), mUseErrorPages(PR_FALSE), mAllowAuth(PR_TRUE), @@ -236,15 +237,15 @@ nsDocShell::nsDocShell(): mFiredUnloadEvent(PR_FALSE), mEODForCurrentDocument(PR_FALSE), mURIResultedInDocument(PR_FALSE), - mUseExternalProtocolHandler(PR_FALSE), mDisallowPopupWindows(PR_FALSE), + mUseExternalProtocolHandler(PR_FALSE), mIsBeingDestroyed(PR_FALSE), mIsExecutingOnLoadHandler(PR_FALSE), + mIsPrintingOrPP(PR_FALSE), mEditorData(nsnull), mParent(nsnull), mTreeOwner(nsnull), - mChromeEventHandler(nsnull), - mIsPrintingOrPP(PR_FALSE) + mChromeEventHandler(nsnull) { #ifdef PR_LOGGING if (! gDocShellLog) @@ -816,21 +817,18 @@ nsDocShell::PrepareForNewContentModel() NS_IMETHODIMP nsDocShell::FireUnloadNotification() { - nsresult rv; - if (mContentViewer && !mFiredUnloadEvent) { mFiredUnloadEvent = PR_TRUE; - rv = mContentViewer->Unload(); + mContentViewer->Unload(); PRInt32 i, n = mChildren.Count(); for (i = 0; i < n; i++) { nsIDocShellTreeItem* item = (nsIDocShellTreeItem*) mChildren.ElementAt(i); - if(item) { - nsCOMPtr shell(do_QueryInterface(item)); - if (shell) { - rv = shell->FireUnloadNotification(); - } + + nsCOMPtr shell(do_QueryInterface(item)); + if (shell) { + shell->FireUnloadNotification(); } } } @@ -2996,20 +2994,27 @@ nsDocShell::Create() mPrefs = do_GetService(NS_PREF_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); + PRBool tmpbool; + // i don't want to read this pref in every time we load a url // so read it in once here and be done with it... mPrefs->GetBoolPref("network.protocols.useSystemDefaults", - &mUseExternalProtocolHandler); - mPrefs->GetBoolPref("browser.block.target_new_window", &mDisallowPopupWindows); - mPrefs->GetBoolPref("browser.frames.enabled", &mAllowSubframes); + &tmpbool); + mUseExternalProtocolHandler = tmpbool; + + mPrefs->GetBoolPref("browser.block.target_new_window", &tmpbool); + mDisallowPopupWindows = tmpbool; + + mPrefs->GetBoolPref("browser.frames.enabled", &tmpbool); + mAllowSubframes = tmpbool; // Check pref to see if we should prevent frameset spoofing - mPrefs->GetBoolPref("browser.frame.validate_origin", &mValidateOrigin); + mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool); + mValidateOrigin = tmpbool; // Should we use XUL error pages instead of alerts if possible? - PRBool useErrorPages = PR_FALSE; - mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &useErrorPages); - mUseErrorPages = useErrorPages; + mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool); + mUseErrorPages = tmpbool; return NS_OK; } @@ -4464,6 +4469,8 @@ nsDocShell::CreateContentViewer(const char *aContentType, nsIRequest * request, nsIStreamListener ** aContentHandler) { + *aContentHandler = nsnull; + // Can we check the content type of the current content viewer // and reuse it without destroying it and re-creating it? @@ -5095,6 +5102,19 @@ nsDocShell::InternalLoad(nsIURI * aURI, return rv; } + // Check if the page doesn't want to be unloaded. The javascript: + // protocol handler deals with this for javascript: URLs. + if (!bIsJavascript && mContentViewer) { + PRBool okToUnload; + rv = mContentViewer->PermitUnload(&okToUnload); + + if (NS_SUCCEEDED(rv) && !okToUnload) { + // The user chose not to unload the page, interrupt the + // load. + return NS_OK; + } + } + // // Load is being targetted at this docshell so return an error if the // docshell is in the process of being destroyed. @@ -6867,8 +6887,8 @@ nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState *aLayoutHistoryState) //*** nsRefreshTimer: Object Management //***************************************************************************** -nsRefreshTimer::nsRefreshTimer():mRepeat(PR_FALSE), mDelay(0), -mMetaRefresh(PR_FALSE) +nsRefreshTimer::nsRefreshTimer() + : mDelay(0), mRepeat(PR_FALSE), mMetaRefresh(PR_FALSE) { } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index bfc74735f8f04..095953d53c475 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -149,10 +149,9 @@ class nsRefreshTimer : public nsITimerCallback nsCOMPtr mDocShell; nsCOMPtr mURI; - PRBool mRepeat; PRInt32 mDelay; - PRBool mMetaRefresh; - + PRPackedBool mRepeat; + PRPackedBool mMetaRefresh; protected: virtual ~nsRefreshTimer(); @@ -359,31 +358,34 @@ friend class nsDSURIContentListener; // Somebody give me better name nsCOMPtr mLSHE; - PRBool mFiredUnloadEvent; + PRPackedBool mFiredUnloadEvent; // this flag is for bug #21358. a docshell may load many urls // which don't result in new documents being created (i.e. a new content viewer) // we want to make sure we don't call a on load event more than once for a given // content viewer. - PRBool mEODForCurrentDocument; - PRBool mURIResultedInDocument; + PRPackedBool mEODForCurrentDocument; + PRPackedBool mURIResultedInDocument; + + PRPackedBool mIsBeingDestroyed; // used to keep track of whether user click links should be handle by us // or immediately kicked out to an external application. mscott: eventually // i'm going to try to fold this up into the uriloader where it belongs but i haven't // figured out how to do that yet. - PRBool mUseExternalProtocolHandler; + PRPackedBool mUseExternalProtocolHandler; // Disallow popping up new windows with target= - PRBool mDisallowPopupWindows; + PRPackedBool mDisallowPopupWindows; // Validate window targets to prevent frameset spoofing - PRBool mValidateOrigin; - - PRBool mIsBeingDestroyed; + PRPackedBool mValidateOrigin; PRPackedBool mIsExecutingOnLoadHandler; + // Indicates that a DocShell in this "docshell tree" is printing + PRPackedBool mIsPrintingOrPP; + // Editor stuff nsDocShellEditorData* mEditorData; // editor data, if any @@ -398,8 +400,6 @@ friend class nsDSURIContentListener; nsIDocShellTreeOwner * mTreeOwner; // Weak Reference nsIChromeEventHandler * mChromeEventHandler; //Weak Reference - // Indicates that a DocShell in this "docshell tree" is printing - PRBool mIsPrintingOrPP; public: class InterfaceRequestorProxy : public nsIInterfaceRequestor { diff --git a/docshell/base/nsIContentViewer.idl b/docshell/base/nsIContentViewer.idl index 6489b9cb8a87d..48dddeb9f5c79 100644 --- a/docshell/base/nsIContentViewer.idl +++ b/docshell/base/nsIContentViewer.idl @@ -25,6 +25,7 @@ interface nsIContentViewer : nsISupports void loadStart(in nsISupports aDoc); void loadComplete(in unsigned long aStatus); + boolean permitUnload(); void unload(); /** diff --git a/dom/public/coreEvents/nsIDOMLoadListener.h b/dom/public/coreEvents/nsIDOMLoadListener.h index 10efc83aaa685..e33fd327c80ae 100644 --- a/dom/public/coreEvents/nsIDOMLoadListener.h +++ b/dom/public/coreEvents/nsIDOMLoadListener.h @@ -63,6 +63,13 @@ class nsIDOMLoadListener : public nsIDOMEventListener { */ NS_IMETHOD Load(nsIDOMEvent* aEvent) = 0; + /** + * Processes a page beforeUnload event + * @param aMouseEvent @see nsIDOMEvent.h + * @returns whether the event was consumed or ignored. @see nsresult + */ + NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent) = 0; + /** * Processes a page unload event * @param aMouseEvent @see nsIDOMEvent.h diff --git a/dom/public/nsIScriptContext.h b/dom/public/nsIScriptContext.h index e1cc306ea320a..bd5453b8bef1b 100644 --- a/dom/public/nsIScriptContext.h +++ b/dom/public/nsIScriptContext.h @@ -195,9 +195,9 @@ class nsIScriptContext : public nsISupports * @param aBoolResult out parameter returning boolean function result, or * true if the result was not boolean. **/ - virtual nsresult CallEventHandler(void* aTarget, void* aHandler, - PRUint32 argc, void* argv, - PRBool* aBoolResult) = 0; + virtual nsresult CallEventHandler(JSObject* aTarget, JSObject* aHandler, + uintN argc, jsval* argv, + jsval* rval) = 0; /** * Bind an already-compiled event handler function to a name in the given diff --git a/dom/resources/locale/dom.properties b/dom/resources/locale/dom.properties index 67f5c62cbdecb..187a3daa062c6 100644 --- a/dom/resources/locale/dom.properties +++ b/dom/resources/locale/dom.properties @@ -1,2 +1,4 @@ JSURLLoadBlockedWarning=Attempt to load a javascript: URL from one host\nin a window displaying content from another host\nwas blocked by the security manager. -WindowCloseBlockedWarning=Scripts may not close windows that were not opened by script. +WindowCloseBlockedWarning=Scripts may not close windows that were not opened by script. +OnBeforeUnloadPreMessage=Are you sure you want to navigate away from this page? +OnBeforeUnloadPostMessage=Press OK to continue, or Cancel to stay on the current page. diff --git a/dom/src/base/nsDOMClassInfo.h b/dom/src/base/nsDOMClassInfo.h index 6ac5c2dde9b6d..bc8645b0b194e 100644 --- a/dom/src/base/nsDOMClassInfo.h +++ b/dom/src/base/nsDOMClassInfo.h @@ -240,6 +240,7 @@ class nsDOMClassInfo : public nsIXPCScriptable, static jsval sOnchange_id; static jsval sOnselect_id; static jsval sOnload_id; + static jsval sOnbeforeunload_id; static jsval sOnunload_id; static jsval sOnabort_id; static jsval sOnerror_id; diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 139a67b31761f..129c90b670513 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -3563,6 +3563,7 @@ GlobalWindowImpl::Close() } } } + return NS_OK; } } @@ -3578,8 +3579,13 @@ GlobalWindowImpl::Close() mDocShell->GetContentViewer(getter_AddRefs(cv)); if (cv) { PRBool canClose; - cv->RequestWindowClose(&canClose); - if (!canClose) + + rv = cv->PermitUnload(&canClose); + if (NS_SUCCEEDED(rv) && !canClose) + return NS_OK; + + rv = cv->RequestWindowClose(&canClose); + if (NS_SUCCEEDED(rv) && !canClose) return NS_OK; } @@ -5133,7 +5139,7 @@ GlobalWindowImpl::RunTimeout(nsTimeoutImpl *aTimeout) lateness = PR_IntervalToMilliseconds(lateness); timeout->mArgv[timeout->mArgc] = INT_TO_JSVAL((jsint) lateness); - PRBool dummy; + jsval dummy; rv = mContext->CallEventHandler(mJSObject, timeout->mFunObj, timeout->mArgc + 1, timeout->mArgv, &dummy); diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index 90e655ddffd36..dacea2e1b6fe5 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -1195,10 +1195,10 @@ nsJSContext::CompileFunction(void* aTarget, } nsresult -nsJSContext::CallEventHandler(void *aTarget, void *aHandler, PRUint32 argc, - void *argv, PRBool *aBoolResult) +nsJSContext::CallEventHandler(JSObject *aTarget, JSObject *aHandler, + uintN argc, jsval *argv, jsval *rval) { - *aBoolResult = PR_TRUE; + *rval = JSVAL_VOID; if (!mScriptsEnabled) { return NS_OK; @@ -1226,21 +1226,21 @@ nsJSContext::CallEventHandler(void *aTarget, void *aHandler, PRUint32 argc, rv = sSecurityManager->CheckFunctionAccess(mContext, aHandler, aTarget); if (NS_SUCCEEDED(rv)) { - jsval val; jsval funval = OBJECT_TO_JSVAL(aHandler); - PRBool ok = ::JS_CallFunctionValue(mContext, (JSObject *)aTarget, funval, - argc, (jsval *)argv, &val); + PRBool ok = ::JS_CallFunctionValue(mContext, aTarget, funval, argc, argv, + rval); ScriptEvaluated(PR_TRUE); - if (ok) { - *aBoolResult = !JSVAL_IS_BOOLEAN(val) || JSVAL_TO_BOOLEAN(val); - } else { + if (!ok) { // Tell XPConnect about any pending exceptions. This is needed // to avoid dropping JS exceptions in case we got here through // nested calls through XPConnect. NotifyXPCIfExceptionPending(mContext); + + // Don't pass back results from failed calls. + *rval = JSVAL_VOID; } } diff --git a/dom/src/base/nsJSEnvironment.h b/dom/src/base/nsJSEnvironment.h index 4da268e15907c..aecfb3809f998 100644 --- a/dom/src/base/nsJSEnvironment.h +++ b/dom/src/base/nsJSEnvironment.h @@ -94,9 +94,8 @@ class nsJSContext : public nsIScriptContext, PRUint32 aLineNo, PRBool aShared, void** aHandler); - virtual nsresult CallEventHandler(void *aTarget, void *aHandler, - PRUint32 argc, void *argv, - PRBool *aBoolResult); + virtual nsresult CallEventHandler(JSObject *aTarget, JSObject *aHandler, + uintN argc, jsval *argv, jsval* rval); virtual nsresult BindCompiledEventHandler(void *aTarget, nsIAtom *aName, void *aHandler); diff --git a/dom/src/events/nsJSEventListener.cpp b/dom/src/events/nsJSEventListener.cpp index c44a16a6ab192..05940460e2aa8 100644 --- a/dom/src/events/nsJSEventListener.cpp +++ b/dom/src/events/nsJSEventListener.cpp @@ -36,6 +36,7 @@ * * ***** END LICENSE BLOCK ***** */ #include "nsJSEventListener.h" +#include "nsJSUtils.h" #include "nsString.h" #include "nsReadableUtils.h" #include "nsIServiceManager.h" @@ -52,9 +53,9 @@ */ nsJSEventListener::nsJSEventListener(nsIScriptContext *aContext, nsISupports *aObject) - : nsIJSEventListener(aContext, aObject) + : nsIJSEventListener(aContext, aObject), + mReturnResult(nsReturnResult_eNotSet) { - mReturnResult = nsReturnResult_eNotSet; } nsJSEventListener::~nsJSEventListener() @@ -168,21 +169,46 @@ nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent) argc = 1; } - PRBool jsBoolResult; + jsval rval; rv = mContext->CallEventHandler(obj, JSVAL_TO_OBJECT(funval), argc, argv, - &jsBoolResult); + &rval); if (argv != &arg) { ::JS_PopArguments(cx, stackPtr); } if (NS_SUCCEEDED(rv)) { - // if the handler returned false and its sense is not reversed, or - // the handler returned true and its sense is reversed from the - // usual (false means cancel), then prevent default. + if (eventString.Equals(NS_LITERAL_STRING("onbeforeunload"))) { + nsCOMPtr priv(do_QueryInterface(aEvent)); + NS_ENSURE_TRUE(priv, NS_ERROR_UNEXPECTED); - if (!(jsBoolResult ^ (mReturnResult == nsReturnResult_eReverseReturnResult))) - aEvent->PreventDefault(); + nsEvent* event; + priv->GetInternalNSEvent(&event); + NS_ENSURE_TRUE(event && event->message == NS_BEFORE_PAGE_UNLOAD, + NS_ERROR_UNEXPECTED); + + nsBeforePageUnloadEvent *beforeUnload = + NS_STATIC_CAST(nsBeforePageUnloadEvent *, event); + + if (!JSVAL_IS_VOID(rval)) { + aEvent->PreventDefault(); + + if (JSVAL_IS_STRING(rval)) { + beforeUnload->text = nsDependentJSString(JSVAL_TO_STRING(rval)); + } + } + } else { + // if the handler returned false and its sense is not reversed, + // or the handler returned true and its sense is reversed from + // the usual (false means cancel), then prevent default. + + PRBool jsBoolResult = !JSVAL_IS_BOOLEAN(rval) || JSVAL_TO_BOOLEAN(rval); + + if (jsBoolResult == + (mReturnResult == nsReturnResult_eReverseReturnResult)) { + aEvent->PreventDefault(); + } + } } return rv; diff --git a/dom/src/jsurl/nsJSProtocolHandler.cpp b/dom/src/jsurl/nsJSProtocolHandler.cpp index 8f38cb33907da..e37f91826b0db 100644 --- a/dom/src/jsurl/nsJSProtocolHandler.cpp +++ b/dom/src/jsurl/nsJSProtocolHandler.cpp @@ -68,6 +68,8 @@ #include "nsEscape.h" #include "nsIJSContextStack.h" #include "nsIWebNavigation.h" +#include "nsIDocShell.h" +#include "nsIContentViewer.h" static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID); static NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID); @@ -534,10 +536,34 @@ nsJSChannel::InternalOpen(PRBool aIsAsync, nsIStreamListener *aListener, mStreamChannel->GetLoadFlags(&loadFlags); if (loadFlags & LOAD_DOCUMENT_URI) { - // We're loaded as the document channel. Stop all pending - // network loads. + // We're loaded as the document channel. If we go on, + // we'll blow away the current document. Make sure that's + // ok. If so, stop all pending network loads. + + nsCOMPtr cb; + mStreamChannel->GetNotificationCallbacks(getter_AddRefs(cb)); + + nsCOMPtr docShell(do_GetInterface(cb)); + if (docShell) { + nsCOMPtr cv; + docShell->GetContentViewer(getter_AddRefs(cv)); + + if (cv) { + PRBool okToUnload; + + if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && + !okToUnload) { + // The user didn't want to unload the current + // page, translate this into an undefined + // return from the javascript: URL... + rv = NS_ERROR_DOM_RETVAL_UNDEFINED; + } + } + } - rv = StopAll(); + if (NS_SUCCEEDED(rv)) { + rv = StopAll(); + } } if (NS_SUCCEEDED(rv)) { @@ -549,7 +575,9 @@ nsJSChannel::InternalOpen(PRBool aIsAsync, nsIStreamListener *aListener, rv = mStreamChannel->Open(aResult); } } - } else { + } + + if (NS_FAILED(rv)) { // Propagate the failure down to the underlying channel... mStreamChannel->Cancel(rv); } diff --git a/extensions/xmlextras/base/src/nsDOMParser.cpp b/extensions/xmlextras/base/src/nsDOMParser.cpp index 2a65fcea6a599..26190f407bf49 100644 --- a/extensions/xmlextras/base/src/nsDOMParser.cpp +++ b/extensions/xmlextras/base/src/nsDOMParser.cpp @@ -301,6 +301,12 @@ nsDOMParser::Load(nsIDOMEvent* aEvent) return NS_OK; } +nsresult +nsDOMParser::BeforeUnload(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + nsresult nsDOMParser::Unload(nsIDOMEvent* aEvent) { diff --git a/extensions/xmlextras/base/src/nsDOMParser.h b/extensions/xmlextras/base/src/nsDOMParser.h index 46845304e625d..c9198cd879f98 100644 --- a/extensions/xmlextras/base/src/nsDOMParser.h +++ b/extensions/xmlextras/base/src/nsDOMParser.h @@ -64,6 +64,7 @@ class nsDOMParser : public nsIDOMParser, // nsIDOMLoadListener NS_IMETHOD Load(nsIDOMEvent* aEvent); + NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent); NS_IMETHOD Unload(nsIDOMEvent* aEvent); NS_IMETHOD Abort(nsIDOMEvent* aEvent); NS_IMETHOD Error(nsIDOMEvent* aEvent); diff --git a/extensions/xmlextras/base/src/nsLoadListenerProxy.cpp b/extensions/xmlextras/base/src/nsLoadListenerProxy.cpp index 102828370f74b..be7259901fbe6 100644 --- a/extensions/xmlextras/base/src/nsLoadListenerProxy.cpp +++ b/extensions/xmlextras/base/src/nsLoadListenerProxy.cpp @@ -74,6 +74,18 @@ nsLoadListenerProxy::Load(nsIDOMEvent* aEvent) return NS_OK; } +NS_IMETHODIMP +nsLoadListenerProxy::BeforeUnload(nsIDOMEvent* aEvent) +{ + nsCOMPtr listener(do_QueryReferent(mParent)); + + if (listener) { + return listener->BeforeUnload(aEvent); + } + + return NS_OK; +} + NS_IMETHODIMP nsLoadListenerProxy::Unload(nsIDOMEvent* aEvent) { diff --git a/extensions/xmlextras/base/src/nsLoadListenerProxy.h b/extensions/xmlextras/base/src/nsLoadListenerProxy.h index 8e1a5ca3ead25..ee33e4c7e9ac2 100644 --- a/extensions/xmlextras/base/src/nsLoadListenerProxy.h +++ b/extensions/xmlextras/base/src/nsLoadListenerProxy.h @@ -66,6 +66,7 @@ class nsLoadListenerProxy : public nsIDOMLoadListener { // nsIDOMLoadListener NS_IMETHOD Load(nsIDOMEvent* aEvent); + NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent); NS_IMETHOD Unload(nsIDOMEvent* aEvent); NS_IMETHOD Abort(nsIDOMEvent* aEvent); NS_IMETHOD Error(nsIDOMEvent* aEvent); diff --git a/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp b/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp index a0c2361dd8b43..caa5afcfc1872 100644 --- a/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp +++ b/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp @@ -1435,6 +1435,12 @@ nsXMLHttpRequest::Unload(nsIDOMEvent* aEvent) return NS_OK; } +nsresult +nsXMLHttpRequest::BeforeUnload(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + nsresult nsXMLHttpRequest::Abort(nsIDOMEvent* aEvent) { diff --git a/extensions/xmlextras/base/src/nsXMLHttpRequest.h b/extensions/xmlextras/base/src/nsXMLHttpRequest.h index 2db968ddef37d..ab8d5455e2cde 100644 --- a/extensions/xmlextras/base/src/nsXMLHttpRequest.h +++ b/extensions/xmlextras/base/src/nsXMLHttpRequest.h @@ -89,6 +89,7 @@ class nsXMLHttpRequest : public nsIXMLHttpRequest, // nsIDOMLoadListener NS_IMETHOD Load(nsIDOMEvent* aEvent); + NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent); NS_IMETHOD Unload(nsIDOMEvent* aEvent); NS_IMETHOD Abort(nsIDOMEvent* aEvent); NS_IMETHOD Error(nsIDOMEvent* aEvent); diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 0dd9f615f9cbd..a966756573594 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -199,6 +199,10 @@ static const char sPrintOptionsContractID[] = "@mozilla.org/gfx/printset static NS_DEFINE_CID(kGalleyContextCID, NS_GALLEYCONTEXT_CID); +static const char kDOMStringBundleURL[] = + "chrome://communicator/locale/dom/dom.properties"; + + #ifdef NS_DEBUG #undef NOISY_VIEWER @@ -414,9 +418,11 @@ class DocumentViewerImpl : public nsIDocumentViewer, nsIWidget* mParentWidget; // purposely won't be ref counted + PRPackedBool mInPermitUnload; + #ifdef NS_PRINTING + PRPackedBool mClosingWhilePrinting; nsPrintEngine* mPrintEngine; - PRBool mClosingWhilePrinting; nsCOMPtr mDialogParentWin; #if NS_PRINT_PREVIEW // These data member support delayed printing when the document is loading @@ -961,6 +967,104 @@ DocumentViewerImpl::LoadComplete(nsresult aStatus) return rv; } +NS_IMETHODIMP +DocumentViewerImpl::PermitUnload(PRBool *aPermitUnload) +{ + *aPermitUnload = PR_TRUE; + + if (!mDocument || mInPermitUnload) { + return NS_OK; + } + + // First, get the script global object from the document... + nsIScriptGlobalObject *global = mDocument->GetScriptGlobalObject(); + + if (!global) { + // This is odd, but not fatal + NS_WARNING("nsIScriptGlobalObject not set for document!"); + return NS_OK; + } + + // Now, fire an BeforeUnload event to the document and see if it's ok + // to unload... + nsEventStatus status = nsEventStatus_eIgnore; + nsBeforePageUnloadEvent event(NS_BEFORE_PAGE_UNLOAD); + + // In evil cases we might be destroyed while handling the + // onbeforeunload event, don't let that happen. + nsRefPtr kungFuDeathGrip(this); + + mInPermitUnload = PR_TRUE; + nsresult rv = global->HandleDOMEvent(mPresContext, &event, nsnull, + NS_EVENT_FLAG_INIT, &status); + mInPermitUnload = PR_FALSE; + + if (NS_SUCCEEDED(rv) && event.flags & NS_EVENT_FLAG_NO_DEFAULT) { + // Ask the user if it's ok to unload the current page + + nsCOMPtr prompt(do_GetInterface(mContainer)); + + if (prompt) { + nsCOMPtr + stringService(do_GetService(NS_STRINGBUNDLE_CONTRACTID)); + NS_ENSURE_TRUE(stringService, NS_OK); + + nsCOMPtr bundle; + stringService->CreateBundle(kDOMStringBundleURL, getter_AddRefs(bundle)); + NS_ENSURE_TRUE(bundle, NS_OK); + + nsXPIDLString preMsg, postMsg; + nsresult rv; + rv = bundle->GetStringFromName(NS_LITERAL_STRING("OnBeforeUnloadPreMessage").get(), getter_Copies(preMsg)); + rv |= bundle->GetStringFromName(NS_LITERAL_STRING("OnBeforeUnloadPostMessage").get(), getter_Copies(postMsg)); + + // GetStringFromName can succeed, yet give NULL strings back. + if (NS_FAILED(rv) || !preMsg || !postMsg) { + NS_ERROR("Failed to get strings from dom.properties!"); + return NS_OK; + } + + // Limit the length of the text the page can inject into this + // dialogue to 1024 characters. + PRInt32 len = PR_MIN(event.text.Length(), 1024); + + nsAutoString msg(preMsg + NS_LITERAL_STRING("\n\n") + + StringHead(event.text, len) + + NS_LITERAL_STRING("\n\n") + postMsg); + + // This doesn't pass a title, which makes the title be + // "Confirm", is that ok, or do we want a localizable title for + // this dialogue? + if (NS_FAILED(prompt->Confirm(nsnull, msg.get(), aPermitUnload))) { + *aPermitUnload = PR_TRUE; + } + } + } + + nsCOMPtr docShellNode(do_QueryInterface(mContainer)); + + if (docShellNode) { + PRInt32 childCount; + docShellNode->GetChildCount(&childCount); + + for (PRInt32 i = 0; i < childCount && *aPermitUnload; ++i) { + nsCOMPtr item; + docShellNode->GetChildAt(i, getter_AddRefs(item)); + + nsCOMPtr docShell(do_QueryInterface(item)); + nsCOMPtr cv; + + docShell->GetContentViewer(getter_AddRefs(cv)); + + if (cv) { + cv->PermitUnload(aPermitUnload); + } + } + } + + return NS_OK; +} + NS_IMETHODIMP DocumentViewerImpl::Unload() { @@ -971,11 +1075,11 @@ DocumentViewerImpl::Unload() } // First, get the script global object from the document... - nsCOMPtr global = mDocument->GetScriptGlobalObject(); + nsIScriptGlobalObject *global = mDocument->GetScriptGlobalObject(); if (!global) { // Fail if no ScriptGlobalObject is available... - NS_ASSERTION(0, "nsIScriptGlobalObject not set for document!"); + NS_ERROR("nsIScriptGlobalObject not set for document!"); return NS_ERROR_NULL_POINTER; } @@ -1121,6 +1225,8 @@ DocumentViewerImpl::Destroy() mPresShell = nsnull; } + mContainer = nsnull; + return NS_OK; } diff --git a/layout/base/nsLayoutAtomList.h b/layout/base/nsLayoutAtomList.h index e68994cec9f4b..e65382caa09bd 100644 --- a/layout/base/nsLayoutAtomList.h +++ b/layout/base/nsLayoutAtomList.h @@ -147,6 +147,7 @@ LAYOUT_ATOM(viewProperty, "ViewProperty") // Alphabetical list of event handler names LAYOUT_ATOM(onabort, "onabort") +LAYOUT_ATOM(onbeforeunload, "onbeforeunload") LAYOUT_ATOM(onblur, "onblur") LAYOUT_ATOM(onbroadcast, "onbroadcast") LAYOUT_ATOM(onchange, "onchange") diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h index 676236a1e26f4..2401c76b17481 100644 --- a/widget/public/nsGUIEvent.h +++ b/widget/public/nsGUIEvent.h @@ -44,6 +44,7 @@ #include "nsRect.h" #include "nsEvent.h" #include "nsHashtable.h" +#include "nsString.h" // nsIDOMEvent contains a long enum which includes a member called ERROR, // which conflicts with something that Windows defines somewhere. @@ -60,33 +61,34 @@ class nsIMenuItem; class nsIAccessible; class nsIContent; class nsIURI; - + /** * Event Struct Types */ -#define NS_EVENT 1 -#define NS_GUI_EVENT 2 -#define NS_SIZE_EVENT 3 -#define NS_SIZEMODE_EVENT 4 -#define NS_ZLEVEL_EVENT 5 -#define NS_PAINT_EVENT 6 -#define NS_SCROLLBAR_EVENT 7 -#define NS_INPUT_EVENT 8 -#define NS_KEY_EVENT 9 -#define NS_MOUSE_EVENT 10 -#define NS_MENU_EVENT 11 -#define NS_SCRIPT_ERROR_EVENT 12 -#define NS_TEXT_EVENT 13 -#define NS_COMPOSITION_EVENT 14 -#define NS_RECONVERSION_EVENT 15 -#define NS_MOUSE_SCROLL_EVENT 16 -#define NS_SCROLLPORT_EVENT 18 -#define NS_ACCESSIBLE_EVENT 20 -#define NS_FORM_EVENT 21 -#define NS_FOCUS_EVENT 22 -#define NS_POPUP_EVENT 23 -#define NS_APPCOMMAND_EVENT 24 -#define NS_POPUPBLOCKED_EVENT 25 +#define NS_EVENT 1 +#define NS_GUI_EVENT 2 +#define NS_SIZE_EVENT 3 +#define NS_SIZEMODE_EVENT 4 +#define NS_ZLEVEL_EVENT 5 +#define NS_PAINT_EVENT 6 +#define NS_SCROLLBAR_EVENT 7 +#define NS_INPUT_EVENT 8 +#define NS_KEY_EVENT 9 +#define NS_MOUSE_EVENT 10 +#define NS_MENU_EVENT 11 +#define NS_SCRIPT_ERROR_EVENT 12 +#define NS_TEXT_EVENT 13 +#define NS_COMPOSITION_EVENT 14 +#define NS_RECONVERSION_EVENT 15 +#define NS_MOUSE_SCROLL_EVENT 16 +#define NS_SCROLLPORT_EVENT 18 +#define NS_ACCESSIBLE_EVENT 20 +#define NS_FORM_EVENT 21 +#define NS_FOCUS_EVENT 22 +#define NS_POPUP_EVENT 23 +#define NS_APPCOMMAND_EVENT 24 +#define NS_POPUPBLOCKED_EVENT 25 +#define NS_BEFORE_PAGE_UNLOAD_EVENT 26 #define NS_EVENT_FLAG_NONE 0x0000 @@ -205,6 +207,16 @@ struct nsScriptErrorEvent : public nsEvent const PRUnichar* fileName; }; +struct nsBeforePageUnloadEvent : public nsEvent +{ + nsBeforePageUnloadEvent(PRUint32 msg) + : nsEvent(msg, NS_BEFORE_PAGE_UNLOAD_EVENT) + { + } + + nsString text; +}; + /** * Window resize event */ @@ -723,6 +735,7 @@ enum nsDragDropEventStatus { #define NS_IMAGE_ABORT (NS_STREAM_EVENT_START + 3) #define NS_IMAGE_ERROR (NS_STREAM_EVENT_START + 4) #define NS_SCRIPT_LOAD (NS_STREAM_EVENT_START + 5) +#define NS_BEFORE_PAGE_UNLOAD (NS_STREAM_EVENT_START + 6) #define NS_FORM_EVENT_START 1200 #define NS_FORM_SUBMIT (NS_FORM_EVENT_START) diff --git a/xpfe/appshell/src/nsWebShellWindow.cpp b/xpfe/appshell/src/nsWebShellWindow.cpp index 9fdc227040f66..401dec3d7999c 100644 --- a/xpfe/appshell/src/nsWebShellWindow.cpp +++ b/xpfe/appshell/src/nsWebShellWindow.cpp @@ -1489,26 +1489,25 @@ PRBool nsWebShellWindow::ExecuteCloseHandler() than it otherwise would.) */ nsCOMPtr kungFuDeathGrip(this); - nsresult rv; - nsCOMPtr globalObjectOwner(do_QueryInterface(mWebShell)); - nsCOMPtr globalObject; + nsCOMPtr globalObject(do_GetInterface(mWebShell)); - if (globalObjectOwner) { - if (NS_SUCCEEDED(globalObjectOwner->GetScriptGlobalObject(getter_AddRefs(globalObject))) && globalObject) { - nsCOMPtr contentViewer; - if (NS_SUCCEEDED(mDocShell->GetContentViewer(getter_AddRefs(contentViewer)))) { - nsCOMPtr docViewer; - nsCOMPtr presContext; - docViewer = do_QueryInterface(contentViewer); - if (docViewer && NS_SUCCEEDED(docViewer->GetPresContext(getter_AddRefs(presContext)))) { - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(NS_XUL_CLOSE); - rv = globalObject->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); - if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault) - return PR_TRUE; - // else fall through and return PR_FALSE - } - } + if (globalObject) { + nsCOMPtr contentViewer; + mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); + nsCOMPtr docViewer(do_QueryInterface(contentViewer)); + + if (docViewer) { + nsCOMPtr presContext; + docViewer->GetPresContext(getter_AddRefs(presContext)); + + nsEventStatus status = nsEventStatus_eIgnore; + nsMouseEvent event(NS_XUL_CLOSE); + + nsresult rv = globalObject->HandleDOMEvent(presContext, &event, nsnull, + NS_EVENT_FLAG_INIT, &status); + if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault) + return PR_TRUE; + // else fall through and return PR_FALSE } } diff --git a/xpfe/browser/resources/content/navigator.js b/xpfe/browser/resources/content/navigator.js index 5c8c717dd6f2c..9b6d6d7b50866 100644 --- a/xpfe/browser/resources/content/navigator.js +++ b/xpfe/browser/resources/content/navigator.js @@ -2279,7 +2279,8 @@ function maybeInitPopupContext() function WindowIsClosing() { var browser = getBrowser(); - var numtabs = browser.mTabContainer.childNodes.length; + var cn = browser.mTabContainer.childNodes; + var numtabs = cn.length; var reallyClose = true; if (numtabs > 1) { @@ -2307,5 +2308,13 @@ function WindowIsClosing() } } //if the warn-me pref was true } //if multiple tabs are open + + for (var i = 0; reallyClose && i < numtabs; ++i) { + var ds = browser.getBrowserForTab(cn[i]).docShell; + + if (ds.contentViewer && !ds.contentViewer.permitUnload()) + reallyClose = false; + } + return reallyClose; } diff --git a/xpfe/global/resources/content/bindings/tabbrowser.xml b/xpfe/global/resources/content/bindings/tabbrowser.xml index c26e62df5ee17..1e18cd8fd8405 100644 --- a/xpfe/global/resources/content/bindings/tabbrowser.xml +++ b/xpfe/global/resources/content/bindings/tabbrowser.xml @@ -852,11 +852,11 @@ else this.mTabContainer.selectedItem = aTab; - while (this.mTabContainer.lastChild != aTab) - this.removeTab(this.mTabContainer.lastChild); - - while (aTab.previousSibling) - this.removeTab(aTab.previousSibling); + var childNodes = this.mTabContainer.childNodes; + for (var i = childNodes.length - 1; i >= 0; --i) { + if (childNodes[i] != aTab) + this.removeTab(childNodes[i]); + } ]]> @@ -884,6 +884,11 @@ return; } + var ds = this.getBrowserForTab(aTab).docShell; + + if (ds.contentViewer && !ds.contentViewer.permitUnload()) + return; + if (l == 2) { var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide"); if (autohide)