Coverage Report

Created: 2018-09-25 14:53

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