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 101817 65a425a98e860afa7132473dbf6a2f6868374b92
parent 101816 c2630a86e9a2ad3859a71e94ae63e939b7636e56
child 101818 bc7286e373c9ddea9fe819473e25e5b05e91cfd3
push id191
push userlsblakk@mozilla.com
push dateFri, 05 Oct 2012 17:12:53 +0000
treeherdermozilla-release@ddb22ac6c03b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, cjones
bugs742944
milestone16.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 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 &&