1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Robert O'Callahan <robert@ocallahan.org>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "FrameLayerBuilder.h"
39 :
40 : #include "nsDisplayList.h"
41 : #include "nsPresContext.h"
42 : #include "nsLayoutUtils.h"
43 : #include "Layers.h"
44 : #include "BasicLayers.h"
45 : #include "nsSubDocumentFrame.h"
46 : #include "nsCSSRendering.h"
47 : #include "nsCSSFrameConstructor.h"
48 : #include "gfxUtils.h"
49 : #include "nsImageFrame.h"
50 : #include "nsRenderingContext.h"
51 :
52 : #include "mozilla/Preferences.h"
53 : #include "sampler.h"
54 :
55 : #ifdef DEBUG
56 : #include <stdio.h>
57 : #endif
58 :
59 : using namespace mozilla::layers;
60 :
61 : namespace mozilla {
62 :
63 : /**
64 : * This is the userdata we associate with a layer manager.
65 : */
66 : class LayerManagerData : public LayerUserData {
67 : public:
68 0 : LayerManagerData(LayerManager *aManager) :
69 : mInvalidateAllLayers(false),
70 0 : mLayerManager(aManager)
71 : {
72 0 : MOZ_COUNT_CTOR(LayerManagerData);
73 0 : mFramesWithLayers.Init();
74 0 : }
75 0 : ~LayerManagerData() {
76 : // Remove display item data properties now, since we won't be able
77 : // to find these frames again without mFramesWithLayers.
78 : mFramesWithLayers.EnumerateEntries(
79 0 : FrameLayerBuilder::RemoveDisplayItemDataForFrame, nsnull);
80 0 : MOZ_COUNT_DTOR(LayerManagerData);
81 0 : }
82 :
83 : /**
84 : * Tracks which frames have layers associated with them.
85 : */
86 : nsTHashtable<FrameLayerBuilder::DisplayItemDataEntry> mFramesWithLayers;
87 : bool mInvalidateAllLayers;
88 : /** Layer manager we belong to, we hold a reference to this object. */
89 : nsRefPtr<LayerManager> mLayerManager;
90 : };
91 :
92 : namespace {
93 :
94 0 : static void DestroyRegion(void* aPropertyValue)
95 : {
96 0 : delete static_cast<nsRegion*>(aPropertyValue);
97 0 : }
98 :
99 : /**
100 : * This property represents a region that should be invalidated in every
101 : * ThebesLayer child whose parent ContainerLayer is associated with the
102 : * frame. This is an nsRegion*; the coordinates of the region are
103 : * relative to the top-left of the border-box of the frame the property
104 : * is attached to (which is the frame for the ContainerLayer).
105 : *
106 : * We add to this region in InvalidateThebesLayerContents. The region
107 : * is propagated to ContainerState in BuildContainerLayerFor, and then
108 : * the region(s) are actually invalidated in CreateOrRecycleThebesLayer.
109 : *
110 : * When the property value is null, the region is infinite --- i.e. all
111 : * areas of the ThebesLayers should be invalidated.
112 : */
113 0 : NS_DECLARE_FRAME_PROPERTY(ThebesLayerInvalidRegionProperty, DestroyRegion)
114 :
115 0 : static void DestroyPoint(void* aPropertyValue)
116 : {
117 : delete static_cast<nsPoint*>(aPropertyValue);
118 0 : }
119 :
120 : /**
121 : * The valid content in our child ThebesLayers is defined relative to
122 : * the offset from this frame to its active scroll root, mapped back
123 : * by the ThebesLayer's inverse transform. Since we accumulate the
124 : * region invalidated between last-paint and next-paint, and because
125 : * the offset of this frame to its active root may change during that
126 : * period, we save the offset at last-paint in this property and use
127 : * it to invalidate at next-paint.
128 : */
129 0 : NS_DECLARE_FRAME_PROPERTY(ThebesLayerLastPaintOffsetProperty, DestroyPoint)
130 :
131 : /**
132 : * This is a helper object used to build up the layer children for
133 : * a ContainerLayer.
134 : */
135 0 : class ContainerState {
136 : public:
137 0 : ContainerState(nsDisplayListBuilder* aBuilder,
138 : LayerManager* aManager,
139 : nsIFrame* aContainerFrame,
140 : ContainerLayer* aContainerLayer,
141 : const FrameLayerBuilder::ContainerParameters& aParameters) :
142 : mBuilder(aBuilder), mManager(aManager),
143 : mContainerFrame(aContainerFrame), mContainerLayer(aContainerLayer),
144 : mParameters(aParameters),
145 : mNextFreeRecycledThebesLayer(0), mNextFreeRecycledColorLayer(0),
146 0 : mNextFreeRecycledImageLayer(0), mInvalidateAllThebesContent(false)
147 : {
148 0 : CollectOldLayers();
149 0 : }
150 :
151 0 : void SetInvalidThebesContent(const nsIntRegion& aRegion)
152 : {
153 0 : mInvalidThebesContent = aRegion;
154 0 : }
155 0 : void SetInvalidateAllThebesContent()
156 : {
157 0 : mInvalidateAllThebesContent = true;
158 0 : }
159 : /**
160 : * This is the method that actually walks a display list and builds
161 : * the child layers. We invoke it recursively to process clipped sublists.
162 : * @param aClipRect the clip rect to apply to the list items, or null
163 : * if no clipping is required
164 : */
165 : void ProcessDisplayItems(const nsDisplayList& aList,
166 : FrameLayerBuilder::Clip& aClip);
167 : /**
168 : * This finalizes all the open ThebesLayers by popping every element off
169 : * mThebesLayerDataStack, then sets the children of the container layer
170 : * to be all the layers in mNewChildLayers in that order and removes any
171 : * layers as children of the container that aren't in mNewChildLayers.
172 : * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA,
173 : * set *aTextContentFlags to CONTENT_COMPONENT_ALPHA
174 : */
175 : void Finish(PRUint32 *aTextContentFlags);
176 :
177 0 : nsRect GetChildrenBounds() { return mBounds; }
178 :
179 : protected:
180 : /**
181 : * We keep a stack of these to represent the ThebesLayers that are
182 : * currently available to have display items added to.
183 : * We use a stack here because as much as possible we want to
184 : * assign display items to existing ThebesLayers, and to the lowest
185 : * ThebesLayer in z-order. This reduces the number of layers and
186 : * makes it more likely a display item will be rendered to an opaque
187 : * layer, giving us the best chance of getting subpixel AA.
188 : */
189 0 : class ThebesLayerData {
190 : public:
191 0 : ThebesLayerData() :
192 : mActiveScrolledRoot(nsnull), mLayer(nsnull),
193 : mIsSolidColorInVisibleRegion(false),
194 : mNeedComponentAlpha(false),
195 : mForceTransparentSurface(false),
196 0 : mImage(nsnull) {}
197 : /**
198 : * Record that an item has been added to the ThebesLayer, so we
199 : * need to update our regions.
200 : * @param aVisibleRect the area of the item that's visible
201 : * @param aDrawRect the area of the item that would be drawn if it
202 : * was completely visible
203 : * @param aOpaqueRect if non-null, the area of the item that's opaque.
204 : * We pass in a separate opaque rect because the opaque rect can be
205 : * bigger than the visible rect, and we want to have the biggest
206 : * opaque rect that we can.
207 : * @param aSolidColor if non-null, the visible area of the item is
208 : * a constant color given by *aSolidColor
209 : */
210 : void Accumulate(ContainerState* aState,
211 : nsDisplayItem* aItem,
212 : const nsIntRect& aVisibleRect,
213 : const nsIntRect& aDrawRect,
214 : const FrameLayerBuilder::Clip& aClip);
215 : nsIFrame* GetActiveScrolledRoot() { return mActiveScrolledRoot; }
216 :
217 : /**
218 : * If this represents only a nsDisplayImage, and the image type
219 : * supports being optimized to an ImageLayer (TYPE_RASTER only) returns
220 : * an ImageContainer for the image.
221 : */
222 : already_AddRefed<ImageContainer> CanOptimizeImageLayer();
223 :
224 : /**
225 : * The region of visible content in the layer, relative to the
226 : * container layer (which is at the snapped top-left of the display
227 : * list reference frame).
228 : */
229 : nsIntRegion mVisibleRegion;
230 : /**
231 : * The region of visible content above the layer and below the
232 : * next ThebesLayerData currently in the stack, if any. Note that not
233 : * all ThebesLayers for the container are in the ThebesLayerData stack.
234 : * Same coordinate system as mVisibleRegion.
235 : * This is a conservative approximation: it contains the true region.
236 : */
237 : nsIntRegion mVisibleAboveRegion;
238 : /**
239 : * The region containing the bounds of all display items in the layer,
240 : * regardless of visbility.
241 : * Same coordinate system as mVisibleRegion.
242 : * This is a conservative approximation: it contains the true region.
243 : */
244 : nsIntRegion mDrawRegion;
245 : /**
246 : * The region containing the bounds of all display items (regardless
247 : * of visibility) in the layer and below the next ThebesLayerData
248 : * currently in the stack, if any.
249 : * Note that not all ThebesLayers for the container are in the
250 : * ThebesLayerData stack.
251 : * Same coordinate system as mVisibleRegion.
252 : */
253 : nsIntRegion mDrawAboveRegion;
254 : /**
255 : * The region of visible content in the layer that is opaque.
256 : * Same coordinate system as mVisibleRegion.
257 : */
258 : nsIntRegion mOpaqueRegion;
259 : /**
260 : * The "active scrolled root" for all content in the layer. Must
261 : * be non-null; all content in a ThebesLayer must have the same
262 : * active scrolled root.
263 : */
264 : nsIFrame* mActiveScrolledRoot;
265 : ThebesLayer* mLayer;
266 : /**
267 : * If mIsSolidColorInVisibleRegion is true, this is the color of the visible
268 : * region.
269 : */
270 : nscolor mSolidColor;
271 : /**
272 : * True if every pixel in mVisibleRegion will have color mSolidColor.
273 : */
274 : bool mIsSolidColorInVisibleRegion;
275 : /**
276 : * True if there is any text visible in the layer that's over
277 : * transparent pixels in the layer.
278 : */
279 : bool mNeedComponentAlpha;
280 : /**
281 : * Set if the layer should be treated as transparent, even if its entire
282 : * area is covered by opaque display items. For example, this needs to
283 : * be set if something is going to "punch holes" in the layer by clearing
284 : * part of its surface.
285 : */
286 : bool mForceTransparentSurface;
287 :
288 : /**
289 : * Stores the pointer to the nsDisplayImage if we want to
290 : * convert this to an ImageLayer.
291 : */
292 : nsDisplayImage* mImage;
293 : /**
294 : * Stores the clip that we need to apply to the image.
295 : */
296 : FrameLayerBuilder::Clip mImageClip;
297 : };
298 : friend class ThebesLayerData;
299 :
300 : /**
301 : * Grab the next recyclable ThebesLayer, or create one if there are no
302 : * more recyclable ThebesLayers. Does any necessary invalidation of
303 : * a recycled ThebesLayer, and sets up the transform on the ThebesLayer
304 : * to account for scrolling.
305 : */
306 : already_AddRefed<ThebesLayer> CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot);
307 : /**
308 : * Grab the next recyclable ColorLayer, or create one if there are no
309 : * more recyclable ColorLayers.
310 : */
311 : already_AddRefed<ColorLayer> CreateOrRecycleColorLayer();
312 : /**
313 : * Grab the next recyclable ImageLayer, or create one if there are no
314 : * more recyclable ImageLayers.
315 : */
316 : already_AddRefed<ImageLayer> CreateOrRecycleImageLayer();
317 : /**
318 : * Grabs all ThebesLayers and ColorLayers from the ContainerLayer and makes them
319 : * available for recycling.
320 : */
321 : void CollectOldLayers();
322 : /**
323 : * If aItem used to belong to a ThebesLayer, invalidates the area of
324 : * aItem in that layer. If aNewLayer is a ThebesLayer, invalidates the area of
325 : * aItem in that layer.
326 : */
327 : void InvalidateForLayerChange(nsDisplayItem* aItem, Layer* aNewLayer);
328 : /**
329 : * Try to determine whether the ThebesLayer at aThebesLayerIndex
330 : * has a single opaque color behind it, over the entire bounds of its visible
331 : * region.
332 : * If successful, return that color, otherwise return NS_RGBA(0,0,0,0).
333 : */
334 : nscolor FindOpaqueBackgroundColorFor(PRInt32 aThebesLayerIndex);
335 : /**
336 : * Indicate that we are done adding items to the ThebesLayer at the top of
337 : * mThebesLayerDataStack. Set the final visible region and opaque-content
338 : * flag, and pop it off the stack.
339 : */
340 : void PopThebesLayerData();
341 : /**
342 : * Find the ThebesLayer to which we should assign the next display item.
343 : * We scan the ThebesLayerData stack to find the topmost ThebesLayer
344 : * that is compatible with the display item (i.e., has the same
345 : * active scrolled root), and that has no content from other layers above
346 : * it and intersecting the aVisibleRect.
347 : * Returns the layer, and also updates the ThebesLayerData. Will
348 : * push a new ThebesLayerData onto the stack if no suitable existing
349 : * layer is found. If we choose a ThebesLayer that's already on the
350 : * ThebesLayerData stack, later elements on the stack will be popped off.
351 : * @param aVisibleRect the area of the next display item that's visible
352 : * @param aActiveScrolledRoot the active scrolled root for the next
353 : * display item
354 : * @param aOpaqueRect if non-null, a region of the display item that is opaque
355 : * @param aSolidColor if non-null, indicates that every pixel in aVisibleRect
356 : * will be painted with aSolidColor by the item
357 : */
358 : already_AddRefed<ThebesLayer> FindThebesLayerFor(nsDisplayItem* aItem,
359 : const nsIntRect& aVisibleRect,
360 : const nsIntRect& aDrawRect,
361 : const FrameLayerBuilder::Clip& aClip,
362 : nsIFrame* aActiveScrolledRoot);
363 0 : ThebesLayerData* GetTopThebesLayerData()
364 : {
365 0 : return mThebesLayerDataStack.IsEmpty() ? nsnull
366 0 : : mThebesLayerDataStack[mThebesLayerDataStack.Length() - 1].get();
367 : }
368 :
369 : nsDisplayListBuilder* mBuilder;
370 : LayerManager* mManager;
371 : nsIFrame* mContainerFrame;
372 : ContainerLayer* mContainerLayer;
373 : FrameLayerBuilder::ContainerParameters mParameters;
374 : /**
375 : * The region of ThebesLayers that should be invalidated every time
376 : * we recycle one.
377 : */
378 : nsIntRegion mInvalidThebesContent;
379 : nsRect mBounds;
380 : nsAutoTArray<nsAutoPtr<ThebesLayerData>,1> mThebesLayerDataStack;
381 : /**
382 : * We collect the list of children in here. During ProcessDisplayItems,
383 : * the layers in this array either have mContainerLayer as their parent,
384 : * or no parent.
385 : */
386 : typedef nsAutoTArray<nsRefPtr<Layer>,1> AutoLayersArray;
387 : AutoLayersArray mNewChildLayers;
388 : nsTArray<nsRefPtr<ThebesLayer> > mRecycledThebesLayers;
389 : nsTArray<nsRefPtr<ColorLayer> > mRecycledColorLayers;
390 : nsTArray<nsRefPtr<ImageLayer> > mRecycledImageLayers;
391 : PRUint32 mNextFreeRecycledThebesLayer;
392 : PRUint32 mNextFreeRecycledColorLayer;
393 : PRUint32 mNextFreeRecycledImageLayer;
394 : bool mInvalidateAllThebesContent;
395 : };
396 :
397 : class ThebesDisplayItemLayerUserData : public LayerUserData
398 0 : {
399 : public:
400 0 : ThebesDisplayItemLayerUserData() :
401 : mForcedBackgroundColor(NS_RGBA(0,0,0,0)),
402 : mXScale(1.f), mYScale(1.f),
403 0 : mActiveScrolledRootPosition(0, 0) {}
404 :
405 : /**
406 : * A color that should be painted over the bounds of the layer's visible
407 : * region before any other content is painted.
408 : */
409 : nscolor mForcedBackgroundColor;
410 : /**
411 : * The resolution scale used.
412 : */
413 : float mXScale, mYScale;
414 : /**
415 : * We try to make 0,0 of the ThebesLayer be the top-left of the
416 : * border-box of the "active scrolled root" frame (i.e. the nearest ancestor
417 : * frame for the display items that is being actively scrolled). But
418 : * we force the ThebesLayer transform to be an integer translation, and we may
419 : * have a resolution scale, so we have to snap the ThebesLayer transform, so
420 : * 0,0 may not be exactly the top-left of the active scrolled root. Here we
421 : * store the coordinates in ThebesLayer space of the top-left of the
422 : * active scrolled root.
423 : */
424 : gfxPoint mActiveScrolledRootPosition;
425 : };
426 :
427 : /**
428 : * The address of gThebesDisplayItemLayerUserData is used as the user
429 : * data key for ThebesLayers created by FrameLayerBuilder.
430 : * It identifies ThebesLayers used to draw non-layer content, which are
431 : * therefore eligible for recycling. We want display items to be able to
432 : * create their own dedicated ThebesLayers in BuildLayer, if necessary,
433 : * and we wouldn't want to accidentally recycle those.
434 : * The user data is a ThebesDisplayItemLayerUserData.
435 : */
436 : PRUint8 gThebesDisplayItemLayerUserData;
437 : /**
438 : * The address of gColorLayerUserData is used as the user
439 : * data key for ColorLayers created by FrameLayerBuilder.
440 : * The user data is null.
441 : */
442 : PRUint8 gColorLayerUserData;
443 : /**
444 : * The address of gImageLayerUserData is used as the user
445 : * data key for ImageLayers created by FrameLayerBuilder.
446 : * The user data is null.
447 : */
448 : PRUint8 gImageLayerUserData;
449 : /**
450 : * The address of gLayerManagerUserData is used as the user
451 : * data key for retained LayerManagers managed by FrameLayerBuilder.
452 : * The user data is a LayerManagerData.
453 : */
454 : PRUint8 gLayerManagerUserData;
455 :
456 : } // anonymous namespace
457 :
458 : void
459 0 : FrameLayerBuilder::Init(nsDisplayListBuilder* aBuilder)
460 : {
461 0 : mRootPresContext = aBuilder->ReferenceFrame()->PresContext()->GetRootPresContext();
462 0 : if (mRootPresContext) {
463 0 : mInitialDOMGeneration = mRootPresContext->GetDOMGeneration();
464 : }
465 0 : }
466 :
467 : bool
468 0 : FrameLayerBuilder::DisplayItemDataEntry::HasNonEmptyContainerLayer()
469 : {
470 0 : for (PRUint32 i = 0; i < mData.Length(); ++i) {
471 0 : if (mData[i].mLayer->GetType() == Layer::TYPE_CONTAINER &&
472 0 : mData[i].mLayerState != LAYER_ACTIVE_EMPTY)
473 0 : return true;
474 : }
475 0 : return false;
476 : }
477 :
478 : void
479 0 : FrameLayerBuilder::FlashPaint(gfxContext *aContext)
480 : {
481 : static bool sPaintFlashingEnabled;
482 : static bool sPaintFlashingPrefCached = false;
483 :
484 0 : if (!sPaintFlashingPrefCached) {
485 0 : sPaintFlashingPrefCached = true;
486 : mozilla::Preferences::AddBoolVarCache(&sPaintFlashingEnabled,
487 0 : "nglayout.debug.paint_flashing");
488 : }
489 :
490 0 : if (sPaintFlashingEnabled) {
491 0 : float r = float(rand()) / RAND_MAX;
492 0 : float g = float(rand()) / RAND_MAX;
493 0 : float b = float(rand()) / RAND_MAX;
494 0 : aContext->SetColor(gfxRGBA(r, g, b, 0.2));
495 0 : aContext->Paint();
496 : }
497 0 : }
498 :
499 : /* static */ nsTArray<FrameLayerBuilder::DisplayItemData>*
500 0 : FrameLayerBuilder::GetDisplayItemDataArrayForFrame(nsIFrame* aFrame)
501 : {
502 0 : FrameProperties props = aFrame->Properties();
503 : LayerManagerData *data =
504 0 : reinterpret_cast<LayerManagerData*>(props.Get(LayerManagerDataProperty()));
505 0 : if (!data)
506 0 : return nsnull;
507 :
508 0 : DisplayItemDataEntry *entry = data->mFramesWithLayers.GetEntry(aFrame);
509 0 : NS_ASSERTION(entry, "out of sync?");
510 0 : if (!entry)
511 0 : return nsnull;
512 :
513 0 : return &entry->mData;
514 : }
515 :
516 : /* static */ void
517 0 : FrameLayerBuilder::RemoveFrameFromLayerManager(nsIFrame* aFrame,
518 : void* aPropertyValue)
519 : {
520 0 : LayerManagerData *data = reinterpret_cast<LayerManagerData*>(aPropertyValue);
521 0 : data->mFramesWithLayers.RemoveEntry(aFrame);
522 0 : if (data->mFramesWithLayers.Count() == 0) {
523 0 : data->mLayerManager->RemoveUserData(&gLayerManagerUserData);
524 : }
525 0 : }
526 :
527 : void
528 0 : FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager)
529 : {
530 0 : mRetainingManager = aManager;
531 : LayerManagerData* data = static_cast<LayerManagerData*>
532 0 : (aManager->GetUserData(&gLayerManagerUserData));
533 0 : if (data) {
534 0 : mInvalidateAllLayers = data->mInvalidateAllLayers;
535 : }
536 0 : }
537 :
538 : /**
539 : * A helper function to remove the mThebesLayerItems entries for every
540 : * layer in aLayer's subtree.
541 : */
542 : void
543 0 : FrameLayerBuilder::RemoveThebesItemsForLayerSubtree(Layer* aLayer)
544 : {
545 0 : ThebesLayer* thebes = aLayer->AsThebesLayer();
546 0 : if (thebes) {
547 0 : mThebesLayerItems.RemoveEntry(thebes);
548 0 : return;
549 : }
550 :
551 0 : for (Layer* child = aLayer->GetFirstChild(); child;
552 : child = child->GetNextSibling()) {
553 0 : RemoveThebesItemsForLayerSubtree(child);
554 : }
555 : }
556 :
557 : void
558 0 : FrameLayerBuilder::DidEndTransaction(LayerManager* aManager)
559 : {
560 0 : if (aManager != mRetainingManager) {
561 0 : Layer* root = aManager->GetRoot();
562 0 : if (root) {
563 0 : RemoveThebesItemsForLayerSubtree(root);
564 : }
565 : }
566 0 : }
567 :
568 : void
569 0 : FrameLayerBuilder::WillEndTransaction(LayerManager* aManager)
570 : {
571 0 : if (aManager != mRetainingManager)
572 0 : return;
573 :
574 : // We need to save the data we'll need to support retaining. We do this
575 : // before we paint so that invalidation triggered by painting will
576 : // be able to update the ThebesLayerInvalidRegionProperty values
577 : // correctly and the NS_FRAME_HAS_CONTAINER_LAYER bits will be set
578 : // correctly.
579 : LayerManagerData* data = static_cast<LayerManagerData*>
580 0 : (mRetainingManager->GetUserData(&gLayerManagerUserData));
581 0 : if (data) {
582 : // Update all the frames that used to have layers.
583 0 : data->mFramesWithLayers.EnumerateEntries(UpdateDisplayItemDataForFrame, this);
584 : } else {
585 0 : data = new LayerManagerData(mRetainingManager);
586 0 : mRetainingManager->SetUserData(&gLayerManagerUserData, data);
587 : }
588 : // Now go through all the frames that didn't have any retained
589 : // display items before, and record those retained display items.
590 : // This also empties mNewDisplayItemData.
591 0 : mNewDisplayItemData.EnumerateEntries(StoreNewDisplayItemData, data);
592 0 : data->mInvalidateAllLayers = false;
593 :
594 0 : NS_ASSERTION(data->mFramesWithLayers.Count() > 0,
595 : "Some frame must have a layer!");
596 : }
597 :
598 : static void
599 0 : SetHasContainerLayer(nsIFrame* aFrame, nsPoint aOffsetToRoot)
600 : {
601 0 : aFrame->AddStateBits(NS_FRAME_HAS_CONTAINER_LAYER);
602 0 : for (nsIFrame* f = aFrame;
603 0 : f && !(f->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT);
604 : f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
605 0 : f->AddStateBits(NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT);
606 : }
607 :
608 0 : FrameProperties props = aFrame->Properties();
609 : nsPoint* lastPaintOffset = static_cast<nsPoint*>
610 0 : (props.Get(ThebesLayerLastPaintOffsetProperty()));
611 0 : if (lastPaintOffset) {
612 0 : *lastPaintOffset = aOffsetToRoot;
613 : } else {
614 0 : props.Set(ThebesLayerLastPaintOffsetProperty(), new nsPoint(aOffsetToRoot));
615 : }
616 0 : }
617 :
618 : static void
619 0 : SetNoContainerLayer(nsIFrame* aFrame)
620 : {
621 0 : FrameProperties props = aFrame->Properties();
622 0 : props.Delete(ThebesLayerInvalidRegionProperty());
623 0 : props.Delete(ThebesLayerLastPaintOffsetProperty());
624 0 : aFrame->RemoveStateBits(NS_FRAME_HAS_CONTAINER_LAYER);
625 0 : }
626 :
627 : /* static */ PLDHashOperator
628 0 : FrameLayerBuilder::UpdateDisplayItemDataForFrame(DisplayItemDataEntry* aEntry,
629 : void* aUserArg)
630 : {
631 0 : FrameLayerBuilder* builder = static_cast<FrameLayerBuilder*>(aUserArg);
632 0 : nsIFrame* f = aEntry->GetKey();
633 0 : FrameProperties props = f->Properties();
634 : DisplayItemDataEntry* newDisplayItems =
635 0 : builder ? builder->mNewDisplayItemData.GetEntry(f) : nsnull;
636 0 : if (!newDisplayItems) {
637 : // This frame was visible, but isn't anymore.
638 : bool found;
639 0 : props.Remove(LayerManagerDataProperty(), &found);
640 0 : NS_ASSERTION(found, "How can the frame property be missing?");
641 0 : SetNoContainerLayer(f);
642 0 : return PL_DHASH_REMOVE;
643 : }
644 :
645 0 : if (newDisplayItems->HasNonEmptyContainerLayer()) {
646 : // Reset or create the invalid region now so we can start collecting
647 : // new dirty areas.
648 : // Note that the NS_FRAME_HAS_CONTAINER_LAYER bit is set in
649 : // BuildContainerLayerFor, so we don't need to set it here.
650 : nsRegion* invalidRegion = static_cast<nsRegion*>
651 0 : (props.Get(ThebesLayerInvalidRegionProperty()));
652 0 : if (invalidRegion) {
653 0 : invalidRegion->SetEmpty();
654 : } else {
655 0 : props.Set(ThebesLayerInvalidRegionProperty(), new nsRegion());
656 : }
657 : } else {
658 0 : SetNoContainerLayer(f);
659 : }
660 :
661 : // Steal the list of display item layers
662 0 : aEntry->mData.SwapElements(newDisplayItems->mData);
663 : // Don't need to process this frame again
664 0 : builder->mNewDisplayItemData.RawRemoveEntry(newDisplayItems);
665 0 : return PL_DHASH_NEXT;
666 : }
667 :
668 : /* static */ PLDHashOperator
669 0 : FrameLayerBuilder::StoreNewDisplayItemData(DisplayItemDataEntry* aEntry,
670 : void* aUserArg)
671 : {
672 0 : LayerManagerData* data = static_cast<LayerManagerData*>(aUserArg);
673 0 : nsIFrame* f = aEntry->GetKey();
674 0 : FrameProperties props = f->Properties();
675 : // Remember that this frame has display items in retained layers
676 0 : NS_ASSERTION(!data->mFramesWithLayers.GetEntry(f),
677 : "We shouldn't get here if we're already in mFramesWithLayers");
678 0 : DisplayItemDataEntry *newEntry = data->mFramesWithLayers.PutEntry(f);
679 0 : NS_ASSERTION(!props.Get(LayerManagerDataProperty()),
680 : "mFramesWithLayers out of sync");
681 :
682 0 : newEntry->mData.SwapElements(aEntry->mData);
683 0 : props.Set(LayerManagerDataProperty(), data);
684 :
685 0 : if (f->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER) {
686 0 : props.Set(ThebesLayerInvalidRegionProperty(), new nsRegion());
687 : }
688 0 : return PL_DHASH_REMOVE;
689 : }
690 :
691 : bool
692 0 : FrameLayerBuilder::HasRetainedLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey)
693 : {
694 0 : nsTArray<DisplayItemData> *array = GetDisplayItemDataArrayForFrame(aFrame);
695 0 : if (!array)
696 0 : return false;
697 :
698 0 : for (PRUint32 i = 0; i < array->Length(); ++i) {
699 0 : if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) {
700 0 : Layer* layer = array->ElementAt(i).mLayer;
701 0 : if (layer->Manager()->GetUserData(&gLayerManagerUserData)) {
702 : // All layer managers with our user data are retained layer managers
703 0 : return true;
704 : }
705 : }
706 : }
707 0 : return false;
708 : }
709 :
710 : Layer*
711 0 : FrameLayerBuilder::GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey)
712 : {
713 : // If we need to build a new layer tree, then just refuse to recycle
714 : // anything.
715 0 : if (!mRetainingManager || mInvalidateAllLayers)
716 0 : return nsnull;
717 :
718 0 : nsTArray<DisplayItemData> *array = GetDisplayItemDataArrayForFrame(aFrame);
719 0 : if (!array)
720 0 : return nsnull;
721 :
722 0 : for (PRUint32 i = 0; i < array->Length(); ++i) {
723 0 : if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) {
724 0 : Layer* layer = array->ElementAt(i).mLayer;
725 0 : if (layer->Manager() == mRetainingManager)
726 0 : return layer;
727 : }
728 : }
729 0 : return nsnull;
730 : }
731 :
732 : /**
733 : * Invalidate aRegion in aLayer. aLayer is in the coordinate system
734 : * *after* aTranslation has been applied, so we need to
735 : * apply the inverse of that transform before calling InvalidateRegion.
736 : */
737 : static void
738 0 : InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsIntRegion& aRegion,
739 : const nsIntPoint& aTranslation)
740 : {
741 : // Convert the region from the coordinates of the container layer
742 : // (relative to the snapped top-left of the display list reference frame)
743 : // to the ThebesLayer's own coordinates
744 0 : nsIntRegion rgn = aRegion;
745 0 : rgn.MoveBy(-aTranslation);
746 0 : aLayer->InvalidateRegion(rgn);
747 0 : }
748 :
749 : already_AddRefed<ColorLayer>
750 0 : ContainerState::CreateOrRecycleColorLayer()
751 : {
752 0 : nsRefPtr<ColorLayer> layer;
753 0 : if (mNextFreeRecycledColorLayer < mRecycledColorLayers.Length()) {
754 : // Recycle a layer
755 0 : layer = mRecycledColorLayers[mNextFreeRecycledColorLayer];
756 0 : ++mNextFreeRecycledColorLayer;
757 : // Clear clip rect so we don't accidentally stay clipped. We will
758 : // reapply any necessary clipping.
759 0 : layer->SetClipRect(nsnull);
760 : } else {
761 : // Create a new layer
762 0 : layer = mManager->CreateColorLayer();
763 0 : if (!layer)
764 0 : return nsnull;
765 : // Mark this layer as being used for Thebes-painting display items
766 0 : layer->SetUserData(&gColorLayerUserData, nsnull);
767 : }
768 0 : return layer.forget();
769 : }
770 :
771 : already_AddRefed<ImageLayer>
772 0 : ContainerState::CreateOrRecycleImageLayer()
773 : {
774 0 : nsRefPtr<ImageLayer> layer;
775 0 : if (mNextFreeRecycledImageLayer < mRecycledImageLayers.Length()) {
776 : // Recycle a layer
777 0 : layer = mRecycledImageLayers[mNextFreeRecycledImageLayer];
778 0 : ++mNextFreeRecycledImageLayer;
779 : // Clear clip rect so we don't accidentally stay clipped. We will
780 : // reapply any necessary clipping.
781 0 : layer->SetClipRect(nsnull);
782 : } else {
783 : // Create a new layer
784 0 : layer = mManager->CreateImageLayer();
785 0 : if (!layer)
786 0 : return nsnull;
787 : // Mark this layer as being used for Thebes-painting display items
788 0 : layer->SetUserData(&gImageLayerUserData, nsnull);
789 : }
790 0 : return layer.forget();
791 : }
792 :
793 : static nsIntPoint
794 0 : GetTranslationForThebesLayer(ThebesLayer* aLayer)
795 : {
796 0 : gfxMatrix transform;
797 0 : if (!aLayer->GetTransform().Is2D(&transform) ||
798 0 : transform.HasNonIntegerTranslation()) {
799 0 : NS_ERROR("ThebesLayers should have integer translations only");
800 0 : return nsIntPoint(0, 0);
801 : }
802 0 : return nsIntPoint(PRInt32(transform.x0), PRInt32(transform.y0));
803 : }
804 :
805 : already_AddRefed<ThebesLayer>
806 0 : ContainerState::CreateOrRecycleThebesLayer(nsIFrame* aActiveScrolledRoot)
807 : {
808 : // We need a new thebes layer
809 0 : nsRefPtr<ThebesLayer> layer;
810 : ThebesDisplayItemLayerUserData* data;
811 0 : if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) {
812 : // Recycle a layer
813 0 : layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer];
814 0 : ++mNextFreeRecycledThebesLayer;
815 : // Clear clip rect so we don't accidentally stay clipped. We will
816 : // reapply any necessary clipping.
817 0 : layer->SetClipRect(nsnull);
818 :
819 : data = static_cast<ThebesDisplayItemLayerUserData*>
820 0 : (layer->GetUserData(&gThebesDisplayItemLayerUserData));
821 0 : NS_ASSERTION(data, "Recycled ThebesLayers must have user data");
822 :
823 : // This gets called on recycled ThebesLayers that are going to be in the
824 : // final layer tree, so it's a convenient time to invalidate the
825 : // content that changed where we don't know what ThebesLayer it belonged
826 : // to, or if we need to invalidate the entire layer, we can do that.
827 : // This needs to be done before we update the ThebesLayer to its new
828 : // transform. See nsGfxScrollFrame::InvalidateInternal, where
829 : // we ensure that mInvalidThebesContent is updated according to the
830 : // scroll position as of the most recent paint.
831 0 : if (mInvalidateAllThebesContent ||
832 : data->mXScale != mParameters.mXScale ||
833 : data->mYScale != mParameters.mYScale) {
834 0 : nsIntRect invalidate = layer->GetValidRegion().GetBounds();
835 0 : layer->InvalidateRegion(invalidate);
836 : } else {
837 : InvalidatePostTransformRegion(layer, mInvalidThebesContent,
838 0 : GetTranslationForThebesLayer(layer));
839 : }
840 : // We do not need to Invalidate these areas in the widget because we
841 : // assume the caller of InvalidateThebesLayerContents has ensured
842 : // the area is invalidated in the widget.
843 : } else {
844 : // Create a new thebes layer
845 0 : layer = mManager->CreateThebesLayer();
846 0 : if (!layer)
847 0 : return nsnull;
848 : // Mark this layer as being used for Thebes-painting display items
849 0 : data = new ThebesDisplayItemLayerUserData();
850 0 : layer->SetUserData(&gThebesDisplayItemLayerUserData, data);
851 : }
852 0 : data->mXScale = mParameters.mXScale;
853 0 : data->mYScale = mParameters.mYScale;
854 : // If we're in a transformed subtree, but no ancestor transform is actively
855 : // changing, we'll use the residual translation when drawing into the
856 : // ThebesLayer to ensure that snapping exactly matches the ideal transform.
857 : layer->SetAllowResidualTranslation(
858 0 : mParameters.mInTransformedSubtree && !mParameters.mInActiveTransformedSubtree);
859 :
860 0 : mBuilder->LayerBuilder()->SaveLastPaintOffset(layer);
861 :
862 : // Set up transform so that 0,0 in the Thebes layer corresponds to the
863 : // (pixel-snapped) top-left of the aActiveScrolledRoot.
864 0 : nsPoint offset = mBuilder->ToReferenceFrame(aActiveScrolledRoot);
865 0 : nscoord appUnitsPerDevPixel = aActiveScrolledRoot->PresContext()->AppUnitsPerDevPixel();
866 : gfxPoint scaledOffset(
867 0 : NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale,
868 0 : NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale);
869 0 : nsIntPoint pixOffset(NSToIntRoundUp(scaledOffset.x), NSToIntRoundUp(scaledOffset.y));
870 0 : gfxMatrix matrix;
871 0 : matrix.Translate(gfxPoint(pixOffset.x, pixOffset.y));
872 0 : layer->SetTransform(gfx3DMatrix::From2D(matrix));
873 :
874 : // Calculate exact position of the top-left of the active scrolled root.
875 : // This might not be 0,0 due to the snapping in ScaleToNearestPixels.
876 0 : gfxPoint activeScrolledRootTopLeft = scaledOffset - matrix.GetTranslation();
877 : // If it has changed, then we need to invalidate the entire layer since the
878 : // pixels in the layer buffer have the content at a (subpixel) offset
879 : // from what we need.
880 0 : if (activeScrolledRootTopLeft != data->mActiveScrolledRootPosition) {
881 0 : data->mActiveScrolledRootPosition = activeScrolledRootTopLeft;
882 0 : nsIntRect invalidate = layer->GetValidRegion().GetBounds();
883 0 : layer->InvalidateRegion(invalidate);
884 : }
885 :
886 0 : return layer.forget();
887 : }
888 :
889 : /**
890 : * Returns the appunits per dev pixel for the item's frame. The item must
891 : * have a frame because only nsDisplayClip items don't have a frame,
892 : * and those items are flattened away by ProcessDisplayItems.
893 : */
894 : static PRInt32
895 0 : AppUnitsPerDevPixel(nsDisplayItem* aItem)
896 : {
897 : // The underlying frame for zoom items is the root frame of the subdocument.
898 : // But zoom display items report their bounds etc using the parent document's
899 : // APD because zoom items act as a conversion layer between the two different
900 : // APDs.
901 0 : if (aItem->GetType() == nsDisplayItem::TYPE_ZOOM) {
902 0 : return static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
903 : }
904 0 : return aItem->GetUnderlyingFrame()->PresContext()->AppUnitsPerDevPixel();
905 : }
906 :
907 : /**
908 : * Restrict the visible region of aLayer to the region that is actually visible.
909 : * Because we only reduce the visible region here, we don't need to worry
910 : * about whether CONTENT_OPAQUE is set; if layer was opauqe in the old
911 : * visible region, it will still be opaque in the new one.
912 : * @param aItemVisible the visible region of the display item (that is,
913 : * after any layer transform has been applied)
914 : */
915 : static void
916 0 : RestrictVisibleRegionForLayer(Layer* aLayer, const nsIntRect& aItemVisible)
917 : {
918 0 : gfx3DMatrix transform = aLayer->GetTransform();
919 :
920 : // if 'transform' is not invertible, then nothing will be displayed
921 : // for the layer, so it doesn't really matter what we do here
922 0 : gfxRect itemVisible(aItemVisible.x, aItemVisible.y, aItemVisible.width, aItemVisible.height);
923 0 : gfxRect layerVisible = transform.Inverse().ProjectRectBounds(itemVisible);
924 0 : layerVisible.RoundOut();
925 :
926 0 : nsIntRect visibleRect;
927 0 : if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect))
928 0 : return;
929 :
930 0 : nsIntRegion rgn = aLayer->GetVisibleRegion();
931 0 : if (!visibleRect.Contains(rgn.GetBounds())) {
932 0 : rgn.And(rgn, visibleRect);
933 0 : aLayer->SetVisibleRegion(rgn);
934 : }
935 : }
936 :
937 : nscolor
938 0 : ContainerState::FindOpaqueBackgroundColorFor(PRInt32 aThebesLayerIndex)
939 : {
940 0 : ThebesLayerData* target = mThebesLayerDataStack[aThebesLayerIndex];
941 0 : for (PRInt32 i = aThebesLayerIndex - 1; i >= 0; --i) {
942 0 : ThebesLayerData* candidate = mThebesLayerDataStack[i];
943 0 : nsIntRegion visibleAboveIntersection;
944 0 : visibleAboveIntersection.And(candidate->mVisibleAboveRegion, target->mVisibleRegion);
945 0 : if (!visibleAboveIntersection.IsEmpty()) {
946 : // Some non-Thebes content between target and candidate; this is
947 : // hopeless
948 : break;
949 : }
950 :
951 0 : nsIntRegion intersection;
952 0 : intersection.And(candidate->mVisibleRegion, target->mVisibleRegion);
953 0 : if (intersection.IsEmpty()) {
954 : // The layer doesn't intersect our target, ignore it and move on
955 0 : continue;
956 : }
957 :
958 : // The candidate intersects our target. If any layer has a solid-color
959 : // area behind our target, this must be it. Scan its display items.
960 0 : nsPresContext* presContext = mContainerFrame->PresContext();
961 0 : nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
962 : nsRect rect =
963 0 : target->mVisibleRegion.GetBounds().ToAppUnits(appUnitsPerDevPixel);
964 0 : rect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale);
965 : return mBuilder->LayerBuilder()->
966 0 : FindOpaqueColorCovering(mBuilder, candidate->mLayer, rect);
967 : }
968 0 : return NS_RGBA(0,0,0,0);
969 : }
970 :
971 : already_AddRefed<ImageContainer>
972 0 : ContainerState::ThebesLayerData::CanOptimizeImageLayer()
973 : {
974 0 : if (!mImage || !mImageClip.mRoundedClipRects.IsEmpty()) {
975 0 : return nsnull;
976 : }
977 :
978 0 : return mImage->GetContainer();
979 : }
980 :
981 : void
982 0 : ContainerState::PopThebesLayerData()
983 : {
984 0 : NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop");
985 :
986 0 : PRInt32 lastIndex = mThebesLayerDataStack.Length() - 1;
987 0 : ThebesLayerData* data = mThebesLayerDataStack[lastIndex];
988 :
989 0 : nsRefPtr<Layer> layer;
990 0 : nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer();
991 :
992 0 : if ((data->mIsSolidColorInVisibleRegion || imageContainer) &&
993 0 : data->mLayer->GetValidRegion().IsEmpty()) {
994 0 : NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
995 : "Can't be a solid color as well as an image!");
996 0 : if (imageContainer) {
997 0 : nsRefPtr<ImageLayer> imageLayer = CreateOrRecycleImageLayer();
998 0 : imageLayer->SetContainer(imageContainer);
999 0 : data->mImage->ConfigureLayer(imageLayer);
1000 0 : if (mParameters.mInActiveTransformedSubtree) {
1001 : // The layer's current transform is applied first, then the result is scaled.
1002 0 : gfx3DMatrix transform = imageLayer->GetTransform()*
1003 0 : gfx3DMatrix::ScalingMatrix(mParameters.mXScale, mParameters.mYScale, 1.0f);
1004 0 : imageLayer->SetTransform(transform);
1005 : }
1006 0 : NS_ASSERTION(data->mImageClip.mRoundedClipRects.IsEmpty(),
1007 : "How did we get rounded clip rects here?");
1008 0 : if (data->mImageClip.mHaveClipRect) {
1009 0 : nsPresContext* presContext = mContainerFrame->PresContext();
1010 0 : nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
1011 : nsIntRect clip = data->mImageClip.mClipRect.ScaleToNearestPixels(
1012 0 : mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel);
1013 0 : imageLayer->IntersectClipRect(clip);
1014 : }
1015 0 : layer = imageLayer;
1016 : } else {
1017 0 : nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer();
1018 0 : colorLayer->SetIsFixedPosition(data->mLayer->GetIsFixedPosition());
1019 0 : colorLayer->SetColor(data->mSolidColor);
1020 :
1021 : // Copy transform
1022 0 : colorLayer->SetTransform(data->mLayer->GetTransform());
1023 :
1024 : // Clip colorLayer to its visible region, since ColorLayers are
1025 : // allowed to paint outside the visible region. Here we rely on the
1026 : // fact that uniform display items fill rectangles; obviously the
1027 : // area to fill must contain the visible region, and because it's
1028 : // a rectangle, it must therefore contain the visible region's GetBounds.
1029 : // Note that the visible region is already clipped appropriately.
1030 0 : nsIntRect visibleRect = data->mVisibleRegion.GetBounds();
1031 0 : colorLayer->SetClipRect(&visibleRect);
1032 :
1033 0 : layer = colorLayer;
1034 : }
1035 :
1036 0 : NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
1037 0 : AutoLayersArray::index_type index = mNewChildLayers.IndexOf(data->mLayer);
1038 0 : NS_ASSERTION(index != AutoLayersArray::NoIndex, "Thebes layer not found?");
1039 0 : mNewChildLayers.InsertElementAt(index + 1, layer);
1040 :
1041 : // Hide the ThebesLayer. We leave it in the layer tree so that we
1042 : // can find and recycle it later.
1043 0 : data->mLayer->IntersectClipRect(nsIntRect());
1044 0 : data->mLayer->SetVisibleRegion(nsIntRegion());
1045 : } else {
1046 0 : layer = data->mLayer;
1047 0 : imageContainer = nsnull;
1048 : }
1049 :
1050 0 : gfxMatrix transform;
1051 0 : if (!layer->GetTransform().Is2D(&transform)) {
1052 0 : NS_ERROR("Only 2D transformations currently supported");
1053 : }
1054 :
1055 : // ImageLayers are already configured with a visible region
1056 0 : if (!imageContainer) {
1057 0 : NS_ASSERTION(!transform.HasNonIntegerTranslation(),
1058 : "Matrix not just an integer translation?");
1059 : // Convert from relative to the container to relative to the
1060 : // ThebesLayer itself.
1061 0 : nsIntRegion rgn = data->mVisibleRegion;
1062 0 : rgn.MoveBy(-nsIntPoint(PRInt32(transform.x0), PRInt32(transform.y0)));
1063 0 : layer->SetVisibleRegion(rgn);
1064 : }
1065 :
1066 0 : nsIntRegion transparentRegion;
1067 0 : transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion);
1068 0 : bool isOpaque = transparentRegion.IsEmpty();
1069 : // For translucent ThebesLayers, try to find an opaque background
1070 : // color that covers the entire area beneath it so we can pull that
1071 : // color into this layer to make it opaque.
1072 0 : if (layer == data->mLayer) {
1073 0 : nscolor backgroundColor = NS_RGBA(0,0,0,0);
1074 0 : if (!isOpaque) {
1075 0 : backgroundColor = FindOpaqueBackgroundColorFor(lastIndex);
1076 0 : if (NS_GET_A(backgroundColor) == 255) {
1077 0 : isOpaque = true;
1078 : }
1079 : }
1080 :
1081 : // Store the background color
1082 : ThebesDisplayItemLayerUserData* userData =
1083 : static_cast<ThebesDisplayItemLayerUserData*>
1084 0 : (data->mLayer->GetUserData(&gThebesDisplayItemLayerUserData));
1085 0 : NS_ASSERTION(userData, "where did our user data go?");
1086 0 : if (userData->mForcedBackgroundColor != backgroundColor) {
1087 : // Invalidate the entire target ThebesLayer since we're changing
1088 : // the background color
1089 0 : data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion());
1090 : }
1091 0 : userData->mForcedBackgroundColor = backgroundColor;
1092 : }
1093 : PRUint32 flags;
1094 0 : if (isOpaque && !data->mForceTransparentSurface) {
1095 0 : flags = Layer::CONTENT_OPAQUE;
1096 0 : } else if (data->mNeedComponentAlpha) {
1097 0 : flags = Layer::CONTENT_COMPONENT_ALPHA;
1098 : } else {
1099 0 : flags = 0;
1100 : }
1101 0 : layer->SetContentFlags(flags);
1102 :
1103 0 : if (lastIndex > 0) {
1104 : // Since we're going to pop off the last ThebesLayerData, the
1105 : // mVisibleAboveRegion of the second-to-last item will need to include
1106 : // the regions of the last item.
1107 0 : ThebesLayerData* nextData = mThebesLayerDataStack[lastIndex - 1];
1108 : nextData->mVisibleAboveRegion.Or(nextData->mVisibleAboveRegion,
1109 0 : data->mVisibleAboveRegion);
1110 : nextData->mVisibleAboveRegion.Or(nextData->mVisibleAboveRegion,
1111 0 : data->mVisibleRegion);
1112 0 : nextData->mVisibleAboveRegion.SimplifyOutward(4);
1113 : nextData->mDrawAboveRegion.Or(nextData->mDrawAboveRegion,
1114 0 : data->mDrawAboveRegion);
1115 : nextData->mDrawAboveRegion.Or(nextData->mDrawAboveRegion,
1116 0 : data->mDrawRegion);
1117 0 : nextData->mDrawAboveRegion.SimplifyOutward(4);
1118 : }
1119 :
1120 0 : mThebesLayerDataStack.RemoveElementAt(lastIndex);
1121 0 : }
1122 :
1123 : static bool
1124 0 : SuppressComponentAlpha(nsDisplayListBuilder* aBuilder,
1125 : nsDisplayItem* aItem,
1126 : const nsRect& aComponentAlphaBounds)
1127 : {
1128 0 : const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion();
1129 0 : if (!windowTransparentRegion || windowTransparentRegion->IsEmpty())
1130 0 : return false;
1131 :
1132 : // Suppress component alpha for items in the toplevel window that are over
1133 : // the window translucent area
1134 0 : nsIFrame* f = aItem->GetUnderlyingFrame();
1135 0 : nsIFrame* ref = aBuilder->ReferenceFrame();
1136 0 : if (f->PresContext() != ref->PresContext())
1137 0 : return false;
1138 :
1139 0 : for (nsIFrame* t = f; t; t = t->GetParent()) {
1140 0 : if (t->IsTransformed())
1141 0 : return false;
1142 : }
1143 :
1144 0 : return windowTransparentRegion->Intersects(aComponentAlphaBounds);
1145 : }
1146 :
1147 : static bool
1148 0 : WindowHasTransparency(nsDisplayListBuilder* aBuilder)
1149 : {
1150 0 : const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion();
1151 0 : return windowTransparentRegion && !windowTransparentRegion->IsEmpty();
1152 : }
1153 :
1154 : void
1155 0 : ContainerState::ThebesLayerData::Accumulate(ContainerState* aState,
1156 : nsDisplayItem* aItem,
1157 : const nsIntRect& aVisibleRect,
1158 : const nsIntRect& aDrawRect,
1159 : const FrameLayerBuilder::Clip& aClip)
1160 : {
1161 : nscolor uniformColor;
1162 0 : bool isUniform = aItem->IsUniform(aState->mBuilder, &uniformColor);
1163 :
1164 : /* Mark as available for conversion to image layer if this is a nsDisplayImage and
1165 : * we are the first visible item in the ThebesLayerData object.
1166 : */
1167 0 : if (aItem->GetType() == nsDisplayItem::TYPE_IMAGE && mVisibleRegion.IsEmpty()) {
1168 0 : mImage = static_cast<nsDisplayImage*>(aItem);
1169 0 : mImageClip = aClip;
1170 : } else {
1171 0 : mImage = nsnull;
1172 : }
1173 :
1174 : // Some display items have to exist (so they can set forceTransparentSurface
1175 : // below) but don't draw anything. They'll return true for isUniform but
1176 : // a color with opacity 0.
1177 0 : if (!isUniform || NS_GET_A(uniformColor) > 0) {
1178 0 : if (isUniform &&
1179 0 : aItem->GetBounds(aState->mBuilder).ScaleToInsidePixels(
1180 : aState->mParameters.mXScale, aState->mParameters.mYScale,
1181 0 : AppUnitsPerDevPixel(aItem)).Contains(aVisibleRect)) {
1182 0 : if (mVisibleRegion.IsEmpty()) {
1183 : // This color is all we have
1184 0 : mSolidColor = uniformColor;
1185 0 : mIsSolidColorInVisibleRegion = true;
1186 0 : } else if (mIsSolidColorInVisibleRegion &&
1187 0 : mVisibleRegion.IsEqual(nsIntRegion(aVisibleRect))) {
1188 : // we can just blend the colors together
1189 0 : mSolidColor = NS_ComposeColors(mSolidColor, uniformColor);
1190 : } else {
1191 0 : mIsSolidColorInVisibleRegion = false;
1192 : }
1193 : } else {
1194 0 : mIsSolidColorInVisibleRegion = false;
1195 : }
1196 :
1197 0 : mVisibleRegion.Or(mVisibleRegion, aVisibleRect);
1198 0 : mVisibleRegion.SimplifyOutward(4);
1199 0 : mDrawRegion.Or(mDrawRegion, aDrawRect);
1200 0 : mDrawRegion.SimplifyOutward(4);
1201 : }
1202 :
1203 0 : bool forceTransparentSurface = false;
1204 0 : nsRegion opaque = aItem->GetOpaqueRegion(aState->mBuilder, &forceTransparentSurface);
1205 0 : if (!opaque.IsEmpty()) {
1206 0 : nsRegionRectIterator iter(opaque);
1207 0 : nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
1208 0 : for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
1209 : // We don't use SimplifyInward here since it's not defined exactly
1210 : // what it will discard. For our purposes the most important case
1211 : // is a large opaque background at the bottom of z-order (e.g.,
1212 : // a canvas background), so we need to make sure that the first rect
1213 : // we see doesn't get discarded.
1214 0 : nsIntRect rect = aClip.ApproximateIntersect(*r).ScaleToInsidePixels(
1215 : aState->mParameters.mXScale, aState->mParameters.mYScale,
1216 0 : appUnitsPerDevPixel);
1217 0 : nsIntRegion tmp;
1218 0 : tmp.Or(mOpaqueRegion, rect);
1219 : // Opaque display items in chrome documents whose window is partially
1220 : // transparent are always added to the opaque region. This helps ensure
1221 : // that we get as much subpixel-AA as possible in the chrome.
1222 0 : if (tmp.GetNumRects() <= 4 ||
1223 0 : (WindowHasTransparency(aState->mBuilder) &&
1224 0 : aItem->GetUnderlyingFrame()->PresContext()->IsChrome())) {
1225 0 : mOpaqueRegion = tmp;
1226 : }
1227 : }
1228 : }
1229 0 : if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
1230 : // Disable component alpha. This is cheaper than calling GetComponentAlphaBounds since for
1231 : // most items this is a single virtual call that does nothing.
1232 : // Note that the transform (if any) on the ThebesLayer is always an integer translation so
1233 : // we don't have to factor that in here.
1234 0 : aItem->DisableComponentAlpha();
1235 : } else {
1236 0 : nsRect componentAlpha = aItem->GetComponentAlphaBounds(aState->mBuilder);
1237 0 : componentAlpha.IntersectRect(componentAlpha, aItem->GetVisibleRect());
1238 0 : if (!componentAlpha.IsEmpty()) {
1239 0 : nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
1240 0 : if (!mOpaqueRegion.Contains(componentAlpha.ScaleToOutsidePixels(
1241 0 : aState->mParameters.mXScale, aState->mParameters.mYScale, appUnitsPerDevPixel))) {
1242 0 : if (SuppressComponentAlpha(aState->mBuilder, aItem, componentAlpha)) {
1243 0 : aItem->DisableComponentAlpha();
1244 : } else {
1245 0 : mNeedComponentAlpha = true;
1246 : }
1247 : }
1248 : }
1249 : }
1250 0 : mForceTransparentSurface = mForceTransparentSurface || forceTransparentSurface;
1251 0 : }
1252 :
1253 : already_AddRefed<ThebesLayer>
1254 0 : ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
1255 : const nsIntRect& aVisibleRect,
1256 : const nsIntRect& aDrawRect,
1257 : const FrameLayerBuilder::Clip& aClip,
1258 : nsIFrame* aActiveScrolledRoot)
1259 : {
1260 : PRInt32 i;
1261 0 : PRInt32 lowestUsableLayerWithScrolledRoot = -1;
1262 0 : PRInt32 topmostLayerWithScrolledRoot = -1;
1263 0 : for (i = mThebesLayerDataStack.Length() - 1; i >= 0; --i) {
1264 0 : ThebesLayerData* data = mThebesLayerDataStack[i];
1265 0 : if (data->mDrawAboveRegion.Intersects(aVisibleRect)) {
1266 0 : ++i;
1267 0 : break;
1268 : }
1269 0 : if (data->mActiveScrolledRoot == aActiveScrolledRoot) {
1270 0 : lowestUsableLayerWithScrolledRoot = i;
1271 0 : if (topmostLayerWithScrolledRoot < 0) {
1272 0 : topmostLayerWithScrolledRoot = i;
1273 : }
1274 : }
1275 0 : if (data->mDrawRegion.Intersects(aVisibleRect))
1276 0 : break;
1277 : }
1278 0 : if (topmostLayerWithScrolledRoot < 0) {
1279 0 : --i;
1280 0 : for (; i >= 0; --i) {
1281 0 : ThebesLayerData* data = mThebesLayerDataStack[i];
1282 0 : if (data->mActiveScrolledRoot == aActiveScrolledRoot) {
1283 0 : topmostLayerWithScrolledRoot = i;
1284 0 : break;
1285 : }
1286 : }
1287 : }
1288 :
1289 0 : if (topmostLayerWithScrolledRoot >= 0) {
1290 0 : while (PRUint32(topmostLayerWithScrolledRoot + 1) < mThebesLayerDataStack.Length()) {
1291 0 : PopThebesLayerData();
1292 : }
1293 : }
1294 :
1295 0 : nsRefPtr<ThebesLayer> layer;
1296 0 : ThebesLayerData* thebesLayerData = nsnull;
1297 0 : if (lowestUsableLayerWithScrolledRoot < 0) {
1298 0 : layer = CreateOrRecycleThebesLayer(aActiveScrolledRoot);
1299 :
1300 0 : NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
1301 0 : mNewChildLayers.AppendElement(layer);
1302 :
1303 0 : thebesLayerData = new ThebesLayerData();
1304 0 : mThebesLayerDataStack.AppendElement(thebesLayerData);
1305 0 : thebesLayerData->mLayer = layer;
1306 0 : thebesLayerData->mActiveScrolledRoot = aActiveScrolledRoot;
1307 : } else {
1308 0 : thebesLayerData = mThebesLayerDataStack[lowestUsableLayerWithScrolledRoot];
1309 0 : layer = thebesLayerData->mLayer;
1310 : }
1311 :
1312 0 : thebesLayerData->Accumulate(this, aItem, aVisibleRect, aDrawRect, aClip);
1313 0 : return layer.forget();
1314 : }
1315 :
1316 : #ifdef MOZ_DUMP_PAINTING
1317 : static void
1318 0 : DumpPaintedImage(nsDisplayItem* aItem, gfxASurface* aSurf)
1319 : {
1320 0 : nsCString string(aItem->Name());
1321 0 : string.Append("-");
1322 0 : string.AppendInt((PRUint64)aItem);
1323 0 : fprintf(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading());
1324 0 : aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile);
1325 0 : fprintf(gfxUtils::sDumpPaintFile, "\";");
1326 0 : }
1327 : #endif
1328 :
1329 : static void
1330 0 : PaintInactiveLayer(nsDisplayListBuilder* aBuilder,
1331 : nsDisplayItem* aItem,
1332 : gfxContext* aContext)
1333 : {
1334 : // This item has an inactive layer. Render it to a ThebesLayer
1335 : // using a temporary BasicLayerManager.
1336 0 : PRInt32 appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
1337 : nsIntRect itemVisibleRect =
1338 0 : aItem->GetVisibleRect().ToOutsidePixels(appUnitsPerDevPixel);
1339 :
1340 0 : nsRefPtr<gfxContext> context = aContext;
1341 : #ifdef MOZ_DUMP_PAINTING
1342 0 : nsRefPtr<gfxASurface> surf;
1343 0 : if (gfxUtils::sDumpPainting) {
1344 0 : surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(itemVisibleRect.Size(),
1345 0 : gfxASurface::CONTENT_COLOR_ALPHA);
1346 0 : surf->SetDeviceOffset(-itemVisibleRect.TopLeft());
1347 0 : context = new gfxContext(surf);
1348 : }
1349 : #endif
1350 :
1351 0 : nsRefPtr<BasicLayerManager> tempManager = new BasicLayerManager();
1352 0 : tempManager->BeginTransactionWithTarget(context);
1353 : nsRefPtr<Layer> layer =
1354 0 : aItem->BuildLayer(aBuilder, tempManager, FrameLayerBuilder::ContainerParameters());
1355 0 : if (!layer) {
1356 0 : tempManager->EndTransaction(nsnull, nsnull);
1357 : return;
1358 : }
1359 0 : RestrictVisibleRegionForLayer(layer, itemVisibleRect);
1360 :
1361 0 : tempManager->SetRoot(layer);
1362 0 : aBuilder->LayerBuilder()->WillEndTransaction(tempManager);
1363 0 : tempManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
1364 0 : aBuilder->LayerBuilder()->DidEndTransaction(tempManager);
1365 :
1366 : #ifdef MOZ_DUMP_PAINTING
1367 0 : if (gfxUtils::sDumpPainting) {
1368 0 : DumpPaintedImage(aItem, surf);
1369 :
1370 0 : surf->SetDeviceOffset(gfxPoint(0, 0));
1371 0 : aContext->SetSource(surf, itemVisibleRect.TopLeft());
1372 0 : aContext->Rectangle(itemVisibleRect);
1373 0 : aContext->Fill();
1374 0 : aItem->SetPainted();
1375 : }
1376 : #endif
1377 : }
1378 :
1379 : /*
1380 : * Iterate through the non-clip items in aList and its descendants.
1381 : * For each item we compute the effective clip rect. Each item is assigned
1382 : * to a layer. We invalidate the areas in ThebesLayers where an item
1383 : * has moved from one ThebesLayer to another. Also,
1384 : * aState->mInvalidThebesContent is invalidated in every ThebesLayer.
1385 : * We set the clip rect for items that generated their own layer.
1386 : * (ThebesLayers don't need a clip rect on the layer, we clip the items
1387 : * individually when we draw them.)
1388 : * If we have to clip to a rounded rect, we treat any active layer as
1389 : * though it's inactive so that we draw it ourselves into the thebes layer.
1390 : * We set the visible rect for all layers, although the actual setting
1391 : * of visible rects for some ThebesLayers is deferred until the calling
1392 : * of ContainerState::Finish.
1393 : */
1394 : void
1395 0 : ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
1396 : FrameLayerBuilder::Clip& aClip)
1397 : {
1398 : PRInt32 appUnitsPerDevPixel =
1399 0 : mContainerFrame->PresContext()->AppUnitsPerDevPixel();
1400 :
1401 0 : for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
1402 0 : nsDisplayItem::Type type = item->GetType();
1403 0 : if (type == nsDisplayItem::TYPE_CLIP ||
1404 : type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
1405 0 : FrameLayerBuilder::Clip childClip(aClip, item);
1406 0 : ProcessDisplayItems(*item->GetList(), childClip);
1407 0 : continue;
1408 : }
1409 :
1410 0 : NS_ASSERTION(appUnitsPerDevPixel == AppUnitsPerDevPixel(item),
1411 : "items in a container layer should all have the same app units per dev pixel");
1412 :
1413 : nsIntRect itemVisibleRect =
1414 0 : item->GetVisibleRect().ScaleToOutsidePixels(
1415 0 : mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel);
1416 0 : nsRect itemContent = item->GetBounds(mBuilder);
1417 0 : if (aClip.mHaveClipRect) {
1418 0 : itemContent.IntersectRect(aClip.mClipRect, itemContent);
1419 : }
1420 0 : mBounds.UnionRect(mBounds, itemContent);
1421 : nsIntRect itemDrawRect = itemContent.ScaleToOutsidePixels(
1422 0 : mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel);
1423 0 : LayerState layerState = item->GetLayerState(mBuilder, mManager);
1424 :
1425 : nsIFrame* activeScrolledRoot =
1426 0 : nsLayoutUtils::GetActiveScrolledRootFor(item, mBuilder);
1427 :
1428 : // Assign the item to a layer
1429 0 : if (layerState == LAYER_ACTIVE_FORCE ||
1430 : layerState == LAYER_ACTIVE_EMPTY ||
1431 : (layerState == LAYER_ACTIVE &&
1432 0 : (aClip.mRoundedClipRects.IsEmpty() ||
1433 : // We can use the visible rect here only because the item has its own
1434 : // layer, like the comment below.
1435 0 : !aClip.IsRectClippedByRoundedCorner(item->GetVisibleRect())))) {
1436 :
1437 : // LAYER_ACTIVE_EMPTY means the layer is created just for its metadata.
1438 : // We should never see an empty layer with any visible content!
1439 0 : NS_ASSERTION(layerState != LAYER_ACTIVE_EMPTY ||
1440 : itemVisibleRect.IsEmpty(),
1441 : "State is LAYER_ACTIVE_EMPTY but visible rect is not.");
1442 :
1443 : // If the item would have its own layer but is invisible, just hide it.
1444 : // Note that items without their own layers can't be skipped this
1445 : // way, since their ThebesLayer may decide it wants to draw them
1446 : // into its buffer even if they're currently covered.
1447 0 : if (itemVisibleRect.IsEmpty() && layerState != LAYER_ACTIVE_EMPTY) {
1448 0 : InvalidateForLayerChange(item, nsnull);
1449 0 : continue;
1450 : }
1451 :
1452 : // Just use its layer.
1453 0 : nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
1454 0 : if (!ownLayer) {
1455 0 : InvalidateForLayerChange(item, ownLayer);
1456 0 : continue;
1457 : }
1458 :
1459 : // If it's not a ContainerLayer, we need to apply the scale transform
1460 : // ourselves.
1461 0 : if (!ownLayer->AsContainerLayer()) {
1462 : // The layer's current transform is applied first, then the result is scaled.
1463 0 : gfx3DMatrix transform = ownLayer->GetTransform()*
1464 0 : gfx3DMatrix::ScalingMatrix(mParameters.mXScale, mParameters.mYScale, 1.0f);
1465 0 : ownLayer->SetTransform(transform);
1466 : }
1467 :
1468 : ownLayer->SetIsFixedPosition(!nsLayoutUtils::ScrolledByViewportScrolling(
1469 0 : activeScrolledRoot, mBuilder));
1470 :
1471 : // Update that layer's clip and visible rects.
1472 0 : NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager");
1473 0 : NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData),
1474 : "We shouldn't have a FrameLayerBuilder-managed layer here!");
1475 0 : NS_ASSERTION(aClip.mHaveClipRect ||
1476 : aClip.mRoundedClipRects.IsEmpty(),
1477 : "If we have rounded rects, we must have a clip rect");
1478 : // It has its own layer. Update that layer's clip and visible rects.
1479 0 : if (aClip.mHaveClipRect) {
1480 : ownLayer->IntersectClipRect(
1481 0 : aClip.NonRoundedIntersection().ScaleToNearestPixels(
1482 0 : mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel));
1483 : }
1484 0 : ThebesLayerData* data = GetTopThebesLayerData();
1485 0 : if (data) {
1486 0 : data->mVisibleAboveRegion.Or(data->mVisibleAboveRegion, itemVisibleRect);
1487 0 : data->mVisibleAboveRegion.SimplifyOutward(4);
1488 : // Add the entire bounds rect to the mDrawAboveRegion.
1489 : // The visible region may be excluding opaque content above the
1490 : // item, and we need to ensure that that content is not placed
1491 : // in a ThebesLayer below the item!
1492 0 : data->mDrawAboveRegion.Or(data->mDrawAboveRegion, itemDrawRect);
1493 0 : data->mDrawAboveRegion.SimplifyOutward(4);
1494 : }
1495 0 : RestrictVisibleRegionForLayer(ownLayer, itemVisibleRect);
1496 0 : ContainerLayer* oldContainer = ownLayer->GetParent();
1497 0 : if (oldContainer && oldContainer != mContainerLayer) {
1498 0 : oldContainer->RemoveChild(ownLayer);
1499 : }
1500 0 : NS_ASSERTION(!mNewChildLayers.Contains(ownLayer),
1501 : "Layer already in list???");
1502 :
1503 0 : InvalidateForLayerChange(item, ownLayer);
1504 :
1505 0 : mNewChildLayers.AppendElement(ownLayer);
1506 0 : mBuilder->LayerBuilder()->AddLayerDisplayItem(ownLayer, item, layerState);
1507 : } else {
1508 : nsRefPtr<ThebesLayer> thebesLayer =
1509 : FindThebesLayerFor(item, itemVisibleRect, itemDrawRect, aClip,
1510 0 : activeScrolledRoot);
1511 :
1512 0 : thebesLayer->SetIsFixedPosition(!nsLayoutUtils::ScrolledByViewportScrolling(
1513 0 : activeScrolledRoot, mBuilder));
1514 :
1515 0 : InvalidateForLayerChange(item, thebesLayer);
1516 :
1517 : mBuilder->LayerBuilder()->
1518 : AddThebesDisplayItem(thebesLayer, item, aClip, mContainerFrame,
1519 0 : layerState);
1520 : }
1521 : }
1522 0 : }
1523 :
1524 : void
1525 0 : ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, Layer* aNewLayer)
1526 : {
1527 0 : nsIFrame* f = aItem->GetUnderlyingFrame();
1528 0 : NS_ASSERTION(f, "Display items that render using Thebes must have a frame");
1529 0 : PRUint32 key = aItem->GetPerFrameKey();
1530 0 : NS_ASSERTION(key, "Display items that render using Thebes must have a key");
1531 0 : Layer* oldLayer = mBuilder->LayerBuilder()->GetOldLayerFor(f, key);
1532 0 : if (!oldLayer) {
1533 : // Nothing to do here, this item didn't have a layer before
1534 0 : return;
1535 : }
1536 0 : if (aNewLayer != oldLayer) {
1537 : // The item has changed layers.
1538 : // Invalidate the bounds in the old layer and new layer.
1539 : // The bounds might have changed, but we assume that any difference
1540 : // in the bounds will have been invalidated for all Thebes layers
1541 : // in the container via regular frame invalidation.
1542 0 : nsRect bounds = aItem->GetBounds(mBuilder);
1543 0 : PRInt32 appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
1544 :
1545 0 : ThebesLayer* t = oldLayer->AsThebesLayer();
1546 0 : if (t) {
1547 : ThebesDisplayItemLayerUserData* data =
1548 0 : static_cast<ThebesDisplayItemLayerUserData*>(t->GetUserData(&gThebesDisplayItemLayerUserData));
1549 : // Note that whenever the layer's scale changes, we invalidate the whole thing,
1550 : // so it doesn't matter whether we are using the old scale at last paint
1551 : // or a new scale here
1552 : InvalidatePostTransformRegion(t,
1553 : bounds.ScaleToOutsidePixels(data->mXScale, data->mYScale, appUnitsPerDevPixel),
1554 0 : mBuilder->LayerBuilder()->GetLastPaintOffset(t));
1555 : }
1556 0 : if (aNewLayer) {
1557 0 : ThebesLayer* newLayer = aNewLayer->AsThebesLayer();
1558 0 : if (newLayer) {
1559 : ThebesDisplayItemLayerUserData* data =
1560 0 : static_cast<ThebesDisplayItemLayerUserData*>(newLayer->GetUserData(&gThebesDisplayItemLayerUserData));
1561 : InvalidatePostTransformRegion(newLayer,
1562 : bounds.ScaleToOutsidePixels(data->mXScale, data->mYScale, appUnitsPerDevPixel),
1563 0 : GetTranslationForThebesLayer(newLayer));
1564 : }
1565 : }
1566 :
1567 0 : NS_ASSERTION(appUnitsPerDevPixel ==
1568 : mContainerFrame->PresContext()->AppUnitsPerDevPixel(),
1569 : "app units per dev pixel should be constant in a container");
1570 : mContainerFrame->InvalidateWithFlags(
1571 0 : bounds - mBuilder->ToReferenceFrame(mContainerFrame),
1572 : nsIFrame::INVALIDATE_NO_THEBES_LAYERS |
1573 0 : nsIFrame::INVALIDATE_EXCLUDE_CURRENT_PAINT);
1574 : }
1575 : }
1576 :
1577 : bool
1578 0 : FrameLayerBuilder::NeedToInvalidateFixedDisplayItem(nsDisplayListBuilder* aBuilder,
1579 : nsDisplayItem* aItem)
1580 : {
1581 0 : return !aItem->ShouldFixToViewport(aBuilder) ||
1582 0 : !HasRetainedLayerFor(aItem->GetUnderlyingFrame(), aItem->GetPerFrameKey());
1583 : }
1584 :
1585 : void
1586 0 : FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer,
1587 : nsDisplayItem* aItem,
1588 : const Clip& aClip,
1589 : nsIFrame* aContainerLayerFrame,
1590 : LayerState aLayerState)
1591 : {
1592 0 : AddLayerDisplayItem(aLayer, aItem, aLayerState);
1593 :
1594 0 : ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
1595 0 : if (entry) {
1596 0 : entry->mContainerLayerFrame = aContainerLayerFrame;
1597 0 : NS_ASSERTION(aItem->GetUnderlyingFrame(), "Must have frame");
1598 : ClippedDisplayItem* cdi =
1599 0 : entry->mItems.AppendElement(ClippedDisplayItem(aItem, aClip));
1600 0 : cdi->mInactiveLayer = aLayerState != LAYER_NONE;
1601 : }
1602 0 : }
1603 :
1604 : void
1605 0 : FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
1606 : nsDisplayItem* aItem,
1607 : LayerState aLayerState)
1608 : {
1609 0 : if (aLayer->Manager() != mRetainingManager)
1610 0 : return;
1611 :
1612 0 : nsIFrame* f = aItem->GetUnderlyingFrame();
1613 0 : DisplayItemDataEntry* entry = mNewDisplayItemData.PutEntry(f);
1614 0 : if (entry) {
1615 0 : entry->mData.AppendElement(DisplayItemData(aLayer, aItem->GetPerFrameKey(), aLayerState));
1616 : }
1617 : }
1618 :
1619 : nsIntPoint
1620 0 : FrameLayerBuilder::GetLastPaintOffset(ThebesLayer* aLayer)
1621 : {
1622 0 : ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
1623 0 : if (entry && entry->mHasExplicitLastPaintOffset)
1624 0 : return entry->mLastPaintOffset;
1625 0 : return GetTranslationForThebesLayer(aLayer);
1626 : }
1627 :
1628 : void
1629 0 : FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer* aLayer)
1630 : {
1631 0 : ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
1632 0 : if (entry) {
1633 0 : entry->mLastPaintOffset = GetTranslationForThebesLayer(aLayer);
1634 0 : entry->mHasExplicitLastPaintOffset = true;
1635 : }
1636 0 : }
1637 :
1638 : nscolor
1639 0 : FrameLayerBuilder::FindOpaqueColorCovering(nsDisplayListBuilder* aBuilder,
1640 : ThebesLayer* aLayer,
1641 : const nsRect& aRect)
1642 : {
1643 0 : ThebesLayerItemsEntry* entry = mThebesLayerItems.GetEntry(aLayer);
1644 0 : NS_ASSERTION(entry, "Must know about this layer!");
1645 0 : for (PRInt32 i = entry->mItems.Length() - 1; i >= 0; --i) {
1646 0 : nsDisplayItem* item = entry->mItems[i].mItem;
1647 0 : const nsRect& visible = item->GetVisibleRect();
1648 0 : if (!visible.Intersects(aRect))
1649 0 : continue;
1650 :
1651 : nscolor color;
1652 0 : if (visible.Contains(aRect) && item->IsUniform(aBuilder, &color) &&
1653 : NS_GET_A(color) == 255)
1654 0 : return color;
1655 0 : break;
1656 : }
1657 0 : return NS_RGBA(0,0,0,0);
1658 : }
1659 :
1660 : void
1661 0 : ContainerState::CollectOldLayers()
1662 : {
1663 0 : for (Layer* layer = mContainerLayer->GetFirstChild(); layer;
1664 : layer = layer->GetNextSibling()) {
1665 0 : if (layer->HasUserData(&gColorLayerUserData)) {
1666 0 : mRecycledColorLayers.AppendElement(static_cast<ColorLayer*>(layer));
1667 0 : } else if (layer->HasUserData(&gImageLayerUserData)) {
1668 0 : mRecycledImageLayers.AppendElement(static_cast<ImageLayer*>(layer));
1669 0 : } else if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
1670 0 : NS_ASSERTION(layer->AsThebesLayer(), "Wrong layer type");
1671 0 : mRecycledThebesLayers.AppendElement(static_cast<ThebesLayer*>(layer));
1672 : }
1673 : }
1674 0 : }
1675 :
1676 : void
1677 0 : ContainerState::Finish(PRUint32* aTextContentFlags)
1678 : {
1679 0 : while (!mThebesLayerDataStack.IsEmpty()) {
1680 0 : PopThebesLayerData();
1681 : }
1682 :
1683 0 : PRUint32 textContentFlags = 0;
1684 :
1685 0 : for (PRUint32 i = 0; i <= mNewChildLayers.Length(); ++i) {
1686 : // An invariant of this loop is that the layers in mNewChildLayers
1687 : // with index < i are the first i child layers of mContainerLayer.
1688 : Layer* layer;
1689 0 : if (i < mNewChildLayers.Length()) {
1690 0 : layer = mNewChildLayers[i];
1691 0 : if (!layer->GetVisibleRegion().IsEmpty()) {
1692 0 : textContentFlags |= layer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA;
1693 : }
1694 0 : if (!layer->GetParent()) {
1695 : // This is not currently a child of the container, so just add it
1696 : // now.
1697 0 : Layer* prevChild = i == 0 ? nsnull : mNewChildLayers[i - 1].get();
1698 0 : mContainerLayer->InsertAfter(layer, prevChild);
1699 0 : continue;
1700 : }
1701 0 : NS_ASSERTION(layer->GetParent() == mContainerLayer,
1702 : "Layer shouldn't be the child of some other container");
1703 : } else {
1704 0 : layer = nsnull;
1705 : }
1706 :
1707 : // If layer is non-null, then it's already a child of the container,
1708 : // so scan forward until we find it, removing the other layers we
1709 : // don't want here.
1710 : // If it's null, scan forward until we've removed all the leftover
1711 : // children.
1712 0 : Layer* nextOldChild = i == 0 ? mContainerLayer->GetFirstChild() :
1713 0 : mNewChildLayers[i - 1]->GetNextSibling();
1714 0 : while (nextOldChild != layer) {
1715 0 : Layer* tmp = nextOldChild;
1716 0 : nextOldChild = nextOldChild->GetNextSibling();
1717 0 : mContainerLayer->RemoveChild(tmp);
1718 : }
1719 : // If non-null, 'layer' is now in the right place in the list, so we
1720 : // can just move on to the next one.
1721 : }
1722 :
1723 0 : *aTextContentFlags = textContentFlags;
1724 0 : }
1725 :
1726 : static FrameLayerBuilder::ContainerParameters
1727 0 : ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
1728 : nsIFrame* aContainerFrame,
1729 : const gfx3DMatrix* aTransform,
1730 : const FrameLayerBuilder::ContainerParameters& aIncomingScale,
1731 : ContainerLayer* aLayer)
1732 : {
1733 : gfx3DMatrix transform =
1734 0 : gfx3DMatrix::ScalingMatrix(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
1735 0 : if (aTransform) {
1736 : // aTransform is applied first, then the scale is applied to the result
1737 0 : transform = (*aTransform)*transform;
1738 : }
1739 :
1740 0 : gfxMatrix transform2d;
1741 0 : bool canDraw2D = transform.CanDraw2D(&transform2d);
1742 0 : gfxSize scale;
1743 0 : bool isRetained = aLayerBuilder->GetRetainingLayerManager() == aLayer->Manager();
1744 : // Only fiddle with scale factors for the retaining layer manager, since
1745 : // it only matters for retained layers
1746 : // XXX Should we do something for 3D transforms?
1747 0 : if (canDraw2D && isRetained) {
1748 : //Scale factors are normalized to a power of 2 to reduce the number of resolution changes
1749 0 : scale = transform2d.ScaleFactors(true);
1750 : // For frames with a changing transform that's not just a translation,
1751 : // round scale factors up to nearest power-of-2 boundary so that we don't
1752 : // keep having to redraw the content as it scales up and down. Rounding up to nearest
1753 : // power-of-2 boundary ensures we never scale up, only down --- avoiding
1754 : // jaggies. It also ensures we never scale down by more than a factor of 2,
1755 : // avoiding bad downscaling quality.
1756 0 : gfxMatrix frameTransform;
1757 0 : if (aContainerFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer) &&
1758 : aTransform &&
1759 0 : (!aTransform->Is2D(&frameTransform) || frameTransform.HasNonTranslationOrFlip())) {
1760 : // Don't clamp the scale factor when the new desired scale factor matches the old one
1761 : // or it was previously unscaled.
1762 0 : bool clamp = true;
1763 0 : gfxMatrix oldFrameTransform2d;
1764 0 : if (aLayer->GetTransform().Is2D(&oldFrameTransform2d)) {
1765 0 : gfxSize oldScale = oldFrameTransform2d.ScaleFactors(true);
1766 0 : if (oldScale == scale || oldScale == gfxSize(1.0, 1.0))
1767 0 : clamp = false;
1768 : }
1769 0 : if (clamp) {
1770 0 : scale.width = gfxUtils::ClampToScaleFactor(scale.width);
1771 0 : scale.height = gfxUtils::ClampToScaleFactor(scale.height);
1772 : }
1773 : } else {
1774 : // XXX Do we need to move nearly-integer values to integers here?
1775 : }
1776 : // If the scale factors are too small, just use 1.0. The content is being
1777 : // scaled out of sight anyway.
1778 0 : if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) {
1779 0 : scale.width = scale.height = 1.0;
1780 0 : }
1781 : } else {
1782 0 : scale = gfxSize(1.0, 1.0);
1783 : }
1784 :
1785 : // Apply the inverse of our resolution-scale before the rest of our transform
1786 0 : transform = gfx3DMatrix::ScalingMatrix(1.0/scale.width, 1.0/scale.height, 1.0)*transform;
1787 0 : aLayer->SetTransform(transform);
1788 :
1789 : FrameLayerBuilder::ContainerParameters
1790 0 : result(scale.width, scale.height, aIncomingScale);
1791 0 : if (aTransform) {
1792 0 : result.mInTransformedSubtree = true;
1793 0 : if (aContainerFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer)) {
1794 0 : result.mInActiveTransformedSubtree = true;
1795 : }
1796 : }
1797 0 : if (isRetained && (!canDraw2D || transform2d.HasNonIntegerTranslation())) {
1798 0 : result.mDisableSubpixelAntialiasingInDescendants = true;
1799 : }
1800 : return result;
1801 : }
1802 :
1803 : already_AddRefed<ContainerLayer>
1804 0 : FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
1805 : LayerManager* aManager,
1806 : nsIFrame* aContainerFrame,
1807 : nsDisplayItem* aContainerItem,
1808 : const nsDisplayList& aChildren,
1809 : const ContainerParameters& aParameters,
1810 : const gfx3DMatrix* aTransform)
1811 : {
1812 0 : FrameProperties props = aContainerFrame->Properties();
1813 : PRUint32 containerDisplayItemKey =
1814 0 : aContainerItem ? aContainerItem->GetPerFrameKey() : 0;
1815 0 : NS_ASSERTION(aContainerFrame, "Container display items here should have a frame");
1816 0 : NS_ASSERTION(!aContainerItem ||
1817 : aContainerItem->GetUnderlyingFrame() == aContainerFrame,
1818 : "Container display item must match given frame");
1819 :
1820 0 : nsRefPtr<ContainerLayer> containerLayer;
1821 0 : if (aManager == mRetainingManager) {
1822 0 : Layer* oldLayer = GetOldLayerFor(aContainerFrame, containerDisplayItemKey);
1823 0 : if (oldLayer) {
1824 0 : NS_ASSERTION(oldLayer->Manager() == aManager, "Wrong manager");
1825 0 : if (oldLayer->HasUserData(&gThebesDisplayItemLayerUserData)) {
1826 : // The old layer for this item is actually our ThebesLayer
1827 : // because we rendered its layer into that ThebesLayer. So we
1828 : // don't actually have a retained container layer.
1829 : } else {
1830 0 : NS_ASSERTION(oldLayer->GetType() == Layer::TYPE_CONTAINER,
1831 : "Wrong layer type");
1832 0 : containerLayer = static_cast<ContainerLayer*>(oldLayer);
1833 : // Clear clip rect; the caller will set it if necessary.
1834 0 : containerLayer->SetClipRect(nsnull);
1835 : }
1836 : }
1837 : }
1838 0 : if (!containerLayer) {
1839 : // No suitable existing layer was found.
1840 0 : containerLayer = aManager->CreateContainerLayer();
1841 0 : if (!containerLayer)
1842 0 : return nsnull;
1843 : }
1844 :
1845 0 : if (aContainerItem &&
1846 0 : aContainerItem->GetLayerState(aBuilder, aManager) == LAYER_ACTIVE_EMPTY) {
1847 : // Empty layers only have metadata and should never have display items. We
1848 : // early exit because later, invalidation will walk up the frame tree to
1849 : // determine which thebes layer gets invalidated. Since an empty layer
1850 : // should never have anything to paint, it should never be invalidated.
1851 0 : NS_ASSERTION(aChildren.IsEmpty(), "Should have no children");
1852 0 : return containerLayer.forget();
1853 : }
1854 :
1855 : ContainerParameters scaleParameters =
1856 : ChooseScaleAndSetTransform(this, aContainerFrame, aTransform, aParameters,
1857 0 : containerLayer);
1858 : ContainerState state(aBuilder, aManager, aContainerFrame, containerLayer,
1859 0 : scaleParameters);
1860 0 : nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
1861 :
1862 0 : if (aManager == mRetainingManager) {
1863 0 : DisplayItemDataEntry* entry = mNewDisplayItemData.PutEntry(aContainerFrame);
1864 0 : if (entry) {
1865 : entry->mData.AppendElement(
1866 0 : DisplayItemData(containerLayer, containerDisplayItemKey, LAYER_ACTIVE));
1867 : }
1868 :
1869 : nsPoint* offsetAtLastPaint = static_cast<nsPoint*>
1870 0 : (props.Get(ThebesLayerLastPaintOffsetProperty()));
1871 0 : nsPoint currentOffset = aBuilder->ToReferenceFrame(aContainerFrame);
1872 :
1873 : nsRegion* invalidThebesContent(static_cast<nsRegion*>
1874 0 : (props.Get(ThebesLayerInvalidRegionProperty())));
1875 0 : if (invalidThebesContent) {
1876 0 : nsPoint offset = offsetAtLastPaint ? *offsetAtLastPaint : currentOffset;
1877 0 : invalidThebesContent->MoveBy(offset);
1878 : state.SetInvalidThebesContent(invalidThebesContent->
1879 : ScaleToOutsidePixels(scaleParameters.mXScale, scaleParameters.mYScale,
1880 0 : appUnitsPerDevPixel));
1881 : // We have to preserve the current contents of invalidThebesContent
1882 : // because there might be multiple container layers for the same
1883 : // frame and we need to invalidate the ThebesLayer children of all
1884 : // of them.
1885 0 : invalidThebesContent->MoveBy(-offset);
1886 : } else {
1887 : // The region was deleted to indicate that everything should be
1888 : // invalidated.
1889 0 : state.SetInvalidateAllThebesContent();
1890 : }
1891 0 : SetHasContainerLayer(aContainerFrame, currentOffset);
1892 : }
1893 :
1894 0 : Clip clip;
1895 0 : state.ProcessDisplayItems(aChildren, clip);
1896 :
1897 : // Set CONTENT_COMPONENT_ALPHA if any of our children have it.
1898 : // This is suboptimal ... a child could have text that's over transparent
1899 : // pixels in its own layer, but over opaque parts of previous siblings.
1900 : PRUint32 flags;
1901 0 : state.Finish(&flags);
1902 :
1903 0 : nsRect bounds = state.GetChildrenBounds();
1904 0 : NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds");
1905 : nsIntRect pixBounds =
1906 : bounds.ScaleToOutsidePixels(scaleParameters.mXScale, scaleParameters.mYScale,
1907 0 : appUnitsPerDevPixel);
1908 0 : containerLayer->SetVisibleRegion(pixBounds);
1909 : // Make sure that rounding the visible region out didn't add any area
1910 : // we won't paint
1911 0 : if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) {
1912 0 : bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale);
1913 0 : if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) {
1914 : // Clear CONTENT_COMPONENT_ALPHA
1915 0 : flags = Layer::CONTENT_OPAQUE;
1916 : }
1917 : }
1918 0 : containerLayer->SetContentFlags(flags);
1919 :
1920 0 : return containerLayer.forget();
1921 : }
1922 :
1923 : Layer*
1924 0 : FrameLayerBuilder::GetLeafLayerFor(nsDisplayListBuilder* aBuilder,
1925 : LayerManager* aManager,
1926 : nsDisplayItem* aItem)
1927 : {
1928 0 : if (aManager != mRetainingManager)
1929 0 : return nsnull;
1930 :
1931 0 : nsIFrame* f = aItem->GetUnderlyingFrame();
1932 0 : NS_ASSERTION(f, "Can only call GetLeafLayerFor on items that have a frame");
1933 0 : Layer* layer = GetOldLayerFor(f, aItem->GetPerFrameKey());
1934 0 : if (!layer)
1935 0 : return nsnull;
1936 0 : if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
1937 : // This layer was created to render Thebes-rendered content for this
1938 : // display item. The display item should not use it for its own
1939 : // layer rendering.
1940 0 : return nsnull;
1941 : }
1942 : // Clear clip rect; the caller is responsible for setting it.
1943 0 : layer->SetClipRect(nsnull);
1944 0 : return layer;
1945 : }
1946 :
1947 : /* static */ void
1948 0 : FrameLayerBuilder::InvalidateThebesLayerContents(nsIFrame* aFrame,
1949 : const nsRect& aRect)
1950 : {
1951 : nsRegion* invalidThebesContent = static_cast<nsRegion*>
1952 0 : (aFrame->Properties().Get(ThebesLayerInvalidRegionProperty()));
1953 0 : if (!invalidThebesContent)
1954 0 : return;
1955 0 : invalidThebesContent->Or(*invalidThebesContent, aRect);
1956 0 : invalidThebesContent->SimplifyOutward(20);
1957 : }
1958 :
1959 : /**
1960 : * Returns true if we find a descendant with a container layer
1961 : */
1962 : static bool
1963 0 : InternalInvalidateThebesLayersInSubtree(nsIFrame* aFrame)
1964 : {
1965 0 : if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT))
1966 0 : return false;
1967 :
1968 0 : bool foundContainerLayer = false;
1969 0 : if (aFrame->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER) {
1970 : // Delete the invalid region to indicate that all Thebes contents
1971 : // need to be invalidated
1972 0 : aFrame->Properties().Delete(ThebesLayerInvalidRegionProperty());
1973 0 : foundContainerLayer = true;
1974 : }
1975 :
1976 0 : nsAutoTArray<nsIFrame::ChildList,4> childListArray;
1977 0 : if (!aFrame->GetFirstPrincipalChild()) {
1978 0 : nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(aFrame);
1979 0 : if (subdocumentFrame) {
1980 : // Descend into the subdocument
1981 0 : nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
1982 0 : if (root) {
1983 : childListArray.AppendElement(nsIFrame::ChildList(
1984 0 : nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
1985 0 : nsIFrame::kPrincipalList));
1986 : }
1987 : }
1988 : }
1989 :
1990 0 : aFrame->GetChildLists(&childListArray);
1991 0 : nsIFrame::ChildListArrayIterator lists(childListArray);
1992 0 : for (; !lists.IsDone(); lists.Next()) {
1993 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
1994 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
1995 0 : if (InternalInvalidateThebesLayersInSubtree(childFrames.get())) {
1996 0 : foundContainerLayer = true;
1997 : }
1998 : }
1999 : }
2000 :
2001 0 : if (!foundContainerLayer) {
2002 0 : aFrame->RemoveStateBits(NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT);
2003 : }
2004 0 : return foundContainerLayer;
2005 : }
2006 :
2007 : /* static */ void
2008 0 : FrameLayerBuilder::InvalidateThebesLayersInSubtree(nsIFrame* aFrame)
2009 : {
2010 0 : InternalInvalidateThebesLayersInSubtree(aFrame);
2011 0 : }
2012 :
2013 : /* static */ void
2014 0 : FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager)
2015 : {
2016 : LayerManagerData* data = static_cast<LayerManagerData*>
2017 0 : (aManager->GetUserData(&gLayerManagerUserData));
2018 0 : if (data) {
2019 0 : data->mInvalidateAllLayers = true;
2020 : }
2021 0 : }
2022 :
2023 : /* static */
2024 : Layer*
2025 0 : FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, PRUint32 aDisplayItemKey)
2026 : {
2027 0 : nsTArray<DisplayItemData>* array = GetDisplayItemDataArrayForFrame(aFrame);
2028 0 : if (!array)
2029 0 : return nsnull;
2030 :
2031 0 : for (PRUint32 i = 0; i < array->Length(); ++i) {
2032 0 : if (array->ElementAt(i).mDisplayItemKey == aDisplayItemKey) {
2033 0 : Layer* layer = array->ElementAt(i).mLayer;
2034 0 : if (!layer->HasUserData(&gColorLayerUserData) &&
2035 0 : !layer->HasUserData(&gImageLayerUserData) &&
2036 0 : !layer->HasUserData(&gThebesDisplayItemLayerUserData))
2037 0 : return layer;
2038 : }
2039 : }
2040 0 : return nsnull;
2041 : }
2042 :
2043 : #ifdef MOZ_DUMP_PAINTING
2044 0 : static void DebugPaintItem(nsRenderingContext* aDest, nsDisplayItem *aItem, nsDisplayListBuilder* aBuilder)
2045 : {
2046 0 : nsRect appUnitBounds = aItem->GetBounds(aBuilder);
2047 0 : gfxRect bounds(appUnitBounds.x, appUnitBounds.y, appUnitBounds.width, appUnitBounds.height);
2048 0 : bounds.ScaleInverse(aDest->AppUnitsPerDevPixel());
2049 :
2050 : nsRefPtr<gfxASurface> surf =
2051 0 : gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height),
2052 0 : gfxASurface::CONTENT_COLOR_ALPHA);
2053 0 : surf->SetDeviceOffset(-bounds.TopLeft());
2054 0 : nsRefPtr<gfxContext> context = new gfxContext(surf);
2055 0 : nsRefPtr<nsRenderingContext> ctx = new nsRenderingContext();
2056 0 : ctx->Init(aDest->DeviceContext(), context);
2057 :
2058 0 : aItem->Paint(aBuilder, ctx);
2059 0 : DumpPaintedImage(aItem, surf);
2060 0 : aItem->SetPainted();
2061 :
2062 0 : surf->SetDeviceOffset(gfxPoint(0, 0));
2063 0 : aDest->ThebesContext()->SetSource(surf, bounds.TopLeft());
2064 0 : aDest->ThebesContext()->Rectangle(bounds);
2065 0 : aDest->ThebesContext()->Fill();
2066 0 : }
2067 : #endif
2068 :
2069 : /*
2070 : * A note on residual transforms:
2071 : *
2072 : * In a transformed subtree we sometimes apply the ThebesLayer's
2073 : * "residual transform" when drawing content into the ThebesLayer.
2074 : * This is a translation by components in the range [-0.5,0.5) provided
2075 : * by the layer system; applying the residual transform followed by the
2076 : * transforms used by layer compositing ensures that the subpixel alignment
2077 : * of the content of the ThebesLayer exactly matches what it would be if
2078 : * we used cairo/Thebes to draw directly to the screen without going through
2079 : * retained layer buffers.
2080 : *
2081 : * The visible and valid regions of the ThebesLayer are computed without
2082 : * knowing the residual transform (because we don't know what the residual
2083 : * transform is going to be until we've built the layer tree!). So we have to
2084 : * consider whether content painted in the range [x, xmost) might be painted
2085 : * outside the visible region we computed for that content. The visible region
2086 : * would be [floor(x), ceil(xmost)). The content would be rendered at
2087 : * [x + r, xmost + r), where -0.5 <= r < 0.5. So some half-rendered pixels could
2088 : * indeed fall outside the computed visible region, which is not a big deal;
2089 : * similar issues already arise when we snap cliprects to nearest pixels.
2090 : * Note that if the rendering of the content is snapped to nearest pixels ---
2091 : * which it often is --- then the content is actually rendered at
2092 : * [snap(x + r), snap(xmost + r)). It turns out that floor(x) <= snap(x + r)
2093 : * and ceil(xmost) >= snap(xmost + r), so the rendering of snapped content
2094 : * always falls within the visible region we computed.
2095 : */
2096 :
2097 : /* static */ void
2098 0 : FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
2099 : gfxContext* aContext,
2100 : const nsIntRegion& aRegionToDraw,
2101 : const nsIntRegion& aRegionToInvalidate,
2102 : void* aCallbackData)
2103 : {
2104 0 : SAMPLE_LABEL("gfx", "DrawThebesLayer");
2105 :
2106 : nsDisplayListBuilder* builder = static_cast<nsDisplayListBuilder*>
2107 0 : (aCallbackData);
2108 :
2109 0 : if (builder->LayerBuilder()->CheckDOMModified())
2110 : return;
2111 :
2112 0 : nsTArray<ClippedDisplayItem> items;
2113 : nsIFrame* containerLayerFrame;
2114 : {
2115 : ThebesLayerItemsEntry* entry =
2116 0 : builder->LayerBuilder()->mThebesLayerItems.GetEntry(aLayer);
2117 0 : NS_ASSERTION(entry, "We shouldn't be drawing into a layer with no items!");
2118 0 : items.SwapElements(entry->mItems);
2119 0 : containerLayerFrame = entry->mContainerLayerFrame;
2120 : // Later after this point, due to calls to DidEndTransaction
2121 : // for temporary layer managers, mThebesLayerItems can change,
2122 : // so 'entry' could become invalid.
2123 : }
2124 :
2125 : ThebesDisplayItemLayerUserData* userData =
2126 : static_cast<ThebesDisplayItemLayerUserData*>
2127 0 : (aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
2128 0 : NS_ASSERTION(userData, "where did our user data go?");
2129 0 : if (NS_GET_A(userData->mForcedBackgroundColor) > 0) {
2130 0 : nsIntRect r = aLayer->GetVisibleRegion().GetBounds();
2131 0 : aContext->NewPath();
2132 0 : aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
2133 0 : aContext->SetColor(gfxRGBA(userData->mForcedBackgroundColor));
2134 0 : aContext->Fill();
2135 : }
2136 :
2137 : // make the origin of the context coincide with the origin of the
2138 : // ThebesLayer
2139 0 : gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
2140 0 : nsIntPoint offset = GetTranslationForThebesLayer(aLayer);
2141 : // Apply the residual transform if it has been enabled, to ensure that
2142 : // snapping when we draw into aContext exactly matches the ideal transform.
2143 : // See above for why this is OK.
2144 0 : aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y));
2145 0 : aContext->Scale(userData->mXScale, userData->mYScale);
2146 :
2147 0 : nsPresContext* presContext = containerLayerFrame->PresContext();
2148 0 : PRInt32 appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
2149 0 : if (!aRegionToInvalidate.IsEmpty()) {
2150 : nsRect r = (aRegionToInvalidate.GetBounds() + offset).
2151 0 : ToAppUnits(appUnitsPerDevPixel);
2152 0 : r.ScaleInverseRoundOut(userData->mXScale, userData->mYScale);
2153 : containerLayerFrame->InvalidateWithFlags(r,
2154 : nsIFrame::INVALIDATE_NO_THEBES_LAYERS |
2155 0 : nsIFrame::INVALIDATE_EXCLUDE_CURRENT_PAINT);
2156 : }
2157 :
2158 : PRUint32 i;
2159 : // Update visible regions. We need perform visibility analysis again
2160 : // because we may be asked to draw into part of a ThebesLayer that
2161 : // isn't actually visible in the window (e.g., because a ThebesLayer
2162 : // expanded its visible region to a rectangle internally), in which
2163 : // case the mVisibleRect stored in the display item may be wrong.
2164 0 : nsRegion visible = aRegionToDraw.ToAppUnits(appUnitsPerDevPixel);
2165 : visible.MoveBy(NSIntPixelsToAppUnits(offset.x, appUnitsPerDevPixel),
2166 0 : NSIntPixelsToAppUnits(offset.y, appUnitsPerDevPixel));
2167 0 : visible.ScaleInverseRoundOut(userData->mXScale, userData->mYScale);
2168 :
2169 0 : for (i = items.Length(); i > 0; --i) {
2170 0 : ClippedDisplayItem* cdi = &items[i - 1];
2171 :
2172 0 : NS_ASSERTION(AppUnitsPerDevPixel(cdi->mItem) == appUnitsPerDevPixel,
2173 : "a thebes layer should contain items only at the same zoom");
2174 :
2175 0 : NS_ABORT_IF_FALSE(cdi->mClip.mHaveClipRect ||
2176 : cdi->mClip.mRoundedClipRects.IsEmpty(),
2177 : "If we have rounded rects, we must have a clip rect");
2178 :
2179 0 : if (!cdi->mClip.mHaveClipRect ||
2180 0 : (cdi->mClip.mRoundedClipRects.IsEmpty() &&
2181 0 : cdi->mClip.mClipRect.Contains(visible.GetBounds()))) {
2182 0 : cdi->mItem->RecomputeVisibility(builder, &visible);
2183 0 : continue;
2184 : }
2185 :
2186 : // Do a little dance to account for the fact that we're clipping
2187 : // to cdi->mClipRect
2188 0 : nsRegion clipped;
2189 0 : clipped.And(visible, cdi->mClip.mClipRect);
2190 0 : nsRegion finalClipped = clipped;
2191 0 : cdi->mItem->RecomputeVisibility(builder, &finalClipped);
2192 : // If we have rounded clip rects, don't subtract from the visible
2193 : // region since we aren't displaying everything inside the rect.
2194 0 : if (cdi->mClip.mRoundedClipRects.IsEmpty()) {
2195 0 : nsRegion removed;
2196 0 : removed.Sub(clipped, finalClipped);
2197 0 : nsRegion newVisible;
2198 0 : newVisible.Sub(visible, removed);
2199 : // Don't let the visible region get too complex.
2200 0 : if (newVisible.GetNumRects() <= 15) {
2201 0 : visible = newVisible;
2202 : }
2203 : }
2204 0 : if (!cdi->mClip.IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) {
2205 0 : cdi->mClip.RemoveRoundedCorners();
2206 : }
2207 : }
2208 :
2209 0 : nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
2210 0 : rc->Init(presContext->DeviceContext(), aContext);
2211 :
2212 0 : Clip currentClip;
2213 0 : bool setClipRect = false;
2214 :
2215 0 : for (i = 0; i < items.Length(); ++i) {
2216 0 : ClippedDisplayItem* cdi = &items[i];
2217 :
2218 0 : if (cdi->mItem->GetVisibleRect().IsEmpty())
2219 0 : continue;
2220 :
2221 : // If the new desired clip state is different from the current state,
2222 : // update the clip.
2223 0 : if (setClipRect != cdi->mClip.mHaveClipRect ||
2224 0 : (cdi->mClip.mHaveClipRect && cdi->mClip != currentClip)) {
2225 0 : if (setClipRect) {
2226 0 : aContext->Restore();
2227 : }
2228 0 : setClipRect = cdi->mClip.mHaveClipRect;
2229 0 : if (setClipRect) {
2230 0 : currentClip = cdi->mClip;
2231 0 : aContext->Save();
2232 0 : currentClip.ApplyTo(aContext, presContext);
2233 : }
2234 : }
2235 :
2236 0 : if (cdi->mInactiveLayer) {
2237 0 : PaintInactiveLayer(builder, cdi->mItem, aContext);
2238 : } else {
2239 0 : nsIFrame* frame = cdi->mItem->GetUnderlyingFrame();
2240 0 : if (frame) {
2241 0 : frame->AddStateBits(NS_FRAME_PAINTED_THEBES);
2242 : }
2243 : #ifdef MOZ_DUMP_PAINTING
2244 :
2245 0 : if (gfxUtils::sDumpPainting) {
2246 0 : DebugPaintItem(rc, cdi->mItem, builder);
2247 : } else {
2248 : #else
2249 : {
2250 : #endif
2251 0 : cdi->mItem->Paint(builder, rc);
2252 : }
2253 : }
2254 :
2255 0 : if (builder->LayerBuilder()->CheckDOMModified())
2256 0 : break;
2257 : }
2258 :
2259 0 : if (setClipRect) {
2260 0 : aContext->Restore();
2261 : }
2262 :
2263 0 : FlashPaint(aContext);
2264 : }
2265 :
2266 : bool
2267 0 : FrameLayerBuilder::CheckDOMModified()
2268 : {
2269 0 : if (!mRootPresContext ||
2270 0 : mInitialDOMGeneration == mRootPresContext->GetDOMGeneration())
2271 0 : return false;
2272 0 : if (mDetectedDOMModification) {
2273 : // Don't spam the console with extra warnings
2274 0 : return true;
2275 : }
2276 0 : mDetectedDOMModification = true;
2277 : // Painting is not going to complete properly. There's not much
2278 : // we can do here though. Invalidating the window to get another repaint
2279 : // is likely to lead to an infinite repaint loop.
2280 0 : NS_WARNING("Detected DOM modification during paint, bailing out!");
2281 0 : return true;
2282 : }
2283 :
2284 : #ifdef MOZ_DUMP_PAINTING
2285 : void
2286 0 : FrameLayerBuilder::DumpRetainedLayerTree(FILE* aFile)
2287 : {
2288 0 : if (mRetainingManager) {
2289 0 : mRetainingManager->Dump(aFile);
2290 : }
2291 0 : }
2292 : #endif
2293 :
2294 0 : FrameLayerBuilder::Clip::Clip(const Clip& aOther, nsDisplayItem* aClipItem)
2295 : : mRoundedClipRects(aOther.mRoundedClipRects),
2296 0 : mHaveClipRect(true)
2297 : {
2298 0 : nsDisplayItem::Type type = aClipItem->GetType();
2299 0 : NS_ABORT_IF_FALSE(type == nsDisplayItem::TYPE_CLIP ||
2300 : type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT,
2301 : "unexpected display item type");
2302 0 : nsDisplayClip* item = static_cast<nsDisplayClip*>(aClipItem);
2303 : // Always intersect with mClipRect, even if we're going to add a
2304 : // rounded rect.
2305 0 : if (aOther.mHaveClipRect) {
2306 0 : mClipRect.IntersectRect(aOther.mClipRect, item->GetClipRect());
2307 : } else {
2308 0 : mClipRect = item->GetClipRect();
2309 : }
2310 :
2311 0 : if (type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
2312 0 : RoundedRect *rr = mRoundedClipRects.AppendElement();
2313 0 : if (rr) {
2314 0 : rr->mRect = item->GetClipRect();
2315 0 : static_cast<nsDisplayClipRoundedRect*>(item)->GetRadii(rr->mRadii);
2316 : }
2317 : }
2318 :
2319 : // FIXME: Optimize away excess rounded rectangles due to the new addition.
2320 0 : }
2321 :
2322 : void
2323 0 : FrameLayerBuilder::Clip::ApplyTo(gfxContext* aContext,
2324 : nsPresContext* aPresContext)
2325 : {
2326 0 : aContext->NewPath();
2327 0 : PRInt32 A2D = aPresContext->AppUnitsPerDevPixel();
2328 0 : gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D);
2329 0 : aContext->Rectangle(clip, true);
2330 0 : aContext->Clip();
2331 :
2332 0 : for (PRUint32 i = 0, iEnd = mRoundedClipRects.Length();
2333 : i < iEnd; ++i) {
2334 0 : const Clip::RoundedRect &rr = mRoundedClipRects[i];
2335 :
2336 0 : gfxCornerSizes pixelRadii;
2337 0 : nsCSSRendering::ComputePixelRadii(rr.mRadii, A2D, &pixelRadii);
2338 :
2339 0 : clip = nsLayoutUtils::RectToGfxRect(rr.mRect, A2D);
2340 0 : clip.Round();
2341 0 : clip.Condition();
2342 : // REVIEW: This might make clip empty. Is that OK?
2343 :
2344 0 : aContext->NewPath();
2345 0 : aContext->RoundedRectangle(clip, pixelRadii);
2346 0 : aContext->Clip();
2347 : }
2348 0 : }
2349 :
2350 : nsRect
2351 0 : FrameLayerBuilder::Clip::ApproximateIntersect(const nsRect& aRect) const
2352 : {
2353 0 : nsRect r = aRect;
2354 0 : if (mHaveClipRect) {
2355 0 : r.IntersectRect(r, mClipRect);
2356 : }
2357 0 : for (PRUint32 i = 0, iEnd = mRoundedClipRects.Length();
2358 : i < iEnd; ++i) {
2359 0 : const Clip::RoundedRect &rr = mRoundedClipRects[i];
2360 0 : nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r);
2361 0 : r = rgn.GetLargestRectangle();
2362 : }
2363 : return r;
2364 : }
2365 :
2366 : // Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter)
2367 : // and radii aXRadius, aYRadius.
2368 0 : bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint,
2369 : nscoord aYRadius, nscoord aYCenter, nscoord aYPoint)
2370 : {
2371 0 : float scaledX = float(aXPoint - aXCenter) / float(aXRadius);
2372 0 : float scaledY = float(aYPoint - aYCenter) / float(aYRadius);
2373 0 : return scaledX * scaledX + scaledY * scaledY < 1.0f;
2374 : }
2375 :
2376 : bool
2377 0 : FrameLayerBuilder::Clip::IsRectClippedByRoundedCorner(const nsRect& aRect) const
2378 : {
2379 0 : if (mRoundedClipRects.IsEmpty())
2380 0 : return false;
2381 :
2382 0 : nsRect rect;
2383 0 : rect.IntersectRect(aRect, NonRoundedIntersection());
2384 0 : for (PRUint32 i = 0, iEnd = mRoundedClipRects.Length();
2385 : i < iEnd; ++i) {
2386 0 : const Clip::RoundedRect &rr = mRoundedClipRects[i];
2387 : // top left
2388 0 : if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X] &&
2389 0 : rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y]) {
2390 0 : if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_LEFT_X],
2391 0 : rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X],
2392 : rect.x,
2393 : rr.mRadii[NS_CORNER_TOP_LEFT_Y],
2394 0 : rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y],
2395 0 : rect.y)) {
2396 0 : return true;
2397 : }
2398 : }
2399 : // top right
2400 0 : if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X] &&
2401 0 : rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y]) {
2402 0 : if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_RIGHT_X],
2403 0 : rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X],
2404 : rect.XMost(),
2405 : rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
2406 0 : rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
2407 0 : rect.y)) {
2408 0 : return true;
2409 : }
2410 : }
2411 : // bottom left
2412 0 : if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X] &&
2413 0 : rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y]) {
2414 0 : if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
2415 0 : rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
2416 : rect.x,
2417 : rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
2418 0 : rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
2419 0 : rect.YMost())) {
2420 0 : return true;
2421 : }
2422 : }
2423 : // bottom right
2424 0 : if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X] &&
2425 0 : rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y]) {
2426 0 : if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
2427 0 : rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
2428 : rect.XMost(),
2429 : rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
2430 0 : rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
2431 0 : rect.YMost())) {
2432 0 : return true;
2433 : }
2434 : }
2435 : }
2436 0 : return false;
2437 : }
2438 :
2439 : nsRect
2440 0 : FrameLayerBuilder::Clip::NonRoundedIntersection() const
2441 : {
2442 0 : nsRect result = mClipRect;
2443 0 : for (PRUint32 i = 0, iEnd = mRoundedClipRects.Length();
2444 : i < iEnd; ++i) {
2445 0 : result.IntersectRect(result, mRoundedClipRects[i].mRect);
2446 : }
2447 : return result;
2448 : }
2449 :
2450 : void
2451 0 : FrameLayerBuilder::Clip::RemoveRoundedCorners()
2452 : {
2453 0 : if (mRoundedClipRects.IsEmpty())
2454 0 : return;
2455 :
2456 0 : mClipRect = NonRoundedIntersection();
2457 0 : mRoundedClipRects.Clear();
2458 : }
2459 :
2460 : } // namespace mozilla
|