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 : #ifndef FRAMELAYERBUILDER_H_
39 : #define FRAMELAYERBUILDER_H_
40 :
41 : #include "nsTHashtable.h"
42 : #include "nsHashKeys.h"
43 : #include "nsTArray.h"
44 : #include "nsRegion.h"
45 : #include "nsIFrame.h"
46 : #include "Layers.h"
47 :
48 : class nsDisplayListBuilder;
49 : class nsDisplayList;
50 : class nsDisplayItem;
51 : class gfxContext;
52 : class nsRootPresContext;
53 :
54 : namespace mozilla {
55 :
56 : enum LayerState {
57 : LAYER_NONE,
58 : LAYER_INACTIVE,
59 : LAYER_ACTIVE,
60 : // Force an active layer even if it causes incorrect rendering, e.g.
61 : // when the layer has rounded rect clips.
62 : LAYER_ACTIVE_FORCE,
63 : // Special layer that is metadata only.
64 : LAYER_ACTIVE_EMPTY
65 : };
66 :
67 : /**
68 : * The FrameLayerBuilder belongs to an nsDisplayListBuilder and is
69 : * responsible for converting display lists into layer trees.
70 : *
71 : * The most important API in this class is BuildContainerLayerFor. This
72 : * method takes a display list as input and constructs a ContainerLayer
73 : * with child layers that render the contents of the display list. It
74 : * also updates userdata for the retained layer manager, and
75 : * DisplayItemDataProperty data for frames, to record the relationship
76 : * between frames and layers.
77 : *
78 : * That data enables us to retain layer trees. When constructing a
79 : * ContainerLayer, we first check to see if there's an existing
80 : * ContainerLayer for the same frame that can be recycled. If we recycle
81 : * it, we also try to reuse its existing ThebesLayer children to render
82 : * the display items without layers of their own. The idea is that by
83 : * recycling layers deterministically, we can ensure that when nothing
84 : * changes in a display list, we will reuse the existing layers without
85 : * changes.
86 : *
87 : * We expose a GetLeafLayerFor method that can be called by display items
88 : * that make their own layers (e.g. canvas and video); this method
89 : * locates the last layer used to render the display item, if any, and
90 : * return it as a candidate for recycling.
91 : *
92 : * FrameLayerBuilder sets up ThebesLayers so that 0,0 in the Thebes layer
93 : * corresponds to the (pixel-snapped) top-left of the aActiveScrolledRoot.
94 : * It sets up ContainerLayers so that 0,0 in the container layer
95 : * corresponds to the snapped top-left of the display list reference frame.
96 : *
97 : * When we construct a container layer, we know the transform that will be
98 : * applied to the layer. If the transform scales the content, we can get
99 : * better results when intermediate buffers are used by pushing some scale
100 : * from the container's transform down to the children. For ThebesLayer
101 : * children, the scaling can be achieved by changing the size of the layer
102 : * and drawing into it with increased or decreased resolution. By convention,
103 : * integer types (nsIntPoint/nsIntSize/nsIntRect/nsIntRegion) are all in layer
104 : * coordinates, post-scaling, whereas appunit types are all pre-scaling.
105 : */
106 0 : class FrameLayerBuilder {
107 : public:
108 : typedef layers::ContainerLayer ContainerLayer;
109 : typedef layers::Layer Layer;
110 : typedef layers::ThebesLayer ThebesLayer;
111 : typedef layers::LayerManager LayerManager;
112 :
113 0 : FrameLayerBuilder() :
114 : mRetainingManager(nsnull),
115 : mDetectedDOMModification(false),
116 0 : mInvalidateAllLayers(false)
117 : {
118 0 : mNewDisplayItemData.Init();
119 0 : mThebesLayerItems.Init();
120 0 : }
121 :
122 : void Init(nsDisplayListBuilder* aBuilder);
123 :
124 : /**
125 : * Call this to notify that we have just started a transaction on the
126 : * retained layer manager aManager.
127 : */
128 : void DidBeginRetainedLayerTransaction(LayerManager* aManager);
129 :
130 : /**
131 : * Call this just before we end a transaction on aManager. If aManager
132 : * is not the retained layer manager then it must be a temporary layer
133 : * manager that will not be used again.
134 : */
135 : void WillEndTransaction(LayerManager* aManager);
136 :
137 : /**
138 : * Call this after we end a transaction on aManager. If aManager
139 : * is not the retained layer manager then it must be a temporary layer
140 : * manager that will not be used again.
141 : */
142 : void DidEndTransaction(LayerManager* aManager);
143 :
144 : struct ContainerParameters {
145 0 : ContainerParameters() :
146 : mXScale(1), mYScale(1),
147 : mInTransformedSubtree(false), mInActiveTransformedSubtree(false),
148 0 : mDisableSubpixelAntialiasingInDescendants(false)
149 0 : {}
150 0 : ContainerParameters(float aXScale, float aYScale) :
151 : mXScale(aXScale), mYScale(aYScale),
152 : mInTransformedSubtree(false), mInActiveTransformedSubtree(false),
153 0 : mDisableSubpixelAntialiasingInDescendants(false)
154 0 : {}
155 0 : ContainerParameters(float aXScale, float aYScale,
156 : const ContainerParameters& aParent) :
157 : mXScale(aXScale), mYScale(aYScale),
158 : mInTransformedSubtree(aParent.mInTransformedSubtree),
159 : mInActiveTransformedSubtree(aParent.mInActiveTransformedSubtree),
160 0 : mDisableSubpixelAntialiasingInDescendants(aParent.mDisableSubpixelAntialiasingInDescendants)
161 0 : {}
162 : float mXScale, mYScale;
163 : bool mInTransformedSubtree;
164 : bool mInActiveTransformedSubtree;
165 : bool mDisableSubpixelAntialiasingInDescendants;
166 : };
167 : /**
168 : * Build a container layer for a display item that contains a child
169 : * list, either reusing an existing one or creating a new one. It
170 : * sets the container layer children to layers which together render
171 : * the contents of the display list. It reuses existing layers from
172 : * the retained layer manager if possible.
173 : * aContainer may be null, in which case we construct a root layer.
174 : * This gets called by display list code. It calls BuildLayer on the
175 : * items in the display list, making items with their own layers
176 : * children of the new container, and assigning all other items to
177 : * ThebesLayer children created and managed by the FrameLayerBuilder.
178 : * Returns a layer with clip rect cleared; it is the
179 : * caller's responsibility to add any clip rect. The visible region
180 : * is set based on what's in the layer.
181 : * The container layer is transformed by aTransform (if non-null), and
182 : * the result is transformed by the scale factors in aContainerParameters.
183 : */
184 : already_AddRefed<ContainerLayer>
185 : BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
186 : LayerManager* aManager,
187 : nsIFrame* aContainerFrame,
188 : nsDisplayItem* aContainerItem,
189 : const nsDisplayList& aChildren,
190 : const ContainerParameters& aContainerParameters,
191 : const gfx3DMatrix* aTransform);
192 :
193 : /**
194 : * Get a retained layer for a display item that needs to create its own
195 : * layer for rendering (i.e. under nsDisplayItem::BuildLayer). Returns
196 : * null if no retained layer is available, which usually means that this
197 : * display item didn't have a layer before so the caller will
198 : * need to create one.
199 : * Returns a layer with clip rect cleared; it is the
200 : * caller's responsibility to add any clip rect and set the visible
201 : * region.
202 : */
203 : Layer* GetLeafLayerFor(nsDisplayListBuilder* aBuilder,
204 : LayerManager* aManager,
205 : nsDisplayItem* aItem);
206 :
207 : /**
208 : * Call this during invalidation if aFrame has
209 : * the NS_FRAME_HAS_CONTAINER_LAYER state bit. Only the nearest
210 : * ancestor frame of the damaged frame that has
211 : * NS_FRAME_HAS_CONTAINER_LAYER needs to be invalidated this way.
212 : */
213 : static void InvalidateThebesLayerContents(nsIFrame* aFrame,
214 : const nsRect& aRect);
215 :
216 : /**
217 : * For any descendant frame of aFrame (including across documents) that
218 : * has an associated container layer, invalidate all the contents of
219 : * all ThebesLayer children of the container. Useful when aFrame is
220 : * being moved and we need to invalidate everything in aFrame's subtree.
221 : */
222 : static void InvalidateThebesLayersInSubtree(nsIFrame* aFrame);
223 :
224 : /**
225 : * Call this to force all retained layers to be discarded and recreated at
226 : * the next paint.
227 : */
228 : static void InvalidateAllLayers(LayerManager* aManager);
229 :
230 : /**
231 : * Call this to determine if a frame has a dedicated (non-Thebes) layer
232 : * for the given display item key. If there isn't one, we return null,
233 : * otherwise we return the layer.
234 : */
235 : static Layer* GetDedicatedLayer(nsIFrame* aFrame, PRUint32 aDisplayItemKey);
236 :
237 : /**
238 : * This callback must be provided to EndTransaction. The callback data
239 : * must be the nsDisplayListBuilder containing this FrameLayerBuilder.
240 : */
241 : static void DrawThebesLayer(ThebesLayer* aLayer,
242 : gfxContext* aContext,
243 : const nsIntRegion& aRegionToDraw,
244 : const nsIntRegion& aRegionToInvalidate,
245 : void* aCallbackData);
246 :
247 : #ifdef MOZ_DUMP_PAINTING
248 : /**
249 : * Dumps this FrameLayerBuilder's retained layer manager's retained
250 : * layer tree to stderr.
251 : */
252 : void DumpRetainedLayerTree(FILE* aFile = stdout);
253 : #endif
254 :
255 : /******* PRIVATE METHODS to FrameLayerBuilder.cpp ********/
256 : /* These are only in the public section because they need
257 : * to be called by file-scope helper functions in FrameLayerBuilder.cpp.
258 : */
259 :
260 : /**
261 : * Record aItem as a display item that is rendered by aLayer.
262 : */
263 : void AddLayerDisplayItem(Layer* aLayer,
264 : nsDisplayItem* aItem,
265 : LayerState aLayerState);
266 :
267 : /**
268 : * Record aItem as a display item that is rendered by the ThebesLayer
269 : * aLayer, with aClipRect, where aContainerLayerFrame is the frame
270 : * for the container layer this ThebesItem belongs to.
271 : * aItem must have an underlying frame.
272 : */
273 : struct Clip;
274 : void AddThebesDisplayItem(ThebesLayer* aLayer,
275 : nsDisplayItem* aItem,
276 : const Clip& aClip,
277 : nsIFrame* aContainerLayerFrame,
278 : LayerState aLayerState);
279 :
280 : /**
281 : * Given a frame and a display item key that uniquely identifies a
282 : * display item for the frame, find the layer that was last used to
283 : * render that display item. Returns null if there is no such layer.
284 : * This could be a dedicated layer for the display item, or a ThebesLayer
285 : * that renders many display items.
286 : */
287 : Layer* GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey);
288 :
289 : /**
290 : * Try to determine whether the ThebesLayer aLayer paints an opaque
291 : * single color everywhere it's visible in aRect.
292 : * If successful, return that color, otherwise return NS_RGBA(0,0,0,0).
293 : */
294 : nscolor FindOpaqueColorCovering(nsDisplayListBuilder* aBuilder,
295 : ThebesLayer* aLayer, const nsRect& aRect);
296 :
297 : /**
298 : * Destroy any stored LayerManagerDataProperty and the associated data for
299 : * aFrame.
300 : */
301 0 : static void DestroyDisplayItemDataFor(nsIFrame* aFrame)
302 : {
303 0 : aFrame->Properties().Delete(LayerManagerDataProperty());
304 0 : }
305 :
306 0 : LayerManager* GetRetainingLayerManager() { return mRetainingManager; }
307 :
308 : /**
309 : * Returns true if the given item (which we assume here is
310 : * background-attachment:fixed) needs to be repainted as we scroll in its
311 : * document.
312 : * Returns false if it doesn't need to be repainted because the layer system
313 : * is ensuring its fixed-ness for us.
314 : */
315 : static bool NeedToInvalidateFixedDisplayItem(nsDisplayListBuilder* aBuilder,
316 : nsDisplayItem* aItem);
317 :
318 : /**
319 : * Returns true if the given display item was rendered directly
320 : * into a retained layer.
321 : * Returns false if it was rendered into a temporary layer manager and then
322 : * into a retained layer.
323 : */
324 : static bool HasRetainedLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey);
325 :
326 : /**
327 : * Save transform that was in aLayer when we last painted. It must be an integer
328 : * translation.
329 : */
330 : void SaveLastPaintOffset(ThebesLayer* aLayer);
331 : /**
332 : * Get the translation transform that was in aLayer when we last painted. It's either
333 : * the transform saved by SaveLastPaintTransform, or else the transform
334 : * that's currently in the layer (which must be an integer translation).
335 : */
336 : nsIntPoint GetLastPaintOffset(ThebesLayer* aLayer);
337 :
338 : /**
339 : * Clip represents the intersection of an optional rectangle with a
340 : * list of rounded rectangles.
341 : */
342 0 : struct Clip {
343 0 : struct RoundedRect {
344 : nsRect mRect;
345 : // Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
346 : nscoord mRadii[8];
347 :
348 0 : bool operator==(const RoundedRect& aOther) const {
349 0 : if (!mRect.IsEqualInterior(aOther.mRect)) {
350 0 : return false;
351 : }
352 :
353 0 : NS_FOR_CSS_HALF_CORNERS(corner) {
354 0 : if (mRadii[corner] != aOther.mRadii[corner]) {
355 0 : return false;
356 : }
357 : }
358 0 : return true;
359 : }
360 : bool operator!=(const RoundedRect& aOther) const {
361 : return !(*this == aOther);
362 : }
363 : };
364 : nsRect mClipRect;
365 : nsTArray<RoundedRect> mRoundedClipRects;
366 : bool mHaveClipRect;
367 :
368 0 : Clip() : mHaveClipRect(false) {}
369 :
370 : // Construct as the intersection of aOther and aClipItem.
371 : Clip(const Clip& aOther, nsDisplayItem* aClipItem);
372 :
373 : // Apply this |Clip| to the given gfxContext. Any saving of state
374 : // or clearing of other clips must be done by the caller.
375 : void ApplyTo(gfxContext* aContext, nsPresContext* aPresContext);
376 :
377 : // Return a rectangle contained in the intersection of aRect with this
378 : // clip region. Tries to return the largest possible rectangle, but may
379 : // not succeed.
380 : nsRect ApproximateIntersect(const nsRect& aRect) const;
381 :
382 : // Returns false if aRect is definitely not clipped by a rounded corner in
383 : // this clip. Returns true if aRect is clipped by a rounded corner in this
384 : // clip or it can not be quickly determined that it is not clipped by a
385 : // rounded corner in this clip.
386 : bool IsRectClippedByRoundedCorner(const nsRect& aRect) const;
387 :
388 : // Intersection of all rects in this clip ignoring any rounded corners.
389 : nsRect NonRoundedIntersection() const;
390 :
391 : // Gets rid of any rounded corners in this clip.
392 : void RemoveRoundedCorners();
393 :
394 0 : bool operator==(const Clip& aOther) const {
395 : return mHaveClipRect == aOther.mHaveClipRect &&
396 0 : (!mHaveClipRect || mClipRect.IsEqualInterior(aOther.mClipRect)) &&
397 0 : mRoundedClipRects == aOther.mRoundedClipRects;
398 : }
399 0 : bool operator!=(const Clip& aOther) const {
400 0 : return !(*this == aOther);
401 : }
402 : };
403 :
404 : protected:
405 : /**
406 : * We store an array of these for each frame that is associated with
407 : * one or more retained layers. Each DisplayItemData records the layer
408 : * used to render one of the frame's display items.
409 : */
410 0 : class DisplayItemData {
411 : public:
412 0 : DisplayItemData(Layer* aLayer, PRUint32 aKey, LayerState aLayerState)
413 0 : : mLayer(aLayer), mDisplayItemKey(aKey), mLayerState(aLayerState) {}
414 :
415 : nsRefPtr<Layer> mLayer;
416 : PRUint32 mDisplayItemKey;
417 : LayerState mLayerState;
418 : };
419 :
420 : static void RemoveFrameFromLayerManager(nsIFrame* aFrame, void* aPropertyValue);
421 :
422 0 : NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(LayerManagerDataProperty,
423 : RemoveFrameFromLayerManager)
424 :
425 : /**
426 : * We accumulate DisplayItemData elements in a hashtable during
427 : * the paint process, and store them in the frame property only when
428 : * paint is complete. This is the hashentry for that hashtable.
429 : */
430 0 : class DisplayItemDataEntry : public nsPtrHashKey<nsIFrame> {
431 : public:
432 0 : DisplayItemDataEntry(const nsIFrame *key) : nsPtrHashKey<nsIFrame>(key) {}
433 0 : DisplayItemDataEntry(DisplayItemDataEntry &toCopy) :
434 0 : nsPtrHashKey<nsIFrame>(toCopy.mKey)
435 : {
436 : // This isn't actually a copy-constructor; notice that it steals toCopy's
437 : // array. Be careful.
438 0 : mData.SwapElements(toCopy.mData);
439 0 : }
440 :
441 : bool HasNonEmptyContainerLayer();
442 :
443 : nsAutoTArray<DisplayItemData, 1> mData;
444 :
445 : enum { ALLOW_MEMMOVE = false };
446 : };
447 :
448 : // LayerManagerData needs to see DisplayItemDataEntry.
449 : friend class LayerManagerData;
450 :
451 : // Flash the area within the context clip if paint flashing is enabled.
452 : static void FlashPaint(gfxContext *aContext);
453 :
454 : /*
455 : * Get the DisplayItemData array associated with this frame, or null if one
456 : * doesn't exist.
457 : *
458 : * Note that the pointer returned here is only valid so long as you don't
459 : * poke the LayerManagerData's mFramesWithLayers hashtable.
460 : */
461 : static nsTArray<DisplayItemData>* GetDisplayItemDataArrayForFrame(nsIFrame *aFrame);
462 :
463 : /**
464 : * A useful hashtable iteration function that removes the
465 : * DisplayItemData property for the frame, clears its
466 : * NS_FRAME_HAS_CONTAINER_LAYER bit and returns PL_DHASH_REMOVE.
467 : * aClosure is ignored.
468 : */
469 0 : static PLDHashOperator RemoveDisplayItemDataForFrame(DisplayItemDataEntry* aEntry,
470 : void* aClosure)
471 : {
472 0 : return UpdateDisplayItemDataForFrame(aEntry, nsnull);
473 : }
474 :
475 : /**
476 : * We store one of these for each display item associated with a
477 : * ThebesLayer, in a hashtable that maps each ThebesLayer to an array
478 : * of ClippedDisplayItems. (ThebesLayerItemsEntry is the hash entry
479 : * for that hashtable.)
480 : * These are only stored during the paint process, so that the
481 : * DrawThebesLayer callback can figure out which items to draw for the
482 : * ThebesLayer.
483 : * mItem always has an underlying frame.
484 : */
485 0 : struct ClippedDisplayItem {
486 0 : ClippedDisplayItem(nsDisplayItem* aItem, const Clip& aClip)
487 0 : : mItem(aItem), mClip(aClip)
488 : {
489 0 : }
490 :
491 : nsDisplayItem* mItem;
492 : Clip mClip;
493 : bool mInactiveLayer;
494 : };
495 :
496 : /**
497 : * We accumulate ClippedDisplayItem elements in a hashtable during
498 : * the paint process. This is the hashentry for that hashtable.
499 : */
500 0 : class ThebesLayerItemsEntry : public nsPtrHashKey<ThebesLayer> {
501 : public:
502 0 : ThebesLayerItemsEntry(const ThebesLayer *key) :
503 : nsPtrHashKey<ThebesLayer>(key), mContainerLayerFrame(nsnull),
504 0 : mHasExplicitLastPaintOffset(false) {}
505 : ThebesLayerItemsEntry(const ThebesLayerItemsEntry &toCopy) :
506 : nsPtrHashKey<ThebesLayer>(toCopy.mKey), mItems(toCopy.mItems)
507 : {
508 : NS_ERROR("Should never be called, since we ALLOW_MEMMOVE");
509 : }
510 :
511 : nsTArray<ClippedDisplayItem> mItems;
512 : nsIFrame* mContainerLayerFrame;
513 : // The translation set on this ThebesLayer before we started updating the
514 : // layer tree.
515 : nsIntPoint mLastPaintOffset;
516 : bool mHasExplicitLastPaintOffset;
517 :
518 : enum { ALLOW_MEMMOVE = true };
519 : };
520 :
521 : void RemoveThebesItemsForLayerSubtree(Layer* aLayer);
522 :
523 : static PLDHashOperator UpdateDisplayItemDataForFrame(DisplayItemDataEntry* aEntry,
524 : void* aUserArg);
525 : static PLDHashOperator StoreNewDisplayItemData(DisplayItemDataEntry* aEntry,
526 : void* aUserArg);
527 :
528 : /**
529 : * Returns true if the DOM has been modified since we started painting,
530 : * in which case we should bail out and not paint anymore. This should
531 : * never happen, but plugins can trigger it in some cases.
532 : */
533 : bool CheckDOMModified();
534 :
535 : /**
536 : * The layer manager belonging to the widget that is being retained
537 : * across paints.
538 : */
539 : LayerManager* mRetainingManager;
540 : /**
541 : * The root prescontext for the display list builder reference frame
542 : */
543 : nsRootPresContext* mRootPresContext;
544 : /**
545 : * A map from frames to a list of (display item key, layer) pairs that
546 : * describes what layers various parts of the frame are assigned to.
547 : */
548 : nsTHashtable<DisplayItemDataEntry> mNewDisplayItemData;
549 : /**
550 : * A map from ThebesLayers to the list of display items (plus
551 : * clipping data) to be rendered in the layer.
552 : */
553 : nsTHashtable<ThebesLayerItemsEntry> mThebesLayerItems;
554 : /**
555 : * Saved generation counter so we can detect DOM changes.
556 : */
557 : PRUint32 mInitialDOMGeneration;
558 : /**
559 : * Set to true if we have detected and reported DOM modification during
560 : * the current paint.
561 : */
562 : bool mDetectedDOMModification;
563 : /**
564 : * Indicates that the entire layer tree should be rerendered
565 : * during this paint.
566 : */
567 : bool mInvalidateAllLayers;
568 : };
569 :
570 : }
571 :
572 : #endif /* FRAMELAYERBUILDER_H_ */
|