/* -*- 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/. */
/** * Note: the log module is created during library initialization which * means that you cannot perform logging before then.
*/
mozilla::LazyLogModule nsIFrame::sFrameLogModule("frame");
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 (constauto& childList : ChildLists()) { for (nsIFrame* child : childList.mList) { if (child->CheckAndClearPaintedState()) {
result = true;
}
}
} return result;
}
if (frame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) { returnfalse;
}
// This method is used to determine if a frame is focusable, because it's // called by nsIFrame::IsFocusable. `content-visibility: auto` should not // force this frame to be unfocusable, so we only take into account // `content-visibility: hidden` here. if (this != frame &&
frame->HidesContent(IncludeContentVisibility::Hidden)) { returnfalse;
}
if (nsIFrame* parent = frame->GetParent()) {
frame = parent;
} else {
parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame); if (!parent) { break;
}
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::operatordelete(void*, size_t) {
MOZ_CRASH("nsIFrame::operator delete should never be called");
}
staticbool 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::IsLineParticipant) 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()) { returntrue;
}
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 =
aFrame->GetDisplay().IsInlineFlow() || 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)));
NS_ASSERTION(!aFrame->IsLineParticipant() || isInline || // br frames and mathml frames report being line // participants even when their position or display is // set
aFrame->IsBrFrame() || aFrame->IsMathMLFrame(), "line participants must not be containers"); return !isInline;
}
staticvoid MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) { if (!aFrame->IsInSVGTextSubtree()) { 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;
}
bool nsIFrame::ShouldPropagateRepaintsToRoot() const { if (!IsPrimaryFrame()) { // special case for table frames because style images are associated to the // table frame, but the table wrapper frame is the primary frame if (IsTableFrame()) {
MOZ_ASSERT(GetParent() && GetParent()->IsTableWrapperFrame()); return GetParent()->ShouldPropagateRepaintsToRoot();
}
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.
// 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();
} elseif (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::GetForStyleFrame(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 (SupportsCSSTransforms()) {
mMayHaveTransformAnimation = true;
AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
} elseif (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
MOZ_ASSERT(
aParent->SupportsCSSTransforms(), "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 (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 (TrackingVisibility() && PresShell()->AssumeAllFramesVisible()) {
IncApproximateVisibleCount();
}
DidSetComputedStyle(nullptr);
// 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();
}
if (!aPrevInFlow && HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) { // We aren't going to get a reflow, so nothing else will call // InvalidateRenderingObservers, we have to do it here.
SVGObserverUtils::InvalidateRenderingObservers(this);
}
}
constbool wasQueryContainer = oldDisp && oldDisp->IsQueryContainer(); constbool isQueryContainer = disp->IsQueryContainer(); if (wasQueryContainer != isQueryContainer) { auto* pc = PresContext(); if (isQueryContainer) {
pc->RegisterContainerQueryFrame(this);
} else {
pc->UnregisterContainerQueryFrame(this);
}
}
constauto cv = disp->ContentVisibility(*this); if (!oldDisp || oldDisp->ContentVisibility(*this) != cv) { if (cv == StyleContentVisibility::Auto) {
PresShell()->RegisterContentVisibilityAutoFrame(this);
} else { if (auto* element = Element::FromNodeOrNull(GetContent())) {
element->ClearContentRelevancy();
}
PresShell()->UnregisterContentVisibilityAutoFrame(this);
}
PresContext()->SetNeedsToUpdateHiddenByContentVisibilityForAnimations();
}
HandleLastRememberedSize();
}
void nsIFrame::Destroy(DestroyContext& aContext) {
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), "destroy called on frame while scripts not blocked");
NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(), "Frames should be removed before destruction.");
MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT), "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?");
constauto* disp = StyleDisplay(); if (disp->mPosition == StylePositionProperty::Sticky) { if (auto* ssc =
StickyScrollContainer::GetStickyScrollContainerForFrame(this)) {
ssc->RemoveFrame(this);
}
}
if (HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { if (nsPlaceholderFrame* placeholder = GetPlaceholderFrame()) {
placeholder->SetOutOfFlowFrame(nullptr);
}
}
nsPresContext* pc = PresContext();
mozilla::PresShell* ps = pc->GetPresShell(); if (IsPrimaryFrame()) { if (disp->IsQueryContainer()) {
pc->UnregisterContainerQueryFrame(this);
} if (disp->ContentVisibility(*this) == StyleContentVisibility::Auto) {
ps->UnregisterContentVisibilityAutoFrame(this);
} // 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::GetForStyleFrame(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 =
pc->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.
ps->RemoveFrameFromApproximatelyVisibleList(this);
ps->NotifyDestroyingFrame(this);
if (HasAnyStateBits(NS_FRAME_EXTERNAL_REFERENCE)) {
ps->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()) {
aContext.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();
// 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);
}
}
}
}
staticvoid 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);
});
}
bool nsIFrame::HasDisplayItem(uint32_t aKey) { for (nsDisplayItem* i : mDisplayItems) { if (i->GetPerFrameKey() == aKey) { returntrue;
}
} returnfalse;
}
template <typename Condition> staticvoid DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) { for (nsDisplayItem* 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();
}
}
}
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 (constauto& data : userDataTable->Values()) {
data->RemoveFromTable();
} delete userDataTable;
}
if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) { // Retained display lists are disabled, no need to update // RetainedDisplayListData. return;
}
for (nsDisplayItem* i : DisplayItems()) { if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
i->Frame()->MarkNeedsDisplayItemRebuild();
}
i->RemoveFrame(this);
}
DisplayItems().Clear();
nsAutoString name; #ifdef DEBUG_FRAME_DUMP if (DL_LOG_TEST(LogLevel::Debug)) {
GetFrameName(name);
} #endif
DL_LOGV("Removing display item data for frame %p (%s)", this,
NS_ConvertUTF16toUTF8(name).get());
auto* data = builder->Data(); if (MayHaveWillChangeBudget()) { // Keep the frame in list, so it can be removed from the will-change budget.
data->Flags(this) = RetainedDisplayListData::FrameFlag::HadWillChange;
} else {
data->Remove(this);
}
}
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 (rootFrame->IsFrameModified()) { // The whole frame tree is modified. return;
}
auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(this); if (!builder) {
MOZ_ASSERT(DisplayItems().IsEmpty()); return;
}
RetainedDisplayListData* data = builder->Data();
MOZ_ASSERT(data);
if (data->AtModifiedFrameLimit()) { // This marks the whole frame tree modified. // See |RetainedDisplayListBuilder::ShouldBuildPartial()|.
data->AddModifiedFrame(rootFrame); return;
}
nsAutoString name; #ifdef DEBUG_FRAME_DUMP if (DL_LOG_TEST(LogLevel::Debug)) {
GetFrameName(name);
} #endif
// Hopefully this is cheap, but we could use a frame state bit to note // the presence of dependencies to speed it up. for (nsDisplayItem* 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) { #ifdef ACCESSIBILITY // Don't notify for reconstructed frames here, since the frame is still being // constructed at this point and so LocalAccessible::GetFrame() will return // null. Style changes for reconstructed frames are handled in // DocAccessible::PruneOrInsertSubtree. if (aOldComputedStyle) { if (nsAccessibilityService* accService = GetAccService()) {
accService->NotifyOfComputedStyleChange(PresShell(), mContent);
}
} #endif
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 :( constbool isNonText = !IsTextFrame(); if (isNonText) {
mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
}
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()) {
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();
};
// SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with // the first continuation so we need to check that in advance. constbool 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) { // FIXME: Bug 1837042. Cache all basic shapes. if (newPath.IsPath()) {
RefPtr<gfx::PathBuilder> builder = MotionPathUtils::GetPathBuilder();
RefPtr<gfx::Path> path =
MotionPathUtils::BuildSVGPath(newPath.AsSVGPathData(), 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());
}
} elseif (oldPath) {
RemoveProperty(nsIFrame::OffsetPathCache());
}
}
if (IsPrimaryFrame()) {
MOZ_ASSERT(aOldComputedStyle);
HandlePrimaryFrameStyleChange(aOldComputedStyle);
}
void nsIFrame::HandleLastRememberedSize() {
MOZ_ASSERT(IsPrimaryFrame()); // Storing a last remembered size requires contain-intrinsic-size. if (!StaticPrefs::layout_css_contain_intrinsic_size_enabled()) { return;
} auto* element = Element::FromNodeOrNull(mContent); if (!element) { return;
} const WritingMode wm = GetWritingMode(); const nsStylePosition* stylePos = StylePosition(); bool canRememberBSize = stylePos->ContainIntrinsicBSize(wm).HasAuto(); bool canRememberISize = stylePos->ContainIntrinsicISize(wm).HasAuto(); if (!canRememberBSize) {
element->RemoveLastRememberedBSize();
} if (!canRememberISize) {
element->RemoveLastRememberedISize();
} if ((canRememberBSize || canRememberISize) && !HidesContent()) { bool isNonReplacedInline = IsLineParticipant() && !IsReplaced(); if (!isNonReplacedInline) {
PresContext()->Document()->ObserveForLastRememberedSize(*element); return;
}
}
PresContext()->Document()->UnobserveForLastRememberedSize(*element);
}
#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 // bug 1465474.
(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) { if (HasView()) { if (IsMenuPopupFrame()) { // This view must be parented by the root view, don't reparent it. return;
}
nsView* view = GetView();
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);
} elseif (HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW)) { for (constauto& 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);
}
}
}
}
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()
? ViewVisibility::Show
: ViewVisibility::Hide);
}
}
if (nsMargin* m = GetProperty(UsedMarginProperty())) {
margin = *m;
} elseif (!StyleMargin()->GetMargin(margin)) { // If we get here, our caller probably shouldn't be calling us...
NS_ERROR( "Returning bogus 0-sized margin, because this margin " "depends on layout & isn't cached!");
} return margin;
}
const nsStyleDisplay* disp = StyleDisplay(); if (IsThemed(disp)) { // Theme methods don't use const-ness.
nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
nsPresContext* pc = PresContext();
LayoutDeviceIntMargin widgetPadding; if (pc->Theme()->GetWidgetPadding(pc->DeviceContext(), mutable_this,
disp->EffectiveAppearance(),
&widgetPadding)) { return LayoutDevicePixel::ToAppUnits(widgetPadding,
pc->AppUnitsPerDevPixel());
}
}
if (nsMargin* p = GetProperty(UsedPaddingProperty())) {
padding = *p;
} elseif (!StylePadding()->GetPadding(padding)) { // If we get here, our caller probably shouldn't be calling us...
NS_ERROR( "Returning bogus 0-sized padding, because this padding " "depends on layout & isn't cached!");
} return padding;
}
bool nsIFrame::Combines3DTransformWithAncestors() const { // Check these first as they are faster then both calls below and are we are // likely to hit the early return (backface hidden is uncommon and // GetReferenceFrame is a hot caller of this which only calls this if // IsCSSTransformed is false). if (!IsCSSTransformed() && !BackfaceIsHidden()) { returnfalse;
}
nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame(); return parent && parent->Extend3DContext();
}
bool nsIFrame::In3DContextAndBackfaceIsHidden() const { // While both tests fail most of the time, test BackfaceIsHidden() // first since it's likely to fail faster. return BackfaceIsHidden() && Combines3DTransformWithAncestors();
}
// css3-background specifies this algorithm for reducing // corner radii when they are too big. bool haveRadius = false; double ratio = 1.0f; for (constauto side : mozilla::AllPhysicalSides()) {
uint32_t hc1 = SideToHalfCorner(side, false, true);
uint32_t hc2 = SideToHalfCorner(side, true, true);
nscoord length =
SideIsVertical(side) ? aBorderArea.height : aBorderArea.width;
nscoord sum = aRadii[hc1] + aRadii[hc2]; if (sum) {
haveRadius = true; // avoid floating point division in the normal case if (length < sum) {
ratio = std::min(ratio, double(length) / sum);
}
}
} if (ratio < 1.0) { for (constauto corner : mozilla::AllPhysicalHalfCorners()) {
aRadii[corner] *= ratio;
}
}
return haveRadius;
}
void nsIFrame::AdjustBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) { auto AdjustOffset = [](const uint32_t aRadius, const nscoord aOffset) { // Implement the cubic formula to adjust offset when aOffset > 0 and // aRadius / aOffset < 1. // https://drafts.csswg.org/css-shapes/#valdef-shape-box-margin-box if (aOffset > 0) { constdouble ratio = aRadius / double(aOffset); if (ratio < 1.0) { return nscoord(aOffset * (1.0 + std::pow(ratio - 1, 3)));
}
} return aOffset;
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.