Bug 798508 - Part 1: Implement the "private" window.open feature; r=bzbarsky
authorEhsan Akhgari <ehsan@mozilla.com>
Sun, 07 Oct 2012 15:04:39 -0400
changeset 109882 099477f00862c3030f4bf850321d7c362e1a81fa
parent 109881 27e8924be2c168414c70174d6aa650586d96be91
child 109883 718453ec2c8ab29868b0c711896dcb3872197c57
push id1150
push userttaubert@mozilla.com
push dateSat, 13 Oct 2012 21:20:55 +0000
treeherderfx-team@5c37e5656453 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs798508
milestone19.0a1
Bug 798508 - Part 1: Implement the "private" window.open feature; r=bzbarsky This patch adds support for a "private" window flag which can be used to open new chrome windows in private browsing mode. It also tests to make sure that the flag is not accessible from content. A test in the next part makes sure that the flag is accessible from chrome.
embedding/browser/webBrowser/nsIWebBrowserChrome.idl
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
embedding/test/Makefile.in
embedding/test/test_private_window_from_content.html
testing/specialpowers/content/specialpowersAPI.js
--- a/embedding/browser/webBrowser/nsIWebBrowserChrome.idl
+++ b/embedding/browser/webBrowser/nsIWebBrowserChrome.idl
@@ -58,16 +58,19 @@ interface nsIWebBrowserChrome : nsISuppo
     // createBrowserWindow specific flags
     const unsigned long CHROME_WITH_SIZE              = 0x00001000;
     const unsigned long CHROME_WITH_POSITION          = 0x00002000;
 
     // special cases
     const unsigned long CHROME_WINDOW_MIN             = 0x00004000;
     const unsigned long CHROME_WINDOW_POPUP           = 0x00008000;
 
+    // private browsing windows
+    const unsigned long CHROME_PRIVATE_WINDOW         = 0x00010000;
+
     // Prevents new window animations on Mac OS X Lion.  Ignored on other
     // platforms.
     const unsigned long CHROME_MAC_SUPPRESS_ANIMATION = 0x01000000;
 
     const unsigned long CHROME_WINDOW_RAISED          = 0x02000000;
     const unsigned long CHROME_WINDOW_LOWERED         = 0x04000000;
     const unsigned long CHROME_CENTER_SCREEN          = 0x08000000;
 
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -893,27 +893,35 @@ nsWindowWatcher::OpenWindowInternal(nsID
     // this call already happened when the window was created, but
     // SetInitialPrincipalToSubject is safe to call multiple times.
     if (newWindow) {
       newWindow->SetInitialPrincipalToSubject();
     }
   }
 
   if (windowIsNew) {
-    // For top level windows, we want to ensure that the privacy status of the parent
-    // is propagated to the new child if it is available.
-    nsCOMPtr<nsIDocShellTreeItem> parentItem;
-    GetWindowTreeItem(aParent, getter_AddRefs(parentItem));
-    nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentItem);
+    // See if the caller has requested a private browsing window.
+    bool isPrivateBrowsingWindow =
+      !!(chromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW);
+    // Otherwise, propagate the privacy status of the parent window, if
+    // available, to the child.
+    if (!isPrivateBrowsingWindow) {
+      nsCOMPtr<nsIDocShellTreeItem> parentItem;
+      GetWindowTreeItem(aParent, getter_AddRefs(parentItem));
+      nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentItem);
+      if (parentContext) {
+        isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
+      }
+    }
 
     nsCOMPtr<nsIDocShellTreeItem> childRoot;
     newDocShellItem->GetRootTreeItem(getter_AddRefs(childRoot));
     nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(childRoot);
-    if (parentContext && childContext) {
-      childContext->SetUsePrivateBrowsing(parentContext->UsePrivateBrowsing());
+    if (childContext) {
+      childContext->SetUsePrivateBrowsing(isPrivateBrowsingWindow);
     }
   }
 
   if (uriToLoad && aNavigate) { // get the script principal and pass it to docshell
     JSContextAutoPopper contextGuard;
 
     cx = GetJSContextFromCallStack();
 
@@ -1485,16 +1493,22 @@ uint32_t nsWindowWatcher::CalculateChrom
   nsresult rv;
   if (securityManager) {
     rv = securityManager->SubjectPrincipalIsSystem(&isChrome);
     if (NS_FAILED(rv)) {
       isChrome = false;
     }
   }
 
+  // Determine whether the window is a private browsing window
+  if (isChrome) {
+    chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ?
+      nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0;
+  }
+
   nsCOMPtr<nsIPrefBranch> prefBranch;
   nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, true);
 
   rv = prefs->GetBranch("dom.disable_window_open_feature.", getter_AddRefs(prefBranch));
   NS_ENSURE_SUCCESS(rv, true);
 
   bool forceEnable = false;
--- a/embedding/test/Makefile.in
+++ b/embedding/test/Makefile.in
@@ -14,12 +14,13 @@ include $(DEPTH)/config/autoconf.mk
 MOCHITEST_FILES = \
 	test_bug293834.html \
 	bug293834_form.html \
 	320x240.ogv \
 	test_bug449141.html \
 	bug449141_page.html \
 	test_bug499115.html \
 	test_nsFind.html \
+	test_private_window_from_content.html \
 	test_window_open_units.html \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/embedding/test/test_private_window_from_content.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+  // Make sure that we cannot open private browsing windows from unprivileged content
+  var win = window.open("about:blank", "_blank", "private");
+  ok(!SpecialPowers.isWindowPrivate(win));
+  win.close();
+</script>
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -6,16 +6,17 @@
  */
 
 var Ci = Components.interfaces;
 var Cc = Components.classes;
 var Cu = Components.utils;
 
 Components.utils.import("resource://specialpowers/MockFilePicker.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 function SpecialPowersAPI() { 
   this._consoleListeners = [];
   this._encounteredCrashDumpFiles = [];
   this._unexpectedCrashDumpFiles = { };
   this._crashDumpDir = null;
   this._mfl = null;
   this._prefEnvUndoStack = [];
@@ -1237,9 +1238,13 @@ SpecialPowersAPI.prototype = {
     };
 
     this._sendSyncMessage('SPPermissionManager', msg);
   },
 
   getMozFullPath: function(file) {
     return file.mozFullPath;
   },
+
+  isWindowPrivate: function(win) {
+    return PrivateBrowsingUtils.isWindowPrivate(win);
+  },
 };