Bug 1639328 - Make sure BrowserChilds for OOP iframes start in a consistent state. r=smaug

Right now they start with a FullyHidden() effect info, but with a
"visible" widget, and thus active docshell and so on.

That's no good :)

Differential Revision: https://phabricator.services.mozilla.com/D86364
This commit is contained in:
Emilio Cobos Álvarez 2020-08-11 16:42:46 +00:00
parent 8b95c9a06f
commit 6c008dd4fd
11 changed files with 123 additions and 23 deletions

View File

@ -6237,11 +6237,13 @@ already_AddRefed<PresShell> Document::CreatePresShell(
void Document::UpdateFrameRequestCallbackSchedulingState( void Document::UpdateFrameRequestCallbackSchedulingState(
PresShell* aOldPresShell) { PresShell* aOldPresShell) {
// If the condition for shouldBeScheduled changes to depend on some other // If this condition changes to depend on some other variable, make sure to
// variable, add UpdateFrameRequestCallbackSchedulingState() calls to the // call UpdateFrameRequestCallbackSchedulingState() calls to the places where
// places where that variable can change. // that variable can change. Also consider if you should change
bool shouldBeScheduled = mPresShell && IsEventHandlingEnabled() && // WouldScheduleFrameRequestCallbacks() instead of adding more stuff to this
!mFrameRequestCallbacks.IsEmpty(); // condition.
bool shouldBeScheduled =
WouldScheduleFrameRequestCallbacks() && !mFrameRequestCallbacks.IsEmpty();
if (shouldBeScheduled == mFrameRequestCallbacksScheduled) { if (shouldBeScheduled == mFrameRequestCallbacksScheduled) {
// nothing to do // nothing to do
return; return;
@ -6269,7 +6271,7 @@ void Document::TakeFrameRequestCallbacks(nsTArray<FrameRequest>& aCallbacks) {
mFrameRequestCallbacksScheduled = false; mFrameRequestCallbacksScheduled = false;
} }
bool Document::ShouldThrottleFrameRequests() { bool Document::ShouldThrottleFrameRequests() const {
if (mStaticCloneCount > 0) { if (mStaticCloneCount > 0) {
// Even if we're not visible, a static clone may be, so run at full speed. // Even if we're not visible, a static clone may be, so run at full speed.
return false; return false;

View File

@ -2682,10 +2682,17 @@ class Document : public nsINode,
uint32_t EventHandlingSuppressed() const { return mEventsSuppressed; } uint32_t EventHandlingSuppressed() const { return mEventsSuppressed; }
bool IsEventHandlingEnabled() { bool IsEventHandlingEnabled() const {
return !EventHandlingSuppressed() && mScriptGlobalObject; return !EventHandlingSuppressed() && mScriptGlobalObject;
} }
bool WouldScheduleFrameRequestCallbacks() const {
// If this function changes to depend on some other variable, make sure to
// call UpdateFrameRequestCallbackSchedulingState() calls to the places
// where that variable can change.
return mPresShell && IsEventHandlingEnabled();
}
void DecreaseEventSuppression() { void DecreaseEventSuppression() {
MOZ_ASSERT(mEventsSuppressed); MOZ_ASSERT(mEventsSuppressed);
--mEventsSuppressed; --mEventsSuppressed;
@ -3017,7 +3024,7 @@ class Document : public nsINode,
* throttled. We throttle requestAnimationFrame for documents which aren't * throttled. We throttle requestAnimationFrame for documents which aren't
* visible (e.g. scrolled out of the viewport). * visible (e.g. scrolled out of the viewport).
*/ */
bool ShouldThrottleFrameRequests(); bool ShouldThrottleFrameRequests() const;
// This returns true when the document tree is being teared down. // This returns true when the document tree is being teared down.
bool InUnlinkOrDeletion() { return mInUnlinkOrDeletion; } bool InUnlinkOrDeletion() { return mInUnlinkOrDeletion; }

View File

@ -4401,3 +4401,14 @@ nsDOMWindowUtils::GetWebrtcRawDeviceId(nsAString& aRawDeviceId) {
aRawDeviceId.AppendInt(rawDeviceId); aRawDeviceId.AppendInt(rawDeviceId);
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsDOMWindowUtils::GetEffectivelyThrottlesFrameRequests(bool* aResult) {
Document* doc = GetDocument();
if (!doc) {
return NS_ERROR_FAILURE;
}
*aResult = !doc->WouldScheduleFrameRequestCallbacks() ||
doc->ShouldThrottleFrameRequests();
return NS_OK;
}

View File

@ -0,0 +1,8 @@
<!doctype html>
<script>
onmessage = function(e) {
parent.postMessage({
throttledFrameRequests: SpecialPowers.DOMWindowUtils.effectivelyThrottlesFrameRequests,
}, e.origin);
};
</script>

View File

@ -305,6 +305,8 @@ skip-if = (os == "android" || headless) # See
skip-if = headless # fails in clipboard mode skip-if = headless # fails in clipboard mode
[test_bug166235.html] [test_bug166235.html]
skip-if = headless # headless != clipboard skip-if = headless # headless != clipboard
[test_bug1639328.html]
support-files = file_bug1639328.html
[test_bug199959.html] [test_bug199959.html]
[test_bug218236.html] [test_bug218236.html]
[test_bug218277.html] [test_bug218277.html]

View File

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Test for bug 1639328</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<style>
/* To ensure that they're all in the viewport when displayed */
iframe {
width: 10px;
height: 10px;
}
</style>
<iframe id="http" src="https://example.com/tests/dom/base/test/file_bug1639328.html"></iframe>
<iframe id="https" src="https://example.com/tests/dom/base/test/file_bug1639328.html"></iframe>
<iframe id="same-origin" src="file_bug1639328.html"></iframe>
<iframe id="display-none-http" style="display: none" src="https://example.com/tests/dom/base/test/file_bug1639328.html"></iframe>
<iframe id="display-none-https" style="display: none" src="https://example.com/tests/dom/base/test/file_bug1639328.html"></iframe>
<iframe id="display-none-same-origin" style="display: none" src="file_bug1639328.html"></iframe>
<script>
SimpleTest.waitForExplicitFinish();
function getOneMessage(frame) {
info("querying " + frame.src);
let resolve;
let promise = new Promise(r => { resolve = r; });
window.addEventListener("message", function(e) {
info("got " + JSON.stringify(e.data));
resolve(e.data);
}, { once: true });
frame.contentWindow.postMessage("ping", "*");
return promise;
}
async function ticks(n) {
for (let i = 0; i < n; ++i) {
await new Promise(resolve => requestAnimationFrame(resolve));
}
}
async function checkFrame(frame, shouldThrottle) {
let message = await getOneMessage(frame);
is(message.throttledFrameRequests, shouldThrottle, frame.id);
}
onload = async function() {
await SimpleTest.promiseFocus(window);
await ticks(2);
is(SpecialPowers.DOMWindowUtils.effectivelyThrottlesFrameRequests, false, "Should not be throttling main page");
for (let frame of document.querySelectorAll("iframe")) {
let shouldThrottle = frame.style.display == "none";
await checkFrame(frame, shouldThrottle);
info("Switching display of " + frame.id);
frame.style.display = shouldThrottle ? "" : "none";
await ticks(2);
await checkFrame(frame, !shouldThrottle);
info("And switching display back for " + frame.id);
frame.style.display = shouldThrottle ? "none" : "";
await ticks(2);
await checkFrame(frame, shouldThrottle);
}
SimpleTest.finish();
};
</script>

View File

@ -2060,6 +2060,9 @@ interface nsIDOMWindowUtils : nsISupports {
// Returns true if we are using overlay scrollbars. // Returns true if we are using overlay scrollbars.
readonly attribute bool usesOverlayScrollbars; readonly attribute bool usesOverlayScrollbars;
// Returns true if we are effectively throttling frame requests.
readonly attribute bool effectivelyThrottlesFrameRequests;
// Returns the ID for the underlying window widget, which can // Returns the ID for the underlying window widget, which can
// be compared against the rawId from a nsIMediaDevice to determine // be compared against the rawId from a nsIMediaDevice to determine
// if the window is being shared. // if the window is being shared.

View File

@ -524,8 +524,7 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
// frames. // frames.
if (mIsTopLevel) { if (mIsTopLevel) {
nsContentUtils::SetScrollbarsVisibility( nsContentUtils::SetScrollbarsVisibility(
window->GetDocShell(), docShell, !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS));
!!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS));
} }
nsWeakPtr weakPtrThis = do_GetWeakReference( nsWeakPtr weakPtrThis = do_GetWeakReference(
@ -548,6 +547,11 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
rv = mSessionStoreListener->Init(); rv = mSessionStoreListener->Init();
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
#endif #endif
// We've all set up, make sure our visibility state is consistent. This is
// important for OOP iframes, which start off as hidden.
UpdateVisibility();
return NS_OK; return NS_OK;
} }

View File

@ -189,8 +189,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
dom::BrowsingContext* aBrowsingContext, uint32_t aChromeFlags, dom::BrowsingContext* aBrowsingContext, uint32_t aChromeFlags,
bool aIsTopLevel); bool aIsTopLevel);
nsresult Init(mozIDOMWindowProxy* aParent, MOZ_CAN_RUN_SCRIPT nsresult Init(mozIDOMWindowProxy* aParent,
WindowGlobalChild* aInitialWindowChild); WindowGlobalChild* aInitialWindowChild);
/** Return a BrowserChild with the given attributes. */ /** Return a BrowserChild with the given attributes. */
static already_AddRefed<BrowserChild> Create( static already_AddRefed<BrowserChild> Create(

View File

@ -1014,7 +1014,8 @@ nsresult ContentChild::ProvideWindowCommon(
// Now that |newChild| has had its IPC link established, call |Init| to set it // Now that |newChild| has had its IPC link established, call |Init| to set it
// up. // up.
nsPIDOMWindowOuter* parentWindow = parent ? parent->GetDOMWindow() : nullptr; RefPtr<nsPIDOMWindowOuter> parentWindow =
parent ? parent->GetDOMWindow() : nullptr;
if (NS_FAILED(newChild->Init(parentWindow, windowChild))) { if (NS_FAILED(newChild->Init(parentWindow, windowChild))) {
return NS_ERROR_ABORT; return NS_ERROR_ABORT;
} }

View File

@ -104,15 +104,13 @@ class ContentChild final : public PContentChild,
nsCString updateURL; nsCString updateURL;
}; };
nsresult ProvideWindowCommon(BrowserChild* aTabOpener, MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult ProvideWindowCommon(
nsIOpenWindowInfo* aOpenWindowInfo, BrowserChild* aTabOpener, nsIOpenWindowInfo* aOpenWindowInfo,
uint32_t aChromeFlags, bool aCalledFromJS, uint32_t aChromeFlags, bool aCalledFromJS, bool aWidthSpecified,
bool aWidthSpecified, nsIURI* aURI, nsIURI* aURI, const nsAString& aName, const nsACString& aFeatures,
const nsAString& aName, bool aForceNoOpener, bool aForceNoReferrer,
const nsACString& aFeatures, bool aForceNoOpener, nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
bool aForceNoReferrer, BrowsingContext** aReturn);
nsDocShellLoadState* aLoadState,
bool* aWindowIsNew, BrowsingContext** aReturn);
bool Init(MessageLoop* aIOLoop, base::ProcessId aParentPid, bool Init(MessageLoop* aIOLoop, base::ProcessId aParentPid,
const char* aParentBuildID, UniquePtr<IPC::Channel> aChannel, const char* aParentBuildID, UniquePtr<IPC::Channel> aChannel,
@ -493,7 +491,7 @@ class ContentChild final : public PContentChild,
bool DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*); bool DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*);
mozilla::ipc::IPCResult RecvConstructBrowser( MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvConstructBrowser(
ManagedEndpoint<PBrowserChild>&& aBrowserEp, ManagedEndpoint<PBrowserChild>&& aBrowserEp,
ManagedEndpoint<PWindowGlobalChild>&& aWindowEp, const TabId& aTabId, ManagedEndpoint<PWindowGlobalChild>&& aWindowEp, const TabId& aTabId,
const IPCTabContext& aContext, const WindowGlobalInit& aWindowInit, const IPCTabContext& aContext, const WindowGlobalInit& aWindowInit,