зеркало из https://github.com/mozilla/gecko-dev.git
1060 строки
38 KiB
C++
1060 строки
38 KiB
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/. */
|
|
|
|
/* rendering object for list-item bullets */
|
|
|
|
#include "nsBulletFrame.h"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "CounterStyleManager.h"
|
|
#include "ImageLayers.h"
|
|
#include "TextDrawTarget.h"
|
|
#include "UnitTransforms.h"
|
|
#include "gfx2DGlue.h"
|
|
#include "gfxContext.h"
|
|
#include "gfxUtils.h"
|
|
#include "imgIContainer.h"
|
|
#include "imgRequestProxy.h"
|
|
#include "mozilla/MathAlgorithms.h"
|
|
#include "mozilla/PresShell.h"
|
|
#include "mozilla/SVGImageContext.h"
|
|
#include "mozilla/css/ImageLoader.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "mozilla/gfx/PathHelpers.h"
|
|
#include "mozilla/layers/LayersMessages.h"
|
|
#include "mozilla/layers/RenderRootStateManager.h"
|
|
#include "mozilla/layers/StackingContextHelper.h"
|
|
#include "mozilla/layers/WebRenderBridgeChild.h"
|
|
#include "mozilla/layers/WebRenderMessages.h"
|
|
#include "nsAttrValueInlines.h"
|
|
#include "nsBidiUtils.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsCSSFrameConstructor.h"
|
|
#include "nsCounterManager.h"
|
|
#include "nsDisplayList.h"
|
|
#include "nsFontMetrics.h"
|
|
#include "nsGenericHTMLElement.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsIURI.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsPresContext.h"
|
|
|
|
#ifdef ACCESSIBILITY
|
|
# include "nsAccessibilityService.h"
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::image;
|
|
using namespace mozilla::layout;
|
|
|
|
nsIFrame* NS_NewBulletFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
|
|
return new (aPresShell) nsBulletFrame(aStyle, aPresShell->GetPresContext());
|
|
}
|
|
|
|
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame)
|
|
|
|
#ifdef DEBUG
|
|
NS_QUERYFRAME_HEAD(nsBulletFrame)
|
|
NS_QUERYFRAME_ENTRY(nsBulletFrame)
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsIFrame)
|
|
#endif
|
|
|
|
nsBulletFrame::~nsBulletFrame() = default;
|
|
|
|
CounterStyle* nsBulletFrame::ResolveCounterStyle() {
|
|
return PresContext()->CounterStyleManager()->ResolveCounterStyle(
|
|
StyleList()->mCounterStyle);
|
|
}
|
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
|
nsresult nsBulletFrame::GetFrameName(nsAString& aResult) const {
|
|
return MakeFrameName(u"Bullet"_ns, aResult);
|
|
}
|
|
#endif
|
|
|
|
bool nsBulletFrame::IsEmpty() { return IsSelfEmpty(); }
|
|
|
|
bool nsBulletFrame::IsSelfEmpty() {
|
|
return StyleList()->mCounterStyle.IsNone();
|
|
}
|
|
|
|
/* virtual */
|
|
void nsBulletFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
|
|
nsIFrame::DidSetComputedStyle(aOldStyle);
|
|
|
|
css::ImageLoader* loader = PresContext()->Document()->StyleImageLoader();
|
|
imgIRequest* oldListImage =
|
|
aOldStyle ? aOldStyle->StyleList()->mListStyleImage.GetImageRequest()
|
|
: nullptr;
|
|
imgIRequest* newListImage = StyleList()->mListStyleImage.GetImageRequest();
|
|
if (oldListImage != newListImage) {
|
|
if (oldListImage && HasImageRequest()) {
|
|
loader->DisassociateRequestFromFrame(oldListImage, this);
|
|
}
|
|
if (newListImage) {
|
|
loader->AssociateRequestToFrame(
|
|
newListImage, this,
|
|
css::ImageLoader::Flags::RequiresReflowOnSizeAvailable);
|
|
|
|
// Image bullets can affect the layout of the page, so boost the image
|
|
// load priority.
|
|
newListImage->BoostPriority(imgIRequest::CATEGORY_SIZE_QUERY);
|
|
}
|
|
}
|
|
|
|
#ifdef ACCESSIBILITY
|
|
// Update the list bullet accessible. If old style list isn't available then
|
|
// no need to update the accessible tree because it's not created yet.
|
|
if (aOldStyle) {
|
|
if (nsAccessibilityService* accService =
|
|
PresShell::GetAccessibilityService()) {
|
|
const nsStyleList* oldStyleList = aOldStyle->StyleList();
|
|
bool hadBullet = !oldStyleList->mListStyleImage.IsNone() ||
|
|
!oldStyleList->mCounterStyle.IsNone();
|
|
|
|
const nsStyleList* newStyleList = StyleList();
|
|
bool hasBullet = !newStyleList->mListStyleImage.IsNone() ||
|
|
!newStyleList->mCounterStyle.IsNone();
|
|
|
|
if (hadBullet != hasBullet) {
|
|
nsIContent* listItem = mContent->GetParent();
|
|
accService->UpdateListBullet(PresContext()->GetPresShell(), listItem,
|
|
hasBullet);
|
|
}
|
|
}
|
|
}
|
|
#endif // #ifdef ACCESSIBILITY
|
|
}
|
|
|
|
class nsDisplayBulletGeometry
|
|
: public nsDisplayItemGenericGeometry,
|
|
public nsImageGeometryMixin<nsDisplayBulletGeometry> {
|
|
public:
|
|
nsDisplayBulletGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
|
|
: nsDisplayItemGenericGeometry(aItem, aBuilder),
|
|
nsImageGeometryMixin(aItem, aBuilder) {
|
|
nsBulletFrame* f = static_cast<nsBulletFrame*>(aItem->Frame());
|
|
mOrdinal = f->Ordinal();
|
|
}
|
|
|
|
virtual bool InvalidateForSyncDecodeImages() const override {
|
|
return ShouldInvalidateToSyncDecodeImages();
|
|
}
|
|
|
|
int32_t mOrdinal;
|
|
};
|
|
|
|
class BulletRenderer final {
|
|
public:
|
|
BulletRenderer(nsIFrame* aFrame, const StyleImage& aImage,
|
|
const nsRect& aDest, uint32_t aFlags)
|
|
: mDest(aDest),
|
|
mColor(NS_RGBA(0, 0, 0, 0)),
|
|
mListStyleType(NS_STYLE_LIST_STYLE_NONE) {
|
|
mImageRenderer.emplace(aFrame, &aImage, aFlags);
|
|
mImageRenderer->SetPreferredSize({}, aDest.Size());
|
|
MOZ_ASSERT(IsImageType());
|
|
}
|
|
|
|
BulletRenderer(Path* path, nscolor color, int32_t listStyleType)
|
|
: mColor(color), mPath(path), mListStyleType(listStyleType) {
|
|
MOZ_ASSERT(IsPathType());
|
|
}
|
|
|
|
BulletRenderer(const LayoutDeviceRect& aPathRect, nscolor color,
|
|
int32_t listStyleType)
|
|
: mPathRect(aPathRect), mColor(color), mListStyleType(listStyleType) {
|
|
MOZ_ASSERT(IsPathType());
|
|
}
|
|
|
|
BulletRenderer(const nsString& text, nsFontMetrics* fm, nscolor color,
|
|
const nsPoint& point, int32_t listStyleType)
|
|
: mColor(color),
|
|
mText(text),
|
|
mFontMetrics(fm),
|
|
mPoint(point),
|
|
mListStyleType(listStyleType) {
|
|
MOZ_ASSERT(IsTextType());
|
|
}
|
|
|
|
ImgDrawResult CreateWebRenderCommands(
|
|
nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
|
|
wr::IpcResourceUpdateQueue& aResources,
|
|
const layers::StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder);
|
|
|
|
ImgDrawResult Paint(gfxContext& aRenderingContext, nsPoint aPt,
|
|
const nsRect& aDirtyRect, bool aDisableSubpixelAA,
|
|
nsIFrame* aFrame);
|
|
|
|
bool IsImageType() const { return !!mImageRenderer; }
|
|
|
|
bool PrepareImage() {
|
|
MOZ_ASSERT(IsImageType());
|
|
return mImageRenderer->PrepareImage();
|
|
}
|
|
|
|
ImgDrawResult PrepareResult() const {
|
|
MOZ_ASSERT(IsImageType());
|
|
return mImageRenderer->PrepareResult();
|
|
}
|
|
|
|
bool IsPathType() const {
|
|
return mListStyleType == NS_STYLE_LIST_STYLE_DISC ||
|
|
mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE ||
|
|
mListStyleType == NS_STYLE_LIST_STYLE_SQUARE ||
|
|
mListStyleType == NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN ||
|
|
mListStyleType == NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED;
|
|
}
|
|
|
|
bool IsTextType() const {
|
|
return mListStyleType != NS_STYLE_LIST_STYLE_NONE &&
|
|
mListStyleType != NS_STYLE_LIST_STYLE_DISC &&
|
|
mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
|
|
mListStyleType != NS_STYLE_LIST_STYLE_SQUARE &&
|
|
mListStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN &&
|
|
mListStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
|
|
!mText.IsEmpty();
|
|
}
|
|
|
|
void PaintTextToContext(nsIFrame* aFrame, gfxContext* aCtx,
|
|
bool aDisableSubpixelAA);
|
|
|
|
private:
|
|
ImgDrawResult CreateWebRenderCommandsForImage(
|
|
nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
|
|
wr::IpcResourceUpdateQueue& aResources,
|
|
const layers::StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder);
|
|
|
|
bool CreateWebRenderCommandsForPath(
|
|
nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
|
|
wr::IpcResourceUpdateQueue& aResources,
|
|
const layers::StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder);
|
|
|
|
bool CreateWebRenderCommandsForText(
|
|
nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
|
|
wr::IpcResourceUpdateQueue& aResources,
|
|
const layers::StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder);
|
|
|
|
private:
|
|
Maybe<nsImageRenderer> mImageRenderer;
|
|
|
|
// The position and size of the image bullet.
|
|
nsRect mDest;
|
|
|
|
// Some bullet types are stored as a rect (in device pixels) instead of a Path
|
|
// to allow generating proper WebRender commands. When webrender is disabled
|
|
// the Path is lazily created for these items before painting.
|
|
// TODO: The size of this structure doesn't seem to be an issue since it has
|
|
// so many fields that are specific to a bullet style or another, but if it
|
|
// becomes one we can easily store mDest and mPathRect into the same memory
|
|
// location since they are never used by the same bullet types.
|
|
LayoutDeviceRect mPathRect;
|
|
|
|
// mColor indicate the color of list-style. Both text and path type would use
|
|
// this member.
|
|
nscolor mColor;
|
|
|
|
// mPath record the path of the list-style for later drawing.
|
|
// Included following types: square, circle, disc, disclosure open and
|
|
// disclosure closed.
|
|
RefPtr<Path> mPath;
|
|
|
|
// mText, mFontMetrics, mPoint are for other list-style-type which can be
|
|
// drawed by text.
|
|
nsString mText;
|
|
RefPtr<nsFontMetrics> mFontMetrics;
|
|
nsPoint mPoint;
|
|
|
|
// Store the type of list-style-type.
|
|
int32_t mListStyleType;
|
|
};
|
|
|
|
ImgDrawResult BulletRenderer::CreateWebRenderCommands(
|
|
nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
|
|
wr::IpcResourceUpdateQueue& aResources,
|
|
const layers::StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder) {
|
|
if (IsImageType()) {
|
|
return CreateWebRenderCommandsForImage(aItem, aBuilder, aResources, aSc,
|
|
aManager, aDisplayListBuilder);
|
|
}
|
|
|
|
bool success;
|
|
if (IsPathType()) {
|
|
success = CreateWebRenderCommandsForPath(aItem, aBuilder, aResources, aSc,
|
|
aManager, aDisplayListBuilder);
|
|
} else {
|
|
MOZ_ASSERT(IsTextType());
|
|
success = CreateWebRenderCommandsForText(aItem, aBuilder, aResources, aSc,
|
|
aManager, aDisplayListBuilder);
|
|
}
|
|
|
|
return success ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_SUPPORTED;
|
|
}
|
|
|
|
ImgDrawResult BulletRenderer::Paint(gfxContext& aRenderingContext, nsPoint aPt,
|
|
const nsRect& aDirtyRect,
|
|
bool aDisableSubpixelAA, nsIFrame* aFrame) {
|
|
if (IsImageType()) {
|
|
return mImageRenderer->DrawLayer(aFrame->PresContext(), aRenderingContext,
|
|
mDest, mDest, mDest.TopLeft(), aDirtyRect,
|
|
mDest.Size(),
|
|
/* aOpacity = */ 1.0f);
|
|
}
|
|
|
|
if (IsPathType()) {
|
|
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
|
|
|
|
if (!mPath) {
|
|
RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
|
|
switch (mListStyleType) {
|
|
case NS_STYLE_LIST_STYLE_CIRCLE:
|
|
case NS_STYLE_LIST_STYLE_DISC:
|
|
AppendEllipseToPath(builder, mPathRect.Center().ToUnknownPoint(),
|
|
mPathRect.Size().ToUnknownSize());
|
|
break;
|
|
case NS_STYLE_LIST_STYLE_SQUARE:
|
|
AppendRectToPath(builder, mPathRect.ToUnknownRect());
|
|
break;
|
|
default:
|
|
MOZ_ASSERT(false, "Should have a parth.");
|
|
}
|
|
mPath = builder->Finish();
|
|
}
|
|
|
|
switch (mListStyleType) {
|
|
case NS_STYLE_LIST_STYLE_CIRCLE:
|
|
drawTarget->Stroke(mPath, ColorPattern(ToDeviceColor(mColor)));
|
|
break;
|
|
case NS_STYLE_LIST_STYLE_DISC:
|
|
case NS_STYLE_LIST_STYLE_SQUARE:
|
|
case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
|
|
case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
|
|
drawTarget->Fill(mPath, ColorPattern(ToDeviceColor(mColor)));
|
|
break;
|
|
default:
|
|
MOZ_CRASH("unreachable");
|
|
}
|
|
}
|
|
|
|
if (IsTextType()) {
|
|
PaintTextToContext(aFrame, &aRenderingContext, aDisableSubpixelAA);
|
|
}
|
|
|
|
return ImgDrawResult::SUCCESS;
|
|
}
|
|
|
|
void BulletRenderer::PaintTextToContext(nsIFrame* aFrame, gfxContext* aCtx,
|
|
bool aDisableSubpixelAA) {
|
|
MOZ_ASSERT(IsTextType());
|
|
|
|
DrawTarget* drawTarget = aCtx->GetDrawTarget();
|
|
DrawTargetAutoDisableSubpixelAntialiasing disable(drawTarget,
|
|
aDisableSubpixelAA);
|
|
|
|
aCtx->SetColor(sRGBColor::FromABGR(mColor));
|
|
|
|
nsPresContext* presContext = aFrame->PresContext();
|
|
if (!presContext->BidiEnabled() && HasRTLChars(mText)) {
|
|
presContext->SetBidiEnabled();
|
|
}
|
|
nsLayoutUtils::DrawString(aFrame, *mFontMetrics, aCtx, mText.get(),
|
|
mText.Length(), mPoint);
|
|
}
|
|
|
|
ImgDrawResult BulletRenderer::CreateWebRenderCommandsForImage(
|
|
nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
|
|
wr::IpcResourceUpdateQueue& aResources,
|
|
const layers::StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder) {
|
|
MOZ_RELEASE_ASSERT(IsImageType());
|
|
return mImageRenderer->BuildWebRenderDisplayItemsForLayer(
|
|
aItem->Frame()->PresContext(), aBuilder, aResources, aSc, aManager, aItem,
|
|
mDest, mDest, mDest.TopLeft(), mDest, mDest.Size(),
|
|
/* aOpacity = */ 1.0f);
|
|
}
|
|
|
|
bool BulletRenderer::CreateWebRenderCommandsForPath(
|
|
nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
|
|
wr::IpcResourceUpdateQueue& aResources,
|
|
const layers::StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder) {
|
|
MOZ_ASSERT(IsPathType());
|
|
wr::LayoutRect dest = wr::ToLayoutRect(mPathRect);
|
|
auto color = wr::ToColorF(ToDeviceColor(mColor));
|
|
bool isBackfaceVisible = !aItem->BackfaceIsHidden();
|
|
switch (mListStyleType) {
|
|
case NS_STYLE_LIST_STYLE_CIRCLE: {
|
|
LayoutDeviceSize radii = mPathRect.Size() / 2.0;
|
|
auto borderWidths = wr::ToBorderWidths(1.0, 1.0, 1.0, 1.0);
|
|
wr::BorderSide side = {color, wr::BorderStyle::Solid};
|
|
wr::BorderSide sides[4] = {side, side, side, side};
|
|
Range<const wr::BorderSide> sidesRange(sides, 4);
|
|
aBuilder.PushBorder(dest, dest, isBackfaceVisible, borderWidths,
|
|
sidesRange,
|
|
wr::ToBorderRadius(radii, radii, radii, radii));
|
|
return true;
|
|
}
|
|
case NS_STYLE_LIST_STYLE_DISC: {
|
|
aBuilder.PushRoundedRect(dest, dest, isBackfaceVisible, color);
|
|
return true;
|
|
}
|
|
case NS_STYLE_LIST_STYLE_SQUARE: {
|
|
aBuilder.PushRect(dest, dest, isBackfaceVisible, color);
|
|
return true;
|
|
}
|
|
default:
|
|
if (!aManager->CommandBuilder().PushItemAsImage(
|
|
aItem, aBuilder, aResources, aSc, aDisplayListBuilder)) {
|
|
NS_WARNING("Fail to create WebRender commands for Bullet path.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BulletRenderer::CreateWebRenderCommandsForText(
|
|
nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
|
|
wr::IpcResourceUpdateQueue& aResources,
|
|
const layers::StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder) {
|
|
MOZ_ASSERT(IsTextType());
|
|
|
|
bool dummy;
|
|
nsRect bounds = aItem->GetBounds(aDisplayListBuilder, &dummy);
|
|
|
|
if (bounds.IsEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
RefPtr<TextDrawTarget> textDrawer =
|
|
new TextDrawTarget(aBuilder, aResources, aSc, aManager, aItem, bounds);
|
|
RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
|
|
PaintTextToContext(aItem->Frame(), captureCtx, aItem->IsSubpixelAADisabled());
|
|
textDrawer->TerminateShadows();
|
|
|
|
return textDrawer->Finish();
|
|
}
|
|
|
|
class nsDisplayBullet final : public nsPaintedDisplayItem {
|
|
public:
|
|
nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
|
|
: nsPaintedDisplayItem(aBuilder, aFrame) {
|
|
MOZ_COUNT_CTOR(nsDisplayBullet);
|
|
}
|
|
MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBullet)
|
|
|
|
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
|
|
bool* aSnap) const override {
|
|
*aSnap = false;
|
|
return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
|
|
}
|
|
|
|
virtual bool CreateWebRenderCommands(
|
|
mozilla::wr::DisplayListBuilder& aBuilder,
|
|
mozilla::wr::IpcResourceUpdateQueue&, const StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder) override;
|
|
|
|
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
|
HitTestState* aState,
|
|
nsTArray<nsIFrame*>* aOutFrames) override {
|
|
aOutFrames->AppendElement(mFrame);
|
|
}
|
|
virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
|
|
NS_DISPLAY_DECL_NAME("Bullet", TYPE_BULLET)
|
|
|
|
virtual nsRect GetComponentAlphaBounds(
|
|
nsDisplayListBuilder* aBuilder) const override {
|
|
bool snap;
|
|
return GetBounds(aBuilder, &snap);
|
|
}
|
|
|
|
virtual nsDisplayItemGeometry* AllocateGeometry(
|
|
nsDisplayListBuilder* aBuilder) override {
|
|
return new nsDisplayBulletGeometry(this, aBuilder);
|
|
}
|
|
|
|
virtual void ComputeInvalidationRegion(
|
|
nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
|
|
nsRegion* aInvalidRegion) const override {
|
|
const nsDisplayBulletGeometry* geometry =
|
|
static_cast<const nsDisplayBulletGeometry*>(aGeometry);
|
|
nsBulletFrame* f = static_cast<nsBulletFrame*>(mFrame);
|
|
|
|
if (f->Ordinal() != geometry->mOrdinal) {
|
|
bool snap;
|
|
aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap));
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<imgIContainer> image = f->GetImage();
|
|
if (aBuilder->ShouldSyncDecodeImages() && image &&
|
|
geometry->ShouldInvalidateToSyncDecodeImages()) {
|
|
bool snap;
|
|
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
|
|
}
|
|
|
|
return nsPaintedDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry,
|
|
aInvalidRegion);
|
|
}
|
|
|
|
protected:
|
|
Maybe<BulletRenderer> mBulletRenderer;
|
|
};
|
|
|
|
bool nsDisplayBullet::CreateWebRenderCommands(
|
|
wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
|
|
const StackingContextHelper& aSc,
|
|
mozilla::layers::RenderRootStateManager* aManager,
|
|
nsDisplayListBuilder* aDisplayListBuilder) {
|
|
// FIXME: avoid needing to make this target if we're drawing text
|
|
// (non-trivial refactor of all this code)
|
|
RefPtr<gfxContext> screenRefCtx = gfxContext::CreateOrNull(
|
|
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
|
|
|
|
ImgDrawResult drawResult;
|
|
Maybe<BulletRenderer> br =
|
|
static_cast<nsBulletFrame*>(mFrame)->CreateBulletRenderer(
|
|
*screenRefCtx, aDisplayListBuilder, ToReferenceFrame(), &drawResult);
|
|
|
|
if (br) {
|
|
ImgDrawResult commandBuilderResult = br->CreateWebRenderCommands(
|
|
this, aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
|
|
if (commandBuilderResult == ImgDrawResult::NOT_SUPPORTED) {
|
|
return false;
|
|
}
|
|
|
|
drawResult &= commandBuilderResult;
|
|
}
|
|
|
|
nsDisplayBulletGeometry::UpdateDrawResult(this, drawResult);
|
|
return true;
|
|
}
|
|
|
|
void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
|
|
ImgDrawResult result = static_cast<nsBulletFrame*>(mFrame)->PaintBullet(
|
|
*aCtx, aBuilder, ToReferenceFrame(), GetPaintRect(),
|
|
IsSubpixelAADisabled());
|
|
nsDisplayBulletGeometry::UpdateDrawResult(this, result);
|
|
}
|
|
|
|
void nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
const nsDisplayListSet& aLists) {
|
|
if (!IsVisibleForPainting()) return;
|
|
|
|
DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame");
|
|
|
|
aLists.Content()->AppendNewToTop<nsDisplayBullet>(aBuilder, this);
|
|
}
|
|
|
|
Maybe<BulletRenderer> nsBulletFrame::CreateBulletRenderer(
|
|
gfxContext& aRenderingContext, nsDisplayListBuilder* aBuilder, nsPoint aPt,
|
|
ImgDrawResult* aDrawResult) {
|
|
*aDrawResult = ImgDrawResult::SUCCESS;
|
|
|
|
CounterStyle* listStyleType = ResolveCounterStyle();
|
|
nsMargin padding = mPadding.GetPhysicalMargin(GetWritingMode());
|
|
const auto& image = StyleList()->mListStyleImage;
|
|
if (!image.IsNone()) {
|
|
nsRect dest(padding.left, padding.top,
|
|
mRect.width - (padding.left + padding.right),
|
|
mRect.height - (padding.top + padding.bottom));
|
|
BulletRenderer br(this, image, dest + aPt,
|
|
aBuilder->GetImageRendererFlags());
|
|
if (br.PrepareImage()) {
|
|
return Some(br);
|
|
}
|
|
*aDrawResult = br.PrepareResult();
|
|
}
|
|
|
|
nscolor color = nsLayoutUtils::GetColor(this, &nsStyleText::mColor);
|
|
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
|
|
int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
|
|
|
|
switch (listStyleType->GetStyle()) {
|
|
case NS_STYLE_LIST_STYLE_NONE:
|
|
return Nothing();
|
|
|
|
case NS_STYLE_LIST_STYLE_DISC:
|
|
case NS_STYLE_LIST_STYLE_CIRCLE: {
|
|
nsRect rect(padding.left + aPt.x, padding.top + aPt.y,
|
|
mRect.width - (padding.left + padding.right),
|
|
mRect.height - (padding.top + padding.bottom));
|
|
auto devPxRect =
|
|
LayoutDeviceRect::FromAppUnits(rect, appUnitsPerDevPixel);
|
|
return Some(BulletRenderer(devPxRect, color, listStyleType->GetStyle()));
|
|
}
|
|
|
|
case NS_STYLE_LIST_STYLE_SQUARE: {
|
|
nsRect rect(aPt, mRect.Size());
|
|
rect.Deflate(padding);
|
|
|
|
// Snap the height and the width of the rectangle to device pixels,
|
|
// and then center the result within the original rectangle, so that
|
|
// all square bullets at the same font size have the same visual
|
|
// size (bug 376690).
|
|
// FIXME: We should really only do this if we're not transformed
|
|
// (like gfxContext::UserToDevicePixelSnapped does).
|
|
nsPresContext* pc = PresContext();
|
|
nsRect snapRect(rect.x, rect.y,
|
|
pc->RoundAppUnitsToNearestDevPixels(rect.width),
|
|
pc->RoundAppUnitsToNearestDevPixels(rect.height));
|
|
snapRect.MoveBy((rect.width - snapRect.width) / 2,
|
|
(rect.height - snapRect.height) / 2);
|
|
auto devPxRect =
|
|
LayoutDeviceRect::FromAppUnits(snapRect, appUnitsPerDevPixel);
|
|
return Some(BulletRenderer(devPxRect, color, listStyleType->GetStyle()));
|
|
}
|
|
|
|
case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
|
|
case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: {
|
|
nsRect rect(aPt, mRect.Size());
|
|
rect.Deflate(padding);
|
|
|
|
WritingMode wm = GetWritingMode();
|
|
bool isVertical = wm.IsVertical();
|
|
bool isClosed =
|
|
listStyleType->GetStyle() == NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED;
|
|
bool isDown = (!isVertical && !isClosed) || (isVertical && isClosed);
|
|
nscoord diff = NSToCoordRound(0.1f * rect.height);
|
|
if (isDown) {
|
|
rect.y += diff * 2;
|
|
rect.height -= diff * 2;
|
|
} else {
|
|
rect.Deflate(diff, 0);
|
|
}
|
|
nsPresContext* pc = PresContext();
|
|
rect.x = pc->RoundAppUnitsToNearestDevPixels(rect.x);
|
|
rect.y = pc->RoundAppUnitsToNearestDevPixels(rect.y);
|
|
|
|
RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
|
|
if (isDown) {
|
|
// to bottom
|
|
builder->MoveTo(NSPointToPoint(rect.TopLeft(), appUnitsPerDevPixel));
|
|
builder->LineTo(NSPointToPoint(rect.TopRight(), appUnitsPerDevPixel));
|
|
builder->LineTo(NSPointToPoint(
|
|
(rect.BottomLeft() + rect.BottomRight()) / 2, appUnitsPerDevPixel));
|
|
} else {
|
|
if (wm.IsPhysicalLTR()) {
|
|
// to right
|
|
builder->MoveTo(NSPointToPoint(rect.TopLeft(), appUnitsPerDevPixel));
|
|
builder->LineTo(NSPointToPoint(
|
|
(rect.TopRight() + rect.BottomRight()) / 2, appUnitsPerDevPixel));
|
|
builder->LineTo(
|
|
NSPointToPoint(rect.BottomLeft(), appUnitsPerDevPixel));
|
|
} else {
|
|
// to left
|
|
builder->MoveTo(NSPointToPoint(rect.TopRight(), appUnitsPerDevPixel));
|
|
builder->LineTo(
|
|
NSPointToPoint(rect.BottomRight(), appUnitsPerDevPixel));
|
|
builder->LineTo(NSPointToPoint(
|
|
(rect.TopLeft() + rect.BottomLeft()) / 2, appUnitsPerDevPixel));
|
|
}
|
|
}
|
|
|
|
RefPtr<Path> path = builder->Finish();
|
|
BulletRenderer br(path, color, listStyleType->GetStyle());
|
|
return Some(br);
|
|
}
|
|
|
|
default: {
|
|
RefPtr<nsFontMetrics> fm =
|
|
nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
|
|
nsAutoString text;
|
|
WritingMode wm = GetWritingMode();
|
|
GetListItemText(listStyleType, wm, Ordinal(), text);
|
|
nscoord ascent = wm.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
|
|
aPt.MoveBy(padding.left, padding.top);
|
|
if (wm.IsVertical()) {
|
|
if (wm.IsVerticalLR()) {
|
|
aPt.x = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineX(
|
|
this, &aRenderingContext, aPt.x, ascent));
|
|
} else {
|
|
aPt.x = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineX(
|
|
this, &aRenderingContext, aPt.x + mRect.width, -ascent));
|
|
}
|
|
} else {
|
|
aPt.y = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineY(
|
|
this, &aRenderingContext, aPt.y, ascent));
|
|
}
|
|
|
|
BulletRenderer br(text, fm, color, aPt, listStyleType->GetStyle());
|
|
return Some(br);
|
|
}
|
|
}
|
|
|
|
MOZ_CRASH("unreachable");
|
|
return Nothing();
|
|
}
|
|
|
|
ImgDrawResult nsBulletFrame::PaintBullet(gfxContext& aRenderingContext,
|
|
nsDisplayListBuilder* aBuilder,
|
|
nsPoint aPt, const nsRect& aDirtyRect,
|
|
bool aDisableSubpixelAA) {
|
|
ImgDrawResult drawResult;
|
|
Maybe<BulletRenderer> br =
|
|
CreateBulletRenderer(aRenderingContext, aBuilder, aPt, &drawResult);
|
|
|
|
if (!br) {
|
|
return drawResult;
|
|
}
|
|
|
|
return drawResult & br->Paint(aRenderingContext, aPt, aDirtyRect,
|
|
aDisableSubpixelAA, this);
|
|
}
|
|
|
|
void nsBulletFrame::GetListItemText(CounterStyle* aStyle,
|
|
mozilla::WritingMode aWritingMode,
|
|
int32_t aOrdinal, nsAString& aResult) {
|
|
NS_ASSERTION(
|
|
aStyle->GetStyle() != NS_STYLE_LIST_STYLE_NONE &&
|
|
aStyle->GetStyle() != NS_STYLE_LIST_STYLE_DISC &&
|
|
aStyle->GetStyle() != NS_STYLE_LIST_STYLE_CIRCLE &&
|
|
aStyle->GetStyle() != NS_STYLE_LIST_STYLE_SQUARE &&
|
|
aStyle->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
|
|
aStyle->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN,
|
|
"we should be using specialized code for these types");
|
|
|
|
bool isRTL;
|
|
nsAutoString counter, prefix, suffix;
|
|
aStyle->GetPrefix(prefix);
|
|
aStyle->GetSuffix(suffix);
|
|
aStyle->GetCounterText(aOrdinal, aWritingMode, counter, isRTL);
|
|
|
|
aResult.Truncate();
|
|
aResult.Append(prefix);
|
|
if (aWritingMode.IsBidiRTL() == isRTL) {
|
|
aResult.Append(counter);
|
|
} else {
|
|
// RLM = 0x200f, LRM = 0x200e
|
|
char16_t mark = isRTL ? 0x200f : 0x200e;
|
|
aResult.Append(mark);
|
|
aResult.Append(counter);
|
|
aResult.Append(mark);
|
|
}
|
|
aResult.Append(suffix);
|
|
}
|
|
|
|
#define MIN_BULLET_SIZE 1
|
|
|
|
void nsBulletFrame::AppendSpacingToPadding(nsFontMetrics* aFontMetrics,
|
|
LogicalMargin* aPadding) {
|
|
aPadding->IEnd(GetWritingMode()) += aFontMetrics->EmHeight() / 2;
|
|
}
|
|
|
|
void nsBulletFrame::GetDesiredSize(nsPresContext* aCX,
|
|
gfxContext* aRenderingContext,
|
|
ReflowOutput& aMetrics,
|
|
float aFontSizeInflation,
|
|
LogicalMargin* aPadding) {
|
|
// Reset our padding. If we need it, we'll set it below.
|
|
WritingMode wm = GetWritingMode();
|
|
aPadding->SizeTo(wm, 0, 0, 0, 0);
|
|
LogicalSize finalSize(wm);
|
|
|
|
nscoord ascent;
|
|
RefPtr<nsFontMetrics> fm =
|
|
nsLayoutUtils::GetFontMetricsForFrame(this, aFontSizeInflation);
|
|
|
|
RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
|
|
|
|
if (RefPtr<imgIContainer> image = GetImage()) {
|
|
int32_t w = 0;
|
|
int32_t h = 0;
|
|
image->GetWidth(&w);
|
|
image->GetHeight(&h);
|
|
LogicalSize size(GetWritingMode(), CSSPixel::ToAppUnits(CSSIntSize(w, h)));
|
|
// auto size the image
|
|
finalSize.ISize(wm) = size.ISize(wm);
|
|
finalSize.BSize(wm) = size.BSize(wm);
|
|
aMetrics.SetBlockStartAscent(size.BSize(wm));
|
|
aMetrics.SetSize(wm, finalSize);
|
|
|
|
AppendSpacingToPadding(fm, aPadding);
|
|
|
|
AddStateBits(BULLET_FRAME_IMAGE_LOADING);
|
|
return;
|
|
}
|
|
|
|
nscoord bulletSize;
|
|
|
|
nsAutoString text;
|
|
CounterStyle* style = ResolveCounterStyle();
|
|
switch (style->GetStyle()) {
|
|
case NS_STYLE_LIST_STYLE_NONE:
|
|
finalSize.ISize(wm) = finalSize.BSize(wm) = 0;
|
|
aMetrics.SetBlockStartAscent(0);
|
|
break;
|
|
case NS_STYLE_LIST_STYLE_DISC:
|
|
case NS_STYLE_LIST_STYLE_CIRCLE:
|
|
case NS_STYLE_LIST_STYLE_SQUARE: {
|
|
ascent = fm->MaxAscent();
|
|
bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
|
|
NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
|
|
aPadding->BEnd(wm) = NSToCoordRound(float(ascent) / 8.0f);
|
|
finalSize.ISize(wm) = finalSize.BSize(wm) = bulletSize;
|
|
aMetrics.SetBlockStartAscent(bulletSize + aPadding->BEnd(wm));
|
|
AppendSpacingToPadding(fm, aPadding);
|
|
break;
|
|
}
|
|
|
|
case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
|
|
case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
|
|
ascent = fm->EmAscent();
|
|
bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
|
|
NSToCoordRound(0.75f * ascent));
|
|
aPadding->BEnd(wm) = NSToCoordRound(0.125f * ascent);
|
|
finalSize.ISize(wm) = finalSize.BSize(wm) = bulletSize;
|
|
if (!wm.IsVertical()) {
|
|
aMetrics.SetBlockStartAscent(bulletSize + aPadding->BEnd(wm));
|
|
}
|
|
AppendSpacingToPadding(fm, aPadding);
|
|
break;
|
|
|
|
default:
|
|
GetListItemText(style, wm, Ordinal(), text);
|
|
finalSize.BSize(wm) = fm->MaxHeight();
|
|
finalSize.ISize(wm) = nsLayoutUtils::AppUnitWidthOfStringBidi(
|
|
text, this, *fm, *aRenderingContext);
|
|
aMetrics.SetBlockStartAscent(wm.IsLineInverted() ? fm->MaxDescent()
|
|
: fm->MaxAscent());
|
|
break;
|
|
}
|
|
aMetrics.SetSize(wm, finalSize);
|
|
}
|
|
|
|
void nsBulletFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
|
|
const ReflowInput& aReflowInput,
|
|
nsReflowStatus& aStatus) {
|
|
MarkInReflow();
|
|
DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
|
|
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
|
|
MOZ_ASSERT(aStatus.IsEmpty(), "The reflow status should be empty!");
|
|
|
|
float inflation = nsLayoutUtils::FontSizeInflationFor(this);
|
|
SetFontSizeInflation(inflation);
|
|
|
|
// Get the base size
|
|
GetDesiredSize(aPresContext, aReflowInput.mRenderingContext, aMetrics,
|
|
inflation, &mPadding);
|
|
|
|
// Add in the border and padding; split the top/bottom between the
|
|
// ascent and descent to make things look nice
|
|
WritingMode wm = aReflowInput.GetWritingMode();
|
|
const auto bp = aReflowInput.ComputedLogicalBorderPadding(wm);
|
|
mPadding.BStart(wm) += NSToCoordRound(bp.BStart(wm) * inflation);
|
|
mPadding.IEnd(wm) += NSToCoordRound(bp.IEnd(wm) * inflation);
|
|
mPadding.BEnd(wm) += NSToCoordRound(bp.BEnd(wm) * inflation);
|
|
mPadding.IStart(wm) += NSToCoordRound(bp.IStart(wm) * inflation);
|
|
|
|
WritingMode lineWM = aMetrics.GetWritingMode();
|
|
LogicalMargin linePadding = mPadding.ConvertTo(lineWM, wm);
|
|
aMetrics.ISize(lineWM) += linePadding.IStartEnd(lineWM);
|
|
aMetrics.BSize(lineWM) += linePadding.BStartEnd(lineWM);
|
|
aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
|
|
linePadding.BStart(lineWM));
|
|
|
|
// XXX this is a bit of a hack, we're assuming that no glyphs used for bullets
|
|
// overflow their font-boxes. It'll do for now; to fix it for real, we really
|
|
// should rewrite all the text-handling code here to use gfxTextRun (bug
|
|
// 397294).
|
|
aMetrics.SetOverflowAreasToDesiredBounds();
|
|
|
|
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
|
|
}
|
|
|
|
/* virtual */
|
|
nscoord nsBulletFrame::GetMinISize(gfxContext* aRenderingContext) {
|
|
WritingMode wm = GetWritingMode();
|
|
ReflowOutput reflowOutput(wm);
|
|
DISPLAY_MIN_INLINE_SIZE(this, reflowOutput.ISize(wm));
|
|
LogicalMargin padding(wm);
|
|
GetDesiredSize(PresContext(), aRenderingContext, reflowOutput, 1.0f,
|
|
&padding);
|
|
reflowOutput.ISize(wm) += padding.IStartEnd(wm);
|
|
return reflowOutput.ISize(wm);
|
|
}
|
|
|
|
/* virtual */
|
|
nscoord nsBulletFrame::GetPrefISize(gfxContext* aRenderingContext) {
|
|
WritingMode wm = GetWritingMode();
|
|
ReflowOutput metrics(wm);
|
|
DISPLAY_PREF_INLINE_SIZE(this, metrics.ISize(wm));
|
|
LogicalMargin padding(wm);
|
|
GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f, &padding);
|
|
metrics.ISize(wm) += padding.IStartEnd(wm);
|
|
return metrics.ISize(wm);
|
|
}
|
|
|
|
// If a bullet has zero size and is "ignorable" from its styling, we behave
|
|
// as if it doesn't exist, from a line-breaking/isize-computation perspective.
|
|
// Otherwise, we use the default implementation, same as nsFrame.
|
|
static inline bool IsIgnoreable(const nsIFrame* aFrame, nscoord aISize) {
|
|
if (aISize != nscoord(0)) {
|
|
return false;
|
|
}
|
|
auto listStyle = aFrame->StyleList();
|
|
return listStyle->mCounterStyle.IsNone() &&
|
|
listStyle->mListStyleImage.IsNone();
|
|
}
|
|
|
|
/* virtual */
|
|
void nsBulletFrame::AddInlineMinISize(gfxContext* aRenderingContext,
|
|
nsIFrame::InlineMinISizeData* aData) {
|
|
nscoord isize = nsLayoutUtils::IntrinsicForContainer(
|
|
aRenderingContext, this, IntrinsicISizeType::MinISize);
|
|
if (MOZ_LIKELY(!::IsIgnoreable(this, isize))) {
|
|
aData->DefaultAddInlineMinISize(this, isize);
|
|
}
|
|
}
|
|
|
|
/* virtual */
|
|
void nsBulletFrame::AddInlinePrefISize(gfxContext* aRenderingContext,
|
|
nsIFrame::InlinePrefISizeData* aData) {
|
|
nscoord isize = nsLayoutUtils::IntrinsicForContainer(
|
|
aRenderingContext, this, IntrinsicISizeType::PrefISize);
|
|
if (MOZ_LIKELY(!::IsIgnoreable(this, isize))) {
|
|
aData->DefaultAddInlinePrefISize(isize);
|
|
}
|
|
}
|
|
|
|
float nsBulletFrame::GetFontSizeInflation() const {
|
|
if (!HasFontSizeInflation()) {
|
|
return 1.0f;
|
|
}
|
|
return GetProperty(FontSizeInflationProperty());
|
|
}
|
|
|
|
void nsBulletFrame::SetFontSizeInflation(float aInflation) {
|
|
if (aInflation == 1.0f) {
|
|
if (HasFontSizeInflation()) {
|
|
RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
|
|
RemoveProperty(FontSizeInflationProperty());
|
|
}
|
|
return;
|
|
}
|
|
|
|
AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
|
|
SetProperty(FontSizeInflationProperty(), aInflation);
|
|
}
|
|
|
|
already_AddRefed<imgIContainer> nsBulletFrame::GetImage() const {
|
|
if (auto* request = StyleList()->mListStyleImage.GetImageRequest()) {
|
|
nsCOMPtr<imgIContainer> imageCon;
|
|
request->GetImage(getter_AddRefs(imageCon));
|
|
return imageCon.forget();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
nscoord nsBulletFrame::GetListStyleAscent() const {
|
|
RefPtr<nsFontMetrics> fm =
|
|
nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
|
|
auto* list = StyleList();
|
|
if (list->mCounterStyle.IsNone()) {
|
|
return 0;
|
|
}
|
|
if (list->mCounterStyle.IsAnonymous()) {
|
|
return fm->MaxAscent();
|
|
}
|
|
// NOTE(emilio): @counter-style can override most of the styles from this
|
|
// list, and we still return the changed ascent. Do we care about that?
|
|
//
|
|
// https://github.com/w3c/csswg-drafts/issues/3584
|
|
nsAtom* style = list->mCounterStyle.AsAtom();
|
|
if (style == nsGkAtoms::disc || style == nsGkAtoms::circle ||
|
|
style == nsGkAtoms::square) {
|
|
nscoord ascent = fm->MaxAscent();
|
|
nscoord baselinePadding = NSToCoordRound(float(ascent) / 8.0f);
|
|
ascent = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
|
|
NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
|
|
return ascent + baselinePadding;
|
|
}
|
|
if (style == nsGkAtoms::disclosure_open ||
|
|
style == nsGkAtoms::disclosure_closed) {
|
|
nscoord ascent = fm->EmAscent();
|
|
nscoord baselinePadding = NSToCoordRound(0.125f * ascent);
|
|
ascent = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
|
|
NSToCoordRound(0.75f * ascent));
|
|
return ascent + baselinePadding;
|
|
}
|
|
return fm->MaxAscent();
|
|
}
|
|
|
|
nscoord nsBulletFrame::GetLogicalBaseline(WritingMode aWritingMode) const {
|
|
nscoord ascent = 0;
|
|
if (HasAnyStateBits(BULLET_FRAME_IMAGE_LOADING)) {
|
|
ascent = BSize(aWritingMode);
|
|
} else {
|
|
ascent = GetListStyleAscent();
|
|
}
|
|
return ascent + GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
|
|
}
|
|
|
|
bool nsBulletFrame::GetNaturalBaselineBOffset(WritingMode aWM,
|
|
BaselineSharingGroup,
|
|
nscoord* aBaseline) const {
|
|
nscoord ascent = 0;
|
|
if (HasAnyStateBits(BULLET_FRAME_IMAGE_LOADING)) {
|
|
ascent = BSize(aWM);
|
|
} else {
|
|
ascent = GetListStyleAscent();
|
|
}
|
|
*aBaseline = ascent;
|
|
return true;
|
|
}
|
|
|
|
#ifdef ACCESSIBILITY
|
|
void nsBulletFrame::GetSpokenText(nsAString& aText) {
|
|
CounterStyle* style =
|
|
PresContext()->CounterStyleManager()->ResolveCounterStyle(
|
|
StyleList()->mCounterStyle);
|
|
bool isBullet;
|
|
style->GetSpokenCounterText(Ordinal(), GetWritingMode(), aText, isBullet);
|
|
if (isBullet) {
|
|
if (!style->IsNone()) {
|
|
aText.Append(' ');
|
|
}
|
|
} else {
|
|
nsAutoString prefix, suffix;
|
|
style->GetPrefix(prefix);
|
|
style->GetSuffix(suffix);
|
|
aText = prefix + aText + suffix;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void nsBulletFrame::SetOrdinal(int32_t aOrdinal, bool aNotify) {
|
|
if (mOrdinal == aOrdinal) {
|
|
return;
|
|
}
|
|
mOrdinal = aOrdinal;
|
|
if (aNotify) {
|
|
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
|
|
NS_FRAME_IS_DIRTY);
|
|
}
|
|
}
|