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