diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index 1168a9d95c2..55c4f52e197 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -5606,7 +5606,7 @@ nsDocShell::RestoreFromHistory() if (shell) shell->Thaw(); - return NS_OK; + return privWin->FireDelayedDOMEvents(); } NS_IMETHODIMP diff --git a/mozilla/dom/public/base/nsPIDOMWindow.h b/mozilla/dom/public/base/nsPIDOMWindow.h index 7e0d7888296..acd8c25551c 100644 --- a/mozilla/dom/public/base/nsPIDOMWindow.h +++ b/mozilla/dom/public/base/nsPIDOMWindow.h @@ -262,6 +262,10 @@ public: // Resume suspended timeouts in this window and in child windows. virtual nsresult ResumeTimeouts() = 0; + + // Fire any DOM notification events related to things that happened while + // the window was frozen. + virtual nsresult FireDelayedDOMEvents() = 0; nsPIDOMWindow *GetOuterWindow() { diff --git a/mozilla/dom/src/base/nsGlobalWindow.cpp b/mozilla/dom/src/base/nsGlobalWindow.cpp index ba3158a9d64..c9c2e98c332 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.cpp +++ b/mozilla/dom/src/base/nsGlobalWindow.cpp @@ -89,6 +89,8 @@ #include "nsIDOMElement.h" #include "nsIDOMDocumentEvent.h" #include "nsIDOMEvent.h" +#include "nsIDOMHTMLDocument.h" +#include "nsIDOMHTMLElement.h" #include "nsIDOMKeyEvent.h" #include "nsIDOMPopupBlockedEvent.h" #include "nsIDOMPkcs11.h" @@ -140,6 +142,8 @@ #include "nsIURIFixup.h" #include "nsCDefaultURIFixup.h" #include "nsEventDispatcher.h" +#include "nsIObserverService.h" +#include "nsNetUtil.h" #include "plbase64.h" @@ -293,6 +297,26 @@ static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1"; static const char kCryptoContractID[] = NS_CRYPTO_CONTRACTID; static const char kPkcs11ContractID[] = NS_PKCS11_CONTRACTID; +/** + * An indirect observer object that means we don't have to implement nsIObserver + * on nsGlobalWindow, where any script could see it. + */ +class nsGlobalWindowObserver : public nsIObserver { +public: + nsGlobalWindowObserver(nsGlobalWindow* aWindow) : mWindow(aWindow) {} + NS_DECL_ISUPPORTS + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) + { + if (!mWindow) + return NS_OK; + return mWindow->Observe(aSubject, aTopic, aData); + } + void Forget() { mWindow = nsnull; } +private: + nsGlobalWindow* mWindow; +}; + +NS_IMPL_ISUPPORTS1(nsGlobalWindowObserver, nsIObserver) //***************************************************************************** //*** nsGlobalWindow: Object Management @@ -325,6 +349,20 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) // |this| is an inner window, add this inner window to the outer // |window list of inners. PR_INSERT_AFTER(this, aOuterWindow); + + mObserver = new nsGlobalWindowObserver(this); + if (mObserver) { + NS_ADDREF(mObserver); + nsCOMPtr os = + do_GetService("@mozilla.org/observer-service;1"); + if (os) { + // Watch for online/offline status changes so we can fire events. Use + // a strong reference. + os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, PR_FALSE); + } + } + } else { + mObserver = nsnull; } // We could have failed the first time through trying @@ -345,7 +383,6 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG, ("DOMWINDOW %p created outer=%p", this, aOuterWindow)); #endif - } nsGlobalWindow::~nsGlobalWindow() @@ -363,6 +400,18 @@ nsGlobalWindow::~nsGlobalWindow() ("DOMWINDOW %p destroyed", this)); #endif + if (mObserver) { + nsCOMPtr os = + do_GetService("@mozilla.org/observer-service;1"); + if (os) { + os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); + } + // Drop its reference to this dying window, in case for some bogus reason + // the object stays around. + mObserver->Forget(); + NS_RELEASE(mObserver); + } + if (IsOuterWindow()) { // An outer window is destroyed with inner windows still possibly // alive, iterate through the inner windows and null out their @@ -5416,6 +5465,62 @@ nsGlobalWindow::GetInterface(const nsIID & aIID, void **aSink) return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE; } +void +nsGlobalWindow::FireOfflineStatusEvent() +{ + if (!mDocument) + return; + nsCOMPtr doc(do_QueryInterface(mDocument)); + nsAutoString name; + if (NS_IsOffline()) { + name.AssignLiteral("offline"); + } else { + name.AssignLiteral("online"); + } + // The event is fired at the body element, or if there is no body element, + // at the document. + nsCOMPtr eventTarget = doc.get(); + nsCOMPtr htmlDoc = do_QueryInterface(doc); + if (htmlDoc) { + nsCOMPtr body; + htmlDoc->GetBody(getter_AddRefs(body)); + if (body) { + eventTarget = body; + } + } + nsContentUtils::DispatchTrustedEvent(doc, eventTarget, name, PR_TRUE, PR_FALSE); +} + +nsresult +nsGlobalWindow::Observe(nsISupports* aSubject, + const char* aTopic, + const PRUnichar* aData) +{ + if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) { + if (IsFrozen()) { + // if an even number of notifications arrive while we're frozen, + // we don't need to fire. + mFireOfflineStatusChangeEventOnThaw = !mFireOfflineStatusChangeEventOnThaw; + } else { + FireOfflineStatusEvent(); + } + return NS_OK; + } + NS_WARNING("unrecognized topic in nsGlobalWindow::Observe"); + return NS_ERROR_FAILURE; +} + +nsresult +nsGlobalWindow::FireDelayedDOMEvents() +{ + FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED); + + if (mFireOfflineStatusChangeEventOnThaw) { + mFireOfflineStatusChangeEventOnThaw = PR_FALSE; + FireOfflineStatusEvent(); + } +} + //***************************************************************************** // nsGlobalWindow: Window Control Functions //***************************************************************************** @@ -7434,14 +7539,7 @@ nsNavigator::GetOnLine(PRBool* aOnline) { NS_PRECONDITION(aOnline, "Null out param"); - *aOnline = PR_FALSE; // No ioservice would mean this is the case - - nsCOMPtr ios(do_GetService(NS_IOSERVICE_CONTRACTID)); - if (ios) { - ios->GetOffline(aOnline); - *aOnline = !*aOnline; - } - + *aOnline = !NS_IsOffline(); return NS_OK; } diff --git a/mozilla/dom/src/base/nsGlobalWindow.h b/mozilla/dom/src/base/nsGlobalWindow.h index 9667902f378..3782d66d288 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.h +++ b/mozilla/dom/src/base/nsGlobalWindow.h @@ -110,6 +110,7 @@ class nsScreen; class nsHistory; class nsIDocShellLoadInfo; class WindowStateHolder; +class nsGlobalWindowObserver; // permissible values for CheckOpenAllow enum OpenAllowValue { @@ -224,6 +225,7 @@ public: virtual NS_HIDDEN_(nsresult) SaveWindowState(nsISupports **aState); virtual NS_HIDDEN_(nsresult) RestoreWindowState(nsISupports *aState); virtual NS_HIDDEN_(nsresult) ResumeTimeouts(); + virtual NS_HIDDEN_(nsresult) FireDelayedDOMEvents(); virtual NS_HIDDEN_(PRBool) WouldReuseInnerWindow(nsIDocument *aNewDocument); @@ -288,6 +290,8 @@ public: { return mIsFrozen; } + + nsresult Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData); static void ShutDown(); static PRBool IsCallerChrome(); @@ -401,7 +405,8 @@ protected: const nsAString &aPopupURL, const nsAString &aPopupWindowName, const nsAString &aPopupWindowFeatures); - + void FireOfflineStatusEvent(); + void FlushPendingNotifications(mozFlushType aType); void EnsureReflowFlushAndPaint(); nsresult CheckSecurityWidthAndHeight(PRInt32* width, PRInt32* height); @@ -481,6 +486,9 @@ protected: PRPackedBool mHadOriginalOpener : 1; PRPackedBool mIsPopupSpam : 1; + // Track what sorts of events we need to fire when thawed + PRPackedBool mFireOfflineStatusChangeEventOnThaw : 1; + nsCOMPtr mContext; nsCOMPtr mOpener; nsCOMPtr mControllers; @@ -499,6 +507,7 @@ protected: nsRefPtr mLocation; nsString mStatus; nsString mDefaultStatus; + nsGlobalWindowObserver* mObserver; nsIScriptGlobalObjectOwner* mGlobalObjectOwner; // Weak Reference nsCOMPtr mCrypto;