зеркало из https://github.com/mozilla/gecko-dev.git
Bug 826325 - Handle preventDefault on mozbrowserwindowopen events r=bz
This commit is contained in:
Родитель
437889b715
Коммит
7dbc6a3357
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче