Bug 1451455 - Fire the 'load' event on out-of-process iframes, r=rhunt

This is done by sending a message over PBrowser and PBrowserBridge when the
event would fire to fire it in the correct process.

Differential Revision: https://phabricator.services.mozilla.com/D33083

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Nika Layzell 2019-06-06 14:57:17 +00:00
Родитель 6abebf8c22
Коммит b5b7f6b728
11 изменённых файлов: 112 добавлений и 20 удалений

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

@ -261,6 +261,7 @@
#include "mozilla/dom/ClientState.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/net/CookieSettings.h"
@ -1917,6 +1918,54 @@ bool nsGlobalWindowInner::DialogsAreBeingAbused() {
return false;
}
void nsGlobalWindowInner::FireFrameLoadEvent(bool aIsTrusted) {
// If we're not in a content frame, or are at a BrowsingContext tree boundary,
// such as the content-chrome boundary, don't fire the "load" event.
if (!GetBrowsingContext()->GetParent() ||
!GetBrowsingContext()->IsContent()) {
return;
}
// If embedder is same-process, fire the event on our embedder element.
//
// XXX: Bug 1440212 is looking into potentially changing this behaviour to act
// more like the remote case when in-process.
RefPtr<Element> element = GetBrowsingContext()->GetEmbedderElement();
if (element) {
nsEventStatus status = nsEventStatus_eIgnore;
WidgetEvent event(aIsTrusted, eLoad);
event.mFlags.mBubbles = false;
event.mFlags.mCancelable = false;
// Most of the time we could get a pres context to pass in here,
// but not always (i.e. if this window is not shown there won't
// be a pres context available). Since we're not firing a GUI
// event we don't need a pres context anyway so we just pass
// null as the pres context all the time here.
EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
return;
}
// We don't have an in-process embedder. Try to get our `BrowserChild` actor
// to send a message to that embedder. We want to double-check that our outer
// window is actually the one at the root of this browserChild though, just in
// case.
RefPtr<BrowserChild> browserChild =
BrowserChild::GetFrom(static_cast<nsPIDOMWindowInner*>(this));
if (browserChild) {
// Double-check that our outer window is actually at the root of this
// `BrowserChild`, in case we're in an odd maybe-unhosted situation like a
// print preview dialog.
nsCOMPtr<nsPIDOMWindowOuter> rootOuter =
do_GetInterface(browserChild->WebNavigation());
if (!rootOuter || rootOuter != GetOuterWindow()) {
return;
}
mozilla::Unused << browserChild->SendFireFrameLoadEvent(aIsTrusted);
}
}
nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) {
// Return early if there is nothing to do.
switch (aVisitor.mEvent->mMessage) {
@ -1977,25 +2026,7 @@ nsresult nsGlobalWindowInner::PostHandleEvent(EventChainPostVisitor& aVisitor) {
mTimeoutManager->OnDocumentLoaded();
nsCOMPtr<Element> element = GetOuterWindow()->GetFrameElementInternal();
nsIDocShell* docShell = GetDocShell();
if (element && GetParentInternal() && docShell &&
docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
// If we're not in chrome, or at a chrome boundary, fire the
// onload event for the frame element.
nsEventStatus status = nsEventStatus_eIgnore;
WidgetEvent event(aVisitor.mEvent->IsTrusted(), eLoad);
event.mFlags.mBubbles = false;
event.mFlags.mCancelable = false;
// Most of the time we could get a pres context to pass in here,
// but not always (i.e. if this window is not shown there won't
// be a pres context available). Since we're not firing a GUI
// event we don't need a pres context anyway so we just pass
// null as the pres context all the time here.
EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
}
FireFrameLoadEvent(aVisitor.mEvent->IsTrusted());
if (mVREventObserver) {
mVREventObserver->NotifyAfterLoad();
@ -7083,6 +7114,7 @@ nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow)
mNumOfOpenWebSockets(0),
mEvent(nullptr) {
MOZ_ASSERT(aOuterWindow);
mBrowsingContext = aOuterWindow->GetBrowsingContext();
}
void nsPIDOMWindowInner::RegisterReportingObserver(ReportingObserver* aObserver,

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

@ -1197,6 +1197,9 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
// activation flag.
bool ShouldResetBrowsingContextUserGestureActivation();
// Try to fire the "load" event on our content embedder if we're an iframe.
void FireFrameLoadEvent(bool aIsTrusted);
public:
// Dispatch a runnable related to the global.
virtual nsresult Dispatch(mozilla::TaskCategory aCategory,

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

@ -388,6 +388,11 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
*/
inline nsIDocShell* GetDocShell() const;
/**
* Get the browsing context in this window.
*/
inline mozilla::dom::BrowsingContext* GetBrowsingContext() const;
/**
* Call this to indicate that some node (this window, its document,
* or content in that document) has a paint event listener.
@ -627,6 +632,8 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
RefPtr<mozilla::dom::TabGroup> mTabGroup;
RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
// A unique (as long as our 64-bit counter doesn't roll over) id for
// this window.
uint64_t mWindowID;

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

@ -72,6 +72,10 @@ mozilla::dom::BrowsingContext* nsPIDOMWindowOuter::GetBrowsingContext() const {
return mBrowsingContext;
}
mozilla::dom::BrowsingContext* nsPIDOMWindowInner::GetBrowsingContext() const {
return mBrowsingContext;
}
mozilla::dom::Element* nsPIDOMWindowOuter::GetFocusedElement() const {
return mInnerWindow ? mInnerWindow->GetFocusedElement() : nullptr;
}

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

@ -44,10 +44,14 @@
iframe.setAttribute("ignoreuserfocus", "true");
iframe.setAttribute("height", "300px");
iframe.addEventListener('load', function (e) {
iframe.addEventListener('mozbrowserloadend', function (e) {
// Get privileged iframe because mozbrowser iframe is not same origin
// with the parent. We need to access its content through the wrapper.
var privilegedIframe = SpecialPowers.wrap(iframe);
if (!privilegedIframe.contentWindow.location.href.endsWith("file_ignoreuserfocus.html")) {
return;
}
privilegedIframe.contentWindow.addEventListener("MozAfterPaint", function(e) {
privilegedIframe.contentWindow.addEventListener("focus",
function(e) {

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

@ -140,6 +140,23 @@ BrowserBridgeChild::RecvSetEmbeddedDocAccessibleCOMProxy(
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserBridgeChild::RecvFireFrameLoadEvent(
bool aIsTrusted) {
RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
if (!owner) {
return IPC_OK();
}
// Fire the `load` event on our embedder element.
nsEventStatus status = nsEventStatus_eIgnore;
WidgetEvent event(aIsTrusted, eLoad);
event.mFlags.mBubbles = false;
event.mFlags.mCancelable = false;
EventDispatcher::Dispatch(owner, nullptr, &event, nullptr, &status);
return IPC_OK();
}
void BrowserBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
mIPCOpen = false;
}

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

@ -83,6 +83,8 @@ class BrowserBridgeChild : public PBrowserBridgeChild {
mozilla::ipc::IPCResult RecvSetEmbeddedDocAccessibleCOMProxy(
const IDispatchHolder& aCOMProxy);
mozilla::ipc::IPCResult RecvFireFrameLoadEvent(bool aIsTrusted);
void ActorDestroy(ActorDestroyReason aWhy) override;
private:

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

@ -3699,6 +3699,17 @@ mozilla::ipc::IPCResult BrowserParent::RecvGetSystemFont(nsCString* aFontName) {
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserParent::RecvFireFrameLoadEvent(bool aIsTrusted) {
BrowserBridgeParent* bridge = GetBrowserBridgeParent();
if (!bridge) {
NS_WARNING("Received `load` event on unbridged BrowserParent!");
return IPC_OK();
}
Unused << bridge->SendFireFrameLoadEvent(aIsTrusted);
return IPC_OK();
}
NS_IMETHODIMP
FakeChannel::OnAuthAvailable(nsISupports* aContext,
nsIAuthInformation* aAuthInfo) {

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

@ -765,6 +765,8 @@ class BrowserParent final : public PBrowserParent,
mozilla::ipc::IPCResult RecvQueryVisitedState(
InfallibleTArray<URIParams>&& aURIs);
mozilla::ipc::IPCResult RecvFireFrameLoadEvent(bool aIsTrusted);
private:
void SuppressDisplayport(bool aEnabled);

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

@ -663,6 +663,11 @@ parent:
sync SetPrefersReducedMotionOverrideForTest(bool aValue);
sync ResetPrefersReducedMotionOverrideForTest();
/**
* Fire a `load` event on this PBrowser's embedding frame element.
*/
async FireFrameLoadEvent(bool aIsTrusted);
child:
/**
* Notify the remote browser that it has been Show()n on this

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

@ -51,6 +51,11 @@ child:
*/
async SetEmbeddedDocAccessibleCOMProxy(IDispatchHolder aCOMProxy);
/**
* Fire a `load` event on this PBrowserBridge's embedding frame element.
*/
async FireFrameLoadEvent(bool aIsTrusted);
parent:
// Destroy the remote web browser due to the nsFrameLoader going away.
async __delete__();