/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* interface for all rendering objects */ #ifndef nsIFrame_h___ #define nsIFrame_h___ #ifndef MOZILLA_INTERNAL_API #error This header/class should only be used within Mozilla code. It should not be used by extensions. #endif #if (defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)) || defined(ANDROID) // Blink's magic depth limit from its HTML parser (513) plus as much as fits in // the default run-time stack on armv7 Android on Dalvik when using display: // block minus a bit just to be sure. The Dalvik default stack crashes at 588. // ART can do a few frames more. Using the same number for 32-bit Windows for // consistency. Over there, Blink's magic depth of 513 doesn't fit in the // default stack of 1 MB, but this magic depth fits when the default is grown by // mere 192 KB (tested in 64 KB increments). // // 32-bit Windows has a different limit compared to 64-bit desktop, because the // default stack size affects all threads and consumes address space. Fixing // that is bug 1257522. // // 32-bit Android on ARM already happens to have defaults that are close enough // to what makes sense as a temporary measure on Windows, so adjusting the // Android stack can be a follow-up. The stack on 64-bit ARM needs adjusting in // any case before 64-bit ARM can become tier-1. See bug 1400811. // // Ideally, we'd get rid of this smaller limit and make 32-bit Windows and // Android capable of working with the Linux/Mac/Win64 number below. # define MAX_REFLOW_DEPTH 585 #else // Blink's magic depth limit from its HTML parser times two. Also just about // fits within the system default runtime stack limit of 8 MB on 64-bit Mac and // Linux with display: table-cell. # define MAX_REFLOW_DEPTH 1026 #endif /* nsIFrame is in the process of being deCOMtaminated, i.e., this file is eventually going to be eliminated, and all callers will use nsFrame instead. At the moment we're midway through this process, so you will see inlined functions and member variables in this file. -dwh */ #include #include #include "FrameProperties.h" #include "LayoutConstants.h" #include "Visibility.h" #include "mozilla/AspectRatio.h" #include "mozilla/Attributes.h" #include "mozilla/Baseline.h" #include "mozilla/ComputedStyle.h" #include "mozilla/EnumSet.h" #include "mozilla/EventForwards.h" #include "mozilla/Maybe.h" #include "mozilla/ReflowInput.h" #include "mozilla/RelativeTo.h" #include "mozilla/Result.h" #include "mozilla/SmallPointerArray.h" #include "mozilla/ToString.h" #include "mozilla/WritingModes.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/CompositorHitTestInfo.h" #include "mozilla/gfx/MatrixFwd.h" #include "mozilla/intl/BidiEmbeddingLevel.h" #include "mozilla/intl/UnicodeProperties.h" #include "nsChangeHint.h" #include "nsDirection.h" #include "nsDisplayItemTypes.h" #include "nsFrameList.h" #include "nsFrameState.h" #include "nsIContent.h" #include "nsITheme.h" #include "nsPresContext.h" #include "nsQueryFrame.h" #include "nsStyleStruct.h" #include "nsStyleStructList.h" #include "nsTHashSet.h" #ifdef ACCESSIBILITY # include "mozilla/a11y/AccTypes.h" #endif /** * New rules of reflow: * 1. you get a WillReflow() followed by a Reflow() followed by a DidReflow() in * order (no separate pass over the tree) * 2. it's the parent frame's responsibility to size/position the child's view * (not the child frame's responsibility as it is today) during reflow (and * before sending the DidReflow() notification) * 3. positioning of child frames (and their views) is done on the way down the * tree, and sizing of child frames (and their views) on the way back up * 4. if you move a frame (outside of the reflow process, or after reflowing * it), then you must make sure that its view (or its child frame's views) * are re-positioned as well. It's reasonable to not position the view until * after all reflowing the entire line, for example, but the frame should * still be positioned and sized (and the view sized) during the reflow * (i.e., before sending the DidReflow() notification) * 5. the view system handles moving of widgets, i.e., it's not our problem */ class nsAtom; class nsView; class nsFrameSelection; class nsIWidget; class nsISelectionController; class nsILineIterator; class nsTextControlFrame; class gfxSkipChars; class gfxSkipCharsIterator; class gfxContext; class nsLineLink; template class GenericLineListIterator; using LineListIterator = GenericLineListIterator; class nsContainerFrame; class nsPlaceholderFrame; class nsStyleChangeList; class nsViewManager; class nsWindowSizes; enum class AttrModType : uint8_t; // Defined by nsIMutationObserver.h struct CharacterDataChangeInfo; namespace mozilla { enum class CaretAssociationHint; enum class IsFocusableFlags : uint8_t; enum class PeekOffsetOption : uint16_t; enum class PseudoStyleType : uint8_t; enum class TableSelectionMode : uint32_t; class AbsoluteContainingBlock; class AnchorPosReferenceData; struct LastSuccessfulPositionData; class EffectSet; class LazyLogModule; class nsDisplayItem; class nsDisplayList; class nsDisplayListBuilder; class nsDisplayListSet; class PresShell; class ScrollContainerFrame; class ServoRestyleState; class WidgetGUIEvent; class WidgetMouseEvent; void DeleteAnchorPosReferenceData(AnchorPosReferenceData*); void DeleteLastSuccessfulPositionData(LastSuccessfulPositionData*); struct PeekOffsetStruct; namespace layers { class Layer; class LayerManager; } // namespace layers namespace layout { class ScrollAnchorContainer; } // namespace layout } // namespace mozilla //---------------------------------------------------------------------- // 1 million CSS pixels less than our max app unit measure. // For reflowing with an "infinite" available inline space per [css-sizing]. // (reflowing with an NS_UNCONSTRAINEDSIZE available inline size isn't allowed // and leads to assertions) #define INFINITE_ISIZE_COORD nscoord(NS_MAXSIZE - (1000000 * 60)) //---------------------------------------------------------------------- namespace mozilla { enum class LayoutFrameType : uint8_t { #define FRAME_TYPE(ty_, ...) ty_, #include "mozilla/FrameTypeList.h" #undef FRAME_TYPE }; // Stores ascent and descent metrics to be used for Ruby annotation positioning // (potentially different from line-box or font ascent and descent). struct RubyMetrics { nscoord mAscent = 0; nscoord mDescent = 0; void CombineWith(const RubyMetrics& aOther) { mAscent = std::max(mAscent, aOther.mAscent); mDescent = std::max(mDescent, aOther.mDescent); } }; } // namespace mozilla enum nsSelectionAmount { eSelectCharacter = 0, // a single Unicode character; // do not use this (prefer Cluster) unless you // are really sure it's what you want eSelectCluster = 1, // a grapheme cluster: this is usually the right // choice for movement or selection by "character" // as perceived by the user eSelectWord = 2, eSelectWordNoSpace = 3, // select a "word" without selecting the following // space, no matter what the default platform // behavior is eSelectLine = 4, // previous drawn line in flow. // NOTE that selection code depends on the ordering of the above values, // allowing simple <= tests to check categories of caret movement. // Don't rearrange without checking the usage in nsSelection.cpp! eSelectBeginLine = 5, eSelectEndLine = 6, eSelectNoAmount = 7, // just bounce back current offset. eSelectParagraph = 8 // select a "paragraph" }; //---------------------------------------------------------------------- // Reflow status returned by the Reflow() methods. class nsReflowStatus final { public: nsReflowStatus() : mFloatClearType(mozilla::UsedClear::None), mInlineBreak(InlineBreak::None), mCompletion(Completion::FullyComplete), mNextInFlowNeedsReflow(false), mFirstLetterComplete(false) {} // Reset all the member variables. void Reset() { mFloatClearType = mozilla::UsedClear::None; mInlineBreak = InlineBreak::None; mCompletion = Completion::FullyComplete; mNextInFlowNeedsReflow = false; mFirstLetterComplete = false; } // Return true if all member variables have their default values. bool IsEmpty() const { return (IsFullyComplete() && !IsInlineBreak() && !mNextInFlowNeedsReflow && !mFirstLetterComplete); } // There are three possible completion statuses, represented by // mCompletion. // // Incomplete means the frame does *not* map all its content, and the // parent frame should create a continuing frame. // // OverflowIncomplete means that the frame has an overflow that is not // complete, but its own box is complete. (This happens when the content // overflows a fixed-height box.) The reflower should place and size the // frame and continue its reflow, but it needs to create an overflow // container as a continuation for this frame. See "Overflow containers" // documentation in nsContainerFrame.h for more information. // // FullyComplete means the frame is neither Incomplete nor // OverflowIncomplete. This is the default state for a nsReflowStatus. // enum class Completion : uint8_t { // The order of the enum values is important, which represents the // precedence when merging. FullyComplete, OverflowIncomplete, Incomplete, }; bool IsIncomplete() const { return mCompletion == Completion::Incomplete; } bool IsOverflowIncomplete() const { return mCompletion == Completion::OverflowIncomplete; } bool IsFullyComplete() const { return mCompletion == Completion::FullyComplete; } // Just for convenience; not a distinct state. bool IsComplete() const { return !IsIncomplete(); } void SetIncomplete() { mCompletion = Completion::Incomplete; } void SetOverflowIncomplete() { mCompletion = Completion::OverflowIncomplete; } // mNextInFlowNeedsReflow bit flag means that the next-in-flow is dirty, // and also needs to be reflowed. This status only makes sense for a frame // that is not complete, i.e. you wouldn't set mNextInFlowNeedsReflow when // IsComplete() is true. bool NextInFlowNeedsReflow() const { return mNextInFlowNeedsReflow; } void SetNextInFlowNeedsReflow() { mNextInFlowNeedsReflow = true; } // Merge the frame completion status bits from aStatus into this. void MergeCompletionStatusFrom(const nsReflowStatus& aStatus) { if (mCompletion < aStatus.mCompletion) { mCompletion = aStatus.mCompletion; } // These asserts ensure that the mCompletion merging works as we expect. // (Incomplete beats OverflowIncomplete, which beats FullyComplete.) static_assert( Completion::Incomplete > Completion::OverflowIncomplete && Completion::OverflowIncomplete > Completion::FullyComplete, "mCompletion merging won't work without this!"); mNextInFlowNeedsReflow |= aStatus.mNextInFlowNeedsReflow; } // There are three possible inline-break statuses, represented by // mInlineBreak. // // "None" means no break is requested. // "Before" means the break should occur before the frame. // "After" means the break should occur after the frame. // (Here, "the frame" is the frame whose reflow results are being reported by // this nsReflowStatus.) // enum class InlineBreak : uint8_t { None, Before, After, }; bool IsInlineBreak() const { return mInlineBreak != InlineBreak::None; } bool IsInlineBreakBefore() const { return mInlineBreak == InlineBreak::Before; } bool IsInlineBreakAfter() const { return mInlineBreak == InlineBreak::After; } mozilla::UsedClear FloatClearType() const { return mFloatClearType; } // Set the inline line-break-before status, and reset other bit flags. Note // that other frame completion status isn't expected to matter after calling // this method. // // Here's one scenario where a child frame would report this status. Suppose // the child has "break-inside:avoid" in its style, and the child (and its // content) won't fit in the available block-size. This child would want to // report this status so that it gets pushed (in its entirety) to the next // column/page where it will hopefully fit. void SetInlineLineBreakBeforeAndReset() { Reset(); mFloatClearType = mozilla::UsedClear::None; mInlineBreak = InlineBreak::Before; } // Set the inline line-break-after status. The clear type can be changed // via the optional aClearType param. void SetInlineLineBreakAfter( mozilla::UsedClear aClearType = mozilla::UsedClear::None) { mFloatClearType = aClearType; mInlineBreak = InlineBreak::After; } // mFirstLetterComplete bit flag means the break was induced by // completion of a first-letter. bool FirstLetterComplete() const { return mFirstLetterComplete; } void SetFirstLetterComplete() { mFirstLetterComplete = true; } private: mozilla::UsedClear mFloatClearType; InlineBreak mInlineBreak; Completion mCompletion; bool mNextInFlowNeedsReflow : 1; bool mFirstLetterComplete : 1; }; // Convert nsReflowStatus to a human-readable string. std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus); namespace mozilla { // Loosely: https://drafts.csswg.org/css-align-3/#shared-alignment-context enum class AlignmentContext { Inline, Table, Flexbox, Grid, }; /* * For replaced elements only. Gets the intrinsic dimensions of this element, * which can be specified on a per-axis basis. */ struct IntrinsicSize { Maybe width; Maybe height; IntrinsicSize() = default; IntrinsicSize(nscoord aWidth, nscoord aHeight) : width(Some(aWidth)), height(Some(aHeight)) {} explicit IntrinsicSize(const nsSize& aSize) : IntrinsicSize(aSize.Width(), aSize.Height()) {} Maybe ToSize() const { return width && height ? Some(nsSize(*width, *height)) : Nothing(); } Maybe& ISize(WritingMode aWM) { return aWM.IsVertical() ? height : width; } const Maybe& ISize(WritingMode aWM) const { return aWM.IsVertical() ? height : width; } Maybe& BSize(WritingMode aWM) { return aWM.IsVertical() ? width : height; } const Maybe& BSize(WritingMode aWM) const { return aWM.IsVertical() ? width : height; } void Zoom(const StyleZoom& aZoom) { if (width) { *width = aZoom.ZoomCoord(*width); } if (height) { *height = aZoom.ZoomCoord(*height); } } bool operator==(const IntrinsicSize&) const = default; bool operator!=(const IntrinsicSize&) const = default; }; // Pseudo bidi embedding level indicating nonexistence. constexpr mozilla::intl::BidiEmbeddingLevel kBidiLevelNone(0xff); struct FrameBidiData { mozilla::intl::BidiEmbeddingLevel baseLevel; mozilla::intl::BidiEmbeddingLevel embeddingLevel; // The embedding level of virtual bidi formatting character before // this frame if any. kBidiLevelNone is used to indicate nonexistence // or unnecessity of such virtual character. mozilla::intl::BidiEmbeddingLevel precedingControl; }; // A struct aggregates necessary data to compute the intrinsic sizes for a // frame, typically the frame whose intrinsic size contribution is being // requested. This struct is used as an input for GetMinISize(), GetPrefISize(), // IntrinsicISize(), and others. struct MOZ_STACK_CLASS IntrinsicSizeInput final { gfxContext* const mContext; // The content-box size of a frame's containing block (in the frame's own // writing mode), used as a percentage basis for percentage-based sizes on the // frame itself that contribute to its intrinsic size. For example, in grid // layout, a percentage value of min-height be can transferred through the // aspect-ratio to determine auto repeat columns specified in // grid-template-columns. // // Note: it is acceptable for mContainingBlockSize to be Nothing() as long as // the frame doesn't have percentage-based value for properties that need to // be resolved in order to compute its intrinsic size. Maybe mContainingBlockSize; // The content-box size of a frame (in the frame's own writing mode), served // as a percentage basis when computing the children's intrinsic // contributions. If the basis is indefinite in a given axis, use // NS_UNCONSTRAINEDSIZE for that component. If the value is Nothing, it is // semantically equivalent to NS_UNCONSTRAINEDSIZE in both axes. // // In most scenarios, this struct is used when computing the inline size // contribution, so the inline component of the percentage basis should be set // to NS_UNCONSTRAINEDSIZE. Maybe mPercentageBasisForChildren; bool HasSomePercentageBasisForChildren() const { return mPercentageBasisForChildren && !mPercentageBasisForChildren->IsAllValues(NS_UNCONSTRAINEDSIZE); } IntrinsicSizeInput(gfxContext* aContext, const Maybe& aContainingBlockSize, const Maybe& aPercentageBasisForChildren) : mContext(aContext), mContainingBlockSize(aContainingBlockSize), mPercentageBasisForChildren(aPercentageBasisForChildren) { MOZ_ASSERT(mContext); } // Construct a new IntrinsicSizeInput for children by copying from // aParentInput. // // Note: since this constructor creates an IntrinsicSizeInput for the // children, it does not copy mContainingBlockSize from aParentInput. // // This constructor converts mPercentageBasisForChildren's writing mode, if it // exists. The original mPercentageBasis in aSource is expected to be in the // writing mode aFromWM, and it will be converted to the writing mode aToWM. IntrinsicSizeInput(const IntrinsicSizeInput& aParentInput, mozilla::WritingMode aToWM, mozilla::WritingMode aFromWM) : IntrinsicSizeInput( aParentInput.mContext, Nothing(), aParentInput.mPercentageBasisForChildren.map([&](const auto& aPB) { return aPB.ConvertTo(aToWM, aFromWM); })) {} }; } // namespace mozilla /// Generic destructor for frame properties. Calls delete. template static void DeleteValue(T* aPropertyValue) { delete aPropertyValue; } /// Generic destructor for frame properties. Calls Release(). template static void ReleaseValue(T* aPropertyValue) { aPropertyValue->Release(); } //---------------------------------------------------------------------- /** * nsIFrame logging constants. We redefine the nspr * PRLogModuleInfo.level field to be a bitfield. Each bit controls a * specific type of logging. Each logging operation has associated * inline methods defined below. * * Due to the redefinition of the level field we cannot use MOZ_LOG directly * as that will cause assertions due to invalid log levels. */ #define NS_FRAME_TRACE_CALLS 0x1 #define NS_FRAME_TRACE_PUSH_PULL 0x2 #define NS_FRAME_TRACE_CHILD_REFLOW 0x4 #define NS_FRAME_TRACE_NEW_FRAMES 0x8 #define NS_FRAME_LOG_TEST(_lm, _bit) \ (int(((mozilla::LogModule*)(_lm))->Level()) & (_bit)) #ifdef DEBUG # define NS_FRAME_LOG(_bit, _args) \ PR_BEGIN_MACRO \ if (NS_FRAME_LOG_TEST(nsIFrame::sFrameLogModule, _bit)) { \ printf_stderr _args; \ } \ PR_END_MACRO #else # define NS_FRAME_LOG(_bit, _args) #endif // XXX Need to rework this so that logging is free when it's off #ifdef DEBUG # define NS_FRAME_TRACE_IN(_method) Trace(_method, true) # define NS_FRAME_TRACE_OUT(_method) Trace(_method, false) # define NS_FRAME_TRACE(_bit, _args) \ PR_BEGIN_MACRO \ if (NS_FRAME_LOG_TEST(nsIFrame::sFrameLogModule, _bit)) { \ TraceMsg _args; \ } \ PR_END_MACRO # define NS_FRAME_TRACE_REFLOW_IN(_method) Trace(_method, true) # define NS_FRAME_TRACE_REFLOW_OUT(_method, _status) \ Trace(_method, false, _status) #else # define NS_FRAME_TRACE(_bits, _args) # define NS_FRAME_TRACE_IN(_method) # define NS_FRAME_TRACE_OUT(_method) # define NS_FRAME_TRACE_REFLOW_IN(_method) # define NS_FRAME_TRACE_REFLOW_OUT(_method, _status) #endif //---------------------------------------------------------------------- // Frame allocation boilerplate macros. Every subclass of nsFrame must // either use NS_{DECL,IMPL}_FRAMEARENA_HELPERS pair for allocating // memory correctly, or use NS_DECL_ABSTRACT_FRAME to declare a frame // class abstract and stop it from being instantiated. If a frame class // without its own operator new and GetFrameId gets instantiated, the // per-frame recycler lists in nsPresArena will not work correctly, // with potentially catastrophic consequences (not enough memory is // allocated for a frame object). #define NS_DECL_FRAMEARENA_HELPERS(class) \ NS_DECL_QUERYFRAME_TARGET(class) \ static constexpr nsIFrame::ClassID kClassID = nsIFrame::ClassID::class##_id; \ void* operator new(size_t, mozilla::PresShell*) MOZ_MUST_OVERRIDE; \ nsQueryFrame::FrameIID GetFrameId() const override MOZ_MUST_OVERRIDE { \ return nsQueryFrame::class##_id; \ } #define NS_IMPL_FRAMEARENA_HELPERS(class) \ void* class ::operator new(size_t sz, mozilla::PresShell* aShell) { \ return aShell->AllocateFrame(nsQueryFrame::class##_id, sz); \ } #define NS_DECL_ABSTRACT_FRAME(class) \ void* operator new(size_t, mozilla::PresShell*) MOZ_MUST_OVERRIDE = delete; \ nsQueryFrame::FrameIID GetFrameId() const override MOZ_MUST_OVERRIDE = 0; //---------------------------------------------------------------------- namespace mozilla { // A simple class to group stuff that we need to keep around when tearing down // a frame tree. // // Native anonymous content created by the frames need to get unbound _after_ // the frame has been destroyed, see bug 1400618. // // We destroy the anonymous content bottom-up (so, in reverse order), because // it's a bit simpler, though we generally don't have that much nested anonymous // content (except for scrollbars). struct MOZ_RAII FrameDestroyContext { explicit FrameDestroyContext(PresShell* aPs) : mPresShell(aPs) {} void AddAnonymousContent(already_AddRefed&& aContent) { if (RefPtr content = aContent) { mAnonymousContent.AppendElement(std::move(content)); } } ~FrameDestroyContext(); private: PresShell* const mPresShell; AutoTArray, 100> mAnonymousContent; }; /** * Bit-flags specific to a given layout class id. */ enum class LayoutFrameClassFlags : uint32_t { None = 0, Leaf = 1 << 0, LeafDynamic = 1 << 1, MathML = 1 << 2, SVG = 1 << 3, SVGContainer = 1 << 4, BidiInlineContainer = 1 << 5, // The frame is for a replaced element, such as an image. Note that HTML //