Bug 675574 - Do not allow more than one call to window.open() when we allow popups. r=jst
☠☠ backed out by 3bb95f690d75 ☠ ☠
authorMounir Lamouri <mounir.lamouri@gmail.com>
Wed, 28 Sep 2011 23:23:36 +0200
changeset 77774 2780356be1a19df79053b7e12e96ce2a2442e10d
parent 77773 d8797563b04d4ee210d777437220b2f54dc0703f
child 77775 0c9d0f4f1c82482389ce8278eaac66f0b239c5bf
push id2245
push usermlamouri@mozilla.com
push dateWed, 28 Sep 2011 21:36:20 +0000
treeherdermozilla-inbound@2780356be1a1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst
bugs675574
milestone10.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 675574 - Do not allow more than one call to window.open() when we allow popups. r=jst
docshell/test/navigation/test_bug13871.html
docshell/test/navigation/test_bug270414.html
docshell/test/navigation/test_not-opener.html
docshell/test/navigation/test_popup-navigates-children.html
docshell/test/navigation/test_sibling-off-domain.html
docshell/test/test_bug598895.html
docshell/test/test_bug637644.html
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
dom/tests/browser/Makefile.in
dom/tests/browser/browser_popup_blocker_multiple_popups.js
dom/tests/mochitest/bugs/test_bug346659.html
dom/tests/mochitest/bugs/test_bug406375.html
dom/tests/mochitest/bugs/test_bug61098.html
dom/tests/mochitest/chrome/test_popup_blocker_chrome.xul
modules/libpref/src/init/all.js
security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
--- a/docshell/test/navigation/test_bug13871.html
+++ b/docshell/test/navigation/test_bug13871.html
@@ -22,20 +22,24 @@ window.onload = function () {
     isInaccessible(window3.frames[0], "Should not be able to navigate off-domain frame by targeted hyperlink.");
 
     window0.close();
     window1.close();
     window2.close();
     window3.close();
 
     xpcCleanupWindows();
+    SpecialPowers.setBoolPref("dom.block_multiple_popups", gBlockMultiplePopups);
     SimpleTest.finish();
   }, 4);
 }
 
+var gBlockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
 var window0 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window0", "width=10,height=10");
 var window1 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window1", "width=10,height=10");
 var window2 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window2", "width=10,height=10");
 var window3 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window3", "width=10,height=10");
 </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=13871">Mozilla Bug 13871</a>
--- a/docshell/test/navigation/test_bug270414.html
+++ b/docshell/test/navigation/test_bug270414.html
@@ -4,16 +4,19 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 50px; }
     </style>
 <script>
+var gBlockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
 var headerHTML = "<html><head>" +
                  "<script src='/tests/SimpleTest/EventUtils.js'></scr" + "ipt>" + 
                  "<script src='NavigationUtils.js'></scr" + "ipt>" +
                  "</head><body>";
 var footerHTML = "</body></html>";
 
 function testChild0() {
   if (!window.window0) {
@@ -66,16 +69,17 @@ xpcWaitForFinishedFrames(function() {
   isNavigated(frames[3], "Should be able to navigate on-domain opener's children by targeted hyperlink.");
 
   window0.close();
   window1.close();
   window2.close();
   window3.close();
 
   xpcCleanupWindows();
+  SpecialPowers.setBoolPref("dom.block_multiple_popups", gBlockMultiplePopups);
   SimpleTest.finish();
 }, 4);
 
 </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=270414">Mozilla Bug 270414</a>
 <div id="frames">
--- a/docshell/test/navigation/test_not-opener.html
+++ b/docshell/test/navigation/test_not-opener.html
@@ -21,20 +21,24 @@ window.onload = function () {
     is(xpcGetFramesByName("window3").length, 2, "Should not be able to navigate popup's popup by targeted hyperlink.");
 
     //opener0.close();
     opener1.close();
     opener2.close();
     opener3.close();
 
     xpcCleanupWindows();
+    SpecialPowers.setBoolPref("dom.block_multiple_popups", gBlockMultiplePopups);
     SimpleTest.finish();
   }, 6);
 }
 
+var gBlockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
 //opener0 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window0", "_blank", "width=10,height=10");
 opener1 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window1", "_blank", "width=10,height=10");
 opener2 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window2", "_blank", "width=10,height=10");
 opener3 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window3", "_blank", "width=10,height=10");
 </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
--- a/docshell/test/navigation/test_popup-navigates-children.html
+++ b/docshell/test/navigation/test_popup-navigates-children.html
@@ -4,16 +4,19 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 50px; }
     </style>
 <script>
+var gBlockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
 function testChild0() {
   if (!window.window0)
     window0 = window.open("navigate.html#opener.frames[0],location", "window0", "width=10,height=10");
 }
 
 function testChild1() {
   if (!window.window1)
     window1 = window.open("navigate.html#child1,open", "window1", "width=10,height=10");
@@ -36,16 +39,17 @@ xpcWaitForFinishedFrames(function() {
   isNavigated(frames[3], "Should be able to navigate on-domain opener's children by targeted hyperlink.");
 
   window0.close();
   window1.close();
   window2.close();
   window3.close();
 
   xpcCleanupWindows();
+  SpecialPowers.setBoolPref("dom.block_multiple_popups", gBlockMultiplePopups);
   SimpleTest.finish();
 }, 4);
 
 </script>
 </head>
 <body>
 <div id="frames">
 <iframe onload="testChild0()" name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
--- a/docshell/test/navigation/test_sibling-off-domain.html
+++ b/docshell/test/navigation/test_sibling-off-domain.html
@@ -4,30 +4,34 @@
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     <script type="text/javascript" src="NavigationUtils.js"></script>        
     <style type="text/css">
       iframe { width: 90%; height: 50px; }
     </style>
 <script>
+var gBlockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
 window.onload = function () {
   document.getElementById('active').innerHTML =
       '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#parent.frames[0],location"></iframe>' +
       '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child1,open"></iframe>' +
       '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child2,form"></iframe>' +
       '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child3,hyperlink"></iframe>';
 
   xpcWaitForFinishedFrames(function() {
     isBlank(frames[0], "Should not be able to navigate off-domain sibling by setting location.");
     isBlank(frames[1], "Should not be able to navigate off-domain sibling by calling window.open.");
     isBlank(frames[2], "Should not be able to navigate off-domain sibling by submitting form.");
     isBlank(frames[3], "Should not be able to navigate off-domain sibling by targeted hyperlink.");
 
     xpcCleanupWindows();
+    SpecialPowers.setBoolPref("dom.block_multiple_popups", gBlockMultiplePopups);
     SimpleTest.finish();
   }, 4);
 }
 </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
 <div id="frames">
--- a/docshell/test/test_bug598895.html
+++ b/docshell/test/test_bug598895.html
@@ -34,20 +34,24 @@ window.onmessage = function (ev) {
     var two = snapshotWindow(win2);
     var three = snapshotWindow(win3);
     win1.close();
     win2.close();
     win3.close();
     ok(compareSnapshots(one, two, true)[0], "Popups should look identical");
     ok(compareSnapshots(one, three, false)[0], "Popups should not look identical");
 
+    SpecialPowers.setBoolPref("dom.block_multiple_popups", gBlockMultiplePopups);
     SimpleTest.finish();
   }
 }
 
+var gBlockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
 var win2 = window.open("data:text/html,<script>window.onload = function() { opener.postMessage('loaded', '*'); }</" + "script><body>Should show</body>");
 
 var win3 = window.open("data:text/html,<script>window.onload = function() { opener.postMessage('loaded', '*'); }</" + "script><body></body>");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/docshell/test/test_bug637644.html
+++ b/docshell/test/test_bug637644.html
@@ -34,20 +34,24 @@ window.onmessage = function (ev) {
     var two = snapshotWindow(win2);
     var three = snapshotWindow(win3);
     win1.close();
     win2.close();
     win3.close();
     ok(compareSnapshots(one, two, true)[0], "Popups should look identical");
     ok(compareSnapshots(one, three, false)[0], "Popups should not look identical");
 
+    SpecialPowers.setBoolPref("dom.block_multiple_popups", gBlockMultiplePopups);
     SimpleTest.finish();
   }
 }
 
+var gBlockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
 var win2 = window.open("data:text/html,<script>window.onload = function() { opener.postMessage('loaded', '*'); }</" + "script><body>Should show</body>", "", "height=500,width=500");
 
 var win3 = window.open("data:text/html,<script>window.onload = function() { opener.postMessage('loaded', '*'); }</" + "script><body></body>", "", "height=500,width=500");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -263,16 +263,17 @@ using mozilla::TimeDuration;
 nsIDOMStorageList *nsGlobalWindow::sGlobalStorageList  = nsnull;
 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nsnull;
 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
 
 static nsIEntropyCollector *gEntropyCollector          = nsnull;
 static PRInt32              gRefCnt                    = 0;
 static PRInt32              gOpenPopupSpamCount        = 0;
 static PopupControlState    gPopupControlState         = openAbused;
+static PopupOpenedState     gPopupOpenedState          = noTrack;
 static PRInt32              gRunningTimeoutDepth       = 0;
 static PRPackedBool         gMouseDown                 = PR_FALSE;
 static PRPackedBool         gDragServiceDisabled       = PR_FALSE;
 static FILE                *gDumpFile                  = nsnull;
 static PRUint64             gNextWindowID              = 0;
 static PRUint32             gSerialCounter             = 0;
 static PRBool               gDoNotTrackEnabled         = PR_FALSE;
 
@@ -1708,16 +1709,40 @@ nsGlobalWindow::PopPopupControlState(Pop
 }
 
 PopupControlState
 nsGlobalWindow::GetPopupControlState() const
 {
   return gPopupControlState;
 }
 
+PopupOpenedState
+GetPopupOpenedState()
+{
+  return gPopupOpenedState;
+}
+
+PopupOpenedState
+nsGlobalWindow::GetPopupOpenedState() const
+{
+  return ::GetPopupOpenedState();
+}
+
+void
+SetPopupOpenedState(PopupOpenedState aValue)
+{
+  gPopupOpenedState = aValue;
+}
+
+void
+nsGlobalWindow::SetPopupOpenedState(PopupOpenedState aValue) const
+{
+  ::SetPopupOpenedState(aValue);
+}
+
 #define WINDOWSTATEHOLDER_IID \
 {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}}
 
 class WindowStateHolder : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
   NS_DECL_ISUPPORTS
@@ -5709,16 +5734,31 @@ nsGlobalWindow::RevisePopupAbuseLevel(Po
 
   // limit the number of simultaneously open popups
   if (abuse == openAbused || abuse == openControlled) {
     PRInt32 popupMax = Preferences::GetInt("dom.popup_maximum", -1);
     if (popupMax >= 0 && gOpenPopupSpamCount >= popupMax)
       abuse = openOverridden;
   }
 
+  if (Preferences::GetBool("dom.block_multiple_popups", PR_TRUE)) {
+    // Do not allow opening more than one popup per event.
+    switch (GetPopupOpenedState()) {
+      case noOpenedPopup:
+        SetPopupOpenedState(openedPopup);
+        break;
+      case openedPopup:
+        abuse = openOverridden;
+        break;
+      case noTrack:
+      default:
+        break;
+    }
+  }
+
   return abuse;
 }
 
 /* If a window open is blocked, fire the appropriate DOM events.
    aBlocked signifies we just blocked a popup.
    aWindow signifies we just opened what is probably a popup.
 */
 void
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -348,16 +348,18 @@ public:
   virtual NS_HIDDEN_(void) SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler);
 
   virtual NS_HIDDEN_(void) SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal);
   virtual NS_HIDDEN_(nsIPrincipal*) GetOpenerScriptPrincipal();
 
   virtual NS_HIDDEN_(PopupControlState) PushPopupControlState(PopupControlState state, PRBool aForce) const;
   virtual NS_HIDDEN_(void) PopPopupControlState(PopupControlState state) const;
   virtual NS_HIDDEN_(PopupControlState) GetPopupControlState() const;
+  virtual NS_HIDDEN_(PopupOpenedState) GetPopupOpenedState() const;
+  virtual NS_HIDDEN_(void) SetPopupOpenedState(PopupOpenedState aValue) const;
 
   virtual NS_HIDDEN_(nsresult) SaveWindowState(nsISupports **aState);
   virtual NS_HIDDEN_(nsresult) RestoreWindowState(nsISupports *aState);
   virtual NS_HIDDEN_(void) SuspendTimeouts(PRUint32 aIncrease = 1,
                                            PRBool aFreezeChildren = PR_TRUE);
   virtual NS_HIDDEN_(nsresult) ResumeTimeouts(PRBool aThawChildren = PR_TRUE);
   virtual NS_HIDDEN_(PRUint32) TimeoutSuspendCount();
   virtual NS_HIDDEN_(nsresult) FireDelayedDOMEvents();
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -64,16 +64,24 @@ class nsIPrincipal;
 // nsGlobalWindow::PushPopupControlState()).
 enum PopupControlState {
   openAllowed = 0,  // open that window without worries
   openControlled,   // it's a popup, but allow it
   openAbused,       // it's a popup. disallow it, but allow domain override.
   openOverridden    // disallow window open
 };
 
+// PopupOpenedState helps track abuse of opened popups while the privilege has
+// been given.
+enum PopupOpenedState {
+  noTrack = -1,      // Don't track opened popups.
+  noOpenedPopup = 0, // Tracking opened popups but none opened yet.
+  openedPopup = 1    // Tracking opened popups and one has been opened.
+};
+
 class nsIDocShell;
 class nsIContent;
 class nsIDocument;
 class nsIScriptTimeoutHandler;
 struct nsTimeout;
 class nsScriptObjectHolder;
 class nsXBLPrototypeHandler;
 class nsIArray;
@@ -291,16 +299,18 @@ public:
   virtual void SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal) = 0;
   // Ask this window who opened it.
   virtual nsIPrincipal* GetOpenerScriptPrincipal() = 0;
 
   virtual PopupControlState PushPopupControlState(PopupControlState aState,
                                                   PRBool aForce) const = 0;
   virtual void PopPopupControlState(PopupControlState state) const = 0;
   virtual PopupControlState GetPopupControlState() const = 0;
+  virtual void SetPopupOpenedState(PopupOpenedState aValue) const = 0;
+  virtual PopupOpenedState GetPopupOpenedState() const = 0;
 
   // Returns an object containing the window's state.  This also suspends
   // all running timeouts in the window.
   virtual nsresult SaveWindowState(nsISupports **aState) = 0;
 
   // Restore the window state from aState.
   virtual nsresult RestoreWindowState(nsISupports *aState) = 0;
 
@@ -682,16 +692,22 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWin
 
 #ifdef _IMPL_NS_LAYOUT
 PopupControlState
 PushPopupControlState(PopupControlState aState, PRBool aForce);
 
 void
 PopPopupControlState(PopupControlState aState);
 
+PopupOpenedState
+GetPopupOpenedState();
+
+void
+SetPopupOpenedState(PopupOpenedState aState);
+
 #define NS_AUTO_POPUP_STATE_PUSHER nsAutoPopupStatePusherInternal
 #else
 #define NS_AUTO_POPUP_STATE_PUSHER nsAutoPopupStatePusherExternal
 #endif
 
 // Helper class that helps with pushing and popping popup control
 // state. Note that this class looks different from within code that's
 // part of the layout library than it does in code outside the layout
@@ -699,45 +715,56 @@ PopPopupControlState(PopupControlState a
 // don't conflict, but code should always use the name
 // |nsAutoPopupStatePusher|.
 class NS_AUTO_POPUP_STATE_PUSHER
 {
 public:
 #ifdef _IMPL_NS_LAYOUT
   NS_AUTO_POPUP_STATE_PUSHER(PopupControlState aState, PRBool aForce = PR_FALSE)
     : mOldState(::PushPopupControlState(aState, aForce))
+    , mPreviousOpenState(::GetPopupOpenedState())
   {
+    SetPopupOpenedState(mPreviousOpenState == openedPopup ? openedPopup
+                                                          : noOpenedPopup);
   }
 
   ~NS_AUTO_POPUP_STATE_PUSHER()
   {
     PopPopupControlState(mOldState);
+    SetPopupOpenedState(mPreviousOpenState);
   }
 #else
   NS_AUTO_POPUP_STATE_PUSHER(nsPIDOMWindow *aWindow, PopupControlState aState)
-    : mWindow(aWindow), mOldState(openAbused)
+    : mWindow(aWindow)
+    , mOldState(openAbused)
+    , mPreviousOpenState(noTrack)
   {
     if (aWindow) {
       mOldState = aWindow->PushPopupControlState(aState, PR_FALSE);
+      mPreviousOpenState = aWindow->GetPopupOpenedState();
+      aWindow->SetPopupOpenedState(mPreviousOpenState == openedPopup ? openedPopup
+                                                                     : noOpenedPopup);
     }
   }
 
   ~NS_AUTO_POPUP_STATE_PUSHER()
   {
     if (mWindow) {
       mWindow->PopPopupControlState(mOldState);
+      mWindow->SetPopupOpenedState(mPreviousOpenState);
     }
   }
 #endif
 
 protected:
 #ifndef _IMPL_NS_LAYOUT
   nsCOMPtr<nsPIDOMWindow> mWindow;
 #endif
   PopupControlState mOldState;
+  PopupOpenedState mPreviousOpenState;
 
 private:
   // Hide so that this class can only be stack-allocated
   static void* operator new(size_t /*size*/) CPP_THROW_NEW { return nsnull; }
   static void operator delete(void* /*memory*/) {}
 };
 
 #define nsAutoPopupStatePusher NS_AUTO_POPUP_STATE_PUSHER
--- a/dom/tests/browser/Makefile.in
+++ b/dom/tests/browser/Makefile.in
@@ -50,12 +50,13 @@ include $(topsrcdir)/config/rules.mk
 		browser_autofocus_background.js \
 		browser_ConsoleAPITests.js \
 		test-console-api.html \
 		browser_ConsoleStorageAPITests.js \
 		browser_ConsoleStoragePBTest.js \
 		browser_autofocus_preference.js \
 		browser_popup_blocker_save_open_panel.js \
 		browser_bug396843.js \
+		browser_popup_blocker_multiple_popups.js \
 		$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_popup_blocker_multiple_popups.js
@@ -0,0 +1,300 @@
+/**
+ * In this test, we check that the content can't open more than one popup at a
+ * time (depending on "dom.allow_mulitple_popups" preference value).
+ */
+
+let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
+           .getService(Ci.nsIWindowMediator);
+let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
+           .getService(Ci.nsIWindowWatcher);
+let prefs = Cc["@mozilla.org/preferences-service;1"]
+              .getService(Components.interfaces.nsIPrefBranch);
+let gMultiplePopupsPref;
+
+let gCurrentTest = -1;
+let gTests = [
+  test1,
+  test2,
+  test3,
+  test4,
+  test5,
+  test6,
+];
+
+function cleanUpPopups()
+{
+  let windowEnumerator = wm.getEnumerator(null);
+
+  while (windowEnumerator.hasMoreElements()) {
+    let win = windowEnumerator.getNext();
+
+    // Close all windows except ourself and the browser test harness window.
+    if (win != window && !win.closed &&
+        win.document.documentElement.getAttribute("id") != "browserTestHarness") {
+      win.close();
+    }
+  }
+}
+
+function cleanUp()
+{
+  prefs.setBoolPref("dom.block_multiple_popups", gMultiplePopupsPref);
+  cleanUpPopups(window);
+}
+
+function nextOrFinish()
+{
+  gCurrentTest++;
+
+  if (gCurrentTest >= gTests.length) {
+    finish();
+    return;
+  }
+
+  gTests[gCurrentTest]();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gMultiplePopupsPref = prefs.getBoolPref("dom.block_multiple_popups");
+
+  nextOrFinish();
+}
+
+/**
+ * Two window.open();
+ */
+function test1()
+{
+  prefs.setBoolPref("dom.block_multiple_popups", true);
+
+  gBrowser.selectedTab.linkedBrowser.addEventListener("load", function () {
+    gBrowser.selectedTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gBrowser.addEventListener("DOMPopupBlocked", function() {
+      gBrowser.removeEventListener("DOMPopupBlocked", arguments.callee, true);
+
+      ok(true, "The popup has been blocked");
+
+      cleanUp();
+      nextOrFinish();
+    }, true);
+
+    waitForFocus(function() {
+      var button = gBrowser.selectedTab.linkedBrowser.contentDocument.getElementsByTagName('button')[0];
+      EventUtils.synthesizeMouseAtCenter(button, {}, gBrowser.selectedTab.linkedBrowser.contentWindow);
+    });
+  }, true);
+
+  gBrowser.selectedTab.linkedBrowser.loadURI("data:text/html,<!DOCTYPE html><html><body><script>function openPopups() { window.open('data:text/html,foo', '', 'foo'); window.open('data:text/html,bar', '', 'foo'); }</script><button onclick='openPopups();'>click</button></body></html>");
+}
+
+/**
+ * window.open followed by w.open with w being the first popup.
+ */
+function test2()
+{
+  prefs.setBoolPref("dom.block_multiple_popups", true);
+
+  let gPopupsCount = 0;
+
+  gBrowser.selectedTab.linkedBrowser.addEventListener("load", function () {
+    gBrowser.selectedTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    ww.registerNotification(function(aSubject, aTopic, aData) {
+      if (aTopic != "domwindowopened") {
+        return;
+      }
+
+      gPopupsCount++;
+
+      if (gPopupsCount > 1) {
+        return;
+      }
+
+      executeSoon(function() {
+      executeSoon(function() {
+      executeSoon(function() {
+      executeSoon(function() {
+        ww.unregisterNotification(arguments.callee);
+
+        is(gPopupsCount, 1, "Only one popup appeared");
+        cleanUp();
+        nextOrFinish();
+      });
+      });
+      });
+      });
+    });
+
+    waitForFocus(function() {
+      var button = gBrowser.selectedTab.linkedBrowser.contentDocument.getElementsByTagName('button')[0];
+      EventUtils.synthesizeMouseAtCenter(button, {}, gBrowser.selectedTab.linkedBrowser.contentWindow);
+    });
+  }, true);
+
+  gBrowser.selectedTab.linkedBrowser.loadURI("data:text/html,<!DOCTYPE html><html><body><script>function openPopups() { var w = window.open('data:text/html,foo', '', 'foo'); w.open('data:text/html,bar', '', 'foo'); }</script><button onclick='openPopups();'>click</button></body></html>");
+}
+
+/**
+ * window.open followed by w.open with w being the first popup and the second popup being actually a tab.
+ */
+function test3()
+{
+  prefs.setBoolPref("dom.block_multiple_popups", true);
+
+  let gPopupsCount = 0;
+
+  gBrowser.selectedTab.linkedBrowser.addEventListener("load", function () {
+    gBrowser.selectedTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    ww.registerNotification(function(aSubject, aTopic, aData) {
+      if (aTopic != "domwindowopened") {
+        return;
+      }
+
+      gPopupsCount++;
+
+      if (gPopupsCount > 1) {
+        return;
+      }
+
+      executeSoon(function() {
+      executeSoon(function() {
+      executeSoon(function() {
+      executeSoon(function() {
+        ww.unregisterNotification(arguments.callee);
+
+        is(gPopupsCount, 1, "Only one popup appeared");
+        cleanUp();
+        nextOrFinish();
+      });
+      });
+      });
+      });
+    });
+
+    waitForFocus(function() {
+      var button = gBrowser.selectedTab.linkedBrowser.contentDocument.getElementsByTagName('button')[0];
+      EventUtils.synthesizeMouseAtCenter(button, {}, gBrowser.selectedTab.linkedBrowser.contentWindow);
+    });
+  }, true);
+
+  gBrowser.selectedTab.linkedBrowser.loadURI("data:text/html,<!DOCTYPE html><html><body><script>function openPopups() { var w = window.open('data:text/html,foo', '', 'foo'); w.open('data:text/html,bar', '', ''); }</script><button onclick='openPopups();'>click</button></body></html>");
+}
+
+/**
+ * window.open and .click() on the element opening the window.
+ */
+function test4()
+{
+  prefs.setBoolPref("dom.block_multiple_popups", true);
+
+  gBrowser.selectedTab.linkedBrowser.addEventListener("load", function () {
+    gBrowser.selectedTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gBrowser.addEventListener("DOMPopupBlocked", function() {
+      gBrowser.removeEventListener("DOMPopupBlocked", arguments.callee, true);
+
+      ok(true, "The popup has been blocked");
+
+      cleanUp();
+      nextOrFinish();
+    }, true);
+
+    waitForFocus(function() {
+      var button = gBrowser.selectedTab.linkedBrowser.contentDocument.getElementsByTagName('button')[0];
+      EventUtils.synthesizeMouseAtCenter(button, {}, gBrowser.selectedTab.linkedBrowser.contentWindow);
+    });
+  }, true);
+
+  gBrowser.selectedTab.linkedBrowser.loadURI("data:text/html,<!DOCTYPE html><html><body><script>var r = false; function openPopups() { window.open('data:text/html,foo', '', 'foo'); if (!r) { document.getElementsByTagName('button')[0].click(); r=true; } }</script><button onclick='openPopups();'>click</button></body></html>");
+}
+
+/**
+ * Two window.open from the chrome.
+ */
+function test5()
+{
+  prefs.setBoolPref("dom.block_multiple_popups", true);
+
+  let gPopupsCount = 0;
+
+  ww.registerNotification(function(aSubject, aTopic, aData) {
+    if (aTopic != "domwindowopened") {
+      return;
+    }
+
+    gPopupsCount++;
+
+    if (gPopupsCount != 2) {
+      return;
+    }
+
+    executeSoon(function() {
+    executeSoon(function() {
+    executeSoon(function() {
+    executeSoon(function() {
+      ww.unregisterNotification(arguments.callee);
+
+      is(gPopupsCount, 2, "Both window appeared");
+      cleanUp();
+      nextOrFinish();
+    });
+    });
+    });
+    });
+  });
+
+  window.open("data:text/html,foo", '', 'foo');
+  window.open("data:text/html,foo", '', 'foo');
+}
+
+/**
+ * Two window.open with the pref being disabled.
+ */
+function test6()
+{
+  prefs.setBoolPref("dom.block_multiple_popups", false);
+
+  let gPopupsCount = 0;
+
+  gBrowser.selectedTab.linkedBrowser.addEventListener("load", function () {
+    gBrowser.selectedTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    ww.registerNotification(function(aSubject, aTopic, aData) {
+      if (aTopic != "domwindowopened") {
+        return;
+      }
+
+      gPopupsCount++;
+
+      if (gPopupsCount != 2) {
+        return;
+      }
+
+      executeSoon(function() {
+      executeSoon(function() {
+      executeSoon(function() {
+      executeSoon(function() {
+        ww.unregisterNotification(arguments.callee);
+
+        is(gPopupsCount, 2, "Both window appeared");
+        cleanUp();
+        nextOrFinish();
+      });
+      });
+      });
+      });
+    });
+
+    waitForFocus(function() {
+      var button = gBrowser.selectedTab.linkedBrowser.contentDocument.getElementsByTagName('button')[0];
+      EventUtils.synthesizeMouseAtCenter(button, {}, gBrowser.selectedTab.linkedBrowser.contentWindow);
+    });
+  }, true);
+
+  gBrowser.selectedTab.linkedBrowser.loadURI("data:text/html,<!DOCTYPE html><html><body><script>function openPopups() { window.open('data:text/html,foo', '', 'foo'); window.open('data:text/html,bar', '', 'foo'); }</script><button onclick='openPopups();'>click</button></body></html>");
+}
--- a/dom/tests/mochitest/bugs/test_bug346659.html
+++ b/dom/tests/mochitest/bugs/test_bug346659.html
@@ -23,16 +23,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 var numTestsSet1 = 6;
 var numTestsSet2 = 4;
 var numTestsSet3 = 2;
 var complete = 0;
 SimpleTest.waitForExplicitFinish();
 
 var wins = [];
 
+var gBlockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
 function r(base, tail) {
   return base.replace(/\/[^\/]*$/, "/" + tail);
 }
 
 function handleCmd(evt) {
   var cmd;
   try {
     cmd = JSON.parse(evt.data);
@@ -148,16 +151,17 @@ function handleTestEnd() {
     }
   } else if (numTestsSet2) {
     if (!--numTestsSet2) {
       // gc to get rid of all the old windows
       gc(); gc(); gc();
       setTimeout(startThirdBatch, 0);
     }
   } else if (!--numTestsSet3) {
+    SpecialPowers.setBoolPref("dom.block_multiple_popups", gBlockMultiplePopups);
     SimpleTest.finish();
   }
 }
 window.addEventListener("message", messageReceiver, false);
 
 var win = window.open("");
 win.x = 1;
 win.location.href = "bug346659-echoer.html";
--- a/dom/tests/mochitest/bugs/test_bug406375.html
+++ b/dom/tests/mochitest/bugs/test_bug406375.html
@@ -18,17 +18,22 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 
 /** Test for Bug 406375 **/
 
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
+  var blockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+  SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
   window.showModalDialog("file_bug406375.html");
   ok(true, "This test should not hang");
+
+  SpecialPowers.setBoolPref("dom.block_multiple_popups", blockMultiplePopups);
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug61098.html
+++ b/dom/tests/mochitest/bugs/test_bug61098.html
@@ -237,16 +237,19 @@ function resetDialogLoopBlocking()
 
   prefs.setIntPref("dom.successive_dialog_time_limit", 0);
 }
 
 var expectedState;
 
 function runtests()
 {
+  var blockMultiplePopups = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+  SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
+
   registerMockPromptService();
   enableDialogLoopBlocking();
 
   // Test that alert() works normally and then gets blocked on the
   // second call.
   w = window.open();
   w.alert("alert message 1");
   is (promptState.method, "alert", "Wrong prompt method called");
@@ -343,14 +346,16 @@ function runtests()
 
   w.close();
 
   resetDialogLoopBlocking();
 
   mockPromptFactoryRegisterer.unregister();
   mockPromptServiceRegisterer.unregister();
 
+  SpecialPowers.setBoolPref("dom.block_multiple_popups", blockMultiplePopups);
+
   SimpleTest.finish();
 }
 
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/chrome/test_popup_blocker_chrome.xul
+++ b/dom/tests/mochitest/chrome/test_popup_blocker_chrome.xul
@@ -1,54 +1,115 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=662519
 -->
-<window title="Mozilla Bug 662519"
+<window title="Mozilla Bug 662519 and Bug 675574"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=662519"
      target="_blank">Mozilla Bug 662519</a>
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=675574"
+     target="_blank">Mozilla Bug 675574</a>
+  <button onclick="window.open('data:text/html,foo', '', 'foo'); window.open('data:text/html,bar', '', 'bar');">click</button>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
 
   /** Test for Bug 662519 **/
 
+  let Cc = Components.classes;
+  let Ci = Components.interfaces;
+
   SimpleTest.waitForExplicitFinish();
 
   // We have to enable dom.disable_open_during_load which is disabled
   // by the test harness.
-  let prefs = Components.classes["@mozilla.org/preferences-service;1"]
-                .getService(Components.interfaces.nsIPrefBranch);
-  var gLastDomLoadValue = prefs.getBoolPref("dom.disable_open_during_load");
-  prefs.setBoolPref("dom.disable_open_during_load", true);
+  let prefs = Cc["@mozilla.org/preferences-service;1"]
+                .getService(Ci.nsIPrefBranch);
+  let gLastDomLoadValue = prefs.getBoolPref("dom.disable_open_during_load");
+  let gMultiplePopupsPref = prefs.getBoolPref("dom.block_multiple_popups");
 
-  let w = window.open("data:text/html,foobar", "", "width=200,height=200");
-  ok(w, "The window object shouldn't be null");
+  prefs.setBoolPref("dom.disable_open_during_load", true);
+  prefs.setBoolPref("dom.block_multiple_popups", true);
 
-  SimpleTest.waitForFocus(function() {
-    w.close();
-    ok(true, "The popup appeared");
+  function test1() {
+    let w = window.open("data:text/html,foobar", "", "width=200,height=200");
+    ok(w, "The window object shouldn't be null");
 
     SimpleTest.waitForFocus(function() {
-      let w = window.open("data:text/html,foobar", "", "width=200,height=200");
-      ok(w, "The window object shouldn't be null");
+      w.close();
+      ok(true, "The popup appeared");
 
       SimpleTest.waitForFocus(function() {
-        w.close();
+        let w = window.open("data:text/html,foobar", "", "width=200,height=200");
+        ok(w, "The window object shouldn't be null");
+
+        SimpleTest.waitForFocus(function() {
+          w.close();
+
+          ok(true, "The popup appeared");
+          test2();
+        }, w, false);
+      });
+    }, w, false);
+  }
+
+  function test2() {
+    let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
+               .getService(Ci.nsIWindowWatcher);
+
+    let gPopupsCount = 0;
+
+    ww.registerNotification(function(aSubject, aTopic, aData) {
+      if (aTopic != "domwindowopened") {
+        return;
+      }
+
+      gPopupsCount++;
 
-        ok(true, "The popup appeared");
+      if (gPopupsCount != 2) {
+        return;
+      }
+
+      SimpleTest.executeSoon(function() {
+      SimpleTest.executeSoon(function() {
+      SimpleTest.executeSoon(function() {
+      SimpleTest.executeSoon(function() {
+        ww.unregisterNotification(arguments.callee);
+
+        is(gPopupsCount, 2, "Both window appeared");
+
+        // Clean-up and finish.
+        let windowEnumerator = wm.getEnumerator(null);
+
+        while (windowEnumerator.hasMoreElements()) {
+          let win = windowEnumerator.getNext();
+
+          // Close all windows except ourself.
+          if (win != window && !win.closed) {
+            win.close();
+          }
+        }
+
+        prefs.setBoolPref("dom.block_multiple_popups", gMultiplePopupsPref);
         prefs.setBoolPref("dom.disable_open_during_load", gLastDomLoadValue);
         SimpleTest.finish();
-      }, w, false);
+      });
+      });
+      });
+      });
     });
-  }, w, false);
+
+    EventUtils.synthesizeMouseAtCenter(document.getElementsByTagName('button')[0], {});
+  }
+
+  SimpleTest.waitForFocus(test1);
   ]]>
   </script>
 </window>
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -557,16 +557,18 @@ pref("capability.policy.default.XMLHttpR
 pref("capability.policy.default.XMLHttpRequest.open-uri", "allAccess");
 pref("capability.policy.default.DOMParser.parseFromStream", "noAccess");
 
 // Clipboard
 pref("capability.policy.default.Clipboard.cutcopy", "noAccess");
 pref("capability.policy.default.Clipboard.paste", "noAccess");
 
 // Scripts & Windows prefs
+pref("dom.block_multiple_popups",           true);
+
 pref("dom.disable_image_src_set",           false);
 pref("dom.disable_window_flip",             false);
 pref("dom.disable_window_move_resize",      false);
 pref("dom.disable_window_status_change",    false);
 
 pref("dom.disable_window_open_feature.titlebar",    false);
 pref("dom.disable_window_open_feature.close",       false);
 pref("dom.disable_window_open_feature.toolbar",     false);
--- a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
@@ -19,16 +19,18 @@ var navigateToInsecure = false;
 var openTwoWindows = false;
 // Override the name of the test page to load, useful e.g. to prevent load
 // of images or other content before the test starts; this is actually
 // a 'redirect' to a different test page.
 var testPage = "";
 // Assign a function to this variable to have a clean up at the end
 var testCleanUp = null;
 
+// Backup the dom.block_multiple_popups pref value to re-set it on finish.
+var blockMultiplePopupsPref;
 
 // Internal variables
 var _windowCount = 0;
 
 window.onload = function onLoad()
 {
   if (location.search == "?runtest")
   {
@@ -62,16 +64,18 @@ window.onload = function onLoad()
       array.push(testPage);
       secureTestLocation = array.join("/");
     }
     secureTestLocation += "?runtest";
 
     if (openTwoWindows)
     {
       _windowCount = 2;
+      blockMultiplePopupsPref = SpecialPowers.getBoolPref("dom.block_multiple_popups");
+      SpecialPowers.setBoolPref("dom.block_multiple_popups", false);
       window.open(secureTestLocation, "_new1", "");
       window.open(secureTestLocation, "_new2", "");
     }
     else
     {
       _windowCount = 1;
       window.open(secureTestLocation);
     }
@@ -83,17 +87,20 @@ function onMessageReceived(event)
   switch (event.data)
   {
     // Indication of all test parts finish (from any of the frames)
     case "done":
       if (--_windowCount == 0)
       {
         if (testCleanUp)
           testCleanUp();
-          
+
+        if (openTwoWindows) {
+          SpecialPowers.setBoolPref("dom.block_multiple_popups", blockMultiplePopupsPref);
+        }
         SimpleTest.finish();
       }
       break;
 
     // Any other message indicates error or succes message of a test
     default:
       var failureRegExp = new RegExp("^FAILURE");
       var todoRegExp = new RegExp("^TODO");