/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 | } |