Bug 815847 - Part 4: Implement a hidden private window managed by the app shell service; r=bzbarsky
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 10 Dec 2012 23:49:17 -0500
changeset 126656 63383825274e2091b594f23b19e5d83b8ca18b07
parent 126655 434aeb3f910f5eeb41c117a801dedcadc4900652
child 126657 16c6a3cf8a773dee482d1189237122a892704d87
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)
reviewersbzbarsky
bugs815847
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 815847 - Part 4: Implement a hidden private window managed by the app shell service; r=bzbarsky
content/base/src/nsCCUncollectableMarker.cpp
toolkit/components/startup/nsAppStartup.cpp
xpfe/appshell/public/nsIAppShellService.idl
xpfe/appshell/src/Makefile.in
xpfe/appshell/src/nsAppShellService.cpp
xpfe/appshell/src/nsAppShellService.h
xpfe/appshell/src/test/Makefile.in
xpfe/appshell/src/test/test_hiddenPrivateWindow.xul
--- a/content/base/src/nsCCUncollectableMarker.cpp
+++ b/content/base/src/nsCCUncollectableMarker.cpp
@@ -362,16 +362,25 @@ nsCCUncollectableMarker::Observe(nsISupp
     nsCOMPtr<nsIXULWindow> hw;
     appShell->GetHiddenWindow(getter_AddRefs(hw));
     if (hw) {
       nsCOMPtr<nsIDocShell> shell;
       hw->GetDocShell(getter_AddRefs(shell));
       nsCOMPtr<nsIDocShellTreeNode> shellTreeNode = do_QueryInterface(shell);
       MarkDocShell(shellTreeNode, cleanupJS, prepareForCC);
     }
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    appShell->GetHiddenPrivateWindow(getter_AddRefs(hw));
+    if (hw) {
+      nsCOMPtr<nsIDocShell> shell;
+      hw->GetDocShell(getter_AddRefs(shell));
+      nsCOMPtr<nsIDocShellTreeNode> shellTreeNode = do_QueryInterface(shell);
+      MarkDocShell(shellTreeNode, cleanupJS, prepareForCC);
+    }
+#endif
   }
 
 #ifdef MOZ_XUL
   nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance();
   if (xulCache) {
     xulCache->MarkInCCGeneration(sGeneration);
   }
 #endif
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -314,32 +314,44 @@ nsAppStartup::Quit(uint32_t aMode)
 
   // If we're considering quitting, we will only do so if:
   if (ferocity == eConsiderQuit) {
     if (mConsiderQuitStopper == 0) {
       // there are no windows...
       ferocity = eAttemptQuit;
     }
 #ifdef XP_MACOSX
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    else if (mConsiderQuitStopper == 2) {
+#else
     else if (mConsiderQuitStopper == 1) {
+#endif
       // ... or there is only a hiddenWindow left, and it's useless:
       nsCOMPtr<nsIAppShellService> appShell
         (do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
 
       // Failure shouldn't be fatal, but will abort quit attempt:
       if (!appShell)
         return NS_OK;
 
       bool usefulHiddenWindow;
       appShell->GetApplicationProvidedHiddenWindow(&usefulHiddenWindow);
       nsCOMPtr<nsIXULWindow> hiddenWindow;
       appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+      nsCOMPtr<nsIXULWindow> hiddenPrivateWindow;
+      appShell->GetHiddenPrivateWindow(getter_AddRefs(hiddenPrivateWindow));
+      // If the remaining windows are useful, we won't quit:
+      if ((!hiddenWindow && !hiddenPrivateWindow) || usefulHiddenWindow)
+        return NS_OK;
+#else
       // If the one window is useful, we won't quit:
       if (!hiddenWindow || usefulHiddenWindow)
         return NS_OK;
+#endif
 
       ferocity = eAttemptQuit;
     }
 #endif
   }
 
   nsCOMPtr<nsIObserverService> obsService;
   if (ferocity == eAttemptQuit || ferocity == eForceQuit) {
--- a/xpfe/appshell/public/nsIAppShellService.idl
+++ b/xpfe/appshell/public/nsIAppShellService.idl
@@ -11,17 +11,17 @@ interface nsIDOMWindow;
 interface nsIAppShell;
 
 [ptr] native JSContext(JSContext);
 
 %{C++
 struct JSContext;
 %}
 
-[scriptable, uuid(EBCD34E2-2E43-45C0-AAC8-E6F1C692B371)]
+[scriptable, uuid(76e6364a-5453-47c7-ad83-8c30eff20a75)]
 interface nsIAppShellService : nsISupports
 {
   /**
    * Create a window, which will be initially invisible.
    * @param aParent the parent window.  Can be null.
    * @param aUrl the contents of the new window.
    * @param aChromeMask chrome flags affecting the kind of OS border
    *                    given to the window. see nsIBrowserWindow for
@@ -60,16 +60,34 @@ interface nsIAppShellService : nsISuppor
    * Return the (singleton) application hidden window, automatically created
    * and maintained by this AppShellService.
    * @param aResult the hidden window.  Do not unhide hidden window.
    *                Do not taunt hidden window.
    */
   readonly attribute nsIDOMWindow hiddenDOMWindow;
 
   /**
+   * Return the (singleton) application hidden private window, automatically
+   * created and maintained by this AppShellService.  This window is created
+   * in private browsing mode.
+   * @param aResult the hidden private window.  Do not unhide hidden window.
+   *                Do not taunt hidden window.
+   */
+  readonly attribute nsIXULWindow hiddenPrivateWindow;
+
+  /**
+   * Return the (singleton) application hidden private window, automatically
+   * created and maintained by this AppShellService.  This window is created
+   * in private browsing mode.
+   * @param aResult the hidden private window.  Do not unhide hidden window.
+   *                Do not taunt hidden window.
+   */
+  readonly attribute nsIDOMWindow hiddenPrivateDOMWindow;
+
+  /**
    * Return the (singleton) application hidden window as an nsIDOMWindow,
    * and, the corresponding JavaScript context pointer.  This is useful
    * if you'd like to subsequently call OpenDialog on the hidden window.
    * @aHiddenDOMWindow the hidden window QI'd to type nsIDOMWindow
    * @aJSContext       the corresponding JavaScript context
    */
   [noscript]
   void getHiddenWindowAndJSContext(out nsIDOMWindow aHiddenDOMWindow,
--- a/xpfe/appshell/src/Makefile.in
+++ b/xpfe/appshell/src/Makefile.in
@@ -26,10 +26,12 @@ CPPSRCS		= \
 		nsXULWindow.cpp \
 		nsAppShellService.cpp \
 		nsAppShellWindowEnumerator.cpp \
 		nsWebShellWindow.cpp \
 		nsWindowMediator.cpp \
 		nsAppShellFactory.cpp \
 		$(NULL)
 
+PARALLEL_DIRS := test
+
 include $(topsrcdir)/config/rules.mk
 
--- a/xpfe/appshell/src/nsAppShellService.cpp
+++ b/xpfe/appshell/src/nsAppShellService.cpp
@@ -104,30 +104,50 @@ nsAppShellService::CreateHiddenWindow()
   nsRefPtr<nsWebShellWindow> newWindow;
   rv = JustCreateTopWindow(nullptr, url,
                            chromeMask, initialWidth, initialHeight,
                            true, getter_AddRefs(newWindow));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mHiddenWindow.swap(newWindow);
 
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  // 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);
+
+  mHiddenPrivateWindow.swap(newWindow);
+#endif
+
   // RegisterTopLevelWindow(newWindow); -- Mac only
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAppShellService::DestroyHiddenWindow()
 {
   if (mHiddenWindow) {
     mHiddenWindow->Destroy();
 
     mHiddenWindow = nullptr;
   }
 
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  if (mHiddenPrivateWindow) {
+    mHiddenPrivateWindow->Destroy();
+
+    mHiddenPrivateWindow = nullptr;
+  }
+#endif
+
   return NS_OK;
 }
 
 /*
  * Create a new top level window and display the given URL within it...
  */
 NS_IMETHODIMP
 nsAppShellService::CreateTopLevelWindow(nsIXULWindow *aParent,
@@ -352,16 +372,22 @@ nsAppShellService::JustCreateTopWindow(n
                                    aUrl, aInitialWidth, aInitialHeight,
                                    aIsHiddenWindow, widgetInitData);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Enforce the Private Browsing autoStart pref first.
   bool isPrivateBrowsingWindow =
     Preferences::GetBool("browser.privatebrowsing.autostart");
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) {
+    // Caller requested a private window
+    isPrivateBrowsingWindow = true;
+  }
+#endif
   if (!isPrivateBrowsingWindow) {
     // Ensure that we propagate any existing private browsing status
     // from the parent, even if it will not actually be used
     // as a parent value.
     nsCOMPtr<nsIDOMWindow> domWin = do_GetInterface(aParent);
     nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(domWin);
     nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(webNav);
     if (parentContext) {
@@ -410,16 +436,52 @@ nsAppShellService::GetHiddenDOMWindow(ns
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aWindow = hiddenDOMWindow;
   NS_IF_ADDREF(*aWindow);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsAppShellService::GetHiddenPrivateWindow(nsIXULWindow **aWindow)
+{
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  NS_ENSURE_ARG_POINTER(aWindow);
+
+  *aWindow = mHiddenPrivateWindow;
+  NS_IF_ADDREF(*aWindow);
+  return *aWindow ? NS_OK : NS_ERROR_FAILURE;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetHiddenPrivateDOMWindow(nsIDOMWindow **aWindow)
+{
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  nsresult rv;
+  nsCOMPtr<nsIDocShell> docShell;
+  NS_ENSURE_TRUE(mHiddenPrivateWindow, NS_ERROR_FAILURE);
+
+  rv = mHiddenPrivateWindow->GetDocShell(getter_AddRefs(docShell));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMWindow> hiddenPrivateDOMWindow(do_GetInterface(docShell, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aWindow = hiddenPrivateDOMWindow;
+  NS_IF_ADDREF(*aWindow);
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
 nsAppShellService::GetHiddenWindowAndJSContext(nsIDOMWindow **aWindow,
                                                JSContext    **aJSContext)
 {
     nsresult rv = NS_OK;
     if ( aWindow && aJSContext ) {
         *aWindow    = nullptr;
         *aJSContext = nullptr;
 
@@ -526,16 +588,22 @@ nsAppShellService::UnregisterTopLevelWin
   }
 
   NS_ENSURE_ARG_POINTER(aWindow);
 
   if (aWindow == mHiddenWindow) {
     // CreateHiddenWindow() does not register the window, so we're done.
     return NS_OK;
   }
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  if (aWindow == mHiddenPrivateWindow) {
+    // CreateHiddenWindow() does not register the window, so we're done.
+    return NS_OK;
+  }
+#endif
 
   // tell the window mediator
   nsCOMPtr<nsIWindowMediator> mediator
     ( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID) );
   NS_ASSERTION(mediator, "Couldn't get window mediator. Doing xpcom shutdown?");
 
   if (mediator)
     mediator->UnregisterWindow(aWindow);
@@ -563,14 +631,19 @@ nsAppShellService::Observe(nsISupports* 
 {
   if (!strcmp(aTopic, "xpcom-will-shutdown")) {
     mXPCOMWillShutDown = true;
   } else if (!strcmp(aTopic, "xpcom-shutdown")) {
     mXPCOMShuttingDown = true;
     if (mHiddenWindow) {
       mHiddenWindow->Destroy();
     }
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    if (mHiddenPrivateWindow) {
+      mHiddenPrivateWindow->Destroy();
+    }
+#endif
   } else {
     NS_ERROR("Unexpected observer topic!");
   }
 
   return NS_OK;
 }
--- a/xpfe/appshell/src/nsAppShellService.h
+++ b/xpfe/appshell/src/nsAppShellService.h
@@ -36,15 +36,18 @@ protected:
                                nsIURI *aUrl, 
                                uint32_t aChromeMask,
                                int32_t aInitialWidth, int32_t aInitialHeight,
                                bool aIsHiddenWindow,
                                nsWebShellWindow **aResult);
   uint32_t CalculateWindowZLevel(nsIXULWindow *aParent, uint32_t aChromeMask);
 
   nsRefPtr<nsWebShellWindow>  mHiddenWindow;
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+  nsRefPtr<nsWebShellWindow>  mHiddenPrivateWindow;
+#endif
   bool                        mXPCOMWillShutDown;
   bool                        mXPCOMShuttingDown;
   uint16_t                    mModalWindowCount;
   bool                        mApplicationProvidedHiddenWindow;
 };
 
 #endif
new file mode 100644
--- /dev/null
+++ b/xpfe/appshell/src/test/Makefile.in
@@ -0,0 +1,19 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH          := @DEPTH@
+topsrcdir      := @top_srcdir@
+srcdir         := @srcdir@
+VPATH          := @srcdir@
+relativesrcdir := @relativesrcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+MOCHITEST_CHROME_FILES := \
+  test_hiddenPrivateWindow.xul \
+  $(NULL)
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/xpfe/appshell/src/test/test_hiddenPrivateWindow.xul
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=815847
+-->
+<window title="Mozilla Bug 815847"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <title>Test for Bug 815847</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body  xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=815847">Mozilla Bug 815847</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+const Cu = Components.utils;
+
+Cu.import('resource://gre/modules/Services.jsm');
+Cu.import('resource://gre/modules/PrivateBrowsingUtils.jsm');
+
+ok(Services.appShell.hiddenWindow, "hiddenWindow should exist");
+ok(Services.appShell.hiddenDOMWindow, "hiddenDOMWindow should exist");
+ok(Services.appShell.hiddenPrivateWindow, "hiddenPrivateWindow should exist");
+ok(Services.appShell.hiddenPrivateDOMWindow, "hiddenPrivateDOMWindow should exist");
+
+ok(!PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenWindow.docShell), "hiddenWindow should not be private");
+ok(!PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenDOMWindow), "hiddenDOMWindow should not be private");
+ok(PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenPrivateWindow.docShell), "hiddenPrivateWindow should be private");
+ok(PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenPrivateDOMWindow), "hiddenPrivateDOMWindow should be private");
+
+]]>
+</script>
+
+</window>