layout/base/FrameLayerBuilder.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:08f387ba763c
1 /* -*- Mode: C++; tab-width: 20; 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 #include "mozilla/DebugOnly.h"
7
8 #include "FrameLayerBuilder.h"
9
10 #include "nsDisplayList.h"
11 #include "nsPresContext.h"
12 #include "nsLayoutUtils.h"
13 #include "Layers.h"
14 #include "BasicLayers.h"
15 #include "gfxUtils.h"
16 #include "nsRenderingContext.h"
17 #include "MaskLayerImageCache.h"
18 #include "nsIScrollableFrame.h"
19 #include "nsPrintfCString.h"
20 #include "LayerTreeInvalidation.h"
21 #include "nsSVGIntegrationUtils.h"
22 #include "ImageContainer.h"
23 #include "ActiveLayerTracker.h"
24 #include "gfx2DGlue.h"
25
26 #include "GeckoProfiler.h"
27 #include "mozilla/gfx/Tools.h"
28 #include "mozilla/gfx/2D.h"
29 #include "gfxPrefs.h"
30
31 #include <algorithm>
32
33 using namespace mozilla::layers;
34 using namespace mozilla::gfx;
35
36 namespace mozilla {
37
38 class ContainerState;
39
40 FrameLayerBuilder::DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey,
41 Layer* aLayer, LayerState aLayerState, uint32_t aGeneration)
42
43 : mParent(aParent)
44 , mLayer(aLayer)
45 , mDisplayItemKey(aKey)
46 , mContainerLayerGeneration(aGeneration)
47 , mLayerState(aLayerState)
48 , mUsed(true)
49 , mIsInvalid(false)
50 {
51 }
52
53 FrameLayerBuilder::DisplayItemData::DisplayItemData(DisplayItemData &toCopy)
54 {
55 // This isn't actually a copy-constructor; notice that it steals toCopy's
56 // mGeometry pointer. Be careful.
57 mParent = toCopy.mParent;
58 mLayer = toCopy.mLayer;
59 mInactiveManager = toCopy.mInactiveManager;
60 mFrameList = toCopy.mFrameList;
61 mGeometry = toCopy.mGeometry;
62 mDisplayItemKey = toCopy.mDisplayItemKey;
63 mClip = toCopy.mClip;
64 mContainerLayerGeneration = toCopy.mContainerLayerGeneration;
65 mLayerState = toCopy.mLayerState;
66 mUsed = toCopy.mUsed;
67 }
68
69 void
70 FrameLayerBuilder::DisplayItemData::AddFrame(nsIFrame* aFrame)
71 {
72 mFrameList.AppendElement(aFrame);
73
74 nsTArray<DisplayItemData*> *array =
75 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty()));
76 if (!array) {
77 array = new nsTArray<DisplayItemData*>();
78 aFrame->Properties().Set(FrameLayerBuilder::LayerManagerDataProperty(), array);
79 }
80 array->AppendElement(this);
81 }
82
83 void
84 FrameLayerBuilder::DisplayItemData::RemoveFrame(nsIFrame* aFrame)
85 {
86 DebugOnly<bool> result = mFrameList.RemoveElement(aFrame);
87 NS_ASSERTION(result, "Can't remove a frame that wasn't added!");
88
89 nsTArray<DisplayItemData*> *array =
90 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty()));
91 NS_ASSERTION(array, "Must be already stored on the frame!");
92 array->RemoveElement(this);
93 }
94
95 void
96 FrameLayerBuilder::DisplayItemData::UpdateContents(Layer* aLayer, LayerState aState,
97 uint32_t aContainerLayerGeneration,
98 nsDisplayItem* aItem /* = nullptr */)
99 {
100 mLayer = aLayer;
101 mOptLayer = nullptr;
102 mInactiveManager = nullptr;
103 mLayerState = aState;
104 mContainerLayerGeneration = aContainerLayerGeneration;
105 mGeometry = nullptr;
106 mClip = DisplayItemClip();
107 mUsed = true;
108
109 if (!aItem) {
110 return;
111 }
112
113 nsAutoTArray<nsIFrame*, 4> copy(mFrameList);
114 if (!copy.RemoveElement(aItem->Frame())) {
115 AddFrame(aItem->Frame());
116 }
117
118 nsAutoTArray<nsIFrame*,4> mergedFrames;
119 aItem->GetMergedFrames(&mergedFrames);
120 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) {
121 if (!copy.RemoveElement(mergedFrames[i])) {
122 AddFrame(mergedFrames[i]);
123 }
124 }
125
126 for (uint32_t i = 0; i < copy.Length(); i++) {
127 RemoveFrame(copy[i]);
128 }
129 }
130
131 static nsIFrame* sDestroyedFrame = nullptr;
132 FrameLayerBuilder::DisplayItemData::~DisplayItemData()
133 {
134 for (uint32_t i = 0; i < mFrameList.Length(); i++) {
135 nsIFrame* frame = mFrameList[i];
136 if (frame == sDestroyedFrame) {
137 continue;
138 }
139 nsTArray<DisplayItemData*> *array =
140 reinterpret_cast<nsTArray<DisplayItemData*>*>(frame->Properties().Get(LayerManagerDataProperty()));
141 array->RemoveElement(this);
142 }
143 }
144
145 void
146 FrameLayerBuilder::DisplayItemData::GetFrameListChanges(nsDisplayItem* aOther,
147 nsTArray<nsIFrame*>& aOut)
148 {
149 aOut = mFrameList;
150 nsAutoTArray<nsIFrame*, 4> added;
151 if (!aOut.RemoveElement(aOther->Frame())) {
152 added.AppendElement(aOther->Frame());
153 }
154
155 nsAutoTArray<nsIFrame*,4> mergedFrames;
156 aOther->GetMergedFrames(&mergedFrames);
157 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) {
158 if (!aOut.RemoveElement(mergedFrames[i])) {
159 added.AppendElement(mergedFrames[i]);
160 }
161 }
162
163 aOut.AppendElements(added);
164 }
165
166 /**
167 * This is the userdata we associate with a layer manager.
168 */
169 class LayerManagerData : public LayerUserData {
170 public:
171 LayerManagerData(LayerManager *aManager)
172 : mLayerManager(aManager)
173 #ifdef DEBUG_DISPLAY_ITEM_DATA
174 , mParent(nullptr)
175 #endif
176 , mInvalidateAllLayers(false)
177 {
178 MOZ_COUNT_CTOR(LayerManagerData);
179 }
180 ~LayerManagerData() {
181 MOZ_COUNT_DTOR(LayerManagerData);
182 }
183
184 #ifdef DEBUG_DISPLAY_ITEM_DATA
185 void Dump(const char *aPrefix = "") {
186 printf_stderr("%sLayerManagerData %p\n", aPrefix, this);
187 nsAutoCString prefix;
188 prefix += aPrefix;
189 prefix += " ";
190 mDisplayItems.EnumerateEntries(
191 FrameLayerBuilder::DumpDisplayItemDataForFrame, (void*)prefix.get());
192 }
193 #endif
194
195 /**
196 * Tracks which frames have layers associated with them.
197 */
198 LayerManager *mLayerManager;
199 #ifdef DEBUG_DISPLAY_ITEM_DATA
200 LayerManagerData *mParent;
201 #endif
202 nsTHashtable<nsRefPtrHashKey<FrameLayerBuilder::DisplayItemData> > mDisplayItems;
203 bool mInvalidateAllLayers;
204 };
205
206 /* static */ void
207 FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame* aFrame)
208 {
209 FrameProperties props = aFrame->Properties();
210 props.Delete(LayerManagerDataProperty());
211 }
212
213 // a global cache of image containers used for mask layers
214 static MaskLayerImageCache* gMaskLayerImageCache = nullptr;
215
216 static inline MaskLayerImageCache* GetMaskLayerImageCache()
217 {
218 if (!gMaskLayerImageCache) {
219 gMaskLayerImageCache = new MaskLayerImageCache();
220 }
221
222 return gMaskLayerImageCache;
223 }
224
225 /**
226 * We keep a stack of these to represent the ThebesLayers that are
227 * currently available to have display items added to.
228 * We use a stack here because as much as possible we want to
229 * assign display items to existing ThebesLayers, and to the lowest
230 * ThebesLayer in z-order. This reduces the number of layers and
231 * makes it more likely a display item will be rendered to an opaque
232 * layer, giving us the best chance of getting subpixel AA.
233 */
234 class ThebesLayerData {
235 public:
236 ThebesLayerData() :
237 mAnimatedGeometryRoot(nullptr),
238 mFixedPosFrameForLayerData(nullptr),
239 mReferenceFrame(nullptr),
240 mLayer(nullptr),
241 mIsSolidColorInVisibleRegion(false),
242 mSingleItemFixedToViewport(false),
243 mNeedComponentAlpha(false),
244 mForceTransparentSurface(false),
245 mImage(nullptr),
246 mCommonClipCount(-1),
247 mAllDrawingAbove(false)
248 {}
249 /**
250 * Record that an item has been added to the ThebesLayer, so we
251 * need to update our regions.
252 * @param aVisibleRect the area of the item that's visible
253 * @param aDrawRect the area of the item that would be drawn if it
254 * was completely visible
255 * @param aOpaqueRect if non-null, the area of the item that's opaque.
256 * We pass in a separate opaque rect because the opaque rect can be
257 * bigger than the visible rect, and we want to have the biggest
258 * opaque rect that we can.
259 * @param aSolidColor if non-null, the visible area of the item is
260 * a constant color given by *aSolidColor
261 */
262 void Accumulate(ContainerState* aState,
263 nsDisplayItem* aItem,
264 const nsIntRect& aVisibleRect,
265 const nsIntRect& aDrawRect,
266 const DisplayItemClip& aClip);
267 const nsIFrame* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; }
268
269 /**
270 * Add aHitRegion and aDispatchToContentHitRegion to the hit regions for
271 * this ThebesLayer.
272 */
273 void AccumulateEventRegions(const nsIntRegion& aHitRegion,
274 const nsIntRegion& aMaybeHitRegion,
275 const nsIntRegion& aDispatchToContentHitRegion)
276 {
277 mHitRegion.Or(mHitRegion, aHitRegion);
278 mMaybeHitRegion.Or(mMaybeHitRegion, aMaybeHitRegion);
279 mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aDispatchToContentHitRegion);
280 }
281
282 /**
283 * If this represents only a nsDisplayImage, and the image type
284 * supports being optimized to an ImageLayer (TYPE_RASTER only) returns
285 * an ImageContainer for the image.
286 */
287 already_AddRefed<ImageContainer> CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder);
288
289 void AddDrawAboveRegion(const nsIntRegion& aAbove)
290 {
291 if (!mAllDrawingAbove) {
292 mDrawAboveRegion.Or(mDrawAboveRegion, aAbove);
293 mDrawAboveRegion.SimplifyOutward(4);
294 }
295 }
296
297 void AddVisibleAboveRegion(const nsIntRegion& aAbove)
298 {
299 if (!mAllDrawingAbove) {
300 mVisibleAboveRegion.Or(mVisibleAboveRegion, aAbove);
301 mVisibleAboveRegion.SimplifyOutward(4);
302 }
303 }
304
305 void CopyAboveRegion(ThebesLayerData* aOther)
306 {
307 if (aOther->mAllDrawingAbove || mAllDrawingAbove) {
308 SetAllDrawingAbove();
309 } else {
310 mVisibleAboveRegion.Or(mVisibleAboveRegion, aOther->mVisibleAboveRegion);
311 mVisibleAboveRegion.Or(mVisibleAboveRegion, aOther->mVisibleRegion);
312 mVisibleAboveRegion.SimplifyOutward(4);
313 mDrawAboveRegion.Or(mDrawAboveRegion, aOther->mDrawAboveRegion);
314 mDrawAboveRegion.Or(mDrawAboveRegion, aOther->mDrawRegion);
315 mDrawAboveRegion.SimplifyOutward(4);
316 }
317 }
318
319 void SetAllDrawingAbove()
320 {
321 mAllDrawingAbove = true;
322 mDrawAboveRegion.SetEmpty();
323 mVisibleAboveRegion.SetEmpty();
324 }
325
326 bool DrawAboveRegionIntersects(const nsIntRect& aRect)
327 {
328 return mAllDrawingAbove || mDrawAboveRegion.Intersects(aRect);
329 }
330
331 bool DrawRegionIntersects(const nsIntRect& aRect)
332 {
333 return IsSubjectToAsyncTransforms() || mDrawRegion.Intersects(aRect);
334 }
335
336 bool IntersectsVisibleAboveRegion(const nsIntRegion& aVisibleRegion)
337 {
338 if (mAllDrawingAbove) {
339 return true;
340 }
341 nsIntRegion visibleAboveIntersection;
342 visibleAboveIntersection.And(mVisibleAboveRegion, aVisibleRegion);
343 if (visibleAboveIntersection.IsEmpty()) {
344 return false;
345 }
346 return true;
347 }
348
349 bool IsSubjectToAsyncTransforms()
350 {
351 return mFixedPosFrameForLayerData != nullptr;
352 }
353
354 /**
355 * The region of visible content in the layer, relative to the
356 * container layer (which is at the snapped top-left of the display
357 * list reference frame).
358 */
359 nsIntRegion mVisibleRegion;
360 /**
361 * The region containing the bounds of all display items in the layer,
362 * regardless of visbility.
363 * Same coordinate system as mVisibleRegion.
364 * This is a conservative approximation: it contains the true region.
365 */
366 nsIntRegion mDrawRegion;
367 /**
368 * The region of visible content in the layer that is opaque.
369 * Same coordinate system as mVisibleRegion.
370 */
371 nsIntRegion mOpaqueRegion;
372 /**
373 * The definitely-hit region for this ThebesLayer.
374 */
375 nsIntRegion mHitRegion;
376 /**
377 * The maybe-hit region for this ThebesLayer.
378 */
379 nsIntRegion mMaybeHitRegion;
380 /**
381 * The dispatch-to-content hit region for this ThebesLayer.
382 */
383 nsIntRegion mDispatchToContentHitRegion;
384 /**
385 * The "active scrolled root" for all content in the layer. Must
386 * be non-null; all content in a ThebesLayer must have the same
387 * active scrolled root.
388 */
389 const nsIFrame* mAnimatedGeometryRoot;
390 /**
391 * If non-null, the frame from which we'll extract "fixed positioning"
392 * metadata for this layer. This can be a position:fixed frame or a viewport
393 * frame; the latter case is used for background-attachment:fixed content.
394 */
395 const nsIFrame* mFixedPosFrameForLayerData;
396 const nsIFrame* mReferenceFrame;
397 ThebesLayer* mLayer;
398 /**
399 * If mIsSolidColorInVisibleRegion is true, this is the color of the visible
400 * region.
401 */
402 nscolor mSolidColor;
403 /**
404 * True if every pixel in mVisibleRegion will have color mSolidColor.
405 */
406 bool mIsSolidColorInVisibleRegion;
407 /**
408 * True if the layer contains exactly one item that returned true for
409 * ShouldFixToViewport.
410 */
411 bool mSingleItemFixedToViewport;
412 /**
413 * True if there is any text visible in the layer that's over
414 * transparent pixels in the layer.
415 */
416 bool mNeedComponentAlpha;
417 /**
418 * Set if the layer should be treated as transparent, even if its entire
419 * area is covered by opaque display items. For example, this needs to
420 * be set if something is going to "punch holes" in the layer by clearing
421 * part of its surface.
422 */
423 bool mForceTransparentSurface;
424
425 /**
426 * Stores the pointer to the nsDisplayImage if we want to
427 * convert this to an ImageLayer.
428 */
429 nsDisplayImageContainer* mImage;
430 /**
431 * Stores the clip that we need to apply to the image or, if there is no
432 * image, a clip for SOME item in the layer. There is no guarantee which
433 * item's clip will be stored here and mItemClip should not be used to clip
434 * the whole layer - only some part of the clip should be used, as determined
435 * by ThebesDisplayItemLayerUserData::GetCommonClipCount() - which may even be
436 * no part at all.
437 */
438 DisplayItemClip mItemClip;
439 /**
440 * The first mCommonClipCount rounded rectangle clips are identical for
441 * all items in the layer.
442 * -1 if there are no items in the layer; must be >=0 by the time that this
443 * data is popped from the stack.
444 */
445 int32_t mCommonClipCount;
446 /*
447 * Updates mCommonClipCount by checking for rounded rect clips in common
448 * between the clip on a new item (aCurrentClip) and the common clips
449 * on items already in the layer (the first mCommonClipCount rounded rects
450 * in mItemClip).
451 */
452 void UpdateCommonClipCount(const DisplayItemClip& aCurrentClip);
453
454 private:
455 /**
456 * The region of visible content above the layer and below the
457 * next ThebesLayerData currently in the stack, if any. Note that not
458 * all ThebesLayers for the container are in the ThebesLayerData stack.
459 * Same coordinate system as mVisibleRegion.
460 * This is a conservative approximation: it contains the true region.
461 */
462 nsIntRegion mVisibleAboveRegion;
463 /**
464 * The region containing the bounds of all display items (regardless
465 * of visibility) in the layer and below the next ThebesLayerData
466 * currently in the stack, if any.
467 * Note that not all ThebesLayers for the container are in the
468 * ThebesLayerData stack.
469 * Same coordinate system as mVisibleRegion.
470 */
471 nsIntRegion mDrawAboveRegion;
472 /**
473 * True if mDrawAboveRegion and mVisibleAboveRegion should be treated
474 * as infinite, and all display items should be considered 'above' this layer.
475 */
476 bool mAllDrawingAbove;
477 };
478
479 /**
480 * This is a helper object used to build up the layer children for
481 * a ContainerLayer.
482 */
483 class ContainerState {
484 public:
485 ContainerState(nsDisplayListBuilder* aBuilder,
486 LayerManager* aManager,
487 FrameLayerBuilder* aLayerBuilder,
488 nsIFrame* aContainerFrame,
489 nsDisplayItem* aContainerItem,
490 ContainerLayer* aContainerLayer,
491 const ContainerLayerParameters& aParameters) :
492 mBuilder(aBuilder), mManager(aManager),
493 mLayerBuilder(aLayerBuilder),
494 mContainerFrame(aContainerFrame),
495 mContainerLayer(aContainerLayer),
496 mParameters(aParameters),
497 mNextFreeRecycledThebesLayer(0)
498 {
499 nsPresContext* presContext = aContainerFrame->PresContext();
500 mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
501 mContainerReferenceFrame = aContainerItem ? aContainerItem->ReferenceFrameForChildren() :
502 mBuilder->FindReferenceFrameFor(mContainerFrame);
503 mContainerAnimatedGeometryRoot = aContainerItem
504 ? nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder)
505 : mContainerReferenceFrame;
506 // When AllowResidualTranslation is false, display items will be drawn
507 // scaled with a translation by integer pixels, so we know how the snapping
508 // will work.
509 mSnappingEnabled = aManager->IsSnappingEffectiveTransforms() &&
510 !mParameters.AllowResidualTranslation();
511 CollectOldLayers();
512 }
513
514 enum ProcessDisplayItemsFlags {
515 NO_COMPONENT_ALPHA = 0x01,
516 };
517
518 /**
519 * This is the method that actually walks a display list and builds
520 * the child layers.
521 */
522 void ProcessDisplayItems(const nsDisplayList& aList, uint32_t aFlags);
523 /**
524 * This finalizes all the open ThebesLayers by popping every element off
525 * mThebesLayerDataStack, then sets the children of the container layer
526 * to be all the layers in mNewChildLayers in that order and removes any
527 * layers as children of the container that aren't in mNewChildLayers.
528 * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA,
529 * set *aTextContentFlags to CONTENT_COMPONENT_ALPHA
530 */
531 void Finish(uint32_t *aTextContentFlags, LayerManagerData* aData);
532
533 nsRect GetChildrenBounds() { return mBounds; }
534
535 nscoord GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel; }
536
537 nsIntRect ScaleToNearestPixels(const nsRect& aRect)
538 {
539 return aRect.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale,
540 mAppUnitsPerDevPixel);
541 }
542 nsIntRegion ScaleRegionToNearestPixels(const nsRegion& aRegion)
543 {
544 return aRegion.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale,
545 mAppUnitsPerDevPixel);
546 }
547 nsIntRect ScaleToOutsidePixels(const nsRect& aRect, bool aSnap = false)
548 {
549 if (aSnap && mSnappingEnabled) {
550 return ScaleToNearestPixels(aRect);
551 }
552 return aRect.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale,
553 mAppUnitsPerDevPixel);
554 }
555 nsIntRect ScaleToInsidePixels(const nsRect& aRect, bool aSnap = false)
556 {
557 if (aSnap && mSnappingEnabled) {
558 return ScaleToNearestPixels(aRect);
559 }
560 return aRect.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale,
561 mAppUnitsPerDevPixel);
562 }
563
564 nsIntRegion ScaleRegionToInsidePixels(const nsRegion& aRegion, bool aSnap = false)
565 {
566 if (aSnap && mSnappingEnabled) {
567 return ScaleRegionToNearestPixels(aRegion);
568 }
569 return aRegion.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale,
570 mAppUnitsPerDevPixel);
571 }
572
573 nsIntRegion ScaleRegionToOutsidePixels(const nsRegion& aRegion, bool aSnap = false)
574 {
575 if (aSnap && mSnappingEnabled) {
576 return ScaleRegionToNearestPixels(aRegion);
577 }
578 return aRegion.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale,
579 mAppUnitsPerDevPixel);
580 }
581
582 protected:
583 friend class ThebesLayerData;
584
585 /**
586 * Grab the next recyclable ThebesLayer, or create one if there are no
587 * more recyclable ThebesLayers. Does any necessary invalidation of
588 * a recycled ThebesLayer, and sets up the transform on the ThebesLayer
589 * to account for scrolling.
590 */
591 already_AddRefed<ThebesLayer> CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot,
592 const nsIFrame *aReferenceFrame,
593 const nsPoint& aTopLeft);
594 /**
595 * Grab the next recyclable ColorLayer, or create one if there are no
596 * more recyclable ColorLayers.
597 */
598 already_AddRefed<ColorLayer> CreateOrRecycleColorLayer(ThebesLayer* aThebes);
599 /**
600 * Grab the next recyclable ImageLayer, or create one if there are no
601 * more recyclable ImageLayers.
602 */
603 already_AddRefed<ImageLayer> CreateOrRecycleImageLayer(ThebesLayer* aThebes);
604 /**
605 * Grab a recyclable ImageLayer for use as a mask layer for aLayer (that is a
606 * mask layer which has been used for aLayer before), or create one if such
607 * a layer doesn't exist.
608 */
609 already_AddRefed<ImageLayer> CreateOrRecycleMaskImageLayerFor(Layer* aLayer);
610 /**
611 * Grabs all ThebesLayers and ColorLayers from the ContainerLayer and makes them
612 * available for recycling.
613 */
614 void CollectOldLayers();
615 /**
616 * If aItem used to belong to a ThebesLayer, invalidates the area of
617 * aItem in that layer. If aNewLayer is a ThebesLayer, invalidates the area of
618 * aItem in that layer.
619 */
620 void InvalidateForLayerChange(nsDisplayItem* aItem,
621 Layer* aNewLayer,
622 const DisplayItemClip& aClip,
623 const nsPoint& aTopLeft,
624 nsDisplayItemGeometry *aGeometry);
625 /**
626 * Try to determine whether the ThebesLayer at aThebesLayerIndex
627 * has a single opaque color behind it, over the entire bounds of its visible
628 * region.
629 * If successful, return that color, otherwise return NS_RGBA(0,0,0,0).
630 */
631 nscolor FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex);
632 /**
633 * Find the fixed-pos frame, if any, containing (or equal to)
634 * aAnimatedGeometryRoot. Only return a fixed-pos frame if its viewport
635 * has a displayport. Updates *aVisibleRegion to be the intersection of
636 * aDrawRegion and the displayport, and updates *aIsSolidColorInVisibleRegion
637 * (if non-null) to false if the visible region grows.
638 * aDisplayItemFixedToViewport is true if the layer contains a single display
639 * item which returned true for ShouldFixToViewport.
640 * This can return the actual viewport frame for layers whose display items
641 * are directly on the viewport (e.g. background-attachment:fixed backgrounds).
642 */
643 const nsIFrame* FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot,
644 bool aDisplayItemFixedToViewport);
645 void AdjustLayerDataForFixedPositioning(const nsIFrame* aFixedPosFrame,
646 const nsIntRegion& aDrawRegion,
647 nsIntRegion* aVisibleRegion,
648 bool* aIsSolidColorInVisibleRegion = nullptr);
649 /**
650 * Set fixed-pos layer metadata on aLayer according to the data for aFixedPosFrame.
651 */
652 void SetFixedPositionLayerData(Layer* aLayer,
653 const nsIFrame* aFixedPosFrame);
654 /**
655 * Indicate that we are done adding items to the ThebesLayer at the top of
656 * mThebesLayerDataStack. Set the final visible region and opaque-content
657 * flag, and pop it off the stack.
658 */
659 void PopThebesLayerData();
660 /**
661 * Find the ThebesLayer to which we should assign the next display item.
662 * We scan the ThebesLayerData stack to find the topmost ThebesLayer
663 * that is compatible with the display item (i.e., has the same
664 * active scrolled root), and that has no content from other layers above
665 * it and intersecting the aVisibleRect.
666 * Returns the layer, and also updates the ThebesLayerData. Will
667 * push a new ThebesLayerData onto the stack if no suitable existing
668 * layer is found. If we choose a ThebesLayer that's already on the
669 * ThebesLayerData stack, later elements on the stack will be popped off.
670 * @param aVisibleRect the area of the next display item that's visible
671 * @param aAnimatedGeometryRoot the active scrolled root for the next
672 * display item
673 * @param aOpaqueRect if non-null, a region of the display item that is opaque
674 * @param aSolidColor if non-null, indicates that every pixel in aVisibleRect
675 * will be painted with aSolidColor by the item
676 * @param aShouldFixToViewport if true, aAnimatedGeometryRoot is the viewport
677 * and we will be adding fixed-pos metadata for this layer because the
678 * display item returned true from ShouldFixToViewport.
679 */
680 ThebesLayerData* FindThebesLayerFor(nsDisplayItem* aItem,
681 const nsIntRect& aVisibleRect,
682 const nsIFrame* aAnimatedGeometryRoot,
683 const nsPoint& aTopLeft,
684 bool aShouldFixToViewport);
685 ThebesLayerData* GetTopThebesLayerData()
686 {
687 return mThebesLayerDataStack.IsEmpty() ? nullptr
688 : mThebesLayerDataStack[mThebesLayerDataStack.Length() - 1].get();
689 }
690
691 /* Build a mask layer to represent the clipping region. Will return null if
692 * there is no clipping specified or a mask layer cannot be built.
693 * Builds an ImageLayer for the appropriate backend; the mask is relative to
694 * aLayer's visible region.
695 * aLayer is the layer to be clipped.
696 * aRoundedRectClipCount is used when building mask layers for ThebesLayers,
697 * SetupMaskLayer will build a mask layer for only the first
698 * aRoundedRectClipCount rounded rects in aClip
699 */
700 void SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
701 uint32_t aRoundedRectClipCount = UINT32_MAX);
702
703 bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
704 const nsIFrame **aAnimatedGeometryRoot);
705
706 nsDisplayListBuilder* mBuilder;
707 LayerManager* mManager;
708 FrameLayerBuilder* mLayerBuilder;
709 nsIFrame* mContainerFrame;
710 const nsIFrame* mContainerReferenceFrame;
711 const nsIFrame* mContainerAnimatedGeometryRoot;
712 ContainerLayer* mContainerLayer;
713 ContainerLayerParameters mParameters;
714 /**
715 * The region of ThebesLayers that should be invalidated every time
716 * we recycle one.
717 */
718 nsIntRegion mInvalidThebesContent;
719 nsRect mBounds;
720 nsAutoTArray<nsAutoPtr<ThebesLayerData>,1> mThebesLayerDataStack;
721 /**
722 * We collect the list of children in here. During ProcessDisplayItems,
723 * the layers in this array either have mContainerLayer as their parent,
724 * or no parent.
725 */
726 typedef nsAutoTArray<nsRefPtr<Layer>,1> AutoLayersArray;
727 AutoLayersArray mNewChildLayers;
728 nsTArray<nsRefPtr<ThebesLayer> > mRecycledThebesLayers;
729 nsDataHashtable<nsPtrHashKey<Layer>, nsRefPtr<ImageLayer> >
730 mRecycledMaskImageLayers;
731 uint32_t mNextFreeRecycledThebesLayer;
732 nscoord mAppUnitsPerDevPixel;
733 bool mSnappingEnabled;
734 };
735
736 class ThebesDisplayItemLayerUserData : public LayerUserData
737 {
738 public:
739 ThebesDisplayItemLayerUserData() :
740 mMaskClipCount(0),
741 mForcedBackgroundColor(NS_RGBA(0,0,0,0)),
742 mXScale(1.f), mYScale(1.f),
743 mAppUnitsPerDevPixel(0),
744 mTranslation(0, 0),
745 mAnimatedGeometryRootPosition(0, 0) {}
746
747 /**
748 * Record the number of clips in the Thebes layer's mask layer.
749 * Should not be reset when the layer is recycled since it is used to track
750 * changes in the use of mask layers.
751 */
752 uint32_t mMaskClipCount;
753
754 /**
755 * A color that should be painted over the bounds of the layer's visible
756 * region before any other content is painted.
757 */
758 nscolor mForcedBackgroundColor;
759
760 /**
761 * The resolution scale used.
762 */
763 float mXScale, mYScale;
764
765 /**
766 * The appunits per dev pixel for the items in this layer.
767 */
768 nscoord mAppUnitsPerDevPixel;
769
770 /**
771 * The offset from the ThebesLayer's 0,0 to the
772 * reference frame. This isn't necessarily the same as the transform
773 * set on the ThebesLayer since we might also be applying an extra
774 * offset specified by the parent ContainerLayer/
775 */
776 nsIntPoint mTranslation;
777
778 /**
779 * We try to make 0,0 of the ThebesLayer be the top-left of the
780 * border-box of the "active scrolled root" frame (i.e. the nearest ancestor
781 * frame for the display items that is being actively scrolled). But
782 * we force the ThebesLayer transform to be an integer translation, and we may
783 * have a resolution scale, so we have to snap the ThebesLayer transform, so
784 * 0,0 may not be exactly the top-left of the active scrolled root. Here we
785 * store the coordinates in ThebesLayer space of the top-left of the
786 * active scrolled root.
787 */
788 gfxPoint mAnimatedGeometryRootPosition;
789
790 nsIntRegion mRegionToInvalidate;
791
792 // The offset between the active scrolled root of this layer
793 // and the root of the container for the previous and current
794 // paints respectively.
795 nsPoint mLastAnimatedGeometryRootOrigin;
796 nsPoint mAnimatedGeometryRootOrigin;
797
798 nsRefPtr<ColorLayer> mColorLayer;
799 nsRefPtr<ImageLayer> mImageLayer;
800 };
801
802 /*
803 * User data for layers which will be used as masks.
804 */
805 struct MaskLayerUserData : public LayerUserData
806 {
807 MaskLayerUserData()
808 : mScaleX(-1.0f)
809 , mScaleY(-1.0f)
810 , mAppUnitsPerDevPixel(-1)
811 { }
812
813 bool
814 operator== (const MaskLayerUserData& aOther) const
815 {
816 return mRoundedClipRects == aOther.mRoundedClipRects &&
817 mScaleX == aOther.mScaleX &&
818 mScaleY == aOther.mScaleY &&
819 mOffset == aOther.mOffset &&
820 mAppUnitsPerDevPixel == aOther.mAppUnitsPerDevPixel;
821 }
822
823 nsRefPtr<const MaskLayerImageCache::MaskLayerImageKey> mImageKey;
824 // properties of the mask layer; the mask layer may be re-used if these
825 // remain unchanged.
826 nsTArray<DisplayItemClip::RoundedRect> mRoundedClipRects;
827 // scale from the masked layer which is applied to the mask
828 float mScaleX, mScaleY;
829 // The ContainerLayerParameters offset which is applied to the mask's transform.
830 nsIntPoint mOffset;
831 int32_t mAppUnitsPerDevPixel;
832 };
833
834 /**
835 * The address of gThebesDisplayItemLayerUserData is used as the user
836 * data key for ThebesLayers created by FrameLayerBuilder.
837 * It identifies ThebesLayers used to draw non-layer content, which are
838 * therefore eligible for recycling. We want display items to be able to
839 * create their own dedicated ThebesLayers in BuildLayer, if necessary,
840 * and we wouldn't want to accidentally recycle those.
841 * The user data is a ThebesDisplayItemLayerUserData.
842 */
843 uint8_t gThebesDisplayItemLayerUserData;
844 /**
845 * The address of gColorLayerUserData is used as the user
846 * data key for ColorLayers created by FrameLayerBuilder.
847 * The user data is null.
848 */
849 uint8_t gColorLayerUserData;
850 /**
851 * The address of gImageLayerUserData is used as the user
852 * data key for ImageLayers created by FrameLayerBuilder.
853 * The user data is null.
854 */
855 uint8_t gImageLayerUserData;
856 /**
857 * The address of gLayerManagerUserData is used as the user
858 * data key for retained LayerManagers managed by FrameLayerBuilder.
859 * The user data is a LayerManagerData.
860 */
861 uint8_t gLayerManagerUserData;
862 /**
863 * The address of gMaskLayerUserData is used as the user
864 * data key for mask layers managed by FrameLayerBuilder.
865 * The user data is a MaskLayerUserData.
866 */
867 uint8_t gMaskLayerUserData;
868
869 /**
870 * Helper functions for getting user data and casting it to the correct type.
871 * aLayer is the layer where the user data is stored.
872 */
873 MaskLayerUserData* GetMaskLayerUserData(Layer* aLayer)
874 {
875 return static_cast<MaskLayerUserData*>(aLayer->GetUserData(&gMaskLayerUserData));
876 }
877
878 ThebesDisplayItemLayerUserData* GetThebesDisplayItemLayerUserData(Layer* aLayer)
879 {
880 return static_cast<ThebesDisplayItemLayerUserData*>(
881 aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
882 }
883
884 /* static */ void
885 FrameLayerBuilder::Shutdown()
886 {
887 if (gMaskLayerImageCache) {
888 delete gMaskLayerImageCache;
889 gMaskLayerImageCache = nullptr;
890 }
891 }
892
893 void
894 FrameLayerBuilder::Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager,
895 ThebesLayerData* aLayerData)
896 {
897 mDisplayListBuilder = aBuilder;
898 mRootPresContext = aBuilder->RootReferenceFrame()->PresContext()->GetRootPresContext();
899 if (mRootPresContext) {
900 mInitialDOMGeneration = mRootPresContext->GetDOMGeneration();
901 }
902 mContainingThebesLayer = aLayerData;
903 aManager->SetUserData(&gLayerManagerLayerBuilder, this);
904 }
905
906 void
907 FrameLayerBuilder::FlashPaint(gfxContext *aContext)
908 {
909 float r = float(rand()) / RAND_MAX;
910 float g = float(rand()) / RAND_MAX;
911 float b = float(rand()) / RAND_MAX;
912 aContext->SetColor(gfxRGBA(r, g, b, 0.4));
913 aContext->Paint();
914 }
915
916 FrameLayerBuilder::DisplayItemData*
917 FrameLayerBuilder::GetDisplayItemData(nsIFrame* aFrame, uint32_t aKey)
918 {
919 nsTArray<DisplayItemData*> *array =
920 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
921 if (array) {
922 for (uint32_t i = 0; i < array->Length(); i++) {
923 DisplayItemData* item = array->ElementAt(i);
924 if (item->mDisplayItemKey == aKey &&
925 item->mLayer->Manager() == mRetainingManager) {
926 return item;
927 }
928 }
929 }
930 return nullptr;
931 }
932
933 nsACString&
934 AppendToString(nsACString& s, const nsIntRect& r,
935 const char* pfx="", const char* sfx="")
936 {
937 s += pfx;
938 s += nsPrintfCString(
939 "(x=%d, y=%d, w=%d, h=%d)",
940 r.x, r.y, r.width, r.height);
941 return s += sfx;
942 }
943
944 nsACString&
945 AppendToString(nsACString& s, const nsIntRegion& r,
946 const char* pfx="", const char* sfx="")
947 {
948 s += pfx;
949
950 nsIntRegionRectIterator it(r);
951 s += "< ";
952 while (const nsIntRect* sr = it.Next()) {
953 AppendToString(s, *sr) += "; ";
954 }
955 s += ">";
956
957 return s += sfx;
958 }
959
960 /**
961 * Invalidate aRegion in aLayer. aLayer is in the coordinate system
962 * *after* aTranslation has been applied, so we need to
963 * apply the inverse of that transform before calling InvalidateRegion.
964 */
965 static void
966 InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsIntRegion& aRegion,
967 const nsIntPoint& aTranslation)
968 {
969 // Convert the region from the coordinates of the container layer
970 // (relative to the snapped top-left of the display list reference frame)
971 // to the ThebesLayer's own coordinates
972 nsIntRegion rgn = aRegion;
973 rgn.MoveBy(-aTranslation);
974 aLayer->InvalidateRegion(rgn);
975 #ifdef MOZ_DUMP_PAINTING
976 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
977 nsAutoCString str;
978 AppendToString(str, rgn);
979 printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
980 }
981 #endif
982 }
983
984 static void
985 InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsRect& aRect,
986 const DisplayItemClip& aClip,
987 const nsIntPoint& aTranslation)
988 {
989 ThebesDisplayItemLayerUserData* data =
990 static_cast<ThebesDisplayItemLayerUserData*>(aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
991
992 nsRect rect = aClip.ApplyNonRoundedIntersection(aRect);
993
994 nsIntRect pixelRect = rect.ScaleToOutsidePixels(data->mXScale, data->mYScale, data->mAppUnitsPerDevPixel);
995 InvalidatePostTransformRegion(aLayer, pixelRect, aTranslation);
996 }
997
998
999 static nsIntPoint
1000 GetTranslationForThebesLayer(ThebesLayer* aLayer)
1001 {
1002 ThebesDisplayItemLayerUserData* data =
1003 static_cast<ThebesDisplayItemLayerUserData*>
1004 (aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
1005 NS_ASSERTION(data, "Must be a tracked thebes layer!");
1006
1007 return data->mTranslation;
1008 }
1009
1010 /**
1011 * Some frames can have multiple, nested, retaining layer managers
1012 * associated with them (normal manager, inactive managers, SVG effects).
1013 * In these cases we store the 'outermost' LayerManager data property
1014 * on the frame since we can walk down the chain from there.
1015 *
1016 * If one of these frames has just been destroyed, we will free the inner
1017 * layer manager when removing the entry from mFramesWithLayers. Destroying
1018 * the layer manager destroys the LayerManagerData and calls into
1019 * the DisplayItemData destructor. If the inner layer manager had any
1020 * items with the same frame, then we attempt to retrieve properties
1021 * from the deleted frame.
1022 *
1023 * Cache the destroyed frame pointer here so we can avoid crashing in this case.
1024 */
1025
1026 /* static */ void
1027 FrameLayerBuilder::RemoveFrameFromLayerManager(nsIFrame* aFrame,
1028 void* aPropertyValue)
1029 {
1030 sDestroyedFrame = aFrame;
1031 nsTArray<DisplayItemData*> *array =
1032 reinterpret_cast<nsTArray<DisplayItemData*>*>(aPropertyValue);
1033
1034 // Hold a reference to all the items so that they don't get
1035 // deleted from under us.
1036 nsTArray<nsRefPtr<DisplayItemData> > arrayCopy;
1037 for (uint32_t i = 0; i < array->Length(); ++i) {
1038 arrayCopy.AppendElement(array->ElementAt(i));
1039 }
1040
1041 #ifdef DEBUG_DISPLAY_ITEM_DATA
1042 if (array->Length()) {
1043 LayerManagerData *rootData = array->ElementAt(0)->mParent;
1044 while (rootData->mParent) {
1045 rootData = rootData->mParent;
1046 }
1047 printf_stderr("Removing frame %p - dumping display data\n", aFrame);
1048 rootData->Dump();
1049 }
1050 #endif
1051
1052 for (uint32_t i = 0; i < array->Length(); ++i) {
1053 DisplayItemData* data = array->ElementAt(i);
1054
1055 ThebesLayer* t = data->mLayer->AsThebesLayer();
1056 if (t) {
1057 ThebesDisplayItemLayerUserData* thebesData =
1058 static_cast<ThebesDisplayItemLayerUserData*>(t->GetUserData(&gThebesDisplayItemLayerUserData));
1059 if (thebesData) {
1060 nsRegion old = data->mGeometry->ComputeInvalidationRegion();
1061 nsIntRegion rgn = old.ScaleToOutsidePixels(thebesData->mXScale, thebesData->mYScale, thebesData->mAppUnitsPerDevPixel);
1062 rgn.MoveBy(-GetTranslationForThebesLayer(t));
1063 thebesData->mRegionToInvalidate.Or(thebesData->mRegionToInvalidate, rgn);
1064 thebesData->mRegionToInvalidate.SimplifyOutward(8);
1065 }
1066 }
1067
1068 data->mParent->mDisplayItems.RemoveEntry(data);
1069 }
1070
1071 arrayCopy.Clear();
1072 delete array;
1073 sDestroyedFrame = nullptr;
1074 }
1075
1076 void
1077 FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager)
1078 {
1079 mRetainingManager = aManager;
1080 LayerManagerData* data = static_cast<LayerManagerData*>
1081 (aManager->GetUserData(&gLayerManagerUserData));
1082 if (data) {
1083 mInvalidateAllLayers = data->mInvalidateAllLayers;
1084 } else {
1085 data = new LayerManagerData(aManager);
1086 aManager->SetUserData(&gLayerManagerUserData, data);
1087 }
1088 }
1089
1090 void
1091 FrameLayerBuilder::StoreOptimizedLayerForFrame(nsDisplayItem* aItem, Layer* aLayer)
1092 {
1093 if (!mRetainingManager) {
1094 return;
1095 }
1096
1097 DisplayItemData* data = GetDisplayItemDataForManager(aItem, aLayer->Manager());
1098 NS_ASSERTION(data, "Must have already stored data for this item!");
1099 data->mOptLayer = aLayer;
1100 }
1101
1102 void
1103 FrameLayerBuilder::DidEndTransaction()
1104 {
1105 GetMaskLayerImageCache()->Sweep();
1106 }
1107
1108 void
1109 FrameLayerBuilder::WillEndTransaction()
1110 {
1111 if (!mRetainingManager) {
1112 return;
1113 }
1114
1115 // We need to save the data we'll need to support retaining.
1116 LayerManagerData* data = static_cast<LayerManagerData*>
1117 (mRetainingManager->GetUserData(&gLayerManagerUserData));
1118 NS_ASSERTION(data, "Must have data!");
1119 // Update all the frames that used to have layers.
1120 data->mDisplayItems.EnumerateEntries(ProcessRemovedDisplayItems, this);
1121 data->mInvalidateAllLayers = false;
1122 }
1123
1124 /* static */ PLDHashOperator
1125 FrameLayerBuilder::ProcessRemovedDisplayItems(nsRefPtrHashKey<DisplayItemData>* aEntry,
1126 void* aUserArg)
1127 {
1128 DisplayItemData* data = aEntry->GetKey();
1129 if (!data->mUsed) {
1130 // This item was visible, but isn't anymore.
1131 FrameLayerBuilder* layerBuilder = static_cast<FrameLayerBuilder*>(aUserArg);
1132
1133 ThebesLayer* t = data->mLayer->AsThebesLayer();
1134 if (t) {
1135 #ifdef MOZ_DUMP_PAINTING
1136 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
1137 printf_stderr("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", data->mDisplayItemKey, data->mFrameList[0], t);
1138 }
1139 #endif
1140 InvalidatePostTransformRegion(t,
1141 data->mGeometry->ComputeInvalidationRegion(),
1142 data->mClip,
1143 layerBuilder->GetLastPaintOffset(t));
1144 }
1145 return PL_DHASH_REMOVE;
1146 }
1147
1148 data->mUsed = false;
1149 data->mIsInvalid = false;
1150 return PL_DHASH_NEXT;
1151 }
1152
1153 /* static */ PLDHashOperator
1154 FrameLayerBuilder::DumpDisplayItemDataForFrame(nsRefPtrHashKey<DisplayItemData>* aEntry,
1155 void* aClosure)
1156 {
1157 #ifdef DEBUG_DISPLAY_ITEM_DATA
1158 DisplayItemData *data = aEntry->GetKey();
1159
1160 nsAutoCString prefix;
1161 prefix += static_cast<const char*>(aClosure);
1162
1163 const char *layerState;
1164 switch (data->mLayerState) {
1165 case LAYER_NONE:
1166 layerState = "LAYER_NONE"; break;
1167 case LAYER_INACTIVE:
1168 layerState = "LAYER_INACTIVE"; break;
1169 case LAYER_ACTIVE:
1170 layerState = "LAYER_ACTIVE"; break;
1171 case LAYER_ACTIVE_FORCE:
1172 layerState = "LAYER_ACTIVE_FORCE"; break;
1173 case LAYER_ACTIVE_EMPTY:
1174 layerState = "LAYER_ACTIVE_EMPTY"; break;
1175 case LAYER_SVG_EFFECTS:
1176 layerState = "LAYER_SVG_EFFECTS"; break;
1177 }
1178 uint32_t mask = (1 << nsDisplayItem::TYPE_BITS) - 1;
1179
1180 nsAutoCString str;
1181 str += prefix;
1182 str += nsPrintfCString("Frame %p ", data->mFrameList[0]);
1183 str += nsDisplayItem::DisplayItemTypeName(static_cast<nsDisplayItem::Type>(data->mDisplayItemKey & mask));
1184 if ((data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS)) {
1185 str += nsPrintfCString("(%i)", data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS);
1186 }
1187 str += nsPrintfCString(", %s, Layer %p", layerState, data->mLayer.get());
1188 if (data->mOptLayer) {
1189 str += nsPrintfCString(", OptLayer %p", data->mOptLayer.get());
1190 }
1191 if (data->mInactiveManager) {
1192 str += nsPrintfCString(", InactiveLayerManager %p", data->mInactiveManager.get());
1193 }
1194 str += "\n";
1195
1196 printf_stderr("%s", str.get());
1197
1198 if (data->mInactiveManager) {
1199 prefix += " ";
1200 printf_stderr("%sDumping inactive layer info:\n", prefix.get());
1201 LayerManagerData* lmd = static_cast<LayerManagerData*>
1202 (data->mInactiveManager->GetUserData(&gLayerManagerUserData));
1203 lmd->Dump(prefix.get());
1204 }
1205 #endif
1206 return PL_DHASH_NEXT;
1207 }
1208
1209 /* static */ FrameLayerBuilder::DisplayItemData*
1210 FrameLayerBuilder::GetDisplayItemDataForManager(nsDisplayItem* aItem,
1211 LayerManager* aManager)
1212 {
1213 nsTArray<DisplayItemData*> *array =
1214 reinterpret_cast<nsTArray<DisplayItemData*>*>(aItem->Frame()->Properties().Get(LayerManagerDataProperty()));
1215 if (array) {
1216 for (uint32_t i = 0; i < array->Length(); i++) {
1217 DisplayItemData* item = array->ElementAt(i);
1218 if (item->mDisplayItemKey == aItem->GetPerFrameKey() &&
1219 item->mLayer->Manager() == aManager) {
1220 return item;
1221 }
1222 }
1223 }
1224 return nullptr;
1225 }
1226
1227 bool
1228 FrameLayerBuilder::HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey)
1229 {
1230 nsTArray<DisplayItemData*> *array =
1231 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
1232 if (array) {
1233 for (uint32_t i = 0; i < array->Length(); i++) {
1234 if (array->ElementAt(i)->mDisplayItemKey == aDisplayItemKey) {
1235 return true;
1236 }
1237 }
1238 }
1239 return false;
1240 }
1241
1242 void
1243 FrameLayerBuilder::IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback)
1244 {
1245 nsTArray<DisplayItemData*> *array =
1246 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
1247 if (!array) {
1248 return;
1249 }
1250
1251 for (uint32_t i = 0; i < array->Length(); i++) {
1252 DisplayItemData* data = array->ElementAt(i);
1253 if (data->mDisplayItemKey != nsDisplayItem::TYPE_ZERO) {
1254 aCallback(aFrame, data);
1255 }
1256 }
1257 }
1258
1259 FrameLayerBuilder::DisplayItemData*
1260 FrameLayerBuilder::GetOldLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKey)
1261 {
1262 // If we need to build a new layer tree, then just refuse to recycle
1263 // anything.
1264 if (!mRetainingManager || mInvalidateAllLayers)
1265 return nullptr;
1266
1267 DisplayItemData *data = GetDisplayItemData(aFrame, aDisplayItemKey);
1268
1269 if (data && data->mLayer->Manager() == mRetainingManager) {
1270 return data;
1271 }
1272 return nullptr;
1273 }
1274
1275 Layer*
1276 FrameLayerBuilder::GetOldLayerFor(nsDisplayItem* aItem,
1277 nsDisplayItemGeometry** aOldGeometry,
1278 DisplayItemClip** aOldClip,
1279 nsTArray<nsIFrame*>* aChangedFrames,
1280 bool *aIsInvalid)
1281 {
1282 uint32_t key = aItem->GetPerFrameKey();
1283 nsIFrame* frame = aItem->Frame();
1284
1285 DisplayItemData* oldData = GetOldLayerForFrame(frame, key);
1286 if (oldData) {
1287 if (aOldGeometry) {
1288 *aOldGeometry = oldData->mGeometry.get();
1289 }
1290 if (aOldClip) {
1291 *aOldClip = &oldData->mClip;
1292 }
1293 if (aChangedFrames) {
1294 oldData->GetFrameListChanges(aItem, *aChangedFrames);
1295 }
1296 if (aIsInvalid) {
1297 *aIsInvalid = oldData->mIsInvalid;
1298 }
1299 return oldData->mLayer;
1300 }
1301
1302 return nullptr;
1303 }
1304
1305 /* static */ Layer*
1306 FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKey)
1307 {
1308 nsTArray<DisplayItemData*> *array =
1309 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
1310
1311 if (!array) {
1312 return nullptr;
1313 }
1314
1315 for (uint32_t i = 0; i < array->Length(); i++) {
1316 DisplayItemData *data = array->ElementAt(i);
1317
1318 if (data->mDisplayItemKey == aDisplayItemKey) {
1319 return data->mLayer;
1320 }
1321 }
1322 return nullptr;
1323 }
1324
1325 already_AddRefed<ColorLayer>
1326 ContainerState::CreateOrRecycleColorLayer(ThebesLayer *aThebes)
1327 {
1328 ThebesDisplayItemLayerUserData* data =
1329 static_cast<ThebesDisplayItemLayerUserData*>(aThebes->GetUserData(&gThebesDisplayItemLayerUserData));
1330 nsRefPtr<ColorLayer> layer = data->mColorLayer;
1331 if (layer) {
1332 layer->SetMaskLayer(nullptr);
1333 } else {
1334 // Create a new layer
1335 layer = mManager->CreateColorLayer();
1336 if (!layer)
1337 return nullptr;
1338 // Mark this layer as being used for Thebes-painting display items
1339 data->mColorLayer = layer;
1340 layer->SetUserData(&gColorLayerUserData, nullptr);
1341
1342 // Remove other layer types we might have stored for this ThebesLayer
1343 data->mImageLayer = nullptr;
1344 }
1345 return layer.forget();
1346 }
1347
1348 already_AddRefed<ImageLayer>
1349 ContainerState::CreateOrRecycleImageLayer(ThebesLayer *aThebes)
1350 {
1351 ThebesDisplayItemLayerUserData* data =
1352 static_cast<ThebesDisplayItemLayerUserData*>(aThebes->GetUserData(&gThebesDisplayItemLayerUserData));
1353 nsRefPtr<ImageLayer> layer = data->mImageLayer;
1354 if (layer) {
1355 layer->SetMaskLayer(nullptr);
1356 } else {
1357 // Create a new layer
1358 layer = mManager->CreateImageLayer();
1359 if (!layer)
1360 return nullptr;
1361 // Mark this layer as being used for Thebes-painting display items
1362 data->mImageLayer = layer;
1363 layer->SetUserData(&gImageLayerUserData, nullptr);
1364
1365 // Remove other layer types we might have stored for this ThebesLayer
1366 data->mColorLayer = nullptr;
1367 }
1368 return layer.forget();
1369 }
1370
1371 already_AddRefed<ImageLayer>
1372 ContainerState::CreateOrRecycleMaskImageLayerFor(Layer* aLayer)
1373 {
1374 nsRefPtr<ImageLayer> result = mRecycledMaskImageLayers.Get(aLayer);
1375 if (result) {
1376 mRecycledMaskImageLayers.Remove(aLayer);
1377 // XXX if we use clip on mask layers, null it out here
1378 } else {
1379 // Create a new layer
1380 result = mManager->CreateImageLayer();
1381 if (!result)
1382 return nullptr;
1383 result->SetUserData(&gMaskLayerUserData, new MaskLayerUserData());
1384 result->SetDisallowBigImage(true);
1385 }
1386
1387 return result.forget();
1388 }
1389
1390 static const double SUBPIXEL_OFFSET_EPSILON = 0.02;
1391
1392 /**
1393 * This normally computes NSToIntRoundUp(aValue). However, if that would
1394 * give a residual near 0.5 while aOldResidual is near -0.5, or
1395 * it would give a residual near -0.5 while aOldResidual is near 0.5, then
1396 * instead we return the integer in the other direction so that the residual
1397 * is close to aOldResidual.
1398 */
1399 static int32_t
1400 RoundToMatchResidual(double aValue, double aOldResidual)
1401 {
1402 int32_t v = NSToIntRoundUp(aValue);
1403 double residual = aValue - v;
1404 if (aOldResidual < 0) {
1405 if (residual > 0 && fabs(residual - 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
1406 // Round up instead
1407 return int32_t(ceil(aValue));
1408 }
1409 } else if (aOldResidual > 0) {
1410 if (residual < 0 && fabs(residual + 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
1411 // Round down instead
1412 return int32_t(floor(aValue));
1413 }
1414 }
1415 return v;
1416 }
1417
1418 static void
1419 ResetScrollPositionForLayerPixelAlignment(const nsIFrame* aAnimatedGeometryRoot)
1420 {
1421 nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(aAnimatedGeometryRoot);
1422 if (sf) {
1423 sf->ResetScrollPositionForLayerPixelAlignment();
1424 }
1425 }
1426
1427 static void
1428 InvalidateEntireThebesLayer(ThebesLayer* aLayer, const nsIFrame* aAnimatedGeometryRoot)
1429 {
1430 #ifdef MOZ_DUMP_PAINTING
1431 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
1432 printf_stderr("Invalidating entire layer %p\n", aLayer);
1433 }
1434 #endif
1435 nsIntRect invalidate = aLayer->GetValidRegion().GetBounds();
1436 aLayer->InvalidateRegion(invalidate);
1437 aLayer->SetInvalidRectToVisibleRegion();
1438 ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot);
1439 }
1440
1441 already_AddRefed<ThebesLayer>
1442 ContainerState::CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot,
1443 const nsIFrame* aReferenceFrame,
1444 const nsPoint& aTopLeft)
1445 {
1446 // We need a new thebes layer
1447 nsRefPtr<ThebesLayer> layer;
1448 ThebesDisplayItemLayerUserData* data;
1449 #ifndef MOZ_ANDROID_OMTC
1450 bool didResetScrollPositionForLayerPixelAlignment = false;
1451 #endif
1452 if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) {
1453 // Recycle a layer
1454 layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer];
1455 ++mNextFreeRecycledThebesLayer;
1456 // Clear clip rect and mask layer so we don't accidentally stay clipped.
1457 // We will reapply any necessary clipping.
1458 layer->SetMaskLayer(nullptr);
1459
1460 data = static_cast<ThebesDisplayItemLayerUserData*>
1461 (layer->GetUserData(&gThebesDisplayItemLayerUserData));
1462 NS_ASSERTION(data, "Recycled ThebesLayers must have user data");
1463
1464 // This gets called on recycled ThebesLayers that are going to be in the
1465 // final layer tree, so it's a convenient time to invalidate the
1466 // content that changed where we don't know what ThebesLayer it belonged
1467 // to, or if we need to invalidate the entire layer, we can do that.
1468 // This needs to be done before we update the ThebesLayer to its new
1469 // transform. See nsGfxScrollFrame::InvalidateInternal, where
1470 // we ensure that mInvalidThebesContent is updated according to the
1471 // scroll position as of the most recent paint.
1472 if (!FuzzyEqual(data->mXScale, mParameters.mXScale, 0.00001f) ||
1473 !FuzzyEqual(data->mYScale, mParameters.mYScale, 0.00001f) ||
1474 data->mAppUnitsPerDevPixel != mAppUnitsPerDevPixel) {
1475 InvalidateEntireThebesLayer(layer, aAnimatedGeometryRoot);
1476 #ifndef MOZ_ANDROID_OMTC
1477 didResetScrollPositionForLayerPixelAlignment = true;
1478 #endif
1479 }
1480 if (!data->mRegionToInvalidate.IsEmpty()) {
1481 #ifdef MOZ_DUMP_PAINTING
1482 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
1483 printf_stderr("Invalidating deleted frame content from layer %p\n", layer.get());
1484 }
1485 #endif
1486 layer->InvalidateRegion(data->mRegionToInvalidate);
1487 #ifdef MOZ_DUMP_PAINTING
1488 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
1489 nsAutoCString str;
1490 AppendToString(str, data->mRegionToInvalidate);
1491 printf_stderr("Invalidating layer %p: %s\n", layer.get(), str.get());
1492 }
1493 #endif
1494 data->mRegionToInvalidate.SetEmpty();
1495 }
1496
1497 // We do not need to Invalidate these areas in the widget because we
1498 // assume the caller of InvalidateThebesLayerContents has ensured
1499 // the area is invalidated in the widget.
1500 } else {
1501 // Check whether the layer will be scrollable. This is used as a hint to
1502 // influence whether tiled layers are used or not.
1503 bool canScroll = false;
1504 nsIFrame* animatedGeometryRootParent = aAnimatedGeometryRoot->GetParent();
1505 if (animatedGeometryRootParent &&
1506 animatedGeometryRootParent->GetType() == nsGkAtoms::scrollFrame) {
1507 canScroll = true;
1508 }
1509 // Create a new thebes layer
1510 layer = mManager->CreateThebesLayerWithHint(canScroll ? LayerManager::SCROLLABLE :
1511 LayerManager::NONE);
1512 if (!layer)
1513 return nullptr;
1514 // Mark this layer as being used for Thebes-painting display items
1515 data = new ThebesDisplayItemLayerUserData();
1516 layer->SetUserData(&gThebesDisplayItemLayerUserData, data);
1517 ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot);
1518 #ifndef MOZ_ANDROID_OMTC
1519 didResetScrollPositionForLayerPixelAlignment = true;
1520 #endif
1521 }
1522 data->mXScale = mParameters.mXScale;
1523 data->mYScale = mParameters.mYScale;
1524 data->mLastAnimatedGeometryRootOrigin = data->mAnimatedGeometryRootOrigin;
1525 data->mAnimatedGeometryRootOrigin = aTopLeft;
1526 data->mAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
1527 layer->SetAllowResidualTranslation(mParameters.AllowResidualTranslation());
1528
1529 mLayerBuilder->SaveLastPaintOffset(layer);
1530
1531 // Set up transform so that 0,0 in the Thebes layer corresponds to the
1532 // (pixel-snapped) top-left of the aAnimatedGeometryRoot.
1533 nsPoint offset = aAnimatedGeometryRoot->GetOffsetToCrossDoc(aReferenceFrame);
1534 nscoord appUnitsPerDevPixel = aAnimatedGeometryRoot->PresContext()->AppUnitsPerDevPixel();
1535 gfxPoint scaledOffset(
1536 NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale,
1537 NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale);
1538 // We call RoundToMatchResidual here so that the residual after rounding
1539 // is close to data->mAnimatedGeometryRootPosition if possible.
1540 nsIntPoint pixOffset(RoundToMatchResidual(scaledOffset.x, data->mAnimatedGeometryRootPosition.x),
1541 RoundToMatchResidual(scaledOffset.y, data->mAnimatedGeometryRootPosition.y));
1542 data->mTranslation = pixOffset;
1543 pixOffset += mParameters.mOffset;
1544 Matrix matrix;
1545 matrix.Translate(pixOffset.x, pixOffset.y);
1546 layer->SetBaseTransform(Matrix4x4::From2D(matrix));
1547
1548 // FIXME: Temporary workaround for bug 681192 and bug 724786.
1549 #ifndef MOZ_ANDROID_OMTC
1550 // Calculate exact position of the top-left of the active scrolled root.
1551 // This might not be 0,0 due to the snapping in ScaleToNearestPixels.
1552 gfxPoint animatedGeometryRootTopLeft = scaledOffset - ThebesPoint(matrix.GetTranslation()) + mParameters.mOffset;
1553 // If it has changed, then we need to invalidate the entire layer since the
1554 // pixels in the layer buffer have the content at a (subpixel) offset
1555 // from what we need.
1556 if (!animatedGeometryRootTopLeft.WithinEpsilonOf(data->mAnimatedGeometryRootPosition, SUBPIXEL_OFFSET_EPSILON)) {
1557 data->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft;
1558 InvalidateEntireThebesLayer(layer, aAnimatedGeometryRoot);
1559 } else if (didResetScrollPositionForLayerPixelAlignment) {
1560 data->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft;
1561 }
1562 #endif
1563
1564 return layer.forget();
1565 }
1566
1567 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
1568 /**
1569 * Returns the appunits per dev pixel for the item's frame
1570 */
1571 static int32_t
1572 AppUnitsPerDevPixel(nsDisplayItem* aItem)
1573 {
1574 // The underlying frame for zoom items is the root frame of the subdocument.
1575 // But zoom display items report their bounds etc using the parent document's
1576 // APD because zoom items act as a conversion layer between the two different
1577 // APDs.
1578 if (aItem->GetType() == nsDisplayItem::TYPE_ZOOM) {
1579 return static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
1580 }
1581 return aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
1582 }
1583 #endif
1584
1585 /**
1586 * Restrict the visible region of aLayer to the region that is actually visible.
1587 * Because we only reduce the visible region here, we don't need to worry
1588 * about whether CONTENT_OPAQUE is set; if layer was opaque in the old
1589 * visible region, it will still be opaque in the new one.
1590 * @param aLayerVisibleRegion the visible region of the layer, in the layer's
1591 * coordinate space
1592 * @param aRestrictToRect the rect to restrict the visible region to, in the
1593 * parent's coordinate system
1594 */
1595 static void
1596 SetVisibleRegionForLayer(Layer* aLayer, const nsIntRegion& aLayerVisibleRegion,
1597 const nsIntRect& aRestrictToRect)
1598 {
1599 gfx3DMatrix transform;
1600 To3DMatrix(aLayer->GetTransform(), transform);
1601
1602 // if 'transform' is not invertible, then nothing will be displayed
1603 // for the layer, so it doesn't really matter what we do here
1604 gfxRect itemVisible(aRestrictToRect.x, aRestrictToRect.y,
1605 aRestrictToRect.width, aRestrictToRect.height);
1606 nsIntRect childBounds = aLayerVisibleRegion.GetBounds();
1607 gfxRect childGfxBounds(childBounds.x, childBounds.y,
1608 childBounds.width, childBounds.height);
1609 gfxRect layerVisible = transform.UntransformBounds(itemVisible, childGfxBounds);
1610 layerVisible.RoundOut();
1611
1612 nsIntRect visibleRect;
1613 if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect)) {
1614 aLayer->SetVisibleRegion(nsIntRegion());
1615 } else {
1616 nsIntRegion rgn;
1617 rgn.And(aLayerVisibleRegion, visibleRect);
1618 aLayer->SetVisibleRegion(rgn);
1619 }
1620 }
1621
1622 nscolor
1623 ContainerState::FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex)
1624 {
1625 ThebesLayerData* target = mThebesLayerDataStack[aThebesLayerIndex];
1626 for (int32_t i = aThebesLayerIndex - 1; i >= 0; --i) {
1627 ThebesLayerData* candidate = mThebesLayerDataStack[i];
1628 if (candidate->IntersectsVisibleAboveRegion(target->mVisibleRegion)) {
1629 // Some non-Thebes content between target and candidate; this is
1630 // hopeless
1631 break;
1632 }
1633
1634 nsIntRegion intersection;
1635 intersection.And(candidate->mVisibleRegion, target->mVisibleRegion);
1636 if (intersection.IsEmpty()) {
1637 // The layer doesn't intersect our target, ignore it and move on
1638 continue;
1639 }
1640
1641 // The candidate intersects our target. If any layer has a solid-color
1642 // area behind our target, this must be it. Scan its display items.
1643 nsIntRect deviceRect = target->mVisibleRegion.GetBounds();
1644 nsRect appUnitRect = deviceRect.ToAppUnits(mAppUnitsPerDevPixel);
1645 appUnitRect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale);
1646
1647 FrameLayerBuilder::ThebesLayerItemsEntry* entry =
1648 mLayerBuilder->GetThebesLayerItemsEntry(candidate->mLayer);
1649 NS_ASSERTION(entry, "Must know about this layer!");
1650 for (int32_t j = entry->mItems.Length() - 1; j >= 0; --j) {
1651 nsDisplayItem* item = entry->mItems[j].mItem;
1652 bool snap;
1653 nsRect bounds = item->GetBounds(mBuilder, &snap);
1654 if (snap && mSnappingEnabled) {
1655 nsIntRect snappedBounds = ScaleToNearestPixels(bounds);
1656 if (!snappedBounds.Intersects(deviceRect))
1657 continue;
1658
1659 if (!snappedBounds.Contains(deviceRect))
1660 break;
1661
1662 } else {
1663 // The layer's visible rect is already (close enough to) pixel
1664 // aligned, so no need to round out and in here.
1665 if (!bounds.Intersects(appUnitRect))
1666 continue;
1667
1668 if (!bounds.Contains(appUnitRect))
1669 break;
1670 }
1671
1672 nscolor color;
1673 if (item->IsUniform(mBuilder, &color) && NS_GET_A(color) == 255)
1674 return color;
1675
1676 break;
1677 }
1678 break;
1679 }
1680 return NS_RGBA(0,0,0,0);
1681 }
1682
1683 void
1684 ThebesLayerData::UpdateCommonClipCount(
1685 const DisplayItemClip& aCurrentClip)
1686 {
1687 if (mCommonClipCount >= 0) {
1688 mCommonClipCount = mItemClip.GetCommonRoundedRectCount(aCurrentClip, mCommonClipCount);
1689 } else {
1690 // first item in the layer
1691 mCommonClipCount = aCurrentClip.GetRoundedRectCount();
1692 }
1693 }
1694
1695 already_AddRefed<ImageContainer>
1696 ThebesLayerData::CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder)
1697 {
1698 if (!mImage) {
1699 return nullptr;
1700 }
1701
1702 return mImage->GetContainer(mLayer->Manager(), aBuilder);
1703 }
1704
1705 const nsIFrame*
1706 ContainerState::FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot,
1707 bool aDisplayItemFixedToViewport)
1708 {
1709 if (!mManager->IsWidgetLayerManager()) {
1710 // Never attach any fixed-pos metadata to inactive layers, it's pointless!
1711 return nullptr;
1712 }
1713
1714 nsPresContext* presContext = mContainerFrame->PresContext();
1715 nsIFrame* viewport = presContext->PresShell()->GetRootFrame();
1716
1717 if (viewport == aAnimatedGeometryRoot && aDisplayItemFixedToViewport &&
1718 nsLayoutUtils::ViewportHasDisplayPort(presContext)) {
1719 // Probably a background-attachment:fixed item
1720 return viewport;
1721 }
1722 // Viewports with no fixed-pos frames are not relevant.
1723 if (!viewport->GetFirstChild(nsIFrame::kFixedList)) {
1724 return nullptr;
1725 }
1726 for (const nsIFrame* f = aAnimatedGeometryRoot; f; f = f->GetParent()) {
1727 if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f)) {
1728 return f;
1729 }
1730 if (f == mContainerReferenceFrame) {
1731 // The metadata will go on an ancestor layer if necessary.
1732 return nullptr;
1733 }
1734 }
1735 return nullptr;
1736 }
1737
1738 void
1739 ContainerState::AdjustLayerDataForFixedPositioning(const nsIFrame* aFixedPosFrame,
1740 const nsIntRegion& aDrawRegion,
1741 nsIntRegion* aVisibleRegion,
1742 bool* aIsSolidColorInVisibleRegion)
1743 {
1744 if (!aFixedPosFrame) {
1745 return;
1746 }
1747
1748 nsRect fixedVisibleRect;
1749 nsPresContext* presContext = aFixedPosFrame->PresContext();
1750 nsIPresShell* presShell = presContext->PresShell();
1751 DebugOnly<bool> hasDisplayPort =
1752 nsLayoutUtils::ViewportHasDisplayPort(presContext, &fixedVisibleRect);
1753 NS_ASSERTION(hasDisplayPort, "No fixed-pos layer data if there's no displayport");
1754 // Display ports are relative to the viewport, convert it to be relative
1755 // to our reference frame.
1756 nsIFrame* viewport = presShell->GetRootFrame();
1757 if (aFixedPosFrame != viewport) {
1758 // position: fixed items are reflowed into and only drawn inside the
1759 // viewport, or the scroll position clamping scrollport size, if one is
1760 // set. We differentiate background-attachment: fixed items from
1761 // position: fixed items by the fact that background-attachment: fixed
1762 // items use the viewport as their aFixedPosFrame.
1763 NS_ASSERTION(aFixedPosFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED,
1764 "should be position fixed items only");
1765 fixedVisibleRect.MoveTo(0, 0);
1766 if (presShell->IsScrollPositionClampingScrollPortSizeSet()) {
1767 fixedVisibleRect.SizeTo(presShell->GetScrollPositionClampingScrollPortSize());
1768 } else {
1769 fixedVisibleRect.SizeTo(viewport->GetSize());
1770 }
1771 }
1772 fixedVisibleRect += viewport->GetOffsetToCrossDoc(mContainerReferenceFrame);
1773 nsIntRegion newVisibleRegion;
1774 newVisibleRegion.And(ScaleToOutsidePixels(fixedVisibleRect, false),
1775 aDrawRegion);
1776 if (!aVisibleRegion->Contains(newVisibleRegion)) {
1777 if (aIsSolidColorInVisibleRegion) {
1778 *aIsSolidColorInVisibleRegion = false;
1779 }
1780 *aVisibleRegion = newVisibleRegion;
1781 }
1782 }
1783
1784 void
1785 ContainerState::SetFixedPositionLayerData(Layer* aLayer,
1786 const nsIFrame* aFixedPosFrame)
1787 {
1788 aLayer->SetIsFixedPosition(aFixedPosFrame != nullptr);
1789 if (!aFixedPosFrame) {
1790 return;
1791 }
1792
1793 nsPresContext* presContext = aFixedPosFrame->PresContext();
1794
1795 const nsIFrame* viewportFrame = aFixedPosFrame->GetParent();
1796 // anchorRect will be in the container's coordinate system (aLayer's parent layer).
1797 // This is the same as the display items' reference frame.
1798 nsRect anchorRect;
1799 if (viewportFrame) {
1800 // Fixed position frames are reflowed into the scroll-port size if one has
1801 // been set.
1802 if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
1803 anchorRect.SizeTo(presContext->PresShell()->GetScrollPositionClampingScrollPortSize());
1804 } else {
1805 anchorRect.SizeTo(viewportFrame->GetSize());
1806 }
1807 } else {
1808 // A display item directly attached to the viewport.
1809 // For background-attachment:fixed items, the anchor point is always the
1810 // top-left of the viewport currently.
1811 viewportFrame = aFixedPosFrame;
1812 }
1813 // The anchorRect top-left is always the viewport top-left.
1814 anchorRect.MoveTo(viewportFrame->GetOffsetToCrossDoc(mContainerReferenceFrame));
1815
1816 nsLayoutUtils::SetFixedPositionLayerData(aLayer,
1817 viewportFrame, anchorRect, aFixedPosFrame, presContext, mParameters);
1818 }
1819
1820 static gfx3DMatrix
1821 GetTransformToRoot(Layer* aLayer)
1822 {
1823 Matrix4x4 transform = aLayer->GetTransform();
1824 for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) {
1825 transform = transform * l->GetTransform();
1826 }
1827 gfx3DMatrix result;
1828 To3DMatrix(transform, result);
1829 return result;
1830 }
1831
1832 static void
1833 AddTransformedBoundsToRegion(const nsIntRegion& aRegion,
1834 const gfx3DMatrix& aTransform,
1835 nsIntRegion* aDest)
1836 {
1837 nsIntRect bounds = aRegion.GetBounds();
1838 gfxRect transformed =
1839 aTransform.TransformBounds(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height));
1840 transformed.RoundOut();
1841 nsIntRect intRect;
1842 if (!gfxUtils::GfxRectToIntRect(transformed, &intRect)) {
1843 // This should only fail if coordinates are too big to fit in an int32
1844 *aDest = nsIntRect(-INT32_MAX/2, -INT32_MAX/2, INT32_MAX, INT32_MAX);
1845 return;
1846 }
1847 aDest->Or(*aDest, intRect);
1848 }
1849
1850 static bool
1851 CanOptimizeAwayThebesLayer(ThebesLayerData* aData,
1852 FrameLayerBuilder* aLayerBuilder)
1853 {
1854 bool isRetained = aData->mLayer->Manager()->IsWidgetLayerManager();
1855 if (!isRetained) {
1856 return false;
1857 }
1858
1859 // If there's no thebes layer with valid content in it that we can reuse,
1860 // always create a color or image layer (and potentially throw away an
1861 // existing completely invalid thebes layer).
1862 if (aData->mLayer->GetValidRegion().IsEmpty()) {
1863 return true;
1864 }
1865
1866 // There is an existing thebes layer we can reuse. Throwing it away can make
1867 // compositing cheaper (see bug 946952), but it might cause us to re-allocate
1868 // the thebes layer frequently due to an animation. So we only discard it if
1869 // we're in tree compression mode, which is triggered at a low frequency.
1870 return aLayerBuilder->CheckInLayerTreeCompressionMode();
1871 }
1872
1873 void
1874 ContainerState::PopThebesLayerData()
1875 {
1876 NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop");
1877
1878 int32_t lastIndex = mThebesLayerDataStack.Length() - 1;
1879 ThebesLayerData* data = mThebesLayerDataStack[lastIndex];
1880
1881 AdjustLayerDataForFixedPositioning(data->mFixedPosFrameForLayerData,
1882 data->mDrawRegion,
1883 &data->mVisibleRegion,
1884 &data->mIsSolidColorInVisibleRegion);
1885 nsRefPtr<Layer> layer;
1886 nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(mBuilder);
1887
1888 if ((data->mIsSolidColorInVisibleRegion || imageContainer) &&
1889 CanOptimizeAwayThebesLayer(data, mLayerBuilder)) {
1890 NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
1891 "Can't be a solid color as well as an image!");
1892 if (imageContainer) {
1893 nsRefPtr<ImageLayer> imageLayer = CreateOrRecycleImageLayer(data->mLayer);
1894 imageLayer->SetContainer(imageContainer);
1895 data->mImage->ConfigureLayer(imageLayer, mParameters.mOffset);
1896 imageLayer->SetPostScale(mParameters.mXScale,
1897 mParameters.mYScale);
1898 if (data->mItemClip.HasClip()) {
1899 nsIntRect clip = ScaleToNearestPixels(data->mItemClip.GetClipRect());
1900 clip.MoveBy(mParameters.mOffset);
1901 imageLayer->SetClipRect(&clip);
1902 } else {
1903 imageLayer->SetClipRect(nullptr);
1904 }
1905 layer = imageLayer;
1906 mLayerBuilder->StoreOptimizedLayerForFrame(data->mImage,
1907 imageLayer);
1908 } else {
1909 nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer(data->mLayer);
1910 colorLayer->SetColor(data->mSolidColor);
1911
1912 // Copy transform
1913 colorLayer->SetBaseTransform(data->mLayer->GetBaseTransform());
1914 colorLayer->SetPostScale(data->mLayer->GetPostXScale(), data->mLayer->GetPostYScale());
1915
1916 nsIntRect visibleRect = data->mVisibleRegion.GetBounds();
1917 visibleRect.MoveBy(-GetTranslationForThebesLayer(data->mLayer));
1918 colorLayer->SetBounds(visibleRect);
1919
1920 layer = colorLayer;
1921 }
1922
1923 NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
1924 AutoLayersArray::index_type index = mNewChildLayers.IndexOf(data->mLayer);
1925 NS_ASSERTION(index != AutoLayersArray::NoIndex, "Thebes layer not found?");
1926 mNewChildLayers.InsertElementAt(index + 1, layer);
1927
1928 // Hide the ThebesLayer. We leave it in the layer tree so that we
1929 // can find and recycle it later.
1930 nsIntRect emptyRect;
1931 data->mLayer->SetClipRect(&emptyRect);
1932 data->mLayer->SetVisibleRegion(nsIntRegion());
1933 data->mLayer->SetEventRegions(EventRegions());
1934 } else {
1935 layer = data->mLayer;
1936 imageContainer = nullptr;
1937 layer->SetClipRect(nullptr);
1938 }
1939
1940 Matrix transform;
1941 if (!layer->GetTransform().Is2D(&transform)) {
1942 NS_ERROR("Only 2D transformations currently supported");
1943 }
1944
1945 // ImageLayers are already configured with a visible region
1946 if (!imageContainer) {
1947 NS_ASSERTION(!transform.HasNonIntegerTranslation(),
1948 "Matrix not just an integer translation?");
1949 // Convert from relative to the container to relative to the
1950 // ThebesLayer itself.
1951 nsIntRegion rgn = data->mVisibleRegion;
1952 rgn.MoveBy(-GetTranslationForThebesLayer(data->mLayer));
1953 layer->SetVisibleRegion(rgn);
1954 }
1955
1956 nsIntRegion transparentRegion;
1957 transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion);
1958 bool isOpaque = transparentRegion.IsEmpty();
1959 // For translucent ThebesLayers, try to find an opaque background
1960 // color that covers the entire area beneath it so we can pull that
1961 // color into this layer to make it opaque.
1962 if (layer == data->mLayer) {
1963 nscolor backgroundColor = NS_RGBA(0,0,0,0);
1964 if (!isOpaque) {
1965 backgroundColor = FindOpaqueBackgroundColorFor(lastIndex);
1966 if (NS_GET_A(backgroundColor) == 255) {
1967 isOpaque = true;
1968 }
1969 }
1970
1971 // Store the background color
1972 ThebesDisplayItemLayerUserData* userData =
1973 GetThebesDisplayItemLayerUserData(data->mLayer);
1974 NS_ASSERTION(userData, "where did our user data go?");
1975 if (userData->mForcedBackgroundColor != backgroundColor) {
1976 // Invalidate the entire target ThebesLayer since we're changing
1977 // the background color
1978 data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion());
1979 }
1980 userData->mForcedBackgroundColor = backgroundColor;
1981
1982 // use a mask layer for rounded rect clipping.
1983 // data->mCommonClipCount may be -1 if we haven't put any actual
1984 // drawable items in this layer (i.e. it's only catching events).
1985 int32_t commonClipCount = std::max(0, data->mCommonClipCount);
1986 SetupMaskLayer(layer, data->mItemClip, commonClipCount);
1987 // copy commonClipCount to the entry
1988 FrameLayerBuilder::ThebesLayerItemsEntry* entry = mLayerBuilder->
1989 GetThebesLayerItemsEntry(static_cast<ThebesLayer*>(layer.get()));
1990 entry->mCommonClipCount = commonClipCount;
1991 } else {
1992 // mask layer for image and color layers
1993 SetupMaskLayer(layer, data->mItemClip);
1994 }
1995
1996 uint32_t flags = 0;
1997 nsIWidget* widget = mContainerReferenceFrame->PresContext()->GetRootWidget();
1998 // See bug 941095. Not quite ready to disable this.
1999 bool hidpi = false && widget && widget->GetDefaultScale().scale >= 2;
2000 if (hidpi) {
2001 flags |= Layer::CONTENT_DISABLE_SUBPIXEL_AA;
2002 }
2003 if (isOpaque && !data->mForceTransparentSurface) {
2004 flags |= Layer::CONTENT_OPAQUE;
2005 } else if (data->mNeedComponentAlpha && !hidpi) {
2006 flags |= Layer::CONTENT_COMPONENT_ALPHA;
2007 }
2008 layer->SetContentFlags(flags);
2009
2010 SetFixedPositionLayerData(layer, data->mFixedPosFrameForLayerData);
2011
2012 ThebesLayerData* containingThebesLayerData =
2013 mLayerBuilder->GetContainingThebesLayerData();
2014 if (containingThebesLayerData) {
2015 gfx3DMatrix matrix = GetTransformToRoot(layer);
2016 nsIntPoint translatedDest = GetTranslationForThebesLayer(containingThebesLayerData->mLayer);
2017 matrix.TranslatePost(-gfxPoint3D(translatedDest.x, translatedDest.y, 0));
2018 AddTransformedBoundsToRegion(data->mDispatchToContentHitRegion, matrix,
2019 &containingThebesLayerData->mDispatchToContentHitRegion);
2020 AddTransformedBoundsToRegion(data->mMaybeHitRegion, matrix,
2021 &containingThebesLayerData->mMaybeHitRegion);
2022 // Our definitely-hit region must go to the maybe-hit-region since
2023 // this function is an approximation.
2024 gfxMatrix matrix2D;
2025 bool isPrecise = matrix.Is2D(&matrix2D) && !matrix2D.HasNonAxisAlignedTransform();
2026 AddTransformedBoundsToRegion(data->mHitRegion, matrix,
2027 isPrecise ? &containingThebesLayerData->mHitRegion
2028 : &containingThebesLayerData->mMaybeHitRegion);
2029 } else {
2030 EventRegions regions;
2031 regions.mHitRegion.Swap(&data->mHitRegion);
2032 // Points whose hit-region status we're not sure about need to be dispatched
2033 // to the content thread.
2034 regions.mDispatchToContentHitRegion.Sub(data->mMaybeHitRegion, regions.mHitRegion);
2035 regions.mDispatchToContentHitRegion.Or(regions.mDispatchToContentHitRegion,
2036 data->mDispatchToContentHitRegion);
2037 layer->SetEventRegions(regions);
2038 }
2039
2040 if (lastIndex > 0) {
2041 // Since we're going to pop off the last ThebesLayerData, the
2042 // mVisibleAboveRegion of the second-to-last item will need to include
2043 // the regions of the last item.
2044 ThebesLayerData* nextData = mThebesLayerDataStack[lastIndex - 1];
2045 nextData->CopyAboveRegion(data);
2046 }
2047
2048 mThebesLayerDataStack.RemoveElementAt(lastIndex);
2049 }
2050
2051 static bool
2052 SuppressComponentAlpha(nsDisplayListBuilder* aBuilder,
2053 nsDisplayItem* aItem,
2054 const nsRect& aComponentAlphaBounds)
2055 {
2056 const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion();
2057 if (!windowTransparentRegion || windowTransparentRegion->IsEmpty())
2058 return false;
2059
2060 // Suppress component alpha for items in the toplevel window that are over
2061 // the window translucent area
2062 nsIFrame* f = aItem->Frame();
2063 nsIFrame* ref = aBuilder->RootReferenceFrame();
2064 if (f->PresContext() != ref->PresContext())
2065 return false;
2066
2067 for (nsIFrame* t = f; t; t = t->GetParent()) {
2068 if (t->IsTransformed())
2069 return false;
2070 }
2071
2072 return windowTransparentRegion->Intersects(aComponentAlphaBounds);
2073 }
2074
2075 static bool
2076 WindowHasTransparency(nsDisplayListBuilder* aBuilder)
2077 {
2078 const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion();
2079 return windowTransparentRegion && !windowTransparentRegion->IsEmpty();
2080 }
2081
2082 void
2083 ThebesLayerData::Accumulate(ContainerState* aState,
2084 nsDisplayItem* aItem,
2085 const nsIntRect& aVisibleRect,
2086 const nsIntRect& aDrawRect,
2087 const DisplayItemClip& aClip)
2088 {
2089 if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
2090 mForceTransparentSurface = true;
2091 }
2092 if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
2093 // Disable component alpha.
2094 // Note that the transform (if any) on the ThebesLayer is always an integer translation so
2095 // we don't have to factor that in here.
2096 aItem->DisableComponentAlpha();
2097 }
2098
2099 /* Mark as available for conversion to image layer if this is a nsDisplayImage and
2100 * we are the first visible item in the ThebesLayerData object.
2101 */
2102 if (mVisibleRegion.IsEmpty() &&
2103 aItem->SupportsOptimizingToImage()) {
2104 mImage = static_cast<nsDisplayImageContainer*>(aItem);
2105 } else {
2106 mImage = nullptr;
2107 }
2108 bool clipMatches = mItemClip == aClip;
2109 mItemClip = aClip;
2110
2111 if (!mIsSolidColorInVisibleRegion && mOpaqueRegion.Contains(aDrawRect) &&
2112 mVisibleRegion.Contains(aVisibleRect)) {
2113 // A very common case! Most pages have a ThebesLayer with the page
2114 // background (opaque) visible and most or all of the page content over the
2115 // top of that background.
2116 // The rest of this method won't do anything. mVisibleRegion, mOpaqueRegion
2117 // and mDrawRegion don't need updating. mVisibleRegion contains aVisibleRect
2118 // already, mOpaqueRegion contains aDrawRect and therefore whatever
2119 // the opaque region of the item is. mDrawRegion must contain mOpaqueRegion
2120 // and therefore aDrawRect.
2121 NS_ASSERTION(mDrawRegion.Contains(aDrawRect), "Draw region not covered");
2122 return;
2123 }
2124
2125 nscolor uniformColor;
2126 bool isUniform = aItem->IsUniform(aState->mBuilder, &uniformColor);
2127
2128 // Some display items have to exist (so they can set forceTransparentSurface
2129 // below) but don't draw anything. They'll return true for isUniform but
2130 // a color with opacity 0.
2131 if (!isUniform || NS_GET_A(uniformColor) > 0) {
2132 // Make sure that the visible area is covered by uniform pixels. In
2133 // particular this excludes cases where the edges of the item are not
2134 // pixel-aligned (thus the item will not be truly uniform).
2135 if (isUniform) {
2136 bool snap;
2137 nsRect bounds = aItem->GetBounds(aState->mBuilder, &snap);
2138 if (!aState->ScaleToInsidePixels(bounds, snap).Contains(aVisibleRect)) {
2139 isUniform = false;
2140 }
2141 }
2142 if (isUniform) {
2143 if (mVisibleRegion.IsEmpty()) {
2144 // This color is all we have
2145 mSolidColor = uniformColor;
2146 mIsSolidColorInVisibleRegion = true;
2147 } else if (mIsSolidColorInVisibleRegion &&
2148 mVisibleRegion.IsEqual(nsIntRegion(aVisibleRect)) &&
2149 clipMatches) {
2150 // we can just blend the colors together
2151 mSolidColor = NS_ComposeColors(mSolidColor, uniformColor);
2152 } else {
2153 mIsSolidColorInVisibleRegion = false;
2154 }
2155 } else {
2156 mIsSolidColorInVisibleRegion = false;
2157 }
2158
2159 mVisibleRegion.Or(mVisibleRegion, aVisibleRect);
2160 mVisibleRegion.SimplifyOutward(4);
2161 mDrawRegion.Or(mDrawRegion, aDrawRect);
2162 mDrawRegion.SimplifyOutward(4);
2163 }
2164
2165 bool snap;
2166 nsRegion opaque = aItem->GetOpaqueRegion(aState->mBuilder, &snap);
2167 if (!opaque.IsEmpty()) {
2168 nsRegion opaqueClipped;
2169 nsRegionRectIterator iter(opaque);
2170 for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
2171 opaqueClipped.Or(opaqueClipped, aClip.ApproximateIntersectInward(*r));
2172 }
2173
2174 nsIntRegion opaquePixels = aState->ScaleRegionToInsidePixels(opaqueClipped, snap);
2175
2176 nsIntRegionRectIterator iter2(opaquePixels);
2177 for (const nsIntRect* r = iter2.Next(); r; r = iter2.Next()) {
2178 // We don't use SimplifyInward here since it's not defined exactly
2179 // what it will discard. For our purposes the most important case
2180 // is a large opaque background at the bottom of z-order (e.g.,
2181 // a canvas background), so we need to make sure that the first rect
2182 // we see doesn't get discarded.
2183 nsIntRegion tmp;
2184 tmp.Or(mOpaqueRegion, *r);
2185 // Opaque display items in chrome documents whose window is partially
2186 // transparent are always added to the opaque region. This helps ensure
2187 // that we get as much subpixel-AA as possible in the chrome.
2188 if (tmp.GetNumRects() <= 4 ||
2189 (WindowHasTransparency(aState->mBuilder) &&
2190 aItem->Frame()->PresContext()->IsChrome())) {
2191 mOpaqueRegion = tmp;
2192 }
2193 }
2194 }
2195
2196 if (!aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
2197 nsRect componentAlpha = aItem->GetComponentAlphaBounds(aState->mBuilder);
2198 if (!componentAlpha.IsEmpty()) {
2199 nsIntRect componentAlphaRect =
2200 aState->ScaleToOutsidePixels(componentAlpha, false).Intersect(aVisibleRect);
2201 if (!mOpaqueRegion.Contains(componentAlphaRect)) {
2202 if (SuppressComponentAlpha(aState->mBuilder, aItem, componentAlpha)) {
2203 aItem->DisableComponentAlpha();
2204 } else {
2205 mNeedComponentAlpha = true;
2206 }
2207 }
2208 }
2209 }
2210 }
2211
2212 ThebesLayerData*
2213 ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
2214 const nsIntRect& aVisibleRect,
2215 const nsIFrame* aAnimatedGeometryRoot,
2216 const nsPoint& aTopLeft,
2217 bool aShouldFixToViewport)
2218 {
2219 int32_t i;
2220 int32_t lowestUsableLayerWithScrolledRoot = -1;
2221 int32_t topmostLayerWithScrolledRoot = -1;
2222 for (i = mThebesLayerDataStack.Length() - 1; i >= 0; --i) {
2223 // Don't let should-fix-to-viewport items share a layer with any other items.
2224 if (aShouldFixToViewport) {
2225 ++i;
2226 break;
2227 }
2228 ThebesLayerData* data = mThebesLayerDataStack[i];
2229 // Give up if there is content drawn above (in z-order) this layer that
2230 // intersects aItem's visible region; aItem must be placed in a
2231 // layer this layer.
2232 if (data->DrawAboveRegionIntersects(aVisibleRect)) {
2233 ++i;
2234 break;
2235 }
2236 // If the animated scrolled roots are the same and we can share this layer
2237 // with the item, note this as a usable layer.
2238 if (data->mAnimatedGeometryRoot == aAnimatedGeometryRoot &&
2239 !data->mSingleItemFixedToViewport) {
2240 lowestUsableLayerWithScrolledRoot = i;
2241 if (topmostLayerWithScrolledRoot < 0) {
2242 topmostLayerWithScrolledRoot = i;
2243 }
2244 }
2245 // If the layer's drawn region intersects the item, stop now since no
2246 // lower layer will be usable. Do the same if the layer is subject to
2247 // async transforms, since we don't know where it will really be drawn.
2248 if (data->DrawRegionIntersects(aVisibleRect))
2249 break;
2250 }
2251 if (topmostLayerWithScrolledRoot < 0) {
2252 --i;
2253 for (; i >= 0; --i) {
2254 ThebesLayerData* data = mThebesLayerDataStack[i];
2255 if (data->mAnimatedGeometryRoot == aAnimatedGeometryRoot) {
2256 topmostLayerWithScrolledRoot = i;
2257 break;
2258 }
2259 }
2260 }
2261
2262 if (topmostLayerWithScrolledRoot >= 0) {
2263 while (uint32_t(topmostLayerWithScrolledRoot + 1) < mThebesLayerDataStack.Length()) {
2264 PopThebesLayerData();
2265 }
2266 }
2267
2268 ThebesLayerData* thebesLayerData = nullptr;
2269 if (lowestUsableLayerWithScrolledRoot < 0) {
2270 nsRefPtr<ThebesLayer> layer =
2271 CreateOrRecycleThebesLayer(aAnimatedGeometryRoot, aItem->ReferenceFrame(), aTopLeft);
2272
2273 thebesLayerData = new ThebesLayerData();
2274 mThebesLayerDataStack.AppendElement(thebesLayerData);
2275 thebesLayerData->mLayer = layer;
2276 thebesLayerData->mAnimatedGeometryRoot = aAnimatedGeometryRoot;
2277 thebesLayerData->mFixedPosFrameForLayerData =
2278 FindFixedPosFrameForLayerData(aAnimatedGeometryRoot, aShouldFixToViewport);
2279 thebesLayerData->mReferenceFrame = aItem->ReferenceFrame();
2280 thebesLayerData->mSingleItemFixedToViewport = aShouldFixToViewport;
2281
2282 NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
2283 *mNewChildLayers.AppendElement() = layer.forget();
2284 } else {
2285 thebesLayerData = mThebesLayerDataStack[lowestUsableLayerWithScrolledRoot];
2286 }
2287
2288 return thebesLayerData;
2289 }
2290
2291 #ifdef MOZ_DUMP_PAINTING
2292 static void
2293 DumpPaintedImage(nsDisplayItem* aItem, gfxASurface* aSurf)
2294 {
2295 nsCString string(aItem->Name());
2296 string.Append("-");
2297 string.AppendInt((uint64_t)aItem);
2298 fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading());
2299 aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile);
2300 fprintf_stderr(gfxUtils::sDumpPaintFile, "\";");
2301 }
2302 #endif
2303
2304 static void
2305 PaintInactiveLayer(nsDisplayListBuilder* aBuilder,
2306 LayerManager* aManager,
2307 nsDisplayItem* aItem,
2308 gfxContext* aContext,
2309 nsRenderingContext* aCtx)
2310 {
2311 // This item has an inactive layer. Render it to a ThebesLayer
2312 // using a temporary BasicLayerManager.
2313 BasicLayerManager* basic = static_cast<BasicLayerManager*>(aManager);
2314 nsRefPtr<gfxContext> context = aContext;
2315 #ifdef MOZ_DUMP_PAINTING
2316 int32_t appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
2317 nsIntRect itemVisibleRect =
2318 aItem->GetVisibleRect().ToOutsidePixels(appUnitsPerDevPixel);
2319
2320 nsRefPtr<gfxASurface> surf;
2321 if (gfxUtils::sDumpPainting) {
2322 surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(itemVisibleRect.Size().ToIntSize(),
2323 gfxContentType::COLOR_ALPHA);
2324 surf->SetDeviceOffset(-itemVisibleRect.TopLeft());
2325 context = new gfxContext(surf);
2326 }
2327 #endif
2328 basic->BeginTransaction();
2329 basic->SetTarget(context);
2330
2331 if (aItem->GetType() == nsDisplayItem::TYPE_SVG_EFFECTS) {
2332 static_cast<nsDisplaySVGEffects*>(aItem)->PaintAsLayer(aBuilder, aCtx, basic);
2333 if (basic->InTransaction()) {
2334 basic->AbortTransaction();
2335 }
2336 } else {
2337 basic->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
2338 }
2339 FrameLayerBuilder *builder = static_cast<FrameLayerBuilder*>(basic->GetUserData(&gLayerManagerLayerBuilder));
2340 if (builder) {
2341 builder->DidEndTransaction();
2342 }
2343
2344 basic->SetTarget(nullptr);
2345
2346 #ifdef MOZ_DUMP_PAINTING
2347 if (gfxUtils::sDumpPainting) {
2348 DumpPaintedImage(aItem, surf);
2349
2350 surf->SetDeviceOffset(gfxPoint(0, 0));
2351 aContext->SetSource(surf, itemVisibleRect.TopLeft());
2352 aContext->Rectangle(itemVisibleRect);
2353 aContext->Fill();
2354 aItem->SetPainted();
2355 }
2356 #endif
2357 }
2358
2359 /**
2360 * Chooses a single active scrolled root for the entire display list, used
2361 * when we are flattening layers.
2362 */
2363 bool
2364 ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
2365 const nsIFrame **aAnimatedGeometryRoot)
2366 {
2367 for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
2368 LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
2369 // Don't use an item that won't be part of any ThebesLayers to pick the
2370 // active scrolled root.
2371 if (layerState == LAYER_ACTIVE_FORCE) {
2372 continue;
2373 }
2374
2375 // Try using the actual active scrolled root of the backmost item, as that
2376 // should result in the least invalidation when scrolling.
2377 *aAnimatedGeometryRoot =
2378 nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder);
2379 return true;
2380 }
2381 return false;
2382 }
2383
2384 /*
2385 * Iterate through the non-clip items in aList and its descendants.
2386 * For each item we compute the effective clip rect. Each item is assigned
2387 * to a layer. We invalidate the areas in ThebesLayers where an item
2388 * has moved from one ThebesLayer to another. Also,
2389 * aState->mInvalidThebesContent is invalidated in every ThebesLayer.
2390 * We set the clip rect for items that generated their own layer, and
2391 * create a mask layer to do any rounded rect clipping.
2392 * (ThebesLayers don't need a clip rect on the layer, we clip the items
2393 * individually when we draw them.)
2394 * We set the visible rect for all layers, although the actual setting
2395 * of visible rects for some ThebesLayers is deferred until the calling
2396 * of ContainerState::Finish.
2397 */
2398 void
2399 ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
2400 uint32_t aFlags)
2401 {
2402 PROFILER_LABEL("ContainerState", "ProcessDisplayItems");
2403
2404 const nsIFrame* lastAnimatedGeometryRoot = mContainerReferenceFrame;
2405 nsPoint topLeft(0,0);
2406
2407 // When NO_COMPONENT_ALPHA is set, items will be flattened into a single
2408 // layer, so we need to choose which active scrolled root to use for all
2409 // items.
2410 if (aFlags & NO_COMPONENT_ALPHA) {
2411 if (ChooseAnimatedGeometryRoot(aList, &lastAnimatedGeometryRoot)) {
2412 topLeft = lastAnimatedGeometryRoot->GetOffsetToCrossDoc(mContainerReferenceFrame);
2413 }
2414 }
2415
2416 int32_t maxLayers = nsDisplayItem::MaxActiveLayers();
2417 int layerCount = 0;
2418
2419 for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
2420 NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item),
2421 "items in a container layer should all have the same app units per dev pixel");
2422
2423 nsIntRect itemVisibleRect =
2424 ScaleToOutsidePixels(item->GetVisibleRect(), false);
2425 bool snap;
2426 nsRect itemContent = item->GetBounds(mBuilder, &snap);
2427 nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap);
2428 nsDisplayItem::Type itemType = item->GetType();
2429 nsIntRect clipRect;
2430 const DisplayItemClip& itemClip = item->GetClip();
2431 if (itemClip.HasClip()) {
2432 itemContent.IntersectRect(itemContent, itemClip.GetClipRect());
2433 clipRect = ScaleToNearestPixels(itemClip.GetClipRect());
2434 itemDrawRect.IntersectRect(itemDrawRect, clipRect);
2435 clipRect.MoveBy(mParameters.mOffset);
2436 }
2437 mBounds.UnionRect(mBounds, itemContent);
2438 itemVisibleRect.IntersectRect(itemVisibleRect, itemDrawRect);
2439
2440 LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
2441 if (layerState == LAYER_INACTIVE &&
2442 nsDisplayItem::ForceActiveLayers()) {
2443 layerState = LAYER_ACTIVE;
2444 }
2445
2446 bool forceInactive;
2447 const nsIFrame* animatedGeometryRoot;
2448 if (aFlags & NO_COMPONENT_ALPHA) {
2449 forceInactive = true;
2450 animatedGeometryRoot = lastAnimatedGeometryRoot;
2451 } else {
2452 forceInactive = false;
2453 if (mManager->IsWidgetLayerManager()) {
2454 animatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder);
2455 } else {
2456 // For inactive layer subtrees, splitting content into ThebesLayers
2457 // based on animated geometry roots is pointless. It's more efficient
2458 // to build the minimum number of layers.
2459 animatedGeometryRoot = mContainerAnimatedGeometryRoot;
2460 }
2461 if (animatedGeometryRoot != lastAnimatedGeometryRoot) {
2462 lastAnimatedGeometryRoot = animatedGeometryRoot;
2463 topLeft = animatedGeometryRoot->GetOffsetToCrossDoc(mContainerReferenceFrame);
2464 }
2465 }
2466 bool shouldFixToViewport = !animatedGeometryRoot->GetParent() &&
2467 item->ShouldFixToViewport(mBuilder);
2468
2469 if (maxLayers != -1 && layerCount >= maxLayers) {
2470 forceInactive = true;
2471 }
2472
2473 // Assign the item to a layer
2474 if (layerState == LAYER_ACTIVE_FORCE ||
2475 (layerState == LAYER_INACTIVE && !mManager->IsWidgetLayerManager()) ||
2476 (!forceInactive &&
2477 (layerState == LAYER_ACTIVE_EMPTY ||
2478 layerState == LAYER_ACTIVE))) {
2479
2480 layerCount++;
2481
2482 // LAYER_ACTIVE_EMPTY means the layer is created just for its metadata.
2483 // We should never see an empty layer with any visible content!
2484 NS_ASSERTION(layerState != LAYER_ACTIVE_EMPTY ||
2485 itemVisibleRect.IsEmpty(),
2486 "State is LAYER_ACTIVE_EMPTY but visible rect is not.");
2487
2488 // As long as the new layer isn't going to be a ThebesLayer,
2489 // InvalidateForLayerChange doesn't need the new layer pointer.
2490 // We also need to check the old data now, because BuildLayer
2491 // can overwrite it.
2492 InvalidateForLayerChange(item, nullptr, itemClip, topLeft, nullptr);
2493
2494 // If the item would have its own layer but is invisible, just hide it.
2495 // Note that items without their own layers can't be skipped this
2496 // way, since their ThebesLayer may decide it wants to draw them
2497 // into its buffer even if they're currently covered.
2498 if (itemVisibleRect.IsEmpty() &&
2499 !item->ShouldBuildLayerEvenIfInvisible(mBuilder)) {
2500 continue;
2501 }
2502
2503 if (itemType == nsDisplayItem::TYPE_TRANSFORM) {
2504 mParameters.mAncestorClipRect = itemClip.HasClip() ? &clipRect : nullptr;
2505 } else {
2506 mParameters.mAncestorClipRect = nullptr;
2507 }
2508
2509 // Just use its layer.
2510 nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
2511 if (!ownLayer) {
2512 continue;
2513 }
2514
2515 NS_ASSERTION(!ownLayer->AsThebesLayer(),
2516 "Should never have created a dedicated Thebes layer!");
2517
2518 const nsIFrame* fixedPosFrame =
2519 FindFixedPosFrameForLayerData(animatedGeometryRoot, shouldFixToViewport);
2520 if (fixedPosFrame) {
2521 nsIntRegion visibleRegion(itemVisibleRect);
2522 AdjustLayerDataForFixedPositioning(fixedPosFrame,
2523 nsIntRegion(itemDrawRect), &visibleRegion);
2524 itemVisibleRect = visibleRegion.GetBounds();
2525 }
2526 SetFixedPositionLayerData(ownLayer, fixedPosFrame);
2527
2528 nsRect invalid;
2529 if (item->IsInvalid(invalid)) {
2530 ownLayer->SetInvalidRectToVisibleRegion();
2531 }
2532
2533 // If it's not a ContainerLayer, we need to apply the scale transform
2534 // ourselves.
2535 if (!ownLayer->AsContainerLayer()) {
2536 ownLayer->SetPostScale(mParameters.mXScale,
2537 mParameters.mYScale);
2538 }
2539
2540 // Update that layer's clip and visible rects.
2541 NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager");
2542 NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData),
2543 "We shouldn't have a FrameLayerBuilder-managed layer here!");
2544 NS_ASSERTION(itemClip.HasClip() ||
2545 itemClip.GetRoundedRectCount() == 0,
2546 "If we have rounded rects, we must have a clip rect");
2547 // It has its own layer. Update that layer's clip and visible rects.
2548 if (itemClip.HasClip()) {
2549 ownLayer->SetClipRect(&clipRect);
2550 } else {
2551 ownLayer->SetClipRect(nullptr);
2552 }
2553 ThebesLayerData* data = GetTopThebesLayerData();
2554 if (data) {
2555 // Prerendered transform items can be updated without layer building
2556 // (async animations or an empty transaction), so we treat all other
2557 // content as being above this so that the transformed layer can correctly
2558 // move behind other content.
2559 if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
2560 nsDisplayTransform::ShouldPrerenderTransformedContent(mBuilder,
2561 item->Frame(),
2562 false)) {
2563 data->SetAllDrawingAbove();
2564 } else {
2565 data->AddVisibleAboveRegion(itemVisibleRect);
2566
2567 // Add the entire bounds rect to the mDrawAboveRegion.
2568 // The visible region may be excluding opaque content above the
2569 // item, and we need to ensure that that content is not placed
2570 // in a ThebesLayer below the item!
2571 data->AddDrawAboveRegion(itemDrawRect);
2572 }
2573 }
2574 itemVisibleRect.MoveBy(mParameters.mOffset);
2575 if (item->SetVisibleRegionOnLayer()) {
2576 SetVisibleRegionForLayer(ownLayer, ownLayer->GetVisibleRegion(), itemVisibleRect);
2577 }
2578
2579 // rounded rectangle clipping using mask layers
2580 // (must be done after visible rect is set on layer)
2581 if (itemClip.IsRectClippedByRoundedCorner(itemContent)) {
2582 SetupMaskLayer(ownLayer, itemClip);
2583 }
2584
2585 ContainerLayer* oldContainer = ownLayer->GetParent();
2586 if (oldContainer && oldContainer != mContainerLayer) {
2587 oldContainer->RemoveChild(ownLayer);
2588 }
2589 NS_ASSERTION(!mNewChildLayers.Contains(ownLayer),
2590 "Layer already in list???");
2591
2592 mNewChildLayers.AppendElement(ownLayer);
2593
2594 /**
2595 * No need to allocate geometry for items that aren't
2596 * part of a ThebesLayer.
2597 */
2598 nsAutoPtr<nsDisplayItemGeometry> dummy;
2599 mLayerBuilder->AddLayerDisplayItem(ownLayer, item,
2600 itemClip, layerState,
2601 topLeft, nullptr,
2602 dummy);
2603 } else {
2604 ThebesLayerData* data =
2605 FindThebesLayerFor(item, itemVisibleRect, animatedGeometryRoot, topLeft,
2606 shouldFixToViewport);
2607
2608 if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
2609 nsDisplayLayerEventRegions* eventRegions =
2610 static_cast<nsDisplayLayerEventRegions*>(item);
2611 data->AccumulateEventRegions(ScaleRegionToOutsidePixels(eventRegions->HitRegion()),
2612 ScaleRegionToOutsidePixels(eventRegions->MaybeHitRegion()),
2613 ScaleRegionToOutsidePixels(eventRegions->DispatchToContentHitRegion()));
2614 } else {
2615 // check to see if the new item has rounded rect clips in common with
2616 // other items in the layer
2617 data->UpdateCommonClipCount(itemClip);
2618 data->Accumulate(this, item, itemVisibleRect, itemDrawRect, itemClip);
2619
2620 nsAutoPtr<nsDisplayItemGeometry> geometry(item->AllocateGeometry(mBuilder));
2621 InvalidateForLayerChange(item, data->mLayer, itemClip, topLeft, geometry);
2622
2623 mLayerBuilder->AddThebesDisplayItem(data, item, itemClip,
2624 mContainerFrame,
2625 layerState, topLeft,
2626 geometry);
2627 }
2628 }
2629 }
2630 }
2631
2632 void
2633 ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem,
2634 Layer* aNewLayer,
2635 const DisplayItemClip& aClip,
2636 const nsPoint& aTopLeft,
2637 nsDisplayItemGeometry *aGeometry)
2638 {
2639 NS_ASSERTION(aItem->GetPerFrameKey(),
2640 "Display items that render using Thebes must have a key");
2641 nsDisplayItemGeometry *oldGeometry = nullptr;
2642 DisplayItemClip* oldClip = nullptr;
2643 nsAutoTArray<nsIFrame*,4> changedFrames;
2644 bool isInvalid = false;
2645 Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem, &oldGeometry, &oldClip, &changedFrames, &isInvalid);
2646 if (aNewLayer != oldLayer && oldLayer) {
2647 // The item has changed layers.
2648 // Invalidate the old bounds in the old layer and new bounds in the new layer.
2649 ThebesLayer* t = oldLayer->AsThebesLayer();
2650 if (t) {
2651 // Note that whenever the layer's scale changes, we invalidate the whole thing,
2652 // so it doesn't matter whether we are using the old scale at last paint
2653 // or a new scale here
2654 #ifdef MOZ_DUMP_PAINTING
2655 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2656 printf_stderr("Display item type %s(%p) changed layers %p to %p!\n", aItem->Name(), aItem->Frame(), t, aNewLayer);
2657 }
2658 #endif
2659 InvalidatePostTransformRegion(t,
2660 oldGeometry->ComputeInvalidationRegion(),
2661 *oldClip,
2662 mLayerBuilder->GetLastPaintOffset(t));
2663 }
2664 if (aNewLayer) {
2665 ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer();
2666 if (newThebesLayer) {
2667 InvalidatePostTransformRegion(newThebesLayer,
2668 aGeometry->ComputeInvalidationRegion(),
2669 aClip,
2670 GetTranslationForThebesLayer(newThebesLayer));
2671 }
2672 }
2673 aItem->NotifyRenderingChanged();
2674 return;
2675 }
2676 if (!aNewLayer) {
2677 return;
2678 }
2679
2680 ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer();
2681 if (!newThebesLayer) {
2682 return;
2683 }
2684
2685 ThebesDisplayItemLayerUserData* data =
2686 static_cast<ThebesDisplayItemLayerUserData*>(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData));
2687 // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to
2688 // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas.
2689 // If we do get an invalid rect, then we want to add this on top of the change areas.
2690 nsRect invalid;
2691 nsRegion combined;
2692 nsPoint shift = aTopLeft - data->mLastAnimatedGeometryRootOrigin;
2693 bool notifyRenderingChanged = true;
2694 if (!oldLayer) {
2695 // This item is being added for the first time, invalidate its entire area.
2696 //TODO: We call GetGeometry again in AddThebesDisplayItem, we should reuse this.
2697 combined = aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion());
2698 #ifdef MOZ_DUMP_PAINTING
2699 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2700 printf_stderr("Display item type %s(%p) added to layer %p!\n", aItem->Name(), aItem->Frame(), aNewLayer);
2701 }
2702 #endif
2703 } else if (isInvalid || (aItem->IsInvalid(invalid) && invalid.IsEmpty())) {
2704 // Either layout marked item as needing repainting, invalidate the entire old and new areas.
2705 combined = oldClip->ApplyNonRoundedIntersection(oldGeometry->ComputeInvalidationRegion());
2706 combined.MoveBy(shift);
2707 combined.Or(combined, aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion()));
2708 #ifdef MOZ_DUMP_PAINTING
2709 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2710 printf_stderr("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", aItem->Name(), aItem->Frame(), aNewLayer);
2711 }
2712 #endif
2713 } else {
2714 // Let the display item check for geometry changes and decide what needs to be
2715 // repainted.
2716
2717 // We have an optimization to cache the drawing background-attachment: fixed canvas
2718 // background images so we can scroll and just blit them when they are flattened into
2719 // the same layer as scrolling content. NotifyRenderingChanged is only used to tell
2720 // the canvas bg image item to purge this cache. We want to be careful not to accidentally
2721 // purge the cache if we are just invalidating due to scrolling (ie the background image
2722 // moves on the scrolling layer but it's rendering stays the same) so if
2723 // AddOffsetAndComputeDifference is the only thing that will invalidate we skip the
2724 // NotifyRenderingChanged call (ComputeInvalidationRegion for background images also calls
2725 // NotifyRenderingChanged if anything changes).
2726 if (oldGeometry->ComputeInvalidationRegion() == aGeometry->ComputeInvalidationRegion() &&
2727 *oldClip == aClip && invalid.IsEmpty() && changedFrames.Length() == 0) {
2728 notifyRenderingChanged = false;
2729 }
2730
2731 oldGeometry->MoveBy(shift);
2732 aItem->ComputeInvalidationRegion(mBuilder, oldGeometry, &combined);
2733 oldClip->AddOffsetAndComputeDifference(shift, oldGeometry->ComputeInvalidationRegion(),
2734 aClip, aGeometry->ComputeInvalidationRegion(),
2735 &combined);
2736
2737 // Add in any rect that the frame specified
2738 combined.Or(combined, invalid);
2739
2740 for (uint32_t i = 0; i < changedFrames.Length(); i++) {
2741 combined.Or(combined, changedFrames[i]->GetVisualOverflowRect());
2742 }
2743
2744 // Restrict invalidation to the clipped region
2745 nsRegion clip;
2746 if (aClip.ComputeRegionInClips(oldClip, shift, &clip)) {
2747 combined.And(combined, clip);
2748 }
2749 #ifdef MOZ_DUMP_PAINTING
2750 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2751 if (!combined.IsEmpty()) {
2752 printf_stderr("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), aItem->Frame(), aNewLayer);
2753 }
2754 }
2755 #endif
2756 }
2757 if (!combined.IsEmpty()) {
2758 if (notifyRenderingChanged) {
2759 aItem->NotifyRenderingChanged();
2760 }
2761 InvalidatePostTransformRegion(newThebesLayer,
2762 combined.ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel),
2763 GetTranslationForThebesLayer(newThebesLayer));
2764 }
2765 }
2766
2767 void
2768 FrameLayerBuilder::AddThebesDisplayItem(ThebesLayerData* aLayerData,
2769 nsDisplayItem* aItem,
2770 const DisplayItemClip& aClip,
2771 nsIFrame* aContainerLayerFrame,
2772 LayerState aLayerState,
2773 const nsPoint& aTopLeft,
2774 nsAutoPtr<nsDisplayItemGeometry> aGeometry)
2775 {
2776 ThebesLayer* layer = aLayerData->mLayer;
2777 ThebesDisplayItemLayerUserData* thebesData =
2778 static_cast<ThebesDisplayItemLayerUserData*>
2779 (layer->GetUserData(&gThebesDisplayItemLayerUserData));
2780 nsRefPtr<BasicLayerManager> tempManager;
2781 nsIntRect intClip;
2782 bool hasClip = false;
2783 if (aLayerState != LAYER_NONE) {
2784 DisplayItemData *data = GetDisplayItemDataForManager(aItem, layer->Manager());
2785 if (data) {
2786 tempManager = data->mInactiveManager;
2787 }
2788 if (!tempManager) {
2789 tempManager = new BasicLayerManager();
2790 }
2791
2792 // We need to grab these before calling AddLayerDisplayItem because it will overwrite them.
2793 nsRegion clip;
2794 DisplayItemClip* oldClip = nullptr;
2795 GetOldLayerFor(aItem, nullptr, &oldClip);
2796 hasClip = aClip.ComputeRegionInClips(oldClip,
2797 aTopLeft - thebesData->mLastAnimatedGeometryRootOrigin,
2798 &clip);
2799
2800 if (hasClip) {
2801 intClip = clip.GetBounds().ScaleToOutsidePixels(thebesData->mXScale,
2802 thebesData->mYScale,
2803 thebesData->mAppUnitsPerDevPixel);
2804 }
2805 }
2806
2807 AddLayerDisplayItem(layer, aItem, aClip, aLayerState, aTopLeft, tempManager, aGeometry);
2808
2809 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(layer);
2810 if (entry) {
2811 entry->mContainerLayerFrame = aContainerLayerFrame;
2812 if (entry->mContainerLayerGeneration == 0) {
2813 entry->mContainerLayerGeneration = mContainerLayerGeneration;
2814 }
2815 if (tempManager) {
2816 FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
2817 layerBuilder->Init(mDisplayListBuilder, tempManager, aLayerData);
2818
2819 tempManager->BeginTransaction();
2820 if (mRetainingManager) {
2821 layerBuilder->DidBeginRetainedLayerTransaction(tempManager);
2822 }
2823
2824 nsAutoPtr<LayerProperties> props(LayerProperties::CloneFrom(tempManager->GetRoot()));
2825 nsRefPtr<Layer> tmpLayer =
2826 aItem->BuildLayer(mDisplayListBuilder, tempManager, ContainerLayerParameters());
2827 // We have no easy way of detecting if this transaction will ever actually get finished.
2828 // For now, I've just silenced the warning with nested transactions in BasicLayers.cpp
2829 if (!tmpLayer) {
2830 tempManager->EndTransaction(nullptr, nullptr);
2831 tempManager->SetUserData(&gLayerManagerLayerBuilder, nullptr);
2832 return;
2833 }
2834
2835 // If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been
2836 // stored in layerBuilder. Manually add it now.
2837 if (mRetainingManager) {
2838 #ifdef DEBUG_DISPLAY_ITEM_DATA
2839 LayerManagerData* parentLmd = static_cast<LayerManagerData*>
2840 (layer->Manager()->GetUserData(&gLayerManagerUserData));
2841 LayerManagerData* lmd = static_cast<LayerManagerData*>
2842 (tempManager->GetUserData(&gLayerManagerUserData));
2843 lmd->mParent = parentLmd;
2844 #endif
2845 layerBuilder->StoreDataForFrame(aItem, tmpLayer, LAYER_ACTIVE);
2846 }
2847
2848 tempManager->SetRoot(tmpLayer);
2849 layerBuilder->WillEndTransaction();
2850 tempManager->AbortTransaction();
2851
2852 nsIntPoint offset = GetLastPaintOffset(layer) - GetTranslationForThebesLayer(layer);
2853 props->MoveBy(-offset);
2854 nsIntRegion invalid = props->ComputeDifferences(tmpLayer, nullptr);
2855 if (aLayerState == LAYER_SVG_EFFECTS) {
2856 invalid = nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(aItem->Frame(),
2857 aItem->ToReferenceFrame(),
2858 invalid);
2859 }
2860 if (!invalid.IsEmpty()) {
2861 #ifdef MOZ_DUMP_PAINTING
2862 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2863 printf_stderr("Inactive LayerManager(%p) for display item %s(%p) has an invalid region - invalidating layer %p\n", tempManager.get(), aItem->Name(), aItem->Frame(), layer);
2864 }
2865 #endif
2866 if (hasClip) {
2867 invalid.And(invalid, intClip);
2868 }
2869
2870 invalid.ScaleRoundOut(thebesData->mXScale, thebesData->mYScale);
2871 InvalidatePostTransformRegion(layer, invalid,
2872 GetTranslationForThebesLayer(layer));
2873 }
2874 }
2875 ClippedDisplayItem* cdi =
2876 entry->mItems.AppendElement(ClippedDisplayItem(aItem,
2877 mContainerLayerGeneration));
2878 cdi->mInactiveLayerManager = tempManager;
2879 }
2880 }
2881
2882 FrameLayerBuilder::DisplayItemData*
2883 FrameLayerBuilder::StoreDataForFrame(nsDisplayItem* aItem, Layer* aLayer, LayerState aState)
2884 {
2885 DisplayItemData* oldData = GetDisplayItemDataForManager(aItem, mRetainingManager);
2886 if (oldData) {
2887 if (!oldData->mUsed) {
2888 oldData->UpdateContents(aLayer, aState, mContainerLayerGeneration, aItem);
2889 }
2890 return oldData;
2891 }
2892
2893 LayerManagerData* lmd = static_cast<LayerManagerData*>
2894 (mRetainingManager->GetUserData(&gLayerManagerUserData));
2895
2896 nsRefPtr<DisplayItemData> data =
2897 new DisplayItemData(lmd, aItem->GetPerFrameKey(),
2898 aLayer, aState, mContainerLayerGeneration);
2899
2900 data->AddFrame(aItem->Frame());
2901
2902 nsAutoTArray<nsIFrame*,4> mergedFrames;
2903 aItem->GetMergedFrames(&mergedFrames);
2904
2905 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) {
2906 data->AddFrame(mergedFrames[i]);
2907 }
2908
2909 lmd->mDisplayItems.PutEntry(data);
2910 return data;
2911 }
2912
2913 void
2914 FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame,
2915 uint32_t aDisplayItemKey,
2916 Layer* aLayer,
2917 LayerState aState)
2918 {
2919 DisplayItemData* oldData = GetDisplayItemData(aFrame, aDisplayItemKey);
2920 if (oldData && oldData->mFrameList.Length() == 1) {
2921 oldData->UpdateContents(aLayer, aState, mContainerLayerGeneration);
2922 return;
2923 }
2924
2925 LayerManagerData* lmd = static_cast<LayerManagerData*>
2926 (mRetainingManager->GetUserData(&gLayerManagerUserData));
2927
2928 nsRefPtr<DisplayItemData> data =
2929 new DisplayItemData(lmd, aDisplayItemKey, aLayer,
2930 aState, mContainerLayerGeneration);
2931
2932 data->AddFrame(aFrame);
2933
2934 lmd->mDisplayItems.PutEntry(data);
2935 }
2936
2937 FrameLayerBuilder::ClippedDisplayItem::~ClippedDisplayItem()
2938 {
2939 if (mInactiveLayerManager) {
2940 BasicLayerManager* basic = static_cast<BasicLayerManager*>(mInactiveLayerManager.get());
2941 basic->SetUserData(&gLayerManagerLayerBuilder, nullptr);
2942 }
2943 }
2944
2945 void
2946 FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
2947 nsDisplayItem* aItem,
2948 const DisplayItemClip& aClip,
2949 LayerState aLayerState,
2950 const nsPoint& aTopLeft,
2951 BasicLayerManager* aManager,
2952 nsAutoPtr<nsDisplayItemGeometry> aGeometry)
2953 {
2954 if (aLayer->Manager() != mRetainingManager)
2955 return;
2956
2957 DisplayItemData *data = StoreDataForFrame(aItem, aLayer, aLayerState);
2958 ThebesLayer *t = aLayer->AsThebesLayer();
2959 if (t) {
2960 data->mGeometry = aGeometry;
2961 data->mClip = aClip;
2962 }
2963 data->mInactiveManager = aManager;
2964 }
2965
2966 nsIntPoint
2967 FrameLayerBuilder::GetLastPaintOffset(ThebesLayer* aLayer)
2968 {
2969 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
2970 if (entry) {
2971 if (entry->mContainerLayerGeneration == 0) {
2972 entry->mContainerLayerGeneration = mContainerLayerGeneration;
2973 }
2974 if (entry->mHasExplicitLastPaintOffset)
2975 return entry->mLastPaintOffset;
2976 }
2977 return GetTranslationForThebesLayer(aLayer);
2978 }
2979
2980 void
2981 FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer* aLayer)
2982 {
2983 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
2984 if (entry) {
2985 if (entry->mContainerLayerGeneration == 0) {
2986 entry->mContainerLayerGeneration = mContainerLayerGeneration;
2987 }
2988 entry->mLastPaintOffset = GetTranslationForThebesLayer(aLayer);
2989 entry->mHasExplicitLastPaintOffset = true;
2990 }
2991 }
2992
2993 bool
2994 FrameLayerBuilder::CheckInLayerTreeCompressionMode()
2995 {
2996 if (mInLayerTreeCompressionMode) {
2997 return true;
2998 }
2999
3000 // If we wanted to be in layer tree compression mode, but weren't, then scheduled
3001 // a delayed repaint where we will be.
3002 mRootPresContext->PresShell()->GetRootFrame()->SchedulePaint(nsIFrame::PAINT_DELAYED_COMPRESS);
3003
3004 return false;
3005 }
3006
3007 void
3008 ContainerState::CollectOldLayers()
3009 {
3010 for (Layer* layer = mContainerLayer->GetFirstChild(); layer;
3011 layer = layer->GetNextSibling()) {
3012 NS_ASSERTION(!layer->HasUserData(&gMaskLayerUserData),
3013 "Mask layer in layer tree; could not be recycled.");
3014 if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
3015 NS_ASSERTION(layer->AsThebesLayer(), "Wrong layer type");
3016 mRecycledThebesLayers.AppendElement(static_cast<ThebesLayer*>(layer));
3017 }
3018
3019 if (Layer* maskLayer = layer->GetMaskLayer()) {
3020 NS_ASSERTION(maskLayer->GetType() == Layer::TYPE_IMAGE,
3021 "Could not recycle mask layer, unsupported layer type.");
3022 mRecycledMaskImageLayers.Put(layer, static_cast<ImageLayer*>(maskLayer));
3023 }
3024 }
3025 }
3026
3027 void
3028 ContainerState::Finish(uint32_t* aTextContentFlags, LayerManagerData* aData)
3029 {
3030 while (!mThebesLayerDataStack.IsEmpty()) {
3031 PopThebesLayerData();
3032 }
3033
3034 uint32_t textContentFlags = 0;
3035
3036 // Make sure that current/existing layers are added to the parent and are
3037 // in the correct order.
3038 Layer* layer = nullptr;
3039 for (uint32_t i = 0; i < mNewChildLayers.Length(); ++i) {
3040 Layer* prevChild = i == 0 ? nullptr : mNewChildLayers[i - 1].get();
3041 layer = mNewChildLayers[i];
3042
3043 if (!layer->GetVisibleRegion().IsEmpty()) {
3044 textContentFlags |= layer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA;
3045 }
3046
3047 if (!layer->GetParent()) {
3048 // This is not currently a child of the container, so just add it
3049 // now.
3050 mContainerLayer->InsertAfter(layer, prevChild);
3051 continue;
3052 }
3053
3054 NS_ASSERTION(layer->GetParent() == mContainerLayer,
3055 "Layer shouldn't be the child of some other container");
3056 if (layer->GetPrevSibling() != prevChild) {
3057 mContainerLayer->RepositionChild(layer, prevChild);
3058 }
3059 }
3060
3061 // Remove old layers that have become unused.
3062 if (!layer) {
3063 layer = mContainerLayer->GetFirstChild();
3064 } else {
3065 layer = layer->GetNextSibling();
3066 }
3067 while (layer) {
3068 Layer *layerToRemove = layer;
3069 layer = layer->GetNextSibling();
3070 mContainerLayer->RemoveChild(layerToRemove);
3071 }
3072
3073 *aTextContentFlags = textContentFlags;
3074 }
3075
3076 static inline gfxSize RoundToFloatPrecision(const gfxSize& aSize)
3077 {
3078 return gfxSize(float(aSize.width), float(aSize.height));
3079 }
3080
3081 static bool
3082 ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
3083 nsDisplayListBuilder* aDisplayListBuilder,
3084 nsIFrame* aContainerFrame,
3085 const gfx3DMatrix* aTransform,
3086 const ContainerLayerParameters& aIncomingScale,
3087 ContainerLayer* aLayer,
3088 LayerState aState,
3089 ContainerLayerParameters& aOutgoingScale)
3090 {
3091 nsIntPoint offset;
3092
3093 gfx3DMatrix transform =
3094 gfx3DMatrix::ScalingMatrix(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
3095 if (aTransform) {
3096 // aTransform is applied first, then the scale is applied to the result
3097 transform = (*aTransform)*transform;
3098 // Set any matrix entries close to integers to be those exact integers.
3099 // This protects against floating-point inaccuracies causing problems
3100 // in the checks below.
3101 transform.NudgeToIntegers();
3102 }
3103 gfxMatrix transform2d;
3104 if (aContainerFrame &&
3105 (aState == LAYER_INACTIVE || aState == LAYER_SVG_EFFECTS) &&
3106 (!aTransform || (aTransform->Is2D(&transform2d) &&
3107 !transform2d.HasNonTranslation()))) {
3108 // When we have an inactive ContainerLayer, translate the container by the offset to the
3109 // reference frame (and offset all child layers by the reverse) so that the coordinate
3110 // space of the child layers isn't affected by scrolling.
3111 // This gets confusing for complicated transform (since we'd have to compute the scale
3112 // factors for the matrix), so we don't bother. Any frames that are building an nsDisplayTransform
3113 // for a css transform would have 0,0 as their offset to the reference frame, so this doesn't
3114 // matter.
3115 nsPoint appUnitOffset = aDisplayListBuilder->ToReferenceFrame(aContainerFrame);
3116 nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
3117 offset = nsIntPoint(
3118 NS_lround(NSAppUnitsToDoublePixels(appUnitOffset.x, appUnitsPerDevPixel)*aIncomingScale.mXScale),
3119 NS_lround(NSAppUnitsToDoublePixels(appUnitOffset.y, appUnitsPerDevPixel)*aIncomingScale.mYScale));
3120 }
3121 transform = transform * gfx3DMatrix::Translation(offset.x + aIncomingScale.mOffset.x, offset.y + aIncomingScale.mOffset.y, 0);
3122
3123 if (transform.IsSingular()) {
3124 return false;
3125 }
3126
3127 bool canDraw2D = transform.CanDraw2D(&transform2d);
3128 gfxSize scale;
3129 // XXX Should we do something for 3D transforms?
3130 if (canDraw2D) {
3131 // If the container's transform is animated off main thread, fix a suitable scale size
3132 // for animation
3133 if (aContainerFrame->GetContent() &&
3134 nsLayoutUtils::HasAnimationsForCompositor(
3135 aContainerFrame->GetContent(), eCSSProperty_transform)) {
3136 scale = nsLayoutUtils::ComputeSuitableScaleForAnimation(aContainerFrame->GetContent());
3137 } else {
3138 // Scale factors are normalized to a power of 2 to reduce the number of resolution changes
3139 scale = RoundToFloatPrecision(transform2d.ScaleFactors(true));
3140 // For frames with a changing transform that's not just a translation,
3141 // round scale factors up to nearest power-of-2 boundary so that we don't
3142 // keep having to redraw the content as it scales up and down. Rounding up to nearest
3143 // power-of-2 boundary ensures we never scale up, only down --- avoiding
3144 // jaggies. It also ensures we never scale down by more than a factor of 2,
3145 // avoiding bad downscaling quality.
3146 gfxMatrix frameTransform;
3147 if (ActiveLayerTracker::IsStyleAnimated(aContainerFrame, eCSSProperty_transform) &&
3148 aTransform &&
3149 (!aTransform->Is2D(&frameTransform) || frameTransform.HasNonTranslationOrFlip())) {
3150 // Don't clamp the scale factor when the new desired scale factor matches the old one
3151 // or it was previously unscaled.
3152 bool clamp = true;
3153 Matrix oldFrameTransform2d;
3154 if (aLayer->GetBaseTransform().Is2D(&oldFrameTransform2d)) {
3155 gfxSize oldScale = RoundToFloatPrecision(ThebesMatrix(oldFrameTransform2d).ScaleFactors(true));
3156 if (oldScale == scale || oldScale == gfxSize(1.0, 1.0)) {
3157 clamp = false;
3158 }
3159 }
3160 if (clamp) {
3161 scale.width = gfxUtils::ClampToScaleFactor(scale.width);
3162 scale.height = gfxUtils::ClampToScaleFactor(scale.height);
3163 }
3164 } else {
3165 // XXX Do we need to move nearly-integer values to integers here?
3166 }
3167 }
3168 // If the scale factors are too small, just use 1.0. The content is being
3169 // scaled out of sight anyway.
3170 if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) {
3171 scale = gfxSize(1.0, 1.0);
3172 }
3173 } else {
3174 scale = gfxSize(1.0, 1.0);
3175 }
3176
3177 // Store the inverse of our resolution-scale on the layer
3178 Matrix4x4 baseTransform;
3179 ToMatrix4x4(transform, baseTransform);
3180 aLayer->SetBaseTransform(baseTransform);
3181 aLayer->SetPreScale(1.0f/float(scale.width),
3182 1.0f/float(scale.height));
3183 aLayer->SetInheritedScale(aIncomingScale.mXScale,
3184 aIncomingScale.mYScale);
3185
3186 aOutgoingScale =
3187 ContainerLayerParameters(scale.width, scale.height, -offset, aIncomingScale);
3188 if (aTransform) {
3189 aOutgoingScale.mInTransformedSubtree = true;
3190 if (ActiveLayerTracker::IsStyleAnimated(aContainerFrame, eCSSProperty_transform)) {
3191 aOutgoingScale.mInActiveTransformedSubtree = true;
3192 }
3193 }
3194 bool isRetained = aLayer->Manager()->IsWidgetLayerManager();
3195 if (isRetained && (!canDraw2D || transform2d.HasNonIntegerTranslation())) {
3196 aOutgoingScale.mDisableSubpixelAntialiasingInDescendants = true;
3197 }
3198 return true;
3199 }
3200
3201 /* static */ PLDHashOperator
3202 FrameLayerBuilder::RestoreDisplayItemData(nsRefPtrHashKey<DisplayItemData>* aEntry, void* aUserArg)
3203 {
3204 DisplayItemData* data = aEntry->GetKey();
3205 uint32_t *generation = static_cast<uint32_t*>(aUserArg);
3206
3207 if (data->mUsed && data->mContainerLayerGeneration >= *generation) {
3208 return PL_DHASH_REMOVE;
3209 }
3210
3211 return PL_DHASH_NEXT;
3212 }
3213
3214 /* static */ PLDHashOperator
3215 FrameLayerBuilder::RestoreThebesLayerItemEntries(ThebesLayerItemsEntry* aEntry, void* aUserArg)
3216 {
3217 uint32_t *generation = static_cast<uint32_t*>(aUserArg);
3218
3219 if (aEntry->mContainerLayerGeneration >= *generation) {
3220 // We can just remove these items rather than attempting to revert them
3221 // because we're going to want to invalidate everything when transitioning
3222 // to component alpha flattening.
3223 return PL_DHASH_REMOVE;
3224 }
3225
3226 for (uint32_t i = 0; i < aEntry->mItems.Length(); i++) {
3227 if (aEntry->mItems[i].mContainerLayerGeneration >= *generation) {
3228 aEntry->mItems.TruncateLength(i);
3229 return PL_DHASH_NEXT;
3230 }
3231 }
3232
3233 return PL_DHASH_NEXT;
3234 }
3235
3236 already_AddRefed<ContainerLayer>
3237 FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
3238 LayerManager* aManager,
3239 nsIFrame* aContainerFrame,
3240 nsDisplayItem* aContainerItem,
3241 const nsDisplayList& aChildren,
3242 const ContainerLayerParameters& aParameters,
3243 const gfx3DMatrix* aTransform,
3244 uint32_t aFlags)
3245 {
3246 uint32_t containerDisplayItemKey =
3247 aContainerItem ? aContainerItem->GetPerFrameKey() : nsDisplayItem::TYPE_ZERO;
3248 NS_ASSERTION(aContainerFrame, "Container display items here should have a frame");
3249 NS_ASSERTION(!aContainerItem ||
3250 aContainerItem->Frame() == aContainerFrame,
3251 "Container display item must match given frame");
3252
3253 if (!aParameters.mXScale || !aParameters.mYScale) {
3254 return nullptr;
3255 }
3256
3257 nsRefPtr<ContainerLayer> containerLayer;
3258 if (aManager == mRetainingManager) {
3259 // Using GetOldLayerFor will search merged frames, as well as the underlying
3260 // frame. The underlying frame can change when a page scrolls, so this
3261 // avoids layer recreation in the situation that a new underlying frame is
3262 // picked for a layer.
3263 Layer* oldLayer = nullptr;
3264 if (aContainerItem) {
3265 oldLayer = GetOldLayerFor(aContainerItem);
3266 } else {
3267 DisplayItemData *data = GetOldLayerForFrame(aContainerFrame, containerDisplayItemKey);
3268 if (data) {
3269 oldLayer = data->mLayer;
3270 }
3271 }
3272
3273 if (oldLayer) {
3274 NS_ASSERTION(oldLayer->Manager() == aManager, "Wrong manager");
3275 if (oldLayer->HasUserData(&gThebesDisplayItemLayerUserData)) {
3276 // The old layer for this item is actually our ThebesLayer
3277 // because we rendered its layer into that ThebesLayer. So we
3278 // don't actually have a retained container layer.
3279 } else {
3280 NS_ASSERTION(oldLayer->GetType() == Layer::TYPE_CONTAINER,
3281 "Wrong layer type");
3282 containerLayer = static_cast<ContainerLayer*>(oldLayer);
3283 containerLayer->SetMaskLayer(nullptr);
3284 }
3285 }
3286 }
3287 if (!containerLayer) {
3288 // No suitable existing layer was found.
3289 containerLayer = aManager->CreateContainerLayer();
3290 if (!containerLayer)
3291 return nullptr;
3292 }
3293
3294 LayerState state = aContainerItem ? aContainerItem->GetLayerState(aBuilder, aManager, aParameters) : LAYER_ACTIVE;
3295 if (state == LAYER_INACTIVE &&
3296 nsDisplayItem::ForceActiveLayers()) {
3297 state = LAYER_ACTIVE;
3298 }
3299
3300 if (aContainerItem && state == LAYER_ACTIVE_EMPTY) {
3301 // Empty layers only have metadata and should never have display items. We
3302 // early exit because later, invalidation will walk up the frame tree to
3303 // determine which thebes layer gets invalidated. Since an empty layer
3304 // should never have anything to paint, it should never be invalidated.
3305 NS_ASSERTION(aChildren.IsEmpty(), "Should have no children");
3306 return containerLayer.forget();
3307 }
3308
3309 ContainerLayerParameters scaleParameters;
3310 if (!ChooseScaleAndSetTransform(this, aBuilder, aContainerFrame, aTransform, aParameters,
3311 containerLayer, state, scaleParameters)) {
3312 return nullptr;
3313 }
3314
3315 uint32_t oldGeneration = mContainerLayerGeneration;
3316 mContainerLayerGeneration = ++mMaxContainerLayerGeneration;
3317
3318 nsRefPtr<RefCountedRegion> thebesLayerInvalidRegion = nullptr;
3319 if (mRetainingManager) {
3320 if (aContainerItem) {
3321 StoreDataForFrame(aContainerItem, containerLayer, LAYER_ACTIVE);
3322 } else {
3323 StoreDataForFrame(aContainerFrame, containerDisplayItemKey, containerLayer, LAYER_ACTIVE);
3324 }
3325 }
3326
3327 LayerManagerData* data = static_cast<LayerManagerData*>
3328 (aManager->GetUserData(&gLayerManagerUserData));
3329
3330 nsRect bounds;
3331 nsIntRect pixBounds;
3332 int32_t appUnitsPerDevPixel;
3333 uint32_t stateFlags = 0;
3334 if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) &&
3335 mRetainingManager && !mRetainingManager->AreComponentAlphaLayersEnabled()) {
3336 stateFlags = ContainerState::NO_COMPONENT_ALPHA;
3337 }
3338 uint32_t flags;
3339 while (true) {
3340 ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(),
3341 aContainerFrame, aContainerItem,
3342 containerLayer, scaleParameters);
3343
3344 state.ProcessDisplayItems(aChildren, stateFlags);
3345
3346 // Set CONTENT_COMPONENT_ALPHA if any of our children have it.
3347 // This is suboptimal ... a child could have text that's over transparent
3348 // pixels in its own layer, but over opaque parts of previous siblings.
3349 state.Finish(&flags, data);
3350 bounds = state.GetChildrenBounds();
3351 pixBounds = state.ScaleToOutsidePixels(bounds, false);
3352 appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel();
3353
3354 if ((flags & Layer::CONTENT_COMPONENT_ALPHA) &&
3355 mRetainingManager &&
3356 !mRetainingManager->AreComponentAlphaLayersEnabled() &&
3357 !stateFlags) {
3358 // Since we don't want any component alpha layers on BasicLayers, we repeat
3359 // the layer building process with this explicitely forced off.
3360 // We restore the previous FrameLayerBuilder state since the first set
3361 // of layer building will have changed it.
3362 stateFlags = ContainerState::NO_COMPONENT_ALPHA;
3363 data->mDisplayItems.EnumerateEntries(RestoreDisplayItemData,
3364 &mContainerLayerGeneration);
3365 mThebesLayerItems.EnumerateEntries(RestoreThebesLayerItemEntries,
3366 &mContainerLayerGeneration);
3367 aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
3368 continue;
3369 }
3370 break;
3371 }
3372
3373 NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds");
3374 pixBounds.MoveBy(nsIntPoint(scaleParameters.mOffset.x, scaleParameters.mOffset.y));
3375 if (aParameters.mAncestorClipRect && !(aFlags & CONTAINER_NOT_CLIPPED_BY_ANCESTORS)) {
3376 SetVisibleRegionForLayer(containerLayer, nsIntRegion(pixBounds),
3377 *aParameters.mAncestorClipRect);
3378 } else {
3379 containerLayer->SetVisibleRegion(pixBounds);
3380 }
3381 // Make sure that rounding the visible region out didn't add any area
3382 // we won't paint
3383 if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) {
3384 bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale);
3385 if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) {
3386 // Clear CONTENT_COMPONENT_ALPHA
3387 flags = Layer::CONTENT_OPAQUE;
3388 }
3389 }
3390 containerLayer->SetContentFlags(flags);
3391
3392 mContainerLayerGeneration = oldGeneration;
3393 nsPresContext::ClearNotifySubDocInvalidationData(containerLayer);
3394
3395 return containerLayer.forget();
3396 }
3397
3398 Layer*
3399 FrameLayerBuilder::GetLeafLayerFor(nsDisplayListBuilder* aBuilder,
3400 nsDisplayItem* aItem)
3401 {
3402 Layer* layer = GetOldLayerFor(aItem);
3403 if (!layer)
3404 return nullptr;
3405 if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
3406 // This layer was created to render Thebes-rendered content for this
3407 // display item. The display item should not use it for its own
3408 // layer rendering.
3409 return nullptr;
3410 }
3411 layer->SetMaskLayer(nullptr);
3412 return layer;
3413 }
3414
3415 /* static */ void
3416 FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager)
3417 {
3418 LayerManagerData* data = static_cast<LayerManagerData*>
3419 (aManager->GetUserData(&gLayerManagerUserData));
3420 if (data) {
3421 data->mInvalidateAllLayers = true;
3422 }
3423 }
3424
3425 /* static */ void
3426 FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame *aFrame)
3427 {
3428 nsTArray<DisplayItemData*> *array =
3429 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
3430 if (array) {
3431 for (uint32_t i = 0; i < array->Length(); i++) {
3432 array->ElementAt(i)->mParent->mInvalidateAllLayers = true;
3433 }
3434 }
3435 }
3436
3437 /* static */
3438 Layer*
3439 FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, uint32_t aDisplayItemKey)
3440 {
3441 //TODO: This isn't completely correct, since a frame could exist as a layer
3442 // in the normal widget manager, and as a different layer (or no layer)
3443 // in the secondary manager
3444
3445 nsTArray<DisplayItemData*> *array =
3446 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
3447 if (array) {
3448 for (uint32_t i = 0; i < array->Length(); i++) {
3449 DisplayItemData *element = array->ElementAt(i);
3450 if (!element->mParent->mLayerManager->IsWidgetLayerManager()) {
3451 continue;
3452 }
3453 if (element->mDisplayItemKey == aDisplayItemKey) {
3454 if (element->mOptLayer) {
3455 return element->mOptLayer;
3456 }
3457
3458 Layer* layer = element->mLayer;
3459 if (!layer->HasUserData(&gColorLayerUserData) &&
3460 !layer->HasUserData(&gImageLayerUserData) &&
3461 !layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
3462 return layer;
3463 }
3464 }
3465 }
3466 }
3467 return nullptr;
3468 }
3469
3470 static gfxSize
3471 PredictScaleForContent(nsIFrame* aFrame, nsIFrame* aAncestorWithScale,
3472 const gfxSize& aScale)
3473 {
3474 gfx3DMatrix transform =
3475 gfx3DMatrix::ScalingMatrix(aScale.width, aScale.height, 1.0);
3476 if (aFrame != aAncestorWithScale) {
3477 // aTransform is applied first, then the scale is applied to the result
3478 transform = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestorWithScale)*transform;
3479 }
3480 gfxMatrix transform2d;
3481 if (transform.CanDraw2D(&transform2d)) {
3482 return transform2d.ScaleFactors(true);
3483 }
3484 return gfxSize(1.0, 1.0);
3485 }
3486
3487 gfxSize
3488 FrameLayerBuilder::GetThebesLayerScaleForFrame(nsIFrame* aFrame)
3489 {
3490 nsIFrame* last;
3491 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
3492 last = f;
3493
3494 if (nsLayoutUtils::IsPopup(f)) {
3495 // Don't examine ancestors of a popup. It won't make sense to check
3496 // the transform from some content inside the popup to some content
3497 // which is an ancestor of the popup.
3498 break;
3499 }
3500
3501 nsTArray<DisplayItemData*> *array =
3502 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
3503 if (!array) {
3504 continue;
3505 }
3506
3507 for (uint32_t i = 0; i < array->Length(); i++) {
3508 Layer* layer = array->ElementAt(i)->mLayer;
3509 ContainerLayer* container = layer->AsContainerLayer();
3510 if (!container ||
3511 !layer->Manager()->IsWidgetLayerManager()) {
3512 continue;
3513 }
3514 for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
3515 ThebesDisplayItemLayerUserData* data =
3516 static_cast<ThebesDisplayItemLayerUserData*>
3517 (l->GetUserData(&gThebesDisplayItemLayerUserData));
3518 if (data) {
3519 return PredictScaleForContent(aFrame, f, gfxSize(data->mXScale, data->mYScale));
3520 }
3521 }
3522 }
3523 }
3524
3525 return PredictScaleForContent(aFrame, last,
3526 last->PresContext()->PresShell()->GetResolution());
3527 }
3528
3529 #ifdef MOZ_DUMP_PAINTING
3530 static void DebugPaintItem(nsRenderingContext* aDest, nsDisplayItem *aItem, nsDisplayListBuilder* aBuilder)
3531 {
3532 bool snap;
3533 nsRect appUnitBounds = aItem->GetBounds(aBuilder, &snap);
3534 gfxRect bounds(appUnitBounds.x, appUnitBounds.y, appUnitBounds.width, appUnitBounds.height);
3535 bounds.ScaleInverse(aDest->AppUnitsPerDevPixel());
3536
3537 nsRefPtr<gfxASurface> surf =
3538 gfxPlatform::GetPlatform()->CreateOffscreenSurface(IntSize(bounds.width, bounds.height),
3539 gfxContentType::COLOR_ALPHA);
3540 surf->SetDeviceOffset(-bounds.TopLeft());
3541 nsRefPtr<gfxContext> context = new gfxContext(surf);
3542 nsRefPtr<nsRenderingContext> ctx = new nsRenderingContext();
3543 ctx->Init(aDest->DeviceContext(), context);
3544
3545 aItem->Paint(aBuilder, ctx);
3546 DumpPaintedImage(aItem, surf);
3547 aItem->SetPainted();
3548
3549 surf->SetDeviceOffset(gfxPoint(0, 0));
3550 aDest->ThebesContext()->SetSource(surf, bounds.TopLeft());
3551 aDest->ThebesContext()->Rectangle(bounds);
3552 aDest->ThebesContext()->Fill();
3553 }
3554 #endif
3555
3556 /* static */ void
3557 FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray<ClippedDisplayItem>& aItems,
3558 nsDisplayListBuilder *aBuilder,
3559 const nsIntRegion& aRegionToDraw,
3560 const nsIntPoint& aOffset,
3561 int32_t aAppUnitsPerDevPixel,
3562 float aXScale,
3563 float aYScale)
3564 {
3565 uint32_t i;
3566 // Update visible regions. We need perform visibility analysis again
3567 // because we may be asked to draw into part of a ThebesLayer that
3568 // isn't actually visible in the window (e.g., because a ThebesLayer
3569 // expanded its visible region to a rectangle internally), in which
3570 // case the mVisibleRect stored in the display item may be wrong.
3571 nsRegion visible = aRegionToDraw.ToAppUnits(aAppUnitsPerDevPixel);
3572 visible.MoveBy(NSIntPixelsToAppUnits(aOffset.x, aAppUnitsPerDevPixel),
3573 NSIntPixelsToAppUnits(aOffset.y, aAppUnitsPerDevPixel));
3574 visible.ScaleInverseRoundOut(aXScale, aYScale);
3575
3576 for (i = aItems.Length(); i > 0; --i) {
3577 ClippedDisplayItem* cdi = &aItems[i - 1];
3578 const DisplayItemClip& clip = cdi->mItem->GetClip();
3579
3580 NS_ASSERTION(AppUnitsPerDevPixel(cdi->mItem) == aAppUnitsPerDevPixel,
3581 "a thebes layer should contain items only at the same zoom");
3582
3583 NS_ABORT_IF_FALSE(clip.HasClip() ||
3584 clip.GetRoundedRectCount() == 0,
3585 "If we have rounded rects, we must have a clip rect");
3586
3587 if (!clip.IsRectAffectedByClip(visible.GetBounds())) {
3588 cdi->mItem->RecomputeVisibility(aBuilder, &visible);
3589 continue;
3590 }
3591
3592 // Do a little dance to account for the fact that we're clipping
3593 // to cdi->mClipRect
3594 nsRegion clipped;
3595 clipped.And(visible, clip.NonRoundedIntersection());
3596 nsRegion finalClipped = clipped;
3597 cdi->mItem->RecomputeVisibility(aBuilder, &finalClipped);
3598 // If we have rounded clip rects, don't subtract from the visible
3599 // region since we aren't displaying everything inside the rect.
3600 if (clip.GetRoundedRectCount() == 0) {
3601 nsRegion removed;
3602 removed.Sub(clipped, finalClipped);
3603 nsRegion newVisible;
3604 newVisible.Sub(visible, removed);
3605 // Don't let the visible region get too complex.
3606 if (newVisible.GetNumRects() <= 15) {
3607 visible = newVisible;
3608 }
3609 }
3610 }
3611 }
3612
3613 void
3614 FrameLayerBuilder::PaintItems(nsTArray<ClippedDisplayItem>& aItems,
3615 const nsIntRect& aRect,
3616 gfxContext *aContext,
3617 nsRenderingContext *aRC,
3618 nsDisplayListBuilder* aBuilder,
3619 nsPresContext* aPresContext,
3620 const nsIntPoint& aOffset,
3621 float aXScale, float aYScale,
3622 int32_t aCommonClipCount)
3623 {
3624 int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
3625 nsRect boundRect = aRect.ToAppUnits(appUnitsPerDevPixel);
3626 boundRect.MoveBy(NSIntPixelsToAppUnits(aOffset.x, appUnitsPerDevPixel),
3627 NSIntPixelsToAppUnits(aOffset.y, appUnitsPerDevPixel));
3628 boundRect.ScaleInverseRoundOut(aXScale, aYScale);
3629
3630 DisplayItemClip currentClip;
3631 bool currentClipIsSetInContext = false;
3632 DisplayItemClip tmpClip;
3633
3634 for (uint32_t i = 0; i < aItems.Length(); ++i) {
3635 ClippedDisplayItem* cdi = &aItems[i];
3636
3637 nsRect paintRect = cdi->mItem->GetVisibleRect().Intersect(boundRect);
3638 if (paintRect.IsEmpty())
3639 continue;
3640
3641 // If the new desired clip state is different from the current state,
3642 // update the clip.
3643 const DisplayItemClip* clip = &cdi->mItem->GetClip();
3644 if (clip->GetRoundedRectCount() > 0 &&
3645 !clip->IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) {
3646 tmpClip = *clip;
3647 tmpClip.RemoveRoundedCorners();
3648 clip = &tmpClip;
3649 }
3650 if (currentClipIsSetInContext != clip->HasClip() ||
3651 (clip->HasClip() && *clip != currentClip)) {
3652 if (currentClipIsSetInContext) {
3653 aContext->Restore();
3654 }
3655 currentClipIsSetInContext = clip->HasClip();
3656 if (currentClipIsSetInContext) {
3657 currentClip = *clip;
3658 aContext->Save();
3659 NS_ASSERTION(aCommonClipCount < 100,
3660 "Maybe you really do have more than a hundred clipping rounded rects, or maybe something has gone wrong.");
3661 currentClip.ApplyTo(aContext, aPresContext, aCommonClipCount);
3662 aContext->NewPath();
3663 }
3664 }
3665
3666 if (cdi->mInactiveLayerManager) {
3667 PaintInactiveLayer(aBuilder, cdi->mInactiveLayerManager, cdi->mItem, aContext, aRC);
3668 } else {
3669 nsIFrame* frame = cdi->mItem->Frame();
3670 frame->AddStateBits(NS_FRAME_PAINTED_THEBES);
3671 #ifdef MOZ_DUMP_PAINTING
3672
3673 if (gfxUtils::sDumpPainting) {
3674 DebugPaintItem(aRC, cdi->mItem, aBuilder);
3675 } else {
3676 #else
3677 {
3678 #endif
3679 cdi->mItem->Paint(aBuilder, aRC);
3680 }
3681 }
3682
3683 if (CheckDOMModified())
3684 break;
3685 }
3686
3687 if (currentClipIsSetInContext) {
3688 aContext->Restore();
3689 }
3690 }
3691
3692 /**
3693 * Returns true if it is preferred to draw the list of display
3694 * items separately for each rect in the visible region rather
3695 * than clipping to a complex region.
3696 */
3697 static bool ShouldDrawRectsSeparately(gfxContext* aContext, DrawRegionClip aClip)
3698 {
3699 if (!gfxPrefs::LayoutPaintRectsSeparately() ||
3700 aContext->IsCairo() ||
3701 aClip == DrawRegionClip::CLIP_NONE) {
3702 return false;
3703 }
3704
3705 DrawTarget *dt = aContext->GetDrawTarget();
3706 return dt->GetType() == BackendType::DIRECT2D;
3707 }
3708
3709 static void DrawForcedBackgroundColor(gfxContext* aContext, Layer* aLayer, nscolor aBackgroundColor)
3710 {
3711 if (NS_GET_A(aBackgroundColor) > 0) {
3712 nsIntRect r = aLayer->GetVisibleRegion().GetBounds();
3713 aContext->NewPath();
3714 aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
3715 aContext->SetColor(gfxRGBA(aBackgroundColor));
3716 aContext->Fill();
3717 }
3718 }
3719
3720 /*
3721 * A note on residual transforms:
3722 *
3723 * In a transformed subtree we sometimes apply the ThebesLayer's
3724 * "residual transform" when drawing content into the ThebesLayer.
3725 * This is a translation by components in the range [-0.5,0.5) provided
3726 * by the layer system; applying the residual transform followed by the
3727 * transforms used by layer compositing ensures that the subpixel alignment
3728 * of the content of the ThebesLayer exactly matches what it would be if
3729 * we used cairo/Thebes to draw directly to the screen without going through
3730 * retained layer buffers.
3731 *
3732 * The visible and valid regions of the ThebesLayer are computed without
3733 * knowing the residual transform (because we don't know what the residual
3734 * transform is going to be until we've built the layer tree!). So we have to
3735 * consider whether content painted in the range [x, xmost) might be painted
3736 * outside the visible region we computed for that content. The visible region
3737 * would be [floor(x), ceil(xmost)). The content would be rendered at
3738 * [x + r, xmost + r), where -0.5 <= r < 0.5. So some half-rendered pixels could
3739 * indeed fall outside the computed visible region, which is not a big deal;
3740 * similar issues already arise when we snap cliprects to nearest pixels.
3741 * Note that if the rendering of the content is snapped to nearest pixels ---
3742 * which it often is --- then the content is actually rendered at
3743 * [snap(x + r), snap(xmost + r)). It turns out that floor(x) <= snap(x + r)
3744 * and ceil(xmost) >= snap(xmost + r), so the rendering of snapped content
3745 * always falls within the visible region we computed.
3746 */
3747
3748 /* static */ void
3749 FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
3750 gfxContext* aContext,
3751 const nsIntRegion& aRegionToDraw,
3752 DrawRegionClip aClip,
3753 const nsIntRegion& aRegionToInvalidate,
3754 void* aCallbackData)
3755 {
3756 PROFILER_LABEL("gfx", "DrawThebesLayer");
3757
3758 nsDisplayListBuilder* builder = static_cast<nsDisplayListBuilder*>
3759 (aCallbackData);
3760
3761 FrameLayerBuilder *layerBuilder = aLayer->Manager()->GetLayerBuilder();
3762 NS_ASSERTION(layerBuilder, "Unexpectedly null layer builder!");
3763
3764 if (layerBuilder->CheckDOMModified())
3765 return;
3766
3767 ThebesLayerItemsEntry* entry = layerBuilder->mThebesLayerItems.GetEntry(aLayer);
3768 NS_ASSERTION(entry, "We shouldn't be drawing into a layer with no items!");
3769 if (!entry->mContainerLayerFrame) {
3770 return;
3771 }
3772
3773
3774 ThebesDisplayItemLayerUserData* userData =
3775 static_cast<ThebesDisplayItemLayerUserData*>
3776 (aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
3777 NS_ASSERTION(userData, "where did our user data go?");
3778
3779 bool shouldDrawRectsSeparately = ShouldDrawRectsSeparately(aContext, aClip);
3780
3781 if (!shouldDrawRectsSeparately) {
3782 if (aClip == DrawRegionClip::DRAW_SNAPPED) {
3783 gfxUtils::ClipToRegionSnapped(aContext, aRegionToDraw);
3784 } else if (aClip == DrawRegionClip::DRAW) {
3785 gfxUtils::ClipToRegion(aContext, aRegionToDraw);
3786 }
3787
3788 DrawForcedBackgroundColor(aContext, aLayer, userData->mForcedBackgroundColor);
3789 }
3790
3791 // make the origin of the context coincide with the origin of the
3792 // ThebesLayer
3793 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
3794 nsIntPoint offset = GetTranslationForThebesLayer(aLayer);
3795
3796 nsPresContext* presContext = entry->mContainerLayerFrame->PresContext();
3797 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
3798
3799 RecomputeVisibilityForItems(entry->mItems, builder, aRegionToDraw,
3800 offset, appUnitsPerDevPixel,
3801 userData->mXScale, userData->mYScale);
3802
3803 nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
3804 rc->Init(presContext->DeviceContext(), aContext);
3805
3806 if (shouldDrawRectsSeparately) {
3807 nsIntRegionRectIterator it(aRegionToDraw);
3808 while (const nsIntRect* iterRect = it.Next()) {
3809 gfxContextAutoSaveRestore save(aContext);
3810 aContext->NewPath();
3811 aContext->Rectangle(*iterRect, aClip == DrawRegionClip::DRAW_SNAPPED);
3812 aContext->Clip();
3813
3814 DrawForcedBackgroundColor(aContext, aLayer, userData->mForcedBackgroundColor);
3815
3816 // Apply the residual transform if it has been enabled, to ensure that
3817 // snapping when we draw into aContext exactly matches the ideal transform.
3818 // See above for why this is OK.
3819 aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y));
3820 aContext->Scale(userData->mXScale, userData->mYScale);
3821
3822 layerBuilder->PaintItems(entry->mItems, *iterRect, aContext, rc,
3823 builder, presContext,
3824 offset, userData->mXScale, userData->mYScale,
3825 entry->mCommonClipCount);
3826 }
3827 } else {
3828 // Apply the residual transform if it has been enabled, to ensure that
3829 // snapping when we draw into aContext exactly matches the ideal transform.
3830 // See above for why this is OK.
3831 aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y));
3832 aContext->Scale(userData->mXScale, userData->mYScale);
3833
3834 layerBuilder->PaintItems(entry->mItems, aRegionToDraw.GetBounds(), aContext, rc,
3835 builder, presContext,
3836 offset, userData->mXScale, userData->mYScale,
3837 entry->mCommonClipCount);
3838 }
3839
3840 if (presContext->GetPaintFlashing()) {
3841 FlashPaint(aContext);
3842 }
3843
3844 if (!aRegionToInvalidate.IsEmpty()) {
3845 aLayer->AddInvalidRect(aRegionToInvalidate.GetBounds());
3846 }
3847 }
3848
3849 bool
3850 FrameLayerBuilder::CheckDOMModified()
3851 {
3852 if (!mRootPresContext ||
3853 mInitialDOMGeneration == mRootPresContext->GetDOMGeneration())
3854 return false;
3855 if (mDetectedDOMModification) {
3856 // Don't spam the console with extra warnings
3857 return true;
3858 }
3859 mDetectedDOMModification = true;
3860 // Painting is not going to complete properly. There's not much
3861 // we can do here though. Invalidating the window to get another repaint
3862 // is likely to lead to an infinite repaint loop.
3863 NS_WARNING("Detected DOM modification during paint, bailing out!");
3864 return true;
3865 }
3866
3867 #ifdef MOZ_DUMP_PAINTING
3868 /* static */ void
3869 FrameLayerBuilder::DumpRetainedLayerTree(LayerManager* aManager, FILE* aFile, bool aDumpHtml)
3870 {
3871 aManager->Dump(aFile, "", aDumpHtml);
3872 }
3873 #endif
3874
3875 gfx::Rect
3876 CalculateBounds(const nsTArray<DisplayItemClip::RoundedRect>& aRects, int32_t A2D)
3877 {
3878 nsRect bounds = aRects[0].mRect;
3879 for (uint32_t i = 1; i < aRects.Length(); ++i) {
3880 bounds.UnionRect(bounds, aRects[i].mRect);
3881 }
3882
3883 return gfx::ToRect(nsLayoutUtils::RectToGfxRect(bounds, A2D));
3884 }
3885
3886 static void
3887 SetClipCount(ThebesDisplayItemLayerUserData* aThebesData,
3888 uint32_t aClipCount)
3889 {
3890 if (aThebesData) {
3891 aThebesData->mMaskClipCount = aClipCount;
3892 }
3893 }
3894
3895 void
3896 ContainerState::SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
3897 uint32_t aRoundedRectClipCount)
3898 {
3899 // if the number of clips we are going to mask has decreased, then aLayer might have
3900 // cached graphics which assume the existence of a soon-to-be non-existent mask layer
3901 // in that case, invalidate the whole layer.
3902 ThebesDisplayItemLayerUserData* thebesData = GetThebesDisplayItemLayerUserData(aLayer);
3903 if (thebesData &&
3904 aRoundedRectClipCount < thebesData->mMaskClipCount) {
3905 ThebesLayer* thebes = aLayer->AsThebesLayer();
3906 thebes->InvalidateRegion(thebes->GetValidRegion().GetBounds());
3907 }
3908
3909 // don't build an unnecessary mask
3910 nsIntRect layerBounds = aLayer->GetVisibleRegion().GetBounds();
3911 if (aClip.GetRoundedRectCount() == 0 ||
3912 aRoundedRectClipCount == 0 ||
3913 layerBounds.IsEmpty()) {
3914 SetClipCount(thebesData, 0);
3915 return;
3916 }
3917
3918 // check if we can re-use the mask layer
3919 nsRefPtr<ImageLayer> maskLayer = CreateOrRecycleMaskImageLayerFor(aLayer);
3920 MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer);
3921
3922 MaskLayerUserData newData;
3923 aClip.AppendRoundedRects(&newData.mRoundedClipRects, aRoundedRectClipCount);
3924 newData.mScaleX = mParameters.mXScale;
3925 newData.mScaleY = mParameters.mYScale;
3926 newData.mOffset = mParameters.mOffset;
3927 newData.mAppUnitsPerDevPixel = mContainerFrame->PresContext()->AppUnitsPerDevPixel();
3928
3929 if (*userData == newData) {
3930 aLayer->SetMaskLayer(maskLayer);
3931 SetClipCount(thebesData, aRoundedRectClipCount);
3932 return;
3933 }
3934
3935 // calculate a more precise bounding rect
3936 gfx::Rect boundingRect = CalculateBounds(newData.mRoundedClipRects,
3937 newData.mAppUnitsPerDevPixel);
3938 boundingRect.Scale(mParameters.mXScale, mParameters.mYScale);
3939
3940 uint32_t maxSize = mManager->GetMaxTextureSize();
3941 NS_ASSERTION(maxSize > 0, "Invalid max texture size");
3942 gfx::Size surfaceSize(std::min<gfx::Float>(boundingRect.Width(), maxSize),
3943 std::min<gfx::Float>(boundingRect.Height(), maxSize));
3944
3945 // maskTransform is applied to the clip when it is painted into the mask (as a
3946 // component of imageTransform), and its inverse used when the mask is used for
3947 // masking.
3948 // It is the transform from the masked layer's space to mask space
3949 gfx::Matrix maskTransform;
3950 maskTransform.Scale(surfaceSize.width/boundingRect.Width(),
3951 surfaceSize.height/boundingRect.Height());
3952 gfx::Point p = boundingRect.TopLeft();
3953 maskTransform.Translate(-p.x, -p.y);
3954 // imageTransform is only used when the clip is painted to the mask
3955 gfx::Matrix imageTransform = maskTransform;
3956 imageTransform.Scale(mParameters.mXScale, mParameters.mYScale);
3957
3958 nsAutoPtr<MaskLayerImageCache::MaskLayerImageKey> newKey(
3959 new MaskLayerImageCache::MaskLayerImageKey());
3960
3961 // copy and transform the rounded rects
3962 for (uint32_t i = 0; i < newData.mRoundedClipRects.Length(); ++i) {
3963 newKey->mRoundedClipRects.AppendElement(
3964 MaskLayerImageCache::PixelRoundedRect(newData.mRoundedClipRects[i],
3965 mContainerFrame->PresContext()));
3966 newKey->mRoundedClipRects[i].ScaleAndTranslate(imageTransform);
3967 }
3968
3969 const MaskLayerImageCache::MaskLayerImageKey* lookupKey = newKey;
3970
3971 // check to see if we can reuse a mask image
3972 nsRefPtr<ImageContainer> container =
3973 GetMaskLayerImageCache()->FindImageFor(&lookupKey);
3974
3975 if (!container) {
3976 IntSize surfaceSizeInt(NSToIntCeil(surfaceSize.width),
3977 NSToIntCeil(surfaceSize.height));
3978 // no existing mask image, so build a new one
3979 RefPtr<DrawTarget> dt =
3980 aLayer->Manager()->CreateOptimalMaskDrawTarget(surfaceSizeInt);
3981
3982 // fail if we can't get the right surface
3983 if (!dt) {
3984 NS_WARNING("Could not create DrawTarget for mask layer.");
3985 SetClipCount(thebesData, 0);
3986 return;
3987 }
3988
3989 nsRefPtr<gfxContext> context = new gfxContext(dt);
3990 context->Multiply(ThebesMatrix(imageTransform));
3991
3992 // paint the clipping rects with alpha to create the mask
3993 context->SetColor(gfxRGBA(1, 1, 1, 1));
3994 aClip.DrawRoundedRectsTo(context,
3995 newData.mAppUnitsPerDevPixel,
3996 0,
3997 aRoundedRectClipCount);
3998
3999 RefPtr<SourceSurface> surface = dt->Snapshot();
4000
4001 // build the image and container
4002 container = aLayer->Manager()->CreateImageContainer();
4003 NS_ASSERTION(container, "Could not create image container for mask layer.");
4004 nsRefPtr<Image> image = container->CreateImage(ImageFormat::CAIRO_SURFACE);
4005 NS_ASSERTION(image, "Could not create image container for mask layer.");
4006 CairoImage::Data data;
4007 data.mSize = surfaceSizeInt;
4008 data.mSourceSurface = surface;
4009
4010 static_cast<CairoImage*>(image.get())->SetData(data);
4011 container->SetCurrentImageInTransaction(image);
4012
4013 GetMaskLayerImageCache()->PutImage(newKey.forget(), container);
4014 }
4015
4016 maskLayer->SetContainer(container);
4017
4018 maskTransform.Invert();
4019 Matrix4x4 matrix = Matrix4x4::From2D(maskTransform);
4020 matrix.Translate(mParameters.mOffset.x, mParameters.mOffset.y, 0);
4021 maskLayer->SetBaseTransform(matrix);
4022
4023 // save the details of the clip in user data
4024 userData->mScaleX = newData.mScaleX;
4025 userData->mScaleY = newData.mScaleY;
4026 userData->mOffset = newData.mOffset;
4027 userData->mAppUnitsPerDevPixel = newData.mAppUnitsPerDevPixel;
4028 userData->mRoundedClipRects.SwapElements(newData.mRoundedClipRects);
4029 userData->mImageKey = lookupKey;
4030
4031 aLayer->SetMaskLayer(maskLayer);
4032 SetClipCount(thebesData, aRoundedRectClipCount);
4033 return;
4034 }
4035
4036 } // namespace mozilla

mercurial