Bug 1688004 - Build top layer items for XUL documents that don't have scrollframes. r=miko

This will allow the browser chrome to use `<dialog>` etc, see
bug 1685313.

Tweak the check to support scrolling="false" on reftests, so that we can
test this.

Differential Revision: https://phabricator.services.mozilla.com/D102623
This commit is contained in:
Emilio Cobos Álvarez 2021-01-22 14:44:17 +00:00
parent bc32435029
commit 6a72aab71b
6 changed files with 108 additions and 77 deletions

View File

@ -2565,7 +2565,7 @@ void nsCSSFrameConstructor::SetUpDocElementContainingBlock(
isScrollable = presContext->HasPaginatedScrolling();
} else if (isXUL) {
isScrollable = false;
} else if (nsContentUtils::IsInChromeDocshell(aDocElement->OwnerDoc()) &&
} else if (aDocElement->OwnerDoc()->AllowXULXBL() &&
aDocElement->AsElement()->AttrValueIs(
kNameSpaceID_None, nsGkAtoms::scrolling, nsGkAtoms::_false,
eCaseMatters)) {

View File

@ -16,9 +16,10 @@
#include "mozilla/RestyleManager.h"
#include "nsGkAtoms.h"
#include "nsIScrollableFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsCanvasFrame.h"
#include "nsAbsoluteContainingBlock.h"
#include "nsCanvasFrame.h"
#include "nsLayoutUtils.h"
#include "nsSubDocumentFrame.h"
#include "GeckoProfiler.h"
#include "nsIMozBrowserFrame.h"
#include "nsPlaceholderFrame.h"
@ -56,12 +57,27 @@ void ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
AUTO_PROFILER_LABEL("ViewportFrame::BuildDisplayList",
GRAPHICS_DisplayListBuilding);
if (nsIFrame* kid = mFrames.FirstChild()) {
// make the kid's BorderBackground our own. This ensures that the canvas
// frame's background becomes our own background and therefore appears
// below negative z-index elements.
BuildDisplayListForChild(aBuilder, kid, aLists);
nsIFrame* kid = mFrames.FirstChild();
if (!kid) {
return;
}
nsDisplayListCollection set(aBuilder);
BuildDisplayListForChild(aBuilder, kid, set);
// If we have a scrollframe then it takes care of creating the display list
// for the top layer, but otherwise we need to do it here.
if (!kid->IsScrollFrame()) {
bool isOpaque = false;
if (auto* list = BuildDisplayListForTopLayer(aBuilder, &isOpaque)) {
if (isOpaque) {
set.DeleteAll(aBuilder);
}
set.PositionedDescendants()->AppendToTop(list);
}
}
set.MoveTo(aLists);
}
#ifdef DEBUG
@ -150,51 +166,54 @@ static bool BackdropListIsOpaque(ViewportFrame* aFrame,
return opaque.Contains(aFrame->GetRect());
}
void ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList,
bool* aIsOpaque) {
nsDisplayWrapList* ViewportFrame::BuildDisplayListForTopLayer(
nsDisplayListBuilder* aBuilder, bool* aIsOpaque) {
nsDisplayList topLayerList;
nsTArray<dom::Element*> topLayer = PresContext()->Document()->GetTopLayer();
for (dom::Element* elem : topLayer) {
if (nsIFrame* frame = elem->GetPrimaryFrame()) {
// There are two cases where an element in fullscreen is not in
// the top layer:
// 1. When building display list for purpose other than painting,
// it is possible that there is inconsistency between the style
// info and the content tree.
// 2. This is an element which we are not going to put in the top
// layer for fullscreen. See ShouldInTopLayerForFullscreen().
// In both cases, we want to skip the frame here and paint it in
// the normal path.
if (frame->StyleDisplay()->mTopLayer == StyleTopLayer::None) {
MOZ_ASSERT(!aBuilder->IsForPainting() ||
!ShouldInTopLayerForFullscreen(elem));
continue;
}
MOZ_ASSERT(ShouldInTopLayerForFullscreen(elem));
// Inner SVG, MathML elements, as well as children of some XUL
// elements are not allowed to be out-of-flow. They should not
// be handled as top layer element here.
if (!frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
MOZ_ASSERT(!elem->GetParent()->IsHTMLElement(),
"HTML element should always be out-of-flow if in the top "
"layer");
continue;
}
if (nsIFrame* backdropPh =
frame->GetChildList(kBackdropList).FirstChild()) {
MOZ_ASSERT(!backdropPh->GetNextSibling(), "more than one ::backdrop?");
MOZ_ASSERT(backdropPh->HasAnyStateBits(NS_FRAME_FIRST_REFLOW),
"did you intend to reflow ::backdrop placeholders?");
nsIFrame* backdropFrame =
nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPh);
BuildDisplayListForTopLayerFrame(aBuilder, backdropFrame, aList);
if (aIsOpaque) {
*aIsOpaque = BackdropListIsOpaque(this, aBuilder, aList);
}
}
BuildDisplayListForTopLayerFrame(aBuilder, frame, aList);
nsIFrame* frame = elem->GetPrimaryFrame();
if (!frame) {
continue;
}
// There are two cases where an element in fullscreen is not in
// the top layer:
// 1. When building display list for purpose other than painting,
// it is possible that there is inconsistency between the style
// info and the content tree.
// 2. This is an element which we are not going to put in the top
// layer for fullscreen. See ShouldInTopLayerForFullscreen().
// In both cases, we want to skip the frame here and paint it in
// the normal path.
if (frame->StyleDisplay()->mTopLayer == StyleTopLayer::None) {
MOZ_ASSERT(!aBuilder->IsForPainting() ||
!ShouldInTopLayerForFullscreen(elem));
continue;
}
MOZ_ASSERT(ShouldInTopLayerForFullscreen(elem));
// Inner SVG, MathML elements, as well as children of some XUL
// elements are not allowed to be out-of-flow. They should not
// be handled as top layer element here.
if (!frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
MOZ_ASSERT(!elem->GetParent()->IsHTMLElement(),
"HTML element should always be out-of-flow if in the top "
"layer");
continue;
}
if (nsIFrame* backdropPh =
frame->GetChildList(kBackdropList).FirstChild()) {
MOZ_ASSERT(!backdropPh->GetNextSibling(), "more than one ::backdrop?");
MOZ_ASSERT(backdropPh->HasAnyStateBits(NS_FRAME_FIRST_REFLOW),
"did you intend to reflow ::backdrop placeholders?");
nsIFrame* backdropFrame =
nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPh);
BuildDisplayListForTopLayerFrame(aBuilder, backdropFrame, &topLayerList);
if (aIsOpaque) {
*aIsOpaque = BackdropListIsOpaque(this, aBuilder, &topLayerList);
}
}
BuildDisplayListForTopLayerFrame(aBuilder, frame, &topLayerList);
}
if (nsCanvasFrame* canvasFrame = PresShell()->GetCanvasFrame()) {
@ -203,10 +222,26 @@ void ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
MOZ_ASSERT(frame->StyleDisplay()->mTopLayer != StyleTopLayer::None,
"ua.css should ensure this");
MOZ_ASSERT(frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
BuildDisplayListForTopLayerFrame(aBuilder, frame, aList);
BuildDisplayListForTopLayerFrame(aBuilder, frame, &topLayerList);
}
}
}
if (topLayerList.IsEmpty()) {
return nullptr;
}
nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(aBuilder,
this);
// Wrap the whole top layer in a single item with maximum z-index,
// and append it at the very end, so that it stays at the topmost.
nsDisplayWrapList* wrapList = MakeDisplayItemWithIndex<nsDisplayWrapList>(
aBuilder, this, 2, &topLayerList, aBuilder->CurrentActiveScrolledRoot(),
false);
if (!wrapList) {
return nullptr;
}
wrapList->SetOverrideZIndex(
std::numeric_limits<decltype(wrapList->ZIndex())>::max());
return wrapList;
}
#ifdef DEBUG

View File

@ -15,6 +15,7 @@
#include "mozilla/Attributes.h"
#include "nsContainerFrame.h"
class nsDisplayWrapList;
class nsPresContext;
namespace mozilla {
@ -51,9 +52,8 @@ class ViewportFrame : public nsContainerFrame {
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) override;
void BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList,
bool* aIsOpaque = nullptr);
nsDisplayWrapList* BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
bool* aIsOpaque = nullptr);
virtual nscoord GetMinISize(gfxContext* aRenderingContext) override;
virtual nscoord GetPrefISize(gfxContext* aRenderingContext) override;

View File

@ -4117,30 +4117,16 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
nsDisplayWrapList* ScrollFrameHelper::MaybeCreateTopLayerItems(
nsDisplayListBuilder* aBuilder, bool* aIsOpaque) {
if (mIsRoot) {
if (ViewportFrame* viewportFrame = do_QueryFrame(mOuter->GetParent())) {
nsDisplayList topLayerList;
viewportFrame->BuildDisplayListForTopLayer(aBuilder, &topLayerList,
aIsOpaque);
if (!topLayerList.IsEmpty()) {
nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
aBuilder, viewportFrame);
// Wrap the whole top layer in a single item with maximum z-index,
// and append it at the very end, so that it stays at the topmost.
nsDisplayWrapList* wrapList =
MakeDisplayItemWithIndex<nsDisplayWrapList>(
aBuilder, viewportFrame, 2, &topLayerList,
aBuilder->CurrentActiveScrolledRoot(), false);
if (wrapList) {
wrapList->SetOverrideZIndex(
std::numeric_limits<decltype(wrapList->ZIndex())>::max());
return wrapList;
}
}
}
if (!mIsRoot) {
return nullptr;
}
return nullptr;
ViewportFrame* viewport = do_QueryFrame(mOuter->GetParent());
if (!viewport) {
return nullptr;
}
return viewport->BuildDisplayListForTopLayer(aBuilder, aIsOpaque);
}
nsRect ScrollFrameHelper::RestrictToRootDisplayPort(

View File

@ -0,0 +1,9 @@
<html xmlns="http://www.w3.org/1999/xhtml" scrolling="false" class="reftest-wait">
<body>
<dialog>ABC</dialog>
<script><![CDATA[
document.querySelector("dialog").showModal();
document.documentElement.className = "";
]]></script>
</body>
</html>

View File

@ -2111,3 +2111,4 @@ fuzzy(0-2,0-96600) == 1648282-1a.html 1648282-1-ref.html
fuzzy(0-2,0-96600) == 1648282-1b.html 1648282-1-ref.html
!= 1672137-1.html 1672137-1-notref.html
== 1686729-1.html 1686729-1-ref.html
!= chrome://reftest/content/bugs/1688004.xhtml about:blank