Bug 742944 - Part 4: Handle window.open in <iframe mozbrowser>. r=bz, cjones
authorJustin Lebar <justin.lebar@gmail.com>
Tue, 12 Jun 2012 18:01:25 -0400
changeset 101332 65a425a98e860afa7132473dbf6a2f6868374b92
parent 101331 c2630a86e9a2ad3859a71e94ae63e939b7636e56
child 101333 bc7286e373c9ddea9fe819473e25e5b05e91cfd3
push idunknown
push userunknown
push dateunknown
reviewersbz, cjones
bugs742944
milestone16.0a1
Bug 742944 - Part 4: Handle window.open in <iframe mozbrowser>. r=bz, cjones
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
content/html/content/src/nsGenericHTMLFrameElement.cpp
content/html/content/src/nsGenericHTMLFrameElement.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsGlobalWindow.cpp
dom/browser-element/BrowserElementParent.cpp
dom/browser-element/BrowserElementParent.h
dom/browser-element/Makefile.in
dom/browser-element/nsIOpenWindowEventDetail.idl
dom/browser-element/nsOpenWindowEventDetail.cpp
dom/browser-element/nsOpenWindowEventDetail.h
dom/interfaces/html/nsIMozBrowserFrame.idl
dom/ipc/PBrowser.ipdl
dom/ipc/PContent.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
embedding/base/nsIWindowProvider.idl
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
layout/build/Makefile.in
mobile/android/installer/package-manifest.in
mobile/xul/installer/package-manifest.in
xpfe/appshell/src/nsContentTreeOwner.cpp
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -172,16 +172,17 @@
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_mms.xpt
 #endif
+@BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_power.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -183,16 +183,17 @@
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_mms.xpt
 #endif
+@BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_power.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_traversal.xpt
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -2206,8 +2206,16 @@ nsFrameLoader::GetTabChildGlobalAsEventT
 
 NS_IMETHODIMP
 nsFrameLoader::GetOwnerElement(nsIDOMElement **aElement)
 {
   nsCOMPtr<nsIDOMElement> ownerElement = do_QueryInterface(mOwnerContent);
   ownerElement.forget(aElement);
   return NS_OK;
 }
+
+void
+nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent)
+{
+  MOZ_ASSERT(!mRemoteBrowser);
+  MOZ_ASSERT(!mCurrentRemoteFrame);
+  mRemoteBrowser = static_cast<TabParent*>(aTabParent);
+}
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -22,16 +22,17 @@
 #include "Layers.h"
 #include "mozilla/dom/Element.h"
 
 class nsIURI;
 class nsSubDocumentFrame;
 class nsIView;
 class nsIInProcessContentFrameMessageManager;
 class AutoResetInShow;
+class nsITabParent;
 
 namespace mozilla {
 namespace dom {
 class PBrowserParent;
 class TabParent;
 }
 
 namespace layout {
@@ -251,16 +252,26 @@ public:
   }
   nsFrameMessageManager* GetFrameMessageManager() { return mMessageManager; }
 
   mozilla::dom::Element* GetOwnerContent() { return mOwnerContent; }
   bool ShouldClipSubdocument() { return mClipSubdocument; }
 
   bool ShouldClampScrollPosition() { return mClampScrollPosition; }
 
+  /**
+   * Tell this FrameLoader to use a particular remote browser.
+   *
+   * This will assert if mRemoteBrowser or mCurrentRemoteFrame is non-null.  In
+   * practice, this means you can't have successfully run TryRemoteBrowser() on
+   * this object, which means you can't have called ShowRemoteFrame() or
+   * ReallyStartLoading().
+   */
+  void SetRemoteBrowser(nsITabParent* aTabParent);
+
 private:
 
   void SetOwnerContent(mozilla::dom::Element* aContent);
 
   bool ShouldUseRemoteProcess();
 
   /**
    * Is this a frameloader for a bona fide <iframe mozbrowser>?  (I.e., does
--- a/content/html/content/src/nsGenericHTMLFrameElement.cpp
+++ b/content/html/content/src/nsGenericHTMLFrameElement.cpp
@@ -93,31 +93,41 @@ nsGenericHTMLFrameElement::GetContentWin
                "Uh, this window should always be an outer window!");
 
   return CallQueryInterface(win, aContentWindow);
 }
 
 nsresult
 nsGenericHTMLFrameElement::EnsureFrameLoader()
 {
-  if (!GetParent() || !IsInDoc() || mFrameLoader) {
+  if (!GetParent() || !IsInDoc() || mFrameLoader || mFrameLoaderCreationDisallowed) {
     // If frame loader is there, we just keep it around, cached
     return NS_OK;
   }
 
   mFrameLoader = nsFrameLoader::Create(this, mNetworkCreated);
   if (!mFrameLoader) {
     // Strangely enough, this method doesn't actually ensure that the
     // frameloader exists.  It's more of a best-effort kind of thing.
     return NS_OK;
   }
 
   return NS_OK;
 }
 
+nsresult
+nsGenericHTMLFrameElement::CreateRemoteFrameLoader(nsITabParent* aTabParent)
+{
+  MOZ_ASSERT(!mFrameLoader);
+  EnsureFrameLoader();
+  NS_ENSURE_STATE(mFrameLoader);
+  mFrameLoader->SetRemoteBrowser(aTabParent);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::GetFrameLoader(nsIFrameLoader **aFrameLoader)
 {
   NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
   return NS_OK;
 }
 
 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
@@ -291,8 +301,26 @@ nsGenericHTMLFrameElement::GetReallyIsBr
                                            "dom.mozBrowserFramesWhitelist")) {
     return NS_OK;
   }
 
   // Otherwise, succeed.
   *aOut = true;
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsGenericHTMLFrameElement::DisallowCreateFrameLoader()
+{
+  MOZ_ASSERT(!mFrameLoader);
+  MOZ_ASSERT(!mFrameLoaderCreationDisallowed);
+  mFrameLoaderCreationDisallowed = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGenericHTMLFrameElement::AllowCreateFrameLoader()
+{
+  MOZ_ASSERT(!mFrameLoader);
+  MOZ_ASSERT(mFrameLoaderCreationDisallowed);
+  mFrameLoaderCreationDisallowed = false;
+  return NS_OK;
+}
--- a/content/html/content/src/nsGenericHTMLFrameElement.h
+++ b/content/html/content/src/nsGenericHTMLFrameElement.h
@@ -20,16 +20,17 @@ class nsGenericHTMLFrameElement : public
                                   public nsIMozBrowserFrame
 {
 public:
   nsGenericHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                             mozilla::dom::FromParser aFromParser)
     : nsGenericHTMLElement(aNodeInfo)
     , mNetworkCreated(aFromParser == mozilla::dom::FROM_PARSER_NETWORK)
     , mBrowserFrameListenersRegistered(false)
+    , mFrameLoaderCreationDisallowed(false)
   {
   }
 
   virtual ~nsGenericHTMLFrameElement();
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
   NS_DECL_NSIFRAMELOADEROWNER
   NS_DECL_NSIDOMMOZBROWSERFRAME
@@ -94,9 +95,10 @@ protected:
   nsRefPtr<nsFrameLoader> mFrameLoader;
 
   // True when the element is created by the parser
   // using NS_FROM_PARSER_NETWORK flag.
   // If the element is modified, it may lose the flag.
   bool                    mNetworkCreated;
 
   bool                    mBrowserFrameListenersRegistered;
+  bool                    mFrameLoaderCreationDisallowed;
 };
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -516,28 +516,28 @@ using mozilla::dom::indexedDB::IDBWrappe
 
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #include "BluetoothAdapter.h"
 #endif
 
 #include "DOMError.h"
 #include "DOMRequest.h"
+#include "nsIOpenWindowEventDetail.h"
+#include "nsIDOMGlobalObjectConstructor.h"
 
 #include "DOMFileHandle.h"
 #include "FileRequest.h"
 #include "LockedFile.h"
 
 #include "mozilla/Likely.h"
 
 #undef None // something included above defines this preprocessor symbol, maybe Xlib headers
 #include "WebGLContext.h"
 
-#include "nsIDOMGlobalObjectConstructor.h"
-
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 static const char kDOMStringBundleURL[] =
   "chrome://global/locale/dom/dom.properties";
 
@@ -1658,16 +1658,18 @@ static nsDOMClassInfoData sClassInfoData
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DOMRequest, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(OpenWindowEventDetail, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(DOMFileHandle, FileHandle, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(FileRequest, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 };
@@ -4521,16 +4523,20 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMError)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DOMRequest, nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(OpenWindowEventDetail, nsIOpenWindowEventDetail)
+    DOM_CLASSINFO_MAP_ENTRY(nsIOpenWindowEventDetail)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(DOMFileHandle, nsIDOMFileHandle)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(FileRequest, nsIDOMFileRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -527,12 +527,13 @@ DOMCI_CLASS(CallEvent)
 
 #ifdef MOZ_B2G_BT
 DOMCI_CLASS(BluetoothManager)
 DOMCI_CLASS(BluetoothAdapter)
 #endif
 
 DOMCI_CLASS(DOMError)
 DOMCI_CLASS(DOMRequest)
+DOMCI_CLASS(OpenWindowEventDetail)
 
 DOMCI_CLASS(DOMFileHandle)
 DOMCI_CLASS(FileRequest)
 DOMCI_CLASS(LockedFile)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8291,20 +8291,18 @@ nsGlobalWindow::GetInterface(const nsIID
         *aSink = webNav;
         NS_ADDREF(((nsISupports *) *aSink));
       }
     }
   }
   else if (aIID.Equals(NS_GET_IID(nsIDocShell))) {
     FORWARD_TO_OUTER(GetInterface, (aIID, aSink), NS_ERROR_NOT_INITIALIZED);
 
-    nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(mDocShell);
-    if (docShell) {
-      docShell.forget(aSink);
-    }
+    nsCOMPtr<nsIDocShell> docShell = mDocShell;
+    docShell.forget(aSink);
   }
 #ifdef NS_PRINTING
   else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) {
     FORWARD_TO_OUTER(GetInterface, (aIID, aSink), NS_ERROR_NOT_INITIALIZED);
 
     if (mDocShell) {
       nsCOMPtr<nsIContentViewer> viewer;
       mDocShell->GetContentViewer(getter_AddRefs(viewer));
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -0,0 +1,209 @@
+/* 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/. */
+
+#include "TabParent.h"
+
+// TabParent.h transitively includes <windows.h>, which does
+//   #define CreateEvent CreateEventW
+// That messes up our call to nsEventDispatcher::CreateEvent below.
+
+#ifdef CreateEvent
+#undef CreateEvent
+#endif
+
+#include "BrowserElementParent.h"
+#include "nsHTMLIFrameElement.h"
+#include "nsOpenWindowEventDetail.h"
+#include "nsEventDispatcher.h"
+#include "nsIDOMCustomEvent.h"
+#include "nsVariant.h"
+
+using mozilla::dom::Element;
+using mozilla::dom::TabParent;
+
+namespace {
+
+/**
+ * Create an <iframe mozbrowser> owned by the same document as
+ * aOpenerFrameElement.
+ */
+already_AddRefed<nsHTMLIFrameElement>
+CreateIframe(Element* aOpenerFrameElement)
+{
+  nsNodeInfoManager *nodeInfoManager =
+    aOpenerFrameElement->OwnerDoc()->NodeInfoManager();
+
+  nsCOMPtr<nsINodeInfo> nodeInfo =
+    nodeInfoManager->GetNodeInfo(nsGkAtoms::iframe,
+                                 /* aPrefix = */ nsnull,
+                                 kNameSpaceID_XHTML,
+                                 nsIDOMNode::ELEMENT_NODE);
+
+  nsRefPtr<nsHTMLIFrameElement> popupFrameElement =
+    static_cast<nsHTMLIFrameElement*>(
+      NS_NewHTMLIFrameElement(nodeInfo.forget(), mozilla::dom::NOT_FROM_PARSER));
+
+  popupFrameElement->SetMozbrowser(true);
+
+  // Copy the opener frame's mozapp attribute to the popup frame.
+  if (aOpenerFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozapp)) {
+    nsAutoString mozapp;
+    aOpenerFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapp, mozapp);
+    popupFrameElement->SetAttr(kNameSpaceID_None, nsGkAtoms::mozapp,
+                               mozapp, /* aNotify = */ false);
+  }
+
+  return popupFrameElement.forget();
+}
+
+/**
+ * Dispatch a mozbrowseropenwindow event to the given opener frame element.
+ * The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|.
+ *
+ * Returns true iff there were no unexpected failures and the window.open call
+ * was accepted by the embedder.
+ */
+bool
+DispatchOpenWindowEvent(Element* aOpenerFrameElement,
+                        Element* aPopupFrameElement,
+                        const nsAString& aURL,
+                        const nsAString& aName,
+                        const nsAString& aFeatures)
+{
+  // Dispatch a CustomEvent at aOpenerFrameElement with a detail object
+  // (nsIOpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and
+  // aFeatures.
+
+  // Create the event's detail object.
+  nsRefPtr<nsOpenWindowEventDetail> detail =
+    new nsOpenWindowEventDetail(aURL, aName, aFeatures,
+                                aPopupFrameElement->AsDOMNode());
+  nsCOMPtr<nsIWritableVariant> detailVariant = new nsVariant();
+  nsresult rv = detailVariant->SetAsISupports(detail);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  // Create the CustomEvent.
+  nsIPresShell *shell = aOpenerFrameElement->OwnerDoc()->GetShell();
+  nsRefPtr<nsPresContext> presContext;
+  if (shell) {
+    presContext = shell->GetPresContext();
+  }
+
+  nsCOMPtr<nsIDOMEvent> domEvent;
+  nsEventDispatcher::CreateEvent(presContext, nsnull,
+                                 NS_LITERAL_STRING("customevent"),
+                                 getter_AddRefs(domEvent));
+  NS_ENSURE_TRUE(domEvent, false);
+
+  nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
+  NS_ENSURE_TRUE(customEvent, false);
+  customEvent->InitCustomEvent(NS_LITERAL_STRING("mozbrowseropenwindow"),
+                               /* bubbles = */ true,
+                               /* cancelable = */ false,
+                               detailVariant);
+  customEvent->SetTrusted(true);
+
+  // Dispatch the event.
+  nsEventStatus status = nsEventStatus_eIgnore;
+  rv = nsEventDispatcher::DispatchDOMEvent(aOpenerFrameElement, nsnull,
+                                           domEvent, presContext, &status);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  // If the iframe is not in some document's DOM at this point, the embedder
+  // has "blocked" the popup.
+  return aPopupFrameElement->IsInDoc();
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+
+/*static*/ bool
+BrowserElementParent::OpenWindowOOP(mozilla::dom::TabParent* aOpenerTabParent,
+                                    mozilla::dom::TabParent* aPopupTabParent,
+                                    const nsAString& aURL,
+                                    const nsAString& aName,
+                                    const nsAString& aFeatures)
+{
+  // Create an iframe owned by the same document which owns openerFrameElement.
+  nsCOMPtr<Element> openerFrameElement =
+    do_QueryInterface(aOpenerTabParent->GetOwnerElement());
+  NS_ENSURE_TRUE(openerFrameElement, false);
+  nsRefPtr<nsHTMLIFrameElement> popupFrameElement =
+    CreateIframe(openerFrameElement);
+
+  // Normally an <iframe> element will try to create a frameLoader when the
+  // page touches iframe.contentWindow or sets iframe.src.
+  //
+  // But in our case, we want to delay the creation of the frameLoader until
+  // we've verified that the popup has gone through successfully.  If the popup
+  // is "blocked" by the embedder, we don't want to load the popup's url.
+  //
+  // Therefore we call DisallowCreateFrameLoader() on the element and call
+  // AllowCreateFrameLoader() only after we've verified that the popup was
+  // allowed.
+  popupFrameElement->DisallowCreateFrameLoader();
+
+  bool dispatchSucceeded =
+    DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
+                            aURL, aName, aFeatures);
+  if (!dispatchSucceeded) {
+    return false;
+  }
+
+  // The popup was not blocked, so hook up the frame element and the popup tab
+  // parent, and return success.
+  aPopupTabParent->SetOwnerElement(popupFrameElement);
+  popupFrameElement->AllowCreateFrameLoader();
+  popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent);
+
+  return true;
+}
+
+/* static */ bool
+BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
+                                          nsIURI* aURI,
+                                          const nsAString& aName,
+                                          const nsACString& aFeatures,
+                                          nsIDOMWindow** aReturnWindow)
+{
+  *aReturnWindow = NULL;
+
+  nsCOMPtr<nsIDOMElement> openerFrameDOMElement;
+  aOpenerWindow->GetFrameElement(getter_AddRefs(openerFrameDOMElement));
+  NS_ENSURE_TRUE(openerFrameDOMElement, false);
+
+  nsCOMPtr<nsINode> openerFrameNode = do_QueryInterface(openerFrameDOMElement);
+  nsRefPtr<Element> openerFrameElement = openerFrameNode->AsElement();
+
+  nsRefPtr<nsHTMLIFrameElement> popupFrameElement =
+    CreateIframe(openerFrameElement);
+  NS_ENSURE_TRUE(popupFrameElement, false);
+
+  nsCAutoString spec;
+  aURI->GetSpec(spec);
+  bool dispatchSucceeded =
+    DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
+                            NS_ConvertUTF8toUTF16(spec),
+                            aName,
+                            NS_ConvertUTF8toUTF16(aFeatures));
+  if (!dispatchSucceeded) {
+    return false;
+  }
+
+  // Return popupFrameElement's window.
+  nsCOMPtr<nsIFrameLoader> frameLoader;
+  popupFrameElement->GetFrameLoader(getter_AddRefs(frameLoader));
+  NS_ENSURE_TRUE(frameLoader, false);
+
+  nsCOMPtr<nsIDocShell> docshell;
+  frameLoader->GetDocShell(getter_AddRefs(docshell));
+  NS_ENSURE_TRUE(docshell, false);
+
+  nsCOMPtr<nsIDOMWindow> window = do_GetInterface(docshell);
+  window.forget(aReturnWindow);
+  return !!*aReturnWindow;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/BrowserElementParent.h
@@ -0,0 +1,89 @@
+/* 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/. */
+
+#ifndef mozilla_BrowserElementHelpers_h
+#define mozilla_BrowserElementHelpers_h
+
+#include "nsAString.h"
+
+class nsIDOMWindow;
+class nsIURI;
+
+namespace mozilla {
+
+namespace dom {
+class TabParent;
+}
+
+/**
+ * BrowserElementParent implements a portion of the parent-process side of
+ * <iframe mozbrowser>.
+ *
+ * Most of the parent-process side of <iframe mozbrowser> is implemented in
+ * BrowserElementParent.js.  This file implements the few parts of this
+ * functionality which must be written in C++.
+ *
+ * We don't communicate with the JS code that lives in BrowserElementParent.js;
+ * the JS and C++ parts are completely separate.
+ */
+class BrowserElementParent
+{
+public:
+  /**
+   * Handle a window.open call from an out-of-process <iframe mozbrowser>.
+   *
+   * window.open inside <iframe mozbrowser> doesn't actually open a new
+   * top-level window.  Instead, the "embedder" (the document which contains
+   * the <iframe mozbrowser> whose content called window.open) gets the
+   * opportunity to place a new <iframe mozbrowser> in the DOM somewhere.  This
+   * new "popup" iframe acts as the opened window.
+   *
+   * This method proceeds in three steps.
+   *
+   * 1) We fire a mozbrowseropenwindow CustomEvent on the opener
+   *    iframe element.  This event's detail is an instance of
+   *    nsIOpenWindowEventDetail.
+   *
+   * 2) The embedder (the document which contains the opener iframe) can accept
+   *    the window.open request by inserting event.detail.frameElement (an iframe
+   *    element) into the DOM somewhere.
+   *
+   * 3) If the embedder accepted the window.open request, we return true and
+   *    set aPopupTabParent's frame element to event.detail.frameElement.
+   *    Otherwise, we return false.
+   *
+   * @param aOpenerTabParent the TabParent whose TabChild called window.open.
+   * @param aPopupTabParent the TabParent inside which the opened window will
+   *                        live.
+   * @return true on success, false otherwise.  Failure is not (necessarily)
+   *         an error; it may indicate that the embedder simply rejected the
+   *         window.open request.
+   */
+  static bool
+  OpenWindowOOP(mozilla::dom::TabParent* aOpenerTabParent,
+                mozilla::dom::TabParent* aPopupTabParent,
+                const nsAString& aURL,
+                const nsAString& aName,
+                const nsAString& aFeatures);
+
+  /**
+   * Handle a window.open call from an in-process <iframe mozbrowser>.
+   *
+   * As with OpenWindowOOP, we return true if the window.open request
+   * succeeded, and return false if the embedder denied the request.
+   *
+   * (These parameter types are silly, but they match what our caller has in
+   * hand.  Feel free to add an override, if they are inconvenient to you.)
+   */
+  static bool
+  OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
+                      nsIURI* aURI,
+                      const nsAString& aName,
+                      const nsACString& aFeatures,
+                      nsIDOMWindow** aReturnWindow);
+};
+
+} // namespace mozilla
+
+#endif
--- a/dom/browser-element/Makefile.in
+++ b/dom/browser-element/Makefile.in
@@ -2,24 +2,56 @@
 # 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            = ../..
 topsrcdir        = @top_srcdir@
 srcdir           = @srcdir@
 VPATH            = @srcdir@
 
-relativesrcdir   = dom/browser-element
-
 include $(DEPTH)/config/autoconf.mk
 
+MODULE           = dom
+LIBRARY_NAME     = dom_browserelement_s
+XPIDL_MODULE     = dom_browserelement
+LIBXUL_LIBRARY   = 1
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/dom/dom-config.mk
+
 TEST_DIRS += mochitest
 
+XPIDLSRCS = \
+  nsIOpenWindowEventDetail.idl \
+  $(NULL)
+
+EXPORTS = \
+  nsOpenWindowEventDetail.h \
+  $(NULL)
+
+EXPORTS_NAMESPACES = mozilla
+EXPORTS_mozilla = \
+  BrowserElementParent.h \
+  $(NULL)
+
+CPPSRCS = \
+  nsOpenWindowEventDetail.cpp \
+  BrowserElementParent.cpp \
+  $(NULL)
+
 EXTRA_COMPONENTS = \
   BrowserElementParent.js \
   BrowserElementParent.manifest \
   $(NULL)
 
 EXTRA_JS_MODULES = \
   BrowserElementPromptService.jsm \
   $(NULL)
 
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
+
+INCLUDES	+= \
+		-I$(topsrcdir)/dom/base \
+		-I$(topsrcdir)/dom/ipc \
+		-I$(topsrcdir)/content/base/src \
+		$(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/nsIOpenWindowEventDetail.idl
@@ -0,0 +1,20 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMNode;
+
+/**
+ * When we send a mozbrowseropenwindow event (an instance of CustomEvent), we
+ * use an instance of this interface as the event's detail.
+ */
+[scriptable, uuid(94377af6-956a-4adf-908b-363f7023ae1a)]
+interface nsIOpenWindowEventDetail : nsISupports
+{
+  readonly attribute AString url;
+  readonly attribute AString name;
+  readonly attribute AString features;
+  readonly attribute nsIDOMNode frameElement;
+};
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/nsOpenWindowEventDetail.cpp
@@ -0,0 +1,49 @@
+/* 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/. */
+
+#include "nsOpenWindowEventDetail.h"
+#include "nsDOMClassInfoID.h"
+#include "nsIDOMClassInfo.h"
+#include "nsIClassInfo.h"
+#include "nsDOMClassInfo.h"
+
+NS_IMPL_CYCLE_COLLECTION_1(nsOpenWindowEventDetail, mFrameElement)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsOpenWindowEventDetail)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsOpenWindowEventDetail)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsOpenWindowEventDetail)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIOpenWindowEventDetail)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(OpenWindowEventDetail)
+NS_INTERFACE_MAP_END
+
+DOMCI_DATA(OpenWindowEventDetail, nsOpenWindowEventDetail)
+
+NS_IMETHODIMP
+nsOpenWindowEventDetail::GetUrl(nsAString& aOut)
+{
+  aOut.Assign(mURL);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOpenWindowEventDetail::GetName(nsAString& aOut)
+{
+  aOut.Assign(mName);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOpenWindowEventDetail::GetFeatures(nsAString& aOut)
+{
+  aOut.Assign(mFeatures);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOpenWindowEventDetail::GetFrameElement(nsIDOMNode** aOut)
+{
+  nsCOMPtr<nsIDOMNode> out = mFrameElement;
+  out.forget(aOut);
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/nsOpenWindowEventDetail.h
@@ -0,0 +1,39 @@
+/* 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/. */
+
+#include "nsIOpenWindowEventDetail.h"
+#include "nsIDOMNode.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+/**
+ * When we send a mozbrowseropenwindow event (an instance of CustomEvent), we
+ * use an instance of this class as the event's detail.
+ */
+class nsOpenWindowEventDetail : public nsIOpenWindowEventDetail
+{
+public:
+  nsOpenWindowEventDetail(const nsAString& aURL,
+                          const nsAString& aName,
+                          const nsAString& aFeatures,
+                          nsIDOMNode* aFrameElement)
+    : mURL(aURL)
+    , mName(aName)
+    , mFeatures(aFeatures)
+    , mFrameElement(aFrameElement)
+  {}
+
+  virtual ~nsOpenWindowEventDetail() {}
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(nsOpenWindowEventDetail)
+  NS_DECL_NSIOPENWINDOWEVENTDETAIL
+
+private:
+  const nsString mURL;
+  const nsString mName;
+  const nsString mFeatures;
+  nsCOMPtr<nsIDOMNode> mFrameElement;
+};
--- a/dom/interfaces/html/nsIMozBrowserFrame.idl
+++ b/dom/interfaces/html/nsIMozBrowserFrame.idl
@@ -2,20 +2,46 @@
 /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
 
 /* 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/. */
 
 #include "nsIDOMMozBrowserFrame.idl"
 
-[scriptable, uuid(076AD76C-2DF6-4760-B914-21D554F0A2B6)]
+interface nsITabParent;
+
+[scriptable, uuid(0acd92dd-2902-48ee-adcf-082d3bb3ec45)]
 interface nsIMozBrowserFrame : nsIDOMMozBrowserFrame
 {
   /**
    * Gets whether this frame really is a browser frame.
    *
    * In order to really be a browser frame, this frame's
    * nsIDOMMozBrowserFrame::mozbrowser attribute must be true, and the frame
    * may have to pass various security checks.
    */
   readonly attribute boolean reallyIsBrowser;
+
+  /**
+   * Normally, a frame tries to create its frame loader when its src is
+   * modified, or its contentWindow is accessed.
+   *
+   * disallowCreateFrameLoader prevents the frame element from creating its
+   * frame loader (in the same way that not being inside a document prevents the
+   * creation of a frame loader).  allowCreateFrameLoader lifts this restriction.
+   *
+   * These methods are not re-entrant -- it is an error to call
+   * disallowCreateFrameLoader twice without first calling allowFrameLoader.
+   *
+   * It's also an error to call either method if we already have a frame loader.
+   */
+  void disallowCreateFrameLoader();
+  void allowCreateFrameLoader();
+
+  /**
+   * Create a remote (i.e., out-of-process) frame loader attached to the given
+   * tab parent.
+   *
+   * It is an error to call this method if we already have a frame loader.
+   */
+  void createRemoteFrameLoader(in nsITabParent aTabParent);
 };
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -200,16 +200,31 @@ parent:
      *   has already been cached (stickDocument=false).
      */
     POfflineCacheUpdate(URI manifestURI, URI documentURI, nsCString clientID,
                         bool stickDocument);
 
     sync PIndexedDB(nsCString asciiOrigin)
         returns (bool allowed);
 
+    /**
+     * window.open from inside <iframe mozbrowser> is special.  When the child
+     * process calls window.open, it creates a new PBrowser (in its own
+     * process), then calls BrowserFrameOpenWindow on it.
+     *
+     * The parent process gets a chance to accept or reject the window.open
+     * call, and windowOpened is set to true if we ended up going through with
+     * the window.open.
+     *
+     * @param opener the PBrowser whose content called window.open.
+     */
+    sync BrowserFrameOpenWindow(PBrowser opener, nsString aURL,
+                                nsString aName, nsString aFeatures)
+      returns (bool windowOpened);
+
     __delete__();
 
 child:
     /**
      * Notify the remote browser that it has been Show()n on this
      * side, with the given |visibleRect|.  This message is expected
      * to trigger creation of the remote browser's "widget".
      *
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -72,19 +72,26 @@ rpc protocol PContent
     manages PExternalHelperApp;
     manages PHal;
     manages PMemoryReportRequest;
     manages PNecko;
     manages PSms;
     manages PStorage;
     manages PTestShell;
 
+both:
+    // Depending on exactly how the new browser is being created, it might be
+    // created from either the child or parent process!
+    //
+    // The child creates the PBrowser as part of
+    // TabChild::BrowserFrameProvideWindow, and the parent creates the PBrowser
+    // as part of ContentParent::CreateTab.
+    async PBrowser(PRUint32 chromeFlags);
+
 child:
-    PBrowser(PRUint32 chromeFlags);
-
     PMemoryReportRequest();
 
     PTestShell();
 
     RegisterChrome(ChromePackage[] packages, ResourceMapping[] resources,
                    OverrideMapping[] overrides, nsCString locale);
 
     async SetOffline(bool offline);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1,10 +1,10 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
-/* vim: set sw=4 ts=8 et tw=80 : */
+/* vim: set sw=2 sts=2 ts=8 et tw=80 : */
 /* 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/. */
 
 #include "TabChild.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/dom/PContentChild.h"
 #include "mozilla/dom/PContentDialogChild.h"
@@ -88,16 +88,17 @@ public:
 
 
 TabChild::TabChild(PRUint32 aChromeFlags)
   : mRemoteFrame(nsnull)
   , mTabChildGlobal(nsnull)
   , mChromeFlags(aChromeFlags)
   , mOuterRect(0, 0, 0, 0)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
+  , mDidFakeShow(false)
 {
     printf("creating %d!\n", NS_IsMainThread());
 }
 
 nsresult
 TabChild::Init()
 {
   nsCOMPtr<nsIWebBrowser> webBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
@@ -326,28 +327,85 @@ TabChild::ProvideWindow(nsIDOMWindow* aP
                         bool aCalledFromJS,
                         bool aPositionSpecified, bool aSizeSpecified,
                         nsIURI* aURI, const nsAString& aName,
                         const nsACString& aFeatures, bool* aWindowIsNew,
                         nsIDOMWindow** aReturn)
 {
     *aReturn = nsnull;
 
+    // If aParent is inside an <iframe mozbrowser> and this isn't a request to
+    // open a modal-type window, we're going to create a new <iframe mozbrowser>
+    // and return its window here.
+    nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
+    bool inBrowserFrame = false;
+    if (docshell) {
+      docshell->GetContainedInBrowserFrame(&inBrowserFrame);
+    }
+
+    if (inBrowserFrame &&
+        !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
+                          nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
+                          nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
+
+      // Note that BrowserFrameProvideWindow may return NS_ERROR_ABORT if the
+      // open window call was canceled.  It's important that we pass this error
+      // code back to our caller.
+      return BrowserFrameProvideWindow(aParent, aURI, aName, aFeatures,
+                                       aWindowIsNew, aReturn);
+    }
+
+    // Otherwise, create a new top-level window.
     PBrowserChild* newChild;
     if (!CallCreateWindow(&newChild)) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     *aWindowIsNew = true;
     nsCOMPtr<nsIDOMWindow> win =
         do_GetInterface(static_cast<TabChild*>(newChild)->mWebNav);
     win.forget(aReturn);
     return NS_OK;
 }
 
+nsresult
+TabChild::BrowserFrameProvideWindow(nsIDOMWindow* aOpener,
+                                    nsIURI* aURI,
+                                    const nsAString& aName,
+                                    const nsACString& aFeatures,
+                                    bool* aWindowIsNew,
+                                    nsIDOMWindow** aReturn)
+{
+  *aReturn = nsnull;
+
+  nsRefPtr<TabChild> newChild =
+    static_cast<TabChild*>(Manager()->SendPBrowserConstructor(0));
+
+  nsCAutoString spec;
+  aURI->GetSpec(spec);
+
+  NS_ConvertUTF8toUTF16 url(spec);
+  nsString name(aName);
+  NS_ConvertUTF8toUTF16 features(aFeatures);
+  newChild->SendBrowserFrameOpenWindow(this, url, name,
+                                       features, aWindowIsNew);
+  if (!*aWindowIsNew) {
+    PBrowserChild::Send__delete__(newChild);
+    return NS_ERROR_ABORT;
+  }
+
+  // Unfortunately we don't get a window unless we've shown the frame.  That's
+  // pretty bogus; see bug 763602.
+  newChild->DoFakeShow();
+
+  nsCOMPtr<nsIDOMWindow> win = do_GetInterface(newChild->mWebNav);
+  win.forget(aReturn);
+  return NS_OK;
+}
+
 static nsInterfaceHashtable<nsPtrHashKey<PContentDialogChild>, nsIDialogParamBlock> gActiveDialogs;
 
 NS_IMETHODIMP
 TabChild::OpenDialog(PRUint32 aType, const nsACString& aName,
                      const nsACString& aFeatures,
                      nsIDialogParamBlock* aArguments,
                      nsIDOMElement* aFrameElement)
 {
@@ -478,19 +536,30 @@ TabChild::RecvLoadURL(const nsCString& u
                                    NULL, NULL, NULL);
     if (NS_FAILED(rv)) {
         NS_WARNING("mWebNav->LoadURI failed. Eating exception, what else can I do?");
     }
 
     return true;
 }
 
+void
+TabChild::DoFakeShow()
+{
+  RecvShow(nsIntSize(0, 0));
+  mDidFakeShow = true;
+}
+
 bool
 TabChild::RecvShow(const nsIntSize& size)
 {
+    if (mDidFakeShow) {
+        return true;
+    }
+
     printf("[TabChild] SHOW (w,h)= (%d, %d)\n", size.width, size.height);
 
     nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mWebNav);
     if (!baseWindow) {
         NS_ERROR("mWebNav doesn't QI to nsIBaseWindow");
         return false;
     }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -241,23 +241,35 @@ protected:
 
 private:
     void ActorDestroy(ActorDestroyReason why);
 
     bool InitTabChildGlobal();
     bool InitWidget(const nsIntSize& size);
     void DestroyWindow();
 
+    // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
+    void DoFakeShow();
+
+    nsresult
+    BrowserFrameProvideWindow(nsIDOMWindow* aOpener,
+                              nsIURI* aURI,
+                              const nsAString& aName,
+                              const nsACString& aFeatures,
+                              bool* aWindowIsNew,
+                              nsIDOMWindow** aReturn);
+
     nsCOMPtr<nsIWebNavigation> mWebNav;
     nsCOMPtr<nsIWidget> mWidget;
     RenderFrameChild* mRemoteFrame;
     nsRefPtr<TabChildGlobal> mTabChildGlobal;
     PRUint32 mChromeFlags;
     nsIntRect mOuterRect;
     nscolor mLastBackgroundColor;
+    bool mDidFakeShow;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 inline TabChild*
 GetTabChildFrom(nsIDocShell* aDocShell)
 {
     nsCOMPtr<nsITabChild> tc = do_GetInterface(aDocShell);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -33,16 +33,17 @@
 #include "nsSerializationHelper.h"
 #include "nsIPromptFactory.h"
 #include "nsIContent.h"
 #include "nsIWidget.h"
 #include "nsIViewManager.h"
 #include "mozilla/unused.h"
 #include "nsDebug.h"
 #include "nsPrintfCString.h"
+#include "mozilla/BrowserElementParent.h"
 #include "IndexedDBParent.h"
 #include "IDBFactory.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layout;
 using namespace mozilla::widget;
 using namespace mozilla::dom::indexedDB;
@@ -917,10 +918,23 @@ TabParent::GetWidget() const
   nsIFrame *frame = content->GetPrimaryFrame();
   if (!frame)
     return nsnull;
 
   nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget();
   return widget.forget();
 }
 
+bool
+TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
+                                      const nsString& aURL,
+                                      const nsString& aName,
+                                      const nsString& aFeatures,
+                                      bool* aOutWindowOpened)
+{
+  *aOutWindowOpened =
+    BrowserElementParent::OpenWindowOOP(static_cast<TabParent*>(aOpener),
+                                        this, aURL, aName, aFeatures);
+  return true;
+}
+
 } // namespace tabs
 } // namespace mozilla
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -47,17 +47,21 @@ public:
     void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserDOMWindow) {
         mBrowserDOMWindow = aBrowserDOMWindow;
     }
  
     void Destroy();
 
     virtual bool RecvMoveFocus(const bool& aForward);
     virtual bool RecvEvent(const RemoteDOMEvent& aEvent);
-
+    virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
+                                            const nsString& aURL,
+                                            const nsString& aName,
+                                            const nsString& aFeatures,
+                                            bool* aOutWindowOpened);
     virtual bool AnswerCreateWindow(PBrowserParent** retval);
     virtual bool RecvSyncMessage(const nsString& aMessage,
                                  const nsString& aJSON,
                                  InfallibleTArray<nsString>* aJSONRetVal);
     virtual bool RecvAsyncMessage(const nsString& aMessage,
                                   const nsString& aJSON);
     virtual bool RecvNotifyIMEFocus(const bool& aFocus,
                                     nsIMEUpdatePreference* aPreference,
--- a/embedding/base/nsIWindowProvider.idl
+++ b/embedding/base/nsIWindowProvider.idl
@@ -72,16 +72,19 @@ interface nsIWindowProvider : nsISupport
    *                           before the provideWindow() call.  The value of this
    *                           out parameter is meaningless if provideWindow()
    *                           returns null.
    * @return A window the caller should use or null if the caller should just
    *         create a new window.  The returned window may be newly opened by
    *         the nsIWindowProvider implementation or may be a window that
    *         already existed.
    *
+   * @throw NS_ERROR_ABORT if the caller should cease its attempt to open a new
+   *                       window.
+   *
    * @see nsIWindowWatcher for more information on aFeatures.
    * @see nsIWebBrowserChrome for more information on aChromeFlags.
    */
   nsIDOMWindow provideWindow(in nsIDOMWindow aParent,
                              in unsigned long aChromeFlags,
                              in boolean aCalledFromJS,
                              in boolean aPositionSpecified,
                              in boolean aSizeSpecified,
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -588,28 +588,37 @@ nsWindowWatcher::OpenWindowJSInternal(ns
         NS_ASSERTION(aParent, "We've _got_ to have a parent here!");
 
         nsCOMPtr<nsIDOMWindow> newWindow;
         rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
                                      sizeSpec.PositionSpecified(),
                                      sizeSpec.SizeSpecified(),
                                      uriToLoad, name, features, &windowIsNew,
                                      getter_AddRefs(newWindow));
+
         if (NS_SUCCEEDED(rv)) {
           GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
           if (windowIsNew && newDocShellItem) {
             // Make sure to stop any loads happening in this window that the
             // window provider might have started.  Otherwise if our caller
             // manipulates the window it just opened and then the load
             // completes their stuff will get blown away.
             nsCOMPtr<nsIWebNavigation> webNav =
               do_QueryInterface(newDocShellItem);
             webNav->Stop(nsIWebNavigation::STOP_NETWORK);
           }
         }
+        else if (rv == NS_ERROR_ABORT) {
+          // NS_ERROR_ABORT means the window provider has flat-out rejected
+          // the open-window call and we should bail.  Don't return an error
+          // here, because our caller may propagate that error, which might
+          // cause e.g. window.open to throw!  Just return null for our out
+          // param.
+          return NS_OK;
+        }
       }
     }
   }
   
   bool newWindowShouldBeModal = false;
   bool parentIsModal = false;
   if (!newDocShellItem) {
     windowIsNew = true;
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -75,16 +75,17 @@ SHARED_LIBRARY_LIBS = \
 	$(DEPTH)/dom/src/storage/$(LIB_PREFIX)jsdomstorage_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/offline/$(LIB_PREFIX)jsdomoffline_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/geolocation/$(LIB_PREFIX)jsdomgeolocation_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/notification/$(LIB_PREFIX)jsdomnotification_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/system/$(LIB_PREFIX)domsystem_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/workers/$(LIB_PREFIX)domworkers_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/indexedDB/$(LIB_PREFIX)dom_indexeddb_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/indexedDB/ipc/$(LIB_PREFIX)dom_indexeddb_ipc_s.$(LIB_SUFFIX) \
+	$(DEPTH)/dom/browser-element/$(LIB_PREFIX)dom_browserelement_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/libeditor/text/$(LIB_PREFIX)texteditor_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/libeditor/base/$(LIB_PREFIX)editorbase_s.$(LIB_SUFFIX) \
 	$(DEPTH)/parser/html/$(LIB_PREFIX)html5p_s.$(LIB_SUFFIX) \
 	$(DEPTH)/caps/src/$(LIB_PREFIX)caps_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/libeditor/html/$(LIB_PREFIX)htmleditor_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/txtsvc/src/$(LIB_PREFIX)txtsvc_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/mathml/content/src/$(LIB_PREFIX)gkcontentmathml_s.$(LIB_SUFFIX) \
 	$(NULL)
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -165,16 +165,17 @@
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_media.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
+@BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_power.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
 @BINPATH@/components/dom_traversal.xpt
--- a/mobile/xul/installer/package-manifest.in
+++ b/mobile/xul/installer/package-manifest.in
@@ -171,16 +171,17 @@
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_file.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
+@BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_json.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
 @BINPATH@/components/dom_traversal.xpt
--- a/xpfe/appshell/src/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/src/nsContentTreeOwner.cpp
@@ -26,16 +26,17 @@
 #include "nsIAuthPrompt.h"
 #include "nsIWindowMediator.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIPrincipal.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIWebNavigation.h"
 #include "nsIJSContextStack.h"
+#include "mozilla/BrowserElementParent.h"
 
 #include "nsIDOMDocument.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 #if defined(XP_MACOSX)
 #include "nsThreadUtils.h"
 #endif
 
@@ -832,16 +833,39 @@ nsContentTreeOwner::ProvideWindow(nsIDOM
 #ifdef DEBUG
   nsCOMPtr<nsIWebNavigation> parentNav = do_GetInterface(aParent);
   nsCOMPtr<nsIDocShellTreeOwner> parentOwner = do_GetInterface(parentNav);
   NS_ASSERTION(SameCOMIdentity(parentOwner,
                                static_cast<nsIDocShellTreeOwner*>(this)),
                "Parent from wrong docshell tree?");
 #endif
 
+  // If aParent is inside an <iframe mozbrowser> and this isn't a request to
+  // open a modal-type window, we're going to create a new <iframe mozbrowser>
+  // and return its window here.
+  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
+  bool inBrowserFrame = false;
+  if (docshell) {
+    docshell->GetContainedInBrowserFrame(&inBrowserFrame);
+  }
+
+  if (inBrowserFrame &&
+      !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
+                        nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
+                        nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
+    bool openSucceeded =
+      BrowserElementParent::OpenWindowInProcess(aParent, aURI, aName,
+                                                aFeatures, aReturn);
+
+    // If OpenWindowInProcess failed (perhaps because the embedder blocked the
+    // popup), tell our caller not to proceed trying to create a new window
+    // through other means.
+    return openSucceeded ? NS_OK : NS_ERROR_ABORT;
+  }
+
   // Where should we open this?
   PRInt32 containerPref;
   if (NS_FAILED(Preferences::GetInt("browser.link.open_newwindow",
                                     &containerPref))) {
     return NS_OK;
   }
 
   if (containerPref != nsIBrowserDOMWindow::OPEN_NEWTAB &&