diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index d966157be917..8ee469281657 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2314,9 +2314,12 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // dirty rect in child-relative coordinates nsRect dirty = aDirtyRect - child->GetOffsetTo(this); + const nsStyleDisplay* disp; nsIAtom* childType = child->GetType(); nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr; - if (childType == nsGkAtoms::placeholderFrame) { + if (childType != nsGkAtoms::placeholderFrame) { + disp = child->StyleDisplay(); + } else { nsPlaceholderFrame* placeholder = static_cast(child); child = placeholder->GetOutOfFlowFrame(); NS_ASSERTION(child, "No out of flow frame?"); @@ -2326,6 +2329,16 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (!child || nsLayoutUtils::IsPopup(child) || (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)) return; + MOZ_ASSERT(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW); + disp = child->StyleDisplay(); + // If the out-of-flow frame is in the top layer, the viewport frame + // will paint it. Skip it here. Note that, only out-of-flow frames + // with this property should be skipped, because non-HTML elements + // may stop their children from being out-of-flow. Those frames + // should still be handled in the normal in-flow path. + if (disp->mTopLayer != NS_STYLE_TOP_LAYER_NONE) { + return; + } // Make sure that any attempt to use childType below is disappointed. We // could call GetType again but since we don't currently need it, let's // avoid the virtual call. @@ -2403,7 +2416,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // Child is composited if it's transformed, partially transparent, or has // SVG effects or a blend mode.. - const nsStyleDisplay* disp = child->StyleDisplay(); const nsStylePosition* pos = child->StylePosition(); bool isVisuallyAtomic = child->HasOpacity() || child->IsTransformed() diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp index a0e73a2f4cca..e51066c04386 100644 --- a/layout/generic/nsViewportFrame.cpp +++ b/layout/generic/nsViewportFrame.cpp @@ -14,6 +14,7 @@ #include "nsSubDocumentFrame.h" #include "nsAbsoluteContainingBlock.h" #include "GeckoProfiler.h" +#include "nsIMozBrowserFrame.h" using namespace mozilla; @@ -51,14 +52,89 @@ ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, PROFILER_LABEL("ViewportFrame", "BuildDisplayList", js::ProfileEntry::Category::GRAPHICS); - nsIFrame* kid = mFrames.FirstChild(); - if (!kid) - return; + 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, aDirtyRect, aLists); + } - // 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, aDirtyRect, aLists); + nsDisplayList topLayerList; + BuildDisplayListForTopLayer(aBuilder, &topLayerList); + if (!topLayerList.IsEmpty()) { + // 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 = + new (aBuilder) nsDisplayWrapList(aBuilder, this, &topLayerList); + wrapList->SetOverrideZIndex( + std::numeric_limitsZIndex())>::max()); + aLists.PositionedDescendants()->AppendNewToTop(wrapList); + } +} + +#ifdef DEBUG +/** + * Returns whether we are going to put an element in the top layer for + * fullscreen. This function should matches the CSS rule in ua.css. + */ +static bool +ShouldInTopLayerForFullscreen(Element* aElement) +{ + if (!aElement->GetParent()) { + return false; + } + nsCOMPtr browserFrame = do_QueryInterface(aElement); + if (browserFrame && browserFrame->GetReallyIsBrowserOrApp()) { + return false; + } + return true; +} +#endif // DEBUG + +void +ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder, + nsDisplayList* aList) +{ + nsIDocument* doc = PresContext()->Document(); + nsTArray fullscreenStack = doc->GetFullscreenStack(); + for (Element* elem : fullscreenStack) { + 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 == NS_STYLE_TOP_LAYER_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->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { + MOZ_ASSERT(!elem->GetParent()->IsHTMLElement(), "HTML element " + "should always be out-of-flow if in the top layer"); + continue; + } + MOZ_ASSERT(frame->GetParent() == this); + + nsRect dirty; + nsDisplayListBuilder::OutOfFlowDisplayData* + savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(frame); + if (savedOutOfFlowData) { + dirty = savedOutOfFlowData->mDirtyRect; + } + nsDisplayList list; + frame->BuildDisplayListForStackingContext(aBuilder, dirty, &list); + aList->AppendToTop(&list); + } + } } #ifdef DEBUG diff --git a/layout/generic/nsViewportFrame.h b/layout/generic/nsViewportFrame.h index ad7739363b3c..13b5950454d9 100644 --- a/layout/generic/nsViewportFrame.h +++ b/layout/generic/nsViewportFrame.h @@ -63,6 +63,9 @@ public: const nsRect& aDirtyRect, const nsDisplayListSet& aLists) override; + void BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder, + nsDisplayList* aList); + virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override; virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override; virtual void Reflow(nsPresContext* aPresContext, diff --git a/layout/style/ua.css b/layout/style/ua.css index 0d1643b8d967..6af848e72725 100644 --- a/layout/style/ua.css +++ b/layout/style/ua.css @@ -280,6 +280,8 @@ box-sizing: border-box !important; } +/* Selectors here should match the check in + * nsViewportFrame.cpp:ShouldInTopLayerForFullscreen() */ *|*:-moz-full-screen:not(:root):not(:-moz-browser-frame) { -moz-top-layer: top !important; }