Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/layout/painting/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 192 kB image not shown  

Quelle  nsCSSRendering.cpp   Sprache: C

 
/* -*- 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/. */


/* utility functions for drawing borders and backgrounds */

#include "nsCSSRendering.h"

#include <ctime>

#include "gfx2DGlue.h"
#include "gfxContext.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Helpers.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/PresShell.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/SVGImageContext.h"
#include "gfxFont.h"
#include "ScaledFontBase.h"
#include "skia/include/core/SkTextBlob.h"

#include "BorderConsts.h"
#include "nsCanvasFrame.h"
#include "nsStyleConsts.h"
#include "nsPresContext.h"
#include "nsIFrame.h"
#include "nsIFrameInlines.h"
#include "nsPageSequenceFrame.h"
#include "nsPoint.h"
#include "nsRect.h"
#include "nsFrameManager.h"
#include "nsGkAtoms.h"
#include "nsCSSAnonBoxes.h"
#include "nsIContent.h"
#include "mozilla/dom/DocumentInlines.h"
#include "imgIContainer.h"
#include "ImageOps.h"
#include "nsCSSColorUtils.h"
#include "nsITheme.h"
#include "nsLayoutUtils.h"
#include "nsBlockFrame.h"
#include "nsStyleStructInlines.h"
#include "nsCSSFrameConstructor.h"
#include "nsCSSProps.h"
#include "nsContentUtils.h"
#include "gfxDrawable.h"
#include "nsCSSRenderingBorders.h"
#include "mozilla/css/ImageLoader.h"
#include "ImageContainer.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/Telemetry.h"
#include "gfxUtils.h"
#include "gfxGradientCache.h"
#include "nsInlineFrame.h"
#include "nsRubyTextContainerFrame.h"
#include <algorithm>
#include "TextDrawTarget.h"

using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::gfx;
using namespace mozilla::image;
using mozilla::CSSSizeOrRatio;
using mozilla::dom::Document;

static int gFrameTreeLockCount = 0;

// To avoid storing this data on nsInlineFrame (bloat) and to avoid
// recalculating this for each frame in a continuation (perf), hold
// a cache of various coordinate information that we need in order
// to paint inline backgrounds.
struct InlineBackgroundData {
  InlineBackgroundData()
      : mFrame(nullptr),
        mLineContainer(nullptr),
        mContinuationPoint(0),
        mUnbrokenMeasure(0),
        mLineContinuationPoint(0),
        mPIStartBorderData{},
        mBidiEnabled(false),
        mVertical(false) {}

  ~InlineBackgroundData() = default;

  void Reset() {
    mBoundingBox.SetRect(0, 0, 0, 0);
    mContinuationPoint = mLineContinuationPoint = mUnbrokenMeasure = 0;
    mFrame = mLineContainer = nullptr;
    mPIStartBorderData.Reset();
  }

  /**
   * Return a continuous rect for (an inline) aFrame relative to the
   * continuation that draws the left-most part of the background.
   * This is used when painting backgrounds.
   */

  nsRect GetContinuousRect(nsIFrame* aFrame) {
    MOZ_ASSERT(static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)));

    SetFrame(aFrame);

    nscoord pos;  // an x coordinate if writing-mode is horizontal;
                  // y coordinate if vertical
    if (mBidiEnabled) {
      pos = mLineContinuationPoint;

      // Scan continuations on the same line as aFrame and accumulate the widths
      // of frames that are to the left (if this is an LTR block) or right
      // (if it's RTL) of the current one.
      bool isRtlBlock = (mLineContainer->StyleVisibility()->mDirection ==
                         StyleDirection::Rtl);
      nscoord curOffset = mVertical ? aFrame->GetOffsetTo(mLineContainer).y
                                    : aFrame->GetOffsetTo(mLineContainer).x;

      // If the continuation is fluid we know inlineFrame is not on the same
      // line. If it's not fluid, we need to test further to be sure.
      nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
      while (inlineFrame && !inlineFrame->GetNextInFlow() &&
             AreOnSameLine(aFrame, inlineFrame)) {
        nscoord frameOffset = mVertical
                                  ? inlineFrame->GetOffsetTo(mLineContainer).y
                                  : inlineFrame->GetOffsetTo(mLineContainer).x;
        if (isRtlBlock == (frameOffset >= curOffset)) {
          pos += mVertical ? inlineFrame->GetSize().height
                           : inlineFrame->GetSize().width;
        }
        inlineFrame = inlineFrame->GetPrevContinuation();
      }

      inlineFrame = aFrame->GetNextContinuation();
      while (inlineFrame && !inlineFrame->GetPrevInFlow() &&
             AreOnSameLine(aFrame, inlineFrame)) {
        nscoord frameOffset = mVertical
                                  ? inlineFrame->GetOffsetTo(mLineContainer).y
                                  : inlineFrame->GetOffsetTo(mLineContainer).x;
        if (isRtlBlock == (frameOffset >= curOffset)) {
          pos += mVertical ? inlineFrame->GetSize().height
                           : inlineFrame->GetSize().width;
        }
        inlineFrame = inlineFrame->GetNextContinuation();
      }
      if (isRtlBlock) {
        // aFrame itself is also to the right of its left edge, so add its
        // width.
        pos += mVertical ? aFrame->GetSize().height : aFrame->GetSize().width;
        // pos is now the distance from the left [top] edge of aFrame to the
        // right [bottom] edge of the unbroken content. Change it to indicate
        // the distance from the left [top] edge of the unbroken content to the
        // left [top] edge of aFrame.
        pos = mUnbrokenMeasure - pos;
      }
    } else {
      pos = mContinuationPoint;
    }

    // Assume background-origin: border and return a rect with offsets
    // relative to (0,0).  If we have a different background-origin,
    // then our rect should be deflated appropriately by our caller.
    return mVertical
               ? nsRect(0, -pos, mFrame->GetSize().width, mUnbrokenMeasure)
               : nsRect(-pos, 0, mUnbrokenMeasure, mFrame->GetSize().height);
  }

  /**
   * Return a continuous rect for (an inline) aFrame relative to the
   * continuation that should draw the left[top]-border.  This is used when
   * painting borders and clipping backgrounds.  This may NOT be the same
   * continuous rect as for drawing backgrounds; the continuation with the
   * left[top]-border might be somewhere in the middle of that rect (e.g. BIDI),
   * in those cases we need the reverse background order starting at the
   * left[top]-border continuation.
   */

  nsRect GetBorderContinuousRect(nsIFrame* aFrame, nsRect aBorderArea) {
    // Calling GetContinuousRect(aFrame) here may lead to Reset/Init which
    // resets our mPIStartBorderData so we save it ...
    PhysicalInlineStartBorderData saved(mPIStartBorderData);
    nsRect joinedBorderArea = GetContinuousRect(aFrame);
    if (!saved.mIsValid || saved.mFrame != mPIStartBorderData.mFrame) {
      if (aFrame == mPIStartBorderData.mFrame) {
        if (mVertical) {
          mPIStartBorderData.SetCoord(joinedBorderArea.y);
        } else {
          mPIStartBorderData.SetCoord(joinedBorderArea.x);
        }
      } else if (mPIStartBorderData.mFrame) {
        // Copy data to a temporary object so that computing the
        // continous rect here doesn't clobber our normal state.
        InlineBackgroundData temp = *this;
        if (mVertical) {
          mPIStartBorderData.SetCoord(
              temp.GetContinuousRect(mPIStartBorderData.mFrame).y);
        } else {
          mPIStartBorderData.SetCoord(
              temp.GetContinuousRect(mPIStartBorderData.mFrame).x);
        }
      }
    } else {
      // ... and restore it when possible.
      mPIStartBorderData.SetCoord(saved.mCoord);
    }
    if (mVertical) {
      if (joinedBorderArea.y > mPIStartBorderData.mCoord) {
        joinedBorderArea.y =
            -(mUnbrokenMeasure + joinedBorderArea.y - aBorderArea.height);
      } else {
        joinedBorderArea.y -= mPIStartBorderData.mCoord;
      }
    } else {
      if (joinedBorderArea.x > mPIStartBorderData.mCoord) {
        joinedBorderArea.x =
            -(mUnbrokenMeasure + joinedBorderArea.x - aBorderArea.width);
      } else {
        joinedBorderArea.x -= mPIStartBorderData.mCoord;
      }
    }
    return joinedBorderArea;
  }

  nsRect GetBoundingRect(nsIFrame* aFrame) {
    SetFrame(aFrame);

    // Move the offsets relative to (0,0) which puts the bounding box into
    // our coordinate system rather than our parent's.  We do this by
    // moving it the back distance from us to the bounding box.
    // This also assumes background-origin: border, so our caller will
    // need to deflate us if needed.
    nsRect boundingBox(mBoundingBox);
    nsPoint point = mFrame->GetPosition();
    boundingBox.MoveBy(-point.x, -point.y);

    return boundingBox;
  }

 protected:
  // This is a coordinate on the inline axis, but is not a true logical inline-
  // coord because it is always measured from left to right (if horizontal) or
  // from top to bottom (if vertical), ignoring any bidi RTL directionality.
  // We'll call this "physical inline start", or PIStart for short.
  struct PhysicalInlineStartBorderData {
    nsIFrame* mFrame;  // the continuation that may have a left-border
    nscoord mCoord;    // cached GetContinuousRect(mFrame).x or .y
    bool mIsValid;     // true if mCoord is valid
    void Reset() {
      mFrame = nullptr;
      mIsValid = false;
    }
    void SetCoord(nscoord aCoord) {
      mCoord = aCoord;
      mIsValid = true;
    }
  };

  nsIFrame* mFrame;
  nsIFrame* mLineContainer;
  nsRect mBoundingBox;
  nscoord mContinuationPoint;
  nscoord mUnbrokenMeasure;
  nscoord mLineContinuationPoint;
  PhysicalInlineStartBorderData mPIStartBorderData;
  bool mBidiEnabled;
  bool mVertical;

  void SetFrame(nsIFrame* aFrame) {
    MOZ_ASSERT(aFrame, "Need a frame");
    NS_ASSERTION(gFrameTreeLockCount > 0,
                 "Can't call this when frame tree is not locked");

    if (aFrame == mFrame) {
      return;
    }

    nsIFrame* prevContinuation = GetPrevContinuation(aFrame);

    if (!prevContinuation || mFrame != prevContinuation) {
      // Ok, we've got the wrong frame.  We have to start from scratch.
      Reset();
      Init(aFrame);
      return;
    }

    // Get our last frame's size and add its width to our continuation
    // point before we cache the new frame.
    mContinuationPoint +=
        mVertical ? mFrame->GetSize().height : mFrame->GetSize().width;

    // If this a new line, update mLineContinuationPoint.
    if (mBidiEnabled &&
        (aFrame->GetPrevInFlow() || !AreOnSameLine(mFrame, aFrame))) {
      mLineContinuationPoint = mContinuationPoint;
    }

    mFrame = aFrame;
  }

  nsIFrame* GetPrevContinuation(nsIFrame* aFrame) {
    nsIFrame* prevCont = aFrame->GetPrevContinuation();
    if (!prevCont && aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
      nsIFrame* block = aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
      if (block) {
        // The {ib} properties are only stored on first continuations
        NS_ASSERTION(!block->GetPrevContinuation(),
                     "Incorrect value for IBSplitPrevSibling");
        prevCont = block->GetProperty(nsIFrame::IBSplitPrevSibling());
        NS_ASSERTION(prevCont, "How did that happen?");
      }
    }
    return prevCont;
  }

  nsIFrame* GetNextContinuation(nsIFrame* aFrame) {
    nsIFrame* nextCont = aFrame->GetNextContinuation();
    if (!nextCont && aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
      // The {ib} properties are only stored on first continuations
      aFrame = aFrame->FirstContinuation();
      nsIFrame* block = aFrame->GetProperty(nsIFrame::IBSplitSibling());
      if (block) {
        nextCont = block->GetProperty(nsIFrame::IBSplitSibling());
        NS_ASSERTION(nextCont, "How did that happen?");
      }
    }
    return nextCont;
  }

  void Init(nsIFrame* aFrame) {
    mPIStartBorderData.Reset();
    mBidiEnabled = aFrame->PresContext()->BidiEnabled();
    if (mBidiEnabled) {
      // Find the line container frame
      mLineContainer = aFrame;
      while (mLineContainer && mLineContainer->IsLineParticipant()) {
        mLineContainer = mLineContainer->GetParent();
      }

      MOZ_ASSERT(mLineContainer, "Cannot find line containing frame.");
      MOZ_ASSERT(mLineContainer != aFrame,
                 "line container frame "
                 "should be an ancestor of the target frame.");
    }

    mVertical = aFrame->GetWritingMode().IsVertical();

    // Start with the previous flow frame as our continuation point
    // is the total of the widths of the previous frames.
    nsIFrame* inlineFrame = GetPrevContinuation(aFrame);
    bool changedLines = false;
    while (inlineFrame) {
      if (!mPIStartBorderData.mFrame &&
          !(mVertical ? inlineFrame->GetSkipSides().Top()
                      : inlineFrame->GetSkipSides().Left())) {
        mPIStartBorderData.mFrame = inlineFrame;
      }
      nsRect rect = inlineFrame->GetRect();
      mContinuationPoint += mVertical ? rect.height : rect.width;
      if (mBidiEnabled &&
          (changedLines || !AreOnSameLine(aFrame, inlineFrame))) {
        mLineContinuationPoint += mVertical ? rect.height : rect.width;
        changedLines = true;
      }
      mUnbrokenMeasure += mVertical ? rect.height : rect.width;
      mBoundingBox.UnionRect(mBoundingBox, rect);
      inlineFrame = GetPrevContinuation(inlineFrame);
    }

    // Next add this frame and subsequent frames to the bounding box and
    // unbroken width.
    inlineFrame = aFrame;
    while (inlineFrame) {
      if (!mPIStartBorderData.mFrame &&
          !(mVertical ? inlineFrame->GetSkipSides().Top()
                      : inlineFrame->GetSkipSides().Left())) {
        mPIStartBorderData.mFrame = inlineFrame;
      }
      nsRect rect = inlineFrame->GetRect();
      mUnbrokenMeasure += mVertical ? rect.height : rect.width;
      mBoundingBox.UnionRect(mBoundingBox, rect);
      inlineFrame = GetNextContinuation(inlineFrame);
    }

    mFrame = aFrame;
  }

  bool AreOnSameLine(nsIFrame* aFrame1, nsIFrame* aFrame2) {
    if (nsBlockFrame* blockFrame = do_QueryFrame(mLineContainer)) {
      bool isValid1, isValid2;
      nsBlockInFlowLineIterator it1(blockFrame, aFrame1, &isValid1);
      nsBlockInFlowLineIterator it2(blockFrame, aFrame2, &isValid2);
      return isValid1 && isValid2 &&
             // Make sure aFrame1 and aFrame2 are in the same continuation of
             // blockFrame.
             it1.GetContainer() == it2.GetContainer() &&
             // And on the same line in it
             it1.GetLine().get() == it2.GetLine().get();
    }
    if (nsRubyTextContainerFrame* rtcFrame = do_QueryFrame(mLineContainer)) {
      nsBlockFrame* block = nsLayoutUtils::FindNearestBlockAncestor(rtcFrame);
      // Ruby text container can only hold one line of text, so if they
      // are in the same continuation, they are in the same line. Since
      // ruby text containers are bidi isolate, they are never split for
      // bidi reordering, which means being in different continuation
      // indicates being in different lines.
      for (nsIFrame* frame = rtcFrame->FirstContinuation(); frame;
           frame = frame->GetNextContinuation()) {
        bool isDescendant1 =
            nsLayoutUtils::IsProperAncestorFrame(frame, aFrame1, block);
        bool isDescendant2 =
            nsLayoutUtils::IsProperAncestorFrame(frame, aFrame2, block);
        if (isDescendant1 && isDescendant2) {
          return true;
        }
        if (isDescendant1 || isDescendant2) {
          return false;
        }
      }
      MOZ_ASSERT_UNREACHABLE("None of the frames is a descendant of this rtc?");
    }
    MOZ_ASSERT_UNREACHABLE("Do we have any other type of line container?");
    return false;
  }
};

static StaticAutoPtr<InlineBackgroundData> gInlineBGData;

// Initialize any static variables used by nsCSSRendering.
void nsCSSRendering::Init() {
  NS_ASSERTION(!gInlineBGData, "Init called twice");
  gInlineBGData = new InlineBackgroundData();
}

// Clean up any global variables used by nsCSSRendering.
void nsCSSRendering::Shutdown() { gInlineBGData = nullptr; }

/**
 * Make a bevel color
 */

static nscolor MakeBevelColor(mozilla::Side whichSide, StyleBorderStyle style,
                              nscolor aBorderColor) {
  nscolor colors[2];
  nscolor theColor;

  // Given a background color and a border color
  // calculate the color used for the shading
  NS_GetSpecial3DColors(colors, aBorderColor);

  if ((style == StyleBorderStyle::Outset) ||
      (style == StyleBorderStyle::Ridge)) {
    // Flip colors for these two border styles
    switch (whichSide) {
      case eSideBottom:
        whichSide = eSideTop;
        break;
      case eSideRight:
        whichSide = eSideLeft;
        break;
      case eSideTop:
        whichSide = eSideBottom;
        break;
      case eSideLeft:
        whichSide = eSideRight;
        break;
    }
  }

  switch (whichSide) {
    case eSideBottom:
      theColor = colors[1];
      break;
    case eSideRight:
      theColor = colors[1];
      break;
    case eSideTop:
      theColor = colors[0];
      break;
    case eSideLeft:
    default:
      theColor = colors[0];
      break;
  }
  return theColor;
}

static bool GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
                     const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
                     nscoord aRadii[8]) {
  bool haveRoundedCorners;
  nsSize sz = aBorderArea.Size();
  nsSize frameSize = aForFrame->GetSize();
  if (&aBorder == aForFrame->StyleBorder() &&
      frameSize == aOrigBorderArea.Size()) {
    haveRoundedCorners = aForFrame->GetBorderRadii(sz, sz, Sides(), aRadii);
  } else {
    haveRoundedCorners = nsIFrame::ComputeBorderRadii(
        aBorder.mBorderRadius, frameSize, sz, Sides(), aRadii);
  }

  return haveRoundedCorners;
}

static bool GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
                     const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
                     RectCornerRadii* aBgRadii) {
  nscoord radii[8];
  bool haveRoundedCorners =
      GetRadii(aForFrame, aBorder, aOrigBorderArea, aBorderArea, radii);

  if (haveRoundedCorners) {
    auto d2a = aForFrame->PresContext()->AppUnitsPerDevPixel();
    nsCSSRendering::ComputePixelRadii(radii, d2a, aBgRadii);
  }
  return haveRoundedCorners;
}

static nsRect JoinBoxesForBlockAxisSlice(nsIFrame* aFrame,
                                         const nsRect& aBorderArea) {
  // Inflate the block-axis size as if our continuations were laid out
  // adjacent in that axis.  Note that we don't touch the inline size.
  const auto wm = aFrame->GetWritingMode();
  const nsSize dummyContainerSize;
  LogicalRect borderArea(wm, aBorderArea, dummyContainerSize);
  nscoord bSize = 0;
  nsIFrame* f = aFrame->GetNextContinuation();
  for (; f; f = f->GetNextContinuation()) {
    bSize += f->BSize(wm);
  }
  borderArea.BSize(wm) += bSize;
  bSize = 0;
  f = aFrame->GetPrevContinuation();
  for (; f; f = f->GetPrevContinuation()) {
    bSize += f->BSize(wm);
  }
  borderArea.BStart(wm) -= bSize;
  borderArea.BSize(wm) += bSize;
  return borderArea.GetPhysicalRect(wm, dummyContainerSize);
}

/**
 * Inflate aBorderArea which is relative to aFrame's origin to calculate
 * a hypothetical non-split frame area for all the continuations.
 * See "Joining Boxes for 'slice'" in
 * http://dev.w3.org/csswg/css-break/#break-decoration
 */

enum InlineBoxOrder { eForBorder, eForBackground };
static nsRect JoinBoxesForSlice(nsIFrame* aFrame, const nsRect& aBorderArea,
                                InlineBoxOrder aOrder) {
  if (static_cast<nsInlineFrame*>(do_QueryFrame(aFrame))) {
    return (aOrder == eForBorder
                ? gInlineBGData->GetBorderContinuousRect(aFrame, aBorderArea)
                : gInlineBGData->GetContinuousRect(aFrame)) +
           aBorderArea.TopLeft();
  }
  return JoinBoxesForBlockAxisSlice(aFrame, aBorderArea);
}

/* static */
bool nsCSSRendering::IsBoxDecorationSlice(const nsStyleBorder& aStyleBorder) {
  return aStyleBorder.mBoxDecorationBreak == StyleBoxDecorationBreak::Slice;
}

/* static */
nsRect nsCSSRendering::BoxDecorationRectForBorder(
    nsIFrame* aFrame, const nsRect& aBorderArea, Sides aSkipSides,
    const nsStyleBorder* aStyleBorder) {
  if (!aStyleBorder) {
    aStyleBorder = aFrame->StyleBorder();
  }
  // If aSkipSides.IsEmpty() then there are no continuations, or it's
  // a ::first-letter that wants all border sides on the first continuation.
  return IsBoxDecorationSlice(*aStyleBorder) && !aSkipSides.IsEmpty()
             ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBorder)
             : aBorderArea;
}

/* static */
nsRect nsCSSRendering::BoxDecorationRectForBackground(
    nsIFrame* aFrame, const nsRect& aBorderArea, Sides aSkipSides,
    const nsStyleBorder* aStyleBorder) {
  if (!aStyleBorder) {
    aStyleBorder = aFrame->StyleBorder();
  }
  // If aSkipSides.IsEmpty() then there are no continuations, or it's
  // a ::first-letter that wants all border sides on the first continuation.
  return IsBoxDecorationSlice(*aStyleBorder) && !aSkipSides.IsEmpty()
             ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBackground)
             : aBorderArea;
}

//----------------------------------------------------------------------
// Thebes Border Rendering Code Start

/*
 * Compute the float-pixel radii that should be used for drawing
 * this border/outline, given the various input bits.
 */

/* static */
void nsCSSRendering::ComputePixelRadii(const nscoord* aAppUnitsRadii,
                                       nscoord aAppUnitsPerPixel,
                                       RectCornerRadii* oBorderRadii) {
  Float radii[8];
  for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
    radii[corner] = Float(aAppUnitsRadii[corner]) / aAppUnitsPerPixel;
  }

  (*oBorderRadii)[C_TL] = Size(radii[eCornerTopLeftX], radii[eCornerTopLeftY]);
  (*oBorderRadii)[C_TR] =
      Size(radii[eCornerTopRightX], radii[eCornerTopRightY]);
  (*oBorderRadii)[C_BR] =
      Size(radii[eCornerBottomRightX], radii[eCornerBottomRightY]);
  (*oBorderRadii)[C_BL] =
      Size(radii[eCornerBottomLeftX], radii[eCornerBottomLeftY]);
}

static Maybe<nsStyleBorder> GetBorderIfVisited(const ComputedStyle& aStyle) {
  Maybe<nsStyleBorder> result;
  // Don't check RelevantLinkVisited here, since we want to take the
  // same amount of time whether or not it's true.
  const ComputedStyle* styleIfVisited = aStyle.GetStyleIfVisited();
  if (MOZ_LIKELY(!styleIfVisited)) {
    return result;
  }

  result.emplace(*aStyle.StyleBorder());
  auto& newBorder = result.ref();
  for (const auto side : mozilla::AllPhysicalSides()) {
    nscolor color = aStyle.GetVisitedDependentColor(
        nsStyleBorder::BorderColorFieldFor(side));
    newBorder.BorderColorFor(side) = StyleColor::FromColor(color);
  }

  return result;
}

ImgDrawResult nsCSSRendering::PaintBorder(
    nsPresContext* aPresContext, gfxContext& aRenderingContext,
    nsIFrame* aForFrame, const nsRect& aDirtyRect, const nsRect& aBorderArea,
    ComputedStyle* aStyle, PaintBorderFlags aFlags, Sides aSkipSides) {
  AUTO_PROFILER_LABEL("nsCSSRendering::PaintBorder", GRAPHICS);

  Maybe<nsStyleBorder> visitedBorder = GetBorderIfVisited(*aStyle);
  return PaintBorderWithStyleBorder(
      aPresContext, aRenderingContext, aForFrame, aDirtyRect, aBorderArea,
      visitedBorder.refOr(*aStyle->StyleBorder()), aStyle, aFlags, aSkipSides);
}

Maybe<nsCSSBorderRenderer> nsCSSRendering::CreateBorderRenderer(
    nsPresContext* aPresContext, DrawTarget* aDrawTarget, nsIFrame* aForFrame,
    const nsRect& aDirtyRect, const nsRect& aBorderArea, ComputedStyle* aStyle,
    bool* aOutBorderIsEmpty, Sides aSkipSides) {
  Maybe<nsStyleBorder> visitedBorder = GetBorderIfVisited(*aStyle);
  return CreateBorderRendererWithStyleBorder(
      aPresContext, aDrawTarget, aForFrame, aDirtyRect, aBorderArea,
      visitedBorder.refOr(*aStyle->StyleBorder()), aStyle, aOutBorderIsEmpty,
      aSkipSides);
}

ImgDrawResult nsCSSRendering::CreateWebRenderCommandsForBorder(
    nsDisplayItem* aItem, nsIFrame* aForFrame, const nsRect& aBorderArea,
    mozilla::wr::DisplayListBuilder& aBuilder,
    mozilla::wr::IpcResourceUpdateQueue& aResources,
    const mozilla::layers::StackingContextHelper& aSc,
    mozilla::layers::RenderRootStateManager* aManager,
    nsDisplayListBuilder* aDisplayListBuilder) {
  const auto* style = aForFrame->Style();
  Maybe<nsStyleBorder> visitedBorder = GetBorderIfVisited(*style);
  return nsCSSRendering::CreateWebRenderCommandsForBorderWithStyleBorder(
      aItem, aForFrame, aBorderArea, aBuilder, aResources, aSc, aManager,
      aDisplayListBuilder, visitedBorder.refOr(*style->StyleBorder()));
}

void nsCSSRendering::CreateWebRenderCommandsForNullBorder(
    nsDisplayItem* aItem, nsIFrame* aForFrame, const nsRect& aBorderArea,
    mozilla::wr::DisplayListBuilder& aBuilder,
    mozilla::wr::IpcResourceUpdateQueue& aResources,
    const mozilla::layers::StackingContextHelper& aSc,
    const nsStyleBorder& aStyleBorder) {
  bool borderIsEmpty = false;
  Maybe<nsCSSBorderRenderer> br =
      nsCSSRendering::CreateNullBorderRendererWithStyleBorder(
          aForFrame->PresContext(), nullptr, aForFrame, nsRect(), aBorderArea,
          aStyleBorder, aForFrame->Style(), &borderIsEmpty,
          aForFrame->GetSkipSides());
  if (!borderIsEmpty && br) {
    br->CreateWebRenderCommands(aItem, aBuilder, aResources, aSc);
  }
}

ImgDrawResult nsCSSRendering::CreateWebRenderCommandsForBorderWithStyleBorder(
    nsDisplayItem* aItem, nsIFrame* aForFrame, const nsRect& aBorderArea,
    mozilla::wr::DisplayListBuilder& aBuilder,
    mozilla::wr::IpcResourceUpdateQueue& aResources,
    const mozilla::layers::StackingContextHelper& aSc,
    mozilla::layers::RenderRootStateManager* aManager,
    nsDisplayListBuilder* aDisplayListBuilder,
    const nsStyleBorder& aStyleBorder) {
  auto& borderImage = aStyleBorder.mBorderImageSource;
  // First try to create commands for simple borders.
  if (borderImage.IsNone()) {
    CreateWebRenderCommandsForNullBorder(
        aItem, aForFrame, aBorderArea, aBuilder, aResources, aSc, aStyleBorder);
    return ImgDrawResult::SUCCESS;
  }

  // Next we try image and gradient borders. Gradients are not supported at
  // this very moment.
  if (!borderImage.IsImageRequestType()) {
    return ImgDrawResult::NOT_SUPPORTED;
  }

  if (aStyleBorder.mBorderImageRepeat._0 ==
          StyleBorderImageRepeatKeyword::Space ||
      aStyleBorder.mBorderImageRepeat._1 ==
          StyleBorderImageRepeatKeyword::Space) {
    return ImgDrawResult::NOT_SUPPORTED;
  }

  uint32_t flags = 0;
  if (aDisplayListBuilder->IsPaintingToWindow()) {
    flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
  }
  if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
    flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
  }

  bool dummy;
  image::ImgDrawResult result;
  Maybe<nsCSSBorderImageRenderer> bir =
      nsCSSBorderImageRenderer::CreateBorderImageRenderer(
          aForFrame->PresContext(), aForFrame, aBorderArea, aStyleBorder,
          aItem->GetBounds(aDisplayListBuilder, &dummy),
          aForFrame->GetSkipSides(), flags, &result);

  if (!bir) {
    // We aren't ready. Try to fallback to the null border image if present but
    // return the draw result for the border image renderer.
    CreateWebRenderCommandsForNullBorder(
        aItem, aForFrame, aBorderArea, aBuilder, aResources, aSc, aStyleBorder);
    return result;
  }

  return bir->CreateWebRenderCommands(aItem, aForFrame, aBuilder, aResources,
                                      aSc, aManager, aDisplayListBuilder);
}

static nsCSSBorderRenderer ConstructBorderRenderer(
    nsPresContext* aPresContext, ComputedStyle* aStyle, DrawTarget* aDrawTarget,
    nsIFrame* aForFrame, const nsRect& aDirtyRect, const nsRect& aBorderArea,
    const nsStyleBorder& aStyleBorder, Sides aSkipSides, bool* aNeedsClip) {
  nsMargin border = aStyleBorder.GetComputedBorder();

  // Compute the outermost boundary of the area that might be painted.
  // Same coordinate space as aBorderArea & aBGClipRect.
  nsRect joinedBorderArea = nsCSSRendering::BoxDecorationRectForBorder(
      aForFrame, aBorderArea, aSkipSides, &aStyleBorder);
  RectCornerRadii bgRadii;
  ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);

  PrintAsFormatString(" joinedBorderArea: %d %d %d %d\n", joinedBorderArea.x,
                      joinedBorderArea.y, joinedBorderArea.width,
                      joinedBorderArea.height);

  // start drawing
  if (nsCSSRendering::IsBoxDecorationSlice(aStyleBorder)) {
    if (joinedBorderArea.IsEqualEdges(aBorderArea)) {
      // No need for a clip, just skip the sides we don't want.
      border.ApplySkipSides(aSkipSides);
    } else {
      // We're drawing borders around the joined continuation boxes so we need
      // to clip that to the slice that we want for this frame.
      *aNeedsClip = true;
    }
  } else {
    MOZ_ASSERT(joinedBorderArea.IsEqualEdges(aBorderArea),
               "Should use aBorderArea for box-decoration-break:clone");
    MOZ_ASSERT(
        aForFrame->GetSkipSides().IsEmpty() ||
            aForFrame->IsTrueOverflowContainer() ||
            aForFrame->IsColumnSetFrame(),  // a little broader than column-rule
        "Should not skip sides for box-decoration-break:clone except "
        "::first-letter/line continuations or other frame types that "
        "don't have borders but those shouldn't reach this point. "
        "Overflow containers do reach this point though, as does "
        "column-rule drawing (which always involves a columnset).");
    border.ApplySkipSides(aSkipSides);
  }

  // Convert to dev pixels.
  nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
  Rect joinedBorderAreaPx = NSRectToRect(joinedBorderArea, oneDevPixel);
  Float borderWidths[4] = {
      Float(border.top) / oneDevPixel, Float(border.right) / oneDevPixel,
      Float(border.bottom) / oneDevPixel, Float(border.left) / oneDevPixel};
  Rect dirtyRect = NSRectToRect(aDirtyRect, oneDevPixel);

  StyleBorderStyle borderStyles[4];
  nscolor borderColors[4];

  // pull out styles, colors
  for (const auto i : mozilla::AllPhysicalSides()) {
    borderStyles[i] = aStyleBorder.GetBorderStyle(i);
    borderColors[i] = aStyleBorder.BorderColorFor(i).CalcColor(*aStyle);
  }

  PrintAsFormatString(
      " borderStyles: %d %d %d %d\n"static_cast<int>(borderStyles[0]),
      static_cast<int>(borderStyles[1]), static_cast<int>(borderStyles[2]),
      static_cast<int>(borderStyles[3]));

  return nsCSSBorderRenderer(
      aPresContext, aDrawTarget, dirtyRect, joinedBorderAreaPx, borderStyles,
      borderWidths, bgRadii, borderColors, !aForFrame->BackfaceIsHidden(),
      *aNeedsClip ? Some(NSRectToRect(aBorderArea, oneDevPixel)) : Nothing());
}

ImgDrawResult nsCSSRendering::PaintBorderWithStyleBorder(
    nsPresContext* aPresContext, gfxContext& aRenderingContext,
    nsIFrame* aForFrame, const nsRect& aDirtyRect, const nsRect& aBorderArea,
    const nsStyleBorder& aStyleBorder, ComputedStyle* aStyle,
    PaintBorderFlags aFlags, Sides aSkipSides) {
  DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();

  PrintAsStringNewline("++ PaintBorder");

  // Check to see if we have an appearance defined.  If so, we let the theme
  // renderer draw the border.  DO not get the data from aForFrame, since the
  // passed in ComputedStyle may be different!  Always use |aStyle|!
  StyleAppearance appearance = aStyle->StyleDisplay()->EffectiveAppearance();
  if (appearance != StyleAppearance::None) {
    nsITheme* theme = aPresContext->Theme();
    if (theme->ThemeSupportsWidget(aPresContext, aForFrame, appearance)) {
      return ImgDrawResult::SUCCESS;  // Let the theme handle it.
    }
  }

  if (!aStyleBorder.mBorderImageSource.IsNone()) {
    ImgDrawResult result = ImgDrawResult::SUCCESS;

    uint32_t irFlags = 0;
    if (aFlags & PaintBorderFlags::SyncDecodeImages) {
      irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
    }

    // Creating the border image renderer will request a decode, and we rely on
    // that happening.
    Maybe<nsCSSBorderImageRenderer> renderer =
        nsCSSBorderImageRenderer::CreateBorderImageRenderer(
            aPresContext, aForFrame, aBorderArea, aStyleBorder, aDirtyRect,
            aSkipSides, irFlags, &result);
    // renderer was created successfully, which means border image is ready to
    // be used.
    if (renderer) {
      MOZ_ASSERT(result == ImgDrawResult::SUCCESS);
      return renderer->DrawBorderImage(aPresContext, aRenderingContext,
                                       aForFrame, aDirtyRect);
    }
  }

  ImgDrawResult result = ImgDrawResult::SUCCESS;

  // If we had a border-image, but it wasn't loaded, then we should return
  // ImgDrawResult::NOT_READY; we'll want to try again if we do a paint with
  // sync decoding enabled.
  if (!aStyleBorder.mBorderImageSource.IsNone()) {
    result = ImgDrawResult::NOT_READY;
  }

  nsMargin border = aStyleBorder.GetComputedBorder();
  if (0 == border.left && 0 == border.right && 0 == border.top &&
      0 == border.bottom) {
    // Empty border area
    return result;
  }

  bool needsClip = false;
  nsCSSBorderRenderer br = ConstructBorderRenderer(
      aPresContext, aStyle, &aDrawTarget, aForFrame, aDirtyRect, aBorderArea,
      aStyleBorder, aSkipSides, &needsClip);
  if (needsClip) {
    aDrawTarget.PushClipRect(NSRectToSnappedRect(
        aBorderArea, aForFrame->PresContext()->AppUnitsPerDevPixel(),
        aDrawTarget));
  }

  br.DrawBorders();

  if (needsClip) {
    aDrawTarget.PopClip();
  }

  PrintAsStringNewline();

  return result;
}

Maybe<nsCSSBorderRenderer> nsCSSRendering::CreateBorderRendererWithStyleBorder(
    nsPresContext* aPresContext, DrawTarget* aDrawTarget, nsIFrame* aForFrame,
    const nsRect& aDirtyRect, const nsRect& aBorderArea,
    const nsStyleBorder& aStyleBorder, ComputedStyle* aStyle,
    bool* aOutBorderIsEmpty, Sides aSkipSides) {
  if (!aStyleBorder.mBorderImageSource.IsNone()) {
    return Nothing();
  }
  return CreateNullBorderRendererWithStyleBorder(
      aPresContext, aDrawTarget, aForFrame, aDirtyRect, aBorderArea,
      aStyleBorder, aStyle, aOutBorderIsEmpty, aSkipSides);
}

Maybe<nsCSSBorderRenderer>
nsCSSRendering::CreateNullBorderRendererWithStyleBorder(
    nsPresContext* aPresContext, DrawTarget* aDrawTarget, nsIFrame* aForFrame,
    const nsRect& aDirtyRect, const nsRect& aBorderArea,
    const nsStyleBorder& aStyleBorder, ComputedStyle* aStyle,
    bool* aOutBorderIsEmpty, Sides aSkipSides) {
  StyleAppearance appearance = aStyle->StyleDisplay()->EffectiveAppearance();
  if (appearance != StyleAppearance::None) {
    nsITheme* theme = aPresContext->Theme();
    if (theme->ThemeSupportsWidget(aPresContext, aForFrame, appearance)) {
      // The border will be draw as part of the themed background item created
      // for this same frame. If no themed background item was created then not
      // drawing also matches that we do without webrender and what
      // nsDisplayBorder does for themed borders.
      if (aOutBorderIsEmpty) {
        *aOutBorderIsEmpty = true;
      }
      return Nothing();
    }
  }

  nsMargin border = aStyleBorder.GetComputedBorder();
  if (0 == border.left && 0 == border.right && 0 == border.top &&
      0 == border.bottom) {
    // Empty border area
    if (aOutBorderIsEmpty) {
      *aOutBorderIsEmpty = true;
    }
    return Nothing();
  }

  bool needsClip = false;
  nsCSSBorderRenderer br = ConstructBorderRenderer(
      aPresContext, aStyle, aDrawTarget, aForFrame, aDirtyRect, aBorderArea,
      aStyleBorder, aSkipSides, &needsClip);
  return Some(br);
}

Maybe<nsCSSBorderRenderer>
nsCSSRendering::CreateBorderRendererForNonThemedOutline(
    nsPresContext* aPresContext, DrawTarget* aDrawTarget, nsIFrame* aForFrame,
    const nsRect& aDirtyRect, const nsRect& aInnerRect, ComputedStyle* aStyle) {
  // Get our ComputedStyle's color struct.
  const nsStyleOutline* ourOutline = aStyle->StyleOutline();
  if (!ourOutline->ShouldPaintOutline()) {
    // Empty outline
    return Nothing();
  }

  nsRect innerRect = aInnerRect;

  const nsSize effectiveOffset = ourOutline->EffectiveOffsetFor(innerRect);
  innerRect.Inflate(effectiveOffset);

  // If the dirty rect is completely inside the border area (e.g., only the
  // content is being painted), then we can skip out now
  // XXX this isn't exactly true for rounded borders, where the inside curves
  // may encroach into the content area.  A safer calculation would be to
  // shorten insideRect by the radius one each side before performing this test.
  if (innerRect.Contains(aDirtyRect)) {
    return Nothing();
  }

  const nscoord width = ourOutline->GetOutlineWidth();

  StyleBorderStyle outlineStyle;
  // Themed outlines are handled by our callers, if supported.
  if (ourOutline->mOutlineStyle.IsAuto()) {
    if (width == 0) {
      return Nothing();  // empty outline
    }
    // http://dev.w3.org/csswg/css-ui/#outline
    // "User agents may treat 'auto' as 'solid'."
    outlineStyle = StyleBorderStyle::Solid;
  } else {
    outlineStyle = ourOutline->mOutlineStyle.AsBorderStyle();
  }

  RectCornerRadii outlineRadii;
  nsRect outerRect = innerRect;
  outerRect.Inflate(width);

  const nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
  Rect oRect(NSRectToRect(outerRect, oneDevPixel));

  const Float outlineWidths[4] = {
      Float(width) / oneDevPixel, Float(width) / oneDevPixel,
      Float(width) / oneDevPixel, Float(width) / oneDevPixel};

  // convert the radii
  nscoord twipsRadii[8];

  // get the radius for our outline
  if (aForFrame->GetBorderRadii(twipsRadii)) {
    RectCornerRadii innerRadii;
    ComputePixelRadii(twipsRadii, oneDevPixel, &innerRadii);

    const auto devPxOffset = LayoutDeviceSize::FromAppUnits(
        effectiveOffset, aPresContext->AppUnitsPerDevPixel());

    const Float widths[4] = {outlineWidths[0] + devPxOffset.Height(),
                             outlineWidths[1] + devPxOffset.Width(),
                             outlineWidths[2] + devPxOffset.Height(),
                             outlineWidths[3] + devPxOffset.Width()};
    nsCSSBorderRenderer::ComputeOuterRadii(innerRadii, widths, &outlineRadii);
  }

  StyleBorderStyle outlineStyles[4] = {outlineStyle, outlineStyle, outlineStyle,
                                       outlineStyle};

  // This handles treating the initial color as 'currentColor'; if we
  // ever want 'invert' back we'll need to do a bit of work here too.
  nscolor outlineColor =
      aStyle->GetVisitedDependentColor(&nsStyleOutline::mOutlineColor);
  nscolor outlineColors[4] = {outlineColor, outlineColor, outlineColor,
                              outlineColor};

  Rect dirtyRect = NSRectToRect(aDirtyRect, oneDevPixel);

  return Some(nsCSSBorderRenderer(
      aPresContext, aDrawTarget, dirtyRect, oRect, outlineStyles, outlineWidths,
      outlineRadii, outlineColors, !aForFrame->BackfaceIsHidden(), Nothing()));
}

void nsCSSRendering::PaintNonThemedOutline(nsPresContext* aPresContext,
                                           gfxContext& aRenderingContext,
                                           nsIFrame* aForFrame,
                                           const nsRect& aDirtyRect,
                                           const nsRect& aInnerRect,
                                           ComputedStyle* aStyle) {
  Maybe<nsCSSBorderRenderer> br = CreateBorderRendererForNonThemedOutline(
      aPresContext, aRenderingContext.GetDrawTarget(), aForFrame, aDirtyRect,
      aInnerRect, aStyle);
  if (!br) {
    return;
  }

  // start drawing
  br->DrawBorders();

  PrintAsStringNewline();
}

void nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
                                DrawTarget* aDrawTarget,
                                const nsRect& aFocusRect, nscolor aColor) {
  nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1);
  nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);

  Rect focusRect(NSRectToRect(aFocusRect, oneDevPixel));

  RectCornerRadii focusRadii;
  {
    nscoord twipsRadii[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    ComputePixelRadii(twipsRadii, oneDevPixel, &focusRadii);
  }
  Float focusWidths[4] = {
      Float(oneCSSPixel) / oneDevPixel, Float(oneCSSPixel) / oneDevPixel,
      Float(oneCSSPixel) / oneDevPixel, Float(oneCSSPixel) / oneDevPixel};

  StyleBorderStyle focusStyles[4] = {
      StyleBorderStyle::Dotted, StyleBorderStyle::Dotted,
      StyleBorderStyle::Dotted, StyleBorderStyle::Dotted};
  nscolor focusColors[4] = {aColor, aColor, aColor, aColor};

  // Because this renders a dotted border, the background color
  // should not be used.  Therefore, we provide a value that will
  // be blatantly wrong if it ever does get used.  (If this becomes
  // something that CSS can style, this function will then have access
  // to a ComputedStyle and can use the same logic that PaintBorder
  // and PaintOutline do.)
  //
  // WebRender layers-free mode don't use PaintFocus function. Just assign
  // the backface-visibility to true for this case.
  nsCSSBorderRenderer br(aPresContext, aDrawTarget, focusRect, focusRect,
                         focusStyles, focusWidths, focusRadii, focusColors,
                         true, Nothing());
  br.DrawBorders();

  PrintAsStringNewline();
}

// Thebes Border Rendering Code End
//----------------------------------------------------------------------

//----------------------------------------------------------------------

/**
 * Helper for ComputeObjectAnchorPoint; parameters are the same as for
 * that function, except they're for a single coordinate / a single size
 * dimension. (so, x/width vs. y/height)
 */

static void ComputeObjectAnchorCoord(const LengthPercentage& aCoord,
                                     const nscoord aOriginBounds,
                                     const nscoord aImageSize,
                                     nscoord* aTopLeftCoord,
                                     nscoord* aAnchorPointCoord) {
  nscoord extraSpace = aOriginBounds - aImageSize;

  // The anchor-point doesn't care about our image's size; just the size
  // of the region we're rendering into.
  *aAnchorPointCoord = aCoord.Resolve(
      aOriginBounds, static_cast<nscoord (*)(float)>(NSToCoordRoundWithClamp));
  // Adjust aTopLeftCoord by the specified % of the extra space.
  *aTopLeftCoord = aCoord.Resolve(
      extraSpace, static_cast<nscoord (*)(float)>(NSToCoordRoundWithClamp));
}

void nsImageRenderer::ComputeObjectAnchorPoint(const Position& aPos,
                                               const nsSize& aOriginBounds,
                                               const nsSize& aImageSize,
                                               nsPoint* aTopLeft,
                                               nsPoint* aAnchorPoint) {
  ComputeObjectAnchorCoord(aPos.horizontal, aOriginBounds.width,
                           aImageSize.width, &aTopLeft->x, &aAnchorPoint->x);

  ComputeObjectAnchorCoord(aPos.vertical, aOriginBounds.height,
                           aImageSize.height, &aTopLeft->y, &aAnchorPoint->y);
}

// In print / print preview we have multiple canvas frames (one for each page,
// and one for the document as a whole). For the topmost one, we really want the
// page sequence page background, not the root or body's background.
static nsIFrame* GetPageSequenceForCanvas(const nsIFrame* aCanvasFrame) {
  MOZ_ASSERT(aCanvasFrame->IsCanvasFrame(), "not a canvas frame");
  nsPresContext* pc = aCanvasFrame->PresContext();
  if (!pc->IsRootPaginatedDocument()) {
    return nullptr;
  }
  auto* ps = pc->PresShell()->GetPageSequenceFrame();
  if (NS_WARN_IF(!ps)) {
    return nullptr;
  }
  if (ps->GetParent() != aCanvasFrame) {
    return nullptr;
  }
  return ps;
}

auto nsCSSRendering::FindEffectiveBackgroundColor(
    nsIFrame* aFrame, bool aStopAtThemed,
    bool aPreferBodyToCanvas) -> EffectiveBackgroundColor {
  MOZ_ASSERT(aFrame);
  nsPresContext* pc = aFrame->PresContext();
  auto BgColorIfNotTransparent = [&](nsIFrame* aFrame) -> Maybe<nscolor> {
    nscolor c =
        aFrame->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
    if (NS_GET_A(c) == 255) {
      return Some(c);
    }
    if (NS_GET_A(c)) {
      // TODO(emilio): We should maybe just blend with ancestor bg colors and
      // such, but this is probably good enough for now, matches pre-existing
      // behavior.
      const nscolor defaultBg = pc->DefaultBackgroundColor();
      MOZ_ASSERT(NS_GET_A(defaultBg) == 255, "PreferenceSheet guarantees this");
      return Some(NS_ComposeColors(defaultBg, c));
    }
    return Nothing();
  };

  for (nsIFrame* frame = aFrame; frame;
       frame = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(frame)) {
    if (auto bg = BgColorIfNotTransparent(frame)) {
      return {*bg};
    }

    if (aStopAtThemed && frame->IsThemed()) {
      return {NS_TRANSPARENT, true};
    }

    if (frame->IsCanvasFrame()) {
      if (aPreferBodyToCanvas && !GetPageSequenceForCanvas(frame)) {
        if (auto* body = pc->Document()->GetBodyElement()) {
          if (nsIFrame* f = body->GetPrimaryFrame()) {
            if (auto bg = BgColorIfNotTransparent(f)) {
              return {*bg};
            }
          }
        }
      }
      if (nsIFrame* bgFrame = FindBackgroundFrame(frame)) {
        if (auto bg = BgColorIfNotTransparent(bgFrame)) {
          return {*bg};
        }
      }
    }
  }

  return {pc->DefaultBackgroundColor()};
}

nsIFrame* nsCSSRendering::FindBackgroundStyleFrame(nsIFrame* aForFrame) {
  const nsStyleBackground* result = aForFrame->StyleBackground();

  // Check if we need to do propagation from BODY rather than HTML.
  if (!result->IsTransparent(aForFrame)) {
    return aForFrame;
  }

  nsIContent* content = aForFrame->GetContent();
  // The root element content can't be null. We wouldn't know what
  // frame to create for aFrame.
  // Use |OwnerDoc| so it works during destruction.
  if (!content) {
    return aForFrame;
  }

  Document* document = content->OwnerDoc();

  dom::Element* bodyContent = document->GetBodyElement();
  // We need to null check the body node (bug 118829) since
  // there are cases, thanks to the fix for bug 5569, where we
  // will reflow a document with no body.  In particular, if a
  // SCRIPT element in the head blocks the parser and then has a
  // SCRIPT that does "document.location.href = 'foo'", then
  // nsParser::Terminate will call |DidBuildModel| methods
  // through to the content sink, which will call |StartLayout|
  // and thus |Initialize| on the pres shell.  See bug 119351
  // for the ugly details.
  if (!bodyContent || aForFrame->StyleDisplay()->IsContainAny()) {
    return aForFrame;
  }

  nsIFrame* bodyFrame = bodyContent->GetPrimaryFrame();
  if (!bodyFrame || bodyFrame->StyleDisplay()->IsContainAny()) {
    return aForFrame;
  }

  return nsLayoutUtils::GetStyleFrame(bodyFrame);
}

/**
 * |FindBackground| finds the correct style data to use to paint the
 * background.  It is responsible for handling the following two
 * statements in section 14.2 of CSS2:
 *
 *   The background of the box generated by the root element covers the
 *   entire canvas.
 *
 *   For HTML documents, however, we recommend that authors specify the
 *   background for the BODY element rather than the HTML element. User
 *   agents should observe the following precedence rules to fill in the
 *   background: if the value of the 'background' property for the HTML
 *   element is different from 'transparent' then use it, else use the
 *   value of the 'background' property for the BODY element. If the
 *   resulting value is 'transparent', the rendering is undefined.
 *
 * Thus, in our implementation, it is responsible for ensuring that:
 *  + we paint the correct background on the |nsCanvasFrame| or |nsPageFrame|,
 *  + we don't paint the background on the root element, and
 *  + we don't paint the background on the BODY element in *some* cases,
 *    and for SGML-based HTML documents only.
 *
 * |FindBackground| checks whether a background should be painted. If yes, it
 * returns the resulting ComputedStyle to use for the background information;
 * Otherwise, it returns nullptr.
 */

ComputedStyle* nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame) {
  return FindBackgroundStyleFrame(aForFrame)->Style();
}

static nsIFrame* FindCanvasBackgroundFrame(const nsIFrame* aForFrame,
                                           nsIFrame* aRootElementFrame) {
  MOZ_ASSERT(aForFrame->IsCanvasFrame(), "not a canvas frame");
  if (auto* ps = GetPageSequenceForCanvas(aForFrame)) {
    return ps;
  }
  if (aRootElementFrame) {
    return nsCSSRendering::FindBackgroundStyleFrame(aRootElementFrame);
  }
  // This should always give transparent, so we'll fill it in with the default
  // color if needed.  This seems to happen a bit while a page is being loaded.
  return const_cast<nsIFrame*>(aForFrame);
}

// Helper for FindBackgroundFrame. Returns true if aForFrame has a meaningful
// background that it should draw (i.e. that it hasn't propagated to another
// frame).  See documentation for FindBackground.
inline bool FrameHasMeaningfulBackground(const nsIFrame* aForFrame,
                                         nsIFrame* aRootElementFrame) {
  MOZ_ASSERT(!aForFrame->IsCanvasFrame(),
             "FindBackgroundFrame handles canvas frames before calling us, "
             "so we don't need to consider them here");

  if (aForFrame == aRootElementFrame) {
    // We must have propagated our background to the viewport or canvas. Abort.
    return false;
  }

  // Return true unless the frame is for a BODY element whose background
  // was propagated to the viewport.

  nsIContent* content = aForFrame->GetContent();
  if (!content || content->NodeInfo()->NameAtom() != nsGkAtoms::body) {
    return true;  // not frame for a "body" element
  }
  // It could be a non-HTML "body" element but that's OK, we'd fail the
  // bodyContent check below

  if (aForFrame->Style()->GetPseudoType() != PseudoStyleType::NotPseudo ||
      aForFrame->StyleDisplay()->IsContainAny()) {
    return true;  // A pseudo-element frame, or contained.
  }

  // We should only look at the <html> background if we're in an HTML document
  Document* document = content->OwnerDoc();

  dom::Element* bodyContent = document->GetBodyElement();
  if (bodyContent != content) {
    return true;  // this wasn't the background that was propagated
  }

  // This can be called even when there's no root element yet, during frame
  // construction, via nsLayoutUtils::FrameHasTransparency and
  // nsContainerFrame::SyncFrameViewProperties.
  if (!aRootElementFrame || aRootElementFrame->StyleDisplay()->IsContainAny()) {
    return true;
  }

  const nsStyleBackground* htmlBG = aRootElementFrame->StyleBackground();
  return !htmlBG->IsTransparent(aRootElementFrame);
}

nsIFrame* nsCSSRendering::FindBackgroundFrame(const nsIFrame* aForFrame) {
  nsIFrame* rootElementFrame =
      aForFrame->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
  if (aForFrame->IsCanvasFrame()) {
    return FindCanvasBackgroundFrame(aForFrame, rootElementFrame);
  }

  if (FrameHasMeaningfulBackground(aForFrame, rootElementFrame)) {
    return const_cast<nsIFrame*>(aForFrame);
  }

  return nullptr;
}

ComputedStyle* nsCSSRendering::FindBackground(const nsIFrame* aForFrame) {
  if (auto* backgroundFrame = FindBackgroundFrame(aForFrame)) {
    return backgroundFrame->Style();
  }
  return nullptr;
}

void nsCSSRendering::BeginFrameTreesLocked() { ++gFrameTreeLockCount; }

void nsCSSRendering::EndFrameTreesLocked() {
  NS_ASSERTION(gFrameTreeLockCount > 0, "Unbalanced EndFrameTreeLocked");
  --gFrameTreeLockCount;
  if (gFrameTreeLockCount == 0) {
    gInlineBGData->Reset();
  }
}

bool nsCSSRendering::HasBoxShadowNativeTheme(nsIFrame* aFrame,
                                             bool& aMaybeHasBorderRadius) {
  const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
  nsITheme::Transparency transparency;
  if (aFrame->IsThemed(styleDisplay, &transparency)) {
    aMaybeHasBorderRadius = false;
    // For opaque (rectangular) theme widgets we can take the generic
    // border-box path with border-radius disabled.
    return transparency != nsITheme::eOpaque;
  }

  aMaybeHasBorderRadius = true;
  return false;
}

gfx::sRGBColor nsCSSRendering::GetShadowColor(const StyleSimpleShadow& aShadow,
                                              nsIFrame* aFrame,
                                              float aOpacity) {
  // Get the shadow color; if not specified, use the foreground color
  nscolor shadowColor = aShadow.color.CalcColor(aFrame);
  sRGBColor color = sRGBColor::FromABGR(shadowColor);
  color.a *= aOpacity;
  return color;
}

nsRect nsCSSRendering::GetShadowRect(const nsRect& aFrameArea,
                                     bool aNativeTheme, nsIFrame* aForFrame) {
  nsRect frameRect = aNativeTheme ? aForFrame->InkOverflowRectRelativeToSelf() +
                                        aFrameArea.TopLeft()
                                  : aFrameArea;
  Sides skipSides = aForFrame->GetSkipSides();
  frameRect = BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);

  // Explicitly do not need to account for the spread radius here
  // Webrender does it for us or PaintBoxShadow will for non-WR
  return frameRect;
}

bool nsCSSRendering::GetBorderRadii(const nsRect& aFrameRect,
                                    const nsRect& aBorderRect, nsIFrame* aFrame,
                                    RectCornerRadii& aOutRadii) {
  const nscoord oneDevPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);
  nscoord twipsRadii[8];
  NS_ASSERTION(
      aBorderRect.Size() == aFrame->VisualBorderRectRelativeToSelf().Size(),
      "unexpected size");
  nsSize sz = aFrameRect.Size();
  bool hasBorderRadius = aFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
  if (hasBorderRadius) {
    ComputePixelRadii(twipsRadii, oneDevPixel, &aOutRadii);
  }

  return hasBorderRadius;
}

void nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
                                         gfxContext& aRenderingContext,
                                         nsIFrame* aForFrame,
                                         const nsRect& aFrameArea,
                                         const nsRect& aDirtyRect,
                                         float aOpacity) {
  DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
  auto shadows = aForFrame->StyleEffects()->mBoxShadow.AsSpan();
  if (shadows.IsEmpty()) {
    return;
  }

  bool hasBorderRadius;
  // mutually exclusive with hasBorderRadius
  bool nativeTheme = HasBoxShadowNativeTheme(aForFrame, hasBorderRadius);
  const nsStyleDisplay* styleDisplay = aForFrame->StyleDisplay();

  nsRect frameRect = GetShadowRect(aFrameArea, nativeTheme, aForFrame);

  // Get any border radius, since box-shadow must also have rounded corners if
  // the frame does.
  RectCornerRadii borderRadii;
  const nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
  if (hasBorderRadius) {
    nscoord twipsRadii[8];
    NS_ASSERTION(
        aFrameArea.Size() == aForFrame->VisualBorderRectRelativeToSelf().Size(),
        "unexpected size");
    nsSize sz = frameRect.Size();
    hasBorderRadius = aForFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
    if (hasBorderRadius) {
      ComputePixelRadii(twipsRadii, oneDevPixel, &borderRadii);
    }
  }

  // We don't show anything that intersects with the frame we're blurring on. So
  // tell the blurrer not to do unnecessary work there.
  gfxRect skipGfxRect = ThebesRect(NSRectToRect(frameRect, oneDevPixel));
  skipGfxRect.Round();
  bool useSkipGfxRect = true;
  if (nativeTheme) {
    // Optimize non-leaf native-themed frames by skipping computing pixels
    // in the padding-box. We assume the padding-box is going to be painted
    // opaquely for non-leaf frames.
    // XXX this may not be a safe assumption; we should make this go away
    // by optimizing box-shadow drawing more for the cases where we don't have a
    // skip-rect.
    useSkipGfxRect = !aForFrame->IsLeaf();
    nsRect paddingRect =
        aForFrame->GetPaddingRectRelativeToSelf() + aFrameArea.TopLeft();
    skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, oneDevPixel);
  } else if (hasBorderRadius) {
    skipGfxRect.Deflate(gfxMargin(
        std::max(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
        std::max(borderRadii[C_BL].height, borderRadii[C_BR].height), 0));
  }

  for (const StyleBoxShadow& shadow : Reversed(shadows)) {
    if (shadow.inset) {
      continue;
    }

    nsRect shadowRect = frameRect;
    nsPoint shadowOffset(shadow.base.horizontal.ToAppUnits(),
                         shadow.base.vertical.ToAppUnits());
    shadowRect.MoveBy(shadowOffset);
    nscoord shadowSpread = shadow.spread.ToAppUnits();
    if (!nativeTheme) {
      shadowRect.Inflate(shadowSpread);
    }

    // shadowRect won't include the blur, so make an extra rect here that
    // includes the blur for use in the even-odd rule below.
    nsRect shadowRectPlusBlur = shadowRect;
    nscoord blurRadius = shadow.base.blur.ToAppUnits();
    shadowRectPlusBlur.Inflate(
        nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, oneDevPixel));

    Rect shadowGfxRectPlusBlur = NSRectToRect(shadowRectPlusBlur, oneDevPixel);
    shadowGfxRectPlusBlur.RoundOut();
    MaybeSnapToDevicePixels(shadowGfxRectPlusBlur, aDrawTarget, true);

    sRGBColor gfxShadowColor = GetShadowColor(shadow.base, aForFrame, aOpacity);

    if (nativeTheme) {
      nsContextBoxBlur blurringArea;

      // When getting the widget shape from the native theme, we're going
      // to draw the widget into the shadow surface to create a mask.
      // We need to ensure that there actually *is* a shadow surface
      // and that we're not going to draw directly into aRenderingContext.
      gfxContext* shadowContext = blurringArea.Init(
          shadowRect, shadowSpread, blurRadius, oneDevPixel, &aRenderingContext,
          aDirtyRect, useSkipGfxRect ? &skipGfxRect : nullptr,
          nsContextBoxBlur::FORCE_MASK);
      if (!shadowContext) {
        continue;
      }

      MOZ_ASSERT(shadowContext == blurringArea.GetContext());

      aRenderingContext.Save();
      aRenderingContext.SetColor(gfxShadowColor);

      // Draw the shape of the frame so it can be blurred. Recall how
      // nsContextBoxBlur doesn't make any temporary surfaces if blur is 0 and
      // it just returns the original surface? If we have no blur, we're
      // painting this fill on the actual content surface (aRenderingContext ==
      // shadowContext) which is why we set up the color and clip before doing
      // this.

      // We don't clip the border-box from the shadow, nor any other box.
      // We assume that the native theme is going to paint over the shadow.

      // Draw the widget shape
      gfxContextMatrixAutoSaveRestore save(shadowContext);
      gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(
          shadowOffset, aPresContext->AppUnitsPerDevPixel());
      shadowContext->SetMatrixDouble(
          shadowContext->CurrentMatrixDouble().PreTranslate(devPixelOffset));

      nsRect nativeRect = aDirtyRect;
      nativeRect.MoveBy(-shadowOffset);
      nativeRect.IntersectRect(frameRect, nativeRect);
      aPresContext->Theme()->DrawWidgetBackground(
          shadowContext, aForFrame, styleDisplay->EffectiveAppearance(),
          aFrameArea, nativeRect, nsITheme::DrawOverflow::No);

      blurringArea.DoPaint();
      aRenderingContext.Restore();
    } else {
      aRenderingContext.Save();

      {
        Rect innerClipRect = NSRectToRect(frameRect, oneDevPixel);
        if (!MaybeSnapToDevicePixels(innerClipRect, aDrawTarget, true)) {
          innerClipRect.Round();
        }

        // Clip out the interior of the frame's border edge so that the shadow
        // is only painted outside that area.
        RefPtr<PathBuilder> builder =
            aDrawTarget.CreatePathBuilder(FillRule::FILL_EVEN_ODD);
        AppendRectToPath(builder, shadowGfxRectPlusBlur);
        if (hasBorderRadius) {
          AppendRoundedRectToPath(builder, innerClipRect, borderRadii);
        } else {
          AppendRectToPath(builder, innerClipRect);
        }
        RefPtr<Path> path = builder->Finish();
        aRenderingContext.Clip(path);
      }

      // Clip the shadow so that we only get the part that applies to aForFrame.
      nsRect fragmentClip = shadowRectPlusBlur;
      Sides skipSides = aForFrame->GetSkipSides();
      if (!skipSides.IsEmpty()) {
        if (skipSides.Left()) {
          nscoord xmost = fragmentClip.XMost();
          fragmentClip.x = aFrameArea.x;
          fragmentClip.width = xmost - fragmentClip.x;
        }
        if (skipSides.Right()) {
          nscoord xmost = fragmentClip.XMost();
          nscoord overflow = xmost - aFrameArea.XMost();
          if (overflow > 0) {
            fragmentClip.width -= overflow;
          }
        }
        if (skipSides.Top()) {
          nscoord ymost = fragmentClip.YMost();
          fragmentClip.y = aFrameArea.y;
          fragmentClip.height = ymost - fragmentClip.y;
        }
        if (skipSides.Bottom()) {
          nscoord ymost = fragmentClip.YMost();
          nscoord overflow = ymost - aFrameArea.YMost();
          if (overflow > 0) {
            fragmentClip.height -= overflow;
          }
        }
      }
      fragmentClip = fragmentClip.Intersect(aDirtyRect);
      aRenderingContext.Clip(NSRectToSnappedRect(
          fragmentClip, aForFrame->PresContext()->AppUnitsPerDevPixel(),
          aDrawTarget));

      RectCornerRadii clipRectRadii;
      if (hasBorderRadius) {
        Float spreadDistance = Float(shadowSpread / oneDevPixel);

        Float borderSizes[4];

        borderSizes[eSideLeft] = spreadDistance;
        borderSizes[eSideTop] = spreadDistance;
        borderSizes[eSideRight] = spreadDistance;
        borderSizes[eSideBottom] = spreadDistance;

        nsCSSBorderRenderer::ComputeOuterRadii(borderRadii, borderSizes,
                                               &clipRectRadii);
      }
      nsContextBoxBlur::BlurRectangle(
          &aRenderingContext, shadowRect, oneDevPixel,
          hasBorderRadius ? &clipRectRadii : nullptr, blurRadius,
          gfxShadowColor, aDirtyRect, skipGfxRect);
      aRenderingContext.Restore();
    }
  }
}

nsRect nsCSSRendering::GetBoxShadowInnerPaddingRect(nsIFrame* aFrame,
                                                    const nsRect& aFrameArea) {
  Sides skipSides = aFrame->GetSkipSides();
  nsRect frameRect = BoxDecorationRectForBorder(aFrame, aFrameArea, skipSides);

  nsRect paddingRect = frameRect;
  nsMargin border = aFrame->GetUsedBorder();
  paddingRect.Deflate(border);
  return paddingRect;
}

bool nsCSSRendering::ShouldPaintBoxShadowInner(nsIFrame* aFrame) {
  const Span<const StyleBoxShadow> shadows =
      aFrame->StyleEffects()->mBoxShadow.AsSpan();
  if (shadows.IsEmpty()) {
    return false;
  }

  if (aFrame->IsThemed() && aFrame->GetContent() &&
      !nsContentUtils::IsChromeDoc(aFrame->GetContent()->GetComposedDoc())) {
    // There's no way of getting hold of a shape corresponding to a
    // "padding-box" for native-themed widgets, so just don't draw
    // inner box-shadows for them. But we allow chrome to paint inner
    // box shadows since chrome can be aware of the platform theme.
    return false;
  }

  return true;
}

bool nsCSSRendering::GetShadowInnerRadii(nsIFrame* aFrame,
                                         const nsRect& aFrameArea,
                                         RectCornerRadii& aOutInnerRadii) {
  // Get any border radius, since box-shadow must also have rounded corners
  // if the frame does.
  nscoord twipsRadii[8];
  nsRect frameRect =
      BoxDecorationRectForBorder(aFrame, aFrameArea, aFrame->GetSkipSides());
  nsSize sz = frameRect.Size();
  nsMargin border = aFrame->GetUsedBorder();
  aFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
  const nscoord oneDevPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);

  RectCornerRadii borderRadii;

  const bool hasBorderRadius =
      GetBorderRadii(frameRect, aFrameArea, aFrame, borderRadii);

  if (hasBorderRadius) {
    ComputePixelRadii(twipsRadii, oneDevPixel, &borderRadii);

    Float borderSizes[4] = {
        Float(border.top) / oneDevPixel, Float(border.right) / oneDevPixel,
        Float(border.bottom) / oneDevPixel, Float(border.left) / oneDevPixel};
    nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
--> --------------------

--> maximum size reached

--> --------------------

95%


¤ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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 ist noch experimentell.