Bug 829383 - Ensure hidden private window docshells aren't counted towards private session lifetime. r=bz a=lsblakk
authorJosh Matthews <josh@joshmatthews.net>
Thu, 24 Jan 2013 00:12:52 +0000
changeset 127298 326cf46aa524f67ced7d005e3062d2bda4792795
parent 127297 2103dd3101ce4b60d7e4f87dc13651b73ed393c2
child 127299 dd357e872f398a62e3b73e9bc933c82d8d1dc9cf
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, lsblakk
bugs829383
milestone20.0a2
Bug 829383 - Ensure hidden private window docshells aren't counted towards private session lifetime. r=bz a=lsblakk
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
docshell/test/chrome/Makefile.in
docshell/test/chrome/test_private_hidden_window.html
xpfe/appshell/src/nsAppShellService.cpp
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -216,23 +216,16 @@ using namespace mozilla::dom;
 static int32_t gNumberOfDocumentsLoading = 0;
 
 // Global count of existing docshells.
 static int32_t gDocShellCount = 0;
 
 // Global count of docshells with the private attribute set
 static uint32_t gNumberOfPrivateDocShells = 0;
 
-// Global count of private docshells which will always remain open
-#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
-static uint32_t gNumberOfAlwaysOpenPrivateDocShells = 0; // the private hidden window
-#else
-static const uint32_t gNumberOfAlwaysOpenPrivateDocShells = 0;
-#endif
-
 // Global reference to the URI fixup service.
 nsIURIFixup *nsDocShell::sURIFixup = 0;
 
 // True means we validate window targets to prevent frameset
 // spoofing. Initialize this to a non-bolean value so we know to check
 // the pref on the creation of the first docshell.
 static uint32_t gValidateOrigin = 0xffffffff;
 
@@ -687,52 +680,34 @@ ConvertLoadTypeToNavigationType(uint32_t
   }
 
   return result;
 }
 
 static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
 
 static void
-AdjustAlwaysOpenPrivateDocShellCount()
-{
-#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
-    nsCOMPtr<nsIAppShellService> appShell
-      (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
-    bool hasHiddenPrivateWindow = false;
-    if (appShell) {
-      appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow);
-    }
-    gNumberOfAlwaysOpenPrivateDocShells = hasHiddenPrivateWindow ? 1 : 0;
-#endif
-}
-
-static void
 IncreasePrivateDocShellCount()
 {
-    AdjustAlwaysOpenPrivateDocShellCount();
-
     gNumberOfPrivateDocShells++;
-    if (gNumberOfPrivateDocShells > gNumberOfAlwaysOpenPrivateDocShells + 1 ||
+    if (gNumberOfPrivateDocShells > 1 ||
         XRE_GetProcessType() != GeckoProcessType_Content) {
         return;
     }
 
     mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
     cc->SendPrivateDocShellsExist(true);
 }
 
 static void
 DecreasePrivateDocShellCount()
 {
-    AdjustAlwaysOpenPrivateDocShellCount();
-
     MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
     gNumberOfPrivateDocShells--;
-    if (gNumberOfPrivateDocShells == gNumberOfAlwaysOpenPrivateDocShells)
+    if (!gNumberOfPrivateDocShells)
     {
         if (XRE_GetProcessType() == GeckoProcessType_Content) {
             mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
             cc->SendPrivateDocShellsExist(false);
             return;
         }
 
         nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
@@ -789,16 +764,17 @@ nsDocShell::nsDocShell():
     mURIResultedInDocument(false),
     mIsBeingDestroyed(false),
     mIsExecutingOnLoadHandler(false),
     mIsPrintingOrPP(false),
     mSavingOldViewer(false),
 #ifdef DEBUG
     mInEnsureScriptEnv(false),
 #endif
+    mAffectPrivateSessionLifetime(true),
     mFrameType(eFrameTypeRegular),
     mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID),
     mParentCharsetSource(0)
 {
     mHistoryID = ++gDocshellIDCounter;
     if (gDocShellCount++ == 0) {
         NS_ASSERTION(sURIFixup == nullptr,
                      "Huh, sURIFixup not null in first nsDocShell ctor!");
@@ -2063,20 +2039,22 @@ nsDocShell::SetUsePrivateBrowsing(bool a
 }
 
 NS_IMETHODIMP
 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
 {
     bool changed = aUsePrivateBrowsing != mInPrivateBrowsing;
     if (changed) {
         mInPrivateBrowsing = aUsePrivateBrowsing;
-        if (aUsePrivateBrowsing) {
-            IncreasePrivateDocShellCount();
-        } else {
-            DecreasePrivateDocShellCount();
+        if (mAffectPrivateSessionLifetime) {
+            if (aUsePrivateBrowsing) {
+                IncreasePrivateDocShellCount();
+            } else {
+                DecreasePrivateDocShellCount();
+            }
         }
     }
 
     int32_t count = mChildList.Count();
     for (int32_t i = 0; i < count; ++i) {
         nsCOMPtr<nsILoadContext> shell = do_QueryInterface(ChildAt(i));
         if (shell) {
             shell->SetPrivateBrowsing(aUsePrivateBrowsing);
@@ -2094,16 +2072,46 @@ nsDocShell::SetPrivateBrowsing(bool aUse
                 obs->PrivateModeChanged(aUsePrivateBrowsing);
             }
         }
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
+{
+    bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
+    if (change && mInPrivateBrowsing) {
+        if (aAffectLifetime) {
+            IncreasePrivateDocShellCount();
+        } else {
+            DecreasePrivateDocShellCount();
+        }
+    }
+    mAffectPrivateSessionLifetime = aAffectLifetime;
+
+    int32_t count = mChildList.Count();
+    for (int32_t i = 0; i < count; ++i) {
+        nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
+        if (shell) {
+            shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
+        }
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime)
+{
+    *aAffectLifetime = mAffectPrivateSessionLifetime;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::AddWeakPrivacyTransitionObserver(nsIPrivacyTransitionObserver* aObserver)
 {
     nsWeakPtr weakObs = do_GetWeakReference(aObserver);
     if (!weakObs) {
         return NS_ERROR_NOT_AVAILABLE;
     }
     return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
 }
@@ -2808,16 +2816,18 @@ nsDocShell::SetDocLoaderParent(nsDocLoad
         if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value)))
         {
             SetIsActive(value);
         }
         if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
             value = false;
         }
         SetAllowDNSPrefetch(value);
+        value = parentAsDocShell->GetAffectPrivateSessionLifetime();
+        SetAffectPrivateSessionLifetime(value);
     }
 
     nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
     if (parentAsLoadContext &&
         NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value)))
     {
         SetPrivateBrowsing(value);
 #ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
@@ -4952,17 +4962,19 @@ nsDocShell::Destroy()
     mSecurityUI = nullptr;
 
     // Cancel any timers that were set for this docshell; this is needed
     // to break the cycle between us and the timers.
     CancelRefreshURITimers();
 
     if (mInPrivateBrowsing) {
         mInPrivateBrowsing = false;
-        DecreasePrivateDocShellCount();
+        if (mAffectPrivateSessionLifetime) {
+            DecreasePrivateDocShellCount();
+        }
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
 {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -835,16 +835,17 @@ protected:
     // should be passed a SHEntry to save itself into.
     bool                       mSavingOldViewer;
     
     // @see nsIDocShellHistory::createdDynamically
     bool                       mDynamicallyCreated;
 #ifdef DEBUG
     bool                       mInEnsureScriptEnv;
 #endif
+    bool                       mAffectPrivateSessionLifetime;
     uint64_t                   mHistoryID;
 
     static nsIURIFixup *sURIFixup;
 
     nsRefPtr<nsDOMNavigationTiming> mTiming;
 
     // Are we a regular frame, a browser frame, or an app frame?
     FrameType mFrameType;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -34,17 +34,17 @@ interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 
-[scriptable, builtinclass, uuid(008f5c86-b915-458c-aaf1-78de3b484e68)]
+[scriptable, builtinclass, uuid(fc7e18cd-c671-4d78-a022-7de87555e15f)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -720,9 +720,11 @@ interface nsIDocShell : nsISupports
    * to propagate the value of the cross process parent's iframe's
    * "allowfullscreen" attribute to the child process. Setting
    * fullscreenAllowed on docshells which aren't content boundaries throws an
    * exception.
    */
   [infallible] readonly attribute boolean fullscreenAllowed;
   
   void setFullscreenAllowed(in boolean allowed);
+
+  [noscript, infallible] attribute boolean affectPrivateSessionLifetime;
 };
--- a/docshell/test/chrome/Makefile.in
+++ b/docshell/test/chrome/Makefile.in
@@ -89,16 +89,17 @@ MOCHITEST_CHROME_FILES =	\
 		test_bug690056.xul \
 		bug690056_window.xul \
 		test_bug311007.xul \
 		bug311007_window.xul \
 		test_principalInherit.xul \
 		test_mozFrameType.xul \
 		mozFrameType_window.xul \
 		test_bug789773.xul \
+		test_private_hidden_window.html \
     docshell_helpers.js \
     generic.html \
     $(NULL)
 
 ifneq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 MOCHITEST_CHROME_FILES += \
 		test_bug454235.xul \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/test_private_hidden_window.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=829383
+-->
+<head>
+  <title>Test for Bug 829383</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=829383">Mozilla Bug 829383</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <iframe name="target"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+const Ci = Components.interfaces;
+var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIWebNavigation)
+                       .QueryInterface(Ci.nsIDocShellTreeItem)
+                       .rootTreeItem
+                       .QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindow);
+
+// We need to wait for the hidden window to load, but can't access
+// an event target for a regular event listener.
+var hidden = mainWindow.Services.appShell.hiddenPrivateDOMWindow;
+var tmp = setTimeout(function poll() {
+  if (hidden.location.href != "resource://gre-resources/hiddenWindow.html" ||
+      !hidden.document.body) {
+    setTimeout(poll, 100);
+    return;
+  }
+
+  var iframe = hidden.document.createElement('iframe');
+  iframe.src = 'generic.html';
+  hidden.document.body.appendChild(iframe);
+
+  var win = mainWindow.OpenBrowserWindow({private: true});
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    win.close();
+    win = null;
+  }, false);
+}, 100);
+
+function observer(aSubject, aTopic, aData) {
+  is(aTopic, "last-pb-context-exited", "Unexpected observer topic");
+  mainWindow.Services.obs.removeObserver(observer, "last-pb-context-exited");
+  SimpleTest.finish();
+}
+mainWindow.Services.obs.addObserver(observer, "last-pb-context-exited", false);
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/xpfe/appshell/src/nsAppShellService.cpp
+++ b/xpfe/appshell/src/nsAppShellService.cpp
@@ -135,16 +135,22 @@ nsAppShellService::CreateHiddenWindowHel
     // Create the hidden private window
     chromeMask |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
 
     rv = JustCreateTopWindow(nullptr, url,
                              chromeMask, initialWidth, initialHeight,
                              true, getter_AddRefs(newWindow));
     NS_ENSURE_SUCCESS(rv, rv);
 
+    nsCOMPtr<nsIDocShell> docShell;
+    newWindow->GetDocShell(getter_AddRefs(docShell));
+    if (docShell) {
+      docShell->SetAffectPrivateSessionLifetime(false);
+    }
+
     mHiddenPrivateWindow.swap(newWindow);
   }
 #endif
 
   // RegisterTopLevelWindow(newWindow); -- Mac only
 
   return NS_OK;
 }