Bug 826325 - Handle preventDefault on mozbrowserwindowopen events r=bz

This commit is contained in:
Wes Johnston 2013-09-09 21:18:46 -07:00
Родитель 437889b715
Коммит 7dbc6a3357
8 изменённых файлов: 121 добавлений и 45 удалений

Просмотреть файл

@ -27,6 +27,7 @@ using namespace mozilla::dom;
namespace {
using mozilla::BrowserElementParent;
/**
* Create an <iframe mozbrowser> owned by the same document as
* aOpenerFrameElement.
@ -72,7 +73,8 @@ CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote)
bool
DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
JSContext* cx, JS::Handle<JS::Value> aDetailValue)
JSContext* cx, JS::Handle<JS::Value> aDetailValue,
nsEventStatus *aStatus)
{
NS_ENSURE_TRUE(aFrameElement, false);
nsIPresShell *shell = aFrameElement->OwnerDoc()->GetShell();
@ -94,7 +96,7 @@ DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
event->InitCustomEvent(cx,
aEventName,
/* bubbles = */ true,
/* cancelable = */ false,
/* cancelable = */ true,
aDetailValue,
res);
if (res.Failed()) {
@ -102,12 +104,16 @@ DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
}
customEvent->SetTrusted(true);
// Dispatch the event.
nsEventStatus status = nsEventStatus_eIgnore;
*aStatus = nsEventStatus_eConsumeNoDefault;
nsresult rv = nsEventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
domEvent, presContext, &status);
domEvent, presContext, aStatus);
return NS_SUCCEEDED(rv);
}
} // anonymous namespace
namespace mozilla {
/**
* Dispatch a mozbrowseropenwindow event to the given opener frame element.
* The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|.
@ -115,8 +121,9 @@ DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
* Returns true iff there were no unexpected failures and the window.open call
* was accepted by the embedder.
*/
bool
DispatchOpenWindowEvent(Element* aOpenerFrameElement,
/*static*/
BrowserElementParent::OpenWindowResult
BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
Element* aPopupFrameElement,
const nsAString& aURL,
const nsAString& aName,
@ -138,32 +145,38 @@ DispatchOpenWindowEvent(Element* aOpenerFrameElement,
nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject();
if (!sgo) {
return false;
return BrowserElementParent::OPEN_WINDOW_IGNORED;
}
JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
JSAutoCompartment ac(cx, global);
if (!detail.ToObject(cx, global, &val)) {
MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
return false;
return BrowserElementParent::OPEN_WINDOW_IGNORED;
}
nsEventStatus status;
bool dispatchSucceeded =
DispatchCustomDOMEvent(aOpenerFrameElement,
NS_LITERAL_STRING("mozbrowseropenwindow"),
cx,
val);
val, &status);
// If the iframe is not in some document's DOM at this point, the embedder
// has "blocked" the popup.
return (dispatchSucceeded && aPopupFrameElement->IsInDoc());
if (dispatchSucceeded) {
if (aPopupFrameElement->IsInDoc()) {
return BrowserElementParent::OPEN_WINDOW_ADDED;
} else if (status == nsEventStatus_eConsumeNoDefault) {
// If the frame was not added to a document, report to callers whether
// preventDefault was called on or not
return BrowserElementParent::OPEN_WINDOW_CANCELLED;
}
}
return BrowserElementParent::OPEN_WINDOW_IGNORED;
}
} // anonymous namespace
namespace mozilla {
/*static*/ bool
/*static*/
BrowserElementParent::OpenWindowResult
BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
TabParent* aPopupTabParent,
const nsAString& aURL,
@ -172,7 +185,8 @@ BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
{
// Create an iframe owned by the same document which owns openerFrameElement.
nsCOMPtr<Element> openerFrameElement = aOpenerTabParent->GetOwnerElement();
NS_ENSURE_TRUE(openerFrameElement, false);
NS_ENSURE_TRUE(openerFrameElement,
BrowserElementParent::OPEN_WINDOW_IGNORED);
nsRefPtr<HTMLIFrameElement> popupFrameElement =
CreateIframe(openerFrameElement, aName, /* aRemote = */ true);
@ -188,11 +202,12 @@ BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
// allowed.
popupFrameElement->DisallowCreateFrameLoader();
bool dispatchSucceeded =
OpenWindowResult opened =
DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
aURL, aName, aFeatures);
if (!dispatchSucceeded) {
return false;
if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
return opened;
}
// The popup was not blocked, so hook up the frame element and the popup tab
@ -200,11 +215,11 @@ BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
aPopupTabParent->SetOwnerElement(popupFrameElement);
popupFrameElement->AllowCreateFrameLoader();
popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent);
return true;
return opened;
}
/* static */ bool
/* static */
BrowserElementParent::OpenWindowResult
BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
nsIURI* aURI,
const nsAString& aName,
@ -226,40 +241,43 @@ BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
nsCOMPtr<nsIDOMElement> openerFrameDOMElement;
topWindow->GetFrameElement(getter_AddRefs(openerFrameDOMElement));
NS_ENSURE_TRUE(openerFrameDOMElement, false);
NS_ENSURE_TRUE(openerFrameDOMElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
nsCOMPtr<Element> openerFrameElement =
do_QueryInterface(openerFrameDOMElement);
nsRefPtr<HTMLIFrameElement> popupFrameElement =
CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
NS_ENSURE_TRUE(popupFrameElement, false);
NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
nsAutoCString spec;
if (aURI) {
aURI->GetSpec(spec);
}
bool dispatchSucceeded =
OpenWindowResult opened =
DispatchOpenWindowEvent(openerFrameElement, popupFrameElement,
NS_ConvertUTF8toUTF16(spec),
aName,
NS_ConvertUTF8toUTF16(aFeatures));
if (!dispatchSucceeded) {
return false;
if (opened != BrowserElementParent::OPEN_WINDOW_ADDED) {
return opened;
}
// Return popupFrameElement's window.
nsCOMPtr<nsIFrameLoader> frameLoader;
popupFrameElement->GetFrameLoader(getter_AddRefs(frameLoader));
NS_ENSURE_TRUE(frameLoader, false);
NS_ENSURE_TRUE(frameLoader, BrowserElementParent::OPEN_WINDOW_IGNORED);
nsCOMPtr<nsIDocShell> docshell;
frameLoader->GetDocShell(getter_AddRefs(docshell));
NS_ENSURE_TRUE(docshell, false);
NS_ENSURE_TRUE(docshell, BrowserElementParent::OPEN_WINDOW_IGNORED);
nsCOMPtr<nsIDOMWindow> window = do_GetInterface(docshell);
window.forget(aReturnWindow);
return !!*aReturnWindow;
return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED;
}
class DispatchAsyncScrollEventRunnable : public nsRunnable
@ -302,10 +320,11 @@ NS_IMETHODIMP DispatchAsyncScrollEventRunnable::Run()
return NS_ERROR_FAILURE;
}
nsEventStatus status = nsEventStatus_eIgnore;
DispatchCustomDOMEvent(frameElement,
NS_LITERAL_STRING("mozbrowserasyncscroll"),
cx,
val);
val, &status);
return NS_OK;
}

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/gfx/Point.h"
#include "mozilla/gfx/Rect.h"
#include "Units.h"
#include "mozilla/dom/Element.h"
class nsIDOMWindow;
class nsIURI;
@ -33,6 +34,22 @@ class TabParent;
class BrowserElementParent
{
public:
/**
* Possible results from a window.open call.
* ADDED - The frame was added to a document (i.e. handled by the embedder).
* IGNORED - The frame was not added to a document and the embedder didn't
* call preventDefault() to prevent the platform from handling the call.
* CANCELLED - The frame was not added to a document, but the embedder still
* called preventDefault() to prevent the platform from handling the call.
*/
enum OpenWindowResult {
OPEN_WINDOW_ADDED,
OPEN_WINDOW_IGNORED,
OPEN_WINDOW_CANCELLED
};
/**
* Handle a window.open call from an out-of-process <iframe mozbrowser>.
*
@ -61,11 +78,11 @@ public:
* @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.
* @return an OpenWindowresult that describes whether the embedder added the
* frame to a document and whether it called preventDefault to prevent
* the platform from handling the open request.
*/
static bool
static OpenWindowResult
OpenWindowOOP(dom::TabParent* aOpenerTabParent,
dom::TabParent* aPopupTabParent,
const nsAString& aURL,
@ -75,15 +92,15 @@ public:
/**
* 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.)
*
* @param aURI the URI the new window should load. May be null.
* @return an OpenWindowResult that describes whether the browser added the
* frame to a document or whether they called preventDefault to prevent
* the platform from handling the open request
*/
static bool
static OpenWindowResult
OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
nsIURI* aURI,
const nsAString& aName,
@ -109,6 +126,14 @@ public:
DispatchAsyncScrollEvent(dom::TabParent* aTabParent,
const CSSRect& aContentRect,
const CSSSize& aContentSize);
private:
static OpenWindowResult
DispatchOpenWindowEvent(dom::Element* aOpenerFrameElement,
dom::Element* aPopupFrameElement,
const nsAString& aURL,
const nsAString& aName,
const nsAString& aFeatures);
};
} // namespace mozilla

Просмотреть файл

@ -10,6 +10,7 @@ include $(topsrcdir)/ipc/chromium/chromium-config.mk
INCLUDES += \
-I$(topsrcdir)/dom/base \
-I$(topsrcdir)/dom/ \
-I$(topsrcdir)/dom/ipc \
-I$(topsrcdir)/content/base/src \
$(NULL)

Просмотреть файл

@ -32,6 +32,9 @@ function runTest() {
iframe2.addEventListener('mozbrowseropenwindow', function(e) {
ok(true, "Got second mozbrowseropenwindow event.");
SpecialPowers.removePermission("embed-apps", document);
// We're not going to open this, but we don't want the platform to either
e.preventDefault();
SimpleTest.finish();
});

Просмотреть файл

@ -21,6 +21,7 @@ function runTest() {
// Don't add e.detail.frameElement to the DOM, so the window.open is
// effectively blocked.
e.preventDefault();
});
iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {

Просмотреть файл

@ -15,6 +15,7 @@ function runTest() {
iframe.addEventListener('mozbrowseropenwindow', function(e) {
is(e.detail.url, 'http://example.com/');
e.preventDefault();
SimpleTest.finish();
});

Просмотреть файл

@ -1524,9 +1524,10 @@ TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
const nsString& aFeatures,
bool* aOutWindowOpened)
{
*aOutWindowOpened =
BrowserElementParent::OpenWindowResult opened =
BrowserElementParent::OpenWindowOOP(static_cast<TabParent*>(aOpener),
this, aURL, aName, aFeatures);
*aOutWindowOpened = (opened != BrowserElementParent::OPEN_WINDOW_CANCELLED);
return true;
}

Просмотреть файл

@ -31,6 +31,9 @@
#include "nsIURIFixup.h"
#include "nsCDefaultURIFixup.h"
#include "nsIWebNavigation.h"
#include "nsDocShellCID.h"
#include "nsIExternalURLHandlerService.h"
#include "nsIMIMEInfo.h"
#include "mozilla/BrowserElementParent.h"
#include "nsIDOMDocument.h"
@ -832,14 +835,36 @@ nsContentTreeOwner::ProvideWindow(nsIDOMWindow* aParent,
!(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
*aWindowIsNew =
BrowserElementParent::OpenWindowResult opened =
BrowserElementParent::OpenWindowInProcess(aParent, aURI, aName,
aFeatures, aReturn);
// If OpenWindowInProcess failed (perhaps because the embedder blocked the
// If OpenWindowInProcess handled the open (by opening it or blocking the
// popup), tell our caller not to proceed trying to create a new window
// through other means.
return *aWindowIsNew ? NS_OK : NS_ERROR_ABORT;
if (opened != BrowserElementParent::OPEN_WINDOW_IGNORED) {
*aWindowIsNew = opened == BrowserElementParent::OPEN_WINDOW_ADDED;
return *aWindowIsNew ? NS_OK : NS_ERROR_ABORT;
}
// If we're in an app and the target is _blank, send the url to the OS
if (aName.LowerCaseEqualsLiteral("_blank")) {
nsCOMPtr<nsIExternalURLHandlerService> exUrlServ(
do_GetService(NS_EXTERNALURLHANDLERSERVICE_CONTRACTID));
if (exUrlServ) {
nsCOMPtr<nsIHandlerInfo> info;
bool found;
exUrlServ->GetURLHandlerInfoFromOS(aURI, &found, getter_AddRefs(info));
if (info && found) {
info->LaunchWithURI(aURI, nullptr);
return NS_ERROR_ABORT;
}
}
}
}
// the parent window is fullscreen mode or not.