Bug 445737 - undo delete bookmark doesn't restore keyword (r=dietrich)
[wine-gecko.git] / layout / base / nsDisplayList.cpp
blob5fb515c2e345d1e90823d53d7c17d227e79d12cd
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
14 * License.
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.
22 * Contributor(s):
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"
50 #include "nsRegion.h"
51 #include "nsFrameManager.h"
52 #include "gfxContext.h"
53 #include "nsStyleStructInlines.h"
54 #include "nsStyleTransformMatrix.h"
55 #include "gfxMatrix.h"
56 #ifdef MOZ_SVG
57 #include "nsSVGIntegrationUtils.h"
58 #endif
60 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
61 PRBool aIsForEvents, PRBool aBuildCaret)
62 : mReferenceFrame(aReferenceFrame),
63 mMovingFrame(nsnull),
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();
74 PRBool suppressed;
75 shell->IsPaintingSuppressed(&suppressed);
76 mIsBackgroundOnly = suppressed;
77 if (pc->IsRenderingOnlySelection()) {
78 nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell));
79 if (selcon) {
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
91 static void
92 DestroyRectFunc(void* aFrame,
93 nsIAtom* aPropertyName,
94 void* aPropertyValue,
95 void* aDtorData)
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)
106 return;
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.
110 break;
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))
120 return;
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))
136 return;
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);
152 nsCaret *
153 nsDisplayListBuilder::GetCaret() {
154 nsRefPtr<nsCaret> caret;
155 CurrentPresShellState()->mPresShell->GetCaret(getter_AddRefs(caret));
156 return caret;
159 void
160 nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
161 const nsRect& aDirtyRect) {
162 PresShellState* state = mPresShellStates.AppendElement();
163 if (!state)
164 return;
165 state->mPresShell = aReferenceFrame->PresContext()->PresShell();
166 state->mCaretFrame = nsnull;
167 state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
169 if (!mBuildCaret)
170 return;
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.
178 nsRect caretRect =
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);
188 void
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
194 // to OOM.
195 return;
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);
208 void
209 nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, nsIFrame* aFrames,
210 const nsRect& aDirtyRect) {
211 while (aFrames) {
212 mFramesMarkedForDisplay.AppendElement(aFrames);
213 MarkOutOfFlowFrameForDisplay(aDirtyFrame, aFrames, aDirtyRect);
214 aFrames = aFrames->GetNextSibling();
218 void*
219 nsDisplayListBuilder::Allocate(size_t aSize) {
220 void *tmp;
221 PL_ARENA_ALLOCATE(tmp, &mPool, aSize);
222 return tmp;
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
236 PRBool
237 nsDisplayItem::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
238 nsRegion* aVisibleRegion) {
239 nsRect bounds = GetBounds(aBuilder);
240 if (!aVisibleRegion->Intersects(bounds))
241 return PR_FALSE;
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;
249 if (isMoving) {
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);
259 return PR_TRUE;
262 void
263 nsDisplayList::FlattenTo(nsTArray<nsDisplayItem*>* aElements) {
264 nsDisplayItem* item;
265 while ((item = RemoveBottom()) != nsnull) {
266 if (item->GetType() == nsDisplayItem::TYPE_WRAPLIST) {
267 item->GetList()->FlattenTo(aElements);
268 item->~nsDisplayItem();
269 } else {
270 aElements->AppendElement(item);
275 nsRect
276 nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const {
277 nsRect bounds;
278 for (nsDisplayItem* i = GetBottom(); i != nsnull; i = i->GetAbove()) {
279 bounds.UnionRect(bounds, i->GetBounds(aBuilder));
281 return bounds;
284 void
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);
297 continue;
300 if (item->OptimizeVisibility(aBuilder, aVisibleRegion)) {
301 AppendToBottom(item);
302 } else {
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 {
317 PRUint32 count = 0;
318 for (nsDisplayItem* i = GetBottom(); i; i = i->GetAbove()) {
319 ++count;
321 return count;
324 nsDisplayItem* nsDisplayList::RemoveBottom() {
325 nsDisplayItem* item = mSentinel.mAbove;
326 if (!item)
327 return nsnull;
328 mSentinel.mAbove = item->mAbove;
329 if (item == mTop) {
330 // must have been the only item
331 mTop = &mSentinel;
333 item->mAbove = nsnull;
334 return item;
337 void nsDisplayList::DeleteBottom() {
338 nsDisplayItem* item = RemoveBottom();
339 if (item) {
340 item->~nsDisplayItem();
344 void nsDisplayList::DeleteAll() {
345 nsDisplayItem* item;
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();
354 nsDisplayItem* item;
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.
367 if (f) {
368 if (!f->GetMouseThrough()) {
369 aState->mItemBuffer.SetLength(itemBufferStart);
370 return f;
375 NS_ASSERTION(aState->mItemBuffer.Length() == PRUint32(itemBufferStart),
376 "How did we forget to pop some elements?");
377 return nsnull;
380 static void Sort(nsDisplayList* aList, PRInt32 aCount, nsDisplayList::SortLEQ aCmp,
381 void* aClosure) {
382 if (aCount < 2)
383 return;
385 nsDisplayList list1;
386 nsDisplayList list2;
387 int i;
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)) {
395 sorted = PR_FALSE;
397 prev = item;
399 if (sorted) {
400 aList->AppendToTop(&list1);
401 aList->AppendToTop(&list2);
402 return;
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());
413 } else {
414 aList->AppendToTop(list2.RemoveBottom());
419 static PRBool IsContentLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
420 void* aClosure) {
421 // These GetUnderlyingFrame calls return non-null because we're only used
422 // in sorting
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,
430 void* aClosure) {
431 // These GetUnderlyingFrame calls return non-null because we're only used
432 // in sorting
433 PRInt32 diff = nsLayoutUtils::GetZIndex(aItem1->GetUnderlyingFrame()) -
434 nsLayoutUtils::GetZIndex(aItem2->GetUnderlyingFrame());
435 if (diff == 0)
436 return IsContentLEQ(aItem1, aItem2, aClosure);
437 return diff < 0;
440 void nsDisplayList::ExplodeAnonymousChildLists(nsDisplayListBuilder* aBuilder) {
441 // See if there's anything to do
442 PRBool anyAnonymousItems = PR_FALSE;
443 nsDisplayItem* i;
444 for (i = GetBottom(); i != nsnull; i = i->GetAbove()) {
445 if (!i->GetUnderlyingFrame()) {
446 anyAnonymousItems = PR_TRUE;
447 break;
450 if (!anyAnonymousItems)
451 return;
453 nsDisplayList tmp;
454 while ((i = RemoveBottom()) != nsnull) {
455 if (i->GetUnderlyingFrame()) {
456 tmp.AppendToTop(i);
457 } else {
458 nsDisplayList* list = i->GetList();
459 NS_ASSERTION(list, "leaf items can't be anonymous");
460 list->ExplodeAnonymousChildLists(aBuilder);
461 nsDisplayItem* j;
462 while ((j = list->RemoveBottom()) != nsnull) {
463 tmp.AppendToTop(static_cast<nsDisplayWrapList*>(i)->
464 WrapWithClone(aBuilder, j));
466 i->~nsDisplayItem();
470 AppendToTop(&tmp);
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);
489 PRBool
490 nsDisplayBackground::IsOpaque(nsDisplayListBuilder* aBuilder) {
491 // theme background overrides any other background
492 if (mIsThemed)
493 return PR_FALSE;
495 const nsStyleBackground* bg;
496 PRBool isCanvas; // not used
497 PRBool hasBG =
498 nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame,
499 &bg, &isCanvas);
501 return (hasBG && NS_GET_A(bg->mBackgroundColor) == 255 &&
502 bg->mBackgroundClip == NS_STYLE_BG_CLIP_BORDER &&
503 !nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->
504 mBorderRadius));
507 PRBool
508 nsDisplayBackground::IsUniform(nsDisplayListBuilder* aBuilder) {
509 // theme background overrides any other background
510 if (mIsThemed)
511 return PR_FALSE;
513 PRBool isCanvas;
514 const nsStyleBackground* bg;
515 PRBool hasBG =
516 nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bg, &isCanvas);
517 if (!hasBG)
518 return PR_TRUE;
519 if ((bg->mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE) &&
520 !nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius) &&
521 bg->mBackgroundClip == NS_STYLE_BG_CLIP_BORDER)
522 return PR_TRUE;
523 return PR_FALSE;
526 PRBool
527 nsDisplayBackground::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder)
529 NS_ASSERTION(aBuilder->IsMovingFrame(mFrame),
530 "IsVaryingRelativeToMovingFrame called on non-moving frame!");
532 nsPresContext* presContext = mFrame->PresContext();
533 PRBool isCanvas;
534 const nsStyleBackground* bg;
535 PRBool hasBG =
536 nsCSSRendering::FindBackground(presContext, mFrame, &bg, &isCanvas);
537 if (!hasBG)
538 return PR_FALSE;
539 if (!bg->HasFixedBackground())
540 return PR_FALSE;
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;
552 void
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());
561 nsRect
562 nsDisplayBackground::GetBounds(nsDisplayListBuilder* aBuilder) {
563 if (mIsThemed)
564 return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
566 return nsRect(aBuilder->ToReferenceFrame(mFrame), mFrame->GetSize());
569 nsRect
570 nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder) {
571 return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
574 void
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());
586 PRBool
587 nsDisplayOutline::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
588 nsRegion* aVisibleRegion) {
589 if (!nsDisplayItem::OptimizeVisibility(aBuilder, aVisibleRegion))
590 return PR_FALSE;
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
599 return PR_FALSE;
603 return PR_TRUE;
606 void
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));
614 PRBool
615 nsDisplayBorder::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
616 nsRegion* aVisibleRegion) {
617 if (!nsDisplayItem::OptimizeVisibility(aBuilder, aVisibleRegion))
618 return PR_FALSE;
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
628 // visible
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
631 // further in).
632 return PR_FALSE;
635 return PR_TRUE;
638 void
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());
649 void
650 nsDisplayBoxShadow::Paint(nsDisplayListBuilder* aBuilder,
651 nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
652 nsPoint offset = aBuilder->ToReferenceFrame(mFrame);
653 nsCSSRendering::PaintBoxShadow(mFrame->PresContext(), *aCtx,
654 mFrame, offset);
657 nsRect
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() {
673 mList.DeleteAll();
676 nsIFrame*
677 nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
678 HitTestState* aState) {
679 return mList.HitTest(aBuilder, aPt, aState);
682 nsRect
683 nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder) {
684 return mList.GetBounds(aBuilder);
687 PRBool
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;
695 PRBool
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
699 return PR_FALSE;
702 PRBool nsDisplayWrapList::IsUniform(nsDisplayListBuilder* aBuilder) {
703 // We could try to do something but let's conservatively just return PR_FALSE.
704 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.
713 return PR_TRUE;
716 void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder,
717 nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
718 mList.Paint(aBuilder, aCtx, aDirtyRect);
721 static nsresult
722 WrapDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
723 nsDisplayList* aList, nsDisplayWrapper* aWrapper) {
724 if (!aList->GetTop())
725 return NS_OK;
726 nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
727 if (!item)
728 return NS_ERROR_OUT_OF_MEMORY;
729 // aList was emptied
730 aList->AppendToTop(item);
731 return NS_OK;
734 static nsresult
735 WrapEachDisplayItem(nsDisplayListBuilder* aBuilder,
736 nsDisplayList* aList, nsDisplayWrapper* aWrapper) {
737 nsDisplayList newList;
738 nsDisplayItem* item;
739 while ((item = aList->RemoveBottom())) {
740 item = aWrapper->WrapItem(aBuilder, item);
741 if (!item)
742 return NS_ERROR_OUT_OF_MEMORY;
743 newList.AppendToTop(item);
745 // aList was emptied
746 aList->AppendToTop(&newList);
747 return NS_OK;
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);
756 if (&aOut == &aIn)
757 return NS_OK;
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());
764 return NS_OK;
767 nsresult nsDisplayWrapper::WrapListsInPlace(nsDisplayListBuilder* aBuilder,
768 nsIFrame* aFrame, const nsDisplayListSet& aLists)
770 nsresult rv;
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);
801 #endif
803 PRBool nsDisplayOpacity::IsOpaque(nsDisplayListBuilder* aBuilder) {
804 // We are never opaque, if our opacity was < 1 then we wouldn't have
805 // been created.
806 return PR_FALSE;
809 void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder,
810 nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
812 float opacity = mFrame->GetStyleDisplay()->mOpacity;
814 nsRect bounds;
815 bounds.IntersectRect(GetBounds(aBuilder), aDirtyRect);
817 nsCOMPtr<nsIDeviceContext> devCtx;
818 aCtx->GetDeviceContext(*getter_AddRefs(devCtx));
820 gfxContext* ctx = aCtx->ThebesContext();
822 ctx->Save();
824 ctx->NewPath();
825 gfxRect r(bounds.x, bounds.y, bounds.width, bounds.height);
826 r.ScaleInverse(devCtx->AppUnitsPerDevPixel());
827 ctx->Rectangle(r, PR_TRUE);
828 ctx->Clip();
830 if (mNeedAlpha)
831 ctx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
832 else
833 ctx->PushGroup(gfxASurface::CONTENT_COLOR);
835 nsDisplayWrapList::Paint(aBuilder, aCtx, bounds);
837 ctx->PopGroupToSource();
838 ctx->SetOperator(gfxContext::OPERATOR_OVER);
839 ctx->Paint(opacity);
841 ctx->Restore();
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)
855 return PR_FALSE;
857 mNeedAlpha = visibleUnderChildren.Intersects(GetBounds(aBuilder));
858 return PR_TRUE;
861 PRBool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
862 if (aItem->GetType() != TYPE_OPACITY)
863 return PR_FALSE;
864 // items for the same content element should be merged into a single
865 // compositing group
866 // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
867 if (aItem->GetUnderlyingFrame()->GetContent() != mFrame->GetContent())
868 return PR_FALSE;
869 mList.AppendToBottom(&static_cast<nsDisplayOpacity*>(aItem)->mList);
870 return PR_TRUE;
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);
890 return r;
893 #ifdef NS_BUILD_REFCNT_LOGGING
894 nsDisplayClip::~nsDisplayClip() {
895 MOZ_COUNT_DTOR(nsDisplayClip);
897 #endif
899 void nsDisplayClip::Paint(nsDisplayListBuilder* aBuilder,
900 nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
901 nsRect dirty;
902 dirty.IntersectRect(mClip, aDirtyRect);
903 aCtx->PushState();
904 aCtx->SetClipRect(dirty, nsClipCombine_kIntersect);
905 nsDisplayWrapList::Paint(aBuilder, aCtx, dirty);
906 aCtx->PopState();
909 PRBool nsDisplayClip::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
910 nsRegion* aVisibleRegion) {
911 nsRegion clipped;
912 clipped.And(*aVisibleRegion, mClip);
913 nsRegion rNew(clipped);
914 PRBool anyVisible = nsDisplayWrapList::OptimizeVisibility(aBuilder, &rNew);
915 nsRegion subtracted;
916 subtracted.Sub(clipped, rNew);
917 aVisibleRegion->SimpleSubtract(subtracted);
918 return anyVisible;
921 PRBool nsDisplayClip::TryMerge(nsDisplayListBuilder* aBuilder,
922 nsDisplayItem* aItem) {
923 if (aItem->GetType() != TYPE_CLIP)
924 return PR_FALSE;
925 nsDisplayClip* other = static_cast<nsDisplayClip*>(aItem);
926 if (other->mClip != mClip || other->mClippingFrame != mClippingFrame)
927 return PR_FALSE;
928 mList.AppendToBottom(&other->mList);
929 return PR_TRUE;
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
951 // detection.
952 #undef UNIFIED_CONTINUATIONS
953 #undef DEBUG_HIT
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
962 * coordinate space.
964 #ifndef UNIFIED_CONTINUATIONS
966 nsRect
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());
973 #else
975 nsRect
976 nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
978 NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
980 nsRect result;
982 /* Iterate through the continuation list, unioning together all the
983 * bounding rects.
985 for (const nsIFrame *currFrame = aFrame->GetFirstContinuation();
986 currFrame != nsnull;
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()));
996 return result;
999 #endif
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.
1005 static
1006 gfxPoint GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
1007 float aFactor,
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. */
1023 gfxPoint result;
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. */
1037 else
1038 *coords[index] =
1039 NSAppUnitsToFloatPixels(display->
1040 mTransformOrigin[index].GetCoordValue(),
1041 aFactor);
1044 /* Adjust based on the origin of the rectangle. */
1045 result.x += NSAppUnitsToFloatPixels(boundingRect.x, aFactor);
1046 result.y += NSAppUnitsToFloatPixels(boundingRect.y, aFactor);
1048 return result;
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
1053 * hands it back.
1055 gfxMatrix
1056 nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame,
1057 const nsPoint &aOrigin,
1058 float aFactor,
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
1085 * the transform.
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
1093 * and can abort.
1095 gfxMatrix newTransformMatrix =
1096 GetResultingTransformMatrix(mFrame, aBuilder->ToReferenceFrame(mFrame),
1097 mFrame->PresContext()->AppUnitsPerDevPixel(),
1098 nsnull);
1099 if (newTransformMatrix.IsSingular())
1100 return;
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
1112 * transform data.
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)
1131 return PR_TRUE;
1134 #ifdef DEBUG_HIT
1135 #include <time.h>
1136 #endif
1138 /* HitTest does some fun stuff with matrix transforms to obtain the answer. */
1139 nsIFrame *nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder,
1140 nsPoint aPt,
1141 HitTestState *aState)
1143 /* Here's how this works:
1144 * 1. Get the matrix. If it's singular, abort (clearly we didn't hit
1145 * anything).
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();
1151 gfxMatrix matrix =
1152 GetResultingTransformMatrix(mFrame, aBuilder->ToReferenceFrame(mFrame),
1153 factor, nsnull);
1154 if (matrix.IsSingular())
1155 return nsnull;
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)
1161 matrix.Invert();
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)));
1167 #ifdef DEBUG_HIT
1168 printf("Frame: %p\n", dynamic_cast<void *>(mFrame));
1169 printf(" Untransformed point: (%f, %f)\n", result.x, result.y);
1170 #endif
1172 nsIFrame* resultFrame =
1173 mStoredList.HitTest(aBuilder,
1174 nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
1175 NSFloatPixelsToAppUnits(float(result.y), factor)), aState);
1177 #ifdef DEBUG_HIT
1178 if (resultFrame)
1179 printf(" Hit! Time: %f, frame: %p\n", static_cast<double>(clock()),
1180 dynamic_cast<void *>(resultFrame));
1181 printf("=== end of hit test ===\n");
1182 #endif
1184 return resultFrame;
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
1197 * is of the form
1199 * |a c e|
1200 * |b d f|
1201 * |0 0 1|
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
1215 * works.
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
1227 * glitches.
1229 #ifndef UNIFIED_CONTINUATIONS
1231 PRBool
1232 nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder,
1233 nsDisplayItem *aItem)
1235 return PR_FALSE;
1238 #else
1240 PRBool
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)
1249 return PR_FALSE;
1251 /* Check to see that both frames are part of the same content. */
1252 if (aItem->GetUnderlyingFrame()->GetContent() != mFrame->GetContent())
1253 return PR_FALSE;
1255 /* Now, move everything over to this frame and signal that
1256 * we merged things!
1258 mStoredList.GetList()->
1259 AppendToBottom(&static_cast<nsDisplayTransform *>(aItem)->mStoredList);
1260 return PR_TRUE;
1263 #endif
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
1275 * specified bounds.
1276 * @return The smallest rectangle containing the image of the transformed
1277 * rectangle.
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),
1292 factor);
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
1305 * empty rect.
1307 float factor = nsPresContext::AppUnitsPerCSSPixel();
1308 gfxMatrix matrix = GetResultingTransformMatrix(aFrame, aOrigin, factor, nsnull);
1309 if (matrix.IsSingular())
1310 return nsRect();
1312 /* We want to untransform the matrix, so invert the transformation first! */
1313 matrix.Invert();
1315 return nsLayoutUtils::MatrixTransformRect(aUntransformedBounds, matrix,
1316 factor);
1319 #ifdef MOZ_SVG
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);
1332 #endif
1334 PRBool nsDisplaySVGEffects::IsOpaque(nsDisplayListBuilder* aBuilder)
1336 return PR_FALSE;
1339 nsIFrame*
1340 nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
1341 HitTestState* aState)
1343 if (!nsSVGIntegrationUtils::HitTestFrameForEffects(mEffectsFrame,
1344 aPt - aBuilder->ToReferenceFrame(mEffectsFrame)))
1345 return nsnull;
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) {
1358 nsRegion vis;
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)
1374 return PR_FALSE;
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())
1379 return PR_FALSE;
1380 nsDisplaySVGEffects* other = static_cast<nsDisplaySVGEffects*>(aItem);
1381 mList.AppendToBottom(&other->mList);
1382 mBounds.UnionRect(mBounds,
1383 other->mBounds + other->mEffectsFrame->GetOffsetTo(mEffectsFrame));
1384 return PR_TRUE;
1386 #endif