Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsBulletFrame.cpp
blobb0c85d68beb4ee4842648adac8fbf22db0c03c6f
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"
11 #include "nsCOMPtr.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"
26 #include "nsIURI.h"
28 #include <algorithm>
30 #ifdef ACCESSIBILITY
31 #include "nsAccessibilityService.h"
32 #endif
34 using namespace mozilla;
36 NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty, nullptr)
38 NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame)
40 #ifdef DEBUG
41 NS_QUERYFRAME_HEAD(nsBulletFrame)
42 NS_QUERYFRAME_ENTRY(nsBulletFrame)
43 NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
44 #endif
46 nsBulletFrame::~nsBulletFrame()
50 void
51 nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot)
53 // Stop image loading first
54 if (mImageRequest) {
55 // Deregister our image request from the refresh driver
56 nsLayoutUtils::DeregisterImageRequest(PresContext(),
57 mImageRequest,
58 &mRequestRegistered);
59 mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
60 mImageRequest = nullptr;
63 if (mListener) {
64 mListener->SetFrame(nullptr);
67 // Let base class do the rest
68 nsFrame::DestroyFrom(aDestructRoot);
71 #ifdef DEBUG_FRAME_DUMP
72 nsresult
73 nsBulletFrame::GetFrameName(nsAString& aResult) const
75 return MakeFrameName(NS_LITERAL_STRING("Bullet"), aResult);
77 #endif
79 nsIAtom*
80 nsBulletFrame::GetType() const
82 return nsGkAtoms::bulletFrame;
85 bool
86 nsBulletFrame::IsEmpty()
88 return IsSelfEmpty();
91 bool
92 nsBulletFrame::IsSelfEmpty()
94 return StyleList()->GetCounterStyle()->IsNone();
97 /* virtual */ void
98 nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
100 nsFrame::DidSetStyleContext(aOldStyleContext);
102 imgRequestProxy *newRequest = StyleList()->GetListStyleImage();
104 if (newRequest) {
106 if (!mListener) {
107 mListener = new nsBulletListener();
108 mListener->SetFrame(this);
111 bool needNewRequest = true;
113 if (mImageRequest) {
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) {
120 bool same;
121 newURI->Equals(oldURI, &same);
122 if (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.
135 if (oldRequest) {
136 nsLayoutUtils::DeregisterImageRequest(PresContext(), oldRequest,
137 &mRequestRegistered);
138 oldRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
139 oldRequest = nullptr;
142 // Register the new request.
143 if (mImageRequest) {
144 nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(),
145 mImageRequest,
146 &mRequestRegistered);
149 } else {
150 // No image request on the new style context
151 if (mImageRequest) {
152 nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
153 &mRequestRegistered);
155 mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
156 mImageRequest = nullptr;
160 #ifdef ACCESSIBILITY
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();
165 if (accService) {
166 const nsStyleList* oldStyleList = aOldStyleContext->PeekStyleList();
167 if (oldStyleList) {
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,
177 hasBullet);
182 #endif
185 class nsDisplayBulletGeometry : public nsDisplayItemGenericGeometry
187 public:
188 nsDisplayBulletGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
189 : nsDisplayItemGenericGeometry(aItem, aBuilder)
191 nsBulletFrame* f = static_cast<nsBulletFrame*>(aItem->Frame());
192 mOrdinal = f->GetOrdinal();
195 int32_t mOrdinal;
198 class nsDisplayBullet MOZ_FINAL : public nsDisplayItem {
199 public:
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);
208 #endif
210 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
211 bool* aSnap) MOZ_OVERRIDE
213 *aSnap = false;
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
227 bool snap;
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) {
244 bool snap;
245 aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap));
246 return;
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.
254 bool snap;
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);
273 void
274 nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
275 const nsRect& aDirtyRect,
276 const nsDisplayListSet& aLists)
278 if (!IsVisibleForPainting(aBuilder))
279 return;
281 DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame");
283 aLists.Content()->AppendNewToTop(
284 new (aBuilder) nsDisplayBullet(aBuilder, this));
287 void
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) {
296 uint32_t status;
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));
302 if (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);
309 return;
314 nsRefPtr<nsFontMetrics> fm;
315 aRenderingContext.SetColor(nsLayoutUtils::GetColor(this, eCSSProperty_color));
317 nsAutoString text;
318 switch (listStyleType->GetStyle()) {
319 case NS_STYLE_LIST_STYLE_NONE:
320 break;
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));
326 break;
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));
332 break;
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);
354 break;
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();
364 bool isClosed =
365 listStyleType->GetStyle() == NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED;
366 bool isDown = (!isVertical && !isClosed) || (isVertical && isClosed);
367 nscoord diff = NSToCoordRound(0.1f * rect.height);
368 if (isDown) {
369 rect.y += diff * 2;
370 rect.height -= diff * 2;
371 } else {
372 rect.Deflate(diff, 0);
374 nsPresContext *pc = PresContext();
375 rect.x = pc->RoundAppUnitsToNearestDevPixels(rect.x);
376 rect.y = pc->RoundAppUnitsToNearestDevPixels(rect.y);
378 nsPoint points[3];
379 if (isDown) {
380 // to bottom
381 points[0] = rect.TopLeft();
382 points[1] = rect.TopRight();
383 points[2] = (rect.BottomLeft() + rect.BottomRight()) / 2;
384 } else {
385 bool isLR = isVertical ? wm.IsVerticalLR() : wm.IsBidiLTR();
386 if (isLR) {
387 // to right
388 points[0] = rect.TopLeft();
389 points[1] = (rect.TopRight() + rect.BottomRight()) / 2;
390 points[2] = rect.BottomLeft();
391 } else {
392 // to left
393 points[0] = rect.TopRight();
394 points[1] = rect.BottomRight();
395 points[2] = (rect.TopLeft() + rect.BottomLeft()) / 2;
398 aRenderingContext.FillPolygon(points, 3);
400 break;
402 default:
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);
417 break;
421 int32_t
422 nsBulletFrame::SetListItemOrdinal(int32_t aNextOrdinal,
423 bool* aChanged,
424 int32_t aIncrement)
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();
437 if (parentContent) {
438 nsGenericHTMLElement *hc =
439 nsGenericHTMLElement::FromContent(parentContent);
440 if (hc) {
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);
454 void
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");
466 bool isRTL;
467 nsAutoString counter, prefix, suffix;
468 style->GetPrefix(prefix);
469 style->GetSuffix(suffix);
470 style->GetCounterText(mOrdinal, GetWritingMode(), counter, isRTL);
472 aResult.Truncate();
473 aResult.Append(prefix);
474 if (GetWritingMode().IsBidiLTR() != isRTL) {
475 aResult.Append(counter);
476 } else {
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
488 void
489 nsBulletFrame::AppendSpacingToPadding(nsFontMetrics* aFontMetrics)
491 mPadding.IEnd(GetWritingMode()) += aFontMetrics->EmHeight() / 2;
494 void
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();
506 nscoord ascent;
507 nsRefPtr<nsFontMetrics> fm;
508 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
509 aFontSizeInflation);
511 RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
513 if (myList->GetListStyleImage() && mImageRequest) {
514 uint32_t status;
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);
528 return;
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);
540 nscoord bulletSize;
542 nsAutoString text;
543 switch (myList->GetCounterStyle()->GetStyle()) {
544 case NS_STYLE_LIST_STYLE_NONE:
545 finalSize.ISize(wm) = finalSize.BSize(wm) = 0;
546 aMetrics.SetBlockStartAscent(0);
547 break;
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);
559 break;
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);
574 break;
576 default:
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());
584 break;
586 aMetrics.SetSize(wm, finalSize);
589 void
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);
601 // Get the base size
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
623 // 397294).
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);
650 NS_IMETHODIMP
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
663 InvalidateFrame();
666 if (aType == imgINotificationObserver::IS_ANIMATED) {
667 // Register the image request with the refresh driver now that we know it's
668 // animated.
669 if (aRequest == mImageRequest) {
670 nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
671 &mRequestRegistered);
675 return NS_OK;
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;
684 uint32_t status;
685 aRequest->GetImageStatus(&status);
686 if (status & imgIRequest::STATUS_ERROR) {
687 return NS_OK;
690 nscoord w, h;
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();
706 if (shell) {
707 shell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
708 NS_FRAME_IS_DIRTY);
712 // Handle animations
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();
719 return NS_OK;
722 void
723 nsBulletFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
725 if (!aPresContext)
726 return;
728 NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
730 nsIPresShell *shell = aPresContext->GetPresShell();
732 if (!shell)
733 return;
735 nsIDocument *doc = shell->GetDocument();
736 if (!doc)
737 return;
739 *aLoadGroup = doc->GetDocumentLoadGroup().take();
742 union VoidPtrOrFloat {
743 VoidPtrOrFloat() : p(nullptr) {}
745 void *p;
746 float f;
749 float
750 nsBulletFrame::GetFontSizeInflation() const
752 if (!HasFontSizeInflation()) {
753 return 1.0f;
755 VoidPtrOrFloat u;
756 u.p = Properties().Get(FontSizeInflationProperty());
757 return u.f;
760 void
761 nsBulletFrame::SetFontSizeInflation(float aInflation)
763 if (aInflation == 1.0f) {
764 if (HasFontSizeInflation()) {
765 RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
766 Properties().Delete(FontSizeInflationProperty());
768 return;
771 AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
772 VoidPtrOrFloat u;
773 u.f = aInflation;
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();
786 return nullptr;
789 nscoord
790 nsBulletFrame::GetLogicalBaseline(WritingMode aWritingMode) const
792 nscoord ascent = 0, baselinePadding;
793 if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
794 ascent = BSize(aWritingMode);
795 } else {
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:
802 break;
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;
812 break;
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);
818 ascent = std::max(
819 nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
820 NSToCoordRound(0.75f * ascent));
821 ascent += baselinePadding;
822 break;
824 default:
825 ascent = fm->MaxAscent();
826 break;
829 return ascent +
830 GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
833 void
834 nsBulletFrame::GetSpokenText(nsAString& aText)
836 CounterStyle* style = StyleList()->GetCounterStyle();
837 bool isBullet;
838 style->GetSpokenCounterText(mOrdinal, GetWritingMode(), aText, isBullet);
839 if (isBullet) {
840 if (!style->IsNone()) {
841 aText.Append(' ');
843 } else {
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() :
861 mFrame(nullptr)
865 nsBulletListener::~nsBulletListener()
869 NS_IMETHODIMP
870 nsBulletListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
872 if (!mFrame)
873 return NS_ERROR_FAILURE;
874 return mFrame->Notify(aRequest, aType, aData);