From 7e78dba969a119b13b7da1dd40a056140665858f Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Thu, 19 Apr 2012 20:13:20 -0400 Subject: [PATCH] Bug 729204 - Make docshell privacy notifications work across multiple processes. r=bz --- docshell/base/Makefile.in | 2 ++ docshell/base/nsDSURIContentListener.cpp | 3 +- docshell/base/nsDocShell.cpp | 25 ++++++++++++-- docshell/test/Makefile.in | 8 +++-- docshell/test/unit/test_pb_notification.js | 23 +++++++++++++ docshell/test/unit/xpcshell.ini | 1 + .../test/unit_ipc/test_pb_notification_ipc.js | 20 +++++++++++ docshell/test/unit_ipc/xpcshell.ini | 5 +++ dom/ipc/ContentChild.cpp | 8 +++++ dom/ipc/ContentChild.h | 2 ++ dom/ipc/ContentParent.cpp | 33 +++++++++++++++++++ dom/ipc/ContentParent.h | 3 ++ dom/ipc/PContent.ipdl | 6 ++++ js/src/json.h | 4 +++ testing/xpcshell/xpcshell.ini | 1 + 15 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 docshell/test/unit/test_pb_notification.js create mode 100644 docshell/test/unit_ipc/test_pb_notification_ipc.js create mode 100644 docshell/test/unit_ipc/xpcshell.ini diff --git a/docshell/base/Makefile.in b/docshell/base/Makefile.in index 79d6af42daea..c0b08e022436 100644 --- a/docshell/base/Makefile.in +++ b/docshell/base/Makefile.in @@ -114,6 +114,8 @@ CPPSRCS = \ # static lib. FORCE_STATIC_LIB = 1 +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk LOCAL_INCLUDES += \ diff --git a/docshell/base/nsDSURIContentListener.cpp b/docshell/base/nsDSURIContentListener.cpp index 7b58b4393f78..1943af69ee8e 100644 --- a/docshell/base/nsDSURIContentListener.cpp +++ b/docshell/base/nsDSURIContentListener.cpp @@ -68,7 +68,8 @@ nsDSURIContentListener::nsDSURIContentListener(nsDocShell* aDocShell) if (NS_UNLIKELY(!initializedPrefCache)) { // Lock the pref so that the user's changes to it, if any, are ignored. nsIPrefBranch *root = Preferences::GetRootBranch(); - root->LockPref("b2g.ignoreXFrameOptions"); + if (XRE_GetProcessType() != GeckoProcessType_Content) + root->LockPref("b2g.ignoreXFrameOptions"); Preferences::AddBoolVarCache(&sIgnoreXFrameOptions, "b2g.ignoreXFrameOptions"); initializedPrefCache = true; diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 2beaaa6152cf..b786a8ed8ed5 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -41,6 +41,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "mozilla/dom/ContentChild.h" #include "mozilla/Util.h" #ifdef MOZ_LOGGING @@ -714,6 +715,19 @@ ConvertLoadTypeToNavigationType(PRUint32 aLoadType) static nsISHEntry* GetRootSHEntry(nsISHEntry *entry); +static void +IncreasePrivateDocShellCount() +{ + gNumberOfPrivateDocShells++; + if (gNumberOfPrivateDocShells > 1 || + XRE_GetProcessType() != GeckoProcessType_Content) { + return; + } + + mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); + cc->SendPrivateDocShellsExist(true); +} + static void DecreasePrivateDocShellCount() { @@ -721,9 +735,16 @@ DecreasePrivateDocShellCount() gNumberOfPrivateDocShells--; if (!gNumberOfPrivateDocShells) { + if (XRE_GetProcessType() == GeckoProcessType_Content) { + mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); + cc->SendPrivateDocShellsExist(false); + return; + } + nsCOMPtr obsvc = mozilla::services::GetObserverService(); - if (obsvc) + if (obsvc) { obsvc->NotifyObservers(nsnull, "last-pb-context-exited", nsnull); + } } } @@ -2023,7 +2044,7 @@ nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) if (aUsePrivateBrowsing != mInPrivateBrowsing) { mInPrivateBrowsing = aUsePrivateBrowsing; if (aUsePrivateBrowsing) { - gNumberOfPrivateDocShells++; + IncreasePrivateDocShellCount(); } else { DecreasePrivateDocShellCount(); } diff --git a/docshell/test/Makefile.in b/docshell/test/Makefile.in index beced352dda6..2f1cbe90b3d6 100644 --- a/docshell/test/Makefile.in +++ b/docshell/test/Makefile.in @@ -47,10 +47,14 @@ DIRS += chrome \ navigation \ $(NULL) -XPCSHELL_TESTS = unit - include $(DEPTH)/config/autoconf.mk +XPCSHELL_TESTS = unit +# FIXME/bug 575918: out-of-process xpcshell is broken on OS X +ifneq ($(OS_ARCH),Darwin) +XPCSHELL_TESTS += unit_ipc +endif + ifneq (mobile,$(MOZ_BUILD_APP)) DIRS += browser endif diff --git a/docshell/test/unit/test_pb_notification.js b/docshell/test/unit/test_pb_notification.js new file mode 100644 index 000000000000..07fbd8f157d5 --- /dev/null +++ b/docshell/test/unit/test_pb_notification.js @@ -0,0 +1,23 @@ +if (typeof Cc === "undefined") + Cc = Components.classes; +if (typeof Ci === "undefined") + Ci = Components.interfaces; + +function destroy_transient_docshell() { + var docshell = Cc["@mozilla.org/docshell;1"].createInstance(Ci.nsIDocShell); + docshell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing = true; + do_test_pending(); + do_timeout(0, Components.utils.forceGC); +} + +function run_test() { + var obs = { + observe: function(aSubject, aTopic, aData) { + do_check_eq(aTopic, "last-pb-context-exited"); + do_test_finished(); + } + }; + var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); + os.addObserver(obs, "last-pb-context-exited", false); + destroy_transient_docshell(); +} \ No newline at end of file diff --git a/docshell/test/unit/xpcshell.ini b/docshell/test/unit/xpcshell.ini index ee44a8d3c52f..c8f51b1f5f43 100644 --- a/docshell/test/unit/xpcshell.ini +++ b/docshell/test/unit/xpcshell.ini @@ -5,3 +5,4 @@ tail = [test_bug414201_jfif.js] [test_bug442584.js] [test_nsIDownloadHistory.js] +[test_pb_notification.js] \ No newline at end of file diff --git a/docshell/test/unit_ipc/test_pb_notification_ipc.js b/docshell/test/unit_ipc/test_pb_notification_ipc.js new file mode 100644 index 000000000000..06a0fc7b1f22 --- /dev/null +++ b/docshell/test/unit_ipc/test_pb_notification_ipc.js @@ -0,0 +1,20 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() { + var notifications = 0; + var obs = { + observe: function(aSubject, aTopic, aData) { + do_check_eq(aTopic, "last-pb-context-exited"); + notifications++; + } + }; + var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); + os.addObserver(obs, "last-pb-context-exited", false); + + run_test_in_child("../unit/test_pb_notification.js", + function() { + do_check_eq(notifications, 1); + do_test_finished(); + }); +} \ No newline at end of file diff --git a/docshell/test/unit_ipc/xpcshell.ini b/docshell/test/unit_ipc/xpcshell.ini new file mode 100644 index 000000000000..8cf1f0678048 --- /dev/null +++ b/docshell/test/unit_ipc/xpcshell.ini @@ -0,0 +1,5 @@ +[DEFAULT] +head = +tail = + +[test_pb_notification_ipc.js] \ No newline at end of file diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index f9fb6d760c1e..f9c7dc746cea 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -820,5 +820,13 @@ ContentChild::RecvSetID(const PRUint64 &id) return true; } +bool +ContentChild::RecvLastPrivateDocShellDestroyed() +{ + nsCOMPtr obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(nsnull, "last-pb-context-exited", nsnull); + return true; +} + } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 4f3be49a30c1..4681590eddd3 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -170,6 +170,8 @@ public: virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID); virtual bool RecvSetID(const PRUint64 &id); + virtual bool RecvLastPrivateDocShellDestroyed(); + #ifdef ANDROID gfxIntSize GetScreenSize() { return mScreenSize; } #endif diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index f62bd0702590..13b198ec5882 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -163,6 +163,7 @@ MemoryReportRequestParent::~MemoryReportRequestParent() } nsTArray* ContentParent::gContentParents; +nsTArray* ContentParent::gPrivateContent; // The first content child has ID 1, so the chrome process can have ID 0. static PRUint64 gContentChildID = 1; @@ -211,6 +212,7 @@ ContentParent::Init() obs->AddObserver(this, "memory-pressure", false); obs->AddObserver(this, "child-gc-request", false); obs->AddObserver(this, "child-cc-request", false); + obs->AddObserver(this, "last-pb-context-exited", false); #ifdef ACCESSIBILITY obs->AddObserver(this, "a11y-init-or-shutdown", false); #endif @@ -308,6 +310,7 @@ ContentParent::ActorDestroy(ActorDestroyReason why) obs->RemoveObserver(static_cast(this), NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC); obs->RemoveObserver(static_cast(this), "child-gc-request"); obs->RemoveObserver(static_cast(this), "child-cc-request"); + obs->RemoveObserver(static_cast(this), "last-pb-context-exited"); #ifdef ACCESSIBILITY obs->RemoveObserver(static_cast(this), "a11y-init-or-shutdown"); #endif @@ -339,6 +342,14 @@ ContentParent::ActorDestroy(ActorDestroyReason why) } } + if (gPrivateContent) { + gPrivateContent->RemoveElement(this); + if (!gPrivateContent->Length()) { + delete gPrivateContent; + gPrivateContent = NULL; + } + } + mIsAlive = false; if (obs) { @@ -723,6 +734,9 @@ ContentParent::Observe(nsISupports* aSubject, else if (!strcmp(aTopic, "child-cc-request")){ SendCycleCollect(); } + else if (!strcmp(aTopic, "last-pb-context-exited")) { + unused << SendLastPrivateDocShellDestroyed(); + } #ifdef ACCESSIBILITY // Make sure accessibility is running in content process when accessibility // gets initiated in chrome process. @@ -1234,5 +1248,24 @@ ContentParent::RecvScriptError(const nsString& aMessage, return true; } +bool +ContentParent::RecvPrivateDocShellsExist(const bool& aExist) +{ + if (!gPrivateContent) + gPrivateContent = new nsTArray; + if (aExist) { + gPrivateContent->AppendElement(this); + } else { + gPrivateContent->RemoveElement(this); + if (!gPrivateContent->Length()) { + nsCOMPtr obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(nsnull, "last-pb-context-exited", nsnull); + delete gPrivateContent; + gPrivateContent = NULL; + } + } + return true; +} + } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 5c044fc22543..27c51234babb 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -110,6 +110,7 @@ protected: private: static nsTArray* gContentParents; + static nsTArray* gPrivateContent; // Hide the raw constructor methods since we don't want client code // using them. @@ -222,6 +223,8 @@ private: const PRUint32& aFlags, const nsCString& aCategory); + virtual bool RecvPrivateDocShellsExist(const bool& aExist); + GeckoChildProcessHost* mSubprocess; PRInt32 mGeolocationWatchID; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index f32a1bce7d6f..a16e810e04ab 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -149,6 +149,9 @@ child: SetID(PRUint64 id); + // Notify child that last-pb-context-exited notification was observed + LastPrivateDocShellDestroyed(); + parent: PAudio(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat); @@ -226,6 +229,9 @@ parent: sync GetShowPasswordSetting() returns (bool showPassword); + // Notify the parent of the presence or absence of private docshells + PrivateDocShellsExist(bool aExist); + both: AsyncMessage(nsString aMessage, nsString aJSON); diff --git a/js/src/json.h b/js/src/json.h index 64c36dbca0a5..c8c0ca0a6b14 100644 --- a/js/src/json.h +++ b/js/src/json.h @@ -53,6 +53,10 @@ extern JSBool js_Stringify(JSContext *cx, js::Value *vp, JSObject *replacer, js::Value space, js::StringBuffer &sb); +// Avoid build errors on certain platforms that define these names as constants +#undef STRICT +#undef LEGACY + /* * The type of JSON decoding to perform. Strict decoding is to-the-spec; * legacy decoding accepts a few non-JSON syntaxes historically accepted by the diff --git a/testing/xpcshell/xpcshell.ini b/testing/xpcshell/xpcshell.ini index 6bd71feb4776..a074d5c86a0d 100644 --- a/testing/xpcshell/xpcshell.ini +++ b/testing/xpcshell/xpcshell.ini @@ -13,6 +13,7 @@ [include:dom/indexedDB/test/unit/xpcshell.ini] [include:content/xtf/test/unit/xpcshell.ini] [include:docshell/test/unit/xpcshell.ini] +[include:docshell/test/unit_ipc/xpcshell.ini] [include:embedding/tests/unit/xpcshell.ini] [include:toolkit/components/commandlines/test/unit/xpcshell.ini] [include:toolkit/components/contentprefs/tests/unit/xpcshell.ini]