Source code

Revision control

Other Tools

/* -*- 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/. */
/* base class of all rendering objects */
#include "nsIFrame.h"
#include <stdarg.h>
#include <algorithm>
#include "gfx2DGlue.h"
#include "gfxUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/DisplayPortUtils.h"
#include "mozilla/dom/ElementInlines.h"
#include "mozilla/dom/ImageTracker.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresShellInlines.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticAnalysisFunctions.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/SVGMaskFrame.h"
#include "mozilla/SVGObserverUtils.h"
#include "mozilla/SVGTextFrame.h"
#include "mozilla/SVGIntegrationUtils.h"
#include "mozilla/SVGUtils.h"
#include "mozilla/ToString.h"
#include "mozilla/ViewportUtils.h"
#include "nsCOMPtr.h"
#include "nsFieldSetFrame.h"
#include "nsFlexContainerFrame.h"
#include "nsFrameList.h"
#include "nsPlaceholderFrame.h"
#include "nsIBaseWindow.h"
#include "nsIContent.h"
#include "nsIContentInlines.h"
#include "nsContentUtils.h"
#include "nsCSSFrameConstructor.h"
#include "nsCSSProps.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSRendering.h"
#include "nsAtom.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsTableWrapperFrame.h"
#include "nsView.h"
#include "nsViewManager.h"
#include "nsIScrollableFrame.h"
#include "nsPresContext.h"
#include "nsPresContextInlines.h"
#include "nsStyleConsts.h"
#include "mozilla/Logging.h"
#include "nsLayoutUtils.h"
#include "LayoutLogging.h"
#include "mozilla/RestyleManager.h"
#include "nsImageFrame.h"
#include "nsInlineFrame.h"
#include "nsFrameSelection.h"
#include "nsGkAtoms.h"
#include "nsCSSAnonBoxes.h"
#include "nsCanvasFrame.h"
#include "nsFieldSetFrame.h"
#include "nsFrameTraversal.h"
#include "nsRange.h"
#include "nsITextControlFrame.h"
#include "nsNameSpaceManager.h"
#include "nsIPercentBSizeObserver.h"
#include "nsStyleStructInlines.h"
#include "FrameLayerBuilder.h"
#include "ImageLayers.h"
#include "nsBidiPresUtils.h"
#include "RubyUtils.h"
#include "TextOverflow.h"
#include "nsAnimationManager.h"
// For triple-click pref
#include "imgIRequest.h"
#include "nsError.h"
#include "nsContainerFrame.h"
#include "nsBoxLayoutState.h"
#include "nsBlockFrame.h"
#include "nsDisplayList.h"
#include "nsChangeHint.h"
#include "nsDeckFrame.h"
#include "nsSubDocumentFrame.h"
#include "RetainedDisplayListBuilder.h"
#include "gfxContext.h"
#include "nsAbsoluteContainingBlock.h"
#include "StickyScrollContainer.h"
#include "nsFontInflationData.h"
#include "nsRegion.h"
#include "nsIFrameInlines.h"
#include "nsStyleChangeList.h"
#include "nsWindowSizes.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/CSSClipPathInstance.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/EventStates.h"
#include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/ServoStyleSetInlines.h"
#include "mozilla/css/ImageLoader.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/SVGPathData.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/gfx/Tools.h"
#include "mozilla/layers/WebRenderUserData.h"
#include "mozilla/layout/ScrollAnchorContainer.h"
#include "nsPrintfCString.h"
#include "ActiveLayerTracker.h"
#include "nsITheme.h"
using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::layout;
typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
using nsStyleTransformMatrix::TransformReferenceBox;
const mozilla::LayoutFrameType nsIFrame::sLayoutFrameTypes[
#define FRAME_ID(...) 1 +
#define ABSTRACT_FRAME_ID(...)
#include "mozilla/FrameIdList.h"
#undef FRAME_ID
#undef ABSTRACT_FRAME_ID
0] = {
#define FRAME_ID(class_, type_, ...) mozilla::LayoutFrameType::type_,
#define ABSTRACT_FRAME_ID(...)
#include "mozilla/FrameIdList.h"
#undef FRAME_ID
#undef ABSTRACT_FRAME_ID
};
const nsIFrame::FrameClassBits nsIFrame::sFrameClassBits[
#define FRAME_ID(...) 1 +
#define ABSTRACT_FRAME_ID(...)
#include "mozilla/FrameIdList.h"
#undef FRAME_ID
#undef ABSTRACT_FRAME_ID
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
};
// Struct containing cached metrics for box-wrapped frames.
struct nsBoxLayoutMetrics {
nsSize mPrefSize;
nsSize mMinSize;
nsSize mMaxSize;
nsSize mBlockMinSize;
nsSize mBlockPrefSize;
nscoord mBlockAscent;
nscoord mFlex;
nscoord mAscent;
nsSize mLastSize;
};
struct nsContentAndOffset {
nsIContent* mContent = nullptr;
int32_t mOffset = 0;
};
// Some Misc #defines
#define SELECTION_DEBUG 0
#define FORCE_SELECTION_UPDATE 1
#define CALC_DEBUG 0
#include "nsILineIterator.h"
#include "prenv.h"
NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty, nsBoxLayoutMetrics)
static void InitBoxMetrics(nsIFrame* aFrame, bool aClear) {
if (aClear) {
aFrame->RemoveProperty(BoxMetricsProperty());
}
nsBoxLayoutMetrics* metrics = new nsBoxLayoutMetrics();
aFrame->SetProperty(BoxMetricsProperty(), metrics);
aFrame->nsIFrame::MarkIntrinsicISizesDirty();
metrics->mBlockAscent = 0;
metrics->mLastSize.SizeTo(0, 0);
}
// Utility function to set a nsRect-valued property table entry on aFrame,
// reusing the existing storage if the property happens to be already set.
template <typename T>
static void SetOrUpdateRectValuedProperty(
nsIFrame* aFrame, FrameProperties::Descriptor<T> aProperty,
const nsRect& aNewValue) {
bool found;
nsRect* rectStorage = aFrame->GetProperty(aProperty, &found);
if (!found) {
rectStorage = new nsRect(aNewValue);
aFrame->AddProperty(aProperty, rectStorage);
} else {
*rectStorage = aNewValue;
}
}
static bool IsXULBoxWrapped(const nsIFrame* aFrame) {
return aFrame->GetParent() && aFrame->GetParent()->IsXULBoxFrame() &&
!aFrame->IsXULBoxFrame();
}
void nsReflowStatus::UpdateTruncated(const ReflowInput& aReflowInput,
const ReflowOutput& aMetrics) {
const WritingMode containerWM = aMetrics.GetWritingMode();
if (aReflowInput.GetWritingMode().IsOrthogonalTo(containerWM)) {
// Orthogonal flows are always reflowed with an unconstrained dimension,
// so should never end up truncated (see ReflowInput::Init()).
mTruncated = false;
} else if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
aReflowInput.AvailableBSize() < aMetrics.BSize(containerWM) &&
!aReflowInput.mFlags.mIsTopOfPage) {
mTruncated = true;
} else {
mTruncated = false;
}
}
/* static */
void nsIFrame::DestroyAnonymousContent(
nsPresContext* aPresContext, already_AddRefed<nsIContent>&& aContent) {
if (nsCOMPtr<nsIContent> content = aContent) {
aPresContext->EventStateManager()->NativeAnonymousContentRemoved(content);
aPresContext->PresShell()->NativeAnonymousContentRemoved(content);
content->UnbindFromTree();
}
}
// Formerly the nsIFrameDebug interface
std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus) {
char complete = 'Y';
if (aStatus.IsIncomplete()) {
complete = 'N';
} else if (aStatus.IsOverflowIncomplete()) {
complete = 'O';
}
char brk = 'N';
if (aStatus.IsInlineBreakBefore()) {
brk = 'B';
} else if (aStatus.IsInlineBreakAfter()) {
brk = 'A';
}
aStream << "["
<< "Complete=" << complete << ","
<< "NIF=" << (aStatus.NextInFlowNeedsReflow() ? 'Y' : 'N') << ","
<< "Truncated=" << (aStatus.IsTruncated() ? 'Y' : 'N') << ","
<< "Break=" << brk << ","
<< "FirstLetter=" << (aStatus.FirstLetterComplete() ? 'Y' : 'N')
<< "]";
return aStream;
}
#ifdef DEBUG
static bool gShowFrameBorders = false;
void nsIFrame::ShowFrameBorders(bool aEnable) { gShowFrameBorders = aEnable; }
bool nsIFrame::GetShowFrameBorders() { return gShowFrameBorders; }
static bool gShowEventTargetFrameBorder = false;
void nsIFrame::ShowEventTargetFrameBorder(bool aEnable) {
gShowEventTargetFrameBorder = aEnable;
}
bool nsIFrame::GetShowEventTargetFrameBorder() {
return gShowEventTargetFrameBorder;
}
/**
* Note: the log module is created during library initialization which
* means that you cannot perform logging before then.
*/
mozilla::LazyLogModule nsIFrame::sFrameLogModule("frame");
#endif
NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,
nsAbsoluteContainingBlock)
bool nsIFrame::HasAbsolutelyPositionedChildren() const {
return IsAbsoluteContainer() &&
GetAbsoluteContainingBlock()->HasAbsoluteFrames();
}
nsAbsoluteContainingBlock* nsIFrame::GetAbsoluteContainingBlock() const {
NS_ASSERTION(IsAbsoluteContainer(),
"The frame is not marked as an abspos container correctly");
nsAbsoluteContainingBlock* absCB =
GetProperty(AbsoluteContainingBlockProperty());
NS_ASSERTION(absCB,
"The frame is marked as an abspos container but doesn't have "
"the property");
return absCB;
}
void nsIFrame::MarkAsAbsoluteContainingBlock() {
MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
NS_ASSERTION(!GetProperty(AbsoluteContainingBlockProperty()),
"Already has an abs-pos containing block property?");
NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
"Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
SetProperty(AbsoluteContainingBlockProperty(),
new nsAbsoluteContainingBlock(GetAbsoluteListID()));
}
void nsIFrame::MarkAsNotAbsoluteContainingBlock() {
NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
NS_ASSERTION(GetProperty(AbsoluteContainingBlockProperty()),
"Should have an abs-pos containing block property");
NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
"Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
RemoveProperty(AbsoluteContainingBlockProperty());
}
bool nsIFrame::CheckAndClearPaintedState() {
bool result = HasAnyStateBits(NS_FRAME_PAINTED_THEBES);
RemoveStateBits(NS_FRAME_PAINTED_THEBES);
for (const auto& childList : ChildLists()) {
for (nsIFrame* child : childList.mList) {
if (child->CheckAndClearPaintedState()) {
result = true;
}
}
}
return result;
}
bool nsIFrame::CheckAndClearDisplayListState() {
bool result = BuiltDisplayList();
SetBuiltDisplayList(false);
for (const auto& childList : ChildLists()) {
for (nsIFrame* child : childList.mList) {
if (child->CheckAndClearDisplayListState()) {
result = true;
}
}
}
return result;
}
bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
if (!StyleVisibility()->IsVisible()) {
return false;
}
if (PresShell()->IsUnderHiddenEmbedderElement()) {
return false;
}
const nsIFrame* frame = this;
while (frame) {
nsView* view = frame->GetView();
if (view && view->GetVisibility() == nsViewVisibility_kHide) return false;
nsIFrame* parent = frame->GetParent();
nsDeckFrame* deck = do_QueryFrame(parent);
if (deck) {
if (deck->GetSelectedBox() != frame) return false;
}
if (parent) {
frame = parent;
} else {
parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
if (!parent) break;
if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
parent->PresContext()->IsChrome() &&
!frame->PresContext()->IsChrome()) {
break;
}
frame = parent;
}
}
return true;
}
void nsIFrame::FindCloserFrameForSelection(
const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
aCurrentBestFrame->mXDistance,
aCurrentBestFrame->mYDistance)) {
aCurrentBestFrame->mFrame = this;
}
}
void nsIFrame::ContentStatesChanged(mozilla::EventStates aStates) {}
void WeakFrame::Clear(mozilla::PresShell* aPresShell) {
if (aPresShell) {
aPresShell->RemoveWeakFrame(this);
}
mFrame = nullptr;
}
AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
: mPrev(nullptr), mFrame(nullptr) {
Init(aOther.GetFrame());
}
void AutoWeakFrame::Clear(mozilla::PresShell* aPresShell) {
if (aPresShell) {
aPresShell->RemoveAutoWeakFrame(this);
}
mFrame = nullptr;
mPrev = nullptr;
}
AutoWeakFrame::~AutoWeakFrame() {
Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
}
void AutoWeakFrame::Init(nsIFrame* aFrame) {
Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
mFrame = aFrame;
if (mFrame) {
mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
NS_WARNING_ASSERTION(presShell, "Null PresShell in AutoWeakFrame!");
if (presShell) {
presShell->AddAutoWeakFrame(this);
} else {
mFrame = nullptr;
}
}
}
void WeakFrame::Init(nsIFrame* aFrame) {
Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
mFrame = aFrame;
if (mFrame) {
mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
MOZ_ASSERT(presShell, "Null PresShell in WeakFrame!");
if (presShell) {
presShell->AddWeakFrame(this);
} else {
mFrame = nullptr;
}
}
}
nsIFrame* NS_NewEmptyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
return new (aPresShell) nsIFrame(aStyle, aPresShell->GetPresContext());
}
nsIFrame::~nsIFrame() {
MOZ_COUNT_DTOR(nsIFrame);
MOZ_ASSERT(GetVisibility() != Visibility::ApproximatelyVisible,
"Visible nsFrame is being destroyed");
}
NS_IMPL_FRAMEARENA_HELPERS(nsIFrame)
// Dummy operator delete. Will never be called, but must be defined
// to satisfy some C++ ABIs.
void nsIFrame::operator delete(void*, size_t) {
MOZ_CRASH("nsIFrame::operator delete should never be called");
}
NS_QUERYFRAME_HEAD(nsIFrame)
NS_QUERYFRAME_ENTRY(nsIFrame)
NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
/////////////////////////////////////////////////////////////////////////////
// nsIFrame
static bool IsFontSizeInflationContainer(nsIFrame* aFrame,
const nsStyleDisplay* aStyleDisplay) {
/*
* Font size inflation is built around the idea that we're inflating
* the fonts for a pan-and-zoom UI so that when the user scales up a
* block or other container to fill the width of the device, the fonts
* will be readable. To do this, we need to pick what counts as a
* container.
*
* From a code perspective, the only hard requirement is that frames
* that are line participants
* (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never
* containers, since line layout assumes that the inflation is
* consistent within a line.
*
* This is not an imposition, since we obviously want a bunch of text
* (possibly with inline elements) flowing within a block to count the
* block (or higher) as its container.
*
* We also want form controls, including the text in the anonymous
* content inside of them, to match each other and the text next to
* them, so they and their anonymous content should also not be a
* container.
*
* However, because we can't reliably compute sizes across XUL during
* reflow, any XUL frame with a XUL parent is always a container.
*
* There are contexts where it would be nice if some blocks didn't
* count as a container, so that, for example, an indented quotation
* didn't end up with a smaller font size. However, it's hard to
* distinguish these situations where we really do want the indented
* thing to count as a container, so we don't try, and blocks are
* always containers.
*/
// The root frame should always be an inflation container.
if (!aFrame->GetParent()) {
return true;
}
nsIContent* content = aFrame->GetContent();
if (content && content->IsInNativeAnonymousSubtree()) {
// Native anonymous content shouldn't be a font inflation root,
// except for the canvas custom content container.
nsCanvasFrame* canvas = aFrame->PresShell()->GetCanvasFrame();
return canvas && canvas->GetCustomContentContainer() == content;
}
LayoutFrameType frameType = aFrame->Type();
bool isInline =
(nsStyleDisplay::IsInlineFlow(aFrame->GetDisplay()) ||
RubyUtils::IsRubyBox(frameType) ||
(aStyleDisplay->IsFloatingStyle() &&
frameType == LayoutFrameType::Letter) ||
// Given multiple frames for the same node, only the
// outer one should be considered a container.
// (Important, e.g., for nsSelectsAreaFrame.)
(aFrame->GetParent()->GetContent() == content) ||
(content &&
// Form controls shouldn't become inflation containers.
(content->IsAnyOfHTMLElements(
nsGkAtoms::option, nsGkAtoms::optgroup, nsGkAtoms::select,
nsGkAtoms::input, nsGkAtoms::button, nsGkAtoms::textarea)))) &&
!(aFrame->IsXULBoxFrame() && aFrame->GetParent()->IsXULBoxFrame());
NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || isInline ||
// br frames and mathml frames report being line
// participants even when their position or display is
// set
aFrame->IsBrFrame() ||
aFrame->IsFrameOfType(nsIFrame::eMathML),
"line participants must not be containers");
NS_ASSERTION(!aFrame->IsBulletFrame() || isInline,
"bullets should not be containers");
return !isInline;
}
static void MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) {
if (!SVGUtils::IsInSVGTextSubtree(aFrame)) {
return;
}
// We need to ensure that any non-display SVGTextFrames get reflowed when a
// child text frame gets new style. Thus we need to schedule a reflow in
// |DidSetComputedStyle|. We also need to call it from |DestroyFrom|,
// because otherwise we won't get notified when style changes to
// "display:none".
SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText));
nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
// Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
// anonymous block frame rather than our aFrame, since NS_FRAME_FIRST_REFLOW
// may be set on us if we're a new frame that has been inserted after the
// document's first reflow. (In which case this DidSetComputedStyle call may
// be happening under frame construction under a Reflow() call.)
if (!anonBlock || anonBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
return;
}
if (!svgTextFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) ||
svgTextFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)) {
return;
}
svgTextFrame->ScheduleReflowSVGNonDisplayText(IntrinsicDirty::StyleChange);
}
bool nsIFrame::IsPrimaryFrameOfRootOrBodyElement() const {
if (!IsPrimaryFrame()) {
return false;
}
nsIContent* content = GetContent();
Document* document = content->OwnerDoc();
return content == document->GetRootElement() ||
content == document->GetBodyElement();
}
bool nsIFrame::IsRenderedLegend() const {
if (auto* parent = GetParent(); parent && parent->IsFieldSetFrame()) {
return static_cast<nsFieldSetFrame*>(parent)->GetLegend() == this;
}
return false;
}
void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId());
MOZ_ASSERT(!mContent, "Double-initing a frame?");
NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) && !IsFrameOfType(eDEBUGNoFrames),
"IsFrameOfType implementation that doesn't call base class");
mContent = aContent;
mParent = aParent;
MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
if (aPrevInFlow) {
mWritingMode = aPrevInFlow->GetWritingMode();
// Copy some state bits from prev-in-flow (the bits that should apply
// throughout a continuation chain). The bits are sorted according to their
// order in nsFrameStateBits.h.
// clang-format off
AddStateBits(aPrevInFlow->GetStateBits() &
(NS_FRAME_GENERATED_CONTENT |
NS_FRAME_OUT_OF_FLOW |
NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN |
NS_FRAME_INDEPENDENT_SELECTION |
NS_FRAME_PART_OF_IBSPLIT |
NS_FRAME_MAY_BE_TRANSFORMED |
NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR));
// clang-format on
// Copy other bits in nsIFrame from prev-in-flow.
mHasColumnSpanSiblings = aPrevInFlow->HasColumnSpanSiblings();
} else {
PresContext()->ConstructedFrame();
}
if (GetParent()) {
if (MOZ_UNLIKELY(mContent == PresContext()->Document()->GetRootElement() &&
mContent == GetParent()->GetContent())) {
// Our content is the root element and we have the same content as our
// parent. That is, we are the internal anonymous frame of the root
// element. Copy the used mWritingMode from our parent because
// mDocElementContainingBlock gets its mWritingMode from <body>.
mWritingMode = GetParent()->GetWritingMode();
}
// Copy some state bits from our parent (the bits that should apply
// recursively throughout a subtree). The bits are sorted according to their
// order in nsFrameStateBits.h.
// clang-format off
AddStateBits(GetParent()->GetStateBits() &
(NS_FRAME_GENERATED_CONTENT |
NS_FRAME_INDEPENDENT_SELECTION |
NS_FRAME_IS_SVG_TEXT |
NS_FRAME_IN_POPUP |
NS_FRAME_IS_NONDISPLAY));
// clang-format on
if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
// Assume all frames in popups are visible.
IncApproximateVisibleCount();
}
}
if (aPrevInFlow) {
mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
} else if (mContent) {
// It's fine to fetch the EffectSet for the style frame here because in the
// following code we take care of the case where animations may target
// a different frame.
EffectSet* effectSet = EffectSet::GetEffectSetForStyleFrame(this);
if (effectSet) {
mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
if (effectSet->MayHaveTransformAnimation()) {
// If we are the inner table frame for display:table content, then
// transform animations should go on our parent frame (the table wrapper
// frame).
//
// We do this when initializing the child frame (table inner frame),
// because when initializng the table wrapper frame, we don't yet have
// access to its children so we can't tell if we have transform
// animations or not.
if (IsFrameOfType(eSupportsCSSTransforms)) {
mMayHaveTransformAnimation = true;
AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
} else if (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
MOZ_ASSERT(
aParent->IsFrameOfType(eSupportsCSSTransforms),
"Style frames that don't support transforms should have parents"
" that do");
aParent->mMayHaveTransformAnimation = true;
aParent->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
}
}
}
}
const nsStyleDisplay* disp = StyleDisplay();
if (disp->HasTransform(this)) {
// If 'transform' dynamically changes, RestyleManager takes care of
// updating this bit.
AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
}
if (disp->IsContainLayout() && disp->IsContainSize() &&
// All frames that support contain:layout also support contain:size.
IsFrameOfType(eSupportsContainLayoutAndPaint) && !IsTableWrapperFrame()) {
// In general, frames that have contain:layout+size can be reflow roots.
// (One exception: table-wrapper frames don't work well as reflow roots,
// because their inner-table ReflowInput init path tries to reuse & deref
// the wrapper's containing block reflow input, which may be null if we
// initiate reflow from the table-wrapper itself.)
//
// Changes to `contain` force frame reconstructions, so this bit can be set
// for the whole lifetime of this frame.
AddStateBits(NS_FRAME_REFLOW_ROOT);
}
if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) ||
!GetParent()
#ifdef DEBUG
// We have assertions that check inflation invariants even when
// font size inflation is not enabled.
|| true
#endif
) {
if (IsFontSizeInflationContainer(this, disp)) {
AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
if (!GetParent() ||
// I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this) ||
GetParent()->IsFlexContainerFrame() ||
GetParent()->IsGridContainerFrame()) {
AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
}
}
NS_ASSERTION(
GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER),
"root frame should always be a container");
}
if (PresShell()->AssumeAllFramesVisible() && TrackingVisibility()) {
IncApproximateVisibleCount();
}
DidSetComputedStyle(nullptr);
if (::IsXULBoxWrapped(this)) ::InitBoxMetrics(this, false);
// For a newly created frame, we need to update this frame's visibility state.
// Usually we update the state when the frame is restyled and has a
// VisibilityChange change hint but we don't generate any change hints for
// newly created frames.
// Note: We don't need to do this for placeholders since placeholders have
// different styles so that the styles don't have visibility:hidden even if
// the parent has visibility:hidden style. We also don't need to update the
// state when creating continuations because its visibility is the same as its
// prev-in-flow, and the animation code cares only primary frames.
if (!IsPlaceholderFrame() && !aPrevInFlow) {
UpdateVisibleDescendantsState();
}
}
void nsIFrame::DestroyFrom(nsIFrame* aDestructRoot,
PostDestroyData& aPostDestroyData) {
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
"destroy called on frame while scripts not blocked");
NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
"Frames should be removed before destruction.");
NS_ASSERTION(aDestructRoot, "Must specify destruct root");
MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
"NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?");
MaybeScheduleReflowSVGNonDisplayText(this);
SVGObserverUtils::InvalidateDirectRenderingObservers(this);
if (StyleDisplay()->mPosition == StylePositionProperty::Sticky) {
StickyScrollContainer* ssc =
StickyScrollContainer::GetStickyScrollContainerForFrame(this);
if (ssc) {
ssc->RemoveFrame(this);
}
}
nsPresContext* presContext = PresContext();
mozilla::PresShell* presShell = presContext->GetPresShell();
if (mState & NS_FRAME_OUT_OF_FLOW) {
nsPlaceholderFrame* placeholder = GetPlaceholderFrame();
NS_ASSERTION(
!placeholder || (aDestructRoot != this),
"Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
NS_ASSERTION(!placeholder || nsLayoutUtils::IsProperAncestorFrame(
aDestructRoot, placeholder),
"Placeholder relationship should have been torn down already; "
"this might mean we have a stray placeholder in the tree.");
if (placeholder) {
placeholder->SetOutOfFlowFrame(nullptr);
}
}
if (IsPrimaryFrame()) {
// This needs to happen before we clear our Properties() table.
ActiveLayerTracker::TransferActivityToContent(this, mContent);
}
ScrollAnchorContainer* anchor = nullptr;
if (IsScrollAnchor(&anchor)) {
anchor->InvalidateAnchor();
}
if (HasCSSAnimations() || HasCSSTransitions() ||
// It's fine to look up the style frame here since if we're destroying the
// frames for display:table content we should be destroying both wrapper
// and inner frame.
EffectSet::GetEffectSetForStyleFrame(this)) {
// If no new frame for this element is created by the end of the
// restyling process, stop animations and transitions for this frame
RestyleManager::AnimationsWithDestroyedFrame* adf =
presContext->RestyleManager()->GetAnimationsWithDestroyedFrame();
// AnimationsWithDestroyedFrame only lives during the restyling process.
if (adf) {
adf->Put(mContent, mComputedStyle);
}
}
// Disable visibility tracking. Note that we have to do this before we clear
// frame properties and lose track of whether we were previously visible.
// XXX(seth): It'd be ideal to assert that we're already marked nonvisible
// here, but it's unfortunately tricky to guarantee in the face of things like
// frame reconstruction induced by style changes.
DisableVisibilityTracking();
// Ensure that we're not in the approximately visible list anymore.
PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
presShell->NotifyDestroyingFrame(this);
if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
presShell->ClearFrameRefs(this);
}
nsView* view = GetView();
if (view) {
view->SetFrame(nullptr);
view->Destroy();
}
// Make sure that our deleted frame can't be returned from GetPrimaryFrame()
if (IsPrimaryFrame()) {
mContent->SetPrimaryFrame(nullptr);
// Pass the root of a generated content subtree (e.g. ::after/::before) to
// aPostDestroyData to unbind it after frame destruction is done.
if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
mContent->IsRootOfNativeAnonymousSubtree()) {
aPostDestroyData.AddAnonymousContent(mContent.forget());
}
}
// Remove all properties attached to the frame, to ensure any property
// destructors that need the frame pointer are handled properly.
RemoveAllProperties();
// Must retrieve the object ID before calling destructors, so the
// vtable is still valid.
//
// Note to future tweakers: having the method that returns the
// object size call the destructor will not avoid an indirect call;
// the compiler cannot devirtualize the call to the destructor even
// if it's from a method defined in the same class.
nsQueryFrame::FrameIID id = GetFrameId();
this->~nsIFrame();
#ifdef DEBUG
{
nsIFrame* rootFrame = presShell->GetRootFrame();
MOZ_ASSERT(rootFrame);
if (this != rootFrame) {
const RetainedDisplayListData* data =
GetRetainedDisplayListData(rootFrame);
const bool inModifiedList =
data && (data->GetFlags(this) &
RetainedDisplayListData::FrameFlags::Modified);
MOZ_ASSERT(!inModifiedList,
"A dtor added this frame to modified frames list!");
}
}
#endif
// Now that we're totally cleaned out, we need to add ourselves to
// the presshell's recycler.
presShell->FreeFrame(id, this);
}
nsresult nsIFrame::GetOffsets(int32_t& aStart, int32_t& aEnd) const {
aStart = 0;
aEnd = 0;
return NS_OK;
}
static void CompareLayers(
const nsStyleImageLayers* aFirstLayers,
const nsStyleImageLayers* aSecondLayers,
const std::function<void(imgRequestProxy* aReq)>& aCallback) {
NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers)) {
const auto& image = aFirstLayers->mLayers[i].mImage;
if (!image.IsImageRequestType() || !image.IsResolved()) {
continue;
}
// aCallback is called when the style image in aFirstLayers is thought to
// be different with the corresponded one in aSecondLayers
if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
(!aSecondLayers->mLayers[i].mImage.IsResolved() ||
image.GetImageRequest() !=
aSecondLayers->mLayers[i].mImage.GetImageRequest())) {
if (imgRequestProxy* req = image.GetImageRequest()) {
aCallback(req);
}
}
}
}
static void AddAndRemoveImageAssociations(
ImageLoader& aImageLoader, nsIFrame* aFrame,
const nsStyleImageLayers* aOldLayers,
const nsStyleImageLayers* aNewLayers) {
// If the old context had a background-image image, or mask-image image,
// and new context does not have the same image, clear the image load
// notifier (which keeps the image loading, if it still is) for the frame.
// We want to do this conservatively because some frames paint their
// backgrounds from some other frame's style data, and we don't want
// to clear those notifiers unless we have to. (They'll be reset
// when we paint, although we could miss a notification in that
// interval.)
if (aOldLayers && aFrame->HasImageRequest()) {
CompareLayers(aOldLayers, aNewLayers, [&](imgRequestProxy* aReq) {
aImageLoader.DisassociateRequestFromFrame(aReq, aFrame);
});
}
CompareLayers(aNewLayers, aOldLayers, [&](imgRequestProxy* aReq) {
aImageLoader.AssociateRequestToFrame(aReq, aFrame);
});
}
void nsIFrame::AddDisplayItem(nsDisplayItemBase* aItem) {
MOZ_DIAGNOSTIC_ASSERT(!mDisplayItems.Contains(aItem));
mDisplayItems.AppendElement(aItem);
}
bool nsIFrame::RemoveDisplayItem(nsDisplayItemBase* aItem) {
return mDisplayItems.RemoveElement(aItem);
}
bool nsIFrame::HasDisplayItems() { return !mDisplayItems.IsEmpty(); }
bool nsIFrame::HasDisplayItem(nsDisplayItemBase* aItem) {
return mDisplayItems.Contains(aItem);
}
bool nsIFrame::HasDisplayItem(uint32_t aKey) {
for (nsDisplayItemBase* i : mDisplayItems) {
if (i->GetPerFrameKey() == aKey) {
return true;
}
}
return false;
}
template <typename Condition>
static void DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) {
for (nsDisplayItemBase* i : aFrame->DisplayItems()) {
// Only discard items that are invalidated by this frame, as we're only
// guaranteed to rebuild those items. Table background items are created by
// the relevant table part, but have the cell frame as the primary frame,
// and we don't want to remove them if this is the cell.
if (aCondition(i) && i->FrameForInvalidation() == aFrame) {
i->SetCantBeReused();
}
}
}
static void DiscardOldItems(nsIFrame* aFrame) {
DiscardDisplayItems(
aFrame, [](nsDisplayItemBase* aItem) { return aItem->IsOldItem(); });
}
void nsIFrame::RemoveDisplayItemDataForDeletion() {
// Destroying a WebRenderUserDataTable can cause destruction of other objects
// which can remove frame properties in their destructor. If we delete a frame
// property it runs the destructor of the stored object in the middle of
// updating the frame property table, so if the destruction of that object
// causes another update to the frame property table it would leave the frame
// property table in an inconsistent state. So we remove it from the table and
// then destroy it. (bug 1530657)
WebRenderUserDataTable* userDataTable =
TakeProperty(WebRenderUserDataProperty::Key());
if (userDataTable) {
for (const auto& data : userDataTable->Values()) {
data->RemoveFromTable();
}
delete userDataTable;
}
FrameLayerBuilder::RemoveFrameFromLayerManager(this);
for (nsDisplayItemBase* i : DisplayItems()) {
if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
i->Frame()->MarkNeedsDisplayItemRebuild();
}
i->RemoveFrame(this);
}
DisplayItems().Clear();
if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
// Retained display lists are disabled, no need to update
// RetainedDisplayListData.
return;
}
const bool updateData = IsFrameModified() || HasOverrideDirtyRegion() ||
MayHaveWillChangeBudget();
if (!updateData) {
// No RetainedDisplayListData to update.
return;
}
nsIFrame* rootFrame = PresShell()->GetRootFrame();
MOZ_ASSERT(rootFrame);
RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
if (MayHaveWillChangeBudget()) {
// Keep the frame in list, so it can be removed from the will-change budget.
data->Flags(this) = RetainedDisplayListData::FrameFlags::HadWillChange;
return;
}
if (IsFrameModified() || HasOverrideDirtyRegion()) {
// Remove deleted frames from RetainedDisplayListData.
DebugOnly<bool> removed = data->Remove(this);
MOZ_ASSERT(removed,
"Frame had flags set, but it was not found in DisplayListData!");
}
}
void nsIFrame::MarkNeedsDisplayItemRebuild() {
if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() || IsFrameModified() ||
HasAnyStateBits(NS_FRAME_IN_POPUP)) {
// Skip frames that are already marked modified.
return;
}
if (Type() == LayoutFrameType::Placeholder) {
nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
if (oof) {
oof->MarkNeedsDisplayItemRebuild();
}
// Do not mark placeholder frames modified.
return;
}
if (!nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(this)) {
return;
}
nsIFrame* rootFrame = PresShell()->GetRootFrame();
MOZ_ASSERT(rootFrame);
if (rootFrame->IsFrameModified()) {
return;
}
RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
if (data->ModifiedFramesCount() >
StaticPrefs::layout_display_list_rebuild_frame_limit()) {
// If the modified frames count is above the rebuild limit, mark the root
// frame modified, and stop marking additional frames modified.
data->AddModifiedFrame(rootFrame);
rootFrame->SetFrameIsModified(true);
return;
}
data->AddModifiedFrame(this);
SetFrameIsModified(true);
MOZ_ASSERT(
PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0);
// Hopefully this is cheap, but we could use a frame state bit to note
// the presence of dependencies to speed it up.
for (nsDisplayItemBase* i : DisplayItems()) {
if (i->HasDeletedFrame() || i->Frame() == this) {
// Ignore the items with deleted frames, and the items with |this| as
// the primary frame.
continue;
}
if (i->GetDependentFrame() == this) {
// For items with |this| as a dependent frame, mark the primary frame
// for rebuild.
i->Frame()->MarkNeedsDisplayItemRebuild();
}
}
}
// Subclass hook for style post processing
/* virtual */
void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
MaybeScheduleReflowSVGNonDisplayText(this);
Document* doc = PresContext()->Document();
ImageLoader* loader = doc->StyleImageLoader();
// Continuing text frame doesn't initialize its continuation pointer before
// reaching here for the first time, so we have to exclude text frames. This
// doesn't affect correctness because text can't match selectors.
//
// FIXME(emilio): We should consider fixing that.
//
// TODO(emilio): Can we avoid doing some / all of the image stuff when
// isNonTextFirstContinuation is false? We should consider doing this just for
// primary frames and pseudos, but the first-line reparenting code makes it
// all bad, should get around to bug 1465474 eventually :(
const bool isNonText = !IsTextFrame();
if (isNonText) {
mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
}
const nsStyleImageLayers* oldLayers =
aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
: nullptr;
const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
oldLayers =
aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
newLayers = &StyleSVGReset()->mMask;
AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
const nsStyleDisplay* disp = StyleDisplay();
bool handleStickyChange = false;
if (aOldComputedStyle) {
// Detect style changes that should trigger a scroll anchor adjustment
// suppression.
bool needAnchorSuppression = false;
// If we detect a change on margin, padding or border, we store the old
// values on the frame itself between now and reflow, so if someone
// calls GetUsed(Margin|Border|Padding)() before the next reflow, we
// can give an accurate answer.
// We don't want to set the property if one already exists.
nsMargin oldValue(0, 0, 0, 0);
nsMargin newValue(0, 0, 0, 0);
const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
if (oldMargin->GetMargin(oldValue)) {
if (!StyleMargin()->GetMargin(newValue) || oldValue != newValue) {
if (!HasProperty(UsedMarginProperty())) {
AddProperty(UsedMarginProperty(), new nsMargin(oldValue));
}
needAnchorSuppression = true;
}
}
const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
if (oldPadding->GetPadding(oldValue)) {
if (!StylePadding()->GetPadding(newValue) || oldValue != newValue) {
if (!HasProperty(UsedPaddingProperty())) {
AddProperty(UsedPaddingProperty(), new nsMargin(oldValue));
}
needAnchorSuppression = true;
}
}
const nsStyleBorder* oldBorder = aOldComputedStyle->StyleBorder();
oldValue = oldBorder->GetComputedBorder();
newValue = StyleBorder()->GetComputedBorder();
if (oldValue != newValue && !HasProperty(UsedBorderProperty())) {
AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
}
const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
if (oldDisp->mOverflowAnchor != disp->mOverflowAnchor) {
if (auto* container = ScrollAnchorContainer::FindFor(this)) {
container->InvalidateAnchor();
}
if (nsIScrollableFrame* scrollableFrame = do_QueryFrame(this)) {
scrollableFrame->Anchor()->InvalidateAnchor();
}
}
if (mInScrollAnchorChain) {
const nsStylePosition* pos = StylePosition();
const nsStylePosition* oldPos = aOldComputedStyle->StylePosition();
if (!needAnchorSuppression &&
(oldPos->mOffset != pos->mOffset || oldPos->mWidth != pos->mWidth ||
oldPos->mMinWidth != pos->mMinWidth ||
oldPos->mMaxWidth != pos->mMaxWidth ||
oldPos->mHeight != pos->mHeight ||
oldPos->mMinHeight != pos->mMinHeight ||
oldPos->mMaxHeight != pos->mMaxHeight ||
oldDisp->mPosition != disp->mPosition ||
oldDisp->mTransform != disp->mTransform)) {
needAnchorSuppression = true;
}
if (needAnchorSuppression &&
StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
}
}
if (disp->mPosition != oldDisp->mPosition) {
if (!disp->IsRelativelyPositionedStyle() &&
oldDisp->IsRelativelyPositionedStyle()) {
RemoveProperty(NormalPositionProperty());
}
handleStickyChange = disp->mPosition == StylePositionProperty::Sticky ||
oldDisp->mPosition == StylePositionProperty::Sticky;
}
} else { // !aOldComputedStyle
handleStickyChange = disp->mPosition == StylePositionProperty::Sticky;
}
if (handleStickyChange && !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) &&
!GetPrevInFlow()) {
// Note that we only add first continuations, but we really only
// want to add first continuation-or-ib-split-siblings. But since we don't
// yet know if we're a later part of a block-in-inline split, we'll just
// add later members of a block-in-inline split here, and then
// StickyScrollContainer will remove them later.
if (auto* ssc =
StickyScrollContainer::GetStickyScrollContainerForFrame(this)) {
if (disp->mPosition == StylePositionProperty::Sticky) {
ssc->AddFrame(this);
} else {
ssc->RemoveFrame(this);
}
}
}
imgIRequest* oldBorderImage =
aOldComputedStyle
? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
: nullptr;
imgIRequest* newBorderImage = StyleBorder()->GetBorderImageRequest();
// FIXME (Bug 759996): The following is no longer true.
// For border-images, we can't be as conservative (we need to set the
// new loaders if there has been any change) since the CalcDifference
// call depended on the result of GetComputedBorder() and that result
// depends on whether the image has loaded, start the image load now
// so that we'll get notified when it completes loading and can do a
// restyle. Otherwise, the image might finish loading from the
// network before we start listening to its notifications, and then
// we'll never know that it's finished loading. Likewise, we want to
// do this for freshly-created frames to prevent a similar race if the
// image loads between reflow (which can depend on whether the image
// is loaded) and paint. We also don't really care about any callers who try
// to paint borders with a different style, because they won't have the
// correct size for the border either.
if (oldBorderImage != newBorderImage) {
// stop and restart the image loading/notification
if (oldBorderImage && HasImageRequest()) {
RemoveProperty(CachedBorderImageDataProperty());
loader->DisassociateRequestFromFrame(oldBorderImage, this);
}
if (newBorderImage) {
loader->AssociateRequestToFrame(newBorderImage, this);
}
}
auto GetShapeImageRequest = [](const ComputedStyle* aStyle) -> imgIRequest* {
if (!aStyle) {
return nullptr;
}
auto& shape = aStyle->StyleDisplay()->mShapeOutside;
if (!shape.IsImage()) {
return nullptr;
}
return shape.AsImage().GetImageRequest();
};
imgIRequest* oldShapeImage = GetShapeImageRequest(aOldComputedStyle);
imgIRequest* newShapeImage = GetShapeImageRequest(Style());
if (oldShapeImage != newShapeImage) {
if (oldShapeImage && HasImageRequest()) {
loader->DisassociateRequestFromFrame(oldShapeImage, this);
}
if (newShapeImage) {
loader->AssociateRequestToFrame(
newShapeImage, this,
ImageLoader::Flags::
RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking);
}
}
// SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with
// the first continuation so we need to check that in advance.
const bool isNonTextFirstContinuation = isNonText && !GetPrevContinuation();
if (isNonTextFirstContinuation) {
// Kick off loading of external SVG resources referenced from properties if
// any. This currently includes filter, clip-path, and mask.
SVGObserverUtils::InitiateResourceDocLoads(this);
}
// If the page contains markup that overrides text direction, and
// does not contain any characters that would activate the Unicode
// bidi algorithm, we need to call |SetBidiEnabled| on the pres
// context before reflow starts. See bug 115921.
if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
PresContext()->SetBidiEnabled();
}
// The following part is for caching offset-path:path(). We cache the
// flatten gfx path, so we don't have to rebuild and re-flattern it at
// each cycle if we have animations on offset-* with a fixed offset-path.
const StyleOffsetPath* oldPath =
aOldComputedStyle ? &aOldComputedStyle->StyleDisplay()->mOffsetPath
: nullptr;
const StyleOffsetPath& newPath = StyleDisplay()->mOffsetPath;
if (!oldPath || *oldPath != newPath) {
if (newPath.IsPath()) {
// Here we only need to build a valid path for motion path, so
// using the default values of stroke-width, stoke-linecap, and fill-rule
// is fine for now because what we want is to get the point and its normal
// vector along the path, instead of rendering it.
RefPtr<gfx::PathBuilder> builder =
gfxPlatform::GetPlatform()
->ScreenReferenceDrawTarget()
->CreatePathBuilder(gfx::FillRule::FILL_WINDING);
RefPtr<gfx::Path> path =
MotionPathUtils::BuildPath(newPath.AsPath(), builder);
if (path) {
// The newPath could be path('') (i.e. empty path), so its gfx path
// could be nullptr, and so we only set property for a non-empty path.
SetProperty(nsIFrame::OffsetPathCache(), path.forget().take());
} else {
// May have an old cached path, so we have to delete it.
RemoveProperty(nsIFrame::OffsetPathCache());
}
} else if (oldPath) {
RemoveProperty(nsIFrame::OffsetPathCache());
}
}
RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS | NS_FRAME_SIMPLE_DISPLAYLIST);
mMayHaveRoundedCorners = true;
}
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
void nsIFrame::AssertNewStyleIsSane(ComputedStyle& aNewStyle) {
MOZ_DIAGNOSTIC_ASSERT(
aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() ||
// ::first-line continuations are weird, this should probably be fixed via
(mComputedStyle->GetPseudoType() == PseudoStyleType::firstLine &&
aNewStyle.GetPseudoType() == PseudoStyleType::mozLineFrame) ||
// ::first-letter continuations are broken, in particular floating ones,
// see bug 1490281. The construction code tries to fix this up after the
// fact, then restyling undoes it...
(mComputedStyle->GetPseudoType() == PseudoStyleType::mozText &&
aNewStyle.GetPseudoType() == PseudoStyleType::firstLetterContinuation) ||
(mComputedStyle->GetPseudoType() ==
PseudoStyleType::firstLetterContinuation &&
aNewStyle.GetPseudoType() == PseudoStyleType::mozText));
}
#endif
void nsIFrame::ReparentFrameViewTo(nsViewManager* aViewManager,
nsView* aNewParentView,
nsView* aOldParentView) {
if (HasView()) {
#ifdef MOZ_XUL
if (IsMenuPopupFrame()) {
// This view must be parented by the root view, don't reparent it.
return;
}
#endif
nsView* view = GetView();
// Verify that the current parent view is what we think it is
// nsView* parentView;
// NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
aViewManager->RemoveChild(view);
// The view will remember the Z-order and other attributes that have been
// set on it.
nsView* insertBefore =
nsLayoutUtils::FindSiblingViewFor(aNewParentView, this);
aViewManager->InsertChild(aNewParentView, view, insertBefore,
insertBefore != nullptr);
} else if (HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW)) {
for (const auto& childList : ChildLists()) {
// Iterate the child frames, and check each child frame to see if it has
// a view
for (nsIFrame* child : childList.mList) {
child->ReparentFrameViewTo(aViewManager, aNewParentView,
aOldParentView);
}
}
}
}
void nsIFrame::SyncFrameViewProperties(nsView* aView) {
if (!aView) {
aView = GetView();
if (!aView) {
return;
}
}
nsViewManager* vm = aView->GetViewManager();
// Make sure visibility is correct. This only affects nsSubDocumentFrame.
if (!SupportsVisibilityHidden()) {
// See if the view should be hidden or visible
ComputedStyle* sc = Style();
vm->SetViewVisibility(aView, sc->StyleVisibility()->IsVisible()
? nsViewVisibility_kShow
: nsViewVisibility_kHide);
}
const auto zIndex = ZIndex();
const bool autoZIndex = !zIndex;
vm->SetViewZIndex(aView, autoZIndex, zIndex.valueOr(0));
}
void nsIFrame::CreateView() {
MOZ_ASSERT(!HasView());
nsView* parentView = GetParent()->GetClosestView();
MOZ_ASSERT(parentView, "no parent with view");
nsViewManager* viewManager = parentView->GetViewManager();
MOZ_ASSERT(viewManager, "null view manager");
nsView* view = viewManager->CreateView(GetRect(), parentView);
SyncFrameViewProperties(view);
nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, this);
// we insert this view 'above' the insertBefore view, unless insertBefore is
// null, in which case we want to call with aAbove == false to insert at the
// beginning in document order
viewManager->InsertChild(parentView, view, insertBefore,
insertBefore != nullptr);
// REVIEW: Don't create a widget for fixed-pos elements anymore.
// ComputeRepaintRegionForCopy will calculate the right area to repaint
// when we scroll.
// Reparent views on any child frames (or their descendants) to this
// view. We can just call ReparentFrameViewTo on this frame because
// we know this frame has no view, so it will crawl the children. Also,
// we know that any descendants with views must have 'parentView' as their
// parent view.
ReparentFrameViewTo(viewManager, view, parentView);
// Remember our view
SetView(view);
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("nsIFrame::CreateView: frame=%p view=%p", this, view));
}
// MSVC fails with link error "one or more multiply defined symbols found",
// gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
// etc if they are not defined.
#ifndef _MSC_VER
// static nsIFrame constants; initialized in the header file.
const nsIFrame::ChildListID nsIFrame::kPrincipalList;
const nsIFrame::ChildListID nsIFrame::kAbsoluteList;
const nsIFrame::ChildListID nsIFrame::kBulletList;
const nsIFrame::ChildListID nsIFrame::kCaptionList;
const nsIFrame::ChildListID