Bug 742944 - Part 4: Handle window.open in <iframe mozbrowser>. r=bz, cjones

--HG--
extra : rebase_source : 4c16c86c5be230af22eea89dfb5f893b7c6091dd
This commit is contained in:
Justin Lebar 2012-06-12 18:01:25 -04:00
parent dd8aad7da8
commit d19ebc4677
28 changed files with 694 additions and 14 deletions

View File

@ -177,6 +177,7 @@
#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

View File

@ -188,6 +188,7 @@
#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

View File

@ -2211,3 +2211,11 @@ nsFrameLoader::GetOwnerElement(nsIDOMElement **aElement)
ownerElement.forget(aElement);
return NS_OK;
}
void
nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent)
{
MOZ_ASSERT(!mRemoteBrowser);
MOZ_ASSERT(!mCurrentRemoteFrame);
mRemoteBrowser = static_cast<TabParent*>(aTabParent);
}

View File

@ -27,6 +27,7 @@ class nsSubDocumentFrame;
class nsIView;
class nsIInProcessContentFrameMessageManager;
class AutoResetInShow;
class nsITabParent;
namespace mozilla {
namespace dom {
@ -256,6 +257,16 @@ public:
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);

View File

@ -98,7 +98,7 @@ nsGenericHTMLFrameElement::GetContentWindow(nsIDOMWindow** 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;
}
@ -113,6 +113,16 @@ nsGenericHTMLFrameElement::EnsureFrameLoader()
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)
{
@ -296,3 +306,21 @@ nsGenericHTMLFrameElement::GetReallyIsBrowser(bool *aOut)
*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;
}

View File

@ -25,6 +25,7 @@ public:
: nsGenericHTMLElement(aNodeInfo)
, mNetworkCreated(aFromParser == mozilla::dom::FROM_PARSER_NETWORK)
, mBrowserFrameListenersRegistered(false)
, mFrameLoaderCreationDisallowed(false)
{
}
@ -99,4 +100,5 @@ protected:
bool mNetworkCreated;
bool mBrowserFrameListenersRegistered;
bool mFrameLoaderCreationDisallowed;
};

View File

@ -521,6 +521,8 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
#include "DOMError.h"
#include "DOMRequest.h"
#include "nsIOpenWindowEventDetail.h"
#include "nsIDOMGlobalObjectConstructor.h"
#include "DOMFileHandle.h"
#include "FileRequest.h"
@ -531,8 +533,6 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
#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;
@ -1663,6 +1663,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
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)
@ -4526,6 +4528,10 @@ nsDOMClassInfo::Init()
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

View File

@ -532,6 +532,7 @@ DOMCI_CLASS(BluetoothAdapter)
DOMCI_CLASS(DOMError)
DOMCI_CLASS(DOMRequest)
DOMCI_CLASS(OpenWindowEventDetail)
DOMCI_CLASS(DOMFileHandle)
DOMCI_CLASS(FileRequest)

View File

@ -8296,10 +8296,8 @@ nsGlobalWindow::GetInterface(const nsIID & aIID, void **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))) {

View File

@ -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

View File

@ -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

View File

@ -7,12 +7,36 @@ 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 \
@ -22,4 +46,12 @@ 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)

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -7,7 +7,9 @@
#include "nsIDOMMozBrowserFrame.idl"
[scriptable, uuid(076AD76C-2DF6-4760-B914-21D554F0A2B6)]
interface nsITabParent;
[scriptable, uuid(0acd92dd-2902-48ee-adcf-082d3bb3ec45)]
interface nsIMozBrowserFrame : nsIDOMMozBrowserFrame
{
/**
@ -18,4 +20,28 @@ interface nsIMozBrowserFrame : nsIDOMMozBrowserFrame
* 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);
};

View File

@ -205,6 +205,21 @@ parent:
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:

View File

@ -77,9 +77,16 @@ rpc protocol PContent
manages PStorage;
manages PTestShell;
child:
PBrowser(PRUint32 chromeFlags);
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:
PMemoryReportRequest();
PTestShell();

View File

@ -1,5 +1,5 @@
/* -*- 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/. */
@ -93,6 +93,7 @@ TabChild::TabChild(PRUint32 aChromeFlags)
, mChromeFlags(aChromeFlags)
, mOuterRect(0, 0, 0, 0)
, mLastBackgroundColor(NS_RGB(255, 255, 255))
, mDidFakeShow(false)
{
printf("creating %d!\n", NS_IsMainThread());
}
@ -331,6 +332,28 @@ TabChild::ProvideWindow(nsIDOMWindow* aParent, PRUint32 aChromeFlags,
{
*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;
@ -343,6 +366,41 @@ TabChild::ProvideWindow(nsIDOMWindow* aParent, PRUint32 aChromeFlags,
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
@ -483,9 +541,20 @@ TabChild::RecvLoadURL(const nsCString& uri)
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);

View File

@ -246,6 +246,17 @@ private:
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;
@ -253,6 +264,7 @@ private:
PRUint32 mChromeFlags;
nsIntRect mOuterRect;
nscolor mLastBackgroundColor;
bool mDidFakeShow;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};

View File

@ -38,6 +38,7 @@
#include "mozilla/unused.h"
#include "nsDebug.h"
#include "nsPrintfCString.h"
#include "mozilla/BrowserElementParent.h"
#include "IndexedDBParent.h"
#include "IDBFactory.h"
@ -922,5 +923,18 @@ TabParent::GetWidget() const
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

View File

@ -52,7 +52,11 @@ public:
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,

View File

@ -77,6 +77,9 @@ interface nsIWindowProvider : nsISupports
* 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.
*/

View File

@ -593,6 +593,7 @@ nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
sizeSpec.SizeSpecified(),
uriToLoad, name, features, &windowIsNew,
getter_AddRefs(newWindow));
if (NS_SUCCEEDED(rv)) {
GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
if (windowIsNew && newDocShellItem) {
@ -605,6 +606,14 @@ nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
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;
}
}
}
}

View File

@ -80,6 +80,7 @@ SHARED_LIBRARY_LIBS = \
$(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) \

View File

@ -170,6 +170,7 @@
@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

View File

@ -176,6 +176,7 @@
@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

View File

@ -31,6 +31,7 @@
#include "nsCDefaultURIFixup.h"
#include "nsIWebNavigation.h"
#include "nsIJSContextStack.h"
#include "mozilla/BrowserElementParent.h"
#include "nsIDOMDocument.h"
#include "nsIScriptObjectPrincipal.h"
@ -837,6 +838,29 @@ nsContentTreeOwner::ProvideWindow(nsIDOMWindow* aParent,
"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",