1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=2 sw=2 et tw=78:
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Novell code.
18 * The Initial Developer of the Original Code is Novell Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
23 * robert@ocallahan.org
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK *****
41 * structures that represent things to be painted (ordered in z-order),
42 * used during painting and hit testing
45 #include "nsDisplayList.h"
47 #include "nsCSSRendering.h"
48 #include "nsISelectionController.h"
49 #include "nsIPresShell.h"
51 #include "nsFrameManager.h"
52 #include "gfxContext.h"
53 #include "nsStyleStructInlines.h"
54 #include "nsStyleTransformMatrix.h"
55 #include "gfxMatrix.h"
57 #include "nsSVGIntegrationUtils.h"
60 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame
* aReferenceFrame
,
61 PRBool aIsForEvents
, PRBool aBuildCaret
)
62 : mReferenceFrame(aReferenceFrame
),
64 mIgnoreScrollFrame(nsnull
),
65 mCurrentTableItem(nsnull
),
66 mBuildCaret(aBuildCaret
),
67 mEventDelivery(aIsForEvents
),
68 mIsAtRootOfPseudoStackingContext(PR_FALSE
),
69 mPaintAllFrames(PR_FALSE
) {
70 PL_InitArenaPool(&mPool
, "displayListArena", 1024, sizeof(void*)-1);
72 nsPresContext
* pc
= aReferenceFrame
->PresContext();
73 nsIPresShell
*shell
= pc
->PresShell();
75 shell
->IsPaintingSuppressed(&suppressed
);
76 mIsBackgroundOnly
= suppressed
;
77 if (pc
->IsRenderingOnlySelection()) {
78 nsCOMPtr
<nsISelectionController
> selcon(do_QueryInterface(shell
));
80 selcon
->GetSelection(nsISelectionController::SELECTION_NORMAL
,
81 getter_AddRefs(mBoundingSelection
));
85 if (mIsBackgroundOnly
) {
86 mBuildCaret
= PR_FALSE
;
90 // Destructor function for the dirty rect property
92 DestroyRectFunc(void* aFrame
,
93 nsIAtom
* aPropertyName
,
97 delete static_cast<nsRect
*>(aPropertyValue
);
100 static void MarkFrameForDisplay(nsIFrame
* aFrame
, nsIFrame
* aStopAtFrame
) {
101 nsFrameManager
* frameManager
= aFrame
->PresContext()->PresShell()->FrameManager();
103 for (nsIFrame
* f
= aFrame
; f
;
104 f
= nsLayoutUtils::GetParentOrPlaceholderFor(frameManager
, f
)) {
105 if (f
->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
)
107 f
->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
);
108 if (f
== aStopAtFrame
) {
109 // we've reached a frame that we know will be painted, so we can stop.
115 static void MarkOutOfFlowFrameForDisplay(nsIFrame
* aDirtyFrame
, nsIFrame
* aFrame
,
116 const nsRect
& aDirtyRect
) {
117 nsRect dirty
= aDirtyRect
- aFrame
->GetOffsetTo(aDirtyFrame
);
118 nsRect overflowRect
= aFrame
->GetOverflowRect();
119 if (!dirty
.IntersectRect(dirty
, overflowRect
))
121 // if "new nsRect" fails, this won't do anything, but that's okay
122 aFrame
->SetProperty(nsGkAtoms::outOfFlowDirtyRectProperty
,
123 new nsRect(dirty
), DestroyRectFunc
);
125 MarkFrameForDisplay(aFrame
, aDirtyFrame
);
128 static void UnmarkFrameForDisplay(nsIFrame
* aFrame
) {
129 aFrame
->DeleteProperty(nsGkAtoms::outOfFlowDirtyRectProperty
);
131 nsFrameManager
* frameManager
= aFrame
->PresContext()->PresShell()->FrameManager();
133 for (nsIFrame
* f
= aFrame
; f
;
134 f
= nsLayoutUtils::GetParentOrPlaceholderFor(frameManager
, f
)) {
135 if (!(f
->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
))
137 f
->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
);
141 nsDisplayListBuilder::~nsDisplayListBuilder() {
142 NS_ASSERTION(mFramesMarkedForDisplay
.Length() == 0,
143 "All frames should have been unmarked");
144 NS_ASSERTION(mPresShellStates
.Length() == 0,
145 "All presshells should have been exited");
146 NS_ASSERTION(!mCurrentTableItem
, "No table item should be active");
148 PL_FreeArenaPool(&mPool
);
149 PL_FinishArenaPool(&mPool
);
153 nsDisplayListBuilder::GetCaret() {
154 nsRefPtr
<nsCaret
> caret
;
155 CurrentPresShellState()->mPresShell
->GetCaret(getter_AddRefs(caret
));
160 nsDisplayListBuilder::EnterPresShell(nsIFrame
* aReferenceFrame
,
161 const nsRect
& aDirtyRect
) {
162 PresShellState
* state
= mPresShellStates
.AppendElement();
165 state
->mPresShell
= aReferenceFrame
->PresContext()->PresShell();
166 state
->mCaretFrame
= nsnull
;
167 state
->mFirstFrameMarkedForDisplay
= mFramesMarkedForDisplay
.Length();
172 nsRefPtr
<nsCaret
> caret
;
173 state
->mPresShell
->GetCaret(getter_AddRefs(caret
));
174 state
->mCaretFrame
= caret
->GetCaretFrame();
176 if (state
->mCaretFrame
) {
177 // Check if the dirty rect intersects with the caret's dirty rect.
179 caret
->GetCaretRect() + state
->mCaretFrame
->GetOffsetTo(aReferenceFrame
);
180 if (caretRect
.Intersects(aDirtyRect
)) {
181 // Okay, our rects intersect, let's mark the frame and all of its ancestors.
182 mFramesMarkedForDisplay
.AppendElement(state
->mCaretFrame
);
183 MarkFrameForDisplay(state
->mCaretFrame
, nsnull
);
189 nsDisplayListBuilder::LeavePresShell(nsIFrame
* aReferenceFrame
,
190 const nsRect
& aDirtyRect
)
192 if (CurrentPresShellState()->mPresShell
!= aReferenceFrame
->PresContext()->PresShell()) {
193 // Must have not allocated a state for this presshell, presumably due
198 // Unmark and pop off the frames marked for display in this pres shell.
199 PRUint32 firstFrameForShell
= CurrentPresShellState()->mFirstFrameMarkedForDisplay
;
200 for (PRUint32 i
= firstFrameForShell
;
201 i
< mFramesMarkedForDisplay
.Length(); ++i
) {
202 UnmarkFrameForDisplay(mFramesMarkedForDisplay
[i
]);
204 mFramesMarkedForDisplay
.SetLength(firstFrameForShell
);
205 mPresShellStates
.SetLength(mPresShellStates
.Length() - 1);
209 nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame
* aDirtyFrame
, nsIFrame
* aFrames
,
210 const nsRect
& aDirtyRect
) {
212 mFramesMarkedForDisplay
.AppendElement(aFrames
);
213 MarkOutOfFlowFrameForDisplay(aDirtyFrame
, aFrames
, aDirtyRect
);
214 aFrames
= aFrames
->GetNextSibling();
219 nsDisplayListBuilder::Allocate(size_t aSize
) {
221 PL_ARENA_ALLOCATE(tmp
, &mPool
, aSize
);
225 void nsDisplayListSet::MoveTo(const nsDisplayListSet
& aDestination
) const
227 aDestination
.BorderBackground()->AppendToTop(BorderBackground());
228 aDestination
.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
229 aDestination
.Floats()->AppendToTop(Floats());
230 aDestination
.Content()->AppendToTop(Content());
231 aDestination
.PositionedDescendants()->AppendToTop(PositionedDescendants());
232 aDestination
.Outlines()->AppendToTop(Outlines());
235 // Suitable for leaf items only, overridden by nsDisplayWrapList
237 nsDisplayItem::OptimizeVisibility(nsDisplayListBuilder
* aBuilder
,
238 nsRegion
* aVisibleRegion
) {
239 nsRect bounds
= GetBounds(aBuilder
);
240 if (!aVisibleRegion
->Intersects(bounds
))
243 nsIFrame
* f
= GetUnderlyingFrame();
244 NS_ASSERTION(f
, "GetUnderlyingFrame() must return non-null for leaf items");
245 PRBool isMoving
= aBuilder
->IsMovingFrame(f
);
247 if (IsOpaque(aBuilder
)) {
248 nsRect opaqueArea
= bounds
;
250 // The display list should include items for both the before and after
251 // states (see nsLayoutUtils::ComputeRepaintRegionForCopy. So the
252 // only area we want to cover is the the area that was opaque in the
253 // before state and in the after state.
254 opaqueArea
.IntersectRect(bounds
- aBuilder
->GetMoveDelta(), bounds
);
256 aVisibleRegion
->SimpleSubtract(opaqueArea
);
263 nsDisplayList::FlattenTo(nsTArray
<nsDisplayItem
*>* aElements
) {
265 while ((item
= RemoveBottom()) != nsnull
) {
266 if (item
->GetType() == nsDisplayItem::TYPE_WRAPLIST
) {
267 item
->GetList()->FlattenTo(aElements
);
268 item
->~nsDisplayItem();
270 aElements
->AppendElement(item
);
276 nsDisplayList::GetBounds(nsDisplayListBuilder
* aBuilder
) const {
278 for (nsDisplayItem
* i
= GetBottom(); i
!= nsnull
; i
= i
->GetAbove()) {
279 bounds
.UnionRect(bounds
, i
->GetBounds(aBuilder
));
285 nsDisplayList::OptimizeVisibility(nsDisplayListBuilder
* aBuilder
,
286 nsRegion
* aVisibleRegion
) {
287 nsAutoTArray
<nsDisplayItem
*, 512> elements
;
288 FlattenTo(&elements
);
290 for (PRInt32 i
= elements
.Length() - 1; i
>= 0; --i
) {
291 nsDisplayItem
* item
= elements
[i
];
292 nsDisplayItem
* belowItem
= i
< 1 ? nsnull
: elements
[i
- 1];
294 if (belowItem
&& item
->TryMerge(aBuilder
, belowItem
)) {
295 belowItem
->~nsDisplayItem();
296 elements
.ReplaceElementsAt(i
- 1, 1, item
);
300 if (item
->OptimizeVisibility(aBuilder
, aVisibleRegion
)) {
301 AppendToBottom(item
);
303 item
->~nsDisplayItem();
308 void nsDisplayList::Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
309 const nsRect
& aDirtyRect
) const {
310 for (nsDisplayItem
* i
= GetBottom(); i
!= nsnull
; i
= i
->GetAbove()) {
311 i
->Paint(aBuilder
, aCtx
, aDirtyRect
);
313 nsCSSRendering::DidPaint();
316 PRUint32
nsDisplayList::Count() const {
318 for (nsDisplayItem
* i
= GetBottom(); i
; i
= i
->GetAbove()) {
324 nsDisplayItem
* nsDisplayList::RemoveBottom() {
325 nsDisplayItem
* item
= mSentinel
.mAbove
;
328 mSentinel
.mAbove
= item
->mAbove
;
330 // must have been the only item
333 item
->mAbove
= nsnull
;
337 void nsDisplayList::DeleteBottom() {
338 nsDisplayItem
* item
= RemoveBottom();
340 item
->~nsDisplayItem();
344 void nsDisplayList::DeleteAll() {
346 while ((item
= RemoveBottom()) != nsnull
) {
347 item
->~nsDisplayItem();
351 nsIFrame
* nsDisplayList::HitTest(nsDisplayListBuilder
* aBuilder
, nsPoint aPt
,
352 nsDisplayItem::HitTestState
* aState
) const {
353 PRInt32 itemBufferStart
= aState
->mItemBuffer
.Length();
355 for (item
= GetBottom(); item
; item
= item
->GetAbove()) {
356 aState
->mItemBuffer
.AppendElement(item
);
358 for (PRInt32 i
= aState
->mItemBuffer
.Length() - 1; i
>= itemBufferStart
; --i
) {
359 // Pop element off the end of the buffer. We want to shorten the buffer
360 // so that recursive calls to HitTest have more buffer space.
361 item
= aState
->mItemBuffer
[i
];
362 aState
->mItemBuffer
.SetLength(i
);
364 if (item
->GetBounds(aBuilder
).Contains(aPt
)) {
365 nsIFrame
* f
= item
->HitTest(aBuilder
, aPt
, aState
);
366 // Handle the XUL 'mousethrough' feature.
368 if (!f
->GetMouseThrough()) {
369 aState
->mItemBuffer
.SetLength(itemBufferStart
);
375 NS_ASSERTION(aState
->mItemBuffer
.Length() == PRUint32(itemBufferStart
),
376 "How did we forget to pop some elements?");
380 static void Sort(nsDisplayList
* aList
, PRInt32 aCount
, nsDisplayList::SortLEQ aCmp
,
388 PRInt32 half
= aCount
/2;
389 PRBool sorted
= PR_TRUE
;
390 nsDisplayItem
* prev
= nsnull
;
391 for (i
= 0; i
< aCount
; ++i
) {
392 nsDisplayItem
* item
= aList
->RemoveBottom();
393 (i
< half
? &list1
: &list2
)->AppendToTop(item
);
394 if (sorted
&& prev
&& !aCmp(prev
, item
, aClosure
)) {
400 aList
->AppendToTop(&list1
);
401 aList
->AppendToTop(&list2
);
405 Sort(&list1
, half
, aCmp
, aClosure
);
406 Sort(&list2
, aCount
- half
, aCmp
, aClosure
);
408 for (i
= 0; i
< aCount
; ++i
) {
409 if (list1
.GetBottom() &&
410 (!list2
.GetBottom() ||
411 aCmp(list1
.GetBottom(), list2
.GetBottom(), aClosure
))) {
412 aList
->AppendToTop(list1
.RemoveBottom());
414 aList
->AppendToTop(list2
.RemoveBottom());
419 static PRBool
IsContentLEQ(nsDisplayItem
* aItem1
, nsDisplayItem
* aItem2
,
421 // These GetUnderlyingFrame calls return non-null because we're only used
423 return nsLayoutUtils::CompareTreePosition(
424 aItem1
->GetUnderlyingFrame()->GetContent(),
425 aItem2
->GetUnderlyingFrame()->GetContent(),
426 static_cast<nsIContent
*>(aClosure
)) <= 0;
429 static PRBool
IsZOrderLEQ(nsDisplayItem
* aItem1
, nsDisplayItem
* aItem2
,
431 // These GetUnderlyingFrame calls return non-null because we're only used
433 PRInt32 diff
= nsLayoutUtils::GetZIndex(aItem1
->GetUnderlyingFrame()) -
434 nsLayoutUtils::GetZIndex(aItem2
->GetUnderlyingFrame());
436 return IsContentLEQ(aItem1
, aItem2
, aClosure
);
440 void nsDisplayList::ExplodeAnonymousChildLists(nsDisplayListBuilder
* aBuilder
) {
441 // See if there's anything to do
442 PRBool anyAnonymousItems
= PR_FALSE
;
444 for (i
= GetBottom(); i
!= nsnull
; i
= i
->GetAbove()) {
445 if (!i
->GetUnderlyingFrame()) {
446 anyAnonymousItems
= PR_TRUE
;
450 if (!anyAnonymousItems
)
454 while ((i
= RemoveBottom()) != nsnull
) {
455 if (i
->GetUnderlyingFrame()) {
458 nsDisplayList
* list
= i
->GetList();
459 NS_ASSERTION(list
, "leaf items can't be anonymous");
460 list
->ExplodeAnonymousChildLists(aBuilder
);
462 while ((j
= list
->RemoveBottom()) != nsnull
) {
463 tmp
.AppendToTop(static_cast<nsDisplayWrapList
*>(i
)->
464 WrapWithClone(aBuilder
, j
));
473 void nsDisplayList::SortByZOrder(nsDisplayListBuilder
* aBuilder
,
474 nsIContent
* aCommonAncestor
) {
475 Sort(aBuilder
, IsZOrderLEQ
, aCommonAncestor
);
478 void nsDisplayList::SortByContentOrder(nsDisplayListBuilder
* aBuilder
,
479 nsIContent
* aCommonAncestor
) {
480 Sort(aBuilder
, IsContentLEQ
, aCommonAncestor
);
483 void nsDisplayList::Sort(nsDisplayListBuilder
* aBuilder
,
484 SortLEQ aCmp
, void* aClosure
) {
485 ExplodeAnonymousChildLists(aBuilder
);
486 ::Sort(this, Count(), aCmp
, aClosure
);
490 nsDisplayBackground::IsOpaque(nsDisplayListBuilder
* aBuilder
) {
491 // theme background overrides any other background
495 const nsStyleBackground
* bg
;
496 PRBool isCanvas
; // not used
498 nsCSSRendering::FindBackground(mFrame
->PresContext(), mFrame
,
501 return (hasBG
&& NS_GET_A(bg
->mBackgroundColor
) == 255 &&
502 bg
->mBackgroundClip
== NS_STYLE_BG_CLIP_BORDER
&&
503 !nsLayoutUtils::HasNonZeroCorner(mFrame
->GetStyleBorder()->
508 nsDisplayBackground::IsUniform(nsDisplayListBuilder
* aBuilder
) {
509 // theme background overrides any other background
514 const nsStyleBackground
* bg
;
516 nsCSSRendering::FindBackground(mFrame
->PresContext(), mFrame
, &bg
, &isCanvas
);
519 if ((bg
->mBackgroundFlags
& NS_STYLE_BG_IMAGE_NONE
) &&
520 !nsLayoutUtils::HasNonZeroCorner(mFrame
->GetStyleBorder()->mBorderRadius
) &&
521 bg
->mBackgroundClip
== NS_STYLE_BG_CLIP_BORDER
)
527 nsDisplayBackground::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder
* aBuilder
)
529 NS_ASSERTION(aBuilder
->IsMovingFrame(mFrame
),
530 "IsVaryingRelativeToMovingFrame called on non-moving frame!");
532 nsPresContext
* presContext
= mFrame
->PresContext();
534 const nsStyleBackground
* bg
;
536 nsCSSRendering::FindBackground(presContext
, mFrame
, &bg
, &isCanvas
);
539 if (!bg
->HasFixedBackground())
542 nsIFrame
* movingFrame
= aBuilder
->GetRootMovingFrame();
543 // movingFrame is the frame that is going to be moved. It must be equal
544 // to mFrame or some ancestor of mFrame, see assertion above.
545 // If mFrame is in the same document as movingFrame, then mFrame
546 // will move relative to its viewport, which means this display item will
547 // change when it is moved. If they are in different documents, we do not
548 // want to return true because mFrame won't move relative to its viewport.
549 return movingFrame
->PresContext() == presContext
;
553 nsDisplayBackground::Paint(nsDisplayListBuilder
* aBuilder
,
554 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
555 nsPoint offset
= aBuilder
->ToReferenceFrame(mFrame
);
556 nsCSSRendering::PaintBackground(mFrame
->PresContext(), *aCtx
, mFrame
,
557 aDirtyRect
, nsRect(offset
, mFrame
->GetSize()),
558 mFrame
->HonorPrintBackgroundSettings());
562 nsDisplayBackground::GetBounds(nsDisplayListBuilder
* aBuilder
) {
564 return mFrame
->GetOverflowRect() + aBuilder
->ToReferenceFrame(mFrame
);
566 return nsRect(aBuilder
->ToReferenceFrame(mFrame
), mFrame
->GetSize());
570 nsDisplayOutline::GetBounds(nsDisplayListBuilder
* aBuilder
) {
571 return mFrame
->GetOverflowRect() + aBuilder
->ToReferenceFrame(mFrame
);
575 nsDisplayOutline::Paint(nsDisplayListBuilder
* aBuilder
,
576 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
577 // TODO join outlines together
578 nsPoint offset
= aBuilder
->ToReferenceFrame(mFrame
);
579 nsCSSRendering::PaintOutline(mFrame
->PresContext(), *aCtx
, mFrame
,
580 aDirtyRect
, nsRect(offset
, mFrame
->GetSize()),
581 *mFrame
->GetStyleBorder(),
582 *mFrame
->GetStyleOutline(),
583 mFrame
->GetStyleContext());
587 nsDisplayOutline::OptimizeVisibility(nsDisplayListBuilder
* aBuilder
,
588 nsRegion
* aVisibleRegion
) {
589 if (!nsDisplayItem::OptimizeVisibility(aBuilder
, aVisibleRegion
))
592 const nsStyleOutline
* outline
= mFrame
->GetStyleOutline();
593 nsPoint origin
= aBuilder
->ToReferenceFrame(mFrame
);
594 if (nsRect(origin
, mFrame
->GetSize()).Contains(aVisibleRegion
->GetBounds()) &&
595 !nsLayoutUtils::HasNonZeroCorner(outline
->mOutlineRadius
)) {
596 if (outline
->mOutlineOffset
>= 0) {
597 // the visible region is entirely inside the border-rect, and the outline
598 // isn't rendered inside the border-rect, so the outline is not visible
607 nsDisplayCaret::Paint(nsDisplayListBuilder
* aBuilder
,
608 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
609 // Note: Because we exist, we know that the caret is visible, so we don't
610 // need to check for the caret's visibility.
611 mCaret
->PaintCaret(aBuilder
, aCtx
, mFrame
, aBuilder
->ToReferenceFrame(mFrame
));
615 nsDisplayBorder::OptimizeVisibility(nsDisplayListBuilder
* aBuilder
,
616 nsRegion
* aVisibleRegion
) {
617 if (!nsDisplayItem::OptimizeVisibility(aBuilder
, aVisibleRegion
))
620 nsRect paddingRect
= mFrame
->GetPaddingRect() - mFrame
->GetPosition() +
621 aBuilder
->ToReferenceFrame(mFrame
);
622 const nsStyleBorder
*styleBorder
;
623 if (paddingRect
.Contains(aVisibleRegion
->GetBounds()) &&
624 !(styleBorder
= mFrame
->GetStyleBorder())->IsBorderImageLoaded() &&
625 !nsLayoutUtils::HasNonZeroCorner(styleBorder
->mBorderRadius
)) {
626 // the visible region is entirely inside the content rect, and no part
627 // of the border is rendered inside the content rect, so we are not
629 // Skip this if there's a border-image (which draws a background
630 // too) or if there is a border-radius (which makes the border draw
639 nsDisplayBorder::Paint(nsDisplayListBuilder
* aBuilder
,
640 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
641 nsPoint offset
= aBuilder
->ToReferenceFrame(mFrame
);
642 nsCSSRendering::PaintBorder(mFrame
->PresContext(), *aCtx
, mFrame
,
643 aDirtyRect
, nsRect(offset
, mFrame
->GetSize()),
644 *mFrame
->GetStyleBorder(),
645 mFrame
->GetStyleContext(),
646 mFrame
->GetSkipSides());
650 nsDisplayBoxShadow::Paint(nsDisplayListBuilder
* aBuilder
,
651 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
652 nsPoint offset
= aBuilder
->ToReferenceFrame(mFrame
);
653 nsCSSRendering::PaintBoxShadow(mFrame
->PresContext(), *aCtx
,
658 nsDisplayBoxShadow::GetBounds(nsDisplayListBuilder
* aBuilder
) {
659 return mFrame
->GetOverflowRect() + aBuilder
->ToReferenceFrame(mFrame
);
662 nsDisplayWrapList::nsDisplayWrapList(nsIFrame
* aFrame
, nsDisplayList
* aList
)
663 : nsDisplayItem(aFrame
) {
664 mList
.AppendToTop(aList
);
667 nsDisplayWrapList::nsDisplayWrapList(nsIFrame
* aFrame
, nsDisplayItem
* aItem
)
668 : nsDisplayItem(aFrame
) {
669 mList
.AppendToTop(aItem
);
672 nsDisplayWrapList::~nsDisplayWrapList() {
677 nsDisplayWrapList::HitTest(nsDisplayListBuilder
* aBuilder
, nsPoint aPt
,
678 HitTestState
* aState
) {
679 return mList
.HitTest(aBuilder
, aPt
, aState
);
683 nsDisplayWrapList::GetBounds(nsDisplayListBuilder
* aBuilder
) {
684 return mList
.GetBounds(aBuilder
);
688 nsDisplayWrapList::OptimizeVisibility(nsDisplayListBuilder
* aBuilder
,
689 nsRegion
* aVisibleRegion
) {
690 mList
.OptimizeVisibility(aBuilder
, aVisibleRegion
);
691 // If none of the items are visible, they will all have been deleted
692 return mList
.GetTop() != nsnull
;
696 nsDisplayWrapList::IsOpaque(nsDisplayListBuilder
* aBuilder
) {
697 // We could try to do something but let's conservatively just return PR_FALSE.
698 // We reimplement OptimizeVisibility and that's what really matters
702 PRBool
nsDisplayWrapList::IsUniform(nsDisplayListBuilder
* aBuilder
) {
703 // We could try to do something but let's conservatively just return PR_FALSE.
707 PRBool
nsDisplayWrapList::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder
* aBuilder
) {
708 // The only existing consumer of IsVaryingRelativeToMovingFrame is
709 // nsLayoutUtils::ComputeRepaintRegionForCopy, which refrains from calling
710 // this on wrapped lists.
711 NS_WARNING("nsDisplayWrapList::IsVaryingRelativeToMovingFrame called unexpectedly");
712 // We could try to do something but let's conservatively just return PR_TRUE.
716 void nsDisplayWrapList::Paint(nsDisplayListBuilder
* aBuilder
,
717 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
718 mList
.Paint(aBuilder
, aCtx
, aDirtyRect
);
722 WrapDisplayList(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
723 nsDisplayList
* aList
, nsDisplayWrapper
* aWrapper
) {
724 if (!aList
->GetTop())
726 nsDisplayItem
* item
= aWrapper
->WrapList(aBuilder
, aFrame
, aList
);
728 return NS_ERROR_OUT_OF_MEMORY
;
730 aList
->AppendToTop(item
);
735 WrapEachDisplayItem(nsDisplayListBuilder
* aBuilder
,
736 nsDisplayList
* aList
, nsDisplayWrapper
* aWrapper
) {
737 nsDisplayList newList
;
739 while ((item
= aList
->RemoveBottom())) {
740 item
= aWrapper
->WrapItem(aBuilder
, item
);
742 return NS_ERROR_OUT_OF_MEMORY
;
743 newList
.AppendToTop(item
);
746 aList
->AppendToTop(&newList
);
750 nsresult
nsDisplayWrapper::WrapLists(nsDisplayListBuilder
* aBuilder
,
751 nsIFrame
* aFrame
, const nsDisplayListSet
& aIn
, const nsDisplayListSet
& aOut
)
753 nsresult rv
= WrapListsInPlace(aBuilder
, aFrame
, aIn
);
754 NS_ENSURE_SUCCESS(rv
, rv
);
758 aOut
.BorderBackground()->AppendToTop(aIn
.BorderBackground());
759 aOut
.BlockBorderBackgrounds()->AppendToTop(aIn
.BlockBorderBackgrounds());
760 aOut
.Floats()->AppendToTop(aIn
.Floats());
761 aOut
.Content()->AppendToTop(aIn
.Content());
762 aOut
.PositionedDescendants()->AppendToTop(aIn
.PositionedDescendants());
763 aOut
.Outlines()->AppendToTop(aIn
.Outlines());
767 nsresult
nsDisplayWrapper::WrapListsInPlace(nsDisplayListBuilder
* aBuilder
,
768 nsIFrame
* aFrame
, const nsDisplayListSet
& aLists
)
771 if (WrapBorderBackground()) {
772 // Our border-backgrounds are in-flow
773 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.BorderBackground(), this);
774 NS_ENSURE_SUCCESS(rv
, rv
);
776 // Our block border-backgrounds are in-flow
777 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.BlockBorderBackgrounds(), this);
778 NS_ENSURE_SUCCESS(rv
, rv
);
779 // The floats are not in flow
780 rv
= WrapEachDisplayItem(aBuilder
, aLists
.Floats(), this);
781 NS_ENSURE_SUCCESS(rv
, rv
);
782 // Our child content is in flow
783 rv
= WrapDisplayList(aBuilder
, aFrame
, aLists
.Content(), this);
784 NS_ENSURE_SUCCESS(rv
, rv
);
785 // The positioned descendants may not be in-flow
786 rv
= WrapEachDisplayItem(aBuilder
, aLists
.PositionedDescendants(), this);
787 NS_ENSURE_SUCCESS(rv
, rv
);
788 // The outlines may not be in-flow
789 return WrapEachDisplayItem(aBuilder
, aLists
.Outlines(), this);
792 nsDisplayOpacity::nsDisplayOpacity(nsIFrame
* aFrame
, nsDisplayList
* aList
)
793 : nsDisplayWrapList(aFrame
, aList
), mNeedAlpha(PR_TRUE
) {
794 MOZ_COUNT_CTOR(nsDisplayOpacity
);
797 #ifdef NS_BUILD_REFCNT_LOGGING
798 nsDisplayOpacity::~nsDisplayOpacity() {
799 MOZ_COUNT_DTOR(nsDisplayOpacity
);
803 PRBool
nsDisplayOpacity::IsOpaque(nsDisplayListBuilder
* aBuilder
) {
804 // We are never opaque, if our opacity was < 1 then we wouldn't have
809 void nsDisplayOpacity::Paint(nsDisplayListBuilder
* aBuilder
,
810 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
812 float opacity
= mFrame
->GetStyleDisplay()->mOpacity
;
815 bounds
.IntersectRect(GetBounds(aBuilder
), aDirtyRect
);
817 nsCOMPtr
<nsIDeviceContext
> devCtx
;
818 aCtx
->GetDeviceContext(*getter_AddRefs(devCtx
));
820 gfxContext
* ctx
= aCtx
->ThebesContext();
825 gfxRect
r(bounds
.x
, bounds
.y
, bounds
.width
, bounds
.height
);
826 r
.ScaleInverse(devCtx
->AppUnitsPerDevPixel());
827 ctx
->Rectangle(r
, PR_TRUE
);
831 ctx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
833 ctx
->PushGroup(gfxASurface::CONTENT_COLOR
);
835 nsDisplayWrapList::Paint(aBuilder
, aCtx
, bounds
);
837 ctx
->PopGroupToSource();
838 ctx
->SetOperator(gfxContext::OPERATOR_OVER
);
844 PRBool
nsDisplayOpacity::OptimizeVisibility(nsDisplayListBuilder
* aBuilder
,
845 nsRegion
* aVisibleRegion
) {
846 // Our children are translucent so we should not allow them to subtract
847 // area from aVisibleRegion. We do need to find out what is visible under
848 // our children in the temporary compositing buffer, because if our children
849 // paint our entire bounds opaquely then we don't need an alpha channel in
850 // the temporary compositing buffer.
851 nsRegion visibleUnderChildren
= *aVisibleRegion
;
852 PRBool anyVisibleChildren
=
853 nsDisplayWrapList::OptimizeVisibility(aBuilder
, &visibleUnderChildren
);
854 if (!anyVisibleChildren
)
857 mNeedAlpha
= visibleUnderChildren
.Intersects(GetBounds(aBuilder
));
861 PRBool
nsDisplayOpacity::TryMerge(nsDisplayListBuilder
* aBuilder
, nsDisplayItem
* aItem
) {
862 if (aItem
->GetType() != TYPE_OPACITY
)
864 // items for the same content element should be merged into a single
866 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
867 if (aItem
->GetUnderlyingFrame()->GetContent() != mFrame
->GetContent())
869 mList
.AppendToBottom(&static_cast<nsDisplayOpacity
*>(aItem
)->mList
);
873 nsDisplayClip::nsDisplayClip(nsIFrame
* aFrame
, nsIFrame
* aClippingFrame
,
874 nsDisplayItem
* aItem
, const nsRect
& aRect
)
875 : nsDisplayWrapList(aFrame
, aItem
),
876 mClippingFrame(aClippingFrame
), mClip(aRect
) {
877 MOZ_COUNT_CTOR(nsDisplayClip
);
880 nsDisplayClip::nsDisplayClip(nsIFrame
* aFrame
, nsIFrame
* aClippingFrame
,
881 nsDisplayList
* aList
, const nsRect
& aRect
)
882 : nsDisplayWrapList(aFrame
, aList
),
883 mClippingFrame(aClippingFrame
), mClip(aRect
) {
884 MOZ_COUNT_CTOR(nsDisplayClip
);
887 nsRect
nsDisplayClip::GetBounds(nsDisplayListBuilder
* aBuilder
) {
888 nsRect r
= nsDisplayWrapList::GetBounds(aBuilder
);
889 r
.IntersectRect(mClip
, r
);
893 #ifdef NS_BUILD_REFCNT_LOGGING
894 nsDisplayClip::~nsDisplayClip() {
895 MOZ_COUNT_DTOR(nsDisplayClip
);
899 void nsDisplayClip::Paint(nsDisplayListBuilder
* aBuilder
,
900 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
902 dirty
.IntersectRect(mClip
, aDirtyRect
);
904 aCtx
->SetClipRect(dirty
, nsClipCombine_kIntersect
);
905 nsDisplayWrapList::Paint(aBuilder
, aCtx
, dirty
);
909 PRBool
nsDisplayClip::OptimizeVisibility(nsDisplayListBuilder
* aBuilder
,
910 nsRegion
* aVisibleRegion
) {
912 clipped
.And(*aVisibleRegion
, mClip
);
913 nsRegion
rNew(clipped
);
914 PRBool anyVisible
= nsDisplayWrapList::OptimizeVisibility(aBuilder
, &rNew
);
916 subtracted
.Sub(clipped
, rNew
);
917 aVisibleRegion
->SimpleSubtract(subtracted
);
921 PRBool
nsDisplayClip::TryMerge(nsDisplayListBuilder
* aBuilder
,
922 nsDisplayItem
* aItem
) {
923 if (aItem
->GetType() != TYPE_CLIP
)
925 nsDisplayClip
* other
= static_cast<nsDisplayClip
*>(aItem
);
926 if (other
->mClip
!= mClip
|| other
->mClippingFrame
!= mClippingFrame
)
928 mList
.AppendToBottom(&other
->mList
);
932 nsDisplayWrapList
* nsDisplayClip::WrapWithClone(nsDisplayListBuilder
* aBuilder
,
933 nsDisplayItem
* aItem
) {
934 return new (aBuilder
)
935 nsDisplayClip(aItem
->GetUnderlyingFrame(), mClippingFrame
, aItem
, mClip
);
940 ///////////////////////////////////////////////////
941 // nsDisplayTransform Implementation
944 // Write #define UNIFIED_CONTINUATIONS here to have the transform property try
945 // to transform content with continuations as one unified block instead of
946 // several smaller ones. This is currently disabled because it doesn't work
947 // correctly, since when the frames are initially being reflown, their
948 // continuations all compute their bounding rects independently of each other
949 // and consequently get the wrong value. Write #define DEBUG_HIT here to have
950 // the nsDisplayTransform class dump out a bunch of information about hit
952 #undef UNIFIED_CONTINUATIONS
955 /* Returns the bounds of a frame as defined for transforms. If
956 * UNIFIED_CONTINUATIONS is not defined, this is simply the frame's bounding
957 * rectangle, translated to the origin. Otherwise, returns the smallest
958 * rectangle containing a frame and all of its continuations. For example, if
959 * there is a <span> element with several continuations split over several
960 * lines, this function will return the rectangle containing all of those
961 * continuations. This rectangle is relative to the origin of the frame's local
964 #ifndef UNIFIED_CONTINUATIONS
967 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame
* aFrame
)
969 NS_PRECONDITION(aFrame
, "Can't get the bounds of a nonexistent frame!");
970 return nsRect(nsPoint(0, 0), aFrame
->GetSize());
976 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame
* aFrame
)
978 NS_PRECONDITION(aFrame
, "Can't get the bounds of a nonexistent frame!");
982 /* Iterate through the continuation list, unioning together all the
985 for (const nsIFrame
*currFrame
= aFrame
->GetFirstContinuation();
987 currFrame
= currFrame
->GetNextContinuation())
989 /* Get the frame rect in local coordinates, then translate back to the
990 * original coordinates.
992 result
.UnionRect(result
, nsRect(currFrame
->GetOffsetTo(aFrame
),
993 currFrame
->GetSize()));
1001 /* Returns the delta specified by the -moz-tranform-origin property.
1002 * This is a positive delta, meaning that it indicates the direction to move
1003 * to get from (0, 0) of the frame to the transform origin.
1006 gfxPoint
GetDeltaToMozTransformOrigin(const nsIFrame
* aFrame
,
1008 const nsRect
* aBoundsOverride
)
1010 NS_PRECONDITION(aFrame
, "Can't get delta for a null frame!");
1011 NS_PRECONDITION(aFrame
->GetStyleDisplay()->HasTransform(),
1012 "Can't get a delta for an untransformed frame!");
1014 /* For both of the coordinates, if the value of -moz-transform is a
1015 * percentage, it's relative to the size of the frame. Otherwise, if it's
1016 * a distance, it's already computed for us!
1018 const nsStyleDisplay
* display
= aFrame
->GetStyleDisplay();
1019 nsRect boundingRect
= (aBoundsOverride
? *aBoundsOverride
:
1020 nsDisplayTransform::GetFrameBoundsForTransform(aFrame
));
1022 /* Allows us to access named variables by index. */
1024 gfxFloat
* coords
[2] = {&result
.x
, &result
.y
};
1025 const nscoord
* dimensions
[2] =
1026 {&boundingRect
.width
, &boundingRect
.height
};
1028 for (PRUint8 index
= 0; index
< 2; ++index
) {
1029 /* If the -moz-transform-origin specifies a percentage, take the percentage
1030 * of the size of the box.
1032 if (display
->mTransformOrigin
[index
].GetUnit() == eStyleUnit_Percent
)
1033 *coords
[index
] = NSAppUnitsToFloatPixels(*dimensions
[index
], aFactor
) *
1034 display
->mTransformOrigin
[index
].GetPercentValue();
1036 /* Otherwise, it's a length. */
1039 NSAppUnitsToFloatPixels(display
->
1040 mTransformOrigin
[index
].GetCoordValue(),
1044 /* Adjust based on the origin of the rectangle. */
1045 result
.x
+= NSAppUnitsToFloatPixels(boundingRect
.x
, aFactor
);
1046 result
.y
+= NSAppUnitsToFloatPixels(boundingRect
.y
, aFactor
);
1051 /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that
1052 * translates from local coordinate space to transform coordinate space, then
1056 nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame
* aFrame
,
1057 const nsPoint
&aOrigin
,
1059 const nsRect
* aBoundsOverride
)
1061 NS_PRECONDITION(aFrame
, "Cannot get transform matrix for a null frame!");
1062 NS_PRECONDITION(aFrame
->GetStyleDisplay()->HasTransform(),
1063 "Cannot get transform matrix if frame isn't transformed!");
1065 /* Account for the -moz-transform-origin property by translating the
1066 * coordinate space to the new origin.
1068 gfxPoint toMozOrigin
= GetDeltaToMozTransformOrigin(aFrame
, aFactor
, aBoundsOverride
);
1069 gfxPoint newOrigin
= gfxPoint(NSAppUnitsToFloatPixels(aOrigin
.x
, aFactor
),
1070 NSAppUnitsToFloatPixels(aOrigin
.y
, aFactor
));
1072 /* Get the underlying transform matrix. This requires us to get the
1073 * bounds of the frame.
1075 const nsStyleDisplay
* disp
= aFrame
->GetStyleDisplay();
1076 nsRect bounds
= (aBoundsOverride
? *aBoundsOverride
:
1077 nsDisplayTransform::GetFrameBoundsForTransform(aFrame
));
1079 /* Get the matrix, then change its basis to factor in the origin. */
1080 return nsLayoutUtils::ChangeMatrixBasis
1081 (newOrigin
+ toMozOrigin
, disp
->mTransform
.GetThebesMatrix(bounds
, aFactor
));
1084 /* Painting applies the transform, paints the sublist, then unapplies
1087 void nsDisplayTransform::Paint(nsDisplayListBuilder
*aBuilder
,
1088 nsIRenderingContext
*aCtx
,
1089 const nsRect
&aDirtyRect
)
1091 /* Get the local transform matrix with which we'll transform all wrapped
1092 * elements. If this matrix is singular, we shouldn't display anything
1095 gfxMatrix newTransformMatrix
=
1096 GetResultingTransformMatrix(mFrame
, aBuilder
->ToReferenceFrame(mFrame
),
1097 mFrame
->PresContext()->AppUnitsPerDevPixel(),
1099 if (newTransformMatrix
.IsSingular())
1102 /* Get the context and automatically save and restore it. */
1103 gfxContext
* gfx
= aCtx
->ThebesContext();
1104 gfxContextAutoSaveRestore
autoRestorer(gfx
);
1106 /* Get the new CTM by applying this transform after all of the
1107 * transforms preceding it.
1109 newTransformMatrix
.Multiply(gfx
->CurrentMatrix());
1111 /* Set the matrix for the transform based on the old matrix and the new
1114 gfx
->SetMatrix(newTransformMatrix
);
1116 /* Now, send the paint call down. As we do this, we need to be sure to
1117 * untransform the dirty rect, since we want everything that's painting to
1118 * think that it's painting in its original rectangular coordinate space.
1120 mStoredList
.Paint(aBuilder
, aCtx
,
1121 UntransformRect(aDirtyRect
, mFrame
,
1122 aBuilder
->ToReferenceFrame(mFrame
)));
1124 /* The AutoSaveRestore object will clean things up. */
1127 /* We don't need to do anything here. */
1128 PRBool
nsDisplayTransform::OptimizeVisibility(nsDisplayListBuilder
*aBuilder
,
1129 nsRegion
*aVisibleRegion
)
1138 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
1139 nsIFrame
*nsDisplayTransform::HitTest(nsDisplayListBuilder
*aBuilder
,
1141 HitTestState
*aState
)
1143 /* Here's how this works:
1144 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
1146 * 2. Invert the matrix.
1147 * 3. Use it to transform the point into the correct space.
1148 * 4. Pass that point down through to the list's version of HitTest.
1150 float factor
= nsPresContext::AppUnitsPerCSSPixel();
1152 GetResultingTransformMatrix(mFrame
, aBuilder
->ToReferenceFrame(mFrame
),
1154 if (matrix
.IsSingular())
1157 /* We want to go from transformed-space to regular space.
1158 * Thus we have to invert the matrix, which normally does
1159 * the reverse operation (e.g. regular->transformed)
1163 /* Now, apply the transform and pass it down the channel. */
1164 gfxPoint result
= matrix
.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPt
.x
, factor
),
1165 NSAppUnitsToFloatPixels(aPt
.y
, factor
)));
1168 printf("Frame: %p\n", dynamic_cast<void *>(mFrame
));
1169 printf(" Untransformed point: (%f, %f)\n", result
.x
, result
.y
);
1172 nsIFrame
* resultFrame
=
1173 mStoredList
.HitTest(aBuilder
,
1174 nsPoint(NSFloatPixelsToAppUnits(float(result
.x
), factor
),
1175 NSFloatPixelsToAppUnits(float(result
.y
), factor
)), aState
);
1179 printf(" Hit! Time: %f, frame: %p\n", static_cast<double>(clock()),
1180 dynamic_cast<void *>(resultFrame
));
1181 printf("=== end of hit test ===\n");
1187 /* The bounding rectangle for the object is the overflow rectangle translated
1188 * by the reference point.
1190 nsRect
nsDisplayTransform::GetBounds(nsDisplayListBuilder
*aBuilder
)
1192 return mFrame
->GetOverflowRect() + aBuilder
->ToReferenceFrame(mFrame
);
1195 /* The transform is opaque iff the transform consists solely of scales and
1196 * transforms and if the underlying content is opaque. Thus if the transform
1203 * We need b and c to be zero.
1205 PRBool
nsDisplayTransform::IsOpaque(nsDisplayListBuilder
*aBuilder
)
1207 const nsStyleDisplay
* disp
= mFrame
->GetStyleDisplay();
1208 return disp
->mTransform
.GetMainMatrixEntry(1) == 0.0f
&&
1209 disp
->mTransform
.GetMainMatrixEntry(2) == 0.0f
&&
1210 mStoredList
.IsOpaque(aBuilder
);
1213 /* The transform is uniform if it fills the entire bounding rect and the
1214 * wrapped list is uniform. See IsOpaque for discussion of why this
1217 PRBool
nsDisplayTransform::IsUniform(nsDisplayListBuilder
*aBuilder
)
1219 const nsStyleDisplay
* disp
= mFrame
->GetStyleDisplay();
1220 return disp
->mTransform
.GetMainMatrixEntry(1) == 0.0f
&&
1221 disp
->mTransform
.GetMainMatrixEntry(2) == 0.0f
&&
1222 mStoredList
.IsUniform(aBuilder
);
1225 /* If UNIFIED_CONTINUATIONS is defined, we can merge two display lists that
1226 * share the same underlying content. Otherwise, doing so results in graphical
1229 #ifndef UNIFIED_CONTINUATIONS
1232 nsDisplayTransform::TryMerge(nsDisplayListBuilder
*aBuilder
,
1233 nsDisplayItem
*aItem
)
1241 nsDisplayTransform::TryMerge(nsDisplayListBuilder
*aBuilder
,
1242 nsDisplayItem
*aItem
)
1244 NS_PRECONDITION(aItem
, "Why did you try merging with a null item?");
1245 NS_PRECONDITION(aBuilder
, "Why did you try merging with a null builder?");
1247 /* Make sure that we're dealing with two transforms. */
1248 if (aItem
->GetType() != TYPE_TRANSFORM
)
1251 /* Check to see that both frames are part of the same content. */
1252 if (aItem
->GetUnderlyingFrame()->GetContent() != mFrame
->GetContent())
1255 /* Now, move everything over to this frame and signal that
1258 mStoredList
.GetList()->
1259 AppendToBottom(&static_cast<nsDisplayTransform
*>(aItem
)->mStoredList
);
1265 /* TransformRect takes in as parameters a rectangle (in app space) and returns
1266 * the smallest rectangle (in app space) containing the transformed image of
1267 * that rectangle. That is, it takes the four corners of the rectangle,
1268 * transforms them according to the matrix associated with the specified frame,
1269 * then returns the smallest rectangle containing the four transformed points.
1271 * @param aUntransformedBounds The rectangle (in app units) to transform.
1272 * @param aFrame The frame whose transformation should be applied.
1273 * @param aOrigin The delta from the frame origin to the coordinate space origin
1274 * @param aBoundsOverride (optional) Force the frame bounds to be the
1276 * @return The smallest rectangle containing the image of the transformed
1279 nsRect
nsDisplayTransform::TransformRect(const nsRect
&aUntransformedBounds
,
1280 const nsIFrame
* aFrame
,
1281 const nsPoint
&aOrigin
,
1282 const nsRect
* aBoundsOverride
)
1284 NS_PRECONDITION(aFrame
, "Can't take the transform based on a null frame!");
1285 NS_PRECONDITION(aFrame
->GetStyleDisplay()->HasTransform(),
1286 "Cannot transform a rectangle if there's no transformation!");
1288 float factor
= nsPresContext::AppUnitsPerCSSPixel();
1289 return nsLayoutUtils::MatrixTransformRect
1290 (aUntransformedBounds
,
1291 GetResultingTransformMatrix(aFrame
, aOrigin
, factor
, aBoundsOverride
),
1295 nsRect
nsDisplayTransform::UntransformRect(const nsRect
&aUntransformedBounds
,
1296 const nsIFrame
* aFrame
,
1297 const nsPoint
&aOrigin
)
1299 NS_PRECONDITION(aFrame
, "Can't take the transform based on a null frame!");
1300 NS_PRECONDITION(aFrame
->GetStyleDisplay()->HasTransform(),
1301 "Cannot transform a rectangle if there's no transformation!");
1304 /* Grab the matrix. If the transform is degenerate, just hand back the
1307 float factor
= nsPresContext::AppUnitsPerCSSPixel();
1308 gfxMatrix matrix
= GetResultingTransformMatrix(aFrame
, aOrigin
, factor
, nsnull
);
1309 if (matrix
.IsSingular())
1312 /* We want to untransform the matrix, so invert the transformation first! */
1315 return nsLayoutUtils::MatrixTransformRect(aUntransformedBounds
, matrix
,
1320 nsDisplaySVGEffects::nsDisplaySVGEffects(nsIFrame
* aFrame
, nsDisplayList
* aList
)
1321 : nsDisplayWrapList(aFrame
, aList
), mEffectsFrame(aFrame
),
1322 mBounds(aFrame
->GetOverflowRectRelativeToSelf())
1324 MOZ_COUNT_CTOR(nsDisplaySVGEffects
);
1327 #ifdef NS_BUILD_REFCNT_LOGGING
1328 nsDisplaySVGEffects::~nsDisplaySVGEffects()
1330 MOZ_COUNT_DTOR(nsDisplaySVGEffects
);
1334 PRBool
nsDisplaySVGEffects::IsOpaque(nsDisplayListBuilder
* aBuilder
)
1340 nsDisplaySVGEffects::HitTest(nsDisplayListBuilder
* aBuilder
, nsPoint aPt
,
1341 HitTestState
* aState
)
1343 if (!nsSVGIntegrationUtils::HitTestFrameForEffects(mEffectsFrame
,
1344 aPt
- aBuilder
->ToReferenceFrame(mEffectsFrame
)))
1346 return mList
.HitTest(aBuilder
, aPt
, aState
);
1349 void nsDisplaySVGEffects::Paint(nsDisplayListBuilder
* aBuilder
,
1350 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
1352 nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx
,
1353 mEffectsFrame
, aDirtyRect
, aBuilder
, &mList
);
1356 PRBool
nsDisplaySVGEffects::OptimizeVisibility(nsDisplayListBuilder
* aBuilder
,
1357 nsRegion
* aVisibleRegion
) {
1359 vis
.And(*aVisibleRegion
, GetBounds(aBuilder
));
1360 nsPoint offset
= aBuilder
->ToReferenceFrame(mEffectsFrame
);
1361 nsRect dirtyRect
= nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mEffectsFrame
,
1362 vis
.GetBounds() - offset
) + offset
;
1364 // Our children may be translucent so we should not allow them to subtract
1365 // area from aVisibleRegion.
1366 nsRegion
childrenVisibleRegion(dirtyRect
);
1367 nsDisplayWrapList::OptimizeVisibility(aBuilder
, &childrenVisibleRegion
);
1368 return !vis
.IsEmpty();
1371 PRBool
nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder
* aBuilder
, nsDisplayItem
* aItem
)
1373 if (aItem
->GetType() != TYPE_SVG_EFFECTS
)
1375 // items for the same content element should be merged into a single
1376 // compositing group
1377 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
1378 if (aItem
->GetUnderlyingFrame()->GetContent() != mFrame
->GetContent())
1380 nsDisplaySVGEffects
* other
= static_cast<nsDisplaySVGEffects
*>(aItem
);
1381 mList
.AppendToBottom(&other
->mList
);
1382 mBounds
.UnionRect(mBounds
,
1383 other
->mBounds
+ other
->mEffectsFrame
->GetOffsetTo(mEffectsFrame
));