From 6489b277410404081dca436893239fd3cbb97d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 10 Mar 2022 05:11:38 +0000 Subject: [PATCH] Bug 1758564 - Allow rendering children of 's shadow root. r=dholbert This doesn't change behavior just yet, but seems worth doing separately. With this patch, we can render content overlaid on top of an image frame, by attaching a shadow root to it. The idea is to use this for text recognition (OCR) of text inside images. I chose to implement this using the DynamicLeaf stuff that I removed in bug 1746310 (because nsMenuPopupFrame was its only user). That seems simpler than either a new frame class, or more special cases in the frame constructor, etc. But let me know if you disagree. There are further changes that we want to do. For example, trying to select the overlaid text with the mouse right now will start dragging the image. For that, we might need to tweak our selection / mouse dragging code. But those seem all changes that can go on top of this. Differential Revision: https://phabricator.services.mozilla.com/D140638 --- layout/base/nsCSSFrameConstructor.cpp | 13 +++++--- layout/generic/FrameClass.py | 1 + layout/generic/FrameClasses.py | 4 +-- layout/generic/nsIFrame.cpp | 2 ++ layout/generic/nsIFrame.h | 6 ++++ layout/generic/nsImageFrame.cpp | 43 +++++++++++++++++++++++++++ layout/generic/nsImageFrame.h | 4 +++ 7 files changed, 67 insertions(+), 6 deletions(-) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index c48d9f963019..d8afd51b974d 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1681,6 +1681,14 @@ void nsCSSFrameConstructor::CreateGeneratedContentFromListStyleType( aAddChild(child); } +// Frames for these may not be leaves in the proper sense, but we still don't +// want to expose generated content on them. For the purposes of the page they +// should be leaves. +static bool HasUAWidget(const Element& aOriginatingElement) { + const ShadowRoot* sr = aOriginatingElement.GetShadowRoot(); + return sr && sr->IsUAWidget(); +} + /* * aParentFrame - the frame that should be the parent of the generated * content. This is the frame for the corresponding content node, @@ -1705,10 +1713,7 @@ void nsCSSFrameConstructor::CreateGeneratedContentItem( aPseudoElement == PseudoStyleType::marker, "unexpected aPseudoElement"); - if (aParentFrame && (aParentFrame->IsHTMLVideoFrame() || - aParentFrame->IsDateTimeControlFrame())) { - // Video frames and date time control frames may not be leafs when backed by - // an UA widget, but we still don't want to expose generated content. + if (HasUAWidget(aOriginatingElement)) { return; } diff --git a/layout/generic/FrameClass.py b/layout/generic/FrameClass.py index 00a57abb7f27..0fae9e115efd 100644 --- a/layout/generic/FrameClass.py +++ b/layout/generic/FrameClass.py @@ -6,6 +6,7 @@ # Leaf constants to pass to Frame's leafness argument. LEAF = "Leaf" NOT_LEAF = "NotLeaf" +DYNAMIC_LEAF = "DynamicLeaf" class FrameClass: diff --git a/layout/generic/FrameClasses.py b/layout/generic/FrameClasses.py index e244974bfe9c..67f685a1460c 100644 --- a/layout/generic/FrameClasses.py +++ b/layout/generic/FrameClasses.py @@ -3,7 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # Frame class definitions, used to generate FrameIdList.h and FrameTypeList.h -from FrameClass import Frame, AbstractFrame, LEAF, NOT_LEAF +from FrameClass import Frame, AbstractFrame, LEAF, NOT_LEAF, DYNAMIC_LEAF FRAME_CLASSES = [ Frame("BRFrame", "Br", LEAF), @@ -41,7 +41,7 @@ FRAME_CLASSES = [ Frame("nsHTMLScrollFrame", "Scroll", NOT_LEAF), Frame("nsImageBoxFrame", "ImageBox", LEAF), Frame("nsImageControlFrame", "ImageControl", LEAF), - Frame("nsImageFrame", "Image", LEAF), + Frame("nsImageFrame", "Image", DYNAMIC_LEAF), Frame("nsInlineFrame", "Inline", NOT_LEAF), Frame("nsLeafBoxFrame", "LeafBox", LEAF), Frame("nsListControlFrame", "ListControl", NOT_LEAF), diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index a71d2ade036e..82a204e0bd20 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -171,11 +171,13 @@ const nsIFrame::FrameClassBits nsIFrame::sFrameClassBits[ 0] = { #define Leaf eFrameClassBitsLeaf #define NotLeaf eFrameClassBitsNone +#define DynamicLeaf eFrameClassBitsDynamicLeaf #define FRAME_ID(class_, type_, leaf_, ...) leaf_, #define ABSTRACT_FRAME_ID(...) #include "mozilla/FrameIdList.h" #undef Leaf #undef NotLeaf +#undef DynamicLeaf #undef FRAME_ID #undef ABSTRACT_FRAME_ID }; diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index dcfba6cb8ee3..0110377976d3 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -3366,9 +3366,14 @@ class nsIFrame : public nsQueryFrame { bool IsLeaf() const { MOZ_ASSERT(uint8_t(mClass) < mozilla::ArrayLength(sFrameClassBits)); FrameClassBits bits = sFrameClassBits[uint8_t(mClass)]; + if (MOZ_UNLIKELY(bits & eFrameClassBitsDynamicLeaf)) { + return IsLeafDynamic(); + } return bits & eFrameClassBitsLeaf; } + virtual bool IsLeafDynamic() const { return false; } + /** * Marks all display items created by this frame as needing a repaint, * and calls SchedulePaint() if requested and one is not already pending. @@ -5413,6 +5418,7 @@ class nsIFrame : public nsQueryFrame { enum FrameClassBits { eFrameClassBitsNone = 0x0, eFrameClassBitsLeaf = 0x1, + eFrameClassBitsDynamicLeaf = 0x2, }; // Maps mClass to IsLeaf() flags. static const FrameClassBits sFrameClassBits[ diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 4a09b9991a19..da860cbc156d 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -1209,6 +1209,31 @@ nscoord nsImageFrame::GetPrefISize(gfxContext* aRenderingContext) { return iSize.valueOr(0); } +void nsImageFrame::ReflowChildren(nsPresContext* aPresContext, + const ReflowInput& aReflowInput, + const LogicalSize& aImageSize) { + for (nsIFrame* child : mFrames) { + ReflowOutput childDesiredSize(aReflowInput); + WritingMode wm = GetWritingMode(); + // Shouldn't be hard to support if we want, but why bother. + MOZ_ASSERT( + wm == child->GetWritingMode(), + "We don't expect mismatched writing-modes in content we control"); + nsReflowStatus childStatus; + + LogicalPoint childOffset(wm); + ReflowInput childReflowInput(aPresContext, aReflowInput, child, aImageSize); + const nsSize containerSize = aImageSize.GetPhysicalSize(wm); + ReflowChild(child, aPresContext, childDesiredSize, childReflowInput, wm, + childOffset, containerSize, ReflowChildFlags::Default, + childStatus); + + FinishReflowChild(child, aPresContext, childDesiredSize, &childReflowInput, + wm, childOffset, containerSize, + ReflowChildFlags::Default); + } +} + void nsImageFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { @@ -1296,6 +1321,9 @@ void nsImageFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, } FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay); + // Reflow the child frames. Our children can't affect our size in any way. + ReflowChildren(aPresContext, aReflowInput, aMetrics.Size(GetWritingMode())); + if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) { mReflowCallbackPosted = true; PresShell()->PostReflowCallback(this); @@ -2326,6 +2354,8 @@ void nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, DisplaySelectionOverlay(aBuilder, aLists.Content(), nsISelectionDisplay::DISPLAY_IMAGES); } + + BuildDisplayListForNonBlockChildren(aBuilder, aLists); } bool nsImageFrame::ShouldDisplaySelection() { @@ -2400,6 +2430,19 @@ bool nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget, return status; } +bool nsImageFrame::IsLeafDynamic() const { + if (mKind != Kind::ImageElement) { + // Image frames created for "content: url()" could have an author-controlled + // shadow root, we want to be a regular leaf for those. + return true; + } + // For elements that create image frames, calling attachShadow() will throw, + // so the only ShadowRoot we could ever have is a UA widget. + const auto* shadow = mContent->AsElement()->GetShadowRoot(); + MOZ_ASSERT_IF(shadow, shadow->IsUAWidget()); + return !shadow; +} + nsresult nsImageFrame::GetContentForEvent(WidgetEvent* aEvent, nsIContent** aContent) { NS_ENSURE_ARG_POINTER(aContent); diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index e90c5c480055..2012cbfb5f1a 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -84,6 +84,7 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { } void Reflow(nsPresContext*, ReflowOutput&, const ReflowInput&, nsReflowStatus&) override; + bool IsLeafDynamic() const override; nsresult GetContentForEvent(mozilla::WidgetEvent*, nsIContent** aContent) final; @@ -212,6 +213,9 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { nsImageFrame(ComputedStyle*, nsPresContext* aPresContext, ClassID, Kind); + void ReflowChildren(nsPresContext*, const ReflowInput&, + const mozilla::LogicalSize& aImageSize); + protected: nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, ClassID aID) : nsImageFrame(aStyle, aPresContext, aID, Kind::ImageElement) {}