author | Wes Johnston <wjohnston@mozilla.com> |
Mon, 09 Sep 2013 21:18:46 -0700 | |
changeset 146396 | bd53e981282f9ed59ca35ce59776c2f94e9f6e7d |
parent 146317 | ea33604f6232a07d3e4be77718226d569490407f |
child 146397 | 32e968bcba07e2296ea6ab9f9a7c20a939f5b025 |
push id | 25260 |
push user | ryanvm@gmail.com |
push date | Wed, 11 Sep 2013 00:29:30 +0000 |
treeherder | mozilla-central@f73bed2856a8 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bz |
bugs | 826325 |
milestone | 26.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
|
--- a/dom/browser-element/BrowserElementParent.cpp +++ b/dom/browser-element/BrowserElementParent.cpp @@ -22,16 +22,17 @@ #include "nsCxPusher.h" #include "GeneratedEventClasses.h" using namespace mozilla; using namespace mozilla::dom; namespace { +using mozilla::BrowserElementParent; /** * Create an <iframe mozbrowser> owned by the same document as * aOpenerFrameElement. */ already_AddRefed<HTMLIFrameElement> CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote) { nsNodeInfoManager *nodeInfoManager = @@ -67,17 +68,18 @@ CreateIframe(Element* aOpenerFrameElemen NS_LITERAL_STRING("false"), /* aNotify = */ false); return popupFrameElement.forget(); } 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(); nsRefPtr<nsPresContext> presContext; if (shell) { presContext = shell->GetPresContext(); } @@ -89,39 +91,44 @@ DispatchCustomDOMEvent(Element* aFrameEl nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent); NS_ENSURE_TRUE(customEvent, false); ErrorResult res; CustomEvent* event = static_cast<CustomEvent*>(customEvent.get()); event->InitCustomEvent(cx, aEventName, /* bubbles = */ true, - /* cancelable = */ false, + /* cancelable = */ true, aDetailValue, res); if (res.Failed()) { return false; } 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|. * * 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, const nsAString& aFeatures) { // Dispatch a CustomEvent at aOpenerFrameElement with a detail object // (OpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and // aFeatures. @@ -133,83 +140,91 @@ DispatchOpenWindowEvent(Element* aOpener detail.mFeatures = aFeatures; detail.mFrameElement = aPopupFrameElement; AutoJSContext cx; JS::Rooted<JS::Value> val(cx); 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, const nsAString& aName, const nsAString& aFeatures) { // 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); // 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 = + 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 // parent, and return success. 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, const nsACString& aFeatures, nsIDOMWindow** aReturnWindow) { *aReturnWindow = NULL; @@ -221,50 +236,53 @@ BrowserElementParent::OpenWindowInProces // GetScriptableTop gets us the <iframe mozbrowser>'s window; we'll use its // frame element, rather than aOpenerWindow's frame element, as our "opener // frame element" below. nsCOMPtr<nsIDOMWindow> topWindow; aOpenerWindow->GetScriptableTop(getter_AddRefs(topWindow)); 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 { public: DispatchAsyncScrollEventRunnable(TabParent* aTabParent, const CSSRect& aContentRect, const CSSSize& aContentSize) @@ -297,20 +315,21 @@ NS_IMETHODIMP DispatchAsyncScrollEventRu // We can get away with a null global here because // AsyncScrollEventDetail only contains numeric values. if (!detail.ToObject(cx, JS::NullPtr(), &val)) { MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM."); return NS_ERROR_FAILURE; } + nsEventStatus status = nsEventStatus_eIgnore; DispatchCustomDOMEvent(frameElement, NS_LITERAL_STRING("mozbrowserasyncscroll"), cx, - val); + val, &status); return NS_OK; } bool BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent, const CSSRect& aContentRect, const CSSSize& aContentSize) {
--- a/dom/browser-element/BrowserElementParent.h +++ b/dom/browser-element/BrowserElementParent.h @@ -4,16 +4,17 @@ #ifndef mozilla_BrowserElementHelpers_h #define mozilla_BrowserElementHelpers_h #include "nsAString.h" #include "mozilla/gfx/Point.h" #include "mozilla/gfx/Rect.h" #include "Units.h" +#include "mozilla/dom/Element.h" class nsIDOMWindow; class nsIURI; namespace mozilla { namespace dom { class TabParent; @@ -28,16 +29,32 @@ class TabParent; * 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: + + /** + * 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>. * * 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. @@ -56,39 +73,39 @@ public: * set aPopupTabParent's frame element to event.detail.frameElement. * Otherwise, we return false. * * @param aURL the URL the new window should load. The empty string is * allowed. * @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, 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.) * * @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, const nsACString& aFeatures, nsIDOMWindow** aReturnWindow); /** * Fire a mozbrowserasyncscroll CustomEvent on the given TabParent's frame element. @@ -104,13 +121,21 @@ public: * page "rubber-bands" after being scrolled all the way to the bottom. * Similarly, aContentRect.left + aContentRect.width may be greater than * contentSize.width, and both left and top may be negative. */ static bool 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 #endif
--- a/dom/browser-element/Makefile.in +++ b/dom/browser-element/Makefile.in @@ -5,11 +5,12 @@ include $(topsrcdir)/dom/dom-config.mk include $(topsrcdir)/config/rules.mk 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)
--- a/dom/browser-element/mochitest/browserElement_AppWindowNamespace.js +++ b/dom/browser-element/mochitest/browserElement_AppWindowNamespace.js @@ -27,16 +27,19 @@ function runTest() { SimpleTest.executeSoon(function() { var iframe2 = document.createElement('iframe'); SpecialPowers.wrap(iframe2).mozbrowser = true; iframe2.setAttribute('mozapp', 'http://example.com/manifest.webapp'); 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(); }); document.body.appendChild(iframe2); iframe2.src = 'http://example.com/tests/dom/browser-element/mochitest/file_browserElement_AppWindowNamespace.html'; }); });
--- a/dom/browser-element/mochitest/browserElement_OpenWindowRejected.js +++ b/dom/browser-element/mochitest/browserElement_OpenWindowRejected.js @@ -16,16 +16,17 @@ function runTest() { iframe.addEventListener('mozbrowseropenwindow', function(e) { ok(e.detail.url.indexOf('does_not_exist.html') != -1, 'Opened URL; got ' + e.detail.url); is(e.detail.name, ''); is(e.detail.features, ''); // Don't add e.detail.frameElement to the DOM, so the window.open is // effectively blocked. + e.preventDefault(); }); iframe.addEventListener('mozbrowsershowmodalprompt', function(e) { var msg = e.detail.message; if (msg.indexOf('success:') == 0) { ok(true, msg); } else if (msg == 'finish') {
--- a/dom/browser-element/mochitest/browserElement_TargetBlank.js +++ b/dom/browser-element/mochitest/browserElement_TargetBlank.js @@ -10,16 +10,17 @@ browserElementTestHelpers.setEnabledPref browserElementTestHelpers.addPermission(); function runTest() { var iframe = document.createElement('iframe'); SpecialPowers.wrap(iframe).mozbrowser = true; iframe.addEventListener('mozbrowseropenwindow', function(e) { is(e.detail.url, 'http://example.com/'); + e.preventDefault(); SimpleTest.finish(); }); iframe.src = "file_browserElement_TargetBlank.html"; document.body.appendChild(iframe); } addEventListener('testready', runTest);
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1519,19 +1519,20 @@ TabParent::MaybeForwardEventToRenderFram bool TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener, const nsString& aURL, const nsString& aName, 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; } bool TabParent::RecvPRenderFrameConstructor(PRenderFrameParent* actor, ScrollingBehavior* scrolling, TextureFactoryIdentifier* factoryIdentifier, uint64_t* layersId)
--- a/xpfe/appshell/src/nsContentTreeOwner.cpp +++ b/xpfe/appshell/src/nsContentTreeOwner.cpp @@ -26,16 +26,19 @@ #include "nsIPrompt.h" #include "nsIAuthPrompt.h" #include "nsIWindowMediator.h" #include "nsIXULBrowserWindow.h" #include "nsIPrincipal.h" #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" #include "nsIScriptObjectPrincipal.h" #include "nsIURI.h" #if defined(XP_MACOSX) #include "nsThreadUtils.h" #endif @@ -827,24 +830,46 @@ nsContentTreeOwner::ProvideWindow(nsIDOM // 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); if (docshell && docshell->GetIsInBrowserOrApp() && !(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. bool isFullScreen = false; if (aParent) { aParent->GetFullScreen(&isFullScreen); }