1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* rendering object for list-item bullets */
8 
9 #include "nsBulletFrame.h"
10 
11 #include <algorithm>
12 #include <utility>
13 
14 #include "CounterStyleManager.h"
15 #include "ImageLayers.h"
16 #include "SVGImageContext.h"
17 #include "TextDrawTarget.h"
18 #include "UnitTransforms.h"
19 #include "gfx2DGlue.h"
20 #include "gfxContext.h"
21 #include "gfxUtils.h"
22 #include "imgIContainer.h"
23 #include "imgRequestProxy.h"
24 #include "mozilla/MathAlgorithms.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/dom/Document.h"
27 #include "mozilla/gfx/2D.h"
28 #include "mozilla/gfx/PathHelpers.h"
29 #include "mozilla/layers/LayersMessages.h"
30 #include "mozilla/layers/RenderRootStateManager.h"
31 #include "mozilla/layers/StackingContextHelper.h"
32 #include "mozilla/layers/WebRenderBridgeChild.h"
33 #include "mozilla/layers/WebRenderMessages.h"
34 #include "nsAttrValueInlines.h"
35 #include "nsBidiUtils.h"
36 #include "nsCOMPtr.h"
37 #include "nsCSSFrameConstructor.h"
38 #include "nsCounterManager.h"
39 #include "nsDisplayList.h"
40 #include "nsFontMetrics.h"
41 #include "nsGenericHTMLElement.h"
42 #include "nsGkAtoms.h"
43 #include "nsIURI.h"
44 #include "nsPresContext.h"
45 
46 #ifdef ACCESSIBILITY
47 #  include "nsAccessibilityService.h"
48 #endif
49 
50 using namespace mozilla;
51 using namespace mozilla::gfx;
52 using namespace mozilla::image;
53 using namespace mozilla::layout;
54 using mozilla::dom::Document;
55 
NS_NewBulletFrame(PresShell * aPresShell,ComputedStyle * aStyle)56 nsIFrame* NS_NewBulletFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
57   return new (aPresShell) nsBulletFrame(aStyle, aPresShell->GetPresContext());
58 }
59 
60 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FontSizeInflationProperty, float)
61 
62 NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame)
63 
64 #ifdef DEBUG
65 NS_QUERYFRAME_HEAD(nsBulletFrame)
66   NS_QUERYFRAME_ENTRY(nsBulletFrame)
67 NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
68 #endif
69 
70 nsBulletFrame::~nsBulletFrame() = default;
71 
ResolveCounterStyle()72 CounterStyle* nsBulletFrame::ResolveCounterStyle() {
73   return PresContext()->CounterStyleManager()->ResolveCounterStyle(
74       StyleList()->mCounterStyle);
75 }
76 
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)77 void nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot,
78                                 PostDestroyData& aPostDestroyData) {
79   // Stop image loading first.
80   DeregisterAndCancelImageRequest();
81 
82   if (mListener) {
83     mListener->SetFrame(nullptr);
84   }
85 
86   // Let base class do the rest
87   nsFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
88 }
89 
90 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const91 nsresult nsBulletFrame::GetFrameName(nsAString& aResult) const {
92   return MakeFrameName(NS_LITERAL_STRING("Bullet"), aResult);
93 }
94 #endif
95 
IsEmpty()96 bool nsBulletFrame::IsEmpty() { return IsSelfEmpty(); }
97 
IsSelfEmpty()98 bool nsBulletFrame::IsSelfEmpty() {
99   return StyleList()->mCounterStyle.IsNone();
100 }
101 
102 /* virtual */
DidSetComputedStyle(ComputedStyle * aOldComputedStyle)103 void nsBulletFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
104   nsFrame::DidSetComputedStyle(aOldComputedStyle);
105 
106   imgRequestProxy* newRequest = StyleList()->GetListStyleImage();
107 
108   if (newRequest) {
109     if (!mListener) {
110       mListener = new nsBulletListener();
111       mListener->SetFrame(this);
112     }
113 
114     bool needNewRequest = true;
115 
116     if (mImageRequest) {
117       // Reload the image, maybe...
118       nsCOMPtr<nsIURI> oldURI;
119       mImageRequest->GetURI(getter_AddRefs(oldURI));
120       nsCOMPtr<nsIURI> newURI;
121       newRequest->GetURI(getter_AddRefs(newURI));
122       if (oldURI && newURI) {
123         bool same;
124         newURI->Equals(oldURI, &same);
125         if (same) {
126           needNewRequest = false;
127         }
128       }
129     }
130 
131     if (needNewRequest) {
132       RefPtr<imgRequestProxy> newRequestClone;
133       newRequest->SyncClone(mListener, PresContext()->Document(),
134                             getter_AddRefs(newRequestClone));
135 
136       // Deregister the old request. We wait until after Clone is done in case
137       // the old request and the new request are the same underlying image
138       // accessed via different URLs.
139       DeregisterAndCancelImageRequest();
140 
141       // Register the new request.
142       mImageRequest = std::move(newRequestClone);
143       RegisterImageRequest(/* aKnownToBeAnimated = */ false);
144 
145       // Image bullets can affect the layout of the page, so boost the image
146       // load priority.
147       mImageRequest->BoostPriority(imgIRequest::CATEGORY_SIZE_QUERY);
148     }
149   } else {
150     // No image request on the new ComputedStyle.
151     DeregisterAndCancelImageRequest();
152   }
153 
154 #ifdef ACCESSIBILITY
155   // Update the list bullet accessible. If old style list isn't available then
156   // no need to update the accessible tree because it's not created yet.
157   if (aOldComputedStyle) {
158     if (nsAccessibilityService* accService =
159             PresShell::GetAccessibilityService()) {
160       const nsStyleList* oldStyleList = aOldComputedStyle->StyleList();
161       bool hadBullet = oldStyleList->GetListStyleImage() ||
162                        !oldStyleList->mCounterStyle.IsNone();
163 
164       const nsStyleList* newStyleList = StyleList();
165       bool hasBullet = newStyleList->GetListStyleImage() ||
166                        !newStyleList->mCounterStyle.IsNone();
167 
168       if (hadBullet != hasBullet) {
169         nsIContent* listItem = mContent->GetParent();
170         accService->UpdateListBullet(PresContext()->GetPresShell(), listItem,
171                                      hasBullet);
172       }
173     }
174   }
175 #endif  // #ifdef ACCESSIBILITY
176 }
177 
178 class nsDisplayBulletGeometry
179     : public nsDisplayItemGenericGeometry,
180       public nsImageGeometryMixin<nsDisplayBulletGeometry> {
181  public:
nsDisplayBulletGeometry(nsDisplayItem * aItem,nsDisplayListBuilder * aBuilder)182   nsDisplayBulletGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
183       : nsDisplayItemGenericGeometry(aItem, aBuilder),
184         nsImageGeometryMixin(aItem, aBuilder) {
185     nsBulletFrame* f = static_cast<nsBulletFrame*>(aItem->Frame());
186     mOrdinal = f->Ordinal();
187   }
188 
InvalidateForSyncDecodeImages() const189   virtual bool InvalidateForSyncDecodeImages() const override {
190     return ShouldInvalidateToSyncDecodeImages();
191   }
192 
193   int32_t mOrdinal;
194 };
195 
196 class BulletRenderer final {
197  public:
BulletRenderer(imgIContainer * image,const nsRect & dest)198   BulletRenderer(imgIContainer* image, const nsRect& dest)
199       : mImage(image),
200         mDest(dest),
201         mColor(NS_RGBA(0, 0, 0, 0)),
202         mListStyleType(NS_STYLE_LIST_STYLE_NONE) {
203     MOZ_ASSERT(IsImageType());
204   }
205 
BulletRenderer(Path * path,nscolor color,int32_t listStyleType)206   BulletRenderer(Path* path, nscolor color, int32_t listStyleType)
207       : mColor(color), mPath(path), mListStyleType(listStyleType) {
208     MOZ_ASSERT(IsPathType());
209   }
210 
BulletRenderer(const LayoutDeviceRect & aPathRect,nscolor color,int32_t listStyleType)211   BulletRenderer(const LayoutDeviceRect& aPathRect, nscolor color,
212                  int32_t listStyleType)
213       : mPathRect(aPathRect), mColor(color), mListStyleType(listStyleType) {
214     MOZ_ASSERT(IsPathType());
215   }
216 
BulletRenderer(const nsString & text,nsFontMetrics * fm,nscolor color,const nsPoint & point,int32_t listStyleType)217   BulletRenderer(const nsString& text, nsFontMetrics* fm, nscolor color,
218                  const nsPoint& point, int32_t listStyleType)
219       : mColor(color),
220         mText(text),
221         mFontMetrics(fm),
222         mPoint(point),
223         mListStyleType(listStyleType) {
224     MOZ_ASSERT(IsTextType());
225   }
226 
227   ImgDrawResult CreateWebRenderCommands(
228       nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
229       wr::IpcResourceUpdateQueue& aResources,
230       const layers::StackingContextHelper& aSc,
231       mozilla::layers::RenderRootStateManager* aManager,
232       nsDisplayListBuilder* aDisplayListBuilder);
233 
234   ImgDrawResult Paint(gfxContext& aRenderingContext, nsPoint aPt,
235                       const nsRect& aDirtyRect, uint32_t aFlags,
236                       bool aDisableSubpixelAA, nsIFrame* aFrame);
237 
IsImageType() const238   bool IsImageType() const {
239     return mListStyleType == NS_STYLE_LIST_STYLE_NONE && mImage;
240   }
241 
IsPathType() const242   bool IsPathType() const {
243     return mListStyleType == NS_STYLE_LIST_STYLE_DISC ||
244            mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE ||
245            mListStyleType == NS_STYLE_LIST_STYLE_SQUARE ||
246            mListStyleType == NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN ||
247            mListStyleType == NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED;
248   }
249 
IsTextType() const250   bool IsTextType() const {
251     return mListStyleType != NS_STYLE_LIST_STYLE_NONE &&
252            mListStyleType != NS_STYLE_LIST_STYLE_DISC &&
253            mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
254            mListStyleType != NS_STYLE_LIST_STYLE_SQUARE &&
255            mListStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN &&
256            mListStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
257            !mText.IsEmpty();
258   }
259 
260   void PaintTextToContext(nsIFrame* aFrame, gfxContext* aCtx,
261                           bool aDisableSubpixelAA);
262 
263   bool IsImageContainerAvailable(layers::LayerManager* aManager,
264                                  uint32_t aFlags);
265 
266  private:
267   ImgDrawResult CreateWebRenderCommandsForImage(
268       nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
269       wr::IpcResourceUpdateQueue& aResources,
270       const layers::StackingContextHelper& aSc,
271       mozilla::layers::RenderRootStateManager* aManager,
272       nsDisplayListBuilder* aDisplayListBuilder);
273 
274   bool CreateWebRenderCommandsForPath(
275       nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
276       wr::IpcResourceUpdateQueue& aResources,
277       const layers::StackingContextHelper& aSc,
278       mozilla::layers::RenderRootStateManager* aManager,
279       nsDisplayListBuilder* aDisplayListBuilder);
280 
281   bool CreateWebRenderCommandsForText(
282       nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
283       wr::IpcResourceUpdateQueue& aResources,
284       const layers::StackingContextHelper& aSc,
285       mozilla::layers::RenderRootStateManager* aManager,
286       nsDisplayListBuilder* aDisplayListBuilder);
287 
288  private:
289   // mImage and mDest are the properties for list-style-image.
290   // mImage is the image content and mDest is the image position.
291   RefPtr<imgIContainer> mImage;
292   nsRect mDest;
293 
294   // Some bullet types are stored as a rect (in device pixels) instead of a Path
295   // to allow generating proper WebRender commands. When webrender is disabled
296   // the Path is lazily created for these items before painting.
297   // TODO: The size of this structure doesn't seem to be an issue since it has
298   // so many fields that are specific to a bullet style or another, but if it
299   // becomes one we can easily store mDest and mPathRect into the same memory
300   // location since they are never used by the same bullet types.
301   LayoutDeviceRect mPathRect;
302 
303   // mColor indicate the color of list-style. Both text and path type would use
304   // this member.
305   nscolor mColor;
306 
307   // mPath record the path of the list-style for later drawing.
308   // Included following types: square, circle, disc, disclosure open and
309   // disclosure closed.
310   RefPtr<Path> mPath;
311 
312   // mText, mFontMetrics, mPoint are for other list-style-type which can be
313   // drawed by text.
314   nsString mText;
315   RefPtr<nsFontMetrics> mFontMetrics;
316   nsPoint mPoint;
317 
318   // Store the type of list-style-type.
319   int32_t mListStyleType;
320 };
321 
CreateWebRenderCommands(nsDisplayItem * aItem,wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const layers::StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)322 ImgDrawResult BulletRenderer::CreateWebRenderCommands(
323     nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
324     wr::IpcResourceUpdateQueue& aResources,
325     const layers::StackingContextHelper& aSc,
326     mozilla::layers::RenderRootStateManager* aManager,
327     nsDisplayListBuilder* aDisplayListBuilder) {
328   if (IsImageType()) {
329     return CreateWebRenderCommandsForImage(aItem, aBuilder, aResources, aSc,
330                                            aManager, aDisplayListBuilder);
331   }
332 
333   bool success;
334   if (IsPathType()) {
335     success = CreateWebRenderCommandsForPath(aItem, aBuilder, aResources, aSc,
336                                              aManager, aDisplayListBuilder);
337   } else {
338     MOZ_ASSERT(IsTextType());
339     success = CreateWebRenderCommandsForText(aItem, aBuilder, aResources, aSc,
340                                              aManager, aDisplayListBuilder);
341   }
342 
343   return success ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_SUPPORTED;
344 }
345 
Paint(gfxContext & aRenderingContext,nsPoint aPt,const nsRect & aDirtyRect,uint32_t aFlags,bool aDisableSubpixelAA,nsIFrame * aFrame)346 ImgDrawResult BulletRenderer::Paint(gfxContext& aRenderingContext, nsPoint aPt,
347                                     const nsRect& aDirtyRect, uint32_t aFlags,
348                                     bool aDisableSubpixelAA, nsIFrame* aFrame) {
349   if (IsImageType()) {
350     SamplingFilter filter = nsLayoutUtils::GetSamplingFilterForFrame(aFrame);
351     return nsLayoutUtils::DrawSingleImage(
352         aRenderingContext, aFrame->PresContext(), mImage, filter, mDest,
353         aDirtyRect,
354         /* no SVGImageContext */ Nothing(), aFlags);
355   }
356 
357   if (IsPathType()) {
358     DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
359 
360     if (!mPath) {
361       RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
362       switch (mListStyleType) {
363         case NS_STYLE_LIST_STYLE_CIRCLE:
364         case NS_STYLE_LIST_STYLE_DISC:
365           AppendEllipseToPath(builder, mPathRect.Center().ToUnknownPoint(),
366                               mPathRect.Size().ToUnknownSize());
367           break;
368         case NS_STYLE_LIST_STYLE_SQUARE:
369           AppendRectToPath(builder, mPathRect.ToUnknownRect());
370           break;
371         default:
372           MOZ_ASSERT(false, "Should have a parth.");
373       }
374       mPath = builder->Finish();
375     }
376 
377     switch (mListStyleType) {
378       case NS_STYLE_LIST_STYLE_CIRCLE:
379         drawTarget->Stroke(mPath, ColorPattern(ToDeviceColor(mColor)));
380         break;
381       case NS_STYLE_LIST_STYLE_DISC:
382       case NS_STYLE_LIST_STYLE_SQUARE:
383       case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
384       case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
385         drawTarget->Fill(mPath, ColorPattern(ToDeviceColor(mColor)));
386         break;
387       default:
388         MOZ_CRASH("unreachable");
389     }
390   }
391 
392   if (IsTextType()) {
393     PaintTextToContext(aFrame, &aRenderingContext, aDisableSubpixelAA);
394   }
395 
396   return ImgDrawResult::SUCCESS;
397 }
398 
PaintTextToContext(nsIFrame * aFrame,gfxContext * aCtx,bool aDisableSubpixelAA)399 void BulletRenderer::PaintTextToContext(nsIFrame* aFrame, gfxContext* aCtx,
400                                         bool aDisableSubpixelAA) {
401   MOZ_ASSERT(IsTextType());
402 
403   DrawTarget* drawTarget = aCtx->GetDrawTarget();
404   DrawTargetAutoDisableSubpixelAntialiasing disable(drawTarget,
405                                                     aDisableSubpixelAA);
406 
407   aCtx->SetColor(sRGBColor::FromABGR(mColor));
408 
409   nsPresContext* presContext = aFrame->PresContext();
410   if (!presContext->BidiEnabled() && HasRTLChars(mText)) {
411     presContext->SetBidiEnabled();
412   }
413   nsLayoutUtils::DrawString(aFrame, *mFontMetrics, aCtx, mText.get(),
414                             mText.Length(), mPoint);
415 }
416 
IsImageContainerAvailable(layers::LayerManager * aManager,uint32_t aFlags)417 bool BulletRenderer::IsImageContainerAvailable(layers::LayerManager* aManager,
418                                                uint32_t aFlags) {
419   MOZ_ASSERT(IsImageType());
420 
421   return mImage->IsImageContainerAvailable(aManager, aFlags);
422 }
423 
CreateWebRenderCommandsForImage(nsDisplayItem * aItem,wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const layers::StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)424 ImgDrawResult BulletRenderer::CreateWebRenderCommandsForImage(
425     nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
426     wr::IpcResourceUpdateQueue& aResources,
427     const layers::StackingContextHelper& aSc,
428     mozilla::layers::RenderRootStateManager* aManager,
429     nsDisplayListBuilder* aDisplayListBuilder) {
430   MOZ_RELEASE_ASSERT(IsImageType());
431   MOZ_RELEASE_ASSERT(mImage);
432 
433   uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
434   if (aDisplayListBuilder->IsPaintingToWindow()) {
435     flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
436   }
437   if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
438     flags |= imgIContainer::FLAG_SYNC_DECODE;
439   }
440 
441   const int32_t appUnitsPerDevPixel =
442       aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
443   LayoutDeviceRect destRect =
444       LayoutDeviceRect::FromAppUnits(mDest, appUnitsPerDevPixel);
445 
446   Maybe<SVGImageContext> svgContext;
447   gfx::IntSize decodeSize =
448       nsLayoutUtils::ComputeImageContainerDrawingParameters(
449           mImage, aItem->Frame(), destRect, aSc, flags, svgContext);
450 
451   RefPtr<layers::ImageContainer> container;
452   ImgDrawResult drawResult = mImage->GetImageContainerAtSize(
453       aManager->LayerManager(), decodeSize, svgContext, flags,
454       getter_AddRefs(container));
455   if (!container) {
456     return drawResult;
457   }
458 
459   mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
460       nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame()));
461   gfx::IntSize size;
462   Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
463       aItem, container, aBuilder, aResources, rendering, aSc, size, Nothing());
464   if (key.isNothing()) {
465     return drawResult;
466   }
467 
468   wr::LayoutRect dest = wr::ToLayoutRect(destRect);
469 
470   aBuilder.PushImage(dest, dest, !aItem->BackfaceIsHidden(), rendering,
471                      key.value());
472 
473   return drawResult;
474 }
475 
CreateWebRenderCommandsForPath(nsDisplayItem * aItem,wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const layers::StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)476 bool BulletRenderer::CreateWebRenderCommandsForPath(
477     nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
478     wr::IpcResourceUpdateQueue& aResources,
479     const layers::StackingContextHelper& aSc,
480     mozilla::layers::RenderRootStateManager* aManager,
481     nsDisplayListBuilder* aDisplayListBuilder) {
482   MOZ_ASSERT(IsPathType());
483   wr::LayoutRect dest = wr::ToLayoutRect(mPathRect);
484   auto color = wr::ToColorF(ToDeviceColor(mColor));
485   bool isBackfaceVisible = !aItem->BackfaceIsHidden();
486   switch (mListStyleType) {
487     case NS_STYLE_LIST_STYLE_CIRCLE: {
488       LayoutDeviceSize radii = mPathRect.Size() / 2.0;
489       auto borderWidths = wr::ToBorderWidths(1.0, 1.0, 1.0, 1.0);
490       wr::BorderSide side = {color, wr::BorderStyle::Solid};
491       wr::BorderSide sides[4] = {side, side, side, side};
492       Range<const wr::BorderSide> sidesRange(sides, 4);
493       aBuilder.PushBorder(dest, dest, isBackfaceVisible, borderWidths,
494                           sidesRange,
495                           wr::ToBorderRadius(radii, radii, radii, radii));
496       return true;
497     }
498     case NS_STYLE_LIST_STYLE_DISC: {
499       aBuilder.PushRoundedRect(dest, dest, isBackfaceVisible, color);
500       return true;
501     }
502     case NS_STYLE_LIST_STYLE_SQUARE: {
503       aBuilder.PushRect(dest, dest, isBackfaceVisible, color);
504       return true;
505     }
506     default:
507       if (!aManager->CommandBuilder().PushItemAsImage(
508               aItem, aBuilder, aResources, aSc, aDisplayListBuilder)) {
509         NS_WARNING("Fail to create WebRender commands for Bullet path.");
510         return false;
511       }
512   }
513 
514   return true;
515 }
516 
CreateWebRenderCommandsForText(nsDisplayItem * aItem,wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const layers::StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)517 bool BulletRenderer::CreateWebRenderCommandsForText(
518     nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
519     wr::IpcResourceUpdateQueue& aResources,
520     const layers::StackingContextHelper& aSc,
521     mozilla::layers::RenderRootStateManager* aManager,
522     nsDisplayListBuilder* aDisplayListBuilder) {
523   MOZ_ASSERT(IsTextType());
524 
525   bool dummy;
526   nsRect bounds = aItem->GetBounds(aDisplayListBuilder, &dummy);
527 
528   if (bounds.IsEmpty()) {
529     return true;
530   }
531 
532   RefPtr<TextDrawTarget> textDrawer =
533       new TextDrawTarget(aBuilder, aResources, aSc, aManager, aItem, bounds);
534   RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
535   PaintTextToContext(aItem->Frame(), captureCtx, aItem->IsSubpixelAADisabled());
536   textDrawer->TerminateShadows();
537 
538   return textDrawer->Finish();
539 }
540 
541 class nsDisplayBullet final : public nsPaintedDisplayItem {
542  public:
nsDisplayBullet(nsDisplayListBuilder * aBuilder,nsBulletFrame * aFrame)543   nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
544       : nsPaintedDisplayItem(aBuilder, aFrame) {
545     MOZ_COUNT_CTOR(nsDisplayBullet);
546   }
MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBullet)547   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayBullet)
548 
549   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
550                            bool* aSnap) const override {
551     *aSnap = false;
552     return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
553   }
554 
555   virtual bool CreateWebRenderCommands(
556       mozilla::wr::DisplayListBuilder& aBuilder,
557       mozilla::wr::IpcResourceUpdateQueue&, const StackingContextHelper& aSc,
558       mozilla::layers::RenderRootStateManager* aManager,
559       nsDisplayListBuilder* aDisplayListBuilder) override;
560 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)561   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
562                        HitTestState* aState,
563                        nsTArray<nsIFrame*>* aOutFrames) override {
564     aOutFrames->AppendElement(mFrame);
565   }
566   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
567   NS_DISPLAY_DECL_NAME("Bullet", TYPE_BULLET)
568 
GetComponentAlphaBounds(nsDisplayListBuilder * aBuilder) const569   virtual nsRect GetComponentAlphaBounds(
570       nsDisplayListBuilder* aBuilder) const override {
571     bool snap;
572     return GetBounds(aBuilder, &snap);
573   }
574 
AllocateGeometry(nsDisplayListBuilder * aBuilder)575   virtual nsDisplayItemGeometry* AllocateGeometry(
576       nsDisplayListBuilder* aBuilder) override {
577     return new nsDisplayBulletGeometry(this, aBuilder);
578   }
579 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const580   virtual void ComputeInvalidationRegion(
581       nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
582       nsRegion* aInvalidRegion) const override {
583     const nsDisplayBulletGeometry* geometry =
584         static_cast<const nsDisplayBulletGeometry*>(aGeometry);
585     nsBulletFrame* f = static_cast<nsBulletFrame*>(mFrame);
586 
587     if (f->Ordinal() != geometry->mOrdinal) {
588       bool snap;
589       aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap));
590       return;
591     }
592 
593     nsCOMPtr<imgIContainer> image = f->GetImage();
594     if (aBuilder->ShouldSyncDecodeImages() && image &&
595         geometry->ShouldInvalidateToSyncDecodeImages()) {
596       bool snap;
597       aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
598     }
599 
600     return nsPaintedDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry,
601                                                            aInvalidRegion);
602   }
603 
604  protected:
605   Maybe<BulletRenderer> mBulletRenderer;
606 };
607 
CreateWebRenderCommands(wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)608 bool nsDisplayBullet::CreateWebRenderCommands(
609     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
610     const StackingContextHelper& aSc,
611     mozilla::layers::RenderRootStateManager* aManager,
612     nsDisplayListBuilder* aDisplayListBuilder) {
613   // FIXME: avoid needing to make this target if we're drawing text
614   // (non-trivial refactor of all this code)
615   RefPtr<gfxContext> screenRefCtx = gfxContext::CreateOrNull(
616       gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
617   Maybe<BulletRenderer> br =
618       static_cast<nsBulletFrame*>(mFrame)->CreateBulletRenderer(
619           *screenRefCtx, ToReferenceFrame());
620 
621   if (!br) {
622     return false;
623   }
624 
625   ImgDrawResult drawResult = br->CreateWebRenderCommands(
626       this, aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
627   if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
628     return false;
629   }
630 
631   nsDisplayBulletGeometry::UpdateDrawResult(this, drawResult);
632   return true;
633 }
634 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)635 void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
636   uint32_t flags = imgIContainer::FLAG_NONE;
637   if (aBuilder->ShouldSyncDecodeImages()) {
638     flags |= imgIContainer::FLAG_SYNC_DECODE;
639   }
640 
641   ImgDrawResult result = static_cast<nsBulletFrame*>(mFrame)->PaintBullet(
642       *aCtx, ToReferenceFrame(), GetPaintRect(), flags, IsSubpixelAADisabled());
643 
644   nsDisplayBulletGeometry::UpdateDrawResult(this, result);
645 }
646 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)647 void nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
648                                      const nsDisplayListSet& aLists) {
649   if (!IsVisibleForPainting()) return;
650 
651   DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame");
652 
653   aLists.Content()->AppendNewToTop<nsDisplayBullet>(aBuilder, this);
654 }
655 
CreateBulletRenderer(gfxContext & aRenderingContext,nsPoint aPt)656 Maybe<BulletRenderer> nsBulletFrame::CreateBulletRenderer(
657     gfxContext& aRenderingContext, nsPoint aPt) {
658   const nsStyleList* myList = StyleList();
659   CounterStyle* listStyleType = ResolveCounterStyle();
660   nsMargin padding = mPadding.GetPhysicalMargin(GetWritingMode());
661 
662   if (myList->GetListStyleImage() && mImageRequest) {
663     uint32_t status;
664     mImageRequest->GetImageStatus(&status);
665     if (!(status & imgIRequest::STATUS_ERROR)) {
666       if (status & imgIRequest::STATUS_LOAD_COMPLETE) {
667         nsCOMPtr<imgIContainer> imageCon;
668         mImageRequest->GetImage(getter_AddRefs(imageCon));
669         if (imageCon) {
670           nsRect dest(padding.left, padding.top,
671                       mRect.width - (padding.left + padding.right),
672                       mRect.height - (padding.top + padding.bottom));
673           BulletRenderer br(imageCon, dest + aPt);
674           return Some(br);
675         }
676       } else {
677         // Boost the load priority further now that we know we want to display
678         // the bullet image.
679         mImageRequest->BoostPriority(imgIRequest::CATEGORY_DISPLAY);
680       }
681     }
682   }
683 
684   nscolor color = nsLayoutUtils::GetColor(this, &nsStyleText::mColor);
685 
686   DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
687   int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
688 
689   switch (listStyleType->GetStyle()) {
690     case NS_STYLE_LIST_STYLE_NONE:
691       return Nothing();
692 
693     case NS_STYLE_LIST_STYLE_DISC:
694     case NS_STYLE_LIST_STYLE_CIRCLE: {
695       nsRect rect(padding.left + aPt.x, padding.top + aPt.y,
696                   mRect.width - (padding.left + padding.right),
697                   mRect.height - (padding.top + padding.bottom));
698       auto devPxRect =
699           LayoutDeviceRect::FromAppUnits(rect, appUnitsPerDevPixel);
700       return Some(BulletRenderer(devPxRect, color, listStyleType->GetStyle()));
701     }
702 
703     case NS_STYLE_LIST_STYLE_SQUARE: {
704       nsRect rect(aPt, mRect.Size());
705       rect.Deflate(padding);
706 
707       // Snap the height and the width of the rectangle to device pixels,
708       // and then center the result within the original rectangle, so that
709       // all square bullets at the same font size have the same visual
710       // size (bug 376690).
711       // FIXME: We should really only do this if we're not transformed
712       // (like gfxContext::UserToDevicePixelSnapped does).
713       nsPresContext* pc = PresContext();
714       nsRect snapRect(rect.x, rect.y,
715                       pc->RoundAppUnitsToNearestDevPixels(rect.width),
716                       pc->RoundAppUnitsToNearestDevPixels(rect.height));
717       snapRect.MoveBy((rect.width - snapRect.width) / 2,
718                       (rect.height - snapRect.height) / 2);
719       auto devPxRect =
720           LayoutDeviceRect::FromAppUnits(snapRect, appUnitsPerDevPixel);
721       return Some(BulletRenderer(devPxRect, color, listStyleType->GetStyle()));
722     }
723 
724     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
725     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: {
726       nsRect rect(aPt, mRect.Size());
727       rect.Deflate(padding);
728 
729       WritingMode wm = GetWritingMode();
730       bool isVertical = wm.IsVertical();
731       bool isClosed =
732           listStyleType->GetStyle() == NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED;
733       bool isDown = (!isVertical && !isClosed) || (isVertical && isClosed);
734       nscoord diff = NSToCoordRound(0.1f * rect.height);
735       if (isDown) {
736         rect.y += diff * 2;
737         rect.height -= diff * 2;
738       } else {
739         rect.Deflate(diff, 0);
740       }
741       nsPresContext* pc = PresContext();
742       rect.x = pc->RoundAppUnitsToNearestDevPixels(rect.x);
743       rect.y = pc->RoundAppUnitsToNearestDevPixels(rect.y);
744 
745       RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
746       if (isDown) {
747         // to bottom
748         builder->MoveTo(NSPointToPoint(rect.TopLeft(), appUnitsPerDevPixel));
749         builder->LineTo(NSPointToPoint(rect.TopRight(), appUnitsPerDevPixel));
750         builder->LineTo(NSPointToPoint(
751             (rect.BottomLeft() + rect.BottomRight()) / 2, appUnitsPerDevPixel));
752       } else {
753         if (wm.IsPhysicalLTR()) {
754           // to right
755           builder->MoveTo(NSPointToPoint(rect.TopLeft(), appUnitsPerDevPixel));
756           builder->LineTo(NSPointToPoint(
757               (rect.TopRight() + rect.BottomRight()) / 2, appUnitsPerDevPixel));
758           builder->LineTo(
759               NSPointToPoint(rect.BottomLeft(), appUnitsPerDevPixel));
760         } else {
761           // to left
762           builder->MoveTo(NSPointToPoint(rect.TopRight(), appUnitsPerDevPixel));
763           builder->LineTo(
764               NSPointToPoint(rect.BottomRight(), appUnitsPerDevPixel));
765           builder->LineTo(NSPointToPoint(
766               (rect.TopLeft() + rect.BottomLeft()) / 2, appUnitsPerDevPixel));
767         }
768       }
769 
770       RefPtr<Path> path = builder->Finish();
771       BulletRenderer br(path, color, listStyleType->GetStyle());
772       return Some(br);
773     }
774 
775     default: {
776       RefPtr<nsFontMetrics> fm =
777           nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
778       nsAutoString text;
779       WritingMode wm = GetWritingMode();
780       GetListItemText(listStyleType, wm, Ordinal(), text);
781       nscoord ascent = wm.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
782       aPt.MoveBy(padding.left, padding.top);
783       if (wm.IsVertical()) {
784         if (wm.IsVerticalLR()) {
785           aPt.x = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineX(
786               this, &aRenderingContext, aPt.x, ascent));
787         } else {
788           aPt.x = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineX(
789               this, &aRenderingContext, aPt.x + mRect.width, -ascent));
790         }
791       } else {
792         aPt.y = NSToCoordRound(nsLayoutUtils::GetSnappedBaselineY(
793             this, &aRenderingContext, aPt.y, ascent));
794       }
795 
796       BulletRenderer br(text, fm, color, aPt, listStyleType->GetStyle());
797       return Some(br);
798     }
799   }
800 
801   MOZ_CRASH("unreachable");
802   return Nothing();
803 }
804 
PaintBullet(gfxContext & aRenderingContext,nsPoint aPt,const nsRect & aDirtyRect,uint32_t aFlags,bool aDisableSubpixelAA)805 ImgDrawResult nsBulletFrame::PaintBullet(gfxContext& aRenderingContext,
806                                          nsPoint aPt, const nsRect& aDirtyRect,
807                                          uint32_t aFlags,
808                                          bool aDisableSubpixelAA) {
809   Maybe<BulletRenderer> br = CreateBulletRenderer(aRenderingContext, aPt);
810 
811   if (!br) {
812     return ImgDrawResult::SUCCESS;
813   }
814 
815   return br->Paint(aRenderingContext, aPt, aDirtyRect, aFlags,
816                    aDisableSubpixelAA, this);
817 }
818 
GetListItemText(CounterStyle * aStyle,mozilla::WritingMode aWritingMode,int32_t aOrdinal,nsAString & aResult)819 void nsBulletFrame::GetListItemText(CounterStyle* aStyle,
820                                     mozilla::WritingMode aWritingMode,
821                                     int32_t aOrdinal, nsAString& aResult) {
822   NS_ASSERTION(
823       aStyle->GetStyle() != NS_STYLE_LIST_STYLE_NONE &&
824           aStyle->GetStyle() != NS_STYLE_LIST_STYLE_DISC &&
825           aStyle->GetStyle() != NS_STYLE_LIST_STYLE_CIRCLE &&
826           aStyle->GetStyle() != NS_STYLE_LIST_STYLE_SQUARE &&
827           aStyle->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
828           aStyle->GetStyle() != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN,
829       "we should be using specialized code for these types");
830 
831   bool isRTL;
832   nsAutoString counter, prefix, suffix;
833   aStyle->GetPrefix(prefix);
834   aStyle->GetSuffix(suffix);
835   aStyle->GetCounterText(aOrdinal, aWritingMode, counter, isRTL);
836 
837   aResult.Truncate();
838   aResult.Append(prefix);
839   if (aWritingMode.IsBidiRTL() == isRTL) {
840     aResult.Append(counter);
841   } else {
842     // RLM = 0x200f, LRM = 0x200e
843     char16_t mark = isRTL ? 0x200f : 0x200e;
844     aResult.Append(mark);
845     aResult.Append(counter);
846     aResult.Append(mark);
847   }
848   aResult.Append(suffix);
849 }
850 
851 #define MIN_BULLET_SIZE 1
852 
AppendSpacingToPadding(nsFontMetrics * aFontMetrics,LogicalMargin * aPadding)853 void nsBulletFrame::AppendSpacingToPadding(nsFontMetrics* aFontMetrics,
854                                            LogicalMargin* aPadding) {
855   aPadding->IEnd(GetWritingMode()) += aFontMetrics->EmHeight() / 2;
856 }
857 
GetDesiredSize(nsPresContext * aCX,gfxContext * aRenderingContext,ReflowOutput & aMetrics,float aFontSizeInflation,LogicalMargin * aPadding)858 void nsBulletFrame::GetDesiredSize(nsPresContext* aCX,
859                                    gfxContext* aRenderingContext,
860                                    ReflowOutput& aMetrics,
861                                    float aFontSizeInflation,
862                                    LogicalMargin* aPadding) {
863   // Reset our padding.  If we need it, we'll set it below.
864   WritingMode wm = GetWritingMode();
865   aPadding->SizeTo(wm, 0, 0, 0, 0);
866   LogicalSize finalSize(wm);
867 
868   const nsStyleList* myList = StyleList();
869   nscoord ascent;
870   RefPtr<nsFontMetrics> fm =
871       nsLayoutUtils::GetFontMetricsForFrame(this, aFontSizeInflation);
872 
873   RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
874 
875   if (myList->GetListStyleImage() && mImageRequest) {
876     uint32_t status;
877     mImageRequest->GetImageStatus(&status);
878     if (status & imgIRequest::STATUS_SIZE_AVAILABLE &&
879         !(status & imgIRequest::STATUS_ERROR)) {
880       // auto size the image
881       finalSize.ISize(wm) = mIntrinsicSize.ISize(wm);
882       aMetrics.SetBlockStartAscent(finalSize.BSize(wm) =
883                                        mIntrinsicSize.BSize(wm));
884       aMetrics.SetSize(wm, finalSize);
885 
886       AppendSpacingToPadding(fm, aPadding);
887 
888       AddStateBits(BULLET_FRAME_IMAGE_LOADING);
889 
890       return;
891     }
892   }
893 
894   // If we're getting our desired size and don't have an image, reset
895   // mIntrinsicSize to (0,0).  Otherwise, if we used to have an image, it
896   // changed, and the new one is coming in, but we're reflowing before it's
897   // fully there, we'll end up with mIntrinsicSize not matching our size, but
898   // won't trigger a reflow in OnStartContainer (because mIntrinsicSize will
899   // match the image size).
900   mIntrinsicSize.SizeTo(wm, 0, 0);
901 
902   nscoord bulletSize;
903 
904   nsAutoString text;
905   CounterStyle* style = ResolveCounterStyle();
906   switch (style->GetStyle()) {
907     case NS_STYLE_LIST_STYLE_NONE:
908       finalSize.ISize(wm) = finalSize.BSize(wm) = 0;
909       aMetrics.SetBlockStartAscent(0);
910       break;
911 
912     case NS_STYLE_LIST_STYLE_DISC:
913     case NS_STYLE_LIST_STYLE_CIRCLE:
914     case NS_STYLE_LIST_STYLE_SQUARE: {
915       ascent = fm->MaxAscent();
916       bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
917                             NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
918       aPadding->BEnd(wm) = NSToCoordRound(float(ascent) / 8.0f);
919       finalSize.ISize(wm) = finalSize.BSize(wm) = bulletSize;
920       aMetrics.SetBlockStartAscent(bulletSize + aPadding->BEnd(wm));
921       AppendSpacingToPadding(fm, aPadding);
922       break;
923     }
924 
925     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED:
926     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN:
927       ascent = fm->EmAscent();
928       bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
929                             NSToCoordRound(0.75f * ascent));
930       aPadding->BEnd(wm) = NSToCoordRound(0.125f * ascent);
931       finalSize.ISize(wm) = finalSize.BSize(wm) = bulletSize;
932       if (!wm.IsVertical()) {
933         aMetrics.SetBlockStartAscent(bulletSize + aPadding->BEnd(wm));
934       }
935       AppendSpacingToPadding(fm, aPadding);
936       break;
937 
938     default:
939       GetListItemText(style, wm, Ordinal(), text);
940       finalSize.BSize(wm) = fm->MaxHeight();
941       finalSize.ISize(wm) = nsLayoutUtils::AppUnitWidthOfStringBidi(
942           text, this, *fm, *aRenderingContext);
943       aMetrics.SetBlockStartAscent(wm.IsLineInverted() ? fm->MaxDescent()
944                                                        : fm->MaxAscent());
945       break;
946   }
947   aMetrics.SetSize(wm, finalSize);
948 }
949 
Reflow(nsPresContext * aPresContext,ReflowOutput & aMetrics,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)950 void nsBulletFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
951                            const ReflowInput& aReflowInput,
952                            nsReflowStatus& aStatus) {
953   MarkInReflow();
954   DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
955   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
956   MOZ_ASSERT(aStatus.IsEmpty(), "The reflow status should be empty!");
957 
958   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
959   SetFontSizeInflation(inflation);
960 
961   // Get the base size
962   GetDesiredSize(aPresContext, aReflowInput.mRenderingContext, aMetrics,
963                  inflation, &mPadding);
964 
965   // Add in the border and padding; split the top/bottom between the
966   // ascent and descent to make things look nice
967   WritingMode wm = aReflowInput.GetWritingMode();
968   const LogicalMargin& bp = aReflowInput.ComputedLogicalBorderPadding();
969   mPadding.BStart(wm) += NSToCoordRound(bp.BStart(wm) * inflation);
970   mPadding.IEnd(wm) += NSToCoordRound(bp.IEnd(wm) * inflation);
971   mPadding.BEnd(wm) += NSToCoordRound(bp.BEnd(wm) * inflation);
972   mPadding.IStart(wm) += NSToCoordRound(bp.IStart(wm) * inflation);
973 
974   WritingMode lineWM = aMetrics.GetWritingMode();
975   LogicalMargin linePadding = mPadding.ConvertTo(lineWM, wm);
976   aMetrics.ISize(lineWM) += linePadding.IStartEnd(lineWM);
977   aMetrics.BSize(lineWM) += linePadding.BStartEnd(lineWM);
978   aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
979                                linePadding.BStart(lineWM));
980 
981   // XXX this is a bit of a hack, we're assuming that no glyphs used for bullets
982   // overflow their font-boxes. It'll do for now; to fix it for real, we really
983   // should rewrite all the text-handling code here to use gfxTextRun (bug
984   // 397294).
985   aMetrics.SetOverflowAreasToDesiredBounds();
986 
987   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
988 }
989 
990 /* virtual */
GetMinISize(gfxContext * aRenderingContext)991 nscoord nsBulletFrame::GetMinISize(gfxContext* aRenderingContext) {
992   WritingMode wm = GetWritingMode();
993   ReflowOutput reflowOutput(wm);
994   DISPLAY_MIN_INLINE_SIZE(this, reflowOutput.ISize(wm));
995   LogicalMargin padding(wm);
996   GetDesiredSize(PresContext(), aRenderingContext, reflowOutput, 1.0f,
997                  &padding);
998   reflowOutput.ISize(wm) += padding.IStartEnd(wm);
999   return reflowOutput.ISize(wm);
1000 }
1001 
1002 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)1003 nscoord nsBulletFrame::GetPrefISize(gfxContext* aRenderingContext) {
1004   WritingMode wm = GetWritingMode();
1005   ReflowOutput metrics(wm);
1006   DISPLAY_PREF_INLINE_SIZE(this, metrics.ISize(wm));
1007   LogicalMargin padding(wm);
1008   GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f, &padding);
1009   metrics.ISize(wm) += padding.IStartEnd(wm);
1010   return metrics.ISize(wm);
1011 }
1012 
1013 // If a bullet has zero size and is "ignorable" from its styling, we behave
1014 // as if it doesn't exist, from a line-breaking/isize-computation perspective.
1015 // Otherwise, we use the default implementation, same as nsFrame.
IsIgnoreable(const nsIFrame * aFrame,nscoord aISize)1016 static inline bool IsIgnoreable(const nsIFrame* aFrame, nscoord aISize) {
1017   if (aISize != nscoord(0)) {
1018     return false;
1019   }
1020   auto listStyle = aFrame->StyleList();
1021   return listStyle->mCounterStyle.IsNone() && !listStyle->GetListStyleImage();
1022 }
1023 
1024 /* virtual */
AddInlineMinISize(gfxContext * aRenderingContext,nsIFrame::InlineMinISizeData * aData)1025 void nsBulletFrame::AddInlineMinISize(gfxContext* aRenderingContext,
1026                                       nsIFrame::InlineMinISizeData* aData) {
1027   nscoord isize = nsLayoutUtils::IntrinsicForContainer(
1028       aRenderingContext, this, nsLayoutUtils::MIN_ISIZE);
1029   if (MOZ_LIKELY(!::IsIgnoreable(this, isize))) {
1030     aData->DefaultAddInlineMinISize(this, isize);
1031   }
1032 }
1033 
1034 /* virtual */
AddInlinePrefISize(gfxContext * aRenderingContext,nsIFrame::InlinePrefISizeData * aData)1035 void nsBulletFrame::AddInlinePrefISize(gfxContext* aRenderingContext,
1036                                        nsIFrame::InlinePrefISizeData* aData) {
1037   nscoord isize = nsLayoutUtils::IntrinsicForContainer(
1038       aRenderingContext, this, nsLayoutUtils::PREF_ISIZE);
1039   if (MOZ_LIKELY(!::IsIgnoreable(this, isize))) {
1040     aData->DefaultAddInlinePrefISize(isize);
1041   }
1042 }
1043 
Notify(imgIRequest * aRequest,int32_t aType,const nsIntRect * aData)1044 void nsBulletFrame::Notify(imgIRequest* aRequest, int32_t aType,
1045                            const nsIntRect* aData) {
1046   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
1047     nsCOMPtr<imgIContainer> image;
1048     aRequest->GetImage(getter_AddRefs(image));
1049     return OnSizeAvailable(aRequest, image);
1050   }
1051 
1052   if (aType == imgINotificationObserver::FRAME_UPDATE) {
1053     // The image has changed.
1054     // Invalidate the entire content area. Maybe it's not optimal but it's
1055     // simple and always correct, and I'll be a stunned mullet if it ever
1056     // matters for performance
1057     InvalidateFrame();
1058   }
1059 
1060   if (aType == imgINotificationObserver::IS_ANIMATED) {
1061     // Register the image request with the refresh driver now that we know it's
1062     // animated.
1063     if (aRequest == mImageRequest) {
1064       RegisterImageRequest(/* aKnownToBeAnimated = */ true);
1065     }
1066   }
1067 
1068   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
1069     // Unconditionally start decoding for now.
1070     // XXX(seth): We eventually want to decide whether to do this based on
1071     // visibility. We should get that for free from bug 1091236.
1072     nsCOMPtr<imgIContainer> container;
1073     aRequest->GetImage(getter_AddRefs(container));
1074     if (container) {
1075       // Retrieve the intrinsic size of the image.
1076       int32_t width = 0;
1077       int32_t height = 0;
1078       container->GetWidth(&width);
1079       container->GetHeight(&height);
1080 
1081       // Request a decode at that size.
1082       container->RequestDecodeForSize(
1083           IntSize(width, height), imgIContainer::DECODE_FLAGS_DEFAULT |
1084                                       imgIContainer::FLAG_HIGH_QUALITY_SCALING);
1085     }
1086 
1087     InvalidateFrame();
1088   }
1089 
1090   if (aType == imgINotificationObserver::DECODE_COMPLETE) {
1091     if (Document* parent = GetOurCurrentDoc()) {
1092       nsCOMPtr<imgIContainer> container;
1093       aRequest->GetImage(getter_AddRefs(container));
1094       if (container) {
1095         container->PropagateUseCounters(parent);
1096       }
1097     }
1098   }
1099 }
1100 
GetOurCurrentDoc() const1101 Document* nsBulletFrame::GetOurCurrentDoc() const {
1102   nsIContent* parentContent = GetParent()->GetContent();
1103   return parentContent ? parentContent->GetComposedDoc() : nullptr;
1104 }
1105 
OnSizeAvailable(imgIRequest * aRequest,imgIContainer * aImage)1106 void nsBulletFrame::OnSizeAvailable(imgIRequest* aRequest,
1107                                     imgIContainer* aImage) {
1108   if (!aImage) return;
1109   if (!aRequest) return;
1110 
1111   uint32_t status;
1112   aRequest->GetImageStatus(&status);
1113   if (status & imgIRequest::STATUS_ERROR) {
1114     return;
1115   }
1116 
1117   nscoord w, h;
1118   aImage->GetWidth(&w);
1119   aImage->GetHeight(&h);
1120 
1121   nsPresContext* presContext = PresContext();
1122 
1123   LogicalSize newsize(GetWritingMode(),
1124                       nsSize(nsPresContext::CSSPixelsToAppUnits(w),
1125                              nsPresContext::CSSPixelsToAppUnits(h)));
1126 
1127   if (mIntrinsicSize != newsize) {
1128     mIntrinsicSize = newsize;
1129 
1130     // Now that the size is available (or an error occurred), trigger
1131     // a reflow of the bullet frame.
1132     mozilla::PresShell* presShell = presContext->GetPresShell();
1133     if (presShell) {
1134       presShell->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
1135                                   NS_FRAME_IS_DIRTY);
1136     }
1137   }
1138 
1139   // Handle animations
1140   aImage->SetAnimationMode(presContext->ImageAnimationMode());
1141   // Ensure the animation (if any) is started. Note: There is no
1142   // corresponding call to Decrement for this. This Increment will be
1143   // 'cleaned up' by the Request when it is destroyed, but only then.
1144   aRequest->IncrementAnimationConsumers();
1145 }
1146 
GetLoadGroup(nsPresContext * aPresContext,nsILoadGroup ** aLoadGroup)1147 void nsBulletFrame::GetLoadGroup(nsPresContext* aPresContext,
1148                                  nsILoadGroup** aLoadGroup) {
1149   if (!aPresContext) return;
1150 
1151   MOZ_ASSERT(nullptr != aLoadGroup, "null OUT parameter pointer");
1152 
1153   mozilla::PresShell* presShell = aPresContext->GetPresShell();
1154   if (!presShell) {
1155     return;
1156   }
1157 
1158   Document* doc = presShell->GetDocument();
1159   if (!doc) return;
1160 
1161   *aLoadGroup = doc->GetDocumentLoadGroup().take();
1162 }
1163 
GetFontSizeInflation() const1164 float nsBulletFrame::GetFontSizeInflation() const {
1165   if (!HasFontSizeInflation()) {
1166     return 1.0f;
1167   }
1168   return GetProperty(FontSizeInflationProperty());
1169 }
1170 
SetFontSizeInflation(float aInflation)1171 void nsBulletFrame::SetFontSizeInflation(float aInflation) {
1172   if (aInflation == 1.0f) {
1173     if (HasFontSizeInflation()) {
1174       RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
1175       RemoveProperty(FontSizeInflationProperty());
1176     }
1177     return;
1178   }
1179 
1180   AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
1181   SetProperty(FontSizeInflationProperty(), aInflation);
1182 }
1183 
GetImage() const1184 already_AddRefed<imgIContainer> nsBulletFrame::GetImage() const {
1185   if (mImageRequest && StyleList()->GetListStyleImage()) {
1186     nsCOMPtr<imgIContainer> imageCon;
1187     mImageRequest->GetImage(getter_AddRefs(imageCon));
1188     return imageCon.forget();
1189   }
1190 
1191   return nullptr;
1192 }
1193 
GetListStyleAscent() const1194 nscoord nsBulletFrame::GetListStyleAscent() const {
1195   RefPtr<nsFontMetrics> fm =
1196       nsLayoutUtils::GetFontMetricsForFrame(this, GetFontSizeInflation());
1197   auto* list = StyleList();
1198   if (list->mCounterStyle.IsNone()) {
1199     return 0;
1200   }
1201   if (list->mCounterStyle.IsAnonymous()) {
1202     return fm->MaxAscent();
1203   }
1204   // NOTE(emilio): @counter-style can override most of the styles from this
1205   // list, and we still return the changed ascent. Do we care about that?
1206   //
1207   // https://github.com/w3c/csswg-drafts/issues/3584
1208   nsAtom* style = list->mCounterStyle.AsAtom();
1209   if (style == nsGkAtoms::disc || style == nsGkAtoms::circle ||
1210       style == nsGkAtoms::square) {
1211     nscoord ascent = fm->MaxAscent();
1212     nscoord baselinePadding = NSToCoordRound(float(ascent) / 8.0f);
1213     ascent = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
1214                       NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
1215     return ascent + baselinePadding;
1216   }
1217   if (style == nsGkAtoms::disclosure_open ||
1218       style == nsGkAtoms::disclosure_closed) {
1219     nscoord ascent = fm->EmAscent();
1220     nscoord baselinePadding = NSToCoordRound(0.125f * ascent);
1221     ascent = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
1222                       NSToCoordRound(0.75f * ascent));
1223     return ascent + baselinePadding;
1224   }
1225   return fm->MaxAscent();
1226 }
1227 
GetLogicalBaseline(WritingMode aWritingMode) const1228 nscoord nsBulletFrame::GetLogicalBaseline(WritingMode aWritingMode) const {
1229   nscoord ascent = 0;
1230   if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
1231     ascent = BSize(aWritingMode);
1232   } else {
1233     ascent = GetListStyleAscent();
1234   }
1235   return ascent + GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
1236 }
1237 
GetNaturalBaselineBOffset(WritingMode aWM,BaselineSharingGroup,nscoord * aBaseline) const1238 bool nsBulletFrame::GetNaturalBaselineBOffset(WritingMode aWM,
1239                                               BaselineSharingGroup,
1240                                               nscoord* aBaseline) const {
1241   nscoord ascent = 0;
1242   if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
1243     ascent = BSize(aWM);
1244   } else {
1245     ascent = GetListStyleAscent();
1246   }
1247   *aBaseline = ascent;
1248   return true;
1249 }
1250 
1251 #ifdef ACCESSIBILITY
GetSpokenText(nsAString & aText)1252 void nsBulletFrame::GetSpokenText(nsAString& aText) {
1253   CounterStyle* style =
1254       PresContext()->CounterStyleManager()->ResolveCounterStyle(
1255           StyleList()->mCounterStyle);
1256   bool isBullet;
1257   style->GetSpokenCounterText(Ordinal(), GetWritingMode(), aText, isBullet);
1258   if (isBullet) {
1259     if (!style->IsNone()) {
1260       aText.Append(' ');
1261     }
1262   } else {
1263     nsAutoString prefix, suffix;
1264     style->GetPrefix(prefix);
1265     style->GetSuffix(suffix);
1266     aText = prefix + aText + suffix;
1267   }
1268 }
1269 #endif
1270 
RegisterImageRequest(bool aKnownToBeAnimated)1271 void nsBulletFrame::RegisterImageRequest(bool aKnownToBeAnimated) {
1272   if (mImageRequest) {
1273     // mRequestRegistered is a bitfield; unpack it temporarily so we can take
1274     // the address.
1275     bool isRequestRegistered = mRequestRegistered;
1276 
1277     if (aKnownToBeAnimated) {
1278       nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
1279                                           &isRequestRegistered);
1280     } else {
1281       nsLayoutUtils::RegisterImageRequestIfAnimated(
1282           PresContext(), mImageRequest, &isRequestRegistered);
1283     }
1284 
1285     mRequestRegistered = isRequestRegistered;
1286   }
1287 }
1288 
DeregisterAndCancelImageRequest()1289 void nsBulletFrame::DeregisterAndCancelImageRequest() {
1290   if (mImageRequest) {
1291     // mRequestRegistered is a bitfield; unpack it temporarily so we can take
1292     // the address.
1293     bool isRequestRegistered = mRequestRegistered;
1294 
1295     // Deregister our image request from the refresh driver.
1296     nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
1297                                           &isRequestRegistered);
1298 
1299     mRequestRegistered = isRequestRegistered;
1300 
1301     // Cancel the image request and forget about it.
1302     mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
1303     mImageRequest = nullptr;
1304   }
1305 }
1306 
SetOrdinal(int32_t aOrdinal,bool aNotify)1307 void nsBulletFrame::SetOrdinal(int32_t aOrdinal, bool aNotify) {
1308   if (mOrdinal == aOrdinal) {
1309     return;
1310   }
1311   mOrdinal = aOrdinal;
1312   if (aNotify) {
1313     PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
1314                                   NS_FRAME_IS_DIRTY);
1315   }
1316 }
1317 
NS_IMPL_ISUPPORTS(nsBulletListener,imgINotificationObserver)1318 NS_IMPL_ISUPPORTS(nsBulletListener, imgINotificationObserver)
1319 
1320 nsBulletListener::nsBulletListener() : mFrame(nullptr) {}
1321 
1322 nsBulletListener::~nsBulletListener() = default;
1323 
Notify(imgIRequest * aRequest,int32_t aType,const nsIntRect * aData)1324 void nsBulletListener::Notify(imgIRequest* aRequest, int32_t aType,
1325                               const nsIntRect* aData) {
1326   if (!mFrame) {
1327     return;
1328   }
1329   return mFrame->Notify(aRequest, aType, aData);
1330 }
1331