1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* rendering object for list-item bullets */
8 #include "nsBulletFrame.h"
10 #include "mozilla/MathAlgorithms.h"
12 #include "nsGkAtoms.h"
13 #include "nsGenericHTMLElement.h"
14 #include "nsAttrValueInlines.h"
15 #include "nsPresContext.h"
16 #include "nsIPresShell.h"
17 #include "nsIDocument.h"
18 #include "nsRenderingContext.h"
19 #include "nsDisplayList.h"
20 #include "nsCounterManager.h"
21 #include "nsBidiUtils.h"
22 #include "CounterStyleManager.h"
24 #include "imgIContainer.h"
25 #include "imgRequestProxy.h"
31 #include "nsAccessibilityService.h"
34 using namespace mozilla
;
36 NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty
, nullptr)
38 NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame
)
41 NS_QUERYFRAME_HEAD(nsBulletFrame
)
42 NS_QUERYFRAME_ENTRY(nsBulletFrame
)
43 NS_QUERYFRAME_TAIL_INHERITING(nsFrame
)
46 nsBulletFrame::~nsBulletFrame()
51 nsBulletFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
53 // Stop image loading first
55 // Deregister our image request from the refresh driver
56 nsLayoutUtils::DeregisterImageRequest(PresContext(),
59 mImageRequest
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
60 mImageRequest
= nullptr;
64 mListener
->SetFrame(nullptr);
67 // Let base class do the rest
68 nsFrame::DestroyFrom(aDestructRoot
);
71 #ifdef DEBUG_FRAME_DUMP
73 nsBulletFrame::GetFrameName(nsAString
& aResult
) const
75 return MakeFrameName(NS_LITERAL_STRING("Bullet"), aResult
);
80 nsBulletFrame::GetType() const
82 return nsGkAtoms::bulletFrame
;
86 nsBulletFrame::IsEmpty()
92 nsBulletFrame::IsSelfEmpty()
94 return StyleList()->GetCounterStyle()->IsNone();
98 nsBulletFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
100 nsFrame::DidSetStyleContext(aOldStyleContext
);
102 imgRequestProxy
*newRequest
= StyleList()->GetListStyleImage();
107 mListener
= new nsBulletListener();
108 mListener
->SetFrame(this);
111 bool needNewRequest
= true;
114 // Reload the image, maybe...
115 nsCOMPtr
<nsIURI
> oldURI
;
116 mImageRequest
->GetURI(getter_AddRefs(oldURI
));
117 nsCOMPtr
<nsIURI
> newURI
;
118 newRequest
->GetURI(getter_AddRefs(newURI
));
119 if (oldURI
&& newURI
) {
121 newURI
->Equals(oldURI
, &same
);
123 needNewRequest
= false;
128 if (needNewRequest
) {
129 nsRefPtr
<imgRequestProxy
> oldRequest
= mImageRequest
;
130 newRequest
->Clone(mListener
, getter_AddRefs(mImageRequest
));
132 // Deregister the old request. We wait until after Clone is done in case
133 // the old request and the new request are the same underlying image
134 // accessed via different URLs.
136 nsLayoutUtils::DeregisterImageRequest(PresContext(), oldRequest
,
137 &mRequestRegistered
);
138 oldRequest
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
139 oldRequest
= nullptr;
142 // Register the new request.
144 nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(),
146 &mRequestRegistered
);
150 // No image request on the new style context
152 nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest
,
153 &mRequestRegistered
);
155 mImageRequest
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
156 mImageRequest
= nullptr;
161 // Update the list bullet accessible. If old style list isn't available then
162 // no need to update the accessible tree because it's not created yet.
163 if (aOldStyleContext
) {
164 nsAccessibilityService
* accService
= nsIPresShell::AccService();
166 const nsStyleList
* oldStyleList
= aOldStyleContext
->PeekStyleList();
168 bool hadBullet
= oldStyleList
->GetListStyleImage() ||
169 !oldStyleList
->GetCounterStyle()->IsNone();
171 const nsStyleList
* newStyleList
= StyleList();
172 bool hasBullet
= newStyleList
->GetListStyleImage() ||
173 !newStyleList
->GetCounterStyle()->IsNone();
175 if (hadBullet
!= hasBullet
) {
176 accService
->UpdateListBullet(PresContext()->GetPresShell(), mContent
,
185 class nsDisplayBulletGeometry
: public nsDisplayItemGenericGeometry
188 nsDisplayBulletGeometry(nsDisplayItem
* aItem
, nsDisplayListBuilder
* aBuilder
)
189 : nsDisplayItemGenericGeometry(aItem
, aBuilder
)
191 nsBulletFrame
* f
= static_cast<nsBulletFrame
*>(aItem
->Frame());
192 mOrdinal
= f
->GetOrdinal();
198 class nsDisplayBullet MOZ_FINAL
: public nsDisplayItem
{
200 nsDisplayBullet(nsDisplayListBuilder
* aBuilder
, nsBulletFrame
* aFrame
) :
201 nsDisplayItem(aBuilder
, aFrame
) {
202 MOZ_COUNT_CTOR(nsDisplayBullet
);
204 #ifdef NS_BUILD_REFCNT_LOGGING
205 virtual ~nsDisplayBullet() {
206 MOZ_COUNT_DTOR(nsDisplayBullet
);
210 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
,
211 bool* aSnap
) MOZ_OVERRIDE
214 return mFrame
->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
216 virtual void HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
217 HitTestState
* aState
,
218 nsTArray
<nsIFrame
*> *aOutFrames
) MOZ_OVERRIDE
{
219 aOutFrames
->AppendElement(mFrame
);
221 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
222 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
223 NS_DISPLAY_DECL_NAME("Bullet", TYPE_BULLET
)
225 virtual nsRect
GetComponentAlphaBounds(nsDisplayListBuilder
* aBuilder
) MOZ_OVERRIDE
228 return GetBounds(aBuilder
, &snap
);
231 virtual nsDisplayItemGeometry
* AllocateGeometry(nsDisplayListBuilder
* aBuilder
) MOZ_OVERRIDE
233 return new nsDisplayBulletGeometry(this, aBuilder
);
236 virtual void ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
237 const nsDisplayItemGeometry
* aGeometry
,
238 nsRegion
*aInvalidRegion
) MOZ_OVERRIDE
240 const nsDisplayBulletGeometry
* geometry
= static_cast<const nsDisplayBulletGeometry
*>(aGeometry
);
241 nsBulletFrame
* f
= static_cast<nsBulletFrame
*>(mFrame
);
243 if (f
->GetOrdinal() != geometry
->mOrdinal
) {
245 aInvalidRegion
->Or(geometry
->mBounds
, GetBounds(aBuilder
, &snap
));
249 nsCOMPtr
<imgIContainer
> image
= f
->GetImage();
250 if (aBuilder
->ShouldSyncDecodeImages() && image
&& !image
->IsDecoded()) {
251 // If we are going to do a sync decode and we are not decoded then we are
252 // going to be drawing something different from what is currently there,
253 // so we add our bounds to the invalid region.
255 aInvalidRegion
->Or(*aInvalidRegion
, GetBounds(aBuilder
, &snap
));
258 return nsDisplayItem::ComputeInvalidationRegion(aBuilder
, aGeometry
, aInvalidRegion
);
262 void nsDisplayBullet::Paint(nsDisplayListBuilder
* aBuilder
,
263 nsRenderingContext
* aCtx
)
265 uint32_t flags
= imgIContainer::FLAG_NONE
;
266 if (aBuilder
->ShouldSyncDecodeImages()) {
267 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
269 static_cast<nsBulletFrame
*>(mFrame
)->
270 PaintBullet(*aCtx
, ToReferenceFrame(), mVisibleRect
, flags
);
274 nsBulletFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
275 const nsRect
& aDirtyRect
,
276 const nsDisplayListSet
& aLists
)
278 if (!IsVisibleForPainting(aBuilder
))
281 DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame");
283 aLists
.Content()->AppendNewToTop(
284 new (aBuilder
) nsDisplayBullet(aBuilder
, this));
288 nsBulletFrame::PaintBullet(nsRenderingContext
& aRenderingContext
, nsPoint aPt
,
289 const nsRect
& aDirtyRect
, uint32_t aFlags
)
291 const nsStyleList
* myList
= StyleList();
292 CounterStyle
* listStyleType
= myList
->GetCounterStyle();
293 nsMargin padding
= mPadding
.GetPhysicalMargin(GetWritingMode());
295 if (myList
->GetListStyleImage() && mImageRequest
) {
297 mImageRequest
->GetImageStatus(&status
);
298 if (status
& imgIRequest::STATUS_LOAD_COMPLETE
&&
299 !(status
& imgIRequest::STATUS_ERROR
)) {
300 nsCOMPtr
<imgIContainer
> imageCon
;
301 mImageRequest
->GetImage(getter_AddRefs(imageCon
));
303 nsRect
dest(padding
.left
, padding
.top
,
304 mRect
.width
- (padding
.left
+ padding
.right
),
305 mRect
.height
- (padding
.top
+ padding
.bottom
));
306 nsLayoutUtils::DrawSingleImage(&aRenderingContext
, PresContext(),
307 imageCon
, nsLayoutUtils::GetGraphicsFilterForFrame(this),
308 dest
+ aPt
, aDirtyRect
, nullptr, aFlags
);
314 nsRefPtr
<nsFontMetrics
> fm
;
315 aRenderingContext
.SetColor(nsLayoutUtils::GetColor(this, eCSSProperty_color
));
318 switch (listStyleType
->GetStyle()) {
319 case NS_STYLE_LIST_STYLE_NONE
:
322 case NS_STYLE_LIST_STYLE_DISC
:
323 aRenderingContext
.FillEllipse(padding
.left
+ aPt
.x
, padding
.top
+ aPt
.y
,
324 mRect
.width
- (padding
.left
+ padding
.right
),
325 mRect
.height
- (padding
.top
+ padding
.bottom
));
328 case NS_STYLE_LIST_STYLE_CIRCLE
:
329 aRenderingContext
.DrawEllipse(padding
.left
+ aPt
.x
, padding
.top
+ aPt
.y
,
330 mRect
.width
- (padding
.left
+ padding
.right
),
331 mRect
.height
- (padding
.top
+ padding
.bottom
));
334 case NS_STYLE_LIST_STYLE_SQUARE
:
336 nsRect
rect(aPt
, mRect
.Size());
337 rect
.Deflate(padding
);
339 // Snap the height and the width of the rectangle to device pixels,
340 // and then center the result within the original rectangle, so that
341 // all square bullets at the same font size have the same visual
342 // size (bug 376690).
343 // FIXME: We should really only do this if we're not transformed
344 // (like gfxContext::UserToDevicePixelSnapped does).
345 nsPresContext
*pc
= PresContext();
346 nsRect
snapRect(rect
.x
, rect
.y
,
347 pc
->RoundAppUnitsToNearestDevPixels(rect
.width
),
348 pc
->RoundAppUnitsToNearestDevPixels(rect
.height
));
349 snapRect
.MoveBy((rect
.width
- snapRect
.width
) / 2,
350 (rect
.height
- snapRect
.height
) / 2);
351 aRenderingContext
.FillRect(snapRect
.x
, snapRect
.y
,
352 snapRect
.width
, snapRect
.height
);
356 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
357 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
359 nsRect
rect(aPt
, mRect
.Size());
360 rect
.Deflate(padding
);
362 WritingMode wm
= GetWritingMode();
363 bool isVertical
= wm
.IsVertical();
365 listStyleType
->GetStyle() == NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
;
366 bool isDown
= (!isVertical
&& !isClosed
) || (isVertical
&& isClosed
);
367 nscoord diff
= NSToCoordRound(0.1f
* rect
.height
);
370 rect
.height
-= diff
* 2;
372 rect
.Deflate(diff
, 0);
374 nsPresContext
*pc
= PresContext();
375 rect
.x
= pc
->RoundAppUnitsToNearestDevPixels(rect
.x
);
376 rect
.y
= pc
->RoundAppUnitsToNearestDevPixels(rect
.y
);
381 points
[0] = rect
.TopLeft();
382 points
[1] = rect
.TopRight();
383 points
[2] = (rect
.BottomLeft() + rect
.BottomRight()) / 2;
385 bool isLR
= isVertical
? wm
.IsVerticalLR() : wm
.IsBidiLTR();
388 points
[0] = rect
.TopLeft();
389 points
[1] = (rect
.TopRight() + rect
.BottomRight()) / 2;
390 points
[2] = rect
.BottomLeft();
393 points
[0] = rect
.TopRight();
394 points
[1] = rect
.BottomRight();
395 points
[2] = (rect
.TopLeft() + rect
.BottomLeft()) / 2;
398 aRenderingContext
.FillPolygon(points
, 3);
403 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
),
404 GetFontSizeInflation());
405 GetListItemText(text
);
406 aRenderingContext
.SetFont(fm
);
407 nscoord ascent
= fm
->MaxAscent();
408 aPt
.MoveBy(padding
.left
, padding
.top
);
409 aPt
.y
= NSToCoordRound(nsLayoutUtils::GetSnappedBaselineY(
410 this, aRenderingContext
.ThebesContext(), aPt
.y
, ascent
));
411 nsPresContext
* presContext
= PresContext();
412 if (!presContext
->BidiEnabled() && HasRTLChars(text
)) {
413 presContext
->SetBidiEnabled();
415 nsLayoutUtils::DrawString(this, &aRenderingContext
,
416 text
.get(), text
.Length(), aPt
);
422 nsBulletFrame::SetListItemOrdinal(int32_t aNextOrdinal
,
426 MOZ_ASSERT(aIncrement
== 1 || aIncrement
== -1,
427 "We shouldn't have weird increments here");
429 // Assume that the ordinal comes from the caller
430 int32_t oldOrdinal
= mOrdinal
;
431 mOrdinal
= aNextOrdinal
;
433 // Try to get value directly from the list-item, if it specifies a
434 // value attribute. Note: we do this with our parent's content
435 // because our parent is the list-item.
436 nsIContent
* parentContent
= GetParent()->GetContent();
438 nsGenericHTMLElement
*hc
=
439 nsGenericHTMLElement::FromContent(parentContent
);
441 const nsAttrValue
* attr
= hc
->GetParsedAttr(nsGkAtoms::value
);
442 if (attr
&& attr
->Type() == nsAttrValue::eInteger
) {
443 // Use ordinal specified by the value attribute
444 mOrdinal
= attr
->GetIntegerValue();
449 *aChanged
= oldOrdinal
!= mOrdinal
;
451 return nsCounterManager::IncrementCounter(mOrdinal
, aIncrement
);
455 nsBulletFrame::GetListItemText(nsAString
& aResult
)
457 CounterStyle
* style
= StyleList()->GetCounterStyle();
458 NS_ASSERTION(style
->GetStyle() != NS_STYLE_LIST_STYLE_NONE
&&
459 style
->GetStyle() != NS_STYLE_LIST_STYLE_DISC
&&
460 style
->GetStyle() != NS_STYLE_LIST_STYLE_CIRCLE
&&
461 style
->GetStyle() != NS_STYLE_LIST_STYLE_SQUARE
&&
462 style
->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
&&
463 style
->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
,
464 "we should be using specialized code for these types");
467 nsAutoString counter
, prefix
, suffix
;
468 style
->GetPrefix(prefix
);
469 style
->GetSuffix(suffix
);
470 style
->GetCounterText(mOrdinal
, GetWritingMode(), counter
, isRTL
);
473 aResult
.Append(prefix
);
474 if (GetWritingMode().IsBidiLTR() != isRTL
) {
475 aResult
.Append(counter
);
477 // RLM = 0x200f, LRM = 0x200e
478 char16_t mark
= isRTL
? 0x200f : 0x200e;
479 aResult
.Append(mark
);
480 aResult
.Append(counter
);
481 aResult
.Append(mark
);
483 aResult
.Append(suffix
);
486 #define MIN_BULLET_SIZE 1
489 nsBulletFrame::AppendSpacingToPadding(nsFontMetrics
* aFontMetrics
)
491 mPadding
.IEnd(GetWritingMode()) += aFontMetrics
->EmHeight() / 2;
495 nsBulletFrame::GetDesiredSize(nsPresContext
* aCX
,
496 nsRenderingContext
*aRenderingContext
,
497 nsHTMLReflowMetrics
& aMetrics
,
498 float aFontSizeInflation
)
500 // Reset our padding. If we need it, we'll set it below.
501 WritingMode wm
= GetWritingMode();
502 mPadding
.SizeTo(wm
, 0, 0, 0, 0);
503 LogicalSize
finalSize(wm
);
505 const nsStyleList
* myList
= StyleList();
507 nsRefPtr
<nsFontMetrics
> fm
;
508 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
),
511 RemoveStateBits(BULLET_FRAME_IMAGE_LOADING
);
513 if (myList
->GetListStyleImage() && mImageRequest
) {
515 mImageRequest
->GetImageStatus(&status
);
516 if (status
& imgIRequest::STATUS_SIZE_AVAILABLE
&&
517 !(status
& imgIRequest::STATUS_ERROR
)) {
518 // auto size the image
519 finalSize
.ISize(wm
) = mIntrinsicSize
.ISize(wm
);
520 aMetrics
.SetBlockStartAscent(finalSize
.BSize(wm
) =
521 mIntrinsicSize
.BSize(wm
));
522 aMetrics
.SetSize(wm
, finalSize
);
524 AppendSpacingToPadding(fm
);
526 AddStateBits(BULLET_FRAME_IMAGE_LOADING
);
532 // If we're getting our desired size and don't have an image, reset
533 // mIntrinsicSize to (0,0). Otherwise, if we used to have an image, it
534 // changed, and the new one is coming in, but we're reflowing before it's
535 // fully there, we'll end up with mIntrinsicSize not matching our size, but
536 // won't trigger a reflow in OnStartContainer (because mIntrinsicSize will
537 // match the image size).
538 mIntrinsicSize
.SizeTo(wm
, 0, 0);
543 switch (myList
->GetCounterStyle()->GetStyle()) {
544 case NS_STYLE_LIST_STYLE_NONE
:
545 finalSize
.ISize(wm
) = finalSize
.BSize(wm
) = 0;
546 aMetrics
.SetBlockStartAscent(0);
549 case NS_STYLE_LIST_STYLE_DISC
:
550 case NS_STYLE_LIST_STYLE_CIRCLE
:
551 case NS_STYLE_LIST_STYLE_SQUARE
: {
552 ascent
= fm
->MaxAscent();
553 bulletSize
= std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE
),
554 NSToCoordRound(0.8f
* (float(ascent
) / 2.0f
)));
555 mPadding
.BEnd(wm
) = NSToCoordRound(float(ascent
) / 8.0f
);
556 finalSize
.ISize(wm
) = finalSize
.BSize(wm
) = bulletSize
;
557 aMetrics
.SetBlockStartAscent(bulletSize
+ mPadding
.BEnd(wm
));
558 AppendSpacingToPadding(fm
);
562 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
563 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
564 ascent
= fm
->EmAscent();
565 bulletSize
= std::max(
566 nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE
),
567 NSToCoordRound(0.75f
* ascent
));
568 mPadding
.BEnd(wm
) = NSToCoordRound(0.125f
* ascent
);
569 finalSize
.ISize(wm
) = finalSize
.BSize(wm
) = bulletSize
;
570 if (!wm
.IsVertical()) {
571 aMetrics
.SetBlockStartAscent(bulletSize
+ mPadding
.BEnd(wm
));
573 AppendSpacingToPadding(fm
);
577 GetListItemText(text
);
578 finalSize
.BSize(wm
) = fm
->MaxHeight();
579 aRenderingContext
->SetFont(fm
);
580 finalSize
.ISize(wm
) =
581 nsLayoutUtils::GetStringWidth(this, aRenderingContext
,
582 text
.get(), text
.Length());
583 aMetrics
.SetBlockStartAscent(fm
->MaxAscent());
586 aMetrics
.SetSize(wm
, finalSize
);
590 nsBulletFrame::Reflow(nsPresContext
* aPresContext
,
591 nsHTMLReflowMetrics
& aMetrics
,
592 const nsHTMLReflowState
& aReflowState
,
593 nsReflowStatus
& aStatus
)
595 DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
596 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aMetrics
, aStatus
);
598 float inflation
= nsLayoutUtils::FontSizeInflationFor(this);
599 SetFontSizeInflation(inflation
);
602 GetDesiredSize(aPresContext
, aReflowState
.rendContext
, aMetrics
, inflation
);
604 // Add in the border and padding; split the top/bottom between the
605 // ascent and descent to make things look nice
606 WritingMode wm
= aReflowState
.GetWritingMode();
607 const LogicalMargin
& bp
= aReflowState
.ComputedLogicalBorderPadding();
608 mPadding
.BStart(wm
) += NSToCoordRound(bp
.BStart(wm
) * inflation
);
609 mPadding
.IEnd(wm
) += NSToCoordRound(bp
.IEnd(wm
) * inflation
);
610 mPadding
.BEnd(wm
) += NSToCoordRound(bp
.BEnd(wm
) * inflation
);
611 mPadding
.IStart(wm
) += NSToCoordRound(bp
.IStart(wm
) * inflation
);
613 WritingMode lineWM
= aMetrics
.GetWritingMode();
614 LogicalMargin linePadding
= mPadding
.ConvertTo(lineWM
, wm
);
615 aMetrics
.ISize(lineWM
) += linePadding
.IStartEnd(lineWM
);
616 aMetrics
.BSize(lineWM
) += linePadding
.BStartEnd(lineWM
);
617 aMetrics
.SetBlockStartAscent(aMetrics
.BlockStartAscent() +
618 linePadding
.BStart(lineWM
));
620 // XXX this is a bit of a hack, we're assuming that no glyphs used for bullets
621 // overflow their font-boxes. It'll do for now; to fix it for real, we really
622 // should rewrite all the text-handling code here to use gfxTextRun (bug
624 aMetrics
.SetOverflowAreasToDesiredBounds();
626 aStatus
= NS_FRAME_COMPLETE
;
627 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aMetrics
);
630 /* virtual */ nscoord
631 nsBulletFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
633 WritingMode wm
= GetWritingMode();
634 nsHTMLReflowMetrics
metrics(wm
);
635 DISPLAY_MIN_WIDTH(this, metrics
.ISize(wm
));
636 GetDesiredSize(PresContext(), aRenderingContext
, metrics
, 1.0f
);
637 return metrics
.ISize(wm
);
640 /* virtual */ nscoord
641 nsBulletFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
643 WritingMode wm
= GetWritingMode();
644 nsHTMLReflowMetrics
metrics(wm
);
645 DISPLAY_PREF_WIDTH(this, metrics
.ISize(wm
));
646 GetDesiredSize(PresContext(), aRenderingContext
, metrics
, 1.0f
);
647 return metrics
.ISize(wm
);
651 nsBulletFrame::Notify(imgIRequest
*aRequest
, int32_t aType
, const nsIntRect
* aData
)
653 if (aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
654 nsCOMPtr
<imgIContainer
> image
;
655 aRequest
->GetImage(getter_AddRefs(image
));
656 return OnStartContainer(aRequest
, image
);
659 if (aType
== imgINotificationObserver::FRAME_UPDATE
) {
660 // The image has changed.
661 // Invalidate the entire content area. Maybe it's not optimal but it's simple and
662 // always correct, and I'll be a stunned mullet if it ever matters for performance
666 if (aType
== imgINotificationObserver::IS_ANIMATED
) {
667 // Register the image request with the refresh driver now that we know it's
669 if (aRequest
== mImageRequest
) {
670 nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest
,
671 &mRequestRegistered
);
678 nsresult
nsBulletFrame::OnStartContainer(imgIRequest
*aRequest
,
679 imgIContainer
*aImage
)
681 if (!aImage
) return NS_ERROR_INVALID_ARG
;
682 if (!aRequest
) return NS_ERROR_INVALID_ARG
;
685 aRequest
->GetImageStatus(&status
);
686 if (status
& imgIRequest::STATUS_ERROR
) {
691 aImage
->GetWidth(&w
);
692 aImage
->GetHeight(&h
);
694 nsPresContext
* presContext
= PresContext();
696 LogicalSize
newsize(GetWritingMode(),
697 nsSize(nsPresContext::CSSPixelsToAppUnits(w
),
698 nsPresContext::CSSPixelsToAppUnits(h
)));
700 if (mIntrinsicSize
!= newsize
) {
701 mIntrinsicSize
= newsize
;
703 // Now that the size is available (or an error occurred), trigger
704 // a reflow of the bullet frame.
705 nsIPresShell
*shell
= presContext
->GetPresShell();
707 shell
->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
713 aImage
->SetAnimationMode(presContext
->ImageAnimationMode());
714 // Ensure the animation (if any) is started. Note: There is no
715 // corresponding call to Decrement for this. This Increment will be
716 // 'cleaned up' by the Request when it is destroyed, but only then.
717 aRequest
->IncrementAnimationConsumers();
723 nsBulletFrame::GetLoadGroup(nsPresContext
*aPresContext
, nsILoadGroup
**aLoadGroup
)
728 NS_PRECONDITION(nullptr != aLoadGroup
, "null OUT parameter pointer");
730 nsIPresShell
*shell
= aPresContext
->GetPresShell();
735 nsIDocument
*doc
= shell
->GetDocument();
739 *aLoadGroup
= doc
->GetDocumentLoadGroup().take();
742 union VoidPtrOrFloat
{
743 VoidPtrOrFloat() : p(nullptr) {}
750 nsBulletFrame::GetFontSizeInflation() const
752 if (!HasFontSizeInflation()) {
756 u
.p
= Properties().Get(FontSizeInflationProperty());
761 nsBulletFrame::SetFontSizeInflation(float aInflation
)
763 if (aInflation
== 1.0f
) {
764 if (HasFontSizeInflation()) {
765 RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION
);
766 Properties().Delete(FontSizeInflationProperty());
771 AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION
);
774 Properties().Set(FontSizeInflationProperty(), u
.p
);
777 already_AddRefed
<imgIContainer
>
778 nsBulletFrame::GetImage() const
780 if (mImageRequest
&& StyleList()->GetListStyleImage()) {
781 nsCOMPtr
<imgIContainer
> imageCon
;
782 mImageRequest
->GetImage(getter_AddRefs(imageCon
));
783 return imageCon
.forget();
790 nsBulletFrame::GetLogicalBaseline(WritingMode aWritingMode
) const
792 nscoord ascent
= 0, baselinePadding
;
793 if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING
) {
794 ascent
= BSize(aWritingMode
);
796 nsRefPtr
<nsFontMetrics
> fm
;
797 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
),
798 GetFontSizeInflation());
799 CounterStyle
* listStyleType
= StyleList()->GetCounterStyle();
800 switch (listStyleType
->GetStyle()) {
801 case NS_STYLE_LIST_STYLE_NONE
:
804 case NS_STYLE_LIST_STYLE_DISC
:
805 case NS_STYLE_LIST_STYLE_CIRCLE
:
806 case NS_STYLE_LIST_STYLE_SQUARE
:
807 ascent
= fm
->MaxAscent();
808 baselinePadding
= NSToCoordRound(float(ascent
) / 8.0f
);
809 ascent
= std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE
),
810 NSToCoordRound(0.8f
* (float(ascent
) / 2.0f
)));
811 ascent
+= baselinePadding
;
814 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
815 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
816 ascent
= fm
->EmAscent();
817 baselinePadding
= NSToCoordRound(0.125f
* ascent
);
819 nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE
),
820 NSToCoordRound(0.75f
* ascent
));
821 ascent
+= baselinePadding
;
825 ascent
= fm
->MaxAscent();
830 GetLogicalUsedMargin(aWritingMode
).BStart(aWritingMode
);
834 nsBulletFrame::GetSpokenText(nsAString
& aText
)
836 CounterStyle
* style
= StyleList()->GetCounterStyle();
838 style
->GetSpokenCounterText(mOrdinal
, GetWritingMode(), aText
, isBullet
);
840 if (!style
->IsNone()) {
844 nsAutoString prefix
, suffix
;
845 style
->GetPrefix(prefix
);
846 style
->GetSuffix(suffix
);
847 aText
= prefix
+ aText
+ suffix
;
858 NS_IMPL_ISUPPORTS(nsBulletListener
, imgINotificationObserver
)
860 nsBulletListener::nsBulletListener() :
865 nsBulletListener::~nsBulletListener()
870 nsBulletListener::Notify(imgIRequest
*aRequest
, int32_t aType
, const nsIntRect
* aData
)
873 return NS_ERROR_FAILURE
;
874 return mFrame
->Notify(aRequest
, aType
, aData
);