1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:ts=2:et:sw=2:
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* base class of all rendering objects */
14 #include "mozilla/Attributes.h"
15 #include "mozilla/DebugOnly.h"
18 #include "nsFrameList.h"
19 #include "nsPlaceholderFrame.h"
20 #include "nsIContent.h"
21 #include "nsContentUtils.h"
24 #include "nsReadableUtils.h"
25 #include "nsStyleContext.h"
26 #include "nsTableOuterFrame.h"
28 #include "nsViewManager.h"
29 #include "nsIScrollableFrame.h"
30 #include "nsPresContext.h"
31 #include "nsStyleConsts.h"
32 #include "nsIPresShell.h"
35 #include "nsFrameManager.h"
36 #include "nsLayoutUtils.h"
37 #include "RestyleManager.h"
39 #include "nsIDOMNode.h"
40 #include "nsISelection.h"
41 #include "nsISelectionPrivate.h"
42 #include "nsFrameSelection.h"
43 #include "nsGkAtoms.h"
44 #include "nsCSSAnonBoxes.h"
46 #include "nsFrameTraversal.h"
48 #include "nsITextControlFrame.h"
49 #include "nsNameSpaceManager.h"
50 #include "nsIPercentHeightObserver.h"
51 #include "nsStyleStructInlines.h"
52 #include "FrameLayerBuilder.h"
54 #include "nsBidiPresUtils.h"
56 // For triple-click pref
57 #include "imgIContainer.h"
58 #include "imgIRequest.h"
60 #include "nsContainerFrame.h"
61 #include "nsBoxLayoutState.h"
62 #include "nsBlockFrame.h"
63 #include "nsDisplayList.h"
64 #include "nsSVGIntegrationUtils.h"
65 #include "nsSVGEffects.h"
66 #include "nsChangeHint.h"
67 #include "nsDeckFrame.h"
68 #include "nsSubDocumentFrame.h"
69 #include "SVGTextFrame.h"
71 #include "gfxContext.h"
72 #include "nsRenderingContext.h"
73 #include "nsAbsoluteContainingBlock.h"
74 #include "StickyScrollContainer.h"
75 #include "nsFontInflationData.h"
76 #include "gfxASurface.h"
78 #include "nsIFrameInlines.h"
80 #include "mozilla/AsyncEventDispatcher.h"
81 #include "mozilla/EventListenerManager.h"
82 #include "mozilla/EventStateManager.h"
83 #include "mozilla/EventStates.h"
84 #include "mozilla/Preferences.h"
85 #include "mozilla/LookAndFeel.h"
86 #include "mozilla/MouseEvents.h"
87 #include "mozilla/css/ImageLoader.h"
88 #include "mozilla/gfx/Tools.h"
89 #include "nsPrintfCString.h"
90 #include "ActiveLayerTracker.h"
93 #include "nsThemeConstants.h"
95 using namespace mozilla
;
96 using namespace mozilla::css
;
97 using namespace mozilla::dom
;
98 using namespace mozilla::gfx
;
99 using namespace mozilla::layers
;
100 using namespace mozilla::layout
;
102 // Struct containing cached metrics for box-wrapped frames.
103 struct nsBoxLayoutMetrics
109 nsSize mBlockMinSize
;
110 nsSize mBlockPrefSize
;
111 nscoord mBlockAscent
;
119 struct nsContentAndOffset
121 nsIContent
* mContent
;
125 // Some Misc #defines
126 #define SELECTION_DEBUG 0
127 #define FORCE_SELECTION_UPDATE 1
131 #include "nsILineIterator.h"
133 //non Hack prototypes
135 static void RefreshContentFrames(nsPresContext
* aPresContext
, nsIContent
* aStartContent
, nsIContent
* aEndContent
);
141 DestroyBoxMetrics(void* aPropertyValue
)
143 delete static_cast<nsBoxLayoutMetrics
*>(aPropertyValue
);
146 NS_DECLARE_FRAME_PROPERTY(BoxMetricsProperty
, DestroyBoxMetrics
)
149 InitBoxMetrics(nsIFrame
* aFrame
, bool aClear
)
151 FrameProperties props
= aFrame
->Properties();
153 props
.Delete(BoxMetricsProperty());
156 nsBoxLayoutMetrics
*metrics
= new nsBoxLayoutMetrics();
157 props
.Set(BoxMetricsProperty(), metrics
);
159 static_cast<nsFrame
*>(aFrame
)->nsFrame::MarkIntrinsicISizesDirty();
160 metrics
->mBlockAscent
= 0;
161 metrics
->mLastSize
.SizeTo(0, 0);
165 IsBoxWrapped(const nsIFrame
* aFrame
)
167 return aFrame
->GetParent() &&
168 aFrame
->GetParent()->IsBoxFrame() &&
169 !aFrame
->IsBoxFrame();
172 // Formerly the nsIFrameDebug interface
175 static bool gShowFrameBorders
= false;
177 void nsFrame::ShowFrameBorders(bool aEnable
)
179 gShowFrameBorders
= aEnable
;
182 bool nsFrame::GetShowFrameBorders()
184 return gShowFrameBorders
;
187 static bool gShowEventTargetFrameBorder
= false;
189 void nsFrame::ShowEventTargetFrameBorder(bool aEnable
)
191 gShowEventTargetFrameBorder
= aEnable
;
194 bool nsFrame::GetShowEventTargetFrameBorder()
196 return gShowEventTargetFrameBorder
;
200 * Note: the log module is created during library initialization which
201 * means that you cannot perform logging before then.
203 static PRLogModuleInfo
* gLogModule
;
205 static PRLogModuleInfo
* gStyleVerifyTreeLogModuleInfo
;
207 static uint32_t gStyleVerifyTreeEnable
= 0x55;
210 nsFrame::GetVerifyStyleTreeEnable()
212 if (gStyleVerifyTreeEnable
== 0x55) {
213 if (nullptr == gStyleVerifyTreeLogModuleInfo
) {
214 gStyleVerifyTreeLogModuleInfo
= PR_NewLogModule("styleverifytree");
215 gStyleVerifyTreeEnable
= 0 != gStyleVerifyTreeLogModuleInfo
->level
;
218 return gStyleVerifyTreeEnable
;
222 nsFrame::SetVerifyStyleTreeEnable(bool aEnabled
)
224 gStyleVerifyTreeEnable
= aEnabled
;
228 nsFrame::GetLogModuleInfo()
230 if (nullptr == gLogModule
) {
231 gLogModule
= PR_NewLogModule("frame");
239 DestroyAbsoluteContainingBlock(void* aPropertyValue
)
241 delete static_cast<nsAbsoluteContainingBlock
*>(aPropertyValue
);
244 NS_DECLARE_FRAME_PROPERTY(AbsoluteContainingBlockProperty
, DestroyAbsoluteContainingBlock
)
247 nsIFrame::HasAbsolutelyPositionedChildren() const {
248 return IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames();
251 nsAbsoluteContainingBlock
*
252 nsIFrame::GetAbsoluteContainingBlock() const {
253 NS_ASSERTION(IsAbsoluteContainer(), "The frame is not marked as an abspos container correctly");
254 nsAbsoluteContainingBlock
* absCB
= static_cast<nsAbsoluteContainingBlock
*>
255 (Properties().Get(AbsoluteContainingBlockProperty()));
256 NS_ASSERTION(absCB
, "The frame is marked as an abspos container but doesn't have the property");
261 nsIFrame::MarkAsAbsoluteContainingBlock()
263 MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN
);
264 NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()),
265 "Already has an abs-pos containing block property?");
266 NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN
),
267 "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
268 AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN
);
269 Properties().Set(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID()));
273 nsIFrame::MarkAsNotAbsoluteContainingBlock()
275 NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
276 NS_ASSERTION(Properties().Get(AbsoluteContainingBlockProperty()),
277 "Should have an abs-pos containing block property");
278 NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN
),
279 "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
280 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN
));
281 RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN
);
282 Properties().Delete(AbsoluteContainingBlockProperty());
286 nsIFrame::CheckAndClearPaintedState()
288 bool result
= (GetStateBits() & NS_FRAME_PAINTED_THEBES
);
289 RemoveStateBits(NS_FRAME_PAINTED_THEBES
);
291 nsIFrame::ChildListIterator
lists(this);
292 for (; !lists
.IsDone(); lists
.Next()) {
293 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
294 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
295 nsIFrame
* child
= childFrames
.get();
296 if (child
->CheckAndClearPaintedState()) {
305 nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags
) const
307 if (!StyleVisibility()->IsVisible()) {
311 const nsIFrame
* frame
= this;
313 nsView
* view
= frame
->GetView();
314 if (view
&& view
->GetVisibility() == nsViewVisibility_kHide
)
317 nsIFrame
* parent
= frame
->GetParent();
318 nsDeckFrame
* deck
= do_QueryFrame(parent
);
320 if (deck
->GetSelectedBox() != frame
)
327 parent
= nsLayoutUtils::GetCrossDocParentFrame(frame
);
331 if ((aFlags
& nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY
) == 0 &&
332 parent
->PresContext()->IsChrome() && !frame
->PresContext()->IsChrome()) {
336 if (!parent
->StyleVisibility()->IsVisible())
347 nsIFrame::FindCloserFrameForSelection(
349 nsIFrame::FrameWithDistance
* aCurrentBestFrame
)
351 if (nsLayoutUtils::PointIsCloserToRect(aPoint
, mRect
,
352 aCurrentBestFrame
->mXDistance
,
353 aCurrentBestFrame
->mYDistance
)) {
354 aCurrentBestFrame
->mFrame
= this;
359 nsIFrame::ContentStatesChanged(mozilla::EventStates aStates
)
364 NS_MergeReflowStatusInto(nsReflowStatus
* aPrimary
, nsReflowStatus aSecondary
)
366 *aPrimary
|= aSecondary
&
367 (NS_FRAME_NOT_COMPLETE
| NS_FRAME_OVERFLOW_INCOMPLETE
|
368 NS_FRAME_TRUNCATED
| NS_FRAME_REFLOW_NEXTINFLOW
);
369 if (*aPrimary
& NS_FRAME_NOT_COMPLETE
) {
370 *aPrimary
&= ~NS_FRAME_OVERFLOW_INCOMPLETE
;
375 nsWeakFrame::Init(nsIFrame
* aFrame
)
377 Clear(mFrame
? mFrame
->PresContext()->GetPresShell() : nullptr);
380 nsIPresShell
* shell
= mFrame
->PresContext()->GetPresShell();
381 NS_WARN_IF_FALSE(shell
, "Null PresShell in nsWeakFrame!");
383 shell
->AddWeakFrame(this);
391 NS_NewEmptyFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
393 return new (aPresShell
) nsFrame(aContext
);
396 nsFrame::nsFrame(nsStyleContext
* aContext
)
398 MOZ_COUNT_CTOR(nsFrame
);
400 mState
= NS_FRAME_FIRST_REFLOW
| NS_FRAME_IS_DIRTY
;
401 mStyleContext
= aContext
;
402 mStyleContext
->AddRef();
407 MOZ_COUNT_DTOR(nsFrame
);
409 NS_IF_RELEASE(mContent
);
410 mStyleContext
->Release();
413 NS_IMPL_FRAMEARENA_HELPERS(nsFrame
)
415 // Dummy operator delete. Will never be called, but must be defined
416 // to satisfy some C++ ABIs.
418 nsFrame::operator delete(void *, size_t)
420 NS_RUNTIMEABORT("nsFrame::operator delete should never be called");
423 NS_QUERYFRAME_HEAD(nsFrame
)
424 NS_QUERYFRAME_ENTRY(nsIFrame
)
425 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
427 /////////////////////////////////////////////////////////////////////////////
431 IsFontSizeInflationContainer(nsIFrame
* aFrame
,
432 const nsStyleDisplay
* aStyleDisplay
)
435 * Font size inflation is built around the idea that we're inflating
436 * the fonts for a pan-and-zoom UI so that when the user scales up a
437 * block or other container to fill the width of the device, the fonts
438 * will be readable. To do this, we need to pick what counts as a
441 * From a code perspective, the only hard requirement is that frames
442 * that are line participants
443 * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never
444 * containers, since line layout assumes that the inflation is
445 * consistent within a line.
447 * This is not an imposition, since we obviously want a bunch of text
448 * (possibly with inline elements) flowing within a block to count the
449 * block (or higher) as its container.
451 * We also want form controls, including the text in the anonymous
452 * content inside of them, to match each other and the text next to
453 * them, so they and their anonymous content should also not be a
456 * However, because we can't reliably compute sizes across XUL during
457 * reflow, any XUL frame with a XUL parent is always a container.
459 * There are contexts where it would be nice if some blocks didn't
460 * count as a container, so that, for example, an indented quotation
461 * didn't end up with a smaller font size. However, it's hard to
462 * distinguish these situations where we really do want the indented
463 * thing to count as a container, so we don't try, and blocks are
467 // The root frame should always be an inflation container.
468 if (!aFrame
->GetParent()) {
472 nsIContent
*content
= aFrame
->GetContent();
473 // Ruby text containers are excluded here because they inherit from block
474 // (should not be considered inline).
475 bool isInline
= (aFrame
->GetDisplay() == NS_STYLE_DISPLAY_INLINE
||
476 (aFrame
->StyleDisplay()->IsRubyDisplayType() &&
477 aFrame
->GetDisplay() !=
478 NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER
) ||
479 (aFrame
->IsFloating() &&
480 aFrame
->GetType() == nsGkAtoms::letterFrame
) ||
481 // Given multiple frames for the same node, only the
482 // outer one should be considered a container.
483 // (Important, e.g., for nsSelectsAreaFrame.)
484 (aFrame
->GetParent()->GetContent() == content
) ||
485 (content
&& (content
->IsHTML(nsGkAtoms::option
) ||
486 content
->IsHTML(nsGkAtoms::optgroup
) ||
487 content
->IsHTML(nsGkAtoms::select
) ||
488 content
->IsInNativeAnonymousSubtree()))) &&
489 !(aFrame
->IsBoxFrame() && aFrame
->GetParent()->IsBoxFrame());
490 NS_ASSERTION(!aFrame
->IsFrameOfType(nsIFrame::eLineParticipant
) ||
492 // br frames and mathml frames report being line
493 // participants even when their position or display is
495 aFrame
->GetType() == nsGkAtoms::brFrame
||
496 aFrame
->IsFrameOfType(nsIFrame::eMathML
),
497 "line participants must not be containers");
498 NS_ASSERTION(aFrame
->GetType() != nsGkAtoms::bulletFrame
|| isInline
,
499 "bullets should not be containers");
504 nsFrame::Init(nsIContent
* aContent
,
505 nsContainerFrame
* aParent
,
506 nsIFrame
* aPrevInFlow
)
508 NS_PRECONDITION(!mContent
, "Double-initing a frame?");
509 NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames
) &&
510 !IsFrameOfType(eDEBUGNoFrames
),
511 "IsFrameOfType implementation that doesn't call base class");
521 // Make sure the general flags bits are the same
522 nsFrameState state
= aPrevInFlow
->GetStateBits();
524 // Make bits that are currently off (see constructor) the same:
525 mState
|= state
& (NS_FRAME_INDEPENDENT_SELECTION
|
526 NS_FRAME_PART_OF_IBSPLIT
|
527 NS_FRAME_MAY_BE_TRANSFORMED
|
528 NS_FRAME_MAY_HAVE_GENERATED_CONTENT
|
529 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN
);
532 nsFrameState state
= GetParent()->GetStateBits();
534 // Make bits that are currently off (see constructor) the same:
535 mState
|= state
& (NS_FRAME_INDEPENDENT_SELECTION
|
536 NS_FRAME_GENERATED_CONTENT
|
537 NS_FRAME_IS_SVG_TEXT
|
539 NS_FRAME_IS_NONDISPLAY
);
541 const nsStyleDisplay
*disp
= StyleDisplay();
542 if (disp
->HasTransform(this)) {
543 // The frame gets reconstructed if we toggle the -moz-transform
544 // property, so we can set this bit here and then ignore it.
545 mState
|= NS_FRAME_MAY_BE_TRANSFORMED
;
547 if (disp
->mPosition
== NS_STYLE_POSITION_STICKY
&&
549 !(mState
& NS_FRAME_IS_NONDISPLAY
) &&
550 !disp
->IsInnerTableStyle()) {
551 // Note that we only add first continuations, but we really only
552 // want to add first continuation-or-ib-split-siblings. But since we
553 // don't yet know if we're a later part of a block-in-inline split,
554 // we'll just add later members of a block-in-inline split here, and
555 // then StickyScrollContainer will remove them later.
556 // We don't currently support relative positioning of inner table
557 // elements (bug 35168), so exclude them from sticky positioning too.
558 StickyScrollContainer
* ssc
=
559 StickyScrollContainer::GetStickyScrollContainerForFrame(this);
565 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent()
567 // We have assertions that check inflation invariants even when
568 // font size inflation is not enabled.
572 if (IsFontSizeInflationContainer(this, disp
)) {
573 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
);
575 // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
576 disp
->IsFloating(this) || disp
->IsAbsolutelyPositioned(this)) {
577 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
580 NS_ASSERTION(GetParent() ||
581 (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER
),
582 "root frame should always be a container");
585 DidSetStyleContext(nullptr);
587 if (::IsBoxWrapped(this))
588 ::InitBoxMetrics(this, false);
592 nsFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
594 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
595 "destroy called on frame while scripts not blocked");
596 NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
597 "Frames should be removed before destruction.");
598 NS_ASSERTION(aDestructRoot
, "Must specify destruct root");
599 MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
601 nsSVGEffects::InvalidateDirectRenderingObservers(this);
603 if (StyleDisplay()->mPosition
== NS_STYLE_POSITION_STICKY
) {
604 StickyScrollContainer
* ssc
=
605 StickyScrollContainer::GetStickyScrollContainerForFrame(this);
607 ssc
->RemoveFrame(this);
611 // Get the view pointer now before the frame properties disappear
612 // when we call NotifyDestroyingFrame()
613 nsView
* view
= GetView();
614 nsPresContext
* presContext
= PresContext();
616 nsIPresShell
*shell
= presContext
->GetPresShell();
617 if (mState
& NS_FRAME_OUT_OF_FLOW
) {
618 nsPlaceholderFrame
* placeholder
=
619 shell
->FrameManager()->GetPlaceholderFrameFor(this);
620 NS_ASSERTION(!placeholder
|| (aDestructRoot
!= this),
621 "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
622 NS_ASSERTION(!placeholder
||
623 nsLayoutUtils::IsProperAncestorFrame(aDestructRoot
, placeholder
),
624 "Placeholder relationship should have been torn down already; "
625 "this might mean we have a stray placeholder in the tree.");
627 shell
->FrameManager()->UnregisterPlaceholderFrame(placeholder
);
628 placeholder
->SetOutOfFlowFrame(nullptr);
632 // If we have any IB split siblings, clear their references to us.
633 // (Note: This has to happen before we call shell->NotifyDestroyingFrame,
634 // because that clears our Properties() table.)
635 if (mState
& NS_FRAME_PART_OF_IBSPLIT
) {
636 // Delete previous sibling's reference to me.
637 nsIFrame
* prevSib
= static_cast<nsIFrame
*>
638 (Properties().Get(nsIFrame::IBSplitPrevSibling()));
640 NS_WARN_IF_FALSE(this ==
641 prevSib
->Properties().Get(nsIFrame::IBSplitSibling()),
642 "IB sibling chain is inconsistent");
643 prevSib
->Properties().Delete(nsIFrame::IBSplitSibling());
646 // Delete next sibling's reference to me.
647 nsIFrame
* nextSib
= static_cast<nsIFrame
*>
648 (Properties().Get(nsIFrame::IBSplitSibling()));
650 NS_WARN_IF_FALSE(this ==
651 nextSib
->Properties().Get(nsIFrame::IBSplitPrevSibling()),
652 "IB sibling chain is inconsistent");
653 nextSib
->Properties().Delete(nsIFrame::IBSplitPrevSibling());
657 bool isPrimaryFrame
= (mContent
&& mContent
->GetPrimaryFrame() == this);
658 if (isPrimaryFrame
) {
659 // This needs to happen before shell->NotifyDestroyingFrame because
660 // that clears our Properties() table.
661 ActiveLayerTracker::TransferActivityToContent(this, mContent
);
663 // Unfortunately, we need to do this for all frames being reframed
664 // and not only those whose current style involves CSS transitions,
665 // because what matters is whether the new style (not the old)
666 // specifies CSS transitions.
667 RestyleManager::ReframingStyleContexts
* rsc
=
668 presContext
->RestyleManager()->GetReframingStyleContexts();
670 rsc
->Put(mContent
, mStyleContext
);
674 shell
->NotifyDestroyingFrame(this);
676 if (mState
& NS_FRAME_EXTERNAL_REFERENCE
) {
677 shell
->ClearFrameRefs(this);
681 // Break association between view and frame
682 view
->SetFrame(nullptr);
688 // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
689 if (isPrimaryFrame
) {
690 mContent
->SetPrimaryFrame(nullptr);
693 // Must retrieve the object ID before calling destructors, so the
694 // vtable is still valid.
696 // Note to future tweakers: having the method that returns the
697 // object size call the destructor will not avoid an indirect call;
698 // the compiler cannot devirtualize the call to the destructor even
699 // if it's from a method defined in the same class.
701 nsQueryFrame::FrameIID id
= GetFrameId();
704 // Now that we're totally cleaned out, we need to add ourselves to
705 // the presshell's recycler.
706 shell
->FreeFrame(id
, this);
710 nsFrame::GetOffsets(int32_t &aStart
, int32_t &aEnd
) const
717 // Subclass hook for style post processing
719 nsFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
722 SVGTextFrame
* svgTextFrame
= static_cast<SVGTextFrame
*>(
723 nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::svgTextFrame
));
724 nsIFrame
* anonBlock
= svgTextFrame
->GetFirstPrincipalChild();
725 // Just as in SVGTextFrame::DidSetStyleContext, we need to ensure that
726 // any non-display SVGTextFrames get reflowed when a child text frame
729 // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
730 // anonymous block frame rather than our self, since NS_FRAME_FIRST_REFLOW
731 // may be set on us if we're a new frame that has been inserted after the
732 // document's first reflow. (In which case this DidSetStyleContext call may
733 // be happening under frame construction under a Reflow() call.)
734 if (anonBlock
&& !(anonBlock
->GetStateBits() & NS_FRAME_FIRST_REFLOW
) &&
735 (svgTextFrame
->GetStateBits() & NS_FRAME_IS_NONDISPLAY
) &&
736 !(svgTextFrame
->GetStateBits() & NS_STATE_SVG_TEXT_IN_REFLOW
)) {
737 svgTextFrame
->ScheduleReflowSVGNonDisplayText();
741 ImageLoader
* imageLoader
= PresContext()->Document()->StyleImageLoader();
743 // If the old context had a background image image and new context
744 // does not have the same image, clear the image load notifier
745 // (which keeps the image loading, if it still is) for the frame.
746 // We want to do this conservatively because some frames paint their
747 // backgrounds from some other frame's style data, and we don't want
748 // to clear those notifiers unless we have to. (They'll be reset
749 // when we paint, although we could miss a notification in that
751 const nsStyleBackground
*oldBG
= aOldStyleContext
?
752 aOldStyleContext
->StyleBackground() :
754 const nsStyleBackground
*newBG
= StyleBackground();
756 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i
, oldBG
) {
757 // If there is an image in oldBG that's not in newBG, drop it.
758 if (i
>= newBG
->mImageCount
||
759 !oldBG
->mLayers
[i
].mImage
.ImageDataEquals(newBG
->mLayers
[i
].mImage
)) {
760 const nsStyleImage
& oldImage
= oldBG
->mLayers
[i
].mImage
;
761 if (oldImage
.GetType() != eStyleImageType_Image
) {
765 imageLoader
->DisassociateRequestFromFrame(oldImage
.GetImageData(),
771 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i
, newBG
) {
772 // If there is an image in newBG that's not in oldBG, add it.
773 if (!oldBG
|| i
>= oldBG
->mImageCount
||
774 !newBG
->mLayers
[i
].mImage
.ImageDataEquals(oldBG
->mLayers
[i
].mImage
)) {
775 const nsStyleImage
& newImage
= newBG
->mLayers
[i
].mImage
;
776 if (newImage
.GetType() != eStyleImageType_Image
) {
780 imageLoader
->AssociateRequestToFrame(newImage
.GetImageData(), this);
784 if (aOldStyleContext
) {
785 // If we detect a change on margin, padding or border, we store the old
786 // values on the frame itself between now and reflow, so if someone
787 // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
788 // can give an accurate answer.
789 // We don't want to set the property if one already exists.
790 FrameProperties props
= Properties();
791 nsMargin
oldValue(0, 0, 0, 0);
792 nsMargin
newValue(0, 0, 0, 0);
793 const nsStyleMargin
* oldMargin
= aOldStyleContext
->PeekStyleMargin();
794 if (oldMargin
&& oldMargin
->GetMargin(oldValue
)) {
795 if ((!StyleMargin()->GetMargin(newValue
) || oldValue
!= newValue
) &&
796 !props
.Get(UsedMarginProperty())) {
797 props
.Set(UsedMarginProperty(), new nsMargin(oldValue
));
801 const nsStylePadding
* oldPadding
= aOldStyleContext
->PeekStylePadding();
802 if (oldPadding
&& oldPadding
->GetPadding(oldValue
)) {
803 if ((!StylePadding()->GetPadding(newValue
) || oldValue
!= newValue
) &&
804 !props
.Get(UsedPaddingProperty())) {
805 props
.Set(UsedPaddingProperty(), new nsMargin(oldValue
));
809 const nsStyleBorder
* oldBorder
= aOldStyleContext
->PeekStyleBorder();
811 oldValue
= oldBorder
->GetComputedBorder();
812 newValue
= StyleBorder()->GetComputedBorder();
813 if (oldValue
!= newValue
&&
814 !props
.Get(UsedBorderProperty())) {
815 props
.Set(UsedBorderProperty(), new nsMargin(oldValue
));
820 imgIRequest
*oldBorderImage
= aOldStyleContext
821 ? aOldStyleContext
->StyleBorder()->GetBorderImageRequest()
823 imgIRequest
*newBorderImage
= StyleBorder()->GetBorderImageRequest();
824 // FIXME (Bug 759996): The following is no longer true.
825 // For border-images, we can't be as conservative (we need to set the
826 // new loaders if there has been any change) since the CalcDifference
827 // call depended on the result of GetComputedBorder() and that result
828 // depends on whether the image has loaded, start the image load now
829 // so that we'll get notified when it completes loading and can do a
830 // restyle. Otherwise, the image might finish loading from the
831 // network before we start listening to its notifications, and then
832 // we'll never know that it's finished loading. Likewise, we want to
833 // do this for freshly-created frames to prevent a similar race if the
834 // image loads between reflow (which can depend on whether the image
835 // is loaded) and paint. We also don't really care about any callers
836 // who try to paint borders with a different style context, because
837 // they won't have the correct size for the border either.
838 if (oldBorderImage
!= newBorderImage
) {
839 // stop and restart the image loading/notification
840 if (oldBorderImage
) {
841 imageLoader
->DisassociateRequestFromFrame(oldBorderImage
, this);
843 if (newBorderImage
) {
844 imageLoader
->AssociateRequestToFrame(newBorderImage
, this);
848 // If the page contains markup that overrides text direction, and
849 // does not contain any characters that would activate the Unicode
850 // bidi algorithm, we need to call |SetBidiEnabled| on the pres
851 // context before reflow starts. See bug 115921.
852 if (StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
853 PresContext()->SetBidiEnabled();
857 // MSVC fails with link error "one or more multiply defined symbols found",
858 // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
859 // etc if they are not defined.
861 // static nsIFrame constants; initialized in the header file.
862 const nsIFrame::ChildListID
nsIFrame::kPrincipalList
;
863 const nsIFrame::ChildListID
nsIFrame::kAbsoluteList
;
864 const nsIFrame::ChildListID
nsIFrame::kBulletList
;
865 const nsIFrame::ChildListID
nsIFrame::kCaptionList
;
866 const nsIFrame::ChildListID
nsIFrame::kColGroupList
;
867 const nsIFrame::ChildListID
nsIFrame::kExcessOverflowContainersList
;
868 const nsIFrame::ChildListID
nsIFrame::kFixedList
;
869 const nsIFrame::ChildListID
nsIFrame::kFloatList
;
870 const nsIFrame::ChildListID
nsIFrame::kOverflowContainersList
;
871 const nsIFrame::ChildListID
nsIFrame::kOverflowList
;
872 const nsIFrame::ChildListID
nsIFrame::kOverflowOutOfFlowList
;
873 const nsIFrame::ChildListID
nsIFrame::kPopupList
;
874 const nsIFrame::ChildListID
nsIFrame::kPushedFloatsList
;
875 const nsIFrame::ChildListID
nsIFrame::kSelectPopupList
;
876 const nsIFrame::ChildListID
nsIFrame::kNoReflowPrincipalList
;
879 /* virtual */ nsMargin
880 nsIFrame::GetUsedMargin() const
882 nsMargin
margin(0, 0, 0, 0);
883 if (((mState
& NS_FRAME_FIRST_REFLOW
) &&
884 !(mState
& NS_FRAME_IN_REFLOW
)) ||
888 nsMargin
*m
= static_cast<nsMargin
*>
889 (Properties().Get(UsedMarginProperty()));
893 DebugOnly
<bool> hasMargin
= StyleMargin()->GetMargin(margin
);
894 NS_ASSERTION(hasMargin
, "We should have a margin here! (out of memory?)");
899 /* virtual */ nsMargin
900 nsIFrame::GetUsedBorder() const
902 nsMargin
border(0, 0, 0, 0);
903 if (((mState
& NS_FRAME_FIRST_REFLOW
) &&
904 !(mState
& NS_FRAME_IN_REFLOW
)) ||
908 // Theme methods don't use const-ness.
909 nsIFrame
*mutable_this
= const_cast<nsIFrame
*>(this);
911 const nsStyleDisplay
*disp
= StyleDisplay();
912 if (mutable_this
->IsThemed(disp
)) {
914 nsPresContext
*presContext
= PresContext();
915 presContext
->GetTheme()->GetWidgetBorder(presContext
->DeviceContext(),
916 mutable_this
, disp
->mAppearance
,
918 border
.left
= presContext
->DevPixelsToAppUnits(result
.left
);
919 border
.top
= presContext
->DevPixelsToAppUnits(result
.top
);
920 border
.right
= presContext
->DevPixelsToAppUnits(result
.right
);
921 border
.bottom
= presContext
->DevPixelsToAppUnits(result
.bottom
);
925 nsMargin
*b
= static_cast<nsMargin
*>
926 (Properties().Get(UsedBorderProperty()));
930 border
= StyleBorder()->GetComputedBorder();
935 /* virtual */ nsMargin
936 nsIFrame::GetUsedPadding() const
938 nsMargin
padding(0, 0, 0, 0);
939 if (((mState
& NS_FRAME_FIRST_REFLOW
) &&
940 !(mState
& NS_FRAME_IN_REFLOW
)) ||
944 // Theme methods don't use const-ness.
945 nsIFrame
*mutable_this
= const_cast<nsIFrame
*>(this);
947 const nsStyleDisplay
*disp
= StyleDisplay();
948 if (mutable_this
->IsThemed(disp
)) {
949 nsPresContext
*presContext
= PresContext();
951 if (presContext
->GetTheme()->GetWidgetPadding(presContext
->DeviceContext(),
955 padding
.top
= presContext
->DevPixelsToAppUnits(widget
.top
);
956 padding
.right
= presContext
->DevPixelsToAppUnits(widget
.right
);
957 padding
.bottom
= presContext
->DevPixelsToAppUnits(widget
.bottom
);
958 padding
.left
= presContext
->DevPixelsToAppUnits(widget
.left
);
963 nsMargin
*p
= static_cast<nsMargin
*>
964 (Properties().Get(UsedPaddingProperty()));
968 DebugOnly
<bool> hasPadding
= StylePadding()->GetPadding(padding
);
969 NS_ASSERTION(hasPadding
, "We should have padding here! (out of memory?)");
975 nsIFrame::GetSkipSides(const nsHTMLReflowState
* aReflowState
) const
977 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
978 NS_STYLE_BOX_DECORATION_BREAK_CLONE
)) {
982 // Convert the logical skip sides to physical sides using the frame's
984 WritingMode writingMode
= GetWritingMode();
985 LogicalSides logicalSkip
= GetLogicalSkipSides(aReflowState
);
988 if (logicalSkip
.BStart()) {
989 if (writingMode
.IsVertical()) {
990 skip
|= writingMode
.IsVerticalLR() ? eSideBitsLeft
: eSideBitsRight
;
992 skip
|= eSideBitsTop
;
996 if (logicalSkip
.BEnd()) {
997 if (writingMode
.IsVertical()) {
998 skip
|= writingMode
.IsVerticalLR() ? eSideBitsRight
: eSideBitsLeft
;
1000 skip
|= eSideBitsBottom
;
1004 if (logicalSkip
.IStart()) {
1005 if (writingMode
.IsVertical()) {
1006 skip
|= eSideBitsTop
;
1008 skip
|= writingMode
.IsBidiLTR() ? eSideBitsLeft
: eSideBitsRight
;
1012 if (logicalSkip
.IEnd()) {
1013 if (writingMode
.IsVertical()) {
1014 skip
|= eSideBitsBottom
;
1016 skip
|= writingMode
.IsBidiLTR() ? eSideBitsRight
: eSideBitsLeft
;
1023 nsIFrame::GetPaddingRectRelativeToSelf() const
1025 nsMargin
border(GetUsedBorder());
1026 border
.ApplySkipSides(GetSkipSides());
1027 nsRect
r(0, 0, mRect
.width
, mRect
.height
);
1033 nsIFrame::GetPaddingRect() const
1035 return GetPaddingRectRelativeToSelf() + GetPosition();
1039 nsIFrame::GetWritingMode(nsIFrame
* aSubFrame
) const
1041 WritingMode writingMode
= GetWritingMode();
1043 if (!writingMode
.IsVertical() &&
1044 (StyleTextReset()->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_PLAINTEXT
)) {
1045 nsBidiLevel frameLevel
= nsBidiPresUtils::GetFrameBaseLevel(aSubFrame
);
1046 writingMode
.SetDirectionFromBidiLevel(frameLevel
);
1053 nsIFrame::GetMarginRectRelativeToSelf() const
1055 nsMargin m
= GetUsedMargin();
1056 m
.ApplySkipSides(GetSkipSides());
1057 nsRect
r(0, 0, mRect
.width
, mRect
.height
);
1063 nsIFrame::IsTransformed() const
1065 return ((mState
& NS_FRAME_MAY_BE_TRANSFORMED
) &&
1066 (StyleDisplay()->HasTransform(this) ||
1067 IsSVGTransformed() ||
1069 nsLayoutUtils::HasAnimationsForCompositor(mContent
,
1070 eCSSProperty_transform
) &&
1071 IsFrameOfType(eSupportsCSSTransforms
) &&
1072 mContent
->GetPrimaryFrame() == this)));
1076 nsIFrame::HasOpacityInternal(float aThreshold
) const
1078 MOZ_ASSERT(0.0 <= aThreshold
&& aThreshold
<= 1.0, "Invalid argument");
1079 const nsStyleDisplay
* displayStyle
= StyleDisplay();
1080 return StyleDisplay()->mOpacity
< aThreshold
||
1081 (displayStyle
->mWillChangeBitField
& NS_STYLE_WILL_CHANGE_OPACITY
) ||
1083 nsLayoutUtils::HasAnimationsForCompositor(mContent
,
1084 eCSSProperty_opacity
) &&
1085 mContent
->GetPrimaryFrame() == this);
1089 nsIFrame::IsSVGTransformed(gfx::Matrix
*aOwnTransforms
,
1090 gfx::Matrix
*aFromParentTransforms
) const
1096 nsIFrame::Preserves3DChildren() const
1098 const nsStyleDisplay
* disp
= StyleDisplay();
1099 if (disp
->mTransformStyle
!= NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D
||
1100 !IsFrameOfType(nsIFrame::eSupportsCSSTransforms
)) {
1104 // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d.
1105 if (GetType() == nsGkAtoms::scrollFrame
) {
1110 return !nsFrame::ShouldApplyOverflowClipping(this, disp
) &&
1111 !GetClipPropClipRect(disp
, &temp
, GetSize()) &&
1112 !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1116 nsIFrame::Preserves3D() const
1118 if (!GetParent() || !GetParent()->Preserves3DChildren()) {
1121 return StyleDisplay()->HasTransform(this) || StyleDisplay()->BackfaceIsHidden();
1125 nsIFrame::HasPerspective() const
1127 if (!IsTransformed()) {
1130 nsStyleContext
* parentStyleContext
= StyleContext()->GetParent();
1131 if (!parentStyleContext
) {
1134 const nsStyleDisplay
* parentDisp
= parentStyleContext
->StyleDisplay();
1135 return parentDisp
->mChildPerspective
.GetUnit() == eStyleUnit_Coord
;
1139 nsIFrame::ChildrenHavePerspective() const
1141 return StyleDisplay()->HasPerspectiveStyle();
1145 nsIFrame::GetContentRectRelativeToSelf() const
1147 nsMargin
bp(GetUsedBorderAndPadding());
1148 bp
.ApplySkipSides(GetSkipSides());
1149 nsRect
r(0, 0, mRect
.width
, mRect
.height
);
1155 nsIFrame::GetContentRect() const
1157 return GetContentRectRelativeToSelf() + GetPosition();
1161 nsIFrame::ComputeBorderRadii(const nsStyleCorners
& aBorderRadius
,
1162 const nsSize
& aFrameSize
,
1163 const nsSize
& aBorderArea
,
1167 // Percentages are relative to whichever side they're on.
1168 NS_FOR_CSS_HALF_CORNERS(i
) {
1169 const nsStyleCoord c
= aBorderRadius
.Get(i
);
1171 NS_HALF_CORNER_IS_X(i
) ? aFrameSize
.width
: aFrameSize
.height
;
1173 if (c
.IsCoordPercentCalcUnit()) {
1174 aRadii
[i
] = nsRuleNode::ComputeCoordPercentCalc(c
, axis
);
1175 if (aRadii
[i
] < 0) {
1180 NS_NOTREACHED("ComputeBorderRadii: bad unit");
1185 if (aSkipSides
.Top()) {
1186 aRadii
[NS_CORNER_TOP_LEFT_X
] = 0;
1187 aRadii
[NS_CORNER_TOP_LEFT_Y
] = 0;
1188 aRadii
[NS_CORNER_TOP_RIGHT_X
] = 0;
1189 aRadii
[NS_CORNER_TOP_RIGHT_Y
] = 0;
1192 if (aSkipSides
.Right()) {
1193 aRadii
[NS_CORNER_TOP_RIGHT_X
] = 0;
1194 aRadii
[NS_CORNER_TOP_RIGHT_Y
] = 0;
1195 aRadii
[NS_CORNER_BOTTOM_RIGHT_X
] = 0;
1196 aRadii
[NS_CORNER_BOTTOM_RIGHT_Y
] = 0;
1199 if (aSkipSides
.Bottom()) {
1200 aRadii
[NS_CORNER_BOTTOM_RIGHT_X
] = 0;
1201 aRadii
[NS_CORNER_BOTTOM_RIGHT_Y
] = 0;
1202 aRadii
[NS_CORNER_BOTTOM_LEFT_X
] = 0;
1203 aRadii
[NS_CORNER_BOTTOM_LEFT_Y
] = 0;
1206 if (aSkipSides
.Left()) {
1207 aRadii
[NS_CORNER_BOTTOM_LEFT_X
] = 0;
1208 aRadii
[NS_CORNER_BOTTOM_LEFT_Y
] = 0;
1209 aRadii
[NS_CORNER_TOP_LEFT_X
] = 0;
1210 aRadii
[NS_CORNER_TOP_LEFT_Y
] = 0;
1213 // css3-background specifies this algorithm for reducing
1214 // corner radii when they are too big.
1215 bool haveRadius
= false;
1216 double ratio
= 1.0f
;
1217 NS_FOR_CSS_SIDES(side
) {
1218 uint32_t hc1
= NS_SIDE_TO_HALF_CORNER(side
, false, true);
1219 uint32_t hc2
= NS_SIDE_TO_HALF_CORNER(side
, true, true);
1221 NS_SIDE_IS_VERTICAL(side
) ? aBorderArea
.height
: aBorderArea
.width
;
1222 nscoord sum
= aRadii
[hc1
] + aRadii
[hc2
];
1226 // avoid floating point division in the normal case
1228 ratio
= std::min(ratio
, double(length
)/sum
);
1231 NS_FOR_CSS_HALF_CORNERS(corner
) {
1232 aRadii
[corner
] *= ratio
;
1240 nsIFrame::InsetBorderRadii(nscoord aRadii
[8], const nsMargin
&aOffsets
)
1242 NS_FOR_CSS_SIDES(side
) {
1243 nscoord offset
= aOffsets
.Side(side
);
1244 uint32_t hc1
= NS_SIDE_TO_HALF_CORNER(side
, false, false);
1245 uint32_t hc2
= NS_SIDE_TO_HALF_CORNER(side
, true, false);
1246 aRadii
[hc1
] = std::max(0, aRadii
[hc1
] - offset
);
1247 aRadii
[hc2
] = std::max(0, aRadii
[hc2
] - offset
);
1252 nsIFrame::OutsetBorderRadii(nscoord aRadii
[8], const nsMargin
&aOffsets
)
1254 NS_FOR_CSS_SIDES(side
) {
1255 nscoord offset
= aOffsets
.Side(side
);
1256 uint32_t hc1
= NS_SIDE_TO_HALF_CORNER(side
, false, false);
1257 uint32_t hc2
= NS_SIDE_TO_HALF_CORNER(side
, true, false);
1258 if (aRadii
[hc1
] > 0)
1259 aRadii
[hc1
] += offset
;
1260 if (aRadii
[hc2
] > 0)
1261 aRadii
[hc2
] += offset
;
1266 nsIFrame::GetBorderRadii(const nsSize
& aFrameSize
, const nsSize
& aBorderArea
,
1267 Sides aSkipSides
, nscoord aRadii
[8]) const
1270 // When we're themed, the native theme code draws the border and
1271 // background, and therefore it doesn't make sense to tell other
1272 // code that's interested in border-radius that we have any radii.
1274 // In an ideal world, we might have a way for the them to tell us an
1275 // border radius, but since we don't, we're better off assuming
1277 NS_FOR_CSS_HALF_CORNERS(corner
) {
1282 return ComputeBorderRadii(StyleBorder()->mBorderRadius
,
1283 aFrameSize
, aBorderArea
,
1284 aSkipSides
, aRadii
);
1288 nsIFrame::GetBorderRadii(nscoord aRadii
[8]) const
1290 nsSize sz
= GetSize();
1291 return GetBorderRadii(sz
, sz
, GetSkipSides(), aRadii
);
1295 nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii
[8]) const
1297 if (!GetBorderRadii(aRadii
))
1299 InsetBorderRadii(aRadii
, GetUsedBorder());
1300 NS_FOR_CSS_HALF_CORNERS(corner
) {
1308 nsIFrame::GetContentBoxBorderRadii(nscoord aRadii
[8]) const
1310 if (!GetBorderRadii(aRadii
))
1312 InsetBorderRadii(aRadii
, GetUsedBorderAndPadding());
1313 NS_FOR_CSS_HALF_CORNERS(corner
) {
1321 nsFrame::GetAdditionalStyleContext(int32_t aIndex
) const
1323 NS_PRECONDITION(aIndex
>= 0, "invalid index number");
1328 nsFrame::SetAdditionalStyleContext(int32_t aIndex
,
1329 nsStyleContext
* aStyleContext
)
1331 NS_PRECONDITION(aIndex
>= 0, "invalid index number");
1335 nsFrame::GetLogicalBaseline(WritingMode aWritingMode
) const
1337 NS_ASSERTION(!NS_SUBTREE_DIRTY(this),
1338 "frame must not be dirty");
1339 // Default to the bottom margin edge, per CSS2.1's definition of the
1340 // 'baseline' value of 'vertical-align'.
1341 return BSize(aWritingMode
) +
1342 GetLogicalUsedMargin(aWritingMode
).BEnd(aWritingMode
);
1346 nsFrame::GetChildList(ChildListID aListID
) const
1348 if (IsAbsoluteContainer() &&
1349 aListID
== GetAbsoluteListID()) {
1350 return GetAbsoluteContainingBlock()->GetChildList();
1352 return nsFrameList::EmptyList();
1357 nsFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const
1359 if (IsAbsoluteContainer()) {
1360 nsFrameList absoluteList
= GetAbsoluteContainingBlock()->GetChildList();
1361 absoluteList
.AppendIfNonempty(aLists
, GetAbsoluteListID());
1366 nsIFrame::GetCrossDocChildLists(nsTArray
<ChildList
>* aLists
)
1368 nsSubDocumentFrame
* subdocumentFrame
= do_QueryFrame(this);
1369 if (subdocumentFrame
) {
1370 // Descend into the subdocument
1371 nsIFrame
* root
= subdocumentFrame
->GetSubdocumentRootFrame();
1373 aLists
->AppendElement(nsIFrame::ChildList(
1374 nsFrameList(root
, nsLayoutUtils::GetLastSibling(root
)),
1375 nsIFrame::kPrincipalList
));
1379 GetChildLists(aLists
);
1383 GetActiveSelectionFrame(nsPresContext
* aPresContext
, nsIFrame
* aFrame
)
1385 nsIContent
* capturingContent
= nsIPresShell::GetCapturingContent();
1386 if (capturingContent
) {
1387 nsIFrame
* activeFrame
= aPresContext
->GetPrimaryFrameFor(capturingContent
);
1388 return activeFrame
? activeFrame
: aFrame
;
1395 nsFrame::DisplaySelection(nsPresContext
* aPresContext
, bool isOkToTurnOn
)
1397 int16_t selType
= nsISelectionController::SELECTION_OFF
;
1399 nsCOMPtr
<nsISelectionController
> selCon
;
1400 nsresult result
= GetSelectionController(aPresContext
, getter_AddRefs(selCon
));
1401 if (NS_SUCCEEDED(result
) && selCon
) {
1402 result
= selCon
->GetDisplaySelection(&selType
);
1403 if (NS_SUCCEEDED(result
) && (selType
!= nsISelectionController::SELECTION_OFF
)) {
1404 // Check whether style allows selection.
1406 IsSelectable(&selectable
, nullptr);
1408 selType
= nsISelectionController::SELECTION_OFF
;
1409 isOkToTurnOn
= false;
1412 if (isOkToTurnOn
&& (selType
== nsISelectionController::SELECTION_OFF
)) {
1413 selCon
->SetDisplaySelection(nsISelectionController::SELECTION_ON
);
1414 selType
= nsISelectionController::SELECTION_ON
;
1420 class nsDisplaySelectionOverlay
: public nsDisplayItem
{
1422 nsDisplaySelectionOverlay(nsDisplayListBuilder
* aBuilder
,
1423 nsFrame
* aFrame
, int16_t aSelectionValue
)
1424 : nsDisplayItem(aBuilder
, aFrame
), mSelectionValue(aSelectionValue
) {
1425 MOZ_COUNT_CTOR(nsDisplaySelectionOverlay
);
1427 #ifdef NS_BUILD_REFCNT_LOGGING
1428 virtual ~nsDisplaySelectionOverlay() {
1429 MOZ_COUNT_DTOR(nsDisplaySelectionOverlay
);
1433 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
1434 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
1435 NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY
)
1437 int16_t mSelectionValue
;
1440 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder
* aBuilder
,
1441 nsRenderingContext
* aCtx
)
1443 LookAndFeel::ColorID colorID
;
1444 if (mSelectionValue
== nsISelectionController::SELECTION_ON
) {
1445 colorID
= LookAndFeel::eColorID_TextSelectBackground
;
1446 } else if (mSelectionValue
== nsISelectionController::SELECTION_ATTENTION
) {
1447 colorID
= LookAndFeel::eColorID_TextSelectBackgroundAttention
;
1449 colorID
= LookAndFeel::eColorID_TextSelectBackgroundDisabled
;
1452 nscolor color
= LookAndFeel::GetColor(colorID
, NS_RGB(255, 255, 255));
1457 gfxContext
*ctx
= aCtx
->ThebesContext();
1461 mVisibleRect
.ToOutsidePixels(mFrame
->PresContext()->AppUnitsPerDevPixel());
1463 ctx
->Rectangle(gfxRect(pxRect
.x
, pxRect
.y
, pxRect
.width
, pxRect
.height
), true);
1467 /********************************************************
1468 * Refreshes each content's frame
1469 *********************************************************/
1472 nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder
* aBuilder
,
1473 nsDisplayList
* aList
,
1474 uint16_t aContentType
)
1476 if (!IsSelected() || !IsVisibleForPainting(aBuilder
))
1479 nsPresContext
* presContext
= PresContext();
1480 nsIPresShell
*shell
= presContext
->PresShell();
1484 int16_t displaySelection
= shell
->GetSelectionFlags();
1485 if (!(displaySelection
& aContentType
))
1488 const nsFrameSelection
* frameSelection
= GetConstFrameSelection();
1489 int16_t selectionValue
= frameSelection
->GetDisplaySelection();
1491 if (selectionValue
<= nsISelectionController::SELECTION_HIDDEN
)
1492 return; // selection is hidden or off
1494 nsIContent
*newContent
= mContent
->GetParent();
1496 //check to see if we are anonymous content
1499 // XXXbz there has GOT to be a better way of determining this!
1500 offset
= newContent
->IndexOf(mContent
);
1503 SelectionDetails
*details
;
1504 //look up to see what selection(s) are on this frame
1505 details
= frameSelection
->LookUpSelection(newContent
, offset
, 1, false);
1509 bool normal
= false;
1511 if (details
->mType
== nsISelectionController::SELECTION_NORMAL
) {
1514 SelectionDetails
*next
= details
->mNext
;
1519 if (!normal
&& aContentType
== nsISelectionDisplay::DISPLAY_IMAGES
) {
1520 // Don't overlay an image if it's not in the primary selection.
1524 aList
->AppendNewToTop(new (aBuilder
)
1525 nsDisplaySelectionOverlay(aBuilder
, this, selectionValue
));
1529 nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder
* aBuilder
,
1530 const nsDisplayListSet
& aLists
)
1532 if (StyleOutline()->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE
)
1535 aLists
.Outlines()->AppendNewToTop(
1536 new (aBuilder
) nsDisplayOutline(aBuilder
, this));
1540 nsFrame::DisplayOutline(nsDisplayListBuilder
* aBuilder
,
1541 const nsDisplayListSet
& aLists
)
1543 if (!IsVisibleForPainting(aBuilder
))
1546 DisplayOutlineUnconditional(aBuilder
, aLists
);
1550 nsIFrame::DisplayCaret(nsDisplayListBuilder
* aBuilder
,
1551 const nsRect
& aDirtyRect
, nsDisplayList
* aList
)
1553 if (!IsVisibleForPainting(aBuilder
))
1556 aList
->AppendNewToTop(new (aBuilder
) nsDisplayCaret(aBuilder
, this));
1560 nsIFrame::GetCaretColorAt(int32_t aOffset
)
1563 return StyleColor()->mColor
;
1567 nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder
* aBuilder
,
1568 const nsDisplayListSet
& aLists
,
1569 bool aForceBackground
)
1571 // Here we don't try to detect background propagation. Frames that might
1572 // receive a propagated background should just set aForceBackground to
1574 if (aBuilder
->IsForEventDelivery() || aForceBackground
||
1575 !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance
) {
1576 return nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1577 aBuilder
, this, aLists
.BorderBackground());
1583 nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder
* aBuilder
,
1584 const nsDisplayListSet
& aLists
,
1585 bool aForceBackground
)
1587 // The visibility check belongs here since child elements have the
1588 // opportunity to override the visibility property and display even if
1589 // their parent is hidden.
1590 if (!IsVisibleForPainting(aBuilder
))
1593 nsCSSShadowArray
* shadows
= StyleBorder()->mBoxShadow
;
1594 if (shadows
&& shadows
->HasShadowWithInset(false)) {
1595 aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
1596 nsDisplayBoxShadowOuter(aBuilder
, this));
1599 bool bgIsThemed
= DisplayBackgroundUnconditional(aBuilder
, aLists
,
1602 if (shadows
&& shadows
->HasShadowWithInset(true)) {
1603 aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
1604 nsDisplayBoxShadowInner(aBuilder
, this));
1607 // If there's a themed background, we should not create a border item.
1608 // It won't be rendered.
1609 if (!bgIsThemed
&& StyleBorder()->HasBorder()) {
1610 aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
1611 nsDisplayBorder(aBuilder
, this));
1614 DisplayOutlineUnconditional(aBuilder
, aLists
);
1617 inline static bool IsSVGContentWithCSSClip(const nsIFrame
*aFrame
)
1619 // The CSS spec says that the 'clip' property only applies to absolutely
1620 // positioned elements, whereas the SVG spec says that it applies to SVG
1621 // elements regardless of the value of the 'position' property. Here we obey
1622 // the CSS spec for outer-<svg> (since that's what we generally do), but
1623 // obey the SVG spec for other SVG elements to which 'clip' applies.
1624 nsIAtom
*tag
= aFrame
->GetContent()->Tag();
1625 return (aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
) &&
1626 (tag
== nsGkAtoms::svg
|| tag
== nsGkAtoms::foreignObject
);
1630 nsIFrame::GetClipPropClipRect(const nsStyleDisplay
* aDisp
, nsRect
* aRect
,
1631 const nsSize
& aSize
) const
1633 NS_PRECONDITION(aRect
, "Must have aRect out parameter");
1635 if (!(aDisp
->mClipFlags
& NS_STYLE_CLIP_RECT
) ||
1636 !(aDisp
->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
1640 *aRect
= aDisp
->mClip
;
1641 if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak
==
1642 NS_STYLE_BOX_DECORATION_BREAK_SLICE
)) {
1643 // The clip applies to the joined boxes so it's relative the first
1646 for (nsIFrame
* f
= GetPrevContinuation(); f
; f
= f
->GetPrevContinuation()) {
1647 y
+= f
->GetRect().height
;
1649 aRect
->MoveBy(nsPoint(0, -y
));
1652 if (NS_STYLE_CLIP_RIGHT_AUTO
& aDisp
->mClipFlags
) {
1653 aRect
->width
= aSize
.width
- aRect
->x
;
1655 if (NS_STYLE_CLIP_BOTTOM_AUTO
& aDisp
->mClipFlags
) {
1656 aRect
->height
= aSize
.height
- aRect
->y
;
1662 * If the CSS 'clip' property applies to this frame, set it up
1663 * in aBuilder->ClipState() to clip all content descendants. Returns true
1664 * if the property applies, and if so also returns the clip rect (relative
1665 * to aFrame) in *aRect.
1668 ApplyClipPropClipping(nsDisplayListBuilder
* aBuilder
,
1669 const nsIFrame
* aFrame
,
1670 const nsStyleDisplay
* aDisp
,
1672 DisplayListClipState::AutoSaveRestore
& aClipState
)
1674 if (!aFrame
->GetClipPropClipRect(aDisp
, aRect
, aFrame
->GetSize()))
1677 nsRect clipRect
= *aRect
+ aBuilder
->ToReferenceFrame(aFrame
);
1678 aClipState
.ClipContentDescendants(clipRect
);
1683 * If the CSS 'overflow' property applies to this frame, and is not
1684 * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
1685 * for that overflow in aBuilder->ClipState() to clip all containing-block
1689 ApplyOverflowClipping(nsDisplayListBuilder
* aBuilder
,
1690 const nsIFrame
* aFrame
,
1691 const nsStyleDisplay
* aDisp
,
1692 DisplayListClipState::AutoClipMultiple
& aClipState
)
1694 // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table
1695 // frames, and any non-visible value for blocks in a paginated context).
1696 // We allow -moz-hidden-unscrollable to apply to any kind of frame. This
1697 // is required by comboboxes which make their display text (an inline frame)
1699 if (!nsFrame::ShouldApplyOverflowClipping(aFrame
, aDisp
)) {
1703 bool haveRadii
= false;
1705 if (aFrame
->StyleDisplay()->mOverflowClipBox
==
1706 NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX
) {
1707 clipRect
= aFrame
->GetPaddingRectRelativeToSelf() +
1708 aBuilder
->ToReferenceFrame(aFrame
);
1709 haveRadii
= aFrame
->GetPaddingBoxBorderRadii(radii
);
1711 clipRect
= aFrame
->GetContentRectRelativeToSelf() +
1712 aBuilder
->ToReferenceFrame(aFrame
);
1713 // XXX border-radius
1715 aClipState
.ClipContainingBlockDescendantsExtra(clipRect
, haveRadii
? radii
: nullptr);
1719 static void PaintDebugBorder(nsIFrame
* aFrame
, nsRenderingContext
* aCtx
,
1720 const nsRect
& aDirtyRect
, nsPoint aPt
) {
1721 nsRect
r(aPt
, aFrame
->GetSize());
1722 if (aFrame
->HasView()) {
1723 aCtx
->SetColor(NS_RGB(0,0,255));
1725 aCtx
->SetColor(NS_RGB(255,0,0));
1730 static void PaintEventTargetBorder(nsIFrame
* aFrame
, nsRenderingContext
* aCtx
,
1731 const nsRect
& aDirtyRect
, nsPoint aPt
) {
1732 nsRect
r(aPt
, aFrame
->GetSize());
1733 aCtx
->SetColor(NS_RGB(128,0,128));
1738 DisplayDebugBorders(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
1739 const nsDisplayListSet
& aLists
) {
1740 // Draw a border around the child
1741 // REVIEW: From nsContainerFrame::PaintChild
1742 if (nsFrame::GetShowFrameBorders() && !aFrame
->GetRect().IsEmpty()) {
1743 aLists
.Outlines()->AppendNewToTop(new (aBuilder
)
1744 nsDisplayGeneric(aBuilder
, aFrame
, PaintDebugBorder
, "DebugBorder",
1745 nsDisplayItem::TYPE_DEBUG_BORDER
));
1747 // Draw a border around the current event target
1748 if (nsFrame::GetShowEventTargetFrameBorder() &&
1749 aFrame
->PresContext()->PresShell()->GetDrawEventTargetFrame() == aFrame
) {
1750 aLists
.Outlines()->AppendNewToTop(new (aBuilder
)
1751 nsDisplayGeneric(aBuilder
, aFrame
, PaintEventTargetBorder
, "EventTargetBorder",
1752 nsDisplayItem::TYPE_EVENT_TARGET_BORDER
));
1758 WrapPreserve3DListInternal(nsIFrame
* aFrame
, nsDisplayListBuilder
*aBuilder
,
1759 nsDisplayList
*aList
, nsDisplayList
*aOutput
,
1760 uint32_t& aIndex
, nsDisplayList
* aTemp
)
1762 if (aIndex
> nsDisplayTransform::INDEX_MAX
) {
1766 nsresult rv
= NS_OK
;
1767 while (nsDisplayItem
*item
= aList
->RemoveBottom()) {
1768 nsIFrame
*childFrame
= item
->Frame();
1770 // We accumulate sequential items that aren't transforms into the 'temp' list
1771 // and then flush this list into aOutput by wrapping the whole lot with a single
1772 // nsDisplayTransform.
1774 if (childFrame
->GetParent() &&
1775 (childFrame
->GetParent()->Preserves3DChildren() || childFrame
== aFrame
)) {
1776 switch (item
->GetType()) {
1777 case nsDisplayItem::TYPE_TRANSFORM
: {
1778 if (!aTemp
->IsEmpty()) {
1779 aOutput
->AppendToTop(new (aBuilder
) nsDisplayTransform(aBuilder
,
1780 aFrame
, aTemp
, aTemp
->GetVisibleRect(), aIndex
++));
1782 // Override item's clipping with our current clip state (if any). Since we're
1783 // bubbling up a preserve-3d transformed child to a preserve-3d parent,
1784 // we can be sure the child doesn't have clip state of its own.
1785 NS_ASSERTION(!item
->GetClip().HasClip(), "Unexpected clip on item");
1786 const DisplayItemClip
* clip
= aBuilder
->ClipState().GetCurrentCombinedClip(aBuilder
);
1788 item
->SetClip(aBuilder
, *clip
);
1790 aOutput
->AppendToTop(item
);
1793 case nsDisplayItem::TYPE_WRAP_LIST
: {
1794 nsDisplayWrapList
*list
= static_cast<nsDisplayWrapList
*>(item
);
1795 rv
= WrapPreserve3DListInternal(aFrame
, aBuilder
,
1796 list
->GetChildren(), aOutput
, aIndex
, aTemp
);
1797 list
->~nsDisplayWrapList();
1800 case nsDisplayItem::TYPE_OPACITY
: {
1801 if (!aTemp
->IsEmpty()) {
1802 aOutput
->AppendToTop(new (aBuilder
) nsDisplayTransform(aBuilder
,
1803 aFrame
, aTemp
, aTemp
->GetVisibleRect(), aIndex
++));
1805 nsDisplayOpacity
*opacity
= static_cast<nsDisplayOpacity
*>(item
);
1806 nsDisplayList output
;
1807 // Call GetChildren, not GetSameCoordinateSystemChildren, because
1808 // the preserve-3d children of 'opacity' are temporarily not in the
1809 // same coordinate system as the opacity --- until this wrapping is done.
1810 rv
= WrapPreserve3DListInternal(aFrame
, aBuilder
,
1811 opacity
->GetChildren(), &output
, aIndex
, aTemp
);
1812 if (!aTemp
->IsEmpty()) {
1813 output
.AppendToTop(new (aBuilder
) nsDisplayTransform(aBuilder
,
1814 aFrame
, aTemp
, aTemp
->GetVisibleRect(), aIndex
++));
1816 opacity
->GetChildren()->AppendToTop(&output
);
1817 opacity
->UpdateBounds(aBuilder
);
1818 aOutput
->AppendToTop(item
);
1822 if (childFrame
->StyleDisplay()->BackfaceIsHidden()) {
1823 if (!aTemp
->IsEmpty()) {
1824 aOutput
->AppendToTop(new (aBuilder
) nsDisplayTransform(aBuilder
,
1825 aFrame
, aTemp
, aTemp
->GetVisibleRect(), aIndex
++));
1828 aOutput
->AppendToTop(new (aBuilder
) nsDisplayTransform(aBuilder
,
1829 childFrame
, item
, item
->GetVisibleRect(), aIndex
++));
1831 aTemp
->AppendToTop(item
);
1837 aTemp
->AppendToTop(item
);
1840 if (NS_FAILED(rv
) || !item
|| aIndex
> nsDisplayTransform::INDEX_MAX
)
1848 IsScrollFrameActive(nsIScrollableFrame
* aScrollableFrame
)
1850 return aScrollableFrame
&& aScrollableFrame
->IsScrollingActive();
1854 WrapPreserve3DList(nsIFrame
* aFrame
, nsDisplayListBuilder
* aBuilder
,
1855 nsDisplayList
*aList
)
1859 nsDisplayList output
;
1860 nsresult rv
= WrapPreserve3DListInternal(aFrame
, aBuilder
, aList
, &output
,
1863 if (!temp
.IsEmpty()) {
1864 output
.AppendToTop(new (aBuilder
) nsDisplayTransform(aBuilder
, aFrame
,
1865 &temp
, temp
.GetVisibleRect(), index
++));
1868 aList
->AppendToTop(&output
);
1872 class AutoSaveRestoreBlendMode
1874 nsDisplayListBuilder
& mBuilder
;
1875 EnumSet
<gfx::CompositionOp
> mSavedBlendModes
;
1877 explicit AutoSaveRestoreBlendMode(nsDisplayListBuilder
& aBuilder
)
1878 : mBuilder(aBuilder
)
1879 , mSavedBlendModes(aBuilder
.ContainedBlendModes())
1882 ~AutoSaveRestoreBlendMode() {
1883 mBuilder
.SetContainsBlendModes(mSavedBlendModes
);
1888 CheckForTouchEventHandler(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
1890 nsIContent
* content
= aFrame
->GetContent();
1894 EventListenerManager
* elm
= nsContentUtils::GetExistingListenerManagerForNode(content
);
1898 if (elm
->HasListenersFor(nsGkAtoms::ontouchstart
) ||
1899 elm
->HasListenersFor(nsGkAtoms::ontouchmove
)) {
1900 aBuilder
->SetAncestorHasTouchEventHandler(true);
1905 nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder
* aBuilder
,
1906 const nsRect
& aDirtyRect
,
1907 nsDisplayList
* aList
) {
1908 if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE
)
1911 // Replaced elements have their visibility handled here, because
1912 // they're visually atomic
1913 if (IsFrameOfType(eReplaced
) && !IsVisibleForPainting(aBuilder
))
1916 const nsStyleDisplay
* disp
= StyleDisplay();
1917 // We can stop right away if this is a zero-opacity stacking context and
1918 // we're painting, and we're not animating opacity. Don't do this
1919 // if we're going to compute plugin geometry, since opacity-0 plugins
1920 // need to have display items built for them.
1921 if (disp
->mOpacity
== 0.0 && aBuilder
->IsForPainting() &&
1922 !aBuilder
->WillComputePluginGeometry() &&
1923 !(disp
->mWillChangeBitField
& NS_STYLE_WILL_CHANGE_OPACITY
) &&
1924 !nsLayoutUtils::HasAnimations(mContent
, eCSSProperty_opacity
)) {
1928 nsRect dirtyRect
= aDirtyRect
;
1930 bool inTransform
= aBuilder
->IsInTransform();
1931 bool isTransformed
= IsTransformed();
1932 // reset blend mode so we can keep track if this stacking context needs have
1933 // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
1934 // so we keep track if the parent stacking context needs a container too.
1935 AutoSaveRestoreBlendMode
autoRestoreBlendMode(*aBuilder
);
1936 aBuilder
->SetContainsBlendModes(BlendModeSet());
1938 nsRect dirtyRectOutsideTransform
= dirtyRect
;
1939 if (isTransformed
) {
1940 const nsRect overflow
= GetVisualOverflowRectRelativeToSelf();
1941 if (aBuilder
->IsForPainting() &&
1942 nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder
, this)) {
1943 dirtyRect
= overflow
;
1945 if (overflow
.IsEmpty() && !Preserves3DChildren()) {
1949 nsRect untransformedDirtyRect
;
1950 if (nsDisplayTransform::UntransformRect(dirtyRect
, overflow
, this,
1951 nsPoint(0,0), &untransformedDirtyRect
)) {
1952 dirtyRect
= untransformedDirtyRect
;
1954 NS_WARNING("Unable to untransform dirty rect!");
1955 // This should only happen if the transform is singular, in which case nothing is visible anyway
1956 dirtyRect
.SetEmpty();
1962 bool usingSVGEffects
= nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1963 nsRect dirtyRectOutsideSVGEffects
= dirtyRect
;
1964 if (usingSVGEffects
) {
1966 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect
);
1969 bool useOpacity
= HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
1970 bool useBlendMode
= disp
->mMixBlendMode
!= NS_STYLE_BLEND_NORMAL
;
1971 bool useStickyPosition
= disp
->mPosition
== NS_STYLE_POSITION_STICKY
&&
1972 IsScrollFrameActive(nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
1973 nsLayoutUtils::SCROLLABLE_SAME_DOC
|
1974 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN
));
1976 nsDisplayListBuilder::AutoBuildingDisplayList
1977 buildingDisplayList(aBuilder
, this, dirtyRect
, true);
1978 DisplayListClipState::AutoSaveRestore
clipState(aBuilder
);
1980 if (isTransformed
|| useOpacity
|| useBlendMode
|| usingSVGEffects
|| useStickyPosition
) {
1981 // We don't need to pass ancestor clipping down to our children;
1982 // everything goes inside a display item's child list, and the display
1983 // item itself will be clipped.
1984 // For transforms we also need to clear ancestor clipping because it's
1985 // relative to the wrong display item reference frame anyway.
1989 nsDisplayListCollection set
;
1991 DisplayListClipState::AutoSaveRestore
nestedClipState(aBuilder
);
1992 nsDisplayListBuilder::AutoInTransformSetter
1993 inTransformSetter(aBuilder
, inTransform
);
1994 CheckForTouchEventHandler(aBuilder
, this);
1996 nsRect clipPropClip
;
1997 if (ApplyClipPropClipping(aBuilder
, this, disp
, &clipPropClip
,
1999 dirtyRect
.IntersectRect(dirtyRect
, clipPropClip
);
2002 MarkAbsoluteFramesForDisplayList(aBuilder
, dirtyRect
);
2004 // Preserve3DChildren() also guarantees that applyAbsPosClipping and usingSVGEffects are false
2005 // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy
2006 if (Preserves3DChildren()) {
2007 aBuilder
->MarkPreserve3DFramesForDisplayList(this, aDirtyRect
);
2010 if (aBuilder
->IsBuildingLayerEventRegions()) {
2011 nsDisplayLayerEventRegions
* eventRegions
=
2012 new (aBuilder
) nsDisplayLayerEventRegions(aBuilder
, this);
2013 aBuilder
->SetLayerEventRegions(eventRegions
);
2014 set
.BorderBackground()->AppendNewToTop(eventRegions
);
2016 BuildDisplayList(aBuilder
, dirtyRect
, set
);
2019 if (aBuilder
->IsBackgroundOnly()) {
2020 set
.BlockBorderBackgrounds()->DeleteAll();
2021 set
.Floats()->DeleteAll();
2022 set
.Content()->DeleteAll();
2023 set
.PositionedDescendants()->DeleteAll();
2024 set
.Outlines()->DeleteAll();
2027 // This z-order sort also sorts secondarily by content order. We need to do
2028 // this so that boxes produced by the same element are placed together
2029 // in the sort. Consider a position:relative inline element that breaks
2030 // across lines and has absolutely positioned children; all the abs-pos
2031 // children should be z-ordered after all the boxes for the position:relative
2033 set
.PositionedDescendants()->SortByZOrder(aBuilder
, GetContent());
2035 nsDisplayList resultList
;
2036 // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
2037 // 1,2: backgrounds and borders
2038 resultList
.AppendToTop(set
.BorderBackground());
2039 // 3: negative z-index children.
2041 nsDisplayItem
* item
= set
.PositionedDescendants()->GetBottom();
2042 if (item
&& item
->ZIndex() < 0) {
2043 set
.PositionedDescendants()->RemoveBottom();
2044 resultList
.AppendToTop(item
);
2049 // 4: block backgrounds
2050 resultList
.AppendToTop(set
.BlockBorderBackgrounds());
2052 resultList
.AppendToTop(set
.Floats());
2053 // 7: general content
2054 resultList
.AppendToTop(set
.Content());
2055 // 7.5: outlines, in content tree order. We need to sort by content order
2056 // because an element with outline that breaks and has children with outline
2057 // might have placed child outline items between its own outline items.
2058 // The element's outline items need to all come before any child outline
2060 nsIContent
* content
= GetContent();
2062 content
= PresContext()->Document()->GetRootElement();
2065 set
.Outlines()->SortByContentOrder(aBuilder
, content
);
2068 DisplayDebugBorders(aBuilder
, this, set
);
2070 resultList
.AppendToTop(set
.Outlines());
2071 // 8, 9: non-negative z-index children
2072 resultList
.AppendToTop(set
.PositionedDescendants());
2074 if (!isTransformed
) {
2075 // Restore saved clip state now so that any display items we create below
2076 // are clipped properly.
2077 clipState
.Restore();
2080 /* If there are any SVG effects, wrap the list up in an SVG effects item
2081 * (which also handles CSS group opacity). Note that we create an SVG effects
2082 * item even if resultList is empty, since a filter can produce graphical
2083 * output even if the element being filtered wouldn't otherwise do so.
2085 if (usingSVGEffects
) {
2086 // Revert to the post-filter dirty rect.
2087 buildingDisplayList
.SetDirtyRect(dirtyRectOutsideSVGEffects
);
2088 /* List now emptied, so add the new list to the top. */
2089 resultList
.AppendNewToTop(
2090 new (aBuilder
) nsDisplaySVGEffects(aBuilder
, this, &resultList
));
2092 /* Else, if the list is non-empty and there is CSS group opacity without SVG
2093 * effects, wrap it up in an opacity item.
2095 else if (useOpacity
&& !resultList
.IsEmpty()) {
2096 resultList
.AppendNewToTop(
2097 new (aBuilder
) nsDisplayOpacity(aBuilder
, this, &resultList
));
2099 /* If we have sticky positioning, wrap it in a sticky position item.
2101 if (useStickyPosition
) {
2102 resultList
.AppendNewToTop(
2103 new (aBuilder
) nsDisplayStickyPosition(aBuilder
, this, &resultList
));
2106 /* If we're going to apply a transformation and don't have preserve-3d set, wrap
2107 * everything in an nsDisplayTransform. If there's nothing in the list, don't add
2110 * For the preserve-3d case we want to individually wrap every child in the list with
2111 * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform,
2112 * we can skip this step, as the computed transform will already include our own.
2114 * We also traverse into sublists created by nsDisplayWrapList or nsDisplayOpacity, so that
2115 * we find all the correct children.
2117 if (isTransformed
&& !resultList
.IsEmpty()) {
2118 // Restore clip state now so nsDisplayTransform is clipped properly.
2119 clipState
.Restore();
2120 // Revert to the dirtyrect coming in from the parent, without our transform
2121 // taken into account.
2122 buildingDisplayList
.SetDirtyRect(dirtyRectOutsideTransform
);
2123 // Revert to the outer reference frame and offset because all display
2124 // items we create from now on are outside the transform.
2125 const nsIFrame
* outerReferenceFrame
=
2126 aBuilder
->FindReferenceFrameFor(nsLayoutUtils::GetTransformRootFrame(this));
2127 buildingDisplayList
.SetReferenceFrameAndCurrentOffset(outerReferenceFrame
,
2128 GetOffsetToCrossDoc(outerReferenceFrame
));
2130 if (Preserves3DChildren()) {
2131 WrapPreserve3DList(this, aBuilder
, &resultList
);
2133 resultList
.AppendNewToTop(
2134 new (aBuilder
) nsDisplayTransform(aBuilder
, this, &resultList
, dirtyRect
));
2138 /* If adding both a nsDisplayBlendContainer and a nsDisplayMixBlendMode to the
2139 * same list, the nsDisplayBlendContainer should be added first. This only
2140 * happens when the element creating this stacking context has mix-blend-mode
2141 * and also contains a child which has mix-blend-mode.
2142 * The nsDisplayBlendContainer must be added to the list first, so it does not
2143 * isolate the containing element blending as well.
2146 if (aBuilder
->ContainsBlendMode()) {
2147 resultList
.AppendNewToTop(
2148 new (aBuilder
) nsDisplayBlendContainer(aBuilder
, this, &resultList
, aBuilder
->ContainedBlendModes()));
2151 /* If there's blending, wrap up the list in a blend-mode item. Note
2152 * that opacity can be applied before blending as the blend color is
2153 * not affected by foreground opacity (only background alpha).
2156 if (useBlendMode
&& !resultList
.IsEmpty()) {
2157 resultList
.AppendNewToTop(
2158 new (aBuilder
) nsDisplayMixBlendMode(aBuilder
, this, &resultList
));
2161 CreateOwnLayerIfNeeded(aBuilder
, &resultList
);
2163 aList
->AppendToTop(&resultList
);
2166 static nsDisplayItem
*
2167 WrapInWrapList(nsDisplayListBuilder
* aBuilder
,
2168 nsIFrame
* aFrame
, nsDisplayList
* aList
)
2170 nsDisplayItem
* item
= aList
->GetBottom();
2171 if (!item
|| item
->GetAbove() || item
->Frame() != aFrame
) {
2172 return new (aBuilder
) nsDisplayWrapList(aBuilder
, aFrame
, aList
);
2174 aList
->RemoveBottom();
2179 nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder
* aBuilder
,
2181 const nsRect
& aDirtyRect
,
2182 const nsDisplayListSet
& aLists
,
2184 // If painting is restricted to just the background of the top level frame,
2185 // then we have nothing to do here.
2186 if (aBuilder
->IsBackgroundOnly())
2189 nsIFrame
* child
= aChild
;
2190 if (child
->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE
)
2193 bool isSVG
= (child
->GetStateBits() & NS_FRAME_SVG_LAYOUT
);
2195 // true if this is a real or pseudo stacking context
2196 bool pseudoStackingContext
=
2197 (aFlags
& DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT
) != 0;
2199 (aFlags
& DISPLAY_CHILD_INLINE
) &&
2200 !child
->IsFrameOfType(eLineParticipant
)) {
2201 // child is a non-inline frame in an inline context, i.e.,
2202 // it acts like inline-block or inline-table. Therefore it is a
2203 // pseudo-stacking-context.
2204 pseudoStackingContext
= true;
2207 // dirty rect in child-relative coordinates
2208 nsRect dirty
= aDirtyRect
- child
->GetOffsetTo(this);
2210 nsIAtom
* childType
= child
->GetType();
2211 nsDisplayListBuilder::OutOfFlowDisplayData
* savedOutOfFlowData
= nullptr;
2212 if (childType
== nsGkAtoms::placeholderFrame
) {
2213 nsPlaceholderFrame
* placeholder
= static_cast<nsPlaceholderFrame
*>(child
);
2214 child
= placeholder
->GetOutOfFlowFrame();
2215 NS_ASSERTION(child
, "No out of flow frame?");
2216 // If 'child' is a pushed float then it's owned by a block that's not an
2217 // ancestor of the placeholder, and it will be painted by that block and
2218 // should not be painted through the placeholder.
2219 if (!child
|| nsLayoutUtils::IsPopup(child
) ||
2220 (child
->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT
))
2222 // Make sure that any attempt to use childType below is disappointed. We
2223 // could call GetType again but since we don't currently need it, let's
2224 // avoid the virtual call.
2225 childType
= nullptr;
2226 // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE
2227 if (child
->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE
)
2229 savedOutOfFlowData
= static_cast<nsDisplayListBuilder::OutOfFlowDisplayData
*>
2230 (child
->Properties().Get(nsDisplayListBuilder::OutOfFlowDisplayDataProperty()));
2231 if (savedOutOfFlowData
) {
2232 dirty
= savedOutOfFlowData
->mDirtyRect
;
2234 // The out-of-flow frame did not intersect the dirty area. We may still
2235 // need to traverse into it, since it may contain placeholders we need
2236 // to enter to reach other out-of-flow frames that are visible.
2239 pseudoStackingContext
= true;
2241 if (child
->Preserves3D()) {
2242 nsRect
* savedDirty
= static_cast<nsRect
*>
2243 (child
->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty()));
2245 dirty
= *savedDirty
;
2251 NS_ASSERTION(childType
!= nsGkAtoms::placeholderFrame
,
2252 "Should have dealt with placeholders already");
2253 if (aBuilder
->GetSelectedFramesOnly() &&
2255 !aChild
->IsSelected()) {
2259 if (aBuilder
->GetIncludeAllOutOfFlows() &&
2260 (child
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)) {
2261 dirty
= child
->GetVisualOverflowRect();
2262 } else if (!(child
->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
)) {
2263 // No need to descend into child to catch placeholders for visible
2264 // positioned stuff. So see if we can short-circuit frame traversal here.
2266 // We can stop if child's frame subtree's intersection with the
2267 // dirty area is empty.
2268 // If the child is a scrollframe that we want to ignore, then we need
2269 // to descend into it because its scrolled child may intersect the dirty
2270 // area even if the scrollframe itself doesn't.
2271 // There are cases where the "ignore scroll frame" on the builder is not set
2272 // correctly, and so we additionally want to catch cases where the child is
2273 // a root scrollframe and we are ignoring scrolling on the viewport.
2274 nsIPresShell
* shell
= PresContext()->PresShell();
2275 bool keepDescending
= child
== aBuilder
->GetIgnoreScrollFrame() ||
2276 (shell
->IgnoringViewportScrolling() && child
== shell
->GetRootScrollFrame());
2277 if (!keepDescending
) {
2279 if (!childDirty
.IntersectRect(dirty
, child
->GetVisualOverflowRect()))
2281 // Usually we could set dirty to childDirty now but there's no
2282 // benefit, and it can be confusing. It can especially confuse
2283 // situations where we're going to ignore a scrollframe's clipping;
2284 // we wouldn't want to clip the dirty area to the scrollframe's
2285 // bounds in that case.
2289 // XXX need to have inline-block and inline-table set pseudoStackingContext
2291 const nsStyleDisplay
* ourDisp
= StyleDisplay();
2292 // REVIEW: Taken from nsBoxFrame::Paint
2293 // Don't paint our children if the theme object is a leaf.
2294 if (IsThemed(ourDisp
) &&
2295 !PresContext()->GetTheme()->WidgetIsContainer(ourDisp
->mAppearance
))
2298 // Child is composited if it's transformed, partially transparent, or has
2299 // SVG effects or a blend mode..
2300 const nsStyleDisplay
* disp
= child
->StyleDisplay();
2301 const nsStylePosition
* pos
= child
->StylePosition();
2302 bool isVisuallyAtomic
= child
->HasOpacity()
2303 || child
->IsTransformed()
2304 // strictly speaking, 'perspective' doesn't require visual atomicity,
2305 // but the spec says it acts like the rest of these
2306 || disp
->mChildPerspective
.GetUnit() == eStyleUnit_Coord
2307 || disp
->mMixBlendMode
!= NS_STYLE_BLEND_NORMAL
2308 || nsSVGIntegrationUtils::UsingEffectsForFrame(child
);
2310 bool isPositioned
= disp
->IsPositioned(child
);
2311 bool isStackingContext
=
2312 (isPositioned
&& (disp
->mPosition
== NS_STYLE_POSITION_STICKY
||
2313 pos
->mZIndex
.GetUnit() == eStyleUnit_Integer
)) ||
2314 (disp
->mWillChangeBitField
& NS_STYLE_WILL_CHANGE_STACKING_CONTEXT
) ||
2315 isVisuallyAtomic
|| (aFlags
& DISPLAY_CHILD_FORCE_STACKING_CONTEXT
);
2317 if (isVisuallyAtomic
|| isPositioned
|| (!isSVG
&& disp
->IsFloating(child
)) ||
2318 ((disp
->mClipFlags
& NS_STYLE_CLIP_RECT
) &&
2319 IsSVGContentWithCSSClip(child
)) ||
2320 (disp
->mWillChangeBitField
& NS_STYLE_WILL_CHANGE_STACKING_CONTEXT
) ||
2321 (aFlags
& DISPLAY_CHILD_FORCE_STACKING_CONTEXT
)) {
2322 // If you change this, also change IsPseudoStackingContextFromStyle()
2323 pseudoStackingContext
= true;
2325 NS_ASSERTION(!isStackingContext
|| pseudoStackingContext
,
2326 "Stacking contexts must also be pseudo-stacking-contexts");
2328 nsDisplayListBuilder::AutoBuildingDisplayList
2329 buildingForChild(aBuilder
, child
, dirty
, pseudoStackingContext
);
2330 DisplayListClipState::AutoClipMultiple
clipState(aBuilder
);
2331 CheckForTouchEventHandler(aBuilder
, child
);
2333 if (savedOutOfFlowData
) {
2334 clipState
.SetClipForContainingBlockDescendants(
2335 &savedOutOfFlowData
->mContainingBlockClip
);
2338 // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
2339 // or overflow:hidden on elements that don't support scrolling (and therefore
2340 // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
2341 // anything directly rendered by the parent, only the rendering of its
2343 // Don't use overflowClip to restrict the dirty rect, since some of the
2344 // descendants may not be clipped by it. Even if we end up with unnecessary
2345 // display items, they'll be pruned during ComputeVisibility.
2346 nsIFrame
* parent
= child
->GetParent();
2347 const nsStyleDisplay
* parentDisp
=
2348 parent
== this ? ourDisp
: parent
->StyleDisplay();
2349 ApplyOverflowClipping(aBuilder
, parent
, parentDisp
, clipState
);
2352 nsDisplayList extraPositionedDescendants
;
2353 if (isStackingContext
) {
2354 if (disp
->mMixBlendMode
!= NS_STYLE_BLEND_NORMAL
) {
2355 aBuilder
->SetContainsBlendMode(disp
->mMixBlendMode
);
2357 // True stacking context.
2358 // For stacking contexts, BuildDisplayListForStackingContext handles
2359 // clipping and MarkAbsoluteFramesForDisplayList.
2360 child
->BuildDisplayListForStackingContext(aBuilder
, dirty
, &list
);
2361 aBuilder
->DisplayCaret(child
, dirty
, &list
);
2364 if (ApplyClipPropClipping(aBuilder
, child
, disp
, &clipRect
, clipState
)) {
2365 // clipRect is in builder-reference-frame coordinates,
2366 // dirty/clippedDirtyRect are in child coordinates
2367 dirty
.IntersectRect(dirty
, clipRect
);
2370 child
->MarkAbsoluteFramesForDisplayList(aBuilder
, dirty
);
2372 if (!pseudoStackingContext
) {
2373 // THIS IS THE COMMON CASE.
2374 // Not a pseudo or real stacking context. Do the simple thing and
2376 nsDisplayLayerEventRegions
* eventRegions
= aBuilder
->GetLayerEventRegions();
2378 eventRegions
->AddFrame(aBuilder
, child
);
2380 child
->BuildDisplayList(aBuilder
, dirty
, aLists
);
2381 aBuilder
->DisplayCaret(child
, dirty
, aLists
.Content());
2383 DisplayDebugBorders(aBuilder
, child
, aLists
);
2388 // A pseudo-stacking context (e.g., a positioned element with z-index auto).
2389 // We allow positioned descendants of the child to escape to our parent
2390 // stacking context's positioned descendant list, because they might be
2392 nsDisplayListCollection pseudoStack
;
2393 if (aBuilder
->IsBuildingLayerEventRegions()) {
2394 nsDisplayLayerEventRegions
* eventRegions
=
2395 new (aBuilder
) nsDisplayLayerEventRegions(aBuilder
, this);
2396 aBuilder
->SetLayerEventRegions(eventRegions
);
2397 pseudoStack
.BorderBackground()->AppendNewToTop(eventRegions
);
2399 child
->BuildDisplayList(aBuilder
, dirty
, pseudoStack
);
2400 aBuilder
->DisplayCaret(child
, dirty
, pseudoStack
.Content());
2402 list
.AppendToTop(pseudoStack
.BorderBackground());
2403 list
.AppendToTop(pseudoStack
.BlockBorderBackgrounds());
2404 list
.AppendToTop(pseudoStack
.Floats());
2405 list
.AppendToTop(pseudoStack
.Content());
2406 list
.AppendToTop(pseudoStack
.Outlines());
2407 extraPositionedDescendants
.AppendToTop(pseudoStack
.PositionedDescendants());
2409 DisplayDebugBorders(aBuilder
, child
, aLists
);
2413 // Clear clip rect for the construction of the items below. Since we're
2414 // clipping all their contents, they themselves don't need to be clipped.
2417 if (isPositioned
|| isVisuallyAtomic
||
2418 (aFlags
& DISPLAY_CHILD_FORCE_STACKING_CONTEXT
)) {
2419 // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
2420 // go in this level.
2421 if (!list
.IsEmpty()) {
2422 nsDisplayItem
* item
= WrapInWrapList(aBuilder
, child
, &list
);
2424 aLists
.Content()->AppendNewToTop(item
);
2426 aLists
.PositionedDescendants()->AppendNewToTop(item
);
2429 } else if (!isSVG
&& disp
->IsFloating(child
)) {
2430 if (!list
.IsEmpty()) {
2431 aLists
.Floats()->AppendNewToTop(WrapInWrapList(aBuilder
, child
, &list
));
2434 aLists
.Content()->AppendToTop(&list
);
2436 // We delay placing the positioned descendants of positioned frames to here,
2437 // because in the absence of z-index this is the correct order for them.
2438 // This doesn't affect correctness because the positioned descendants list
2439 // is sorted by z-order and content in BuildDisplayListForStackingContext,
2440 // but it means that sort routine needs to do less work.
2441 aLists
.PositionedDescendants()->AppendToTop(&extraPositionedDescendants
);
2445 nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder
* aBuilder
,
2446 const nsRect
& aDirtyRect
)
2448 if (IsAbsoluteContainer()) {
2449 aBuilder
->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList(), aDirtyRect
);
2454 nsFrame::GetContentForEvent(WidgetEvent
* aEvent
,
2455 nsIContent
** aContent
)
2457 nsIFrame
* f
= nsLayoutUtils::GetNonGeneratedAncestor(this);
2458 *aContent
= f
->GetContent();
2459 NS_IF_ADDREF(*aContent
);
2464 nsFrame::FireDOMEvent(const nsAString
& aDOMEventName
, nsIContent
*aContent
)
2466 nsIContent
* target
= aContent
? aContent
: mContent
;
2469 nsRefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
2470 new AsyncEventDispatcher(target
, aDOMEventName
, true, false);
2471 DebugOnly
<nsresult
> rv
= asyncDispatcher
->PostDOMEvent();
2472 NS_ASSERTION(NS_SUCCEEDED(rv
), "AsyncEventDispatcher failed to dispatch");
2477 nsFrame::HandleEvent(nsPresContext
* aPresContext
,
2478 WidgetGUIEvent
* aEvent
,
2479 nsEventStatus
* aEventStatus
)
2482 if (aEvent
->message
== NS_MOUSE_MOVE
) {
2483 // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
2484 // the implementation becomes simpler.
2485 return HandleDrag(aPresContext
, aEvent
, aEventStatus
);
2488 if ((aEvent
->mClass
== eMouseEventClass
&&
2489 aEvent
->AsMouseEvent()->button
== WidgetMouseEvent::eLeftButton
) ||
2490 aEvent
->mClass
== eTouchEventClass
) {
2491 if (aEvent
->message
== NS_MOUSE_BUTTON_DOWN
|| aEvent
->message
== NS_TOUCH_START
) {
2492 HandlePress(aPresContext
, aEvent
, aEventStatus
);
2493 } else if (aEvent
->message
== NS_MOUSE_BUTTON_UP
|| aEvent
->message
== NS_TOUCH_END
) {
2494 HandleRelease(aPresContext
, aEvent
, aEventStatus
);
2501 nsFrame::GetDataForTableSelection(const nsFrameSelection
* aFrameSelection
,
2502 nsIPresShell
* aPresShell
,
2503 WidgetMouseEvent
* aMouseEvent
,
2504 nsIContent
** aParentContent
,
2505 int32_t* aContentOffset
,
2508 if (!aFrameSelection
|| !aPresShell
|| !aMouseEvent
|| !aParentContent
|| !aContentOffset
|| !aTarget
)
2509 return NS_ERROR_NULL_POINTER
;
2511 *aParentContent
= nullptr;
2512 *aContentOffset
= 0;
2515 int16_t displaySelection
= aPresShell
->GetSelectionFlags();
2517 bool selectingTableCells
= aFrameSelection
->GetTableCellSelection();
2519 // DISPLAY_ALL means we're in an editor.
2520 // If already in cell selection mode,
2521 // continue selecting with mouse drag or end on mouse up,
2522 // or when using shift key to extend block of cells
2523 // (Mouse down does normal selection unless Ctrl/Cmd is pressed)
2524 bool doTableSelection
=
2525 displaySelection
== nsISelectionDisplay::DISPLAY_ALL
&& selectingTableCells
&&
2526 (aMouseEvent
->message
== NS_MOUSE_MOVE
||
2527 (aMouseEvent
->message
== NS_MOUSE_BUTTON_UP
&&
2528 aMouseEvent
->button
== WidgetMouseEvent::eLeftButton
) ||
2529 aMouseEvent
->IsShift());
2531 if (!doTableSelection
)
2533 // In Browser, special 'table selection' key must be pressed for table selection
2534 // or when just Shift is pressed and we're already in table/cell selection mode
2536 doTableSelection
= aMouseEvent
->IsMeta() || (aMouseEvent
->IsShift() && selectingTableCells
);
2538 doTableSelection
= aMouseEvent
->IsControl() || (aMouseEvent
->IsShift() && selectingTableCells
);
2541 if (!doTableSelection
)
2544 // Get the cell frame or table frame (or parent) of the current content node
2545 nsIFrame
*frame
= this;
2546 bool foundCell
= false;
2547 bool foundTable
= false;
2549 // Get the limiting node to stop parent frame search
2550 nsIContent
* limiter
= aFrameSelection
->GetLimiter();
2552 // If our content node is an ancestor of the limiting node,
2553 // we should stop the search right now.
2554 if (limiter
&& nsContentUtils::ContentIsDescendantOf(limiter
, GetContent()))
2557 //We don't initiate row/col selection from here now,
2558 // but we may in future
2559 //bool selectColumn = false;
2560 //bool selectRow = false;
2564 // Check for a table cell by querying to a known CellFrame interface
2565 nsITableCellLayout
*cellElement
= do_QueryFrame(frame
);
2569 //TODO: If we want to use proximity to top or left border
2570 // for row and column selection, this is the place to do it
2575 // If not a cell, check for table
2576 // This will happen when starting frame is the table or child of a table,
2577 // such as a row (we were inbetween cells or in table border)
2578 nsTableOuterFrame
*tableFrame
= do_QueryFrame(frame
);
2582 //TODO: How can we select row when along left table edge
2583 // or select column when along top edge?
2586 frame
= frame
->GetParent();
2587 // Stop if we have hit the selection's limiting content node
2588 if (frame
&& frame
->GetContent() == limiter
)
2593 // We aren't in a cell or table
2594 if (!foundCell
&& !foundTable
) return NS_OK
;
2596 nsIContent
* tableOrCellContent
= frame
->GetContent();
2597 if (!tableOrCellContent
) return NS_ERROR_FAILURE
;
2599 nsCOMPtr
<nsIContent
> parentContent
= tableOrCellContent
->GetParent();
2600 if (!parentContent
) return NS_ERROR_FAILURE
;
2602 int32_t offset
= parentContent
->IndexOf(tableOrCellContent
);
2604 if (offset
< 0) return NS_ERROR_FAILURE
;
2606 // Everything is OK -- set the return values
2607 *aParentContent
= parentContent
;
2608 NS_ADDREF(*aParentContent
);
2610 *aContentOffset
= offset
;
2614 *aTarget
= nsISelectionPrivate::TABLESELECTION_ROW
;
2615 else if (selectColumn
)
2616 *aTarget
= nsISelectionPrivate::TABLESELECTION_COLUMN
;
2620 *aTarget
= nsISelectionPrivate::TABLESELECTION_CELL
;
2621 else if (foundTable
)
2622 *aTarget
= nsISelectionPrivate::TABLESELECTION_TABLE
;
2628 nsFrame::IsSelectable(bool* aSelectable
, uint8_t* aSelectStyle
) const
2630 if (!aSelectable
) //it's ok if aSelectStyle is null
2631 return NS_ERROR_NULL_POINTER
;
2633 // Like 'visibility', we must check all the parents: if a parent
2634 // is not selectable, none of its children is selectable.
2636 // The -moz-all value acts similarly: if a frame has 'user-select:-moz-all',
2637 // all its children are selectable, even those with 'user-select:none'.
2639 // As a result, if 'none' and '-moz-all' are not present in the frame hierarchy,
2640 // aSelectStyle returns the first style that is not AUTO. If these values
2641 // are present in the frame hierarchy, aSelectStyle returns the style of the
2642 // topmost parent that has either 'none' or '-moz-all'.
2644 // For instance, if the frame hierarchy is:
2645 // AUTO -> _MOZ_ALL -> NONE -> TEXT, the returned value is _MOZ_ALL
2646 // TEXT -> NONE -> AUTO -> _MOZ_ALL, the returned value is TEXT
2647 // _MOZ_ALL -> TEXT -> AUTO -> AUTO, the returned value is _MOZ_ALL
2648 // AUTO -> CELL -> TEXT -> AUTO, the returned value is TEXT
2650 uint8_t selectStyle
= NS_STYLE_USER_SELECT_AUTO
;
2651 nsIFrame
* frame
= const_cast<nsFrame
*>(this);
2654 const nsStyleUIReset
* userinterface
= frame
->StyleUIReset();
2655 switch (userinterface
->mUserSelect
) {
2656 case NS_STYLE_USER_SELECT_ALL
:
2657 case NS_STYLE_USER_SELECT_MOZ_ALL
:
2658 // override the previous values
2659 selectStyle
= userinterface
->mUserSelect
;
2662 // otherwise return the first value which is not 'auto'
2663 if (selectStyle
== NS_STYLE_USER_SELECT_AUTO
) {
2664 selectStyle
= userinterface
->mUserSelect
;
2668 frame
= frame
->GetParent();
2671 // convert internal values to standard values
2672 if (selectStyle
== NS_STYLE_USER_SELECT_AUTO
)
2673 selectStyle
= NS_STYLE_USER_SELECT_TEXT
;
2675 if (selectStyle
== NS_STYLE_USER_SELECT_MOZ_ALL
)
2676 selectStyle
= NS_STYLE_USER_SELECT_ALL
;
2680 *aSelectStyle
= selectStyle
;
2681 if (mState
& NS_FRAME_GENERATED_CONTENT
)
2682 *aSelectable
= false;
2684 *aSelectable
= (selectStyle
!= NS_STYLE_USER_SELECT_NONE
);
2689 * Handles the Mouse Press Event for the frame
2692 nsFrame::HandlePress(nsPresContext
* aPresContext
,
2693 WidgetGUIEvent
* aEvent
,
2694 nsEventStatus
* aEventStatus
)
2696 NS_ENSURE_ARG_POINTER(aEventStatus
);
2697 if (nsEventStatus_eConsumeNoDefault
== *aEventStatus
) {
2701 NS_ENSURE_ARG_POINTER(aEvent
);
2702 if (aEvent
->mClass
== eTouchEventClass
) {
2706 //We often get out of sync state issues with mousedown events that
2707 //get interrupted by alerts/dialogs.
2708 //Check with the ESM to see if we should process this one
2709 if (!aPresContext
->EventStateManager()->EventStatusOK(aEvent
))
2713 nsIPresShell
*shell
= aPresContext
->GetPresShell();
2715 return NS_ERROR_FAILURE
;
2717 // if we are in Navigator and the click is in a draggable node, we don't want
2718 // to start selection because we don't want to interfere with a potential
2719 // drag of said node and steal all its glory.
2720 int16_t isEditor
= shell
->GetSelectionFlags();
2721 //weaaak. only the editor can display frame selection not just text and images
2722 isEditor
= isEditor
== nsISelectionDisplay::DISPLAY_ALL
;
2724 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
2726 if (!mouseEvent
->IsAlt()) {
2727 for (nsIContent
* content
= mContent
; content
;
2728 content
= content
->GetParent()) {
2729 if (nsContentUtils::ContentIsDraggable(content
) &&
2730 !content
->IsEditable()) {
2731 // coordinate stuff is the fix for bug #55921
2732 if ((mRect
- GetPosition()).Contains(
2733 nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent
, this))) {
2740 // check whether style allows selection
2741 // if not, don't tell selection the mouse event even occurred.
2743 uint8_t selectStyle
;
2744 rv
= IsSelectable(&selectable
, &selectStyle
);
2745 if (NS_FAILED(rv
)) return rv
;
2747 // check for select: none
2751 // When implementing NS_STYLE_USER_SELECT_ELEMENT, NS_STYLE_USER_SELECT_ELEMENTS and
2752 // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic
2753 bool useFrameSelection
= (selectStyle
== NS_STYLE_USER_SELECT_TEXT
);
2755 // If the mouse is dragged outside the nearest enclosing scrollable area
2756 // while making a selection, the area will be scrolled. To do this, capture
2757 // the mouse on the nearest scrollable frame. If there isn't a scrollable
2758 // frame, or something else is already capturing the mouse, there's no
2759 // reason to capture.
2760 bool hasCapturedContent
= false;
2761 if (!nsIPresShell::GetCapturingContent()) {
2762 nsIScrollableFrame
* scrollFrame
=
2763 nsLayoutUtils::GetNearestScrollableFrame(this,
2764 nsLayoutUtils::SCROLLABLE_SAME_DOC
|
2765 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN
);
2767 nsIFrame
* capturingFrame
= do_QueryFrame(scrollFrame
);
2768 nsIPresShell::SetCapturingContent(capturingFrame
->GetContent(),
2769 CAPTURE_IGNOREALLOWED
);
2770 hasCapturedContent
= true;
2774 // XXX This is screwy; it really should use the selection frame, not the
2776 const nsFrameSelection
* frameselection
= nullptr;
2777 if (useFrameSelection
)
2778 frameselection
= GetConstFrameSelection();
2780 frameselection
= shell
->ConstFrameSelection();
2782 if (!frameselection
|| frameselection
->GetDisplaySelection() == nsISelectionController::SELECTION_OFF
)
2783 return NS_OK
;//nothing to do we cannot affect selection from here
2786 if (mouseEvent
->IsControl())
2787 return NS_OK
;//short circuit. hard coded for mac due to time restraints.
2788 bool control
= mouseEvent
->IsMeta();
2790 bool control
= mouseEvent
->IsControl();
2793 nsRefPtr
<nsFrameSelection
> fc
= const_cast<nsFrameSelection
*>(frameselection
);
2794 if (mouseEvent
->clickCount
> 1) {
2795 // These methods aren't const but can't actually delete anything,
2796 // so no need for nsWeakFrame.
2797 fc
->SetDragState(true);
2798 fc
->SetMouseDoubleDown(true);
2799 return HandleMultiplePress(aPresContext
, mouseEvent
, aEventStatus
, control
);
2802 nsPoint pt
= nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent
, this);
2803 ContentOffsets offsets
= GetContentOffsetsFromPoint(pt
, SKIP_HIDDEN
);
2805 if (!offsets
.content
)
2806 return NS_ERROR_FAILURE
;
2808 // On touchables devices, touch the screen is usually a pan action,
2809 // so let's reposition the caret if needed but do not select text
2810 // if the touch did not happen over an editable element. Otherwise,
2811 // let the user move the caret by tapping and dragging.
2812 if (!offsets
.content
->IsEditable() &&
2813 Preferences::GetBool("browser.ignoreNativeFrameTextSelection", false)) {
2814 // On touchables devices, mouse events are generated if the gesture is a tap.
2815 // Such events are never going to generate a drag action, so let's release
2816 // captured content if any.
2817 if (hasCapturedContent
) {
2818 nsIPresShell::SetCapturingContent(nullptr, 0);
2821 return fc
->HandleClick(offsets
.content
, offsets
.StartOffset(),
2822 offsets
.EndOffset(), false, false,
2826 // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
2827 nsCOMPtr
<nsIContent
>parentContent
;
2828 int32_t contentOffset
;
2830 rv
= GetDataForTableSelection(frameselection
, shell
, mouseEvent
,
2831 getter_AddRefs(parentContent
), &contentOffset
,
2833 if (NS_SUCCEEDED(rv
) && parentContent
)
2835 fc
->SetDragState(true);
2836 return fc
->HandleTableSelection(parentContent
, contentOffset
, target
,
2840 fc
->SetDelayedCaretData(0);
2842 // Check if any part of this frame is selected, and if the
2843 // user clicked inside the selected region. If so, we delay
2844 // starting a new selection since the user may be trying to
2845 // drag the selected region to some other app.
2847 SelectionDetails
*details
= 0;
2848 if (GetContent()->IsSelectionDescendant())
2850 bool inSelection
= false;
2851 details
= frameselection
->LookUpSelection(offsets
.content
, 0,
2852 offsets
.EndOffset(), false);
2855 // If there are any details, check to see if the user clicked
2856 // within any selected region of the frame.
2859 SelectionDetails
*curDetail
= details
;
2864 // If the user clicked inside a selection, then just
2865 // return without doing anything. We will handle placing
2866 // the caret later on when the mouse is released. We ignore
2867 // the spellcheck, find and url formatting selections.
2869 if (curDetail
->mType
!= nsISelectionController::SELECTION_SPELLCHECK
&&
2870 curDetail
->mType
!= nsISelectionController::SELECTION_FIND
&&
2871 curDetail
->mType
!= nsISelectionController::SELECTION_URLSECONDARY
&&
2872 curDetail
->mStart
<= offsets
.StartOffset() &&
2873 offsets
.EndOffset() <= curDetail
->mEnd
)
2878 SelectionDetails
*nextDetail
= curDetail
->mNext
;
2880 curDetail
= nextDetail
;
2884 fc
->SetDragState(false);
2885 fc
->SetDelayedCaretData(mouseEvent
);
2890 fc
->SetDragState(true);
2892 // Do not touch any nsFrame members after this point without adding
2893 // weakFrame checks.
2894 rv
= fc
->HandleClick(offsets
.content
, offsets
.StartOffset(),
2895 offsets
.EndOffset(), mouseEvent
->IsShift(), control
,
2901 if (offsets
.offset
!= offsets
.secondaryOffset
)
2902 fc
->MaintainSelection();
2904 if (isEditor
&& !mouseEvent
->IsShift() &&
2905 (offsets
.EndOffset() - offsets
.StartOffset()) == 1)
2907 // A single node is selected and we aren't extending an existing
2908 // selection, which means the user clicked directly on an object (either
2909 // -moz-user-select: all or a non-text node without children).
2910 // Therefore, disable selection extension during mouse moves.
2911 // XXX This is a bit hacky; shouldn't editor be able to deal with this?
2912 fc
->SetDragState(false);
2919 * SelectByTypeAtPoint
2921 * Search for selectable content at point and attempt to select
2922 * based on the start and end selection behaviours.
2924 * @param aPresContext Presentation context
2925 * @param aPoint Point at which selection will occur. Coordinates
2926 * should be relaitve to this frame.
2927 * @param aBeginAmountType, aEndAmountType Selection behavior, see
2928 * nsIFrame for definitions.
2929 * @param aSelectFlags Selection flags defined in nsFame.h.
2930 * @return success or failure at finding suitable content to select.
2933 nsFrame::SelectByTypeAtPoint(nsPresContext
* aPresContext
,
2934 const nsPoint
& aPoint
,
2935 nsSelectionAmount aBeginAmountType
,
2936 nsSelectionAmount aEndAmountType
,
2937 uint32_t aSelectFlags
)
2939 NS_ENSURE_ARG_POINTER(aPresContext
);
2941 // No point in selecting if selection is turned off
2942 if (DisplaySelection(aPresContext
) == nsISelectionController::SELECTION_OFF
)
2945 ContentOffsets offsets
= GetContentOffsetsFromPoint(aPoint
, SKIP_HIDDEN
);
2946 if (!offsets
.content
)
2947 return NS_ERROR_FAILURE
;
2950 const nsFrameSelection
* frameSelection
=
2951 PresContext()->GetPresShell()->ConstFrameSelection();
2952 nsIFrame
* theFrame
= frameSelection
->
2953 GetFrameForNodeOffset(offsets
.content
, offsets
.offset
,
2954 offsets
.associate
, &offset
);
2956 return NS_ERROR_FAILURE
;
2958 nsFrame
* frame
= static_cast<nsFrame
*>(theFrame
);
2959 return frame
->PeekBackwardAndForward(aBeginAmountType
, aEndAmountType
,
2960 offset
, aPresContext
,
2961 aBeginAmountType
!= eSelectWord
,
2966 * Multiple Mouse Press -- line or paragraph selection -- for the frame.
2967 * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
2970 nsFrame::HandleMultiplePress(nsPresContext
* aPresContext
,
2971 WidgetGUIEvent
* aEvent
,
2972 nsEventStatus
* aEventStatus
,
2975 NS_ENSURE_ARG_POINTER(aEvent
);
2976 NS_ENSURE_ARG_POINTER(aEventStatus
);
2978 if (nsEventStatus_eConsumeNoDefault
== *aEventStatus
||
2979 DisplaySelection(aPresContext
) == nsISelectionController::SELECTION_OFF
) {
2983 // Find out whether we're doing line or paragraph selection.
2984 // If browser.triple_click_selects_paragraph is true, triple-click selects paragraph.
2985 // Otherwise, triple-click selects line, and quadruple-click selects paragraph
2986 // (on platforms that support quadruple-click).
2987 nsSelectionAmount beginAmount
, endAmount
;
2988 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
2993 if (mouseEvent
->clickCount
== 4) {
2994 beginAmount
= endAmount
= eSelectParagraph
;
2995 } else if (mouseEvent
->clickCount
== 3) {
2996 if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
2997 beginAmount
= endAmount
= eSelectParagraph
;
2999 beginAmount
= eSelectBeginLine
;
3000 endAmount
= eSelectEndLine
;
3002 } else if (mouseEvent
->clickCount
== 2) {
3003 // We only want inline frames; PeekBackwardAndForward dislikes blocks
3004 beginAmount
= endAmount
= eSelectWord
;
3010 nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent
, this);
3011 return SelectByTypeAtPoint(aPresContext
, relPoint
, beginAmount
, endAmount
,
3012 (aControlHeld
? SELECT_ACCUMULATE
: 0));
3016 nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack
,
3017 nsSelectionAmount aAmountForward
,
3019 nsPresContext
* aPresContext
,
3021 uint32_t aSelectFlags
)
3023 nsIFrame
* baseFrame
= this;
3024 int32_t baseOffset
= aStartPos
;
3027 if (aAmountBack
== eSelectWord
) {
3028 // To avoid selecting the previous word when at start of word,
3029 // first move one character forward.
3030 nsPeekOffsetStruct
pos(eSelectCharacter
,
3035 true, //limit on scrolled views
3038 rv
= PeekOffset(&pos
);
3039 if (NS_SUCCEEDED(rv
)) {
3040 baseFrame
= pos
.mResultFrame
;
3041 baseOffset
= pos
.mContentOffset
;
3045 // Use peek offset one way then the other:
3046 nsPeekOffsetStruct
startpos(aAmountBack
,
3051 true, //limit on scrolled views
3054 rv
= baseFrame
->PeekOffset(&startpos
);
3058 nsPeekOffsetStruct
endpos(aAmountForward
,
3063 true, //limit on scrolled views
3066 rv
= PeekOffset(&endpos
);
3070 // Keep frameSelection alive.
3071 nsRefPtr
<nsFrameSelection
> frameSelection
= GetFrameSelection();
3073 rv
= frameSelection
->HandleClick(startpos
.mResultContent
,
3074 startpos
.mContentOffset
, startpos
.mContentOffset
,
3075 false, (aSelectFlags
& SELECT_ACCUMULATE
),
3076 CARET_ASSOCIATE_AFTER
);
3080 rv
= frameSelection
->HandleClick(endpos
.mResultContent
,
3081 endpos
.mContentOffset
, endpos
.mContentOffset
,
3083 CARET_ASSOCIATE_BEFORE
);
3087 // maintain selection
3088 return frameSelection
->MaintainSelection(aAmountBack
);
3091 NS_IMETHODIMP
nsFrame::HandleDrag(nsPresContext
* aPresContext
,
3092 WidgetGUIEvent
* aEvent
,
3093 nsEventStatus
* aEventStatus
)
3095 MOZ_ASSERT(aEvent
->mClass
== eMouseEventClass
,
3096 "HandleDrag can only handle mouse event");
3099 IsSelectable(&selectable
, nullptr);
3101 // XXX Do we really need to exclude non-selectable content here?
3102 // GetContentOffsetsFromPoint can handle it just fine, although some
3103 // other stuff might not like it.
3106 if (DisplaySelection(aPresContext
) == nsISelectionController::SELECTION_OFF
) {
3109 nsIPresShell
*presShell
= aPresContext
->PresShell();
3111 nsRefPtr
<nsFrameSelection
> frameselection
= GetFrameSelection();
3112 bool mouseDown
= frameselection
->GetDragState();
3116 frameselection
->StopAutoScrollTimer();
3118 // Check if we are dragging in a table cell
3119 nsCOMPtr
<nsIContent
> parentContent
;
3120 int32_t contentOffset
;
3122 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
3124 result
= GetDataForTableSelection(frameselection
, presShell
, mouseEvent
,
3125 getter_AddRefs(parentContent
),
3126 &contentOffset
, &target
);
3128 nsWeakFrame weakThis
= this;
3129 if (NS_SUCCEEDED(result
) && parentContent
) {
3130 frameselection
->HandleTableSelection(parentContent
, contentOffset
, target
,
3133 nsPoint pt
= nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent
, this);
3134 frameselection
->HandleDrag(this, pt
);
3137 // The frameselection object notifies selection listeners synchronously above
3138 // which might have killed us.
3139 if (!weakThis
.IsAlive()) {
3143 // get the nearest scrollframe
3144 nsIScrollableFrame
* scrollFrame
=
3145 nsLayoutUtils::GetNearestScrollableFrame(this,
3146 nsLayoutUtils::SCROLLABLE_SAME_DOC
|
3147 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN
);
3150 nsIFrame
* capturingFrame
= scrollFrame
->GetScrolledFrame();
3151 if (capturingFrame
) {
3152 nsPoint pt
= nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent
,
3154 frameselection
->StartAutoScrollTimer(capturingFrame
, pt
, 30);
3162 * This static method handles part of the nsFrame::HandleRelease in a way
3163 * which doesn't rely on the nsFrame object to stay alive.
3166 HandleFrameSelection(nsFrameSelection
* aFrameSelection
,
3167 nsIFrame::ContentOffsets
& aOffsets
,
3168 bool aHandleTableSel
,
3169 int32_t aContentOffsetForTableSel
,
3170 int32_t aTargetForTableSel
,
3171 nsIContent
* aParentContentForTableSel
,
3172 WidgetGUIEvent
* aEvent
,
3173 nsEventStatus
* aEventStatus
)
3175 if (!aFrameSelection
) {
3179 nsresult rv
= NS_OK
;
3181 if (nsEventStatus_eConsumeNoDefault
!= *aEventStatus
) {
3182 if (!aHandleTableSel
) {
3183 if (!aOffsets
.content
|| !aFrameSelection
->HasDelayedCaretData()) {
3184 return NS_ERROR_FAILURE
;
3187 // We are doing this to simulate what we would have done on HandlePress.
3188 // We didn't do it there to give the user an opportunity to drag
3189 // the text, but since they didn't drag, we want to place the
3191 // However, we'll use the mouse position from the release, since:
3193 // * that's the normal click position to use (although really, in
3194 // the normal case, small movements that don't count as a drag
3195 // can do selection)
3196 aFrameSelection
->SetDragState(true);
3198 rv
= aFrameSelection
->HandleClick(aOffsets
.content
,
3199 aOffsets
.StartOffset(),
3200 aOffsets
.EndOffset(),
3201 aFrameSelection
->IsShiftDownInDelayedCaretData(),
3203 aOffsets
.associate
);
3204 if (NS_FAILED(rv
)) {
3207 } else if (aParentContentForTableSel
) {
3208 aFrameSelection
->SetDragState(false);
3209 rv
= aFrameSelection
->HandleTableSelection(
3210 aParentContentForTableSel
,
3211 aContentOffsetForTableSel
,
3213 aEvent
->AsMouseEvent());
3214 if (NS_FAILED(rv
)) {
3218 aFrameSelection
->SetDelayedCaretData(0);
3221 aFrameSelection
->SetDragState(false);
3222 aFrameSelection
->StopAutoScrollTimer();
3227 NS_IMETHODIMP
nsFrame::HandleRelease(nsPresContext
* aPresContext
,
3228 WidgetGUIEvent
* aEvent
,
3229 nsEventStatus
* aEventStatus
)
3231 if (aEvent
->mClass
!= eMouseEventClass
) {
3235 nsIFrame
* activeFrame
= GetActiveSelectionFrame(aPresContext
, this);
3237 nsCOMPtr
<nsIContent
> captureContent
= nsIPresShell::GetCapturingContent();
3239 // We can unconditionally stop capturing because
3240 // we should never be capturing when the mouse button is up
3241 nsIPresShell::SetCapturingContent(nullptr, 0);
3244 (DisplaySelection(aPresContext
) == nsISelectionController::SELECTION_OFF
);
3246 nsRefPtr
<nsFrameSelection
> frameselection
;
3247 ContentOffsets offsets
;
3248 nsCOMPtr
<nsIContent
> parentContent
;
3249 int32_t contentOffsetForTableSel
= 0;
3250 int32_t targetForTableSel
= 0;
3251 bool handleTableSelection
= true;
3253 if (!selectionOff
) {
3254 frameselection
= GetFrameSelection();
3255 if (nsEventStatus_eConsumeNoDefault
!= *aEventStatus
&& frameselection
) {
3256 // Check if the frameselection recorded the mouse going down.
3257 // If not, the user must have clicked in a part of the selection.
3258 // Place the caret before continuing!
3260 bool mouseDown
= frameselection
->GetDragState();
3262 if (!mouseDown
&& frameselection
->HasDelayedCaretData() &&
3263 frameselection
->GetClickCountInDelayedCaretData() < 2) {
3264 nsPoint pt
= nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, this);
3265 offsets
= GetContentOffsetsFromPoint(pt
, SKIP_HIDDEN
);
3266 handleTableSelection
= false;
3268 GetDataForTableSelection(frameselection
, PresContext()->PresShell(),
3269 aEvent
->AsMouseEvent(),
3270 getter_AddRefs(parentContent
),
3271 &contentOffsetForTableSel
,
3272 &targetForTableSel
);
3277 // We might be capturing in some other document and the event just happened to
3278 // trickle down here. Make sure that document's frame selection is notified.
3279 // Note, this may cause the current nsFrame object to be deleted, bug 336592.
3280 nsRefPtr
<nsFrameSelection
> frameSelection
;
3281 if (activeFrame
!= this &&
3282 static_cast<nsFrame
*>(activeFrame
)->DisplaySelection(activeFrame
->PresContext())
3283 != nsISelectionController::SELECTION_OFF
) {
3284 frameSelection
= activeFrame
->GetFrameSelection();
3287 // Also check the selection of the capturing content which might be in a
3288 // different document.
3289 if (!frameSelection
&& captureContent
) {
3290 nsIDocument
* doc
= captureContent
->GetCurrentDoc();
3292 nsIPresShell
* capturingShell
= doc
->GetShell();
3293 if (capturingShell
&& capturingShell
!= PresContext()->GetPresShell()) {
3294 frameSelection
= capturingShell
->FrameSelection();
3299 if (frameSelection
) {
3300 frameSelection
->SetDragState(false);
3301 frameSelection
->StopAutoScrollTimer();
3304 // Do not call any methods of the current object after this point!!!
3305 // The object is perhaps dead!
3309 : HandleFrameSelection(frameselection
, offsets
, handleTableSelection
,
3310 contentOffsetForTableSel
, targetForTableSel
,
3311 parentContent
, aEvent
, aEventStatus
);
3314 struct MOZ_STACK_CLASS FrameContentRange
{
3315 FrameContentRange(nsIContent
* aContent
, int32_t aStart
, int32_t aEnd
) :
3316 content(aContent
), start(aStart
), end(aEnd
) { }
3317 nsCOMPtr
<nsIContent
> content
;
3322 // Retrieve the content offsets of a frame
3323 static FrameContentRange
GetRangeForFrame(nsIFrame
* aFrame
) {
3324 nsCOMPtr
<nsIContent
> content
, parent
;
3325 content
= aFrame
->GetContent();
3327 NS_WARNING("Frame has no content");
3328 return FrameContentRange(nullptr, -1, -1);
3330 nsIAtom
* type
= aFrame
->GetType();
3331 if (type
== nsGkAtoms::textFrame
) {
3332 int32_t offset
, offsetEnd
;
3333 aFrame
->GetOffsets(offset
, offsetEnd
);
3334 return FrameContentRange(content
, offset
, offsetEnd
);
3336 if (type
== nsGkAtoms::brFrame
) {
3337 parent
= content
->GetParent();
3338 int32_t beginOffset
= parent
->IndexOf(content
);
3339 return FrameContentRange(parent
, beginOffset
, beginOffset
);
3341 // Loop to deal with anonymous content, which has no index; this loop
3342 // probably won't run more than twice under normal conditions
3344 parent
= content
->GetParent();
3346 int32_t beginOffset
= parent
->IndexOf(content
);
3347 if (beginOffset
>= 0)
3348 return FrameContentRange(parent
, beginOffset
, beginOffset
+ 1);
3353 // The root content node must act differently
3354 return FrameContentRange(content
, 0, content
->GetChildCount());
3357 // The FrameTarget represents the closest frame to a point that can be selected
3358 // The frame is the frame represented, frameEdge says whether one end of the
3359 // frame is the result (in which case different handling is needed), and
3360 // afterFrame says which end is repersented if frameEdge is true
3361 struct FrameTarget
{
3362 FrameTarget(nsIFrame
* aFrame
, bool aFrameEdge
, bool aAfterFrame
,
3363 bool aEmptyBlock
= false) :
3364 frame(aFrame
), frameEdge(aFrameEdge
), afterFrame(aAfterFrame
),
3365 emptyBlock(aEmptyBlock
) { }
3366 static FrameTarget
Null() {
3367 return FrameTarget(nullptr, false, false);
3378 // See function implementation for information
3379 static FrameTarget
GetSelectionClosestFrame(nsIFrame
* aFrame
, nsPoint aPoint
,
3382 static bool SelfIsSelectable(nsIFrame
* aFrame
, uint32_t aFlags
)
3384 if ((aFlags
& nsIFrame::SKIP_HIDDEN
) &&
3385 !aFrame
->StyleVisibility()->IsVisible()) {
3388 return !aFrame
->IsGeneratedContentFrame() &&
3389 aFrame
->StyleUIReset()->mUserSelect
!= NS_STYLE_USER_SELECT_NONE
;
3392 static bool SelectionDescendToKids(nsIFrame
* aFrame
) {
3393 uint8_t style
= aFrame
->StyleUIReset()->mUserSelect
;
3394 nsIFrame
* parent
= aFrame
->GetParent();
3395 // If we are only near (not directly over) then don't traverse
3396 // frames with independent selection (e.g. text and list controls)
3397 // unless we're already inside such a frame (see bug 268497). Note that this
3398 // prevents any of the users of this method from entering form controls.
3399 // XXX We might want some way to allow using the up-arrow to go into a form
3400 // control, but the focus didn't work right anyway; it'd probably be enough
3401 // if the left and right arrows could enter textboxes (which I don't believe
3402 // they can at the moment)
3403 return !aFrame
->IsGeneratedContentFrame() &&
3404 style
!= NS_STYLE_USER_SELECT_ALL
&&
3405 style
!= NS_STYLE_USER_SELECT_NONE
&&
3406 ((parent
->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION
) ||
3407 !(aFrame
->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION
));
3410 static FrameTarget
GetSelectionClosestFrameForChild(nsIFrame
* aChild
,
3414 nsIFrame
* parent
= aChild
->GetParent();
3415 if (SelectionDescendToKids(aChild
)) {
3416 nsPoint pt
= aPoint
- aChild
->GetOffsetTo(parent
);
3417 return GetSelectionClosestFrame(aChild
, pt
, aFlags
);
3419 return FrameTarget(aChild
, false, false);
3422 // When the cursor needs to be at the beginning of a block, it shouldn't be
3423 // before the first child. A click on a block whose first child is a block
3424 // should put the cursor in the child. The cursor shouldn't be between the
3425 // blocks, because that's not where it's expected.
3426 // Note that this method is guaranteed to succeed.
3427 static FrameTarget
DrillDownToSelectionFrame(nsIFrame
* aFrame
,
3428 bool aEndFrame
, uint32_t aFlags
) {
3429 if (SelectionDescendToKids(aFrame
)) {
3430 nsIFrame
* result
= nullptr;
3431 nsIFrame
*frame
= aFrame
->GetFirstPrincipalChild();
3433 while (frame
&& (!SelfIsSelectable(frame
, aFlags
) ||
3435 frame
= frame
->GetNextSibling();
3439 // Because the frame tree is singly linked, to find the last frame,
3440 // we have to iterate through all the frames
3441 // XXX I have a feeling this could be slow for long blocks, although
3442 // I can't find any slowdowns
3444 if (!frame
->IsEmpty() && SelfIsSelectable(frame
, aFlags
))
3446 frame
= frame
->GetNextSibling();
3450 return DrillDownToSelectionFrame(result
, aEndFrame
, aFlags
);
3452 // If the current frame has no targetable children, target the current frame
3453 return FrameTarget(aFrame
, true, aEndFrame
);
3456 // This method finds the closest valid FrameTarget on a given line; if there is
3457 // no valid FrameTarget on the line, it returns a null FrameTarget
3458 static FrameTarget
GetSelectionClosestFrameForLine(
3459 nsBlockFrame
* aParent
,
3460 nsBlockFrame::line_iterator aLine
,
3464 nsIFrame
*frame
= aLine
->mFirstChild
;
3465 // Account for end of lines (any iterator from the block is valid)
3466 if (aLine
== aParent
->end_lines())
3467 return DrillDownToSelectionFrame(aParent
, true, aFlags
);
3468 nsIFrame
*closestFromIStart
= nullptr, *closestFromIEnd
= nullptr;
3469 nscoord closestIStart
= aLine
->IStart(), closestIEnd
= aLine
->IEnd();
3470 WritingMode wm
= aLine
->mWritingMode
;
3471 LogicalPoint
pt(wm
, aPoint
, aLine
->mContainerWidth
);
3472 for (int32_t n
= aLine
->GetChildCount(); n
;
3473 --n
, frame
= frame
->GetNextSibling()) {
3474 if (!SelfIsSelectable(frame
, aFlags
) || frame
->IsEmpty())
3476 LogicalRect frameRect
= LogicalRect(wm
, frame
->GetRect(),
3477 aLine
->mContainerWidth
);
3478 if (pt
.I(wm
) >= frameRect
.IStart(wm
)) {
3479 if (pt
.I(wm
) < frameRect
.IEnd(wm
)) {
3480 return GetSelectionClosestFrameForChild(frame
, aPoint
, aFlags
);
3482 if (frameRect
.IEnd(wm
) >= closestIStart
) {
3483 closestFromIStart
= frame
;
3484 closestIStart
= frameRect
.IEnd(wm
);
3487 if (frameRect
.IStart(wm
) <= closestIEnd
) {
3488 closestFromIEnd
= frame
;
3489 closestIEnd
= frameRect
.IStart(wm
);
3493 if (!closestFromIStart
&& !closestFromIEnd
) {
3494 // We should only get here if there are no selectable frames on a line
3495 // XXX Do we need more elaborate handling here?
3496 return FrameTarget::Null();
3498 if (closestFromIStart
&&
3499 (!closestFromIEnd
||
3500 (abs(pt
.I(wm
) - closestIStart
) <= abs(pt
.I(wm
) - closestIEnd
)))) {
3501 return GetSelectionClosestFrameForChild(closestFromIStart
, aPoint
,
3504 return GetSelectionClosestFrameForChild(closestFromIEnd
, aPoint
, aFlags
);
3507 // This method is for the special handling we do for block frames; they're
3508 // special because they represent paragraphs and because they are organized
3509 // into lines, which have bounds that are not stored elsewhere in the
3510 // frame tree. Returns a null FrameTarget for frames which are not
3511 // blocks or blocks with no lines except editable one.
3512 static FrameTarget
GetSelectionClosestFrameForBlock(nsIFrame
* aFrame
,
3516 nsBlockFrame
* bf
= nsLayoutUtils::GetAsBlock(aFrame
); // used only for QI
3518 return FrameTarget::Null();
3520 // This code searches for the correct line
3521 nsBlockFrame::line_iterator firstLine
= bf
->begin_lines();
3522 nsBlockFrame::line_iterator end
= bf
->end_lines();
3523 if (firstLine
== end
) {
3524 nsIContent
*blockContent
= aFrame
->GetContent();
3526 // Return with empty flag true.
3527 return FrameTarget(aFrame
, false, false, true);
3529 return FrameTarget::Null();
3531 nsBlockFrame::line_iterator curLine
= firstLine
;
3532 nsBlockFrame::line_iterator closestLine
= end
;
3533 // Convert aPoint into a LogicalPoint in the writing-mode of this block
3534 WritingMode wm
= curLine
->mWritingMode
;
3535 LogicalPoint
pt(wm
, aPoint
, curLine
->mContainerWidth
);
3536 while (curLine
!= end
) {
3537 // Check to see if our point lies within the line's block-direction bounds
3538 nscoord BCoord
= pt
.B(wm
) - curLine
->BStart();
3539 nscoord BSize
= curLine
->BSize();
3540 if (BCoord
>= 0 && BCoord
< BSize
) {
3541 closestLine
= curLine
;
3542 break; // We found the line; stop looking
3549 if (closestLine
== end
) {
3550 nsBlockFrame::line_iterator prevLine
= curLine
.prev();
3551 nsBlockFrame::line_iterator nextLine
= curLine
;
3552 // Avoid empty lines
3553 while (nextLine
!= end
&& nextLine
->IsEmpty())
3555 while (prevLine
!= end
&& prevLine
->IsEmpty())
3558 // This hidden pref dictates whether a point above or below all lines comes
3559 // up with a line or the beginning or end of the frame; 0 on Windows,
3560 // 1 on other platforms by default at the writing of this code
3561 int32_t dragOutOfFrame
=
3562 Preferences::GetInt("browser.drag_out_of_frame_style");
3564 if (prevLine
== end
) {
3565 if (dragOutOfFrame
== 1 || nextLine
== end
)
3566 return DrillDownToSelectionFrame(aFrame
, false, aFlags
);
3567 closestLine
= nextLine
;
3568 } else if (nextLine
== end
) {
3569 if (dragOutOfFrame
== 1)
3570 return DrillDownToSelectionFrame(aFrame
, true, aFlags
);
3571 closestLine
= prevLine
;
3572 } else { // Figure out which line is closer
3573 if (pt
.B(wm
) - prevLine
->BEnd() < nextLine
->BStart() - pt
.B(wm
))
3574 closestLine
= prevLine
;
3576 closestLine
= nextLine
;
3581 FrameTarget target
= GetSelectionClosestFrameForLine(bf
, closestLine
,
3583 if (!target
.IsNull())
3586 } while (closestLine
!= end
);
3587 // Fall back to just targeting the last targetable place
3588 return DrillDownToSelectionFrame(aFrame
, true, aFlags
);
3591 // GetSelectionClosestFrame is the helper function that calculates the closest
3592 // frame to the given point.
3593 // It doesn't completely account for offset styles, so needs to be used in
3594 // restricted environments.
3595 // Cannot handle overlapping frames correctly, so it should receive the output
3596 // of GetFrameForPoint
3597 // Guaranteed to return a valid FrameTarget
3598 static FrameTarget
GetSelectionClosestFrame(nsIFrame
* aFrame
, nsPoint aPoint
,
3602 // Handle blocks; if the frame isn't a block, the method fails
3603 FrameTarget target
= GetSelectionClosestFrameForBlock(aFrame
, aPoint
, aFlags
);
3604 if (!target
.IsNull())
3608 nsIFrame
*kid
= aFrame
->GetFirstPrincipalChild();
3611 // Go through all the child frames to find the closest one
3612 nsIFrame::FrameWithDistance closest
= { nullptr, nscoord_MAX
, nscoord_MAX
};
3613 for (; kid
; kid
= kid
->GetNextSibling()) {
3614 if (!SelfIsSelectable(kid
, aFlags
) || kid
->IsEmpty())
3617 kid
->FindCloserFrameForSelection(aPoint
, &closest
);
3619 if (closest
.mFrame
) {
3620 if (closest
.mFrame
->IsSVGText())
3621 return FrameTarget(closest
.mFrame
, false, false);
3622 return GetSelectionClosestFrameForChild(closest
.mFrame
, aPoint
, aFlags
);
3625 return FrameTarget(aFrame
, false, false);
3628 nsIFrame::ContentOffsets
OffsetsForSingleFrame(nsIFrame
* aFrame
, nsPoint aPoint
)
3630 nsIFrame::ContentOffsets offsets
;
3631 FrameContentRange range
= GetRangeForFrame(aFrame
);
3632 offsets
.content
= range
.content
;
3633 // If there are continuations (meaning it's not one rectangle), this is the
3634 // best this function can do
3635 if (aFrame
->GetNextContinuation() || aFrame
->GetPrevContinuation()) {
3636 offsets
.offset
= range
.start
;
3637 offsets
.secondaryOffset
= range
.end
;
3638 offsets
.associate
= CARET_ASSOCIATE_AFTER
;
3642 // Figure out whether the offsets should be over, after, or before the frame
3643 nsRect
rect(nsPoint(0, 0), aFrame
->GetSize());
3645 bool isBlock
= aFrame
->GetDisplay() != NS_STYLE_DISPLAY_INLINE
;
3646 bool isRtl
= (aFrame
->StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
);
3647 if ((isBlock
&& rect
.y
< aPoint
.y
) ||
3648 (!isBlock
&& ((isRtl
&& rect
.x
+ rect
.width
/ 2 > aPoint
.x
) ||
3649 (!isRtl
&& rect
.x
+ rect
.width
/ 2 < aPoint
.x
)))) {
3650 offsets
.offset
= range
.end
;
3651 if (rect
.Contains(aPoint
))
3652 offsets
.secondaryOffset
= range
.start
;
3654 offsets
.secondaryOffset
= range
.end
;
3656 offsets
.offset
= range
.start
;
3657 if (rect
.Contains(aPoint
))
3658 offsets
.secondaryOffset
= range
.end
;
3660 offsets
.secondaryOffset
= range
.start
;
3663 offsets
.offset
== range
.start
? CARET_ASSOCIATE_AFTER
: CARET_ASSOCIATE_BEFORE
;
3667 static nsIFrame
* AdjustFrameForSelectionStyles(nsIFrame
* aFrame
) {
3668 nsIFrame
* adjustedFrame
= aFrame
;
3669 for (nsIFrame
* frame
= aFrame
; frame
; frame
= frame
->GetParent())
3671 // These are the conditions that make all children not able to handle
3673 if (frame
->StyleUIReset()->mUserSelect
== NS_STYLE_USER_SELECT_ALL
||
3674 frame
->IsGeneratedContentFrame()) {
3675 adjustedFrame
= frame
;
3678 return adjustedFrame
;
3681 nsIFrame::ContentOffsets
nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint
,
3684 nsIFrame
*adjustedFrame
;
3685 if (aFlags
& IGNORE_SELECTION_STYLE
) {
3686 adjustedFrame
= this;
3689 // This section of code deals with special selection styles. Note that
3690 // -moz-all exists, even though it doesn't need to be explicitly handled.
3692 // The offset is forced not to end up in generated content; content offsets
3693 // cannot represent content outside of the document's content tree.
3695 adjustedFrame
= AdjustFrameForSelectionStyles(this);
3697 // -moz-user-select: all needs special handling, because clicking on it
3698 // should lead to the whole frame being selected
3699 if (adjustedFrame
&& adjustedFrame
->StyleUIReset()->mUserSelect
==
3700 NS_STYLE_USER_SELECT_ALL
) {
3701 nsPoint adjustedPoint
= aPoint
+ this->GetOffsetTo(adjustedFrame
);
3702 return OffsetsForSingleFrame(adjustedFrame
, adjustedPoint
);
3705 // For other cases, try to find a closest frame starting from the parent of
3706 // the unselectable frame
3707 if (adjustedFrame
!= this)
3708 adjustedFrame
= adjustedFrame
->GetParent();
3711 nsPoint adjustedPoint
= aPoint
+ this->GetOffsetTo(adjustedFrame
);
3713 FrameTarget closest
=
3714 GetSelectionClosestFrame(adjustedFrame
, adjustedPoint
, aFlags
);
3716 if (closest
.emptyBlock
) {
3717 ContentOffsets offsets
;
3718 NS_ASSERTION(closest
.frame
,
3719 "closest.frame must not be null when it's empty");
3720 offsets
.content
= closest
.frame
->GetContent();
3722 offsets
.secondaryOffset
= 0;
3723 offsets
.associate
= CARET_ASSOCIATE_AFTER
;
3727 // If the correct offset is at one end of a frame, use offset-based
3728 // calculation method
3729 if (closest
.frameEdge
) {
3730 ContentOffsets offsets
;
3731 FrameContentRange range
= GetRangeForFrame(closest
.frame
);
3732 offsets
.content
= range
.content
;
3733 if (closest
.afterFrame
)
3734 offsets
.offset
= range
.end
;
3736 offsets
.offset
= range
.start
;
3737 offsets
.secondaryOffset
= offsets
.offset
;
3738 offsets
.associate
= offsets
.offset
== range
.start
?
3739 CARET_ASSOCIATE_AFTER
: CARET_ASSOCIATE_BEFORE
;
3744 if (closest
.frame
!= this) {
3745 if (closest
.frame
->IsSVGText()) {
3746 pt
= nsLayoutUtils::TransformAncestorPointToFrame(closest
.frame
,
3749 pt
= aPoint
- closest
.frame
->GetOffsetTo(this);
3754 return static_cast<nsFrame
*>(closest
.frame
)->CalcContentOffsetsFromFramePoint(pt
);
3756 // XXX should I add some kind of offset standardization?
3757 // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
3758 // x and first z put the cursor in the same logical position in addition
3759 // to the same visual position?
3762 nsIFrame::ContentOffsets
nsFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint
)
3764 return OffsetsForSingleFrame(this, aPoint
);
3768 nsIFrame::AssociateImage(const nsStyleImage
& aImage
, nsPresContext
* aPresContext
)
3770 if (aImage
.GetType() != eStyleImageType_Image
) {
3774 imgIRequest
*req
= aImage
.GetImageData();
3775 mozilla::css::ImageLoader
* loader
=
3776 aPresContext
->Document()->StyleImageLoader();
3778 // If this fails there's not much we can do ...
3779 loader
->AssociateRequestToFrame(req
, this);
3783 nsFrame::GetCursor(const nsPoint
& aPoint
,
3784 nsIFrame::Cursor
& aCursor
)
3786 FillCursorInformationFromStyle(StyleUserInterface(), aCursor
);
3787 if (NS_STYLE_CURSOR_AUTO
== aCursor
.mCursor
) {
3788 // If this is editable, I-beam cursor is better for most elements.
3790 (mContent
&& mContent
->IsEditable()) ? NS_STYLE_CURSOR_TEXT
:
3791 NS_STYLE_CURSOR_DEFAULT
;
3798 // Resize and incremental reflow
3801 nsFrame::MarkIntrinsicISizesDirty()
3803 // This version is meant only for what used to be box-to-block adaptors.
3804 // It should not be called by other derived classes.
3805 if (::IsBoxWrapped(this)) {
3806 nsBoxLayoutMetrics
*metrics
= BoxMetrics();
3808 SizeNeedsRecalc(metrics
->mPrefSize
);
3809 SizeNeedsRecalc(metrics
->mMinSize
);
3810 SizeNeedsRecalc(metrics
->mMaxSize
);
3811 SizeNeedsRecalc(metrics
->mBlockPrefSize
);
3812 SizeNeedsRecalc(metrics
->mBlockMinSize
);
3813 CoordNeedsRecalc(metrics
->mFlex
);
3814 CoordNeedsRecalc(metrics
->mAscent
);
3817 if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT
) {
3818 nsFontInflationData::MarkFontInflationDataTextDirty(this);
3822 /* virtual */ nscoord
3823 nsFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
3826 DISPLAY_MIN_WIDTH(this, result
);
3830 /* virtual */ nscoord
3831 nsFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
3834 DISPLAY_PREF_WIDTH(this, result
);
3839 nsFrame::AddInlineMinISize(nsRenderingContext
*aRenderingContext
,
3840 nsIFrame::InlineMinISizeData
*aData
)
3842 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
3843 nsIFrame
* parent
= GetParent();
3844 bool canBreak
= !CanContinueTextRun() &&
3845 parent
->StyleText()->WhiteSpaceCanWrap(parent
);
3848 aData
->OptionallyBreak(aRenderingContext
);
3849 aData
->trailingWhitespace
= 0;
3850 aData
->skipWhitespace
= false;
3851 aData
->trailingTextFrame
= nullptr;
3852 aData
->currentLine
+= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
3853 this, nsLayoutUtils::MIN_ISIZE
);
3854 aData
->atStartOfLine
= false;
3856 aData
->OptionallyBreak(aRenderingContext
);
3860 nsFrame::AddInlinePrefISize(nsRenderingContext
*aRenderingContext
,
3861 nsIFrame::InlinePrefISizeData
*aData
)
3863 aData
->trailingWhitespace
= 0;
3864 aData
->skipWhitespace
= false;
3865 nscoord myPref
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
3866 this, nsLayoutUtils::PREF_ISIZE
);
3867 aData
->currentLine
= NSCoordSaturatingAdd(aData
->currentLine
, myPref
);
3871 nsIFrame::InlineMinISizeData::ForceBreak(nsRenderingContext
*aRenderingContext
)
3873 currentLine
-= trailingWhitespace
;
3874 prevLines
= std::max(prevLines
, currentLine
);
3875 currentLine
= trailingWhitespace
= 0;
3877 for (uint32_t i
= 0, i_end
= floats
.Length(); i
!= i_end
; ++i
) {
3878 nscoord float_min
= floats
[i
].Width();
3879 if (float_min
> prevLines
)
3880 prevLines
= float_min
;
3883 trailingTextFrame
= nullptr;
3884 skipWhitespace
= true;
3888 nsIFrame::InlineMinISizeData::OptionallyBreak(nsRenderingContext
*aRenderingContext
,
3889 nscoord aHyphenWidth
)
3891 trailingTextFrame
= nullptr;
3893 // If we can fit more content into a smaller width by staying on this
3894 // line (because we're still at a negative offset due to negative
3895 // text-indent or negative margin), don't break. Otherwise, do the
3896 // same as ForceBreak. it doesn't really matter when we accumulate
3898 if (currentLine
+ aHyphenWidth
< 0 || atStartOfLine
)
3900 currentLine
+= aHyphenWidth
;
3901 ForceBreak(aRenderingContext
);
3905 nsIFrame::InlinePrefISizeData::ForceBreak(nsRenderingContext
*aRenderingContext
)
3907 if (floats
.Length() != 0) {
3908 // preferred widths accumulated for floats that have already
3909 // been cleared past
3910 nscoord floats_done
= 0,
3911 // preferred widths accumulated for floats that have not yet
3912 // been cleared past
3913 floats_cur_left
= 0,
3914 floats_cur_right
= 0;
3916 for (uint32_t i
= 0, i_end
= floats
.Length(); i
!= i_end
; ++i
) {
3917 const FloatInfo
& floatInfo
= floats
[i
];
3918 const nsStyleDisplay
*floatDisp
= floatInfo
.Frame()->StyleDisplay();
3919 if (floatDisp
->mBreakType
== NS_STYLE_CLEAR_LEFT
||
3920 floatDisp
->mBreakType
== NS_STYLE_CLEAR_RIGHT
||
3921 floatDisp
->mBreakType
== NS_STYLE_CLEAR_BOTH
) {
3922 nscoord floats_cur
= NSCoordSaturatingAdd(floats_cur_left
,
3924 if (floats_cur
> floats_done
)
3925 floats_done
= floats_cur
;
3926 if (floatDisp
->mBreakType
!= NS_STYLE_CLEAR_RIGHT
)
3927 floats_cur_left
= 0;
3928 if (floatDisp
->mBreakType
!= NS_STYLE_CLEAR_LEFT
)
3929 floats_cur_right
= 0;
3932 nscoord
&floats_cur
= floatDisp
->mFloats
== NS_STYLE_FLOAT_LEFT
3933 ? floats_cur_left
: floats_cur_right
;
3934 nscoord floatWidth
= floatInfo
.Width();
3935 // Negative-width floats don't change the available space so they
3936 // shouldn't change our intrinsic line width either.
3938 NSCoordSaturatingAdd(floats_cur
, std::max(0, floatWidth
));
3941 nscoord floats_cur
=
3942 NSCoordSaturatingAdd(floats_cur_left
, floats_cur_right
);
3943 if (floats_cur
> floats_done
)
3944 floats_done
= floats_cur
;
3946 currentLine
= NSCoordSaturatingAdd(currentLine
, floats_done
);
3952 NSCoordSaturatingSubtract(currentLine
, trailingWhitespace
, nscoord_MAX
);
3953 prevLines
= std::max(prevLines
, currentLine
);
3954 currentLine
= trailingWhitespace
= 0;
3955 skipWhitespace
= true;
3959 AddCoord(const nsStyleCoord
& aStyle
,
3960 nsRenderingContext
* aRenderingContext
,
3962 nscoord
* aCoord
, float* aPercent
,
3963 bool aClampNegativeToZero
)
3965 switch (aStyle
.GetUnit()) {
3966 case eStyleUnit_Coord
: {
3967 NS_ASSERTION(!aClampNegativeToZero
|| aStyle
.GetCoordValue() >= 0,
3968 "unexpected negative value");
3969 *aCoord
+= aStyle
.GetCoordValue();
3972 case eStyleUnit_Percent
: {
3973 NS_ASSERTION(!aClampNegativeToZero
|| aStyle
.GetPercentValue() >= 0.0f
,
3974 "unexpected negative value");
3975 *aPercent
+= aStyle
.GetPercentValue();
3978 case eStyleUnit_Calc
: {
3979 const nsStyleCoord::Calc
*calc
= aStyle
.GetCalcValue();
3980 if (aClampNegativeToZero
) {
3981 // This is far from ideal when one is negative and one is positive.
3982 *aCoord
+= std::max(calc
->mLength
, 0);
3983 *aPercent
+= std::max(calc
->mPercent
, 0.0f
);
3985 *aCoord
+= calc
->mLength
;
3986 *aPercent
+= calc
->mPercent
;
3996 /* virtual */ nsIFrame::IntrinsicISizeOffsetData
3997 nsFrame::IntrinsicISizeOffsets(nsRenderingContext
* aRenderingContext
)
3999 IntrinsicISizeOffsetData result
;
4001 const nsStyleMargin
*styleMargin
= StyleMargin();
4002 AddCoord(styleMargin
->mMargin
.GetLeft(), aRenderingContext
, this,
4003 &result
.hMargin
, &result
.hPctMargin
, false);
4004 AddCoord(styleMargin
->mMargin
.GetRight(), aRenderingContext
, this,
4005 &result
.hMargin
, &result
.hPctMargin
, false);
4007 const nsStylePadding
*stylePadding
= StylePadding();
4008 AddCoord(stylePadding
->mPadding
.GetLeft(), aRenderingContext
, this,
4009 &result
.hPadding
, &result
.hPctPadding
, true);
4010 AddCoord(stylePadding
->mPadding
.GetRight(), aRenderingContext
, this,
4011 &result
.hPadding
, &result
.hPctPadding
, true);
4013 const nsStyleBorder
*styleBorder
= StyleBorder();
4014 result
.hBorder
+= styleBorder
->GetComputedBorderWidth(NS_SIDE_LEFT
);
4015 result
.hBorder
+= styleBorder
->GetComputedBorderWidth(NS_SIDE_RIGHT
);
4017 const nsStyleDisplay
*disp
= StyleDisplay();
4018 if (IsThemed(disp
)) {
4019 nsPresContext
*presContext
= PresContext();
4022 presContext
->GetTheme()->GetWidgetBorder(presContext
->DeviceContext(),
4023 this, disp
->mAppearance
,
4025 result
.hBorder
= presContext
->DevPixelsToAppUnits(border
.LeftRight());
4027 nsIntMargin padding
;
4028 if (presContext
->GetTheme()->GetWidgetPadding(presContext
->DeviceContext(),
4029 this, disp
->mAppearance
,
4031 result
.hPadding
= presContext
->DevPixelsToAppUnits(padding
.LeftRight());
4032 result
.hPctPadding
= 0;
4039 /* virtual */ IntrinsicSize
4040 nsFrame::GetIntrinsicSize()
4042 return IntrinsicSize(); // default is width/height set to eStyleUnit_None
4045 /* virtual */ nsSize
4046 nsFrame::GetIntrinsicRatio()
4048 return nsSize(0, 0);
4053 nsFrame::ComputeSize(nsRenderingContext
*aRenderingContext
,
4055 const LogicalSize
& aCBSize
,
4056 nscoord aAvailableISize
,
4057 const LogicalSize
& aMargin
,
4058 const LogicalSize
& aBorder
,
4059 const LogicalSize
& aPadding
,
4062 LogicalSize result
= ComputeAutoSize(aRenderingContext
, aWM
,
4063 aCBSize
, aAvailableISize
,
4064 aMargin
, aBorder
, aPadding
,
4065 aFlags
& eShrinkWrap
);
4066 LogicalSize
boxSizingAdjust(aWM
);
4067 const nsStylePosition
*stylePos
= StylePosition();
4069 switch (stylePos
->mBoxSizing
) {
4070 case NS_STYLE_BOX_SIZING_BORDER
:
4071 boxSizingAdjust
+= aBorder
;
4073 case NS_STYLE_BOX_SIZING_PADDING
:
4074 boxSizingAdjust
+= aPadding
;
4076 nscoord boxSizingToMarginEdgeISize
=
4077 aMargin
.ISize(aWM
) + aBorder
.ISize(aWM
) + aPadding
.ISize(aWM
) -
4078 boxSizingAdjust
.ISize(aWM
);
4080 const nsStyleCoord
* inlineStyleCoord
;
4081 const nsStyleCoord
* blockStyleCoord
;
4082 if (aWM
.IsVertical()) {
4083 inlineStyleCoord
= &(stylePos
->mHeight
);
4084 blockStyleCoord
= &(stylePos
->mWidth
);
4086 inlineStyleCoord
= &(stylePos
->mWidth
);
4087 blockStyleCoord
= &(stylePos
->mHeight
);
4090 bool isFlexItem
= IsFlexItem();
4091 bool isInlineFlexItem
= false;
4094 // Flex items use their "flex-basis" property in place of their main-size
4095 // property (e.g. "width") for sizing purposes, *unless* they have
4096 // "flex-basis:auto", in which case they use their main-size property after
4098 uint32_t flexDirection
= GetParent()->StylePosition()->mFlexDirection
;
4100 flexDirection
== NS_STYLE_FLEX_DIRECTION_ROW
||
4101 flexDirection
== NS_STYLE_FLEX_DIRECTION_ROW_REVERSE
;
4103 // NOTE: The logic here should match the similar chunk for determining
4104 // inlineStyleCoord and blockStyleCoord in
4105 // nsLayoutUtils::ComputeSizeWithIntrinsicDimensions().
4106 const nsStyleCoord
* flexBasis
= &(stylePos
->mFlexBasis
);
4107 if (flexBasis
->GetUnit() != eStyleUnit_Auto
) {
4108 if (isInlineFlexItem
) {
4109 inlineStyleCoord
= flexBasis
;
4111 // One caveat for vertical flex items: We don't support enumerated
4112 // values (e.g. "max-content") for height properties yet. So, if our
4113 // computed flex-basis is an enumerated value, we'll just behave as if
4114 // it were "auto", which means "use the main-size property after all"
4115 // (which is "height", in this case).
4116 // NOTE: Once we support intrinsic sizing keywords for "height",
4117 // we should remove this check.
4118 if (flexBasis
->GetUnit() != eStyleUnit_Enumerated
) {
4119 blockStyleCoord
= flexBasis
;
4125 // Compute inline-axis size
4127 if (inlineStyleCoord
->GetUnit() != eStyleUnit_Auto
) {
4129 nsLayoutUtils::ComputeWidthValue(aRenderingContext
, this,
4130 aCBSize
.ISize(aWM
), boxSizingAdjust
.ISize(aWM
), boxSizingToMarginEdgeISize
,
4134 // Flex items ignore their min & max sizing properties in their
4135 // flex container's main-axis. (Those properties get applied later in
4136 // the flexbox algorithm.)
4137 if (stylePos
->mMaxWidth
.GetUnit() != eStyleUnit_None
&&
4138 !(isFlexItem
&& isInlineFlexItem
)) {
4140 nsLayoutUtils::ComputeWidthValue(aRenderingContext
, this,
4141 aCBSize
.ISize(aWM
), boxSizingAdjust
.ISize(aWM
), boxSizingToMarginEdgeISize
,
4142 stylePos
->mMaxWidth
);
4143 result
.ISize(aWM
) = std::min(maxISize
, result
.ISize(aWM
));
4147 if (stylePos
->mMinWidth
.GetUnit() != eStyleUnit_Auto
&&
4148 !(isFlexItem
&& isInlineFlexItem
)) {
4150 nsLayoutUtils::ComputeWidthValue(aRenderingContext
, this,
4151 aCBSize
.ISize(aWM
), boxSizingAdjust
.ISize(aWM
), boxSizingToMarginEdgeISize
,
4152 stylePos
->mMinWidth
);
4154 // Treat "min-width: auto" as 0.
4155 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
4156 // flex items. However, we don't need to worry about that here, because
4157 // flex items' min-sizes are intentionally ignored until the flex
4158 // container explicitly considers them during space distribution.
4161 result
.ISize(aWM
) = std::max(minISize
, result
.ISize(aWM
));
4163 // Compute block-axis size
4164 // (but not if we're auto-height or if we recieved the "eUseAutoHeight"
4165 // flag -- then, we'll just stick with the height that we already calculated
4166 // in the initial ComputeAutoSize() call.)
4167 if (!nsLayoutUtils::IsAutoHeight(*blockStyleCoord
, aCBSize
.BSize(aWM
)) &&
4168 !(aFlags
& nsIFrame::eUseAutoHeight
)) {
4170 nsLayoutUtils::ComputeHeightValue(aCBSize
.BSize(aWM
),
4171 boxSizingAdjust
.BSize(aWM
),
4175 if (result
.BSize(aWM
) != NS_UNCONSTRAINEDSIZE
) {
4176 if (!nsLayoutUtils::IsAutoHeight(stylePos
->mMaxHeight
, aCBSize
.BSize(aWM
)) &&
4177 !(isFlexItem
&& !isInlineFlexItem
)) {
4179 nsLayoutUtils::ComputeHeightValue(aCBSize
.BSize(aWM
),
4180 boxSizingAdjust
.BSize(aWM
),
4181 stylePos
->mMaxHeight
);
4182 result
.BSize(aWM
) = std::min(maxBSize
, result
.BSize(aWM
));
4185 if (!nsLayoutUtils::IsAutoHeight(stylePos
->mMinHeight
, aCBSize
.BSize(aWM
)) &&
4186 !(isFlexItem
&& !isInlineFlexItem
)) {
4188 nsLayoutUtils::ComputeHeightValue(aCBSize
.BSize(aWM
),
4189 boxSizingAdjust
.BSize(aWM
),
4190 stylePos
->mMinHeight
);
4191 result
.BSize(aWM
) = std::max(minBSize
, result
.BSize(aWM
));
4195 const nsStyleDisplay
*disp
= StyleDisplay();
4196 if (IsThemed(disp
)) {
4197 nsIntSize
widget(0, 0);
4198 bool canOverride
= true;
4199 nsPresContext
*presContext
= PresContext();
4200 presContext
->GetTheme()->
4201 GetMinimumWidgetSize(presContext
, this, disp
->mAppearance
,
4202 &widget
, &canOverride
);
4204 // Dimensions from themed widgets are applied physically...
4206 size
.width
= presContext
->DevPixelsToAppUnits(widget
.width
);
4207 size
.height
= presContext
->DevPixelsToAppUnits(widget
.height
);
4209 // GMWS() returns border-box; we need content-box
4210 size
.width
-= aBorder
.Width(aWM
) + aPadding
.Width(aWM
);
4211 size
.height
-= aBorder
.Height(aWM
) + aPadding
.Height(aWM
);
4213 if (size
.height
> result
.Height(aWM
) || !canOverride
) {
4214 result
.Height(aWM
) = size
.height
;
4216 if (size
.width
> result
.Width(aWM
) || !canOverride
) {
4217 result
.Width(aWM
) = size
.width
;
4221 result
.ISize(aWM
) = std::max(0, result
.ISize(aWM
));
4222 result
.BSize(aWM
) = std::max(0, result
.BSize(aWM
));
4228 nsIFrame::ComputeTightBounds(gfxContext
* aContext
) const
4230 return GetVisualOverflowRect();
4234 nsFrame::ComputeSimpleTightBounds(gfxContext
* aContext
) const
4236 if (StyleOutline()->GetOutlineStyle() != NS_STYLE_BORDER_STYLE_NONE
||
4237 StyleBorder()->HasBorder() || !StyleBackground()->IsTransparent() ||
4238 StyleDisplay()->mAppearance
) {
4239 // Not necessarily tight, due to clipping, negative
4240 // outline-offset, and lots of other issues, but that's OK
4241 return GetVisualOverflowRect();
4244 nsRect
r(0, 0, 0, 0);
4245 ChildListIterator
lists(this);
4246 for (; !lists
.IsDone(); lists
.Next()) {
4247 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
4248 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
4249 nsIFrame
* child
= childFrames
.get();
4250 r
.UnionRect(r
, child
->ComputeTightBounds(aContext
) + child
->GetPosition());
4256 /* virtual */ nsresult
4257 nsIFrame::GetPrefWidthTightBounds(nsRenderingContext
* aContext
,
4261 return NS_ERROR_NOT_IMPLEMENTED
;
4266 nsFrame::ComputeAutoSize(nsRenderingContext
*aRenderingContext
,
4268 const mozilla::LogicalSize
& aCBSize
,
4269 nscoord aAvailableISize
,
4270 const mozilla::LogicalSize
& aMargin
,
4271 const mozilla::LogicalSize
& aBorder
,
4272 const mozilla::LogicalSize
& aPadding
,
4275 // Use basic shrink-wrapping as a default implementation.
4276 LogicalSize
result(aWM
, 0xdeadbeef, NS_UNCONSTRAINEDSIZE
);
4278 // don't bother setting it if the result won't be used
4279 const nsStyleCoord
& inlineStyleCoord
=
4280 aWM
.IsVertical() ? StylePosition()->mHeight
: StylePosition()->mWidth
;
4281 if (inlineStyleCoord
.GetUnit() == eStyleUnit_Auto
) {
4282 nscoord availBased
= aAvailableISize
- aMargin
.ISize(aWM
) -
4283 aBorder
.ISize(aWM
) - aPadding
.ISize(aWM
);
4284 result
.ISize(aWM
) = ShrinkWidthToFit(aRenderingContext
, availBased
);
4290 nsFrame::ShrinkWidthToFit(nsRenderingContext
*aRenderingContext
,
4293 // If we're a container for font size inflation, then shrink
4294 // wrapping inside of us should not apply font size inflation.
4295 AutoMaybeDisableFontInflation
an(this);
4298 nscoord minWidth
= GetMinISize(aRenderingContext
);
4299 if (minWidth
> aWidthInCB
) {
4302 nscoord prefWidth
= GetPrefISize(aRenderingContext
);
4303 if (prefWidth
> aWidthInCB
) {
4304 result
= aWidthInCB
;
4313 nsFrame::WillReflow(nsPresContext
* aPresContext
)
4315 #ifdef DEBUG_dbaron_off
4317 NS_ASSERTION(!(mState
& NS_FRAME_IN_REFLOW
),
4318 "nsFrame::WillReflow: frame is already in reflow");
4321 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS
,
4322 ("WillReflow: oldState=%x", mState
));
4323 mState
|= NS_FRAME_IN_REFLOW
;
4327 nsFrame::DidReflow(nsPresContext
* aPresContext
,
4328 const nsHTMLReflowState
* aReflowState
,
4329 nsDidReflowStatus aStatus
)
4331 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS
,
4332 ("nsFrame::DidReflow: aStatus=%d", static_cast<uint32_t>(aStatus
)));
4334 nsSVGEffects::InvalidateDirectRenderingObservers(this, nsSVGEffects::INVALIDATE_REFLOW
);
4336 if (nsDidReflowStatus::FINISHED
== aStatus
) {
4337 mState
&= ~(NS_FRAME_IN_REFLOW
| NS_FRAME_FIRST_REFLOW
| NS_FRAME_IS_DIRTY
|
4338 NS_FRAME_HAS_DIRTY_CHILDREN
);
4341 // Notify the percent height observer if there is a percent height.
4342 // The observer may be able to initiate another reflow with a computed
4343 // height. This happens in the case where a table cell has no computed
4344 // height but can fabricate one when the cell height is known.
4345 if (aReflowState
&& aReflowState
->mPercentHeightObserver
&&
4347 const nsStyleCoord
&height
= aReflowState
->mStylePosition
->mHeight
;
4348 if (height
.HasPercent()) {
4349 aReflowState
->mPercentHeightObserver
->NotifyPercentHeight(*aReflowState
);
4355 nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext
* aPresContext
,
4356 nsHTMLReflowMetrics
& aDesiredSize
,
4357 const nsHTMLReflowState
& aReflowState
,
4358 nsReflowStatus
& aStatus
,
4359 bool aConstrainHeight
)
4361 ReflowAbsoluteFrames(aPresContext
, aDesiredSize
, aReflowState
, aStatus
, aConstrainHeight
);
4363 FinishAndStoreOverflow(&aDesiredSize
);
4367 nsFrame::ReflowAbsoluteFrames(nsPresContext
* aPresContext
,
4368 nsHTMLReflowMetrics
& aDesiredSize
,
4369 const nsHTMLReflowState
& aReflowState
,
4370 nsReflowStatus
& aStatus
,
4371 bool aConstrainHeight
)
4373 if (HasAbsolutelyPositionedChildren()) {
4374 nsAbsoluteContainingBlock
* absoluteContainer
= GetAbsoluteContainingBlock();
4376 // Let the absolutely positioned container reflow any absolutely positioned
4377 // child frames that need to be reflowed
4379 // The containing block for the abs pos kids is formed by our padding edge.
4380 nsMargin computedBorder
=
4381 aReflowState
.ComputedPhysicalBorderPadding() - aReflowState
.ComputedPhysicalPadding();
4382 nscoord containingBlockWidth
=
4383 aDesiredSize
.Width() - computedBorder
.LeftRight();
4384 nscoord containingBlockHeight
=
4385 aDesiredSize
.Height() - computedBorder
.TopBottom();
4387 nsContainerFrame
* container
= do_QueryFrame(this);
4388 NS_ASSERTION(container
, "Abs-pos children only supported on container frames for now");
4390 nsRect
containingBlock(0, 0, containingBlockWidth
, containingBlockHeight
);
4391 absoluteContainer
->Reflow(container
, aPresContext
, aReflowState
, aStatus
,
4393 aConstrainHeight
, true, true, // XXX could be optimized
4394 &aDesiredSize
.mOverflowAreas
);
4399 nsFrame::PushDirtyBitToAbsoluteFrames()
4401 if (!(GetStateBits() & NS_FRAME_IS_DIRTY
)) {
4402 return; // No dirty bit to push.
4404 if (!HasAbsolutelyPositionedChildren()) {
4405 return; // No absolute children to push to.
4407 GetAbsoluteContainingBlock()->MarkAllFramesDirty();
4411 nsFrame::CanContinueTextRun() const
4413 // By default, a frame will *not* allow a text run to be continued
4419 nsFrame::Reflow(nsPresContext
* aPresContext
,
4420 nsHTMLReflowMetrics
& aDesiredSize
,
4421 const nsHTMLReflowState
& aReflowState
,
4422 nsReflowStatus
& aStatus
)
4424 DO_GLOBAL_REFLOW_COUNT("nsFrame");
4425 aDesiredSize
.ClearSize();
4426 aStatus
= NS_FRAME_COMPLETE
;
4427 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
4431 nsFrame::CharacterDataChanged(CharacterDataChangeInfo
* aInfo
)
4433 NS_NOTREACHED("should only be called for text frames");
4438 nsFrame::AttributeChanged(int32_t aNameSpaceID
,
4439 nsIAtom
* aAttribute
,
4445 // Flow member functions
4448 nsFrame::GetSplittableType() const
4450 return NS_FRAME_NOT_SPLITTABLE
;
4453 nsIFrame
* nsFrame::GetPrevContinuation() const
4459 nsFrame::SetPrevContinuation(nsIFrame
* aPrevContinuation
)
4461 MOZ_ASSERT(false, "not splittable");
4464 nsIFrame
* nsFrame::GetNextContinuation() const
4470 nsFrame::SetNextContinuation(nsIFrame
*)
4472 MOZ_ASSERT(false, "not splittable");
4475 nsIFrame
* nsFrame::GetPrevInFlowVirtual() const
4481 nsFrame::SetPrevInFlow(nsIFrame
* aPrevInFlow
)
4483 MOZ_ASSERT(false, "not splittable");
4486 nsIFrame
* nsFrame::GetNextInFlowVirtual() const
4492 nsFrame::SetNextInFlow(nsIFrame
*)
4494 MOZ_ASSERT(false, "not splittable");
4497 nsIFrame
* nsIFrame::GetTailContinuation()
4499 nsIFrame
* frame
= this;
4500 while (frame
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
) {
4501 frame
= frame
->GetPrevContinuation();
4502 NS_ASSERTION(frame
, "first continuation can't be overflow container");
4504 for (nsIFrame
* next
= frame
->GetNextContinuation();
4505 next
&& !(next
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
);
4506 next
= frame
->GetNextContinuation()) {
4509 NS_POSTCONDITION(frame
, "illegal state in continuation chain.");
4513 NS_DECLARE_FRAME_PROPERTY(ViewProperty
, nullptr)
4515 // Associated view object
4517 nsIFrame::GetView() const
4519 // Check the frame state bit and see if the frame has a view
4520 if (!(GetStateBits() & NS_FRAME_HAS_VIEW
))
4523 // Check for a property on the frame
4524 void* value
= Properties().Get(ViewProperty());
4525 NS_ASSERTION(value
, "frame state bit was set but frame has no view");
4526 return static_cast<nsView
*>(value
);
4529 /* virtual */ nsView
*
4530 nsIFrame::GetViewExternal() const
4536 nsIFrame::SetView(nsView
* aView
)
4539 aView
->SetFrame(this);
4542 nsIAtom
* frameType
= GetType();
4543 NS_ASSERTION(frameType
== nsGkAtoms::scrollFrame
||
4544 frameType
== nsGkAtoms::subDocumentFrame
||
4545 frameType
== nsGkAtoms::listControlFrame
||
4546 frameType
== nsGkAtoms::objectFrame
||
4547 frameType
== nsGkAtoms::viewportFrame
||
4548 frameType
== nsGkAtoms::menuPopupFrame
,
4549 "Only specific frame types can have an nsView");
4552 // Set a property on the frame
4553 Properties().Set(ViewProperty(), aView
);
4555 // Set the frame state bit that says the frame has a view
4556 AddStateBits(NS_FRAME_HAS_VIEW
);
4558 // Let all of the ancestors know they have a descendant with a view.
4559 for (nsIFrame
* f
= GetParent();
4560 f
&& !(f
->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW
);
4562 f
->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW
);
4568 nsIFrame
* nsIFrame::GetAncestorWithViewExternal() const
4570 return GetAncestorWithView();
4573 // Find the first geometric parent that has a view
4574 nsIFrame
* nsIFrame::GetAncestorWithView() const
4576 for (nsIFrame
* f
= GetParent(); nullptr != f
; f
= f
->GetParent()) {
4585 nsPoint
nsIFrame::GetOffsetToExternal(const nsIFrame
* aOther
) const
4587 return GetOffsetTo(aOther
);
4590 nsPoint
nsIFrame::GetOffsetTo(const nsIFrame
* aOther
) const
4592 NS_PRECONDITION(aOther
,
4593 "Must have frame for destination coordinate system!");
4595 NS_ASSERTION(PresContext() == aOther
->PresContext(),
4596 "GetOffsetTo called on frames in different documents");
4598 nsPoint
offset(0, 0);
4600 for (f
= this; f
!= aOther
&& f
; f
= f
->GetParent()) {
4601 offset
+= f
->GetPosition();
4605 // Looks like aOther wasn't an ancestor of |this|. So now we have
4606 // the root-frame-relative position of |this| in |offset|. Convert back
4607 // to the coordinates of aOther
4609 offset
-= aOther
->GetPosition();
4610 aOther
= aOther
->GetParent();
4617 nsPoint
nsIFrame::GetOffsetToCrossDoc(const nsIFrame
* aOther
) const
4619 return GetOffsetToCrossDoc(aOther
, PresContext()->AppUnitsPerDevPixel());
4623 nsIFrame::GetOffsetToCrossDoc(const nsIFrame
* aOther
, const int32_t aAPD
) const
4625 NS_PRECONDITION(aOther
,
4626 "Must have frame for destination coordinate system!");
4627 NS_ASSERTION(PresContext()->GetRootPresContext() ==
4628 aOther
->PresContext()->GetRootPresContext(),
4629 "trying to get the offset between frames in different document "
4631 if (PresContext()->GetRootPresContext() !=
4632 aOther
->PresContext()->GetRootPresContext()) {
4633 // crash right away, we are almost certainly going to crash anyway.
4634 NS_RUNTIMEABORT("trying to get the offset between frames in different "
4635 "document hierarchies?");
4638 const nsIFrame
* root
= nullptr;
4639 // offset will hold the final offset
4640 // docOffset holds the currently accumulated offset at the current APD, it
4641 // will be converted and added to offset when the current APD changes.
4642 nsPoint
offset(0, 0), docOffset(0, 0);
4643 const nsIFrame
* f
= this;
4644 int32_t currAPD
= PresContext()->AppUnitsPerDevPixel();
4645 while (f
&& f
!= aOther
) {
4646 docOffset
+= f
->GetPosition();
4647 nsIFrame
* parent
= f
->GetParent();
4651 nsPoint
newOffset(0, 0);
4653 f
= nsLayoutUtils::GetCrossDocParentFrame(f
, &newOffset
);
4654 int32_t newAPD
= f
? f
->PresContext()->AppUnitsPerDevPixel() : 0;
4655 if (!f
|| newAPD
!= currAPD
) {
4656 // Convert docOffset to the right APD and add it to offset.
4657 offset
+= docOffset
.ConvertAppUnits(currAPD
, aAPD
);
4658 docOffset
.x
= docOffset
.y
= 0;
4661 docOffset
+= newOffset
;
4665 offset
+= docOffset
.ConvertAppUnits(currAPD
, aAPD
);
4667 // Looks like aOther wasn't an ancestor of |this|. So now we have
4668 // the root-document-relative position of |this| in |offset|. Subtract the
4669 // root-document-relative position of |aOther| from |offset|.
4670 // This call won't try to recurse again because root is an ancestor of
4672 nsPoint negOffset
= aOther
->GetOffsetToCrossDoc(root
, aAPD
);
4673 offset
-= negOffset
;
4680 nsIntRect
nsIFrame::GetScreenRectExternal() const
4682 return GetScreenRect();
4685 nsIntRect
nsIFrame::GetScreenRect() const
4687 return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel());
4691 nsRect
nsIFrame::GetScreenRectInAppUnitsExternal() const
4693 return GetScreenRectInAppUnits();
4696 nsRect
nsIFrame::GetScreenRectInAppUnits() const
4698 nsPresContext
* presContext
= PresContext();
4699 nsIFrame
* rootFrame
=
4700 presContext
->PresShell()->FrameManager()->GetRootFrame();
4701 nsPoint
rootScreenPos(0, 0);
4702 nsPoint
rootFrameOffsetInParent(0, 0);
4703 nsIFrame
* rootFrameParent
=
4704 nsLayoutUtils::GetCrossDocParentFrame(rootFrame
, &rootFrameOffsetInParent
);
4705 if (rootFrameParent
) {
4706 nsRect parentScreenRectAppUnits
= rootFrameParent
->GetScreenRectInAppUnits();
4707 nsPresContext
* parentPresContext
= rootFrameParent
->PresContext();
4708 double parentScale
= double(presContext
->AppUnitsPerDevPixel())/
4709 parentPresContext
->AppUnitsPerDevPixel();
4710 nsPoint rootPt
= parentScreenRectAppUnits
.TopLeft() + rootFrameOffsetInParent
;
4711 rootScreenPos
.x
= NS_round(parentScale
*rootPt
.x
);
4712 rootScreenPos
.y
= NS_round(parentScale
*rootPt
.y
);
4714 nsCOMPtr
<nsIWidget
> rootWidget
;
4715 presContext
->PresShell()->GetViewManager()->GetRootWidget(getter_AddRefs(rootWidget
));
4717 nsIntPoint rootDevPx
= rootWidget
->WidgetToScreenOffset();
4718 rootScreenPos
.x
= presContext
->DevPixelsToAppUnits(rootDevPx
.x
);
4719 rootScreenPos
.y
= presContext
->DevPixelsToAppUnits(rootDevPx
.y
);
4723 return nsRect(rootScreenPos
+ GetOffsetTo(rootFrame
), GetSize());
4726 // Returns the offset from this frame to the closest geometric parent that
4727 // has a view. Also returns the containing view or null in case of error
4729 nsIFrame::GetOffsetFromView(nsPoint
& aOffset
, nsView
** aView
) const
4731 NS_PRECONDITION(nullptr != aView
, "null OUT parameter pointer");
4732 nsIFrame
* frame
= const_cast<nsIFrame
*>(this);
4735 aOffset
.MoveTo(0, 0);
4737 aOffset
+= frame
->GetPosition();
4738 frame
= frame
->GetParent();
4739 } while (frame
&& !frame
->HasView());
4742 *aView
= frame
->GetView();
4747 nsIFrame::GetNearestWidget() const
4749 return GetClosestView()->GetNearestWidget(nullptr);
4753 nsIFrame::GetNearestWidget(nsPoint
& aOffset
) const
4755 nsPoint offsetToView
;
4756 nsPoint offsetToWidget
;
4758 GetClosestView(&offsetToView
)->GetNearestWidget(&offsetToWidget
);
4759 aOffset
= offsetToView
+ offsetToWidget
;
4764 nsFrame::GetType() const
4770 nsIFrame::IsLeaf() const
4776 nsIFrame::GetTransformMatrix(const nsIFrame
* aStopAtAncestor
,
4777 nsIFrame
** aOutAncestor
)
4779 NS_PRECONDITION(aOutAncestor
, "Need a place to put the ancestor!");
4781 /* If we're transformed, we want to hand back the combination
4782 * transform/translate matrix that will apply our current transform, then
4783 * shift us to our parent.
4785 if (IsTransformed()) {
4786 /* Compute the delta to the parent, which we need because we are converting
4787 * coordinates to our parent.
4789 NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
4790 "Cannot transform the viewport frame!");
4791 int32_t scaleFactor
= PresContext()->AppUnitsPerDevPixel();
4793 Matrix4x4 result
= ToMatrix4x4(
4794 nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0), scaleFactor
, nullptr, aOutAncestor
));
4795 // XXXjwatt: seems like this will double count offsets in the face of preserve-3d:
4796 nsPoint delta
= GetOffsetToCrossDoc(*aOutAncestor
);
4797 /* Combine the raw transform with a translation to our parent. */
4798 result
= result
* Matrix4x4().Translate
4799 (NSAppUnitsToFloatPixels(delta
.x
, scaleFactor
),
4800 NSAppUnitsToFloatPixels(delta
.y
, scaleFactor
),
4805 if (nsLayoutUtils::IsPopup(this) &&
4806 GetType() == nsGkAtoms::listControlFrame
) {
4807 nsPresContext
* presContext
= PresContext();
4808 nsIFrame
* docRootFrame
= presContext
->PresShell()->GetRootFrame();
4810 // Compute a matrix that transforms from the popup widget to the toplevel
4811 // widget. We use the widgets because they're the simplest and most
4812 // accurate approach --- this should work no matter how the widget position
4814 nsIWidget
* widget
= GetView()->GetWidget();
4815 nsPresContext
* rootPresContext
= PresContext()->GetRootPresContext();
4816 // Maybe the widget hasn't been created yet? Popups without widgets are
4817 // treated as regular frames. That should work since they'll be rendered
4818 // as part of the page if they're rendered at all.
4819 if (widget
&& rootPresContext
) {
4820 nsIWidget
* toplevel
= rootPresContext
->GetNearestWidget();
4822 nsIntRect screenBounds
;
4823 widget
->GetClientBounds(screenBounds
);
4824 nsIntRect toplevelScreenBounds
;
4825 toplevel
->GetClientBounds(toplevelScreenBounds
);
4826 nsIntPoint translation
= screenBounds
.TopLeft() - toplevelScreenBounds
.TopLeft();
4828 Matrix4x4 transformToTop
;
4829 transformToTop
._41
= translation
.x
;
4830 transformToTop
._42
= translation
.y
;
4832 *aOutAncestor
= docRootFrame
;
4833 Matrix4x4 docRootTransformToTop
=
4834 nsLayoutUtils::GetTransformToAncestor(docRootFrame
, nullptr);
4835 if (docRootTransformToTop
.IsSingular()) {
4836 NS_WARNING("Containing document is invisible, we can't compute a valid transform");
4838 docRootTransformToTop
.Invert();
4839 return transformToTop
* docRootTransformToTop
;
4845 *aOutAncestor
= nsLayoutUtils::GetCrossDocParentFrame(this);
4847 /* Otherwise, we're not transformed. In that case, we'll walk up the frame
4848 * tree until we either hit the root frame or something that may be
4849 * transformed. We'll then change coordinates into that frame, since we're
4850 * guaranteed that nothing in-between can be transformed. First, however,
4851 * we have to check to see if we have a parent. If not, we'll set the
4852 * outparam to null (indicating that there's nothing left) and will hand back
4853 * the identity matrix.
4858 /* Keep iterating while the frame can't possibly be transformed. */
4859 while (!(*aOutAncestor
)->IsTransformed() &&
4860 !nsLayoutUtils::IsPopup(*aOutAncestor
) &&
4861 *aOutAncestor
!= aStopAtAncestor
) {
4862 /* If no parent, stop iterating. Otherwise, update the ancestor. */
4863 nsIFrame
* parent
= nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor
);
4867 *aOutAncestor
= parent
;
4870 NS_ASSERTION(*aOutAncestor
, "Somehow ended up with a null ancestor...?");
4872 /* Translate from this frame to our ancestor, if it exists. That's the
4873 * entire transform, so we're done.
4875 nsPoint delta
= GetOffsetToCrossDoc(*aOutAncestor
);
4876 int32_t scaleFactor
= PresContext()->AppUnitsPerDevPixel();
4877 return Matrix4x4().Translate
4878 (NSAppUnitsToFloatPixels(delta
.x
, scaleFactor
),
4879 NSAppUnitsToFloatPixels(delta
.y
, scaleFactor
),
4883 static void InvalidateFrameInternal(nsIFrame
*aFrame
, bool aHasDisplayItem
= true)
4885 if (aHasDisplayItem
) {
4886 aFrame
->AddStateBits(NS_FRAME_NEEDS_PAINT
);
4888 nsSVGEffects::InvalidateDirectRenderingObservers(aFrame
);
4889 bool needsSchedulePaint
= false;
4890 if (nsLayoutUtils::IsPopup(aFrame
)) {
4891 needsSchedulePaint
= true;
4893 nsIFrame
*parent
= nsLayoutUtils::GetCrossDocParentFrame(aFrame
);
4894 while (parent
&& !parent
->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT
)) {
4895 if (aHasDisplayItem
) {
4896 parent
->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT
);
4898 nsSVGEffects::InvalidateDirectRenderingObservers(parent
);
4900 // If we're inside a popup, then we need to make sure that we
4901 // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
4902 // flag gets added to the popup display root frame.
4903 if (nsLayoutUtils::IsPopup(parent
)) {
4904 needsSchedulePaint
= true;
4907 parent
= nsLayoutUtils::GetCrossDocParentFrame(parent
);
4910 needsSchedulePaint
= true;
4913 if (!aHasDisplayItem
) {
4916 if (needsSchedulePaint
) {
4917 aFrame
->SchedulePaint();
4919 if (aFrame
->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT
)) {
4920 aFrame
->Properties().Delete(nsIFrame::InvalidationRect());
4921 aFrame
->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT
);
4926 nsIFrame::InvalidateFrameSubtree(uint32_t aDisplayItemKey
)
4928 bool hasDisplayItem
=
4929 !aDisplayItemKey
|| FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey
);
4930 InvalidateFrame(aDisplayItemKey
);
4932 if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT
) || !hasDisplayItem
) {
4936 AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT
);
4938 nsAutoTArray
<nsIFrame::ChildList
,4> childListArray
;
4939 GetCrossDocChildLists(&childListArray
);
4941 nsIFrame::ChildListArrayIterator
lists(childListArray
);
4942 for (; !lists
.IsDone(); lists
.Next()) {
4943 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
4944 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
4945 childFrames
.get()->InvalidateFrameSubtree();
4951 nsIFrame::ClearInvalidationStateBits()
4953 if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT
)) {
4954 nsAutoTArray
<nsIFrame::ChildList
,4> childListArray
;
4955 GetCrossDocChildLists(&childListArray
);
4957 nsIFrame::ChildListArrayIterator
lists(childListArray
);
4958 for (; !lists
.IsDone(); lists
.Next()) {
4959 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
4960 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
4961 childFrames
.get()->ClearInvalidationStateBits();
4966 RemoveStateBits(NS_FRAME_NEEDS_PAINT
|
4967 NS_FRAME_DESCENDANT_NEEDS_PAINT
|
4968 NS_FRAME_ALL_DESCENDANTS_NEED_PAINT
);
4972 nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey
)
4974 bool hasDisplayItem
=
4975 !aDisplayItemKey
|| FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey
);
4976 InvalidateFrameInternal(this, hasDisplayItem
);
4980 nsIFrame::InvalidateFrameWithRect(const nsRect
& aRect
, uint32_t aDisplayItemKey
)
4982 bool hasDisplayItem
=
4983 !aDisplayItemKey
|| FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey
);
4984 bool alreadyInvalid
= false;
4985 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT
)) {
4986 InvalidateFrameInternal(this, hasDisplayItem
);
4988 alreadyInvalid
= true;
4991 if (!hasDisplayItem
) {
4995 nsRect
*rect
= static_cast<nsRect
*>(Properties().Get(InvalidationRect()));
4997 if (alreadyInvalid
) {
5000 rect
= new nsRect();
5001 Properties().Set(InvalidationRect(), rect
);
5002 AddStateBits(NS_FRAME_HAS_INVALID_RECT
);
5005 *rect
= rect
->Union(aRect
);
5008 /*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey
;
5011 nsIFrame::TryUpdateTransformOnly(Layer
** aLayerResult
)
5013 Layer
* layer
= FrameLayerBuilder::GetDedicatedLayer(
5014 this, nsDisplayItem::TYPE_TRANSFORM
);
5015 if (!layer
|| !layer
->HasUserData(LayerIsPrerenderedDataKey())) {
5016 // If this layer isn't prerendered or we clip composites to our OS
5017 // window, then we can't correctly optimize to an empty
5018 // transaction in general.
5022 gfx::Matrix4x4 transform3d
;
5023 if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d
)) {
5024 // We're not able to compute a layer transform that we know would
5025 // be used at the next layers transaction, so we can't only update
5026 // the transform and will need to schedule an invalidating paint.
5029 gfx::Matrix transform
;
5030 gfx::Matrix previousTransform
;
5031 // FIXME/bug 796690 and 796705: in general, changes to 3D
5032 // transforms, or transform changes to properties other than
5033 // translation, may lead us to choose a different rendering
5034 // resolution for our layer. So if the transform is 3D or has a
5035 // non-translation change, bail and schedule an invalidating paint.
5036 // (We can often do better than this, for example for scale-down
5038 static const gfx::Float kError
= 0.0001f
;
5039 if (!transform3d
.Is2D(&transform
) ||
5040 !layer
->GetBaseTransform().Is2D(&previousTransform
) ||
5041 !gfx::FuzzyEqual(transform
._11
, previousTransform
._11
, kError
) ||
5042 !gfx::FuzzyEqual(transform
._22
, previousTransform
._22
, kError
) ||
5043 !gfx::FuzzyEqual(transform
._21
, previousTransform
._21
, kError
) ||
5044 !gfx::FuzzyEqual(transform
._12
, previousTransform
._12
, kError
)) {
5047 layer
->SetBaseTransformForNextTransaction(transform3d
);
5048 *aLayerResult
= layer
;
5053 nsIFrame::IsInvalid(nsRect
& aRect
)
5055 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT
)) {
5059 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT
)) {
5060 nsRect
*rect
= static_cast<nsRect
*>(Properties().Get(InvalidationRect()));
5061 NS_ASSERTION(rect
, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
5070 nsIFrame::SchedulePaint(PaintType aType
)
5072 nsIFrame
*displayRoot
= nsLayoutUtils::GetDisplayRootFrame(this);
5073 nsPresContext
*pres
= displayRoot
->PresContext()->GetRootPresContext();
5075 // No need to schedule a paint for an external document since they aren't
5076 // painted directly.
5077 if (!pres
|| (pres
->Document() && pres
->Document()->IsResourceDoc())) {
5080 if (!pres
->GetContainerWeak()) {
5081 NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
5085 pres
->PresShell()->ScheduleViewManagerFlush(aType
== PAINT_DELAYED_COMPRESS
?
5086 nsIPresShell::PAINT_DELAYED_COMPRESS
:
5087 nsIPresShell::PAINT_DEFAULT
);
5089 if (aType
== PAINT_DELAYED_COMPRESS
) {
5093 if (aType
== PAINT_DEFAULT
) {
5094 displayRoot
->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE
);
5096 nsIPresShell
* shell
= PresContext()->PresShell();
5098 shell
->AddInvalidateHiddenPresShellObserver(pres
->RefreshDriver());
5103 nsIFrame::InvalidateLayer(uint32_t aDisplayItemKey
,
5104 const nsIntRect
* aDamageRect
,
5105 uint32_t aFlags
/* = 0 */)
5107 NS_ASSERTION(aDisplayItemKey
> 0, "Need a key");
5109 Layer
* layer
= FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey
);
5111 // If the layer is being updated asynchronously, and it's being forwarded
5112 // to a compositor, then we don't need to invalidate.
5113 if ((aFlags
& UPDATE_IS_ASYNC
) && layer
&&
5114 layer
->Manager()->GetBackendType() == LayersBackend::LAYERS_CLIENT
) {
5118 if (aDamageRect
&& aDamageRect
->IsEmpty()) {
5123 // Plugins can transition from not rendering anything to rendering,
5124 // and still only call this. So always invalidate, with specifying
5125 // the display item type just in case.
5127 // In the bug 930056, dialer app startup but not shown on the
5128 // screen because sometimes we don't have any retainned data
5129 // for remote type displayitem and thus Repaint event is not
5130 // triggered. So, always invalidate here as well.
5131 if (aDisplayItemKey
== nsDisplayItem::TYPE_PLUGIN
||
5132 aDisplayItemKey
== nsDisplayItem::TYPE_REMOTE
) {
5135 InvalidateFrame(aDisplayItemKey
);
5141 layer
->AddInvalidRect(*aDamageRect
);
5143 layer
->SetInvalidRectToVisibleRegion();
5146 SchedulePaint(PAINT_COMPOSITE_ONLY
);
5151 ComputeEffectsRect(nsIFrame
* aFrame
, const nsRect
& aOverflowRect
,
5152 const nsSize
& aNewSize
)
5154 nsRect r
= aOverflowRect
;
5156 if (aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
) {
5157 // For SVG frames, we only need to account for filters.
5158 // TODO: We could also take account of clipPath and mask to reduce the
5159 // visual overflow, but that's not essential.
5160 if (aFrame
->StyleSVGReset()->HasFilters()) {
5161 aFrame
->Properties().
5162 Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r
));
5163 r
= nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame
, aOverflowRect
);
5169 r
.UnionRect(r
, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame
, aNewSize
));
5171 // border-image-outset.
5172 // We need to include border-image-outset because it can cause the
5173 // border image to be drawn beyond the border box.
5175 // (1) It's important we not check whether there's a border-image
5176 // since the style hint for a change in border image doesn't cause
5177 // reflow, and that's probably more important than optimizing the
5178 // overflow areas for the silly case of border-image-outset without
5180 // (2) It's important that we not check whether the border-image
5181 // is actually loaded, since that would require us to reflow when
5183 const nsStyleBorder
* styleBorder
= aFrame
->StyleBorder();
5184 nsMargin outsetMargin
= styleBorder
->GetImageOutset();
5186 if (outsetMargin
!= nsMargin(0, 0, 0, 0)) {
5187 nsRect
outsetRect(nsPoint(0, 0), aNewSize
);
5188 outsetRect
.Inflate(outsetMargin
);
5189 r
.UnionRect(r
, outsetRect
);
5192 // Note that we don't remove the outlineInnerRect if a frame loses outline
5193 // style. That would require an extra property lookup for every frame,
5194 // or a new frame state bit to track whether a property had been stored,
5195 // or something like that. It's not worth doing that here. At most it's
5196 // only one heap-allocated rect per frame and it will be cleaned up when
5199 if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame
)) {
5200 aFrame
->Properties().
5201 Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r
));
5202 r
= nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame
, r
);
5209 nsIFrame::MovePositionBy(const nsPoint
& aTranslation
)
5211 nsPoint position
= GetNormalPosition() + aTranslation
;
5213 const nsMargin
* computedOffsets
= nullptr;
5214 if (IsRelativelyPositioned()) {
5215 computedOffsets
= static_cast<nsMargin
*>
5216 (Properties().Get(nsIFrame::ComputedOffsetProperty()));
5218 nsHTMLReflowState::ApplyRelativePositioning(this, computedOffsets
?
5219 *computedOffsets
: nsMargin(),
5221 SetPosition(position
);
5225 nsIFrame::GetNormalPosition() const
5227 // It might be faster to first check
5228 // StyleDisplay()->IsRelativelyPositionedStyle().
5229 nsPoint
* normalPosition
= static_cast<nsPoint
*>
5230 (Properties().Get(NormalPositionProperty()));
5231 if (normalPosition
) {
5232 return *normalPosition
;
5234 return GetPosition();
5238 nsIFrame::GetPositionIgnoringScrolling()
5240 return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
5245 nsIFrame::GetOverflowRect(nsOverflowType aType
) const
5247 NS_ABORT_IF_FALSE(aType
== eVisualOverflow
|| aType
== eScrollableOverflow
,
5250 // Note that in some cases the overflow area might not have been
5251 // updated (yet) to reflect any outline set on the frame or the area
5252 // of child frames. That's OK because any reflow that updates these
5253 // areas will invalidate the appropriate area, so any (mis)uses of
5254 // this method will be fixed up.
5256 if (mOverflow
.mType
== NS_FRAME_OVERFLOW_LARGE
) {
5257 // there is an overflow rect, and it's not stored as deltas but as
5258 // a separately-allocated rect
5259 return static_cast<nsOverflowAreas
*>(const_cast<nsIFrame
*>(this)->
5260 GetOverflowAreasProperty())->Overflow(aType
);
5263 if (aType
== eVisualOverflow
&&
5264 mOverflow
.mType
!= NS_FRAME_OVERFLOW_NONE
) {
5265 return GetVisualOverflowFromDeltas();
5268 return nsRect(nsPoint(0, 0), GetSize());
5272 nsIFrame::GetOverflowAreas() const
5274 if (mOverflow
.mType
== NS_FRAME_OVERFLOW_LARGE
) {
5275 // there is an overflow rect, and it's not stored as deltas but as
5276 // a separately-allocated rect
5277 return *const_cast<nsIFrame
*>(this)->GetOverflowAreasProperty();
5280 return nsOverflowAreas(GetVisualOverflowFromDeltas(),
5281 nsRect(nsPoint(0, 0), GetSize()));
5285 nsIFrame::GetOverflowAreasRelativeToSelf() const
5287 if (IsTransformed()) {
5288 nsOverflowAreas
* preTransformOverflows
= static_cast<nsOverflowAreas
*>
5289 (Properties().Get(PreTransformOverflowAreasProperty()));
5290 if (preTransformOverflows
) {
5291 return nsOverflowAreas(preTransformOverflows
->VisualOverflow(),
5292 preTransformOverflows
->ScrollableOverflow());
5295 return nsOverflowAreas(GetVisualOverflowRect(),
5296 GetScrollableOverflowRect());
5300 nsIFrame::GetScrollableOverflowRectRelativeToParent() const
5302 return GetScrollableOverflowRect() + mRect
.TopLeft();
5306 nsIFrame::GetVisualOverflowRectRelativeToParent() const
5308 return GetVisualOverflowRect() + mRect
.TopLeft();
5312 nsIFrame::GetScrollableOverflowRectRelativeToSelf() const
5314 if (IsTransformed()) {
5315 nsOverflowAreas
* preTransformOverflows
= static_cast<nsOverflowAreas
*>
5316 (Properties().Get(PreTransformOverflowAreasProperty()));
5317 if (preTransformOverflows
)
5318 return preTransformOverflows
->ScrollableOverflow();
5320 return GetScrollableOverflowRect();
5324 nsIFrame::GetVisualOverflowRectRelativeToSelf() const
5326 if (IsTransformed()) {
5327 nsOverflowAreas
* preTransformOverflows
= static_cast<nsOverflowAreas
*>
5328 (Properties().Get(PreTransformOverflowAreasProperty()));
5329 if (preTransformOverflows
)
5330 return preTransformOverflows
->VisualOverflow();
5332 return GetVisualOverflowRect();
5336 nsIFrame::GetPreEffectsVisualOverflowRect() const
5338 nsRect
* r
= static_cast<nsRect
*>
5339 (Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
5340 return r
? *r
: GetVisualOverflowRectRelativeToSelf();
5344 FrameMaintainsOverflow(nsIFrame
* aFrame
)
5346 return (aFrame
->GetStateBits() &
5347 (NS_FRAME_SVG_LAYOUT
| NS_FRAME_IS_NONDISPLAY
)) !=
5348 (NS_FRAME_SVG_LAYOUT
| NS_FRAME_IS_NONDISPLAY
);
5352 nsFrame::UpdateOverflow()
5354 MOZ_ASSERT(FrameMaintainsOverflow(this),
5355 "Non-display SVG do not maintain visual overflow rects");
5357 nsRect
rect(nsPoint(0, 0), GetSize());
5358 nsOverflowAreas
overflowAreas(rect
, rect
);
5360 if (!DoesClipChildren() &&
5361 !(IsCollapsed() && (IsBoxFrame() || ::IsBoxWrapped(this)))) {
5362 nsLayoutUtils::UnionChildOverflow(this, overflowAreas
);
5365 if (FinishAndStoreOverflow(overflowAreas
, GetSize())) {
5366 nsView
* view
= GetView();
5369 GetLayoutFlags(flags
);
5371 if ((flags
& NS_FRAME_NO_SIZE_VIEW
) == 0) {
5372 // Make sure the frame's view is properly sized.
5373 nsViewManager
* vm
= view
->GetViewManager();
5374 vm
->ResizeView(view
, overflowAreas
.VisualOverflow(), true);
5384 // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus
5385 // 4 for the frames above the document's frames:
5386 // the Viewport, GFXScroll, ScrollPort, and Canvas
5387 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH+4)
5390 nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState
& aReflowState
,
5391 nsHTMLReflowMetrics
& aMetrics
,
5392 nsReflowStatus
& aStatus
)
5394 if (aReflowState
.mReflowDepth
> MAX_FRAME_DEPTH
) {
5395 NS_WARNING("frame tree too deep; setting zero size and returning");
5396 mState
|= NS_FRAME_TOO_DEEP_IN_FRAME_TREE
;
5397 ClearOverflowRects();
5398 aMetrics
.ClearSize();
5399 aMetrics
.SetBlockStartAscent(0);
5400 aMetrics
.mCarriedOutBottomMargin
.Zero();
5401 aMetrics
.mOverflowAreas
.Clear();
5403 if (GetNextInFlow()) {
5404 // Reflow depth might vary between reflows, so we might have
5405 // successfully reflowed and split this frame before. If so, we
5406 // shouldn't delete its continuations.
5407 aStatus
= NS_FRAME_NOT_COMPLETE
;
5409 aStatus
= NS_FRAME_COMPLETE
;
5414 mState
&= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE
;
5419 nsIFrame::IsBlockWrapper() const
5421 nsIAtom
*pseudoType
= StyleContext()->GetPseudo();
5422 return (pseudoType
== nsCSSAnonBoxes::mozAnonymousBlock
||
5423 pseudoType
== nsCSSAnonBoxes::mozAnonymousPositionedBlock
||
5424 pseudoType
== nsCSSAnonBoxes::buttonContent
||
5425 pseudoType
== nsCSSAnonBoxes::cellContent
);
5429 GetNearestBlockContainer(nsIFrame
* frame
)
5431 // The block wrappers we use to wrap blocks inside inlines aren't
5432 // described in the CSS spec. We need to make them not be containing
5434 // Since the parent of such a block is either a normal block or
5435 // another such pseudo, this shouldn't cause anything bad to happen.
5436 // Also the anonymous blocks inside table cells are not containing blocks.
5437 while (frame
->IsFrameOfType(nsIFrame::eLineParticipant
) ||
5438 frame
->IsBlockWrapper() ||
5439 // Table rows are not containing blocks either
5440 frame
->GetType() == nsGkAtoms::tableRowFrame
) {
5441 frame
= frame
->GetParent();
5442 NS_ASSERTION(frame
, "How come we got to the root frame without seeing a containing block?");
5448 nsIFrame::GetContainingBlock() const
5450 // MathML frames might have absolute positioning style, but they would
5451 // still be in-flow. So we have to check to make sure that the frame
5452 // is really out-of-flow too.
5453 if (IsAbsolutelyPositioned() &&
5454 (GetStateBits() & NS_FRAME_OUT_OF_FLOW
)) {
5455 return GetParent(); // the parent is always the containing block
5457 return GetNearestBlockContainer(GetParent());
5460 #ifdef DEBUG_FRAME_DUMP
5462 int32_t nsFrame::ContentIndexInContainer(const nsIFrame
* aFrame
)
5464 int32_t result
= -1;
5466 nsIContent
* content
= aFrame
->GetContent();
5468 nsIContent
* parentContent
= content
->GetParent();
5469 if (parentContent
) {
5470 result
= parentContent
->IndexOf(content
);
5478 * List a frame tree to stderr. Meant to be called from gdb.
5481 DebugListFrameTree(nsIFrame
* aFrame
)
5483 ((nsFrame
*)aFrame
)->List(stderr
);
5487 nsIFrame::ListTag(nsACString
& aTo
) const
5494 nsIFrame::ListTag(nsACString
& aTo
, const nsIFrame
* aFrame
) {
5496 aFrame
->GetFrameName(tmp
);
5497 aTo
+= NS_ConvertUTF16toUTF8(tmp
).get();
5498 aTo
+= nsPrintfCString("@%p", static_cast<const void*>(aFrame
));
5503 nsIFrame::ListGeneric(nsACString
& aTo
, const char* aPrefix
, uint32_t aFlags
) const
5508 aTo
+= nsPrintfCString(" [view=%p]", static_cast<void*>(GetView()));
5510 if (GetNextSibling()) {
5511 aTo
+= nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
5513 if (GetPrevContinuation()) {
5514 bool fluid
= GetPrevInFlow() == GetPrevContinuation();
5515 aTo
+= nsPrintfCString(" prev-%s=%p", fluid
?"in-flow":"continuation",
5516 static_cast<void*>(GetPrevContinuation()));
5518 if (GetNextContinuation()) {
5519 bool fluid
= GetNextInFlow() == GetNextContinuation();
5520 aTo
+= nsPrintfCString(" next-%s=%p", fluid
?"in-flow":"continuation",
5521 static_cast<void*>(GetNextContinuation()));
5523 void* IBsibling
= Properties().Get(IBSplitSibling());
5525 aTo
+= nsPrintfCString(" IBSplitSibling=%p", IBsibling
);
5527 void* IBprevsibling
= Properties().Get(IBSplitPrevSibling());
5528 if (IBprevsibling
) {
5529 aTo
+= nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling
);
5531 aTo
+= nsPrintfCString(" {%d,%d,%d,%d}", mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
);
5532 nsIFrame
* f
= const_cast<nsIFrame
*>(this);
5533 if (f
->HasOverflowAreas()) {
5534 nsRect vo
= f
->GetVisualOverflowRect();
5535 if (!vo
.IsEqualEdges(mRect
)) {
5536 aTo
+= nsPrintfCString(" vis-overflow=%d,%d,%d,%d", vo
.x
, vo
.y
, vo
.width
, vo
.height
);
5538 nsRect so
= f
->GetScrollableOverflowRect();
5539 if (!so
.IsEqualEdges(mRect
)) {
5540 aTo
+= nsPrintfCString(" scr-overflow=%d,%d,%d,%d", so
.x
, so
.y
, so
.width
, so
.height
);
5544 aTo
+= nsPrintfCString(" [state=%016llx]", (unsigned long long)mState
);
5546 if (IsTransformed()) {
5547 aTo
+= nsPrintfCString(" transformed");
5549 if (ChildrenHavePerspective()) {
5550 aTo
+= nsPrintfCString(" perspective");
5552 if (Preserves3DChildren()) {
5553 aTo
+= nsPrintfCString(" preserves-3d-children");
5555 if (Preserves3D()) {
5556 aTo
+= nsPrintfCString(" preserves-3d");
5559 aTo
+= nsPrintfCString(" [content=%p]", static_cast<void*>(mContent
));
5561 aTo
+= nsPrintfCString(" [sc=%p", static_cast<void*>(mStyleContext
));
5562 if (mStyleContext
) {
5563 nsIAtom
* pseudoTag
= mStyleContext
->GetPseudo();
5565 nsAutoString atomString
;
5566 pseudoTag
->ToString(atomString
);
5567 aTo
+= nsPrintfCString("%s", NS_LossyConvertUTF16toASCII(atomString
).get());
5569 if (GetParent() && mStyleContext
->GetParent() != GetParent()->StyleContext()) {
5570 aTo
+= nsPrintfCString(",parent=%p", mStyleContext
->GetParent());
5577 nsIFrame::List(FILE* out
, const char* aPrefix
, uint32_t aFlags
) const
5580 ListGeneric(str
, aPrefix
, aFlags
);
5581 fprintf_stderr(out
, "%s\n", str
.get());
5585 nsFrame::GetFrameName(nsAString
& aResult
) const
5587 return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult
);
5591 nsFrame::MakeFrameName(const nsAString
& aType
, nsAString
& aResult
) const
5594 if (mContent
&& !mContent
->IsNodeOfType(nsINode::eTEXT
)) {
5596 mContent
->Tag()->ToString(buf
);
5597 if (GetType() == nsGkAtoms::subDocumentFrame
) {
5599 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::src
, src
);
5600 buf
.AppendLiteral(" src=");
5603 aResult
.Append('(');
5604 aResult
.Append(buf
);
5605 aResult
.Append(')');
5608 PR_snprintf(buf
, sizeof(buf
), "(%d)", ContentIndexInContainer(this));
5609 AppendASCIItoUTF16(buf
, aResult
);
5614 nsIFrame::DumpFrameTree()
5616 RootFrameList(PresContext(), stderr
);
5620 nsIFrame::DumpFrameTreeLimited()
5626 nsIFrame::RootFrameList(nsPresContext
* aPresContext
, FILE* out
, const char* aPrefix
)
5628 if (!aPresContext
|| !out
)
5631 nsIPresShell
*shell
= aPresContext
->GetPresShell();
5633 nsIFrame
* frame
= shell
->FrameManager()->GetRootFrame();
5635 frame
->List(out
, aPrefix
);
5643 nsFrame::GetDebugStateBits() const
5645 // We'll ignore these flags for the purposes of comparing frame state:
5647 // NS_FRAME_EXTERNAL_REFERENCE
5648 // because this is set by the event state manager or the
5649 // caret code when a frame is focused. Depending on whether
5650 // or not the regression tests are run as the focused window
5651 // will make this value vary randomly.
5652 #define IRRELEVANT_FRAME_STATE_FLAGS NS_FRAME_EXTERNAL_REFERENCE
5654 #define FRAME_STATE_MASK (~(IRRELEVANT_FRAME_STATE_FLAGS))
5656 return GetStateBits() & FRAME_STATE_MASK
;
5660 nsFrame::XMLQuote(nsString
& aString
)
5662 int32_t i
, len
= aString
.Length();
5663 for (i
= 0; i
< len
; i
++) {
5664 char16_t ch
= aString
.CharAt(i
);
5666 nsAutoString
tmp(NS_LITERAL_STRING("<"));
5668 aString
.Insert(tmp
, i
);
5672 else if (ch
== '>') {
5673 nsAutoString
tmp(NS_LITERAL_STRING(">"));
5675 aString
.Insert(tmp
, i
);
5679 else if (ch
== '\"') {
5680 nsAutoString
tmp(NS_LITERAL_STRING("""));
5682 aString
.Insert(tmp
, i
);
5691 nsIFrame::IsVisibleForPainting(nsDisplayListBuilder
* aBuilder
) {
5692 if (!StyleVisibility()->IsVisible())
5694 nsISelection
* sel
= aBuilder
->GetBoundingSelection();
5695 return !sel
|| IsVisibleInSelection(sel
);
5699 nsIFrame::IsVisibleForPainting() {
5700 if (!StyleVisibility()->IsVisible())
5703 nsPresContext
* pc
= PresContext();
5704 if (!pc
->IsRenderingOnlySelection())
5707 nsCOMPtr
<nsISelectionController
> selcon(do_QueryInterface(pc
->PresShell()));
5709 nsCOMPtr
<nsISelection
> sel
;
5710 selcon
->GetSelection(nsISelectionController::SELECTION_NORMAL
,
5711 getter_AddRefs(sel
));
5713 return IsVisibleInSelection(sel
);
5719 nsIFrame::IsVisibleInSelection(nsDisplayListBuilder
* aBuilder
) {
5720 nsISelection
* sel
= aBuilder
->GetBoundingSelection();
5721 return !sel
|| IsVisibleInSelection(sel
);
5725 nsIFrame::IsVisibleOrCollapsedForPainting(nsDisplayListBuilder
* aBuilder
) {
5726 if (!StyleVisibility()->IsVisibleOrCollapsed())
5728 nsISelection
* sel
= aBuilder
->GetBoundingSelection();
5729 return !sel
|| IsVisibleInSelection(sel
);
5733 nsIFrame::IsVisibleInSelection(nsISelection
* aSelection
)
5735 if (!GetContent() || !GetContent()->IsSelectionDescendant()) {
5739 nsCOMPtr
<nsIDOMNode
> node(do_QueryInterface(mContent
));
5741 nsresult rv
= aSelection
->ContainsNode(node
, true, &vis
);
5742 return NS_FAILED(rv
) || vis
;
5752 nsIFrame::CachedIsEmpty()
5754 NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY
),
5755 "Must only be called on reflowed lines");
5760 nsFrame::IsSelfEmpty()
5766 nsFrame::GetSelectionController(nsPresContext
*aPresContext
, nsISelectionController
**aSelCon
)
5768 if (!aPresContext
|| !aSelCon
)
5769 return NS_ERROR_INVALID_ARG
;
5771 nsIFrame
*frame
= this;
5772 while (frame
&& (frame
->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION
)) {
5773 nsITextControlFrame
*tcf
= do_QueryFrame(frame
);
5775 return tcf
->GetOwnedSelectionController(aSelCon
);
5777 frame
= frame
->GetParent();
5780 return CallQueryInterface(aPresContext
->GetPresShell(), aSelCon
);
5783 already_AddRefed
<nsFrameSelection
>
5784 nsIFrame::GetFrameSelection()
5786 nsRefPtr
<nsFrameSelection
> fs
=
5787 const_cast<nsFrameSelection
*>(GetConstFrameSelection());
5791 const nsFrameSelection
*
5792 nsIFrame::GetConstFrameSelection() const
5794 nsIFrame
* frame
= const_cast<nsIFrame
*>(this);
5795 while (frame
&& (frame
->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION
)) {
5796 nsITextControlFrame
* tcf
= do_QueryFrame(frame
);
5798 return tcf
->GetOwnedFrameSelection();
5800 frame
= frame
->GetParent();
5803 return PresContext()->PresShell()->ConstFrameSelection();
5808 nsFrame::DumpRegressionData(nsPresContext
* aPresContext
, FILE* out
, int32_t aIndent
)
5810 IndentBy(out
, aIndent
);
5811 fprintf(out
, "<frame va=\"%p\" type=\"", (void*)this);
5815 fputs(NS_LossyConvertUTF16toASCII(name
).get(), out
);
5816 fprintf(out
, "\" state=\"%016llx\" parent=\"%p\">\n",
5817 (unsigned long long)GetDebugStateBits(), (void*)GetParent());
5820 DumpBaseRegressionData(aPresContext
, out
, aIndent
);
5823 IndentBy(out
, aIndent
);
5824 fprintf(out
, "</frame>\n");
5830 nsFrame::DumpBaseRegressionData(nsPresContext
* aPresContext
, FILE* out
, int32_t aIndent
)
5832 if (GetNextSibling()) {
5833 IndentBy(out
, aIndent
);
5834 fprintf(out
, "<next-sibling va=\"%p\"/>\n", (void*)GetNextSibling());
5838 IndentBy(out
, aIndent
);
5839 fprintf(out
, "<view va=\"%p\">\n", (void*)GetView());
5841 // XXX add in code to dump out view state too...
5843 IndentBy(out
, aIndent
);
5844 fprintf(out
, "</view>\n");
5847 IndentBy(out
, aIndent
);
5848 fprintf(out
, "<bbox x=\"%d\" y=\"%d\" w=\"%d\" h=\"%d\"/>\n",
5849 mRect
.x
, mRect
.y
, mRect
.width
, mRect
.height
);
5851 // Now dump all of the children on all of the child lists
5852 ChildListIterator
lists(this);
5853 for (; !lists
.IsDone(); lists
.Next()) {
5854 IndentBy(out
, aIndent
);
5855 if (lists
.CurrentID() != kPrincipalList
) {
5856 fprintf(out
, "<child-list name=\"%s\">\n", mozilla::layout::ChildListName(lists
.CurrentID()));
5859 fprintf(out
, "<child-list>\n");
5862 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
5863 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
5864 nsIFrame
* kid
= childFrames
.get();
5865 kid
->DumpRegressionData(aPresContext
, out
, aIndent
);
5868 IndentBy(out
, aIndent
);
5869 fprintf(out
, "</child-list>\n");
5875 nsIFrame::IsFrameSelected() const
5877 NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(),
5878 "use the public IsSelected() instead");
5879 return nsRange::IsNodeSelected(GetContent(), 0,
5880 GetContent()->GetChildCount());
5884 nsFrame::GetPointFromOffset(int32_t inOffset
, nsPoint
* outPoint
)
5886 NS_PRECONDITION(outPoint
!= nullptr, "Null parameter");
5887 nsRect contentRect
= GetContentRect() - GetPosition();
5888 nsPoint pt
= contentRect
.TopLeft();
5891 nsIContent
* newContent
= mContent
->GetParent();
5893 int32_t newOffset
= newContent
->IndexOf(mContent
);
5895 bool isRTL
= (NS_GET_EMBEDDING_LEVEL(this) & 1) == 1;
5896 if ((!isRTL
&& inOffset
> newOffset
) ||
5897 (isRTL
&& inOffset
<= newOffset
)) {
5898 pt
= contentRect
.TopRight();
5907 nsFrame::GetChildFrameContainingOffset(int32_t inContentOffset
, bool inHint
, int32_t* outFrameContentOffset
, nsIFrame
**outChildFrame
)
5909 NS_PRECONDITION(outChildFrame
&& outFrameContentOffset
, "Null parameter");
5910 *outFrameContentOffset
= (int32_t)inHint
;
5911 //the best frame to reflect any given offset would be a visible frame if possible
5912 //i.e. we are looking for a valid frame to place the blinking caret
5913 nsRect rect
= GetRect();
5914 if (!rect
.width
|| !rect
.height
)
5916 //if we have a 0 width or height then lets look for another frame that possibly has
5917 //the same content. If we have no frames in flow then just let us return 'this' frame
5918 nsIFrame
* nextFlow
= GetNextInFlow();
5920 return nextFlow
->GetChildFrameContainingOffset(inContentOffset
, inHint
, outFrameContentOffset
, outChildFrame
);
5922 *outChildFrame
= this;
5927 // What I've pieced together about this routine:
5928 // Starting with a block frame (from which a line frame can be gotten)
5929 // and a line number, drill down and get the first/last selectable
5930 // frame on that line, depending on aPos->mDirection.
5931 // aOutSideLimit != 0 means ignore aLineStart, instead work from
5932 // the end (if > 0) or beginning (if < 0).
5935 nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext
* aPresContext
,
5936 nsPeekOffsetStruct
*aPos
,
5937 nsIFrame
*aBlockFrame
,
5939 int8_t aOutSideLimit
5942 //magic numbers aLineStart will be -1 for end of block 0 will be start of block
5943 if (!aBlockFrame
|| !aPos
)
5944 return NS_ERROR_NULL_POINTER
;
5946 aPos
->mResultFrame
= nullptr;
5947 aPos
->mResultContent
= nullptr;
5949 aPos
->mDirection
== eDirNext
? CARET_ASSOCIATE_AFTER
: CARET_ASSOCIATE_BEFORE
;
5951 nsAutoLineIterator it
= aBlockFrame
->GetLineIterator();
5953 return NS_ERROR_FAILURE
;
5954 int32_t searchingLine
= aLineStart
;
5955 int32_t countLines
= it
->GetNumLines();
5956 if (aOutSideLimit
> 0) //start at end
5957 searchingLine
= countLines
;
5958 else if (aOutSideLimit
<0)//start at beginning
5959 searchingLine
= -1;//"next" will be 0
5961 if ((aPos
->mDirection
== eDirPrevious
&& searchingLine
== 0) ||
5962 (aPos
->mDirection
== eDirNext
&& searchingLine
>= (countLines
-1) )){
5963 //we need to jump to new block frame.
5964 return NS_ERROR_FAILURE
;
5966 int32_t lineFrameCount
;
5967 nsIFrame
*resultFrame
= nullptr;
5968 nsIFrame
*farStoppingFrame
= nullptr; //we keep searching until we find a "this" frame then we go to next line
5969 nsIFrame
*nearStoppingFrame
= nullptr; //if we are backing up from edge, stop here
5970 nsIFrame
*firstFrame
;
5971 nsIFrame
*lastFrame
;
5973 bool isBeforeFirstFrame
, isAfterLastFrame
;
5976 nsresult result
= NS_OK
;
5979 if (aPos
->mDirection
== eDirPrevious
)
5983 if ((aPos
->mDirection
== eDirPrevious
&& searchingLine
< 0) ||
5984 (aPos
->mDirection
== eDirNext
&& searchingLine
>= countLines
))
5986 //we need to jump to new block frame.
5987 return NS_ERROR_FAILURE
;
5990 result
= it
->GetLine(searchingLine
, &firstFrame
, &lineFrameCount
,
5992 if (!lineFrameCount
)
5994 if (NS_SUCCEEDED(result
)){
5995 lastFrame
= firstFrame
;
5996 for (;lineFrameCount
> 1;lineFrameCount
--){
5997 //result = lastFrame->GetNextSibling(&lastFrame, searchingLine);
5998 result
= it
->GetNextSiblingOnLine(lastFrame
, searchingLine
);
5999 if (NS_FAILED(result
) || !lastFrame
){
6000 NS_ERROR("GetLine promised more frames than could be found");
6001 return NS_ERROR_FAILURE
;
6004 GetLastLeaf(aPresContext
, &lastFrame
);
6006 if (aPos
->mDirection
== eDirNext
){
6007 nearStoppingFrame
= firstFrame
;
6008 farStoppingFrame
= lastFrame
;
6011 nearStoppingFrame
= lastFrame
;
6012 farStoppingFrame
= firstFrame
;
6015 nsView
* view
; //used for call of get offset from view
6016 aBlockFrame
->GetOffsetFromView(offset
,&view
);
6017 nscoord newDesiredX
= aPos
->mDesiredX
- offset
.x
;//get desired x into blockframe coordinates!
6018 result
= it
->FindFrameAt(searchingLine
, newDesiredX
, &resultFrame
, &isBeforeFirstFrame
, &isAfterLastFrame
);
6019 if(NS_FAILED(result
))
6023 if (NS_SUCCEEDED(result
) && resultFrame
)
6025 //check to see if this is ANOTHER blockframe inside the other one if so then call into its lines
6026 nsAutoLineIterator newIt
= resultFrame
->GetLineIterator();
6029 aPos
->mResultFrame
= resultFrame
;
6032 //resultFrame is not a block frame
6033 result
= NS_ERROR_FAILURE
;
6035 nsCOMPtr
<nsIFrameEnumerator
> frameTraversal
;
6036 result
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
6037 aPresContext
, resultFrame
,
6040 aPos
->mScrollViewStop
,
6041 false // aFollowOOFs
6043 if (NS_FAILED(result
))
6046 nsIFrame
*storeOldResultFrame
= resultFrame
;
6049 point
.x
= aPos
->mDesiredX
;
6051 nsRect tempRect
= resultFrame
->GetRect();
6053 nsView
* view
; //used for call of get offset from view
6054 resultFrame
->GetOffsetFromView(offset
, &view
);
6056 return NS_ERROR_FAILURE
;
6058 point
.y
= tempRect
.height
+ offset
.y
;
6060 //special check. if we allow non-text selection then we can allow a hit location to fall before a table.
6061 //otherwise there is no way to get and click signal to fall before a table (it being a line iterator itself)
6062 nsIPresShell
*shell
= aPresContext
->GetPresShell();
6064 return NS_ERROR_FAILURE
;
6065 int16_t isEditor
= shell
->GetSelectionFlags();
6066 isEditor
= isEditor
== nsISelectionDisplay::DISPLAY_ALL
;
6069 if (resultFrame
->GetType() == nsGkAtoms::tableOuterFrame
)
6071 if (((point
.x
- offset
.x
+ tempRect
.x
)<0) || ((point
.x
- offset
.x
+ tempRect
.x
)>tempRect
.width
))//off left/right side
6073 nsIContent
* content
= resultFrame
->GetContent();
6076 nsIContent
* parent
= content
->GetParent();
6079 aPos
->mResultContent
= parent
;
6080 aPos
->mContentOffset
= parent
->IndexOf(content
);
6081 aPos
->mAttach
= CARET_ASSOCIATE_BEFORE
;
6082 if ((point
.x
- offset
.x
+ tempRect
.x
)>tempRect
.width
)
6084 aPos
->mContentOffset
++;//go to end of this frame
6085 aPos
->mAttach
= CARET_ASSOCIATE_AFTER
;
6087 //result frame is the result frames parent.
6088 aPos
->mResultFrame
= resultFrame
->GetParent();
6089 return NS_POSITION_BEFORE_TABLE
;
6096 if (!resultFrame
->HasView())
6100 resultFrame
->GetOffsetFromView(offset
, &view
);
6101 ContentOffsets offsets
=
6102 resultFrame
->GetContentOffsetsFromPoint(point
- offset
);
6103 aPos
->mResultContent
= offsets
.content
;
6104 aPos
->mContentOffset
= offsets
.offset
;
6105 aPos
->mAttach
= offsets
.associate
;
6106 if (offsets
.content
)
6109 resultFrame
->IsSelectable(&selectable
, nullptr);
6118 if (aPos
->mDirection
== eDirPrevious
&& (resultFrame
== farStoppingFrame
))
6120 if (aPos
->mDirection
== eDirNext
&& (resultFrame
== nearStoppingFrame
))
6122 //always try previous on THAT line if that fails go the other way
6123 frameTraversal
->Prev();
6124 resultFrame
= frameTraversal
->CurrentItem();
6126 return NS_ERROR_FAILURE
;
6130 resultFrame
= storeOldResultFrame
;
6132 result
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
6133 aPresContext
, resultFrame
,
6136 aPos
->mScrollViewStop
,
6137 false // aFollowOOFs
6141 nsPoint
point(aPos
->mDesiredX
, 0);
6144 resultFrame
->GetOffsetFromView(offset
, &view
);
6145 ContentOffsets offsets
=
6146 resultFrame
->GetContentOffsetsFromPoint(point
- offset
);
6147 aPos
->mResultContent
= offsets
.content
;
6148 aPos
->mContentOffset
= offsets
.offset
;
6149 aPos
->mAttach
= offsets
.associate
;
6150 if (offsets
.content
)
6153 resultFrame
->IsSelectable(&selectable
, nullptr);
6157 if (resultFrame
== farStoppingFrame
)
6158 aPos
->mAttach
= CARET_ASSOCIATE_BEFORE
;
6160 aPos
->mAttach
= CARET_ASSOCIATE_AFTER
;
6164 if (aPos
->mDirection
== eDirPrevious
&& (resultFrame
== nearStoppingFrame
))
6166 if (aPos
->mDirection
== eDirNext
&& (resultFrame
== farStoppingFrame
))
6168 //previous didnt work now we try "next"
6169 frameTraversal
->Next();
6170 nsIFrame
*tempFrame
= frameTraversal
->CurrentItem();
6173 resultFrame
= tempFrame
;
6175 aPos
->mResultFrame
= resultFrame
;
6178 //we need to jump to new block frame.
6179 aPos
->mAmount
= eSelectLine
;
6180 aPos
->mStartOffset
= 0;
6181 aPos
->mAttach
= aPos
->mDirection
== eDirNext
?
6182 CARET_ASSOCIATE_BEFORE
: CARET_ASSOCIATE_AFTER
;
6183 if (aPos
->mDirection
== eDirPrevious
)
6184 aPos
->mStartOffset
= -1;//start from end
6185 return aBlockFrame
->PeekOffset(aPos
);
6191 nsIFrame::CaretPosition
6192 nsIFrame::GetExtremeCaretPosition(bool aStart
)
6194 CaretPosition result
;
6196 FrameTarget targetFrame
= DrillDownToSelectionFrame(this, !aStart
, 0);
6197 FrameContentRange range
= GetRangeForFrame(targetFrame
.frame
);
6198 result
.mResultContent
= range
.content
;
6199 result
.mContentOffset
= aStart
? range
.start
: range
.end
;
6203 // Find the first (or last) descendant of the given frame
6204 // which is either a block frame or a BRFrame.
6205 static nsContentAndOffset
6206 FindBlockFrameOrBR(nsIFrame
* aFrame
, nsDirection aDirection
)
6208 nsContentAndOffset result
;
6209 result
.mContent
= nullptr;
6212 if (aFrame
->IsGeneratedContentFrame())
6215 // Treat form controls as inline leaves
6216 // XXX we really need a way to determine whether a frame is inline-level
6217 nsIFormControlFrame
* fcf
= do_QueryFrame(aFrame
);
6221 // Check the frame itself
6222 // Fall through block-in-inline split frames because their mContent is
6223 // the content of the inline frames they were created from. The
6224 // first/last child of such frames is the real block frame we're
6226 if ((nsLayoutUtils::GetAsBlock(aFrame
) &&
6227 !(aFrame
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
)) ||
6228 aFrame
->GetType() == nsGkAtoms::brFrame
) {
6229 nsIContent
* content
= aFrame
->GetContent();
6230 result
.mContent
= content
->GetParent();
6231 // In some cases (bug 310589, bug 370174) we end up here with a null content.
6232 // This probably shouldn't ever happen, but since it sometimes does, we want
6233 // to avoid crashing here.
6234 NS_ASSERTION(result
.mContent
, "Unexpected orphan content");
6235 if (result
.mContent
)
6236 result
.mOffset
= result
.mContent
->IndexOf(content
) +
6237 (aDirection
== eDirPrevious
? 1 : 0);
6241 // If this is a preformatted text frame, see if it ends with a newline
6242 if (aFrame
->HasSignificantTerminalNewline()) {
6243 int32_t startOffset
, endOffset
;
6244 aFrame
->GetOffsets(startOffset
, endOffset
);
6245 result
.mContent
= aFrame
->GetContent();
6246 result
.mOffset
= endOffset
- (aDirection
== eDirPrevious
? 0 : 1);
6250 // Iterate over children and call ourselves recursively
6251 if (aDirection
== eDirPrevious
) {
6252 nsIFrame
* child
= aFrame
->GetLastChild(nsIFrame::kPrincipalList
);
6253 while(child
&& !result
.mContent
) {
6254 result
= FindBlockFrameOrBR(child
, aDirection
);
6255 child
= child
->GetPrevSibling();
6257 } else { // eDirNext
6258 nsIFrame
* child
= aFrame
->GetFirstPrincipalChild();
6259 while(child
&& !result
.mContent
) {
6260 result
= FindBlockFrameOrBR(child
, aDirection
);
6261 child
= child
->GetNextSibling();
6268 nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct
*aPos
)
6270 nsIFrame
* frame
= this;
6271 nsContentAndOffset blockFrameOrBR
;
6272 blockFrameOrBR
.mContent
= nullptr;
6273 bool reachedBlockAncestor
= false;
6275 // Go through containing frames until reaching a block frame.
6276 // In each step, search the previous (or next) siblings for the closest
6277 // "stop frame" (a block frame or a BRFrame).
6278 // If found, set it to be the selection boundray and abort.
6280 if (aPos
->mDirection
== eDirPrevious
) {
6281 while (!reachedBlockAncestor
) {
6282 nsIFrame
* parent
= frame
->GetParent();
6283 // Treat a frame associated with the root content as if it were a block frame.
6284 if (!frame
->mContent
|| !frame
->mContent
->GetParent()) {
6285 reachedBlockAncestor
= true;
6288 nsIFrame
* sibling
= frame
->GetPrevSibling();
6289 while (sibling
&& !blockFrameOrBR
.mContent
) {
6290 blockFrameOrBR
= FindBlockFrameOrBR(sibling
, eDirPrevious
);
6291 sibling
= sibling
->GetPrevSibling();
6293 if (blockFrameOrBR
.mContent
) {
6294 aPos
->mResultContent
= blockFrameOrBR
.mContent
;
6295 aPos
->mContentOffset
= blockFrameOrBR
.mOffset
;
6299 reachedBlockAncestor
= (nsLayoutUtils::GetAsBlock(frame
) != nullptr);
6301 if (reachedBlockAncestor
) { // no "stop frame" found
6302 aPos
->mResultContent
= frame
->GetContent();
6303 aPos
->mContentOffset
= 0;
6305 } else { // eDirNext
6306 while (!reachedBlockAncestor
) {
6307 nsIFrame
* parent
= frame
->GetParent();
6308 // Treat a frame associated with the root content as if it were a block frame.
6309 if (!frame
->mContent
|| !frame
->mContent
->GetParent()) {
6310 reachedBlockAncestor
= true;
6313 nsIFrame
* sibling
= frame
;
6314 while (sibling
&& !blockFrameOrBR
.mContent
) {
6315 blockFrameOrBR
= FindBlockFrameOrBR(sibling
, eDirNext
);
6316 sibling
= sibling
->GetNextSibling();
6318 if (blockFrameOrBR
.mContent
) {
6319 aPos
->mResultContent
= blockFrameOrBR
.mContent
;
6320 aPos
->mContentOffset
= blockFrameOrBR
.mOffset
;
6324 reachedBlockAncestor
= (nsLayoutUtils::GetAsBlock(frame
) != nullptr);
6326 if (reachedBlockAncestor
) { // no "stop frame" found
6327 aPos
->mResultContent
= frame
->GetContent();
6328 if (aPos
->mResultContent
)
6329 aPos
->mContentOffset
= aPos
->mResultContent
->GetChildCount();
6335 // Determine movement direction relative to frame
6336 static bool IsMovingInFrameDirection(nsIFrame
* frame
, nsDirection aDirection
, bool aVisual
)
6338 bool isReverseDirection
= aVisual
?
6339 (NS_GET_EMBEDDING_LEVEL(frame
) & 1) != (NS_GET_BASE_LEVEL(frame
) & 1) : false;
6340 return aDirection
== (isReverseDirection
? eDirPrevious
: eDirNext
);
6344 nsIFrame::PeekOffset(nsPeekOffsetStruct
* aPos
)
6347 return NS_ERROR_NULL_POINTER
;
6348 nsresult result
= NS_ERROR_FAILURE
;
6350 if (mState
& NS_FRAME_IS_DIRTY
)
6351 return NS_ERROR_UNEXPECTED
;
6353 // Translate content offset to be relative to frame
6354 FrameContentRange range
= GetRangeForFrame(this);
6355 int32_t offset
= aPos
->mStartOffset
- range
.start
;
6356 nsIFrame
* current
= this;
6358 switch (aPos
->mAmount
) {
6359 case eSelectCharacter
:
6360 case eSelectCluster
:
6362 bool eatingNonRenderableWS
= false;
6363 nsIFrame::FrameSearchResult peekSearchState
= CONTINUE
;
6364 bool jumpedLine
= false;
6365 bool movedOverNonSelectableText
= false;
6367 while (peekSearchState
!= FOUND
) {
6368 bool movingInFrameDirection
=
6369 IsMovingInFrameDirection(current
, aPos
->mDirection
, aPos
->mVisual
);
6371 if (eatingNonRenderableWS
)
6372 peekSearchState
= current
->PeekOffsetNoAmount(movingInFrameDirection
, &offset
);
6374 peekSearchState
= current
->PeekOffsetCharacter(movingInFrameDirection
, &offset
,
6375 aPos
->mAmount
== eSelectCluster
);
6377 movedOverNonSelectableText
|= (peekSearchState
== CONTINUE_UNSELECTABLE
);
6379 if (peekSearchState
!= FOUND
) {
6381 current
->GetFrameFromDirection(aPos
->mDirection
, aPos
->mVisual
,
6382 aPos
->mJumpLines
, aPos
->mScrollViewStop
,
6383 ¤t
, &offset
, &jumpedLine
);
6384 if (NS_FAILED(result
))
6387 // If we jumped lines, it's as if we found a character, but we still need
6388 // to eat non-renderable content on the new line.
6390 eatingNonRenderableWS
= true;
6393 // Found frame, but because we moved over non selectable text we want the offset
6394 // to be at the frame edge.
6395 if (peekSearchState
== FOUND
&& movedOverNonSelectableText
)
6398 current
->GetOffsets(start
, end
);
6399 offset
= aPos
->mDirection
== eDirNext
? 0 : end
- start
;
6404 range
= GetRangeForFrame(current
);
6405 aPos
->mResultFrame
= current
;
6406 aPos
->mResultContent
= range
.content
;
6407 // Output offset is relative to content, not frame
6408 aPos
->mContentOffset
= offset
< 0 ? range
.end
: range
.start
+ offset
;
6409 // If we're dealing with a text frame and moving backward positions us at
6410 // the end of that line, decrease the offset by one to make sure that
6411 // we're placed before the linefeed character on the previous line.
6412 if (offset
< 0 && jumpedLine
&&
6413 aPos
->mDirection
== eDirPrevious
&&
6414 current
->HasSignificantTerminalNewline()) {
6415 --aPos
->mContentOffset
;
6420 case eSelectWordNoSpace
:
6421 // eSelectWordNoSpace means that we should not be eating any whitespace when
6422 // moving to the adjacent word. This means that we should set aPos->
6423 // mWordMovementType to eEndWord if we're moving forwards, and to eStartWord
6424 // if we're moving backwards.
6425 if (aPos
->mDirection
== eDirPrevious
) {
6426 aPos
->mWordMovementType
= eStartWord
;
6428 aPos
->mWordMovementType
= eEndWord
;
6430 // Intentionally fall through the eSelectWord case.
6433 // wordSelectEatSpace means "are we looking for a boundary between whitespace
6434 // and non-whitespace (in the direction we're moving in)".
6435 // It is true when moving forward and looking for a beginning of a word, or
6436 // when moving backwards and looking for an end of a word.
6437 bool wordSelectEatSpace
;
6438 if (aPos
->mWordMovementType
!= eDefaultBehavior
) {
6439 // aPos->mWordMovementType possible values:
6440 // eEndWord: eat the space if we're moving backwards
6441 // eStartWord: eat the space if we're moving forwards
6442 wordSelectEatSpace
= ((aPos
->mWordMovementType
== eEndWord
) == (aPos
->mDirection
== eDirPrevious
));
6445 // Use the hidden preference which is based on operating system behavior.
6446 // This pref only affects whether moving forward by word should go to the end of this word or start of the next word.
6447 // When going backwards, the start of the word is always used, on every operating system.
6448 wordSelectEatSpace
= aPos
->mDirection
== eDirNext
&&
6449 Preferences::GetBool("layout.word_select.eat_space_to_next_word");
6452 // mSawBeforeType means "we already saw characters of the type
6453 // before the boundary we're looking for". Examples:
6454 // 1. If we're moving forward, looking for a word beginning (i.e. a boundary
6455 // between whitespace and non-whitespace), then eatingWS==true means
6456 // "we already saw some whitespace".
6457 // 2. If we're moving backward, looking for a word beginning (i.e. a boundary
6458 // between non-whitespace and whitespace), then eatingWS==true means
6459 // "we already saw some non-whitespace".
6460 PeekWordState state
;
6461 int32_t offsetAdjustment
= 0;
6464 bool movingInFrameDirection
=
6465 IsMovingInFrameDirection(current
, aPos
->mDirection
, aPos
->mVisual
);
6467 done
= current
->PeekOffsetWord(movingInFrameDirection
, wordSelectEatSpace
,
6468 aPos
->mIsKeyboardSelect
, &offset
, &state
) == FOUND
;
6471 nsIFrame
* nextFrame
;
6472 int32_t nextFrameOffset
;
6475 current
->GetFrameFromDirection(aPos
->mDirection
, aPos
->mVisual
,
6476 aPos
->mJumpLines
, aPos
->mScrollViewStop
,
6477 &nextFrame
, &nextFrameOffset
, &jumpedLine
);
6478 // We can't jump lines if we're looking for whitespace following
6479 // non-whitespace, and we already encountered non-whitespace.
6480 if (NS_FAILED(result
) ||
6481 (jumpedLine
&& !wordSelectEatSpace
&& state
.mSawBeforeType
)) {
6483 // If we've crossed the line boundary, check to make sure that we
6484 // have not consumed a trailing newline as whitesapce if it's significant.
6485 if (jumpedLine
&& wordSelectEatSpace
&&
6486 current
->HasSignificantTerminalNewline()) {
6487 offsetAdjustment
= -1;
6491 state
.mContext
.Truncate();
6493 current
= nextFrame
;
6494 offset
= nextFrameOffset
;
6495 // Jumping a line is equivalent to encountering whitespace
6496 if (wordSelectEatSpace
&& jumpedLine
)
6497 state
.SetSawBeforeType();
6503 range
= GetRangeForFrame(current
);
6504 aPos
->mResultFrame
= current
;
6505 aPos
->mResultContent
= range
.content
;
6506 // Output offset is relative to content, not frame
6507 aPos
->mContentOffset
= (offset
< 0 ? range
.end
: range
.start
+ offset
) + offsetAdjustment
;
6512 nsAutoLineIterator iter
;
6513 nsIFrame
*blockFrame
= this;
6515 while (NS_FAILED(result
)){
6516 int32_t thisLine
= nsFrame::GetLineNumber(blockFrame
, aPos
->mScrollViewStop
, &blockFrame
);
6518 return NS_ERROR_FAILURE
;
6519 iter
= blockFrame
->GetLineIterator();
6520 NS_ASSERTION(iter
, "GetLineNumber() succeeded but no block frame?");
6523 int edgeCase
= 0;//no edge case. this should look at thisLine
6525 bool doneLooping
= false;//tells us when no more block frames hit.
6526 //this part will find a frame or a block frame. if it's a block frame
6527 //it will "drill down" to find a viable frame or it will return an error.
6528 nsIFrame
*lastFrame
= this;
6530 result
= nsFrame::GetNextPrevLineFromeBlockFrame(PresContext(),
6534 edgeCase
//start from thisLine
6536 if (NS_SUCCEEDED(result
) && (!aPos
->mResultFrame
|| aPos
->mResultFrame
== lastFrame
))//we came back to same spot! keep going
6538 aPos
->mResultFrame
= nullptr;
6539 if (aPos
->mDirection
== eDirPrevious
)
6544 else //if failure or success with different frame.
6545 doneLooping
= true; //do not continue with while loop
6547 lastFrame
= aPos
->mResultFrame
; //set last frame
6549 if (NS_SUCCEEDED(result
) && aPos
->mResultFrame
6550 && blockFrame
!= aPos
->mResultFrame
)// make sure block element is not the same as the one we had before
6552 /* SPECIAL CHECK FOR TABLE NAVIGATION
6553 tables need to navigate also and the frame that supports it is nsTableRowGroupFrame which is INSIDE
6554 nsTableOuterFrame. if we have stumbled onto an nsTableOuter we need to drill into nsTableRowGroup
6555 if we hit a header or footer that's ok just go into them,
6557 bool searchTableBool
= false;
6558 if (aPos
->mResultFrame
->GetType() == nsGkAtoms::tableOuterFrame
||
6559 aPos
->mResultFrame
->GetType() == nsGkAtoms::tableCellFrame
)
6561 nsIFrame
*frame
= aPos
->mResultFrame
->GetFirstPrincipalChild();
6562 //got the table frame now
6563 while(frame
) //ok time to drill down to find iterator
6565 iter
= frame
->GetLineIterator();
6568 aPos
->mResultFrame
= frame
;
6569 searchTableBool
= true;
6571 break; //while(frame)
6573 result
= NS_ERROR_FAILURE
;
6574 frame
= frame
->GetFirstPrincipalChild();
6578 if (!searchTableBool
) {
6579 iter
= aPos
->mResultFrame
->GetLineIterator();
6580 result
= iter
? NS_OK
: NS_ERROR_FAILURE
;
6582 if (NS_SUCCEEDED(result
) && iter
)//we've struck another block element!
6584 doneLooping
= false;
6585 if (aPos
->mDirection
== eDirPrevious
)
6586 edgeCase
= 1;//far edge, search from end backwards
6588 edgeCase
= -1;//near edge search from beginning onwards
6589 thisLine
=0;//this line means nothing now.
6590 //everything else means something so keep looking "inside" the block
6591 blockFrame
= aPos
->mResultFrame
;
6596 result
= NS_OK
;//THIS is to mean that everything is ok to the containing while loop
6600 } while (!doneLooping
);
6605 case eSelectParagraph
:
6606 return PeekOffsetParagraph(aPos
);
6608 case eSelectBeginLine
:
6609 case eSelectEndLine
:
6611 // Adjusted so that the caret can't get confused when content changes
6612 nsIFrame
* blockFrame
= AdjustFrameForSelectionStyles(this);
6613 int32_t thisLine
= nsFrame::GetLineNumber(blockFrame
, aPos
->mScrollViewStop
, &blockFrame
);
6615 return NS_ERROR_FAILURE
;
6616 nsAutoLineIterator it
= blockFrame
->GetLineIterator();
6617 NS_ASSERTION(it
, "GetLineNumber() succeeded but no block frame?");
6619 int32_t lineFrameCount
;
6620 nsIFrame
*firstFrame
;
6623 nsIFrame
* baseFrame
= nullptr;
6624 bool endOfLine
= (eSelectEndLine
== aPos
->mAmount
);
6626 if (aPos
->mVisual
&& PresContext()->BidiEnabled()) {
6627 bool lineIsRTL
= it
->GetDirection();
6629 nsIFrame
*lastFrame
;
6630 result
= it
->CheckLineOrder(thisLine
, &isReordered
, &firstFrame
, &lastFrame
);
6631 baseFrame
= endOfLine
? lastFrame
: firstFrame
;
6633 nsBidiLevel embeddingLevel
= nsBidiPresUtils::GetFrameEmbeddingLevel(baseFrame
);
6634 // If the direction of the frame on the edge is opposite to that of the line,
6635 // we'll need to drill down to its opposite end, so reverse endOfLine.
6636 if ((embeddingLevel
& 1) == !lineIsRTL
)
6637 endOfLine
= !endOfLine
;
6640 it
->GetLine(thisLine
, &firstFrame
, &lineFrameCount
, usedRect
, &lineFlags
);
6642 nsIFrame
* frame
= firstFrame
;
6643 for (int32_t count
= lineFrameCount
; count
;
6644 --count
, frame
= frame
->GetNextSibling()) {
6645 if (!frame
->IsGeneratedContentFrame()) {
6653 return NS_ERROR_FAILURE
;
6654 FrameTarget targetFrame
= DrillDownToSelectionFrame(baseFrame
,
6656 FrameContentRange range
= GetRangeForFrame(targetFrame
.frame
);
6657 aPos
->mResultContent
= range
.content
;
6658 aPos
->mContentOffset
= endOfLine
? range
.end
: range
.start
;
6659 if (endOfLine
&& targetFrame
.frame
->HasSignificantTerminalNewline()) {
6660 // Do not position the caret after the terminating newline if we're
6661 // trying to move to the end of line (see bug 596506)
6662 --aPos
->mContentOffset
;
6664 aPos
->mResultFrame
= targetFrame
.frame
;
6665 aPos
->mAttach
= aPos
->mContentOffset
== range
.start
?
6666 CARET_ASSOCIATE_AFTER
: CARET_ASSOCIATE_BEFORE
;
6668 return NS_ERROR_FAILURE
;
6674 NS_ASSERTION(false, "Invalid amount");
6675 return NS_ERROR_FAILURE
;
6681 nsIFrame::FrameSearchResult
6682 nsFrame::PeekOffsetNoAmount(bool aForward
, int32_t* aOffset
)
6684 NS_ASSERTION (aOffset
&& *aOffset
<= 1, "aOffset out of range");
6685 // Sure, we can stop right here.
6689 nsIFrame::FrameSearchResult
6690 nsFrame::PeekOffsetCharacter(bool aForward
, int32_t* aOffset
,
6691 bool aRespectClusters
)
6693 NS_ASSERTION (aOffset
&& *aOffset
<= 1, "aOffset out of range");
6694 int32_t startOffset
= *aOffset
;
6695 // A negative offset means "end of frame", which in our case means offset 1.
6696 if (startOffset
< 0)
6698 if (aForward
== (startOffset
== 0)) {
6699 // We're before the frame and moving forward, or after it and moving backwards:
6700 // skip to the other side and we're done.
6701 *aOffset
= 1 - startOffset
;
6707 nsIFrame::FrameSearchResult
6708 nsFrame::PeekOffsetWord(bool aForward
, bool aWordSelectEatSpace
, bool aIsKeyboardSelect
,
6709 int32_t* aOffset
, PeekWordState
* aState
)
6711 NS_ASSERTION (aOffset
&& *aOffset
<= 1, "aOffset out of range");
6712 int32_t startOffset
= *aOffset
;
6713 // This isn't text, so truncate the context
6714 aState
->mContext
.Truncate();
6715 if (startOffset
< 0)
6717 if (aForward
== (startOffset
== 0)) {
6718 // We're before the frame and moving forward, or after it and moving backwards.
6719 // If we're looking for non-whitespace, we found it (without skipping this frame).
6720 if (!aState
->mAtStart
) {
6721 if (aState
->mLastCharWasPunctuation
) {
6722 // We're not punctuation, so this is a punctuation boundary.
6723 if (BreakWordBetweenPunctuation(aState
, aForward
, false, false, aIsKeyboardSelect
))
6726 // This is not a punctuation boundary.
6727 if (aWordSelectEatSpace
&& aState
->mSawBeforeType
)
6731 // Otherwise skip to the other side and note that we encountered non-whitespace.
6732 *aOffset
= 1 - startOffset
;
6733 aState
->Update(false, // not punctuation
6734 false // not whitespace
6736 if (!aWordSelectEatSpace
)
6737 aState
->SetSawBeforeType();
6743 nsFrame::BreakWordBetweenPunctuation(const PeekWordState
* aState
,
6745 bool aPunctAfter
, bool aWhitespaceAfter
,
6746 bool aIsKeyboardSelect
)
6748 NS_ASSERTION(aPunctAfter
!= aState
->mLastCharWasPunctuation
,
6749 "Call this only at punctuation boundaries");
6750 if (aState
->mLastCharWasWhitespace
) {
6751 // We always stop between whitespace and punctuation
6754 if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) {
6755 // When this pref is false, we never stop at a punctuation boundary unless
6756 // it's followed by whitespace (in the relevant direction).
6757 return aWhitespaceAfter
;
6759 if (!aIsKeyboardSelect
) {
6760 // mouse caret movement (e.g. word selection) always stops at every punctuation boundary
6763 bool afterPunct
= aForward
? aState
->mLastCharWasPunctuation
: aPunctAfter
;
6765 // keyboard caret movement only stops after punctuation (in content order)
6768 // Stop only if we've seen some non-punctuation since the last whitespace;
6769 // don't stop after punctuation that follows whitespace.
6770 return aState
->mSeenNonPunctuationSinceWhitespace
;
6774 nsFrame::CheckVisibility(nsPresContext
* , int32_t , int32_t , bool , bool *, bool *)
6776 return NS_ERROR_NOT_IMPLEMENTED
;
6781 nsFrame::GetLineNumber(nsIFrame
*aFrame
, bool aLockScroll
, nsIFrame
** aContainingBlock
)
6783 NS_ASSERTION(aFrame
, "null aFrame");
6784 nsFrameManager
* frameManager
= aFrame
->PresContext()->FrameManager();
6785 nsIFrame
*blockFrame
= aFrame
;
6786 nsIFrame
*thisBlock
;
6787 nsAutoLineIterator it
;
6788 nsresult result
= NS_ERROR_FAILURE
;
6789 while (NS_FAILED(result
) && blockFrame
)
6791 thisBlock
= blockFrame
;
6792 if (thisBlock
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) {
6793 //if we are searching for a frame that is not in flow we will not find it.
6794 //we must instead look for its placeholder
6795 if (thisBlock
->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER
) {
6796 // abspos continuations don't have placeholders, get the fif
6797 thisBlock
= thisBlock
->FirstInFlow();
6799 thisBlock
= frameManager
->GetPlaceholderFrameFor(thisBlock
);
6803 blockFrame
= thisBlock
->GetParent();
6806 if (aLockScroll
&& blockFrame
->GetType() == nsGkAtoms::scrollFrame
)
6808 it
= blockFrame
->GetLineIterator();
6810 result
= NS_ERROR_FAILURE
;
6813 if (!blockFrame
|| !it
)
6816 if (aContainingBlock
)
6817 *aContainingBlock
= blockFrame
;
6818 return it
->FindLineContaining(thisBlock
);
6822 nsIFrame::GetFrameFromDirection(nsDirection aDirection
, bool aVisual
,
6823 bool aJumpLines
, bool aScrollViewStop
,
6824 nsIFrame
** aOutFrame
, int32_t* aOutOffset
, bool* aOutJumpedLine
)
6828 if (!aOutFrame
|| !aOutOffset
|| !aOutJumpedLine
)
6829 return NS_ERROR_NULL_POINTER
;
6831 nsPresContext
* presContext
= PresContext();
6832 *aOutFrame
= nullptr;
6834 *aOutJumpedLine
= false;
6836 // Find the prev/next selectable frame
6837 bool selectable
= false;
6838 nsIFrame
*traversedFrame
= this;
6839 while (!selectable
) {
6840 nsIFrame
*blockFrame
;
6842 int32_t thisLine
= nsFrame::GetLineNumber(traversedFrame
, aScrollViewStop
, &blockFrame
);
6844 return NS_ERROR_FAILURE
;
6846 nsAutoLineIterator it
= blockFrame
->GetLineIterator();
6847 NS_ASSERTION(it
, "GetLineNumber() succeeded but no block frame?");
6850 nsIFrame
*firstFrame
;
6851 nsIFrame
*lastFrame
;
6852 if (aVisual
&& presContext
->BidiEnabled()) {
6853 bool lineIsRTL
= it
->GetDirection();
6855 result
= it
->CheckLineOrder(thisLine
, &isReordered
, &firstFrame
, &lastFrame
);
6856 nsIFrame
** framePtr
= aDirection
== eDirPrevious
? &firstFrame
: &lastFrame
;
6858 nsBidiLevel embeddingLevel
= nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr
);
6859 if ((((embeddingLevel
& 1) && lineIsRTL
) || (!(embeddingLevel
& 1) && !lineIsRTL
)) ==
6860 (aDirection
== eDirPrevious
)) {
6861 nsFrame::GetFirstLeaf(presContext
, framePtr
);
6863 nsFrame::GetLastLeaf(presContext
, framePtr
);
6865 atLineEdge
= *framePtr
== traversedFrame
;
6871 int32_t lineFrameCount
;
6873 result
= it
->GetLine(thisLine
, &firstFrame
, &lineFrameCount
,nonUsedRect
,
6875 if (NS_FAILED(result
))
6878 if (aDirection
== eDirPrevious
) {
6879 nsFrame::GetFirstLeaf(presContext
, &firstFrame
);
6880 atLineEdge
= firstFrame
== traversedFrame
;
6881 } else { // eDirNext
6882 lastFrame
= firstFrame
;
6883 for (;lineFrameCount
> 1;lineFrameCount
--){
6884 result
= it
->GetNextSiblingOnLine(lastFrame
, thisLine
);
6885 if (NS_FAILED(result
) || !lastFrame
){
6886 NS_ERROR("should not be reached nsFrame");
6887 return NS_ERROR_FAILURE
;
6890 nsFrame::GetLastLeaf(presContext
, &lastFrame
);
6891 atLineEdge
= lastFrame
== traversedFrame
;
6896 *aOutJumpedLine
= true;
6898 return NS_ERROR_FAILURE
; //we are done. cannot jump lines
6901 nsCOMPtr
<nsIFrameEnumerator
> frameTraversal
;
6902 result
= NS_NewFrameTraversal(getter_AddRefs(frameTraversal
),
6903 presContext
, traversedFrame
,
6905 aVisual
&& presContext
->BidiEnabled(),
6909 if (NS_FAILED(result
))
6912 if (aDirection
== eDirNext
)
6913 frameTraversal
->Next();
6915 frameTraversal
->Prev();
6917 traversedFrame
= frameTraversal
->CurrentItem();
6918 if (!traversedFrame
)
6919 return NS_ERROR_FAILURE
;
6920 traversedFrame
->IsSelectable(&selectable
, nullptr);
6921 } // while (!selectable)
6923 *aOutOffset
= (aDirection
== eDirNext
) ? 0 : -1;
6926 uint8_t newLevel
= NS_GET_EMBEDDING_LEVEL(traversedFrame
);
6927 uint8_t newBaseLevel
= NS_GET_BASE_LEVEL(traversedFrame
);
6928 if ((newLevel
& 1) != (newBaseLevel
& 1)) // The new frame is reverse-direction, go to the other end
6929 *aOutOffset
= -1 - *aOutOffset
;
6931 *aOutFrame
= traversedFrame
;
6935 nsView
* nsIFrame::GetClosestView(nsPoint
* aOffset
) const
6937 nsPoint
offset(0,0);
6938 for (const nsIFrame
*f
= this; f
; f
= f
->GetParent()) {
6942 return f
->GetView();
6944 offset
+= f
->GetPosition();
6947 NS_NOTREACHED("No view on any parent? How did that happen?");
6953 nsFrame::ChildIsDirty(nsIFrame
* aChild
)
6955 NS_NOTREACHED("should never be called on a frame that doesn't inherit from "
6956 "nsContainerFrame");
6960 #ifdef ACCESSIBILITY
6962 nsFrame::AccessibleType()
6964 return a11y::eNoType
;
6968 NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty
,
6969 nsIFrame::DestroyOverflowAreas
)
6972 nsIFrame::ClearOverflowRects()
6974 if (mOverflow
.mType
== NS_FRAME_OVERFLOW_NONE
) {
6977 if (mOverflow
.mType
== NS_FRAME_OVERFLOW_LARGE
) {
6978 Properties().Delete(OverflowAreasProperty());
6980 mOverflow
.mType
= NS_FRAME_OVERFLOW_NONE
;
6984 /** Create or retrieve the previously stored overflow area, if the frame does
6985 * not overflow and no creation is required return nullptr.
6986 * @return pointer to the overflow area rectangle
6989 nsIFrame::GetOverflowAreasProperty()
6991 FrameProperties props
= Properties();
6992 nsOverflowAreas
*overflow
=
6993 static_cast<nsOverflowAreas
*>(props
.Get(OverflowAreasProperty()));
6996 return overflow
; // the property already exists
6999 // The property isn't set yet, so allocate a new rect, set the property,
7000 // and return the newly allocated rect
7001 overflow
= new nsOverflowAreas
;
7002 props
.Set(OverflowAreasProperty(), overflow
);
7006 /** Set the overflowArea rect, storing it as deltas or a separate rect
7007 * depending on its size in relation to the primary frame rect.
7010 nsIFrame::SetOverflowAreas(const nsOverflowAreas
& aOverflowAreas
)
7012 if (mOverflow
.mType
== NS_FRAME_OVERFLOW_LARGE
) {
7013 nsOverflowAreas
*overflow
=
7014 static_cast<nsOverflowAreas
*>(Properties().Get(OverflowAreasProperty()));
7015 bool changed
= *overflow
!= aOverflowAreas
;
7016 *overflow
= aOverflowAreas
;
7018 // Don't bother with converting to the deltas form if we already
7023 const nsRect
& vis
= aOverflowAreas
.VisualOverflow();
7024 uint32_t l
= -vis
.x
, // left edge: positive delta is leftwards
7025 t
= -vis
.y
, // top: positive is upwards
7026 r
= vis
.XMost() - mRect
.width
, // right: positive is rightwards
7027 b
= vis
.YMost() - mRect
.height
; // bottom: positive is downwards
7028 if (aOverflowAreas
.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) &&
7029 l
<= NS_FRAME_OVERFLOW_DELTA_MAX
&&
7030 t
<= NS_FRAME_OVERFLOW_DELTA_MAX
&&
7031 r
<= NS_FRAME_OVERFLOW_DELTA_MAX
&&
7032 b
<= NS_FRAME_OVERFLOW_DELTA_MAX
&&
7033 // we have to check these against zero because we *never* want to
7034 // set a frame as having no overflow in this function. This is
7035 // because FinishAndStoreOverflow calls this function prior to
7036 // SetRect based on whether the overflow areas match aNewSize.
7037 // In the case where the overflow areas exactly match mRect but
7038 // do not match aNewSize, we need to store overflow in a property
7039 // so that our eventual SetRect/SetSize will know that it has to
7040 // reset our overflow areas.
7041 (l
| t
| r
| b
) != 0) {
7042 VisualDeltas oldDeltas
= mOverflow
.mVisualDeltas
;
7043 // It's a "small" overflow area so we store the deltas for each edge
7044 // directly in the frame, rather than allocating a separate rect.
7045 // If they're all zero, that's fine; we're setting things to
7047 mOverflow
.mVisualDeltas
.mLeft
= l
;
7048 mOverflow
.mVisualDeltas
.mTop
= t
;
7049 mOverflow
.mVisualDeltas
.mRight
= r
;
7050 mOverflow
.mVisualDeltas
.mBottom
= b
;
7051 // There was no scrollable overflow before, and there isn't now.
7052 return oldDeltas
!= mOverflow
.mVisualDeltas
;
7054 bool changed
= !aOverflowAreas
.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) ||
7055 !aOverflowAreas
.VisualOverflow().IsEqualEdges(GetVisualOverflowFromDeltas());
7057 // it's a large overflow area that we need to store as a property
7058 mOverflow
.mType
= NS_FRAME_OVERFLOW_LARGE
;
7059 nsOverflowAreas
* overflow
= GetOverflowAreasProperty();
7060 NS_ASSERTION(overflow
, "should have created areas");
7061 *overflow
= aOverflowAreas
;
7067 IsInlineFrame(nsIFrame
*aFrame
)
7069 nsIAtom
*type
= aFrame
->GetType();
7070 return type
== nsGkAtoms::inlineFrame
;
7074 * Compute the union of the border boxes of aFrame and its descendants,
7075 * in aFrame's coordinate space (if aApplyTransform is false) or its
7076 * post-transform coordinate space (if aApplyTransform is true).
7079 UnionBorderBoxes(nsIFrame
* aFrame
, bool aApplyTransform
,
7080 const nsSize
* aSizeOverride
= nullptr,
7081 const nsOverflowAreas
* aOverflowOverride
= nullptr)
7083 const nsRect
bounds(nsPoint(0, 0),
7084 aSizeOverride
? *aSizeOverride
: aFrame
->GetSize());
7086 // Start from our border-box, transformed. See comment below about
7087 // transform of children.
7089 bool doTransform
= aApplyTransform
&& aFrame
->IsTransformed();
7091 u
= nsDisplayTransform::TransformRect(bounds
, aFrame
,
7092 nsPoint(0, 0), &bounds
);
7097 // Only iterate through the children if the overflow areas suggest
7098 // that we might need to, and if the frame doesn't clip its overflow
7100 if (aOverflowOverride
) {
7102 bounds
.IsEqualEdges(aOverflowOverride
->VisualOverflow()) &&
7103 bounds
.IsEqualEdges(aOverflowOverride
->ScrollableOverflow())) {
7108 bounds
.IsEqualEdges(aFrame
->GetVisualOverflowRect()) &&
7109 bounds
.IsEqualEdges(aFrame
->GetScrollableOverflowRect())) {
7113 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
7114 nsIAtom
* fType
= aFrame
->GetType();
7115 if (nsFrame::ShouldApplyOverflowClipping(aFrame
, disp
) ||
7116 fType
== nsGkAtoms::scrollFrame
||
7117 fType
== nsGkAtoms::svgOuterSVGFrame
) {
7121 nsRect clipPropClipRect
;
7122 bool hasClipPropClip
=
7123 aFrame
->GetClipPropClipRect(disp
, &clipPropClipRect
, bounds
.Size());
7125 // Iterate over all children except pop-ups.
7126 const nsIFrame::ChildListIDs
skip(nsIFrame::kPopupList
|
7127 nsIFrame::kSelectPopupList
);
7128 for (nsIFrame::ChildListIterator
childLists(aFrame
);
7129 !childLists
.IsDone(); childLists
.Next()) {
7130 if (skip
.Contains(childLists
.CurrentID())) {
7134 nsFrameList children
= childLists
.CurrentList();
7135 for (nsFrameList::Enumerator
e(children
); !e
.AtEnd(); e
.Next()) {
7136 nsIFrame
* child
= e
.get();
7137 // Note that passing |true| for aApplyTransform when
7138 // child->Preserves3D() is incorrect if our aApplyTransform is
7139 // false... but the opposite would be as well. This is because
7140 // elements within a preserve-3d scene are always transformed up
7141 // to the top of the scene. This means we don't have a
7142 // mechanism for getting a transform up to an intermediate point
7143 // within the scene. We choose to over-transform rather than
7144 // under-transform because this is consistent with other
7146 nsRect childRect
= UnionBorderBoxes(child
, true) +
7147 child
->GetPosition();
7149 if (hasClipPropClip
) {
7150 // Intersect with the clip before transforming.
7151 childRect
.IntersectRect(childRect
, clipPropClipRect
);
7154 // Note that we transform each child separately according to
7155 // aFrame's transform, and then union, which gives a different
7156 // (smaller) result from unioning and then transforming the
7157 // union. This doesn't match the way we handle overflow areas
7158 // with 2-D transforms, though it does match the way we handle
7159 // overflow areas in preserve-3d 3-D scenes.
7160 if (doTransform
&& !child
->Preserves3D()) {
7161 childRect
= nsDisplayTransform::TransformRect(childRect
, aFrame
,
7162 nsPoint(0, 0), &bounds
);
7164 u
.UnionRectEdges(u
, childRect
);
7172 ComputeAndIncludeOutlineArea(nsIFrame
* aFrame
, nsOverflowAreas
& aOverflowAreas
,
7173 const nsSize
& aNewSize
)
7175 const nsStyleOutline
* outline
= aFrame
->StyleOutline();
7176 const uint8_t outlineStyle
= outline
->GetOutlineStyle();
7177 if (outlineStyle
== NS_STYLE_BORDER_STYLE_NONE
) {
7182 DebugOnly
<bool> result
= outline
->GetOutlineWidth(width
);
7183 NS_ASSERTION(result
, "GetOutlineWidth had no cached outline width");
7184 if (width
<= 0 && outlineStyle
!= NS_STYLE_BORDER_STYLE_AUTO
) {
7188 // When the outline property is set on :-moz-anonymous-block or
7189 // :-moz-anonymous-positioned-block pseudo-elements, it inherited
7190 // that outline from the inline that was broken because it
7191 // contained a block. In that case, we don't want a really wide
7192 // outline if the block inside the inline is narrow, so union the
7193 // actual contents of the anonymous blocks.
7194 nsIFrame
*frameForArea
= aFrame
;
7196 nsIAtom
*pseudoType
= frameForArea
->StyleContext()->GetPseudo();
7197 if (pseudoType
!= nsCSSAnonBoxes::mozAnonymousBlock
&&
7198 pseudoType
!= nsCSSAnonBoxes::mozAnonymousPositionedBlock
)
7200 // If we're done, we really want it and all its later siblings.
7201 frameForArea
= frameForArea
->GetFirstPrincipalChild();
7202 NS_ASSERTION(frameForArea
, "anonymous block with no children?");
7203 } while (frameForArea
);
7205 // Find the union of the border boxes of all descendants, or in
7206 // the block-in-inline case, all descendants we care about.
7208 // Note that the interesting perspective-related cases are taken
7209 // care of by the code that handles those issues for overflow
7210 // calling FinishAndStoreOverflow again, which in turn calls this
7211 // function again. We still need to deal with preserve-3d a bit.
7213 if (frameForArea
== aFrame
) {
7214 innerRect
= UnionBorderBoxes(aFrame
, false, &aNewSize
, &aOverflowAreas
);
7216 for (; frameForArea
; frameForArea
= frameForArea
->GetNextSibling()) {
7217 nsRect
r(UnionBorderBoxes(frameForArea
, true));
7219 // Adjust for offsets transforms up to aFrame's pre-transform
7220 // (i.e., normal) coordinate space; see comments in
7221 // UnionBorderBoxes for some of the subtlety here.
7222 for (nsIFrame
*f
= frameForArea
, *parent
= f
->GetParent();
7223 /* see middle of loop */;
7224 f
= parent
, parent
= f
->GetParent()) {
7225 r
+= f
->GetPosition();
7226 if (parent
== aFrame
) {
7229 if (parent
->IsTransformed() && !f
->Preserves3D()) {
7230 r
= nsDisplayTransform::TransformRect(r
, parent
, nsPoint(0, 0));
7234 innerRect
.UnionRect(innerRect
, r
);
7238 // Keep this code in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
7239 aFrame
->Properties().Set(nsIFrame::OutlineInnerRectProperty(),
7240 new nsRect(innerRect
));
7241 const nscoord offset
= outline
->mOutlineOffset
;
7242 nsRect
outerRect(innerRect
);
7243 bool useOutlineAuto
= false;
7244 if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
7245 useOutlineAuto
= outlineStyle
== NS_STYLE_BORDER_STYLE_AUTO
;
7246 if (MOZ_UNLIKELY(useOutlineAuto
)) {
7247 nsPresContext
* presContext
= aFrame
->PresContext();
7248 nsITheme
* theme
= presContext
->GetTheme();
7249 if (theme
&& theme
->ThemeSupportsWidget(presContext
, aFrame
,
7250 NS_THEME_FOCUS_OUTLINE
)) {
7251 outerRect
.Inflate(offset
);
7252 theme
->GetWidgetOverflow(presContext
->DeviceContext(), aFrame
,
7253 NS_THEME_FOCUS_OUTLINE
, &outerRect
);
7255 useOutlineAuto
= false;
7259 if (MOZ_LIKELY(!useOutlineAuto
)) {
7260 outerRect
.Inflate(width
+ offset
);
7263 nsRect
& vo
= aOverflowAreas
.VisualOverflow();
7264 vo
.UnionRectEdges(vo
, innerRect
.Union(outerRect
));
7268 nsIFrame::FinishAndStoreOverflow(nsOverflowAreas
& aOverflowAreas
,
7269 nsSize aNewSize
, nsSize
* aOldSize
)
7271 NS_ASSERTION(FrameMaintainsOverflow(this),
7272 "Don't call - overflow rects not maintained on these SVG frames");
7274 nsRect
bounds(nsPoint(0, 0), aNewSize
);
7275 // Store the passed in overflow area if we are a preserve-3d frame or we have
7276 // a transform, and it's not just the frame bounds.
7277 if (Preserves3D() || HasPerspective() || IsTransformed()) {
7278 if (!aOverflowAreas
.VisualOverflow().IsEqualEdges(bounds
) ||
7279 !aOverflowAreas
.ScrollableOverflow().IsEqualEdges(bounds
)) {
7280 nsOverflowAreas
* initial
=
7281 static_cast<nsOverflowAreas
*>(Properties().Get(nsIFrame::InitialOverflowProperty()));
7283 Properties().Set(nsIFrame::InitialOverflowProperty(),
7284 new nsOverflowAreas(aOverflowAreas
));
7285 } else if (initial
!= &aOverflowAreas
) {
7286 *initial
= aOverflowAreas
;
7289 Properties().Delete(nsIFrame::InitialOverflowProperty());
7292 Properties().Set(nsIFrame::DebugInitialOverflowPropertyApplied(), nullptr);
7296 Properties().Delete(nsIFrame::DebugInitialOverflowPropertyApplied());
7300 // This is now called FinishAndStoreOverflow() instead of
7301 // StoreOverflow() because frame-generic ways of adding overflow
7302 // can happen here, e.g. CSS2 outline and native theme.
7303 // If the overflow area width or height is nscoord_MAX, then a
7304 // saturating union may have encounted an overflow, so the overflow may not
7305 // contain the frame border-box. Don't warn in that case.
7306 // Don't warn for SVG either, since SVG doesn't need the overflow area
7307 // to contain the frame bounds.
7308 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
7309 DebugOnly
<nsRect
*> r
= &aOverflowAreas
.Overflow(otype
);
7310 NS_ASSERTION(aNewSize
.width
== 0 || aNewSize
.height
== 0 ||
7311 r
->width
== nscoord_MAX
|| r
->height
== nscoord_MAX
||
7312 (mState
& NS_FRAME_SVG_LAYOUT
) ||
7313 r
->Contains(nsRect(nsPoint(0,0), aNewSize
)),
7314 "Computed overflow area must contain frame bounds");
7317 // If we clip our children, clear accumulated overflow area. The
7318 // children are actually clipped to the padding-box, but since the
7319 // overflow area should include the entire border-box, just set it to
7320 // the border-box here.
7321 const nsStyleDisplay
* disp
= StyleDisplay();
7322 NS_ASSERTION((disp
->mOverflowY
== NS_STYLE_OVERFLOW_CLIP
) ==
7323 (disp
->mOverflowX
== NS_STYLE_OVERFLOW_CLIP
),
7324 "If one overflow is clip, the other should be too");
7325 if (nsFrame::ShouldApplyOverflowClipping(this, disp
)) {
7326 // The contents are actually clipped to the padding area
7327 aOverflowAreas
.SetAllTo(bounds
);
7330 // Overflow area must always include the frame's top-left and bottom-right,
7331 // even if the frame rect is empty (so we can scroll to those positions).
7332 // Pending a real fix for bug 426879, don't do this for inline frames
7334 // Do not do this for SVG either, since it will usually massively increase
7335 // the area unnecessarily.
7336 if ((aNewSize
.width
!= 0 || !IsInlineFrame(this)) &&
7337 !(GetStateBits() & NS_FRAME_SVG_LAYOUT
)) {
7338 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
7339 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
7340 o
.UnionRectEdges(o
, bounds
);
7344 // Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background,
7345 // so we add theme background overflow here so it's not clipped.
7346 if (!::IsBoxWrapped(this) && IsThemed(disp
)) {
7348 nsPresContext
*presContext
= PresContext();
7349 if (presContext
->GetTheme()->
7350 GetWidgetOverflow(presContext
->DeviceContext(), this,
7351 disp
->mAppearance
, &r
)) {
7352 nsRect
& vo
= aOverflowAreas
.VisualOverflow();
7353 vo
.UnionRectEdges(vo
, r
);
7357 ComputeAndIncludeOutlineArea(this, aOverflowAreas
, aNewSize
);
7359 // Nothing in here should affect scrollable overflow.
7360 aOverflowAreas
.VisualOverflow() =
7361 ComputeEffectsRect(this, aOverflowAreas
.VisualOverflow(), aNewSize
);
7363 // Absolute position clipping
7364 nsRect clipPropClipRect
;
7365 bool hasClipPropClip
= GetClipPropClipRect(disp
, &clipPropClipRect
, aNewSize
);
7366 if (hasClipPropClip
) {
7367 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
7368 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
7369 o
.IntersectRect(o
, clipPropClipRect
);
7373 /* If we're transformed, transform the overflow rect by the current transformation. */
7374 bool hasTransform
= IsTransformed();
7375 nsSize oldSize
= aOldSize
? *aOldSize
: mRect
.Size();
7376 bool sizeChanged
= (oldSize
!= aNewSize
);
7378 Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(),
7379 new nsOverflowAreas(aOverflowAreas
));
7380 /* Since our size might not actually have been computed yet, we need to make sure that we use the
7381 * correct dimensions by overriding the stored bounding rectangle with the value the caller has
7382 * ensured us we'll use.
7384 nsRect
newBounds(nsPoint(0, 0), aNewSize
);
7385 // Transform affects both overflow areas.
7386 NS_FOR_FRAME_OVERFLOW_TYPES(otype
) {
7387 nsRect
& o
= aOverflowAreas
.Overflow(otype
);
7388 o
= nsDisplayTransform::TransformRect(o
, this, nsPoint(0, 0), &newBounds
);
7390 if (Preserves3DChildren()) {
7391 ComputePreserve3DChildrenOverflow(aOverflowAreas
, newBounds
);
7392 } else if (sizeChanged
&& ChildrenHavePerspective()) {
7393 RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds
);
7396 Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
7397 if (ChildrenHavePerspective() && sizeChanged
) {
7398 nsRect
newBounds(nsPoint(0, 0), aNewSize
);
7399 RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds
);
7403 bool anyOverflowChanged
;
7404 if (aOverflowAreas
!= nsOverflowAreas(bounds
, bounds
)) {
7405 anyOverflowChanged
= SetOverflowAreas(aOverflowAreas
);
7407 anyOverflowChanged
= ClearOverflowRects();
7410 if (anyOverflowChanged
) {
7411 nsSVGEffects::InvalidateDirectRenderingObservers(this);
7413 return anyOverflowChanged
;
7417 nsIFrame::RecomputePerspectiveChildrenOverflow(const nsStyleContext
* aStartStyle
, const nsRect
* aBounds
)
7419 // Children may check our size when getting our transform, make sure it's valid.
7420 nsSize oldSize
= GetSize();
7422 SetSize(aBounds
->Size());
7424 nsIFrame::ChildListIterator
lists(this);
7425 for (; !lists
.IsDone(); lists
.Next()) {
7426 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
7427 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
7428 nsIFrame
* child
= childFrames
.get();
7429 if (!FrameMaintainsOverflow(child
)) {
7430 continue; // frame does not maintain overflow rects
7432 if (child
->HasPerspective()) {
7433 nsOverflowAreas
* overflow
=
7434 static_cast<nsOverflowAreas
*>(child
->Properties().Get(nsIFrame::InitialOverflowProperty()));
7435 nsRect
bounds(nsPoint(0, 0), child
->GetSize());
7437 nsOverflowAreas overflowCopy
= *overflow
;
7438 child
->FinishAndStoreOverflow(overflowCopy
, bounds
.Size());
7440 nsOverflowAreas boundsOverflow
;
7441 boundsOverflow
.SetAllTo(bounds
);
7442 child
->FinishAndStoreOverflow(boundsOverflow
, bounds
.Size());
7444 } else if (child
->StyleContext()->GetParent() == aStartStyle
||
7445 child
->StyleContext() == aStartStyle
) {
7446 // If a frame is using perspective, then the size used to compute
7447 // perspective-origin is the size of the frame belonging to its parent
7448 // style context. We must find any descendant frames using our size
7449 // (by recurse into frames with the same style context, or a direct
7450 // child style context) to update their overflow rects too.
7451 child
->RecomputePerspectiveChildrenOverflow(aStartStyle
, nullptr);
7455 // Restore our old size just in case something depends on this elesewhere.
7459 /* The overflow rects for leaf nodes in a preserve-3d hierarchy depends on
7460 * the mRect value for their parents (since we use their transform, and transform
7461 * depends on this for transform-origin etc). These weren't necessarily correct
7462 * when we reflowed initially, so walk over all preserve-3d children and repeat the
7463 * overflow calculation.
7466 RecomputePreserve3DChildrenOverflow(nsIFrame
* aFrame
, const nsRect
* aBounds
)
7468 // Children may check our size when getting our transform, make sure it's valid.
7469 nsSize oldSize
= aFrame
->GetSize();
7471 aFrame
->SetSize(aBounds
->Size());
7473 nsIFrame::ChildListIterator
lists(aFrame
);
7474 for (; !lists
.IsDone(); lists
.Next()) {
7475 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
7476 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
7477 nsIFrame
* child
= childFrames
.get();
7478 if (!FrameMaintainsOverflow(child
)) {
7479 continue; // frame does not maintain overflow rects
7481 if (child
->Preserves3DChildren()) {
7482 RecomputePreserve3DChildrenOverflow(child
, nullptr);
7483 } else if (child
->Preserves3D()) {
7484 nsOverflowAreas
* overflow
=
7485 static_cast<nsOverflowAreas
*>(child
->Properties().Get(nsIFrame::InitialOverflowProperty()));
7486 nsRect
bounds(nsPoint(0, 0), child
->GetSize());
7488 nsOverflowAreas overflowCopy
= *overflow
;
7489 child
->FinishAndStoreOverflow(overflowCopy
, bounds
.Size());
7491 nsOverflowAreas boundsOverflow
;
7492 boundsOverflow
.SetAllTo(bounds
);
7493 child
->FinishAndStoreOverflow(boundsOverflow
, bounds
.Size());
7498 // Restore our old size just in case something depends on this elesewhere.
7499 aFrame
->SetSize(oldSize
);
7501 // Only repeat computing our overflow in recursive calls since the initial caller is still
7502 // in the middle of doing this and we don't want an infinite loop.
7504 nsOverflowAreas
* overflow
=
7505 static_cast<nsOverflowAreas
*>(aFrame
->Properties().Get(nsIFrame::InitialOverflowProperty()));
7506 nsRect
bounds(nsPoint(0, 0), aFrame
->GetSize());
7508 nsOverflowAreas overflowCopy
= *overflow
;
7509 overflowCopy
.UnionAllWith(bounds
);
7510 aFrame
->FinishAndStoreOverflow(overflowCopy
, bounds
.Size());
7512 nsOverflowAreas boundsOverflow
;
7513 boundsOverflow
.SetAllTo(bounds
);
7514 aFrame
->FinishAndStoreOverflow(boundsOverflow
, bounds
.Size());
7520 nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas
& aOverflowAreas
, const nsRect
& aBounds
)
7522 // When we are preserving 3d we need to iterate over all children separately.
7523 // If the child also preserves 3d then their overflow will already been in our
7524 // coordinate space, otherwise we need to transform.
7526 // If we're the top frame in a preserve 3d chain then we need to recalculate the overflow
7527 // areas of all our children since they will have used our size/offset which was invalid at
7529 if (!Preserves3D()) {
7530 RecomputePreserve3DChildrenOverflow(this, &aBounds
);
7534 nsRect childScrollable
;
7535 nsIFrame::ChildListIterator
lists(this);
7536 for (; !lists
.IsDone(); lists
.Next()) {
7537 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
7538 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
7539 nsIFrame
* child
= childFrames
.get();
7540 nsPoint offset
= child
->GetPosition();
7541 nsRect visual
= child
->GetVisualOverflowRect();
7542 nsRect scrollable
= child
->GetScrollableOverflowRect();
7543 visual
.MoveBy(offset
);
7544 scrollable
.MoveBy(offset
);
7545 if (child
->Preserves3D()) {
7546 childVisual
= childVisual
.Union(visual
);
7547 childScrollable
= childScrollable
.Union(scrollable
);
7550 childVisual
.Union(nsDisplayTransform::TransformRect(visual
,
7551 this, nsPoint(0,0), &aBounds
));
7553 childScrollable
.Union(nsDisplayTransform::TransformRect(scrollable
,
7554 this, nsPoint(0,0), &aBounds
));
7559 aOverflowAreas
.Overflow(eVisualOverflow
) = aOverflowAreas
.Overflow(eVisualOverflow
).Union(childVisual
);
7560 aOverflowAreas
.Overflow(eScrollableOverflow
) = aOverflowAreas
.Overflow(eScrollableOverflow
).Union(childScrollable
);
7564 nsIFrame::GetDepthInFrameTree() const
7566 uint32_t result
= 0;
7567 for (nsContainerFrame
* ancestor
= GetParent(); ancestor
;
7568 ancestor
= ancestor
->GetParent()) {
7575 nsFrame::ConsiderChildOverflow(nsOverflowAreas
& aOverflowAreas
,
7576 nsIFrame
* aChildFrame
)
7578 aOverflowAreas
.UnionWith(aChildFrame
->GetOverflowAreas() +
7579 aChildFrame
->GetPosition());
7583 * This function takes a frame that is part of a block-in-inline split,
7584 * and _if_ that frame is an anonymous block created by an ib split it
7585 * returns the block's preceding inline. This is needed because the
7586 * split inline's style context is the parent of the anonymous block's
7589 * If aFrame is not an anonymous block, null is returned.
7592 GetIBSplitSiblingForAnonymousBlock(const nsIFrame
* aFrame
)
7594 NS_PRECONDITION(aFrame
, "Must have a non-null frame!");
7595 NS_ASSERTION(aFrame
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
,
7596 "GetIBSplitSibling should only be called on ib-split frames");
7598 nsIAtom
* type
= aFrame
->StyleContext()->GetPseudo();
7599 if (type
!= nsCSSAnonBoxes::mozAnonymousBlock
&&
7600 type
!= nsCSSAnonBoxes::mozAnonymousPositionedBlock
) {
7601 // it's not an anonymous block
7605 // Find the first continuation of the frame. (Ugh. This ends up
7606 // being O(N^2) when it is called O(N) times.)
7607 aFrame
= aFrame
->FirstContinuation();
7610 * Now look up the nsGkAtoms::IBSplitPrevSibling
7613 nsIFrame
*ibSplitSibling
= static_cast<nsIFrame
*>
7614 (aFrame
->Properties().Get(nsIFrame::IBSplitPrevSibling()));
7615 NS_ASSERTION(ibSplitSibling
, "Broken frame tree?");
7616 return ibSplitSibling
;
7620 * Get the parent, corrected for the mangled frame tree resulting from
7621 * having a block within an inline. The result only differs from the
7622 * result of |GetParent| when |GetParent| returns an anonymous block
7623 * that was created for an element that was 'display: inline' because
7624 * that element contained a block.
7626 * Also skip anonymous scrolled-content parents; inherit directly from the
7627 * outer scroll frame.
7630 GetCorrectedParent(const nsIFrame
* aFrame
)
7632 nsIFrame
*parent
= aFrame
->GetParent();
7637 // Outer tables are always anon boxes; if we're in here for an outer
7638 // table, that actually means its the _inner_ table that wants to
7639 // know its parent. So get the pseudo of the inner in that case.
7640 nsIAtom
* pseudo
= aFrame
->StyleContext()->GetPseudo();
7641 if (pseudo
== nsCSSAnonBoxes::tableOuter
) {
7642 pseudo
= aFrame
->GetFirstPrincipalChild()->StyleContext()->GetPseudo();
7644 return nsFrame::CorrectStyleParentFrame(parent
, pseudo
);
7649 nsFrame::CorrectStyleParentFrame(nsIFrame
* aProspectiveParent
,
7650 nsIAtom
* aChildPseudo
)
7652 NS_PRECONDITION(aProspectiveParent
, "Must have a prospective parent");
7654 // Anon boxes are parented to their actual parent already, except
7655 // for non-elements. Those should not be treated as an anon box.
7656 if (aChildPseudo
&& aChildPseudo
!= nsCSSAnonBoxes::mozNonElement
&&
7657 nsCSSAnonBoxes::IsAnonBox(aChildPseudo
)) {
7658 NS_ASSERTION(aChildPseudo
!= nsCSSAnonBoxes::mozAnonymousBlock
&&
7659 aChildPseudo
!= nsCSSAnonBoxes::mozAnonymousPositionedBlock
,
7660 "Should have dealt with kids that have "
7661 "NS_FRAME_PART_OF_IBSPLIT elsewhere");
7662 return aProspectiveParent
;
7665 // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out
7666 // of all pseudo-elements as well. Otherwise ReparentStyleContext could cause
7667 // style data to be out of sync with the frame tree.
7668 nsIFrame
* parent
= aProspectiveParent
;
7670 if (parent
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
) {
7671 nsIFrame
* sibling
= GetIBSplitSiblingForAnonymousBlock(parent
);
7674 // |parent| was a block in an {ib} split; use the inline as
7675 // |the style parent.
7680 nsIAtom
* parentPseudo
= parent
->StyleContext()->GetPseudo();
7681 if (!parentPseudo
||
7682 (!nsCSSAnonBoxes::IsAnonBox(parentPseudo
) &&
7683 // nsPlaceholderFrame pases in nsGkAtoms::placeholderFrame for
7684 // aChildPseudo (even though that's not a valid pseudo-type) just to
7685 // trigger this behavior of walking up to the nearest non-pseudo
7687 aChildPseudo
!= nsGkAtoms::placeholderFrame
)) {
7691 parent
= parent
->GetParent();
7694 if (aProspectiveParent
->StyleContext()->GetPseudo() ==
7695 nsCSSAnonBoxes::viewportScroll
) {
7696 // aProspectiveParent is the scrollframe for a viewport
7697 // and the kids are the anonymous scrollbars
7698 return aProspectiveParent
;
7701 // We can get here if the root element is absolutely positioned.
7702 // We can't test for this very accurately, but it can only happen
7703 // when the prospective parent is a canvas frame.
7704 NS_ASSERTION(aProspectiveParent
->GetType() == nsGkAtoms::canvasFrame
,
7705 "Should have found a parent before this");
7710 nsFrame::DoGetParentStyleContextFrame() const
7712 if (mContent
&& !mContent
->GetParent() &&
7713 !StyleContext()->GetPseudo()) {
7714 // we're a frame for the root. We have no style context parent.
7718 if (!(mState
& NS_FRAME_OUT_OF_FLOW
)) {
7720 * If this frame is an anonymous block created when an inline with a block
7721 * inside it got split, then the parent style context is on its preceding
7722 * inline. We can get to it using GetIBSplitSiblingForAnonymousBlock.
7724 if (mState
& NS_FRAME_PART_OF_IBSPLIT
) {
7725 nsIFrame
* ibSplitSibling
= GetIBSplitSiblingForAnonymousBlock(this);
7726 if (ibSplitSibling
) {
7727 return ibSplitSibling
;
7731 // If this frame is one of the blocks that split an inline, we must
7732 // return the "special" inline parent, i.e., the parent that this
7733 // frame would have if we didn't mangle the frame structure.
7734 return GetCorrectedParent(this);
7737 // We're an out-of-flow frame. For out-of-flow frames, we must
7738 // resolve underneath the placeholder's parent. The placeholder is
7739 // reached from the first-in-flow.
7740 nsIFrame
* placeholder
= PresContext()->FrameManager()->
7741 GetPlaceholderFrameFor(FirstInFlow());
7743 NS_NOTREACHED("no placeholder frame for out-of-flow frame");
7744 return GetCorrectedParent(this);
7746 return placeholder
->GetParentStyleContextFrame();
7750 nsFrame::GetLastLeaf(nsPresContext
* aPresContext
, nsIFrame
**aFrame
)
7752 if (!aFrame
|| !*aFrame
)
7754 nsIFrame
*child
= *aFrame
;
7755 //if we are a block frame then go for the last line of 'this'
7757 child
= child
->GetFirstPrincipalChild();
7759 return;//nothing to do
7760 nsIFrame
* siblingFrame
;
7761 nsIContent
* content
;
7762 //ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
7763 //see bug 278197 comment #12 #13 for details
7764 while ((siblingFrame
= child
->GetNextSibling()) &&
7765 (content
= siblingFrame
->GetContent()) &&
7766 !content
->IsRootOfNativeAnonymousSubtree())
7767 child
= siblingFrame
;
7773 nsFrame::GetFirstLeaf(nsPresContext
* aPresContext
, nsIFrame
**aFrame
)
7775 if (!aFrame
|| !*aFrame
)
7777 nsIFrame
*child
= *aFrame
;
7779 child
= child
->GetFirstPrincipalChild();
7781 return;//nothing to do
7787 nsIFrame::IsFocusable(int32_t *aTabIndex
, bool aWithMouse
)
7789 int32_t tabIndex
= -1;
7791 *aTabIndex
= -1; // Default for early return is not focusable
7793 bool isFocusable
= false;
7795 if (mContent
&& mContent
->IsElement() && IsVisibleConsideringAncestors()) {
7796 const nsStyleUserInterface
* ui
= StyleUserInterface();
7797 if (ui
->mUserFocus
!= NS_STYLE_USER_FOCUS_IGNORE
&&
7798 ui
->mUserFocus
!= NS_STYLE_USER_FOCUS_NONE
) {
7799 // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
7802 isFocusable
= mContent
->IsFocusable(&tabIndex
, aWithMouse
);
7803 if (!isFocusable
&& !aWithMouse
&&
7804 GetType() == nsGkAtoms::scrollFrame
&&
7805 mContent
->IsHTML() &&
7806 !mContent
->IsRootOfNativeAnonymousSubtree() &&
7807 mContent
->GetParent() &&
7808 !mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
)) {
7809 // Elements with scrollable view are focusable with script & tabbable
7810 // Otherwise you couldn't scroll them with keyboard, which is
7811 // an accessibility issue (e.g. Section 508 rules)
7812 // However, we don't make them to be focusable with the mouse,
7813 // because the extra focus outlines are considered unnecessarily ugly.
7814 // When clicked on, the selection position within the element
7815 // will be enough to make them keyboard scrollable.
7816 nsIScrollableFrame
*scrollFrame
= do_QueryFrame(this);
7818 scrollFrame
->GetScrollbarStyles() != ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN
, NS_STYLE_OVERFLOW_HIDDEN
) &&
7819 !scrollFrame
->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) {
7820 // Scroll bars will be used for overflow
7828 *aTabIndex
= tabIndex
;
7834 * @return true if this text frame ends with a newline character which is
7835 * treated as preformatted. It should return false if this is not a text frame.
7838 nsIFrame::HasSignificantTerminalNewline() const
7844 ConvertSVGDominantBaselineToVerticalAlign(uint8_t aDominantBaseline
)
7846 // Most of these are approximate mappings.
7847 switch (aDominantBaseline
) {
7848 case NS_STYLE_DOMINANT_BASELINE_HANGING
:
7849 case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE
:
7850 return NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
;
7851 case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE
:
7852 case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC
:
7853 return NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
;
7854 case NS_STYLE_DOMINANT_BASELINE_CENTRAL
:
7855 case NS_STYLE_DOMINANT_BASELINE_MIDDLE
:
7856 case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL
:
7857 return NS_STYLE_VERTICAL_ALIGN_MIDDLE
;
7858 case NS_STYLE_DOMINANT_BASELINE_AUTO
:
7859 case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC
:
7860 return NS_STYLE_VERTICAL_ALIGN_BASELINE
;
7861 case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT
:
7862 case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE
:
7863 case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE
:
7864 // These three should not simply map to 'baseline', but we don't
7865 // support the complex baseline model that SVG 1.1 has and which
7866 // css3-linebox now defines.
7867 return NS_STYLE_VERTICAL_ALIGN_BASELINE
;
7869 NS_NOTREACHED("unexpected aDominantBaseline value");
7870 return NS_STYLE_VERTICAL_ALIGN_BASELINE
;
7875 nsIFrame::VerticalAlignEnum() const
7878 uint8_t dominantBaseline
;
7879 for (const nsIFrame
* frame
= this; frame
; frame
= frame
->GetParent()) {
7880 dominantBaseline
= frame
->StyleSVGReset()->mDominantBaseline
;
7881 if (dominantBaseline
!= NS_STYLE_DOMINANT_BASELINE_AUTO
||
7882 frame
->GetType() == nsGkAtoms::svgTextFrame
) {
7886 return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline
);
7889 const nsStyleCoord
& verticalAlign
= StyleTextReset()->mVerticalAlign
;
7890 if (verticalAlign
.GetUnit() == eStyleUnit_Enumerated
) {
7891 return verticalAlign
.GetIntValue();
7894 return eInvalidVerticalAlign
;
7898 void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface
* ui
,
7899 nsIFrame::Cursor
& aCursor
)
7901 aCursor
.mCursor
= ui
->mCursor
;
7902 aCursor
.mHaveHotspot
= false;
7903 aCursor
.mHotspotX
= aCursor
.mHotspotY
= 0.0f
;
7905 for (nsCursorImage
*item
= ui
->mCursorArray
,
7906 *item_end
= ui
->mCursorArray
+ ui
->mCursorArrayLength
;
7907 item
< item_end
; ++item
) {
7909 nsresult rv
= item
->GetImage()->GetImageStatus(&status
);
7910 if (NS_SUCCEEDED(rv
) && (status
& imgIRequest::STATUS_LOAD_COMPLETE
)) {
7911 // This is the one we want
7912 item
->GetImage()->GetImage(getter_AddRefs(aCursor
.mContainer
));
7913 aCursor
.mHaveHotspot
= item
->mHaveHotspot
;
7914 aCursor
.mHotspotX
= item
->mHotspotX
;
7915 aCursor
.mHotspotY
= item
->mHotspotY
;
7922 nsFrame::RefreshSizeCache(nsBoxLayoutState
& aState
)
7924 // XXXbz this comment needs some rewriting to make sense in the
7925 // post-reflow-branch world.
7927 // Ok we need to compute our minimum, preferred, and maximum sizes.
7928 // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS.
7929 // 2) Preferred size. This is a little harder. This is the size the block would be
7930 // if it were laid out on an infinite canvas. So we can get this by reflowing
7931 // the block with and INTRINSIC width and height. We can also do a nice optimization
7932 // for incremental reflow. If the reflow is incremental then we can pass a flag to
7933 // have the block compute the preferred width for us! Preferred height can just be
7934 // the minimum height;
7935 // 3) Minimum size. This is a toughy. We can pass the block a flag asking for the max element
7936 // size. That would give us the width. Unfortunately you can only ask for a maxElementSize
7937 // during an incremental reflow. So on other reflows we will just have to use 0.
7938 // The min height on the other hand is fairly easy we need to get the largest
7939 // line height. This can be done with the line iterator.
7941 // if we do have a rendering context
7942 nsRenderingContext
* rendContext
= aState
.GetRenderingContext();
7944 nsPresContext
* presContext
= aState
.PresContext();
7946 // If we don't have any HTML constraints and it's a resize, then nothing in the block
7947 // could have changed, so no refresh is necessary.
7948 nsBoxLayoutMetrics
* metrics
= BoxMetrics();
7949 if (!DoesNeedRecalc(metrics
->mBlockPrefSize
))
7952 // the rect we plan to size to.
7953 nsRect rect
= GetRect();
7955 nsMargin
bp(0,0,0,0);
7956 GetBorderAndPadding(bp
);
7959 // If we're a container for font size inflation, then shrink
7960 // wrapping inside of us should not apply font size inflation.
7961 AutoMaybeDisableFontInflation
an(this);
7963 metrics
->mBlockPrefSize
.width
=
7964 GetPrefISize(rendContext
) + bp
.LeftRight();
7965 metrics
->mBlockMinSize
.width
=
7966 GetMinISize(rendContext
) + bp
.LeftRight();
7970 const WritingMode wm
= aState
.OuterReflowState() ?
7971 aState
.OuterReflowState()->GetWritingMode() : GetWritingMode();
7972 nsHTMLReflowMetrics
desiredSize(wm
);
7973 BoxReflow(aState
, presContext
, desiredSize
, rendContext
,
7975 metrics
->mBlockPrefSize
.width
, NS_UNCONSTRAINEDSIZE
);
7977 metrics
->mBlockMinSize
.height
= 0;
7978 // ok we need the max ascent of the items on the line. So to do this
7979 // ask the block for its line iterator. Get the max ascent.
7980 nsAutoLineIterator lines
= GetLineIterator();
7983 metrics
->mBlockMinSize
.height
= 0;
7985 nsIFrame
* firstFrame
= nullptr;
7986 int32_t framesOnLine
;
7991 lines
->GetLine(count
, &firstFrame
, &framesOnLine
, lineBounds
, &lineFlags
);
7993 if (lineBounds
.height
> metrics
->mBlockMinSize
.height
)
7994 metrics
->mBlockMinSize
.height
= lineBounds
.height
;
7997 } while(firstFrame
);
7999 metrics
->mBlockMinSize
.height
= desiredSize
.Height();
8002 metrics
->mBlockPrefSize
.height
= metrics
->mBlockMinSize
.height
;
8004 if (desiredSize
.BlockStartAscent() ==
8005 nsHTMLReflowMetrics::ASK_FOR_BASELINE
) {
8006 if (!nsLayoutUtils::GetFirstLineBaseline(wm
, this,
8007 &metrics
->mBlockAscent
))
8008 metrics
->mBlockAscent
= GetLogicalBaseline(wm
);
8010 metrics
->mBlockAscent
= desiredSize
.BlockStartAscent();
8013 #ifdef DEBUG_adaptor
8014 printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n", metrics
->mBlockMinSize
.width
,
8015 metrics
->mBlockMinSize
.height
,
8016 metrics
->mBlockPrefSize
.width
,
8017 metrics
->mBlockPrefSize
.height
,
8018 metrics
->mBlockAscent
);
8025 /* virtual */ nsILineIterator
*
8026 nsFrame::GetLineIterator()
8032 nsFrame::GetPrefSize(nsBoxLayoutState
& aState
)
8035 DISPLAY_PREF_SIZE(this, size
);
8036 // If the size is cached, and there are no HTML constraints that we might
8037 // be depending on, then we just return the cached size.
8038 nsBoxLayoutMetrics
*metrics
= BoxMetrics();
8039 if (!DoesNeedRecalc(metrics
->mPrefSize
)) {
8040 return metrics
->mPrefSize
;
8046 // get our size in CSS.
8047 bool widthSet
, heightSet
;
8048 bool completelyRedefined
= nsIFrame::AddCSSPrefSize(this, size
, widthSet
, heightSet
);
8050 // Refresh our caches with new sizes.
8051 if (!completelyRedefined
) {
8052 RefreshSizeCache(aState
);
8053 nsSize blockSize
= metrics
->mBlockPrefSize
;
8055 // notice we don't need to add our borders or padding
8056 // in. That's because the block did it for us.
8058 size
.width
= blockSize
.width
;
8060 size
.height
= blockSize
.height
;
8063 metrics
->mPrefSize
= size
;
8068 nsFrame::GetMinSize(nsBoxLayoutState
& aState
)
8071 DISPLAY_MIN_SIZE(this, size
);
8072 // Don't use the cache if we have HTMLReflowState constraints --- they might have changed
8073 nsBoxLayoutMetrics
*metrics
= BoxMetrics();
8074 if (!DoesNeedRecalc(metrics
->mMinSize
)) {
8075 size
= metrics
->mMinSize
;
8082 // get our size in CSS.
8083 bool widthSet
, heightSet
;
8084 bool completelyRedefined
=
8085 nsIFrame::AddCSSMinSize(aState
, this, size
, widthSet
, heightSet
);
8087 // Refresh our caches with new sizes.
8088 if (!completelyRedefined
) {
8089 RefreshSizeCache(aState
);
8090 nsSize blockSize
= metrics
->mBlockMinSize
;
8093 size
.width
= blockSize
.width
;
8095 size
.height
= blockSize
.height
;
8098 metrics
->mMinSize
= size
;
8103 nsFrame::GetMaxSize(nsBoxLayoutState
& aState
)
8105 nsSize
size(NS_INTRINSICSIZE
, NS_INTRINSICSIZE
);
8106 DISPLAY_MAX_SIZE(this, size
);
8107 // Don't use the cache if we have HTMLReflowState constraints --- they might have changed
8108 nsBoxLayoutMetrics
*metrics
= BoxMetrics();
8109 if (!DoesNeedRecalc(metrics
->mMaxSize
)) {
8110 size
= metrics
->mMaxSize
;
8117 size
= nsBox::GetMaxSize(aState
);
8118 metrics
->mMaxSize
= size
;
8124 nsFrame::GetFlex(nsBoxLayoutState
& aState
)
8126 nsBoxLayoutMetrics
*metrics
= BoxMetrics();
8127 if (!DoesNeedRecalc(metrics
->mFlex
))
8128 return metrics
->mFlex
;
8130 metrics
->mFlex
= nsBox::GetFlex(aState
);
8132 return metrics
->mFlex
;
8136 nsFrame::GetBoxAscent(nsBoxLayoutState
& aState
)
8138 nsBoxLayoutMetrics
*metrics
= BoxMetrics();
8139 if (!DoesNeedRecalc(metrics
->mAscent
))
8140 return metrics
->mAscent
;
8142 if (IsCollapsed()) {
8143 metrics
->mAscent
= 0;
8145 // Refresh our caches with new sizes.
8146 RefreshSizeCache(aState
);
8147 metrics
->mAscent
= metrics
->mBlockAscent
;
8150 return metrics
->mAscent
;
8154 nsFrame::DoLayout(nsBoxLayoutState
& aState
)
8156 nsRect
ourRect(mRect
);
8158 nsRenderingContext
* rendContext
= aState
.GetRenderingContext();
8159 nsPresContext
* presContext
= aState
.PresContext();
8160 WritingMode ourWM
= GetWritingMode();
8161 const WritingMode outerWM
= aState
.OuterReflowState() ?
8162 aState
.OuterReflowState()->GetWritingMode() : ourWM
;
8163 nsHTMLReflowMetrics
desiredSize(outerWM
);
8164 LogicalSize ourSize
= GetLogicalSize(outerWM
);
8168 BoxReflow(aState
, presContext
, desiredSize
, rendContext
,
8169 ourRect
.x
, ourRect
.y
, ourRect
.width
, ourRect
.height
);
8171 if (IsCollapsed()) {
8172 SetSize(nsSize(0, 0));
8175 // if our child needs to be bigger. This might happend with
8176 // wrapping text. There is no way to predict its height until we
8177 // reflow it. Now that we know the height reshuffle upward.
8178 if (desiredSize
.ISize(outerWM
) > ourSize
.ISize(outerWM
) ||
8179 desiredSize
.BSize(outerWM
) > ourSize
.BSize(outerWM
)) {
8183 printf(" GREW from (%d,%d) -> (%d,%d)\n",
8184 ourSize
.ISize(outerWM
), ourSize
.BSize(outerWM
),
8185 desiredSize
.ISize(outerWM
), desiredSize
.BSize(outerWM
));
8188 if (desiredSize
.ISize(outerWM
) > ourSize
.ISize(outerWM
)) {
8189 ourSize
.ISize(outerWM
) = desiredSize
.ISize(outerWM
);
8192 if (desiredSize
.BSize(outerWM
) > ourSize
.BSize(outerWM
)) {
8193 ourSize
.BSize(outerWM
) = desiredSize
.BSize(outerWM
);
8197 // ensure our size is what we think is should be. Someone could have
8198 // reset the frame to be smaller or something dumb like that.
8199 SetSize(ourSize
.ConvertTo(ourWM
, outerWM
));
8203 // Should we do this if IsCollapsed() is true?
8204 LogicalSize
size(GetLogicalSize(outerWM
));
8205 desiredSize
.ISize(outerWM
) = size
.ISize(outerWM
);
8206 desiredSize
.BSize(outerWM
) = size
.BSize(outerWM
);
8207 desiredSize
.UnionOverflowAreasWithDesiredBounds();
8209 if (HasAbsolutelyPositionedChildren()) {
8210 // Set up a |reflowState| to pass into ReflowAbsoluteFrames
8211 nsHTMLReflowState
reflowState(aState
.PresContext(), this,
8212 aState
.GetRenderingContext(),
8213 LogicalSize(ourWM
, ISize(),
8214 NS_UNCONSTRAINEDSIZE
),
8215 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE
);
8217 AddStateBits(NS_FRAME_IN_REFLOW
);
8218 // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
8219 // (just a dummy value; hopefully that's OK)
8220 nsReflowStatus reflowStatus
= NS_FRAME_COMPLETE
;
8221 ReflowAbsoluteFrames(aState
.PresContext(), desiredSize
,
8222 reflowState
, reflowStatus
);
8223 RemoveStateBits(NS_FRAME_IN_REFLOW
);
8226 nsSize
oldSize(ourRect
.Size());
8227 FinishAndStoreOverflow(desiredSize
.mOverflowAreas
,
8228 size
.GetPhysicalSize(outerWM
), &oldSize
);
8236 nsFrame::BoxReflow(nsBoxLayoutState
& aState
,
8237 nsPresContext
* aPresContext
,
8238 nsHTMLReflowMetrics
& aDesiredSize
,
8239 nsRenderingContext
* aRenderingContext
,
8246 DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
8249 nsAdaptorAddIndents();
8250 printf("Reflowing: ");
8251 nsFrame::ListTag(stdout
, mFrame
);
8256 nsBoxLayoutMetrics
*metrics
= BoxMetrics();
8257 nsReflowStatus status
= NS_FRAME_COMPLETE
;
8258 WritingMode wm
= aDesiredSize
.GetWritingMode();
8260 bool needsReflow
= NS_SUBTREE_DIRTY(this);
8262 // if we don't need a reflow then
8263 // lets see if we are already that size. Yes? then don't even reflow. We are done.
8266 if (aWidth
!= NS_INTRINSICSIZE
&& aHeight
!= NS_INTRINSICSIZE
) {
8268 // if the new calculated size has a 0 width or a 0 height
8269 if ((metrics
->mLastSize
.width
== 0 || metrics
->mLastSize
.height
== 0) && (aWidth
== 0 || aHeight
== 0)) {
8270 needsReflow
= false;
8271 aDesiredSize
.Width() = aWidth
;
8272 aDesiredSize
.Height() = aHeight
;
8273 SetSize(aDesiredSize
.Size(wm
).ConvertTo(GetWritingMode(), wm
));
8275 aDesiredSize
.Width() = metrics
->mLastSize
.width
;
8276 aDesiredSize
.Height() = metrics
->mLastSize
.height
;
8278 // remove the margin. The rect of our child does not include it but our calculated size does.
8279 // don't reflow if we are already the right size
8280 if (metrics
->mLastSize
.width
== aWidth
&& metrics
->mLastSize
.height
== aHeight
)
8281 needsReflow
= false;
8287 // if the width or height are intrinsic alway reflow because
8288 // we don't know what it should be.
8293 // ok now reflow the child into the spacers calculated space
8296 aDesiredSize
.ClearSize();
8298 // create a reflow state to tell our child to flow at the given size.
8300 // Construct a bogus parent reflow state so that there's a usable
8301 // containing block reflow state.
8302 nsMargin
margin(0,0,0,0);
8305 nsSize
parentSize(aWidth
, aHeight
);
8306 if (parentSize
.height
!= NS_INTRINSICSIZE
)
8307 parentSize
.height
+= margin
.TopBottom();
8308 if (parentSize
.width
!= NS_INTRINSICSIZE
)
8309 parentSize
.width
+= margin
.LeftRight();
8311 nsIFrame
*parentFrame
= GetParent();
8312 nsFrameState savedState
= parentFrame
->GetStateBits();
8313 WritingMode parentWM
= parentFrame
->GetWritingMode();
8315 parentReflowState(aPresContext
, parentFrame
, aRenderingContext
,
8316 LogicalSize(parentWM
, parentSize
),
8317 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE
);
8318 parentFrame
->RemoveStateBits(~nsFrameState(0));
8319 parentFrame
->AddStateBits(savedState
);
8321 // This may not do very much useful, but it's probably worth trying.
8322 if (parentSize
.width
!= NS_INTRINSICSIZE
)
8323 parentReflowState
.SetComputedWidth(std::max(parentSize
.width
, 0));
8324 if (parentSize
.height
!= NS_INTRINSICSIZE
)
8325 parentReflowState
.SetComputedHeight(std::max(parentSize
.height
, 0));
8326 parentReflowState
.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
8327 // XXX use box methods
8328 parentFrame
->GetPadding(parentReflowState
.ComputedPhysicalPadding());
8329 parentFrame
->GetBorder(parentReflowState
.ComputedPhysicalBorderPadding());
8330 parentReflowState
.ComputedPhysicalBorderPadding() +=
8331 parentReflowState
.ComputedPhysicalPadding();
8333 // Construct the parent chain manually since constructing it normally
8334 // messes up dimensions.
8335 const nsHTMLReflowState
*outerReflowState
= aState
.OuterReflowState();
8336 NS_ASSERTION(!outerReflowState
|| outerReflowState
->frame
!= this,
8337 "in and out of XUL on a single frame?");
8338 const nsHTMLReflowState
* parentRS
;
8339 if (outerReflowState
&& outerReflowState
->frame
== parentFrame
) {
8340 // We're a frame (such as a text control frame) that jumps into
8341 // box reflow and then straight out of it on the child frame.
8342 // This means we actually have a real parent reflow state.
8343 // nsLayoutUtils::InflationMinFontSizeFor used to need this to be
8344 // linked up correctly for text control frames, so do so here).
8345 parentRS
= outerReflowState
;
8347 parentRS
= &parentReflowState
;
8350 // XXX Is it OK that this reflow state has only one ancestor?
8351 // (It used to have a bogus parent, skipping all the boxes).
8352 WritingMode wm
= GetWritingMode();
8353 LogicalSize
logicalSize(wm
, nsSize(aWidth
, aHeight
));
8354 logicalSize
.BSize(wm
) = NS_INTRINSICSIZE
;
8355 nsHTMLReflowState
reflowState(aPresContext
, *parentRS
, this,
8356 logicalSize
, -1, -1,
8357 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE
);
8359 // XXX_jwir3: This is somewhat fishy. If this is actually changing the value
8360 // here (which it might be), then we should make sure that it's
8361 // correct the first time around, rather than changing it later.
8362 reflowState
.mCBReflowState
= parentRS
;
8364 reflowState
.mReflowDepth
= aState
.GetReflowDepth();
8366 // mComputedWidth and mComputedHeight are content-box, not
8368 if (aWidth
!= NS_INTRINSICSIZE
) {
8369 nscoord computedWidth
=
8370 aWidth
- reflowState
.ComputedPhysicalBorderPadding().LeftRight();
8371 computedWidth
= std::max(computedWidth
, 0);
8372 reflowState
.SetComputedWidth(computedWidth
);
8375 // Most child frames of box frames (e.g. subdocument or scroll frames)
8376 // need to be constrained to the provided size and overflow as necessary.
8377 // The one exception are block frames, because we need to know their
8378 // natural height excluding any overflow area which may be caused by
8379 // various CSS effects such as shadow or outline.
8380 if (!IsFrameOfType(eBlockFrame
)) {
8381 if (aHeight
!= NS_INTRINSICSIZE
) {
8382 nscoord computedHeight
=
8383 aHeight
- reflowState
.ComputedPhysicalBorderPadding().TopBottom();
8384 computedHeight
= std::max(computedHeight
, 0);
8385 reflowState
.SetComputedHeight(computedHeight
);
8387 reflowState
.SetComputedHeight(
8388 ComputeSize(aRenderingContext
, wm
,
8390 logicalSize
.ISize(wm
),
8391 reflowState
.ComputedLogicalMargin().Size(wm
),
8392 reflowState
.ComputedLogicalBorderPadding().Size(wm
) -
8393 reflowState
.ComputedLogicalPadding().Size(wm
),
8394 reflowState
.ComputedLogicalPadding().Size(wm
),
8399 // Box layout calls SetRect before Layout, whereas non-box layout
8400 // calls SetRect after Reflow.
8401 // XXX Perhaps we should be doing this by twiddling the rect back to
8402 // mLastSize before calling Reflow and then switching it back, but
8403 // However, mLastSize can also be the size passed to BoxReflow by
8404 // RefreshSizeCache, so that doesn't really make sense.
8405 if (metrics
->mLastSize
.width
!= aWidth
) {
8406 reflowState
.mFlags
.mHResize
= true;
8408 // When font size inflation is enabled, a horizontal resize
8409 // requires a full reflow. See nsHTMLReflowState::InitResizeFlags
8410 // for more details.
8411 if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext
)) {
8412 AddStateBits(NS_FRAME_IS_DIRTY
);
8415 if (metrics
->mLastSize
.height
!= aHeight
)
8416 reflowState
.mFlags
.mVResize
= true;
8419 nsAdaptorAddIndents();
8420 printf("Size=(%d,%d)\n",reflowState
.ComputedWidth(),
8421 reflowState
.ComputedHeight());
8422 nsAdaptorAddIndents();
8423 nsAdaptorPrintReason(reflowState
);
8427 // place the child and reflow
8428 WillReflow(aPresContext
);
8430 Reflow(aPresContext
, aDesiredSize
, reflowState
, status
);
8432 NS_ASSERTION(NS_FRAME_IS_COMPLETE(status
), "bad status");
8434 uint32_t layoutFlags
= aState
.LayoutFlags();
8435 nsContainerFrame::FinishReflowChild(this, aPresContext
, aDesiredSize
,
8436 &reflowState
, aX
, aY
, layoutFlags
| NS_FRAME_NO_MOVE_FRAME
);
8438 // Save the ascent. (bug 103925)
8439 if (IsCollapsed()) {
8440 metrics
->mAscent
= 0;
8442 if (aDesiredSize
.BlockStartAscent() ==
8443 nsHTMLReflowMetrics::ASK_FOR_BASELINE
) {
8444 if (!nsLayoutUtils::GetFirstLineBaseline(wm
, this, &metrics
->mAscent
))
8445 metrics
->mAscent
= GetLogicalBaseline(wm
);
8447 metrics
->mAscent
= aDesiredSize
.BlockStartAscent();
8451 aDesiredSize
.SetBlockStartAscent(metrics
->mBlockAscent
);
8455 if (aHeight
!= NS_INTRINSICSIZE
&& aDesiredSize
.Height() != aHeight
)
8457 nsAdaptorAddIndents();
8458 printf("*****got taller!*****\n");
8461 if (aWidth
!= NS_INTRINSICSIZE
&& aDesiredSize
.Width() != aWidth
)
8463 nsAdaptorAddIndents();
8464 printf("*****got wider!******\n");
8469 if (aWidth
== NS_INTRINSICSIZE
)
8470 aWidth
= aDesiredSize
.Width();
8472 if (aHeight
== NS_INTRINSICSIZE
)
8473 aHeight
= aDesiredSize
.Height();
8475 metrics
->mLastSize
.width
= aDesiredSize
.Width();
8476 metrics
->mLastSize
.height
= aDesiredSize
.Height();
8484 nsFrame::BoxMetrics() const
8486 nsBoxLayoutMetrics
* metrics
=
8487 static_cast<nsBoxLayoutMetrics
*>(Properties().Get(BoxMetricsProperty()));
8488 NS_ASSERTION(metrics
, "A box layout method was called but InitBoxMetrics was never called");
8493 nsIFrame::AddInPopupStateBitToDescendants(nsIFrame
* aFrame
)
8495 aFrame
->AddStateBits(NS_FRAME_IN_POPUP
);
8497 nsAutoTArray
<nsIFrame::ChildList
,4> childListArray
;
8498 aFrame
->GetCrossDocChildLists(&childListArray
);
8500 nsIFrame::ChildListArrayIterator
lists(childListArray
);
8501 for (; !lists
.IsDone(); lists
.Next()) {
8502 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
8503 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
8504 AddInPopupStateBitToDescendants(childFrames
.get());
8510 nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame
* aFrame
)
8512 if (!aFrame
->HasAnyStateBits(NS_FRAME_IN_POPUP
) ||
8513 nsLayoutUtils::IsPopup(aFrame
)) {
8517 aFrame
->RemoveStateBits(NS_FRAME_IN_POPUP
);
8519 nsAutoTArray
<nsIFrame::ChildList
,4> childListArray
;
8520 aFrame
->GetCrossDocChildLists(&childListArray
);
8522 nsIFrame::ChildListArrayIterator
lists(childListArray
);
8523 for (; !lists
.IsDone(); lists
.Next()) {
8524 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
8525 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
8526 RemoveInPopupStateBitFromDescendants(childFrames
.get());
8532 nsIFrame::SetParent(nsContainerFrame
* aParent
)
8534 bool wasBoxWrapped
= ::IsBoxWrapped(this);
8536 if (!wasBoxWrapped
&& ::IsBoxWrapped(this)) {
8537 ::InitBoxMetrics(this, true);
8538 } else if (wasBoxWrapped
&& !::IsBoxWrapped(this)) {
8539 Properties().Delete(BoxMetricsProperty());
8542 if (GetStateBits() & (NS_FRAME_HAS_VIEW
| NS_FRAME_HAS_CHILD_WITH_VIEW
)) {
8543 for (nsIFrame
* f
= aParent
;
8544 f
&& !(f
->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW
);
8545 f
= f
->GetParent()) {
8546 f
->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW
);
8550 if (HasInvalidFrameInSubtree()) {
8551 for (nsIFrame
* f
= aParent
;
8552 f
&& !f
->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT
);
8553 f
= nsLayoutUtils::GetCrossDocParentFrame(f
)) {
8554 f
->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT
);
8558 if (aParent
->HasAnyStateBits(NS_FRAME_IN_POPUP
)) {
8559 AddInPopupStateBitToDescendants(this);
8561 RemoveInPopupStateBitFromDescendants(this);
8564 // If our new parent only has invalid children, then we just invalidate
8565 // ourselves too. This is probably faster than clearing the flag all
8566 // the way up the frame tree.
8567 if (aParent
->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT
)) {
8573 nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder
* aBuilder
,
8574 nsDisplayList
* aList
)
8577 GetContent()->IsXUL() &&
8578 GetContent()->HasAttr(kNameSpaceID_None
, nsGkAtoms::layer
)) {
8579 aList
->AppendNewToTop(new (aBuilder
)
8580 nsDisplayOwnLayer(aBuilder
, this, aList
));
8585 nsIFrame::IsSelected() const
8587 return (GetContent() && GetContent()->IsSelectionDescendant()) ?
8588 IsFrameSelected() : false;
8592 nsIFrame::DestroySurface(void* aPropertyValue
)
8594 static_cast<gfxASurface
*>(aPropertyValue
)->Release();
8598 nsIFrame::DestroyDT(void* aPropertyValue
)
8600 static_cast<mozilla::gfx::DrawTarget
*>(aPropertyValue
)->Release();
8604 nsIFrame::DestroyRegion(void* aPropertyValue
)
8606 delete static_cast<nsRegion
*>(aPropertyValue
);
8610 nsIFrame::IsPseudoStackingContextFromStyle() {
8611 const nsStyleDisplay
* disp
= StyleDisplay();
8612 // If you change this, also change the computation of pseudoStackingContext
8613 // in BuildDisplayListForChild()
8614 return disp
->mOpacity
!= 1.0f
||
8615 disp
->IsPositioned(this) ||
8616 disp
->IsFloating(this) ||
8617 (disp
->mWillChangeBitField
& NS_STYLE_WILL_CHANGE_STACKING_CONTEXT
);
8621 nsIFrame::GetPseudoElement(nsCSSPseudoElements::Type aType
)
8623 nsIFrame
* frame
= nullptr;
8625 if (aType
== nsCSSPseudoElements::ePseudo_before
) {
8626 frame
= nsLayoutUtils::GetBeforeFrame(this);
8627 } else if (aType
== nsCSSPseudoElements::ePseudo_after
) {
8628 frame
= nsLayoutUtils::GetAfterFrame(this);
8632 nsIContent
* content
= frame
->GetContent();
8633 if (content
->IsElement()) {
8634 return content
->AsElement();
8641 nsIFrame::ContentOffsets::ContentOffsets()
8645 nsIFrame::ContentOffsets::ContentOffsets(const ContentOffsets
& rhs
)
8646 : content(rhs
.content
),
8648 secondaryOffset(rhs
.secondaryOffset
),
8649 associate(rhs
.associate
)
8653 nsIFrame::ContentOffsets::~ContentOffsets()
8657 nsIFrame::CaretPosition::CaretPosition()
8662 nsIFrame::CaretPosition::~CaretPosition()
8666 // Box layout debugging
8668 int32_t gIndent2
= 0;
8671 nsAdaptorAddIndents()
8673 for(int32_t i
=0; i
< gIndent2
; i
++)
8680 nsAdaptorPrintReason(nsHTMLReflowState
& aReflowState
)
8682 char* reflowReasonString
;
8684 switch(aReflowState
.reason
)
8686 case eReflowReason_Initial
:
8687 reflowReasonString
= "initial";
8690 case eReflowReason_Resize
:
8691 reflowReasonString
= "resize";
8693 case eReflowReason_Dirty
:
8694 reflowReasonString
= "dirty";
8696 case eReflowReason_StyleChange
:
8697 reflowReasonString
= "stylechange";
8699 case eReflowReason_Incremental
:
8701 switch (aReflowState
.reflowCommand
->Type()) {
8702 case eReflowType_StyleChanged
:
8703 reflowReasonString
= "incremental (StyleChanged)";
8705 case eReflowType_ReflowDirty
:
8706 reflowReasonString
= "incremental (ReflowDirty)";
8709 reflowReasonString
= "incremental (Unknown)";
8714 reflowReasonString
= "unknown";
8718 printf("%s",reflowReasonString
);
8724 nsFrame::GetBoxName(nsAutoString
& aName
)
8726 GetFrameName(aName
);
8732 GetTagName(nsFrame
* aFrame
, nsIContent
* aContent
, int aResultSize
,
8736 PR_snprintf(aResult
, aResultSize
, "%s@%p",
8737 nsAtomCString(aContent
->Tag()).get(), aFrame
);
8740 PR_snprintf(aResult
, aResultSize
, "@%p", aFrame
);
8745 nsFrame::Trace(const char* aMethod
, bool aEnter
)
8747 if (NS_FRAME_LOG_TEST(gLogModule
, NS_FRAME_TRACE_CALLS
)) {
8749 GetTagName(this, mContent
, sizeof(tagbuf
), tagbuf
);
8750 PR_LogPrint("%s: %s %s", tagbuf
, aEnter
? "enter" : "exit", aMethod
);
8755 nsFrame::Trace(const char* aMethod
, bool aEnter
, nsReflowStatus aStatus
)
8757 if (NS_FRAME_LOG_TEST(gLogModule
, NS_FRAME_TRACE_CALLS
)) {
8759 GetTagName(this, mContent
, sizeof(tagbuf
), tagbuf
);
8760 PR_LogPrint("%s: %s %s, status=%scomplete%s",
8761 tagbuf
, aEnter
? "enter" : "exit", aMethod
,
8762 NS_FRAME_IS_NOT_COMPLETE(aStatus
) ? "not" : "",
8763 (NS_FRAME_REFLOW_NEXTINFLOW
& aStatus
) ? "+reflow" : "");
8768 nsFrame::TraceMsg(const char* aFormatString
, ...)
8770 if (NS_FRAME_LOG_TEST(gLogModule
, NS_FRAME_TRACE_CALLS
)) {
8771 // Format arguments into a buffer
8774 va_start(ap
, aFormatString
);
8775 PR_vsnprintf(argbuf
, sizeof(argbuf
), aFormatString
, ap
);
8779 GetTagName(this, mContent
, sizeof(tagbuf
), tagbuf
);
8780 PR_LogPrint("%s: %s", tagbuf
, argbuf
);
8785 nsFrame::VerifyDirtyBitSet(const nsFrameList
& aFrameList
)
8787 for (nsFrameList::Enumerator
e(aFrameList
); !e
.AtEnd(); e
.Next()) {
8788 NS_ASSERTION(e
.get()->GetStateBits() & NS_FRAME_IS_DIRTY
,
8789 "dirty bit not set");
8793 // Start Display Reflow
8796 DR_cookie::DR_cookie(nsPresContext
* aPresContext
,
8798 const nsHTMLReflowState
& aReflowState
,
8799 nsHTMLReflowMetrics
& aMetrics
,
8800 nsReflowStatus
& aStatus
)
8801 :mPresContext(aPresContext
), mFrame(aFrame
), mReflowState(aReflowState
), mMetrics(aMetrics
), mStatus(aStatus
)
8803 MOZ_COUNT_CTOR(DR_cookie
);
8804 mValue
= nsFrame::DisplayReflowEnter(aPresContext
, mFrame
, mReflowState
);
8807 DR_cookie::~DR_cookie()
8809 MOZ_COUNT_DTOR(DR_cookie
);
8810 nsFrame::DisplayReflowExit(mPresContext
, mFrame
, mMetrics
, mStatus
, mValue
);
8813 DR_layout_cookie::DR_layout_cookie(nsIFrame
* aFrame
)
8816 MOZ_COUNT_CTOR(DR_layout_cookie
);
8817 mValue
= nsFrame::DisplayLayoutEnter(mFrame
);
8820 DR_layout_cookie::~DR_layout_cookie()
8822 MOZ_COUNT_DTOR(DR_layout_cookie
);
8823 nsFrame::DisplayLayoutExit(mFrame
, mValue
);
8826 DR_intrinsic_width_cookie::DR_intrinsic_width_cookie(
8834 MOZ_COUNT_CTOR(DR_intrinsic_width_cookie
);
8835 mValue
= nsFrame::DisplayIntrinsicISizeEnter(mFrame
, mType
);
8838 DR_intrinsic_width_cookie::~DR_intrinsic_width_cookie()
8840 MOZ_COUNT_DTOR(DR_intrinsic_width_cookie
);
8841 nsFrame::DisplayIntrinsicISizeExit(mFrame
, mType
, mResult
, mValue
);
8844 DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(
8852 MOZ_COUNT_CTOR(DR_intrinsic_size_cookie
);
8853 mValue
= nsFrame::DisplayIntrinsicSizeEnter(mFrame
, mType
);
8856 DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie()
8858 MOZ_COUNT_DTOR(DR_intrinsic_size_cookie
);
8859 nsFrame::DisplayIntrinsicSizeExit(mFrame
, mType
, mResult
, mValue
);
8862 DR_init_constraints_cookie::DR_init_constraints_cookie(
8864 nsHTMLReflowState
* aState
,
8867 const nsMargin
* aMargin
,
8868 const nsMargin
* aPadding
)
8872 MOZ_COUNT_CTOR(DR_init_constraints_cookie
);
8873 mValue
= nsHTMLReflowState::DisplayInitConstraintsEnter(mFrame
, mState
,
8874 aCBWidth
, aCBHeight
,
8878 DR_init_constraints_cookie::~DR_init_constraints_cookie()
8880 MOZ_COUNT_DTOR(DR_init_constraints_cookie
);
8881 nsHTMLReflowState::DisplayInitConstraintsExit(mFrame
, mState
, mValue
);
8884 DR_init_offsets_cookie::DR_init_offsets_cookie(
8886 nsCSSOffsetState
* aState
,
8887 nscoord aHorizontalPercentBasis
,
8888 nscoord aVerticalPercentBasis
,
8889 const nsMargin
* aMargin
,
8890 const nsMargin
* aPadding
)
8894 MOZ_COUNT_CTOR(DR_init_offsets_cookie
);
8895 mValue
= nsCSSOffsetState::DisplayInitOffsetsEnter(mFrame
, mState
,
8896 aHorizontalPercentBasis
,
8897 aVerticalPercentBasis
,
8901 DR_init_offsets_cookie::~DR_init_offsets_cookie()
8903 MOZ_COUNT_DTOR(DR_init_offsets_cookie
);
8904 nsCSSOffsetState::DisplayInitOffsetsExit(mFrame
, mState
, mValue
);
8907 DR_init_type_cookie::DR_init_type_cookie(
8909 nsHTMLReflowState
* aState
)
8913 MOZ_COUNT_CTOR(DR_init_type_cookie
);
8914 mValue
= nsHTMLReflowState::DisplayInitFrameTypeEnter(mFrame
, mState
);
8917 DR_init_type_cookie::~DR_init_type_cookie()
8919 MOZ_COUNT_DTOR(DR_init_type_cookie
);
8920 nsHTMLReflowState::DisplayInitFrameTypeExit(mFrame
, mState
, mValue
);
8923 struct DR_FrameTypeInfo
;
8924 struct DR_FrameTreeNode
;
8932 void AddFrameTypeInfo(nsIAtom
* aFrameType
,
8933 const char* aFrameNameAbbrev
,
8934 const char* aFrameName
);
8935 DR_FrameTypeInfo
* GetFrameTypeInfo(nsIAtom
* aFrameType
);
8936 DR_FrameTypeInfo
* GetFrameTypeInfo(char* aFrameName
);
8937 void InitFrameTypeTable();
8938 DR_FrameTreeNode
* CreateTreeNode(nsIFrame
* aFrame
,
8939 const nsHTMLReflowState
* aReflowState
);
8940 void FindMatchingRule(DR_FrameTreeNode
& aNode
);
8941 bool RuleMatches(DR_Rule
& aRule
,
8942 DR_FrameTreeNode
& aNode
);
8943 bool GetToken(FILE* aFile
,
8946 DR_Rule
* ParseRule(FILE* aFile
);
8947 void ParseRulesFile();
8948 void AddRule(nsTArray
<DR_Rule
*>& aRules
,
8950 bool IsWhiteSpace(int c
);
8951 bool GetNumber(char* aBuf
,
8953 void PrettyUC(nscoord aSize
,
8955 void PrintMargin(const char* tag
, const nsMargin
* aMargin
);
8956 void DisplayFrameTypeInfo(nsIFrame
* aFrame
,
8958 void DeleteTreeNode(DR_FrameTreeNode
& aNode
);
8965 bool mIndentUndisplayedFrames
;
8966 bool mDisplayPixelErrors
;
8967 nsTArray
<DR_Rule
*> mWildRules
;
8968 nsTArray
<DR_FrameTypeInfo
> mFrameTypeTable
;
8969 // reflow specific state
8970 nsTArray
<DR_FrameTreeNode
*> mFrameTreeLeaves
;
8973 static DR_State
*DR_state
; // the one and only DR_State
8977 explicit DR_RulePart(nsIAtom
* aFrameType
) : mFrameType(aFrameType
), mNext(0) {}
8980 nsIAtom
* mFrameType
;
8984 void DR_RulePart::Destroy()
8994 DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) {
8995 MOZ_COUNT_CTOR(DR_Rule
);
8998 if (mTarget
) mTarget
->Destroy();
8999 MOZ_COUNT_DTOR(DR_Rule
);
9001 void AddPart(nsIAtom
* aFrameType
);
9004 DR_RulePart
* mTarget
;
9008 void DR_Rule::AddPart(nsIAtom
* aFrameType
)
9010 DR_RulePart
* newPart
= new DR_RulePart(aFrameType
);
9011 newPart
->mNext
= mTarget
;
9016 struct DR_FrameTypeInfo
9018 DR_FrameTypeInfo(nsIAtom
* aFrmeType
, const char* aFrameNameAbbrev
, const char* aFrameName
);
9019 ~DR_FrameTypeInfo() {
9020 int32_t numElements
;
9021 numElements
= mRules
.Length();
9022 for (int32_t i
= numElements
- 1; i
>= 0; i
--) {
9023 delete mRules
.ElementAt(i
);
9028 char mNameAbbrev
[16];
9030 nsTArray
<DR_Rule
*> mRules
;
9032 DR_FrameTypeInfo
& operator=(const DR_FrameTypeInfo
&) MOZ_DELETE
;
9035 DR_FrameTypeInfo::DR_FrameTypeInfo(nsIAtom
* aFrameType
,
9036 const char* aFrameNameAbbrev
,
9037 const char* aFrameName
)
9040 PL_strncpyz(mNameAbbrev
, aFrameNameAbbrev
, sizeof(mNameAbbrev
));
9041 PL_strncpyz(mName
, aFrameName
, sizeof(mName
));
9044 struct DR_FrameTreeNode
9046 DR_FrameTreeNode(nsIFrame
* aFrame
, DR_FrameTreeNode
* aParent
) : mFrame(aFrame
), mParent(aParent
), mDisplay(0), mIndent(0)
9048 MOZ_COUNT_CTOR(DR_FrameTreeNode
);
9053 MOZ_COUNT_DTOR(DR_FrameTreeNode
);
9057 DR_FrameTreeNode
* mParent
;
9062 // DR_State implementation
9064 DR_State::DR_State()
9065 : mInited(false), mActive(false), mCount(0), mAssert(-1), mIndent(0),
9066 mIndentUndisplayedFrames(false), mDisplayPixelErrors(false)
9068 MOZ_COUNT_CTOR(DR_State
);
9071 void DR_State::Init()
9073 char* env
= PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT");
9076 if (GetNumber(env
, num
))
9079 printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env
);
9082 env
= PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
9084 if (GetNumber(env
, num
))
9087 printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env
);
9090 env
= PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
9092 if (GetNumber(env
, num
))
9093 mIndentUndisplayedFrames
= num
;
9095 printf("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", env
);
9098 env
= PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
9100 if (GetNumber(env
, num
))
9101 mDisplayPixelErrors
= num
;
9103 printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", env
);
9106 InitFrameTypeTable();
9111 DR_State::~DR_State()
9113 MOZ_COUNT_DTOR(DR_State
);
9114 int32_t numElements
, i
;
9115 numElements
= mWildRules
.Length();
9116 for (i
= numElements
- 1; i
>= 0; i
--) {
9117 delete mWildRules
.ElementAt(i
);
9119 numElements
= mFrameTreeLeaves
.Length();
9120 for (i
= numElements
- 1; i
>= 0; i
--) {
9121 delete mFrameTreeLeaves
.ElementAt(i
);
9125 bool DR_State::GetNumber(char* aBuf
,
9128 if (sscanf(aBuf
, "%d", &aNumber
) > 0)
9134 bool DR_State::IsWhiteSpace(int c
) {
9135 return (c
== ' ') || (c
== '\t') || (c
== '\n') || (c
== '\r');
9138 bool DR_State::GetToken(FILE* aFile
,
9142 bool haveToken
= false;
9144 // get the 1st non whitespace char
9146 for (c
= getc(aFile
); (c
> 0) && IsWhiteSpace(c
); c
= getc(aFile
)) {
9152 // get everything up to the next whitespace char
9154 for (cX
= 1; cX
+ 1 < aBufSize
; cX
++) {
9161 if (IsWhiteSpace(c
)) {
9174 DR_Rule
* DR_State::ParseRule(FILE* aFile
)
9178 DR_Rule
* rule
= nullptr;
9179 while (GetToken(aFile
, buf
, sizeof(buf
))) {
9180 if (GetNumber(buf
, doDisplay
)) {
9182 rule
->mDisplay
= !!doDisplay
;
9186 printf("unexpected token - %s \n", buf
);
9193 if (strcmp(buf
, "*") == 0) {
9194 rule
->AddPart(nullptr);
9197 DR_FrameTypeInfo
* info
= GetFrameTypeInfo(buf
);
9199 rule
->AddPart(info
->mType
);
9202 printf("invalid frame type - %s \n", buf
);
9210 void DR_State::AddRule(nsTArray
<DR_Rule
*>& aRules
,
9213 int32_t numRules
= aRules
.Length();
9214 for (int32_t ruleX
= 0; ruleX
< numRules
; ruleX
++) {
9215 DR_Rule
* rule
= aRules
.ElementAt(ruleX
);
9216 NS_ASSERTION(rule
, "program error");
9217 if (aRule
.mLength
> rule
->mLength
) {
9218 aRules
.InsertElementAt(ruleX
, &aRule
);
9222 aRules
.AppendElement(&aRule
);
9225 void DR_State::ParseRulesFile()
9227 char* path
= PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
9229 FILE* inFile
= fopen(path
, "r");
9231 for (DR_Rule
* rule
= ParseRule(inFile
); rule
; rule
= ParseRule(inFile
)) {
9232 if (rule
->mTarget
) {
9233 nsIAtom
* fType
= rule
->mTarget
->mFrameType
;
9235 DR_FrameTypeInfo
* info
= GetFrameTypeInfo(fType
);
9237 AddRule(info
->mRules
, *rule
);
9241 AddRule(mWildRules
, *rule
);
9251 void DR_State::AddFrameTypeInfo(nsIAtom
* aFrameType
,
9252 const char* aFrameNameAbbrev
,
9253 const char* aFrameName
)
9255 mFrameTypeTable
.AppendElement(DR_FrameTypeInfo(aFrameType
, aFrameNameAbbrev
, aFrameName
));
9258 DR_FrameTypeInfo
* DR_State::GetFrameTypeInfo(nsIAtom
* aFrameType
)
9260 int32_t numEntries
= mFrameTypeTable
.Length();
9261 NS_ASSERTION(numEntries
!= 0, "empty FrameTypeTable");
9262 for (int32_t i
= 0; i
< numEntries
; i
++) {
9263 DR_FrameTypeInfo
& info
= mFrameTypeTable
.ElementAt(i
);
9264 if (info
.mType
== aFrameType
) {
9268 return &mFrameTypeTable
.ElementAt(numEntries
- 1); // return unknown frame type
9271 DR_FrameTypeInfo
* DR_State::GetFrameTypeInfo(char* aFrameName
)
9273 int32_t numEntries
= mFrameTypeTable
.Length();
9274 NS_ASSERTION(numEntries
!= 0, "empty FrameTypeTable");
9275 for (int32_t i
= 0; i
< numEntries
; i
++) {
9276 DR_FrameTypeInfo
& info
= mFrameTypeTable
.ElementAt(i
);
9277 if ((strcmp(aFrameName
, info
.mName
) == 0) || (strcmp(aFrameName
, info
.mNameAbbrev
) == 0)) {
9281 return &mFrameTypeTable
.ElementAt(numEntries
- 1); // return unknown frame type
9284 void DR_State::InitFrameTypeTable()
9286 AddFrameTypeInfo(nsGkAtoms::blockFrame
, "block", "block");
9287 AddFrameTypeInfo(nsGkAtoms::brFrame
, "br", "br");
9288 AddFrameTypeInfo(nsGkAtoms::bulletFrame
, "bullet", "bullet");
9289 AddFrameTypeInfo(nsGkAtoms::colorControlFrame
, "color", "colorControl");
9290 AddFrameTypeInfo(nsGkAtoms::gfxButtonControlFrame
, "button", "gfxButtonControl");
9291 AddFrameTypeInfo(nsGkAtoms::HTMLButtonControlFrame
, "HTMLbutton", "HTMLButtonControl");
9292 AddFrameTypeInfo(nsGkAtoms::HTMLCanvasFrame
, "HTMLCanvas","HTMLCanvas");
9293 AddFrameTypeInfo(nsGkAtoms::subDocumentFrame
, "subdoc", "subDocument");
9294 AddFrameTypeInfo(nsGkAtoms::imageFrame
, "img", "image");
9295 AddFrameTypeInfo(nsGkAtoms::inlineFrame
, "inline", "inline");
9296 AddFrameTypeInfo(nsGkAtoms::letterFrame
, "letter", "letter");
9297 AddFrameTypeInfo(nsGkAtoms::lineFrame
, "line", "line");
9298 AddFrameTypeInfo(nsGkAtoms::listControlFrame
, "select", "select");
9299 AddFrameTypeInfo(nsGkAtoms::objectFrame
, "obj", "object");
9300 AddFrameTypeInfo(nsGkAtoms::pageFrame
, "page", "page");
9301 AddFrameTypeInfo(nsGkAtoms::placeholderFrame
, "place", "placeholder");
9302 AddFrameTypeInfo(nsGkAtoms::canvasFrame
, "canvas", "canvas");
9303 AddFrameTypeInfo(nsGkAtoms::rootFrame
, "root", "root");
9304 AddFrameTypeInfo(nsGkAtoms::scrollFrame
, "scroll", "scroll");
9305 AddFrameTypeInfo(nsGkAtoms::tableCaptionFrame
, "caption", "tableCaption");
9306 AddFrameTypeInfo(nsGkAtoms::tableCellFrame
, "cell", "tableCell");
9307 AddFrameTypeInfo(nsGkAtoms::bcTableCellFrame
, "bcCell", "bcTableCell");
9308 AddFrameTypeInfo(nsGkAtoms::tableColFrame
, "col", "tableCol");
9309 AddFrameTypeInfo(nsGkAtoms::tableColGroupFrame
, "colG", "tableColGroup");
9310 AddFrameTypeInfo(nsGkAtoms::tableFrame
, "tbl", "table");
9311 AddFrameTypeInfo(nsGkAtoms::tableOuterFrame
, "tblO", "tableOuter");
9312 AddFrameTypeInfo(nsGkAtoms::tableRowGroupFrame
, "rowG", "tableRowGroup");
9313 AddFrameTypeInfo(nsGkAtoms::tableRowFrame
, "row", "tableRow");
9314 AddFrameTypeInfo(nsGkAtoms::textInputFrame
, "textCtl", "textInput");
9315 AddFrameTypeInfo(nsGkAtoms::textFrame
, "text", "text");
9316 AddFrameTypeInfo(nsGkAtoms::viewportFrame
, "VP", "viewport");
9318 AddFrameTypeInfo(nsGkAtoms::XULLabelFrame
, "XULLabel", "XULLabel");
9319 AddFrameTypeInfo(nsGkAtoms::boxFrame
, "Box", "Box");
9320 AddFrameTypeInfo(nsGkAtoms::sliderFrame
, "Slider", "Slider");
9321 AddFrameTypeInfo(nsGkAtoms::popupSetFrame
, "PopupSet", "PopupSet");
9323 AddFrameTypeInfo(nullptr, "unknown", "unknown");
9327 void DR_State::DisplayFrameTypeInfo(nsIFrame
* aFrame
,
9330 DR_FrameTypeInfo
* frameTypeInfo
= GetFrameTypeInfo(aFrame
->GetType());
9331 if (frameTypeInfo
) {
9332 for (int32_t i
= 0; i
< aIndent
; i
++) {
9335 if(!strcmp(frameTypeInfo
->mNameAbbrev
, "unknown")) {
9338 aFrame
->GetFrameName(name
);
9339 printf("%s %p ", NS_LossyConvertUTF16toASCII(name
).get(), (void*)aFrame
);
9342 printf("%s %p ", frameTypeInfo
->mNameAbbrev
, (void*)aFrame
);
9346 printf("%s %p ", frameTypeInfo
->mNameAbbrev
, (void*)aFrame
);
9351 bool DR_State::RuleMatches(DR_Rule
& aRule
,
9352 DR_FrameTreeNode
& aNode
)
9354 NS_ASSERTION(aRule
.mTarget
, "program error");
9356 DR_RulePart
* rulePart
;
9357 DR_FrameTreeNode
* parentNode
;
9358 for (rulePart
= aRule
.mTarget
->mNext
, parentNode
= aNode
.mParent
;
9359 rulePart
&& parentNode
;
9360 rulePart
= rulePart
->mNext
, parentNode
= parentNode
->mParent
) {
9361 if (rulePart
->mFrameType
) {
9362 if (parentNode
->mFrame
) {
9363 if (rulePart
->mFrameType
!= parentNode
->mFrame
->GetType()) {
9367 else NS_ASSERTION(false, "program error");
9369 // else wild card match
9374 void DR_State::FindMatchingRule(DR_FrameTreeNode
& aNode
)
9376 if (!aNode
.mFrame
) {
9377 NS_ASSERTION(false, "invalid DR_FrameTreeNode \n");
9381 bool matchingRule
= false;
9383 DR_FrameTypeInfo
* info
= GetFrameTypeInfo(aNode
.mFrame
->GetType());
9384 NS_ASSERTION(info
, "program error");
9385 int32_t numRules
= info
->mRules
.Length();
9386 for (int32_t ruleX
= 0; ruleX
< numRules
; ruleX
++) {
9387 DR_Rule
* rule
= info
->mRules
.ElementAt(ruleX
);
9388 if (rule
&& RuleMatches(*rule
, aNode
)) {
9389 aNode
.mDisplay
= rule
->mDisplay
;
9390 matchingRule
= true;
9394 if (!matchingRule
) {
9395 int32_t numWildRules
= mWildRules
.Length();
9396 for (int32_t ruleX
= 0; ruleX
< numWildRules
; ruleX
++) {
9397 DR_Rule
* rule
= mWildRules
.ElementAt(ruleX
);
9398 if (rule
&& RuleMatches(*rule
, aNode
)) {
9399 aNode
.mDisplay
= rule
->mDisplay
;
9406 DR_FrameTreeNode
* DR_State::CreateTreeNode(nsIFrame
* aFrame
,
9407 const nsHTMLReflowState
* aReflowState
)
9409 // find the frame of the parent reflow state (usually just the parent of aFrame)
9410 nsIFrame
* parentFrame
;
9412 const nsHTMLReflowState
* parentRS
= aReflowState
->parentReflowState
;
9413 parentFrame
= (parentRS
) ? parentRS
->frame
: nullptr;
9415 parentFrame
= aFrame
->GetParent();
9418 // find the parent tree node leaf
9419 DR_FrameTreeNode
* parentNode
= nullptr;
9421 DR_FrameTreeNode
* lastLeaf
= nullptr;
9422 if(mFrameTreeLeaves
.Length())
9423 lastLeaf
= mFrameTreeLeaves
.ElementAt(mFrameTreeLeaves
.Length() - 1);
9425 for (parentNode
= lastLeaf
; parentNode
&& (parentNode
->mFrame
!= parentFrame
); parentNode
= parentNode
->mParent
) {
9428 DR_FrameTreeNode
* newNode
= new DR_FrameTreeNode(aFrame
, parentNode
);
9429 FindMatchingRule(*newNode
);
9431 newNode
->mIndent
= mIndent
;
9432 if (newNode
->mDisplay
|| mIndentUndisplayedFrames
) {
9436 if (lastLeaf
&& (lastLeaf
== parentNode
)) {
9437 mFrameTreeLeaves
.RemoveElementAt(mFrameTreeLeaves
.Length() - 1);
9439 mFrameTreeLeaves
.AppendElement(newNode
);
9445 void DR_State::PrettyUC(nscoord aSize
,
9448 if (NS_UNCONSTRAINEDSIZE
== aSize
) {
9452 if ((nscoord
)0xdeadbeefU
== aSize
)
9454 strcpy(aBuf
, "deadbeef");
9457 sprintf(aBuf
, "%d", aSize
);
9462 void DR_State::PrintMargin(const char *tag
, const nsMargin
* aMargin
)
9465 char t
[16], r
[16], b
[16], l
[16];
9466 PrettyUC(aMargin
->top
, t
);
9467 PrettyUC(aMargin
->right
, r
);
9468 PrettyUC(aMargin
->bottom
, b
);
9469 PrettyUC(aMargin
->left
, l
);
9470 printf(" %s=%s,%s,%s,%s", tag
, t
, r
, b
, l
);
9472 // use %p here for consistency with other null-pointer printouts
9473 printf(" %s=%p", tag
, (void*)aMargin
);
9477 void DR_State::DeleteTreeNode(DR_FrameTreeNode
& aNode
)
9479 mFrameTreeLeaves
.RemoveElement(&aNode
);
9480 int32_t numLeaves
= mFrameTreeLeaves
.Length();
9481 if ((0 == numLeaves
) || (aNode
.mParent
!= mFrameTreeLeaves
.ElementAt(numLeaves
- 1))) {
9482 mFrameTreeLeaves
.AppendElement(aNode
.mParent
);
9485 if (aNode
.mDisplay
|| mIndentUndisplayedFrames
) {
9488 // delete the tree node
9493 CheckPixelError(nscoord aSize
,
9494 int32_t aPixelToTwips
)
9496 if (NS_UNCONSTRAINEDSIZE
!= aSize
) {
9497 if ((aSize
% aPixelToTwips
) > 0) {
9498 printf("VALUE %d is not a whole pixel \n", aSize
);
9503 static void DisplayReflowEnterPrint(nsPresContext
* aPresContext
,
9505 const nsHTMLReflowState
& aReflowState
,
9506 DR_FrameTreeNode
& aTreeNode
,
9509 if (aTreeNode
.mDisplay
) {
9510 DR_state
->DisplayFrameTypeInfo(aFrame
, aTreeNode
.mIndent
);
9515 DR_state
->PrettyUC(aReflowState
.AvailableWidth(), width
);
9516 DR_state
->PrettyUC(aReflowState
.AvailableHeight(), height
);
9517 printf("Reflow a=%s,%s ", width
, height
);
9519 DR_state
->PrettyUC(aReflowState
.ComputedWidth(), width
);
9520 DR_state
->PrettyUC(aReflowState
.ComputedHeight(), height
);
9521 printf("c=%s,%s ", width
, height
);
9523 if (aFrame
->GetStateBits() & NS_FRAME_IS_DIRTY
)
9526 if (aFrame
->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN
)
9527 printf("dirty-children ");
9529 if (aReflowState
.mFlags
.mSpecialHeightReflow
)
9530 printf("special-height ");
9532 if (aReflowState
.mFlags
.mHResize
)
9533 printf("h-resize ");
9535 if (aReflowState
.mFlags
.mVResize
)
9536 printf("v-resize ");
9538 nsIFrame
* inFlow
= aFrame
->GetPrevInFlow();
9540 printf("pif=%p ", (void*)inFlow
);
9542 inFlow
= aFrame
->GetNextInFlow();
9544 printf("nif=%p ", (void*)inFlow
);
9547 printf("CHANGED \n");
9549 printf("cnt=%d \n", DR_state
->mCount
);
9550 if (DR_state
->mDisplayPixelErrors
) {
9551 int32_t p2t
= aPresContext
->AppUnitsPerDevPixel();
9552 CheckPixelError(aReflowState
.AvailableWidth(), p2t
);
9553 CheckPixelError(aReflowState
.AvailableHeight(), p2t
);
9554 CheckPixelError(aReflowState
.ComputedWidth(), p2t
);
9555 CheckPixelError(aReflowState
.ComputedHeight(), p2t
);
9560 void* nsFrame::DisplayReflowEnter(nsPresContext
* aPresContext
,
9562 const nsHTMLReflowState
& aReflowState
)
9564 if (!DR_state
->mInited
) DR_state
->Init();
9565 if (!DR_state
->mActive
) return nullptr;
9567 NS_ASSERTION(aFrame
, "invalid call");
9569 DR_FrameTreeNode
* treeNode
= DR_state
->CreateTreeNode(aFrame
, &aReflowState
);
9571 DisplayReflowEnterPrint(aPresContext
, aFrame
, aReflowState
, *treeNode
, false);
9576 void* nsFrame::DisplayLayoutEnter(nsIFrame
* aFrame
)
9578 if (!DR_state
->mInited
) DR_state
->Init();
9579 if (!DR_state
->mActive
) return nullptr;
9581 NS_ASSERTION(aFrame
, "invalid call");
9583 DR_FrameTreeNode
* treeNode
= DR_state
->CreateTreeNode(aFrame
, nullptr);
9584 if (treeNode
&& treeNode
->mDisplay
) {
9585 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9591 void* nsFrame::DisplayIntrinsicISizeEnter(nsIFrame
* aFrame
,
9594 if (!DR_state
->mInited
) DR_state
->Init();
9595 if (!DR_state
->mActive
) return nullptr;
9597 NS_ASSERTION(aFrame
, "invalid call");
9599 DR_FrameTreeNode
* treeNode
= DR_state
->CreateTreeNode(aFrame
, nullptr);
9600 if (treeNode
&& treeNode
->mDisplay
) {
9601 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9602 printf("Get%sWidth\n", aType
);
9607 void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame
* aFrame
,
9610 if (!DR_state
->mInited
) DR_state
->Init();
9611 if (!DR_state
->mActive
) return nullptr;
9613 NS_ASSERTION(aFrame
, "invalid call");
9615 DR_FrameTreeNode
* treeNode
= DR_state
->CreateTreeNode(aFrame
, nullptr);
9616 if (treeNode
&& treeNode
->mDisplay
) {
9617 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9618 printf("Get%sSize\n", aType
);
9623 void nsFrame::DisplayReflowExit(nsPresContext
* aPresContext
,
9625 nsHTMLReflowMetrics
& aMetrics
,
9626 nsReflowStatus aStatus
,
9627 void* aFrameTreeNode
)
9629 if (!DR_state
->mActive
) return;
9631 NS_ASSERTION(aFrame
, "DisplayReflowExit - invalid call");
9632 if (!aFrameTreeNode
) return;
9634 DR_FrameTreeNode
* treeNode
= (DR_FrameTreeNode
*)aFrameTreeNode
;
9635 if (treeNode
->mDisplay
) {
9636 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9642 DR_state
->PrettyUC(aMetrics
.Width(), width
);
9643 DR_state
->PrettyUC(aMetrics
.Height(), height
);
9644 printf("Reflow d=%s,%s", width
, height
);
9646 if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus
)) {
9647 printf(" status=0x%x", aStatus
);
9649 if (aFrame
->HasOverflowAreas()) {
9650 DR_state
->PrettyUC(aMetrics
.VisualOverflow().x
, x
);
9651 DR_state
->PrettyUC(aMetrics
.VisualOverflow().y
, y
);
9652 DR_state
->PrettyUC(aMetrics
.VisualOverflow().width
, width
);
9653 DR_state
->PrettyUC(aMetrics
.VisualOverflow().height
, height
);
9654 printf(" vis-o=(%s,%s) %s x %s", x
, y
, width
, height
);
9656 nsRect storedOverflow
= aFrame
->GetVisualOverflowRect();
9657 DR_state
->PrettyUC(storedOverflow
.x
, x
);
9658 DR_state
->PrettyUC(storedOverflow
.y
, y
);
9659 DR_state
->PrettyUC(storedOverflow
.width
, width
);
9660 DR_state
->PrettyUC(storedOverflow
.height
, height
);
9661 printf(" vis-sto=(%s,%s) %s x %s", x
, y
, width
, height
);
9663 DR_state
->PrettyUC(aMetrics
.ScrollableOverflow().x
, x
);
9664 DR_state
->PrettyUC(aMetrics
.ScrollableOverflow().y
, y
);
9665 DR_state
->PrettyUC(aMetrics
.ScrollableOverflow().width
, width
);
9666 DR_state
->PrettyUC(aMetrics
.ScrollableOverflow().height
, height
);
9667 printf(" scr-o=(%s,%s) %s x %s", x
, y
, width
, height
);
9669 storedOverflow
= aFrame
->GetScrollableOverflowRect();
9670 DR_state
->PrettyUC(storedOverflow
.x
, x
);
9671 DR_state
->PrettyUC(storedOverflow
.y
, y
);
9672 DR_state
->PrettyUC(storedOverflow
.width
, width
);
9673 DR_state
->PrettyUC(storedOverflow
.height
, height
);
9674 printf(" scr-sto=(%s,%s) %s x %s", x
, y
, width
, height
);
9677 if (DR_state
->mDisplayPixelErrors
) {
9678 int32_t p2t
= aPresContext
->AppUnitsPerDevPixel();
9679 CheckPixelError(aMetrics
.Width(), p2t
);
9680 CheckPixelError(aMetrics
.Height(), p2t
);
9683 DR_state
->DeleteTreeNode(*treeNode
);
9686 void nsFrame::DisplayLayoutExit(nsIFrame
* aFrame
,
9687 void* aFrameTreeNode
)
9689 if (!DR_state
->mActive
) return;
9691 NS_ASSERTION(aFrame
, "non-null frame required");
9692 if (!aFrameTreeNode
) return;
9694 DR_FrameTreeNode
* treeNode
= (DR_FrameTreeNode
*)aFrameTreeNode
;
9695 if (treeNode
->mDisplay
) {
9696 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9697 nsRect rect
= aFrame
->GetRect();
9698 printf("Layout=%d,%d,%d,%d\n", rect
.x
, rect
.y
, rect
.width
, rect
.height
);
9700 DR_state
->DeleteTreeNode(*treeNode
);
9703 void nsFrame::DisplayIntrinsicISizeExit(nsIFrame
* aFrame
,
9706 void* aFrameTreeNode
)
9708 if (!DR_state
->mActive
) return;
9710 NS_ASSERTION(aFrame
, "non-null frame required");
9711 if (!aFrameTreeNode
) return;
9713 DR_FrameTreeNode
* treeNode
= (DR_FrameTreeNode
*)aFrameTreeNode
;
9714 if (treeNode
->mDisplay
) {
9715 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9717 DR_state
->PrettyUC(aResult
, width
);
9718 printf("Get%sWidth=%s\n", aType
, width
);
9720 DR_state
->DeleteTreeNode(*treeNode
);
9723 void nsFrame::DisplayIntrinsicSizeExit(nsIFrame
* aFrame
,
9726 void* aFrameTreeNode
)
9728 if (!DR_state
->mActive
) return;
9730 NS_ASSERTION(aFrame
, "non-null frame required");
9731 if (!aFrameTreeNode
) return;
9733 DR_FrameTreeNode
* treeNode
= (DR_FrameTreeNode
*)aFrameTreeNode
;
9734 if (treeNode
->mDisplay
) {
9735 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9739 DR_state
->PrettyUC(aResult
.width
, width
);
9740 DR_state
->PrettyUC(aResult
.height
, height
);
9741 printf("Get%sSize=%s,%s\n", aType
, width
, height
);
9743 DR_state
->DeleteTreeNode(*treeNode
);
9747 nsFrame::DisplayReflowStartup()
9749 DR_state
= new DR_State();
9753 nsFrame::DisplayReflowShutdown()
9759 void DR_cookie::Change() const
9761 DR_FrameTreeNode
* treeNode
= (DR_FrameTreeNode
*)mValue
;
9762 if (treeNode
&& treeNode
->mDisplay
) {
9763 DisplayReflowEnterPrint(mPresContext
, mFrame
, mReflowState
, *treeNode
, true);
9768 nsHTMLReflowState::DisplayInitConstraintsEnter(nsIFrame
* aFrame
,
9769 nsHTMLReflowState
* aState
,
9770 nscoord aContainingBlockWidth
,
9771 nscoord aContainingBlockHeight
,
9772 const nsMargin
* aBorder
,
9773 const nsMargin
* aPadding
)
9775 NS_PRECONDITION(aFrame
, "non-null frame required");
9776 NS_PRECONDITION(aState
, "non-null state required");
9778 if (!DR_state
->mInited
) DR_state
->Init();
9779 if (!DR_state
->mActive
) return nullptr;
9781 DR_FrameTreeNode
* treeNode
= DR_state
->CreateTreeNode(aFrame
, aState
);
9782 if (treeNode
&& treeNode
->mDisplay
) {
9783 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9785 printf("InitConstraints parent=%p",
9786 (void*)aState
->parentReflowState
);
9791 DR_state
->PrettyUC(aContainingBlockWidth
, width
);
9792 DR_state
->PrettyUC(aContainingBlockHeight
, height
);
9793 printf(" cb=%s,%s", width
, height
);
9795 DR_state
->PrettyUC(aState
->AvailableWidth(), width
);
9796 DR_state
->PrettyUC(aState
->AvailableHeight(), height
);
9797 printf(" as=%s,%s", width
, height
);
9799 DR_state
->PrintMargin("b", aBorder
);
9800 DR_state
->PrintMargin("p", aPadding
);
9807 nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame
* aFrame
,
9808 nsHTMLReflowState
* aState
,
9811 NS_PRECONDITION(aFrame
, "non-null frame required");
9812 NS_PRECONDITION(aState
, "non-null state required");
9814 if (!DR_state
->mActive
) return;
9815 if (!aValue
) return;
9817 DR_FrameTreeNode
* treeNode
= (DR_FrameTreeNode
*)aValue
;
9818 if (treeNode
->mDisplay
) {
9819 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9820 char cmiw
[16], cw
[16], cmxw
[16], cmih
[16], ch
[16], cmxh
[16];
9821 DR_state
->PrettyUC(aState
->ComputedMinWidth(), cmiw
);
9822 DR_state
->PrettyUC(aState
->ComputedWidth(), cw
);
9823 DR_state
->PrettyUC(aState
->ComputedMaxWidth(), cmxw
);
9824 DR_state
->PrettyUC(aState
->ComputedMinHeight(), cmih
);
9825 DR_state
->PrettyUC(aState
->ComputedHeight(), ch
);
9826 DR_state
->PrettyUC(aState
->ComputedMaxHeight(), cmxh
);
9827 printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)",
9828 cmiw
, cw
, cmxw
, cmih
, ch
, cmxh
);
9829 DR_state
->PrintMargin("co", &aState
->ComputedPhysicalOffsets());
9832 DR_state
->DeleteTreeNode(*treeNode
);
9837 nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame
* aFrame
,
9838 nsCSSOffsetState
* aState
,
9839 nscoord aHorizontalPercentBasis
,
9840 nscoord aVerticalPercentBasis
,
9841 const nsMargin
* aBorder
,
9842 const nsMargin
* aPadding
)
9844 NS_PRECONDITION(aFrame
, "non-null frame required");
9845 NS_PRECONDITION(aState
, "non-null state required");
9847 if (!DR_state
->mInited
) DR_state
->Init();
9848 if (!DR_state
->mActive
) return nullptr;
9850 // aState is not necessarily a nsHTMLReflowState
9851 DR_FrameTreeNode
* treeNode
= DR_state
->CreateTreeNode(aFrame
, nullptr);
9852 if (treeNode
&& treeNode
->mDisplay
) {
9853 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9855 char horizPctBasisStr
[16];
9856 char vertPctBasisStr
[16];
9857 DR_state
->PrettyUC(aHorizontalPercentBasis
, horizPctBasisStr
);
9858 DR_state
->PrettyUC(aVerticalPercentBasis
, vertPctBasisStr
);
9859 printf("InitOffsets pct_basis=%s,%s", horizPctBasisStr
, vertPctBasisStr
);
9861 DR_state
->PrintMargin("b", aBorder
);
9862 DR_state
->PrintMargin("p", aPadding
);
9869 nsCSSOffsetState::DisplayInitOffsetsExit(nsIFrame
* aFrame
,
9870 nsCSSOffsetState
* aState
,
9873 NS_PRECONDITION(aFrame
, "non-null frame required");
9874 NS_PRECONDITION(aState
, "non-null state required");
9876 if (!DR_state
->mActive
) return;
9877 if (!aValue
) return;
9879 DR_FrameTreeNode
* treeNode
= (DR_FrameTreeNode
*)aValue
;
9880 if (treeNode
->mDisplay
) {
9881 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9882 printf("InitOffsets=");
9883 DR_state
->PrintMargin("m", &aState
->ComputedPhysicalMargin());
9884 DR_state
->PrintMargin("p", &aState
->ComputedPhysicalPadding());
9885 DR_state
->PrintMargin("p+b", &aState
->ComputedPhysicalBorderPadding());
9888 DR_state
->DeleteTreeNode(*treeNode
);
9892 nsHTMLReflowState::DisplayInitFrameTypeEnter(nsIFrame
* aFrame
,
9893 nsHTMLReflowState
* aState
)
9895 NS_PRECONDITION(aFrame
, "non-null frame required");
9896 NS_PRECONDITION(aState
, "non-null state required");
9898 if (!DR_state
->mInited
) DR_state
->Init();
9899 if (!DR_state
->mActive
) return nullptr;
9901 // we don't print anything here
9902 return DR_state
->CreateTreeNode(aFrame
, aState
);
9906 nsHTMLReflowState::DisplayInitFrameTypeExit(nsIFrame
* aFrame
,
9907 nsHTMLReflowState
* aState
,
9910 NS_PRECONDITION(aFrame
, "non-null frame required");
9911 NS_PRECONDITION(aState
, "non-null state required");
9913 if (!DR_state
->mActive
) return;
9914 if (!aValue
) return;
9916 DR_FrameTreeNode
* treeNode
= (DR_FrameTreeNode
*)aValue
;
9917 if (treeNode
->mDisplay
) {
9918 DR_state
->DisplayFrameTypeInfo(aFrame
, treeNode
->mIndent
);
9919 printf("InitFrameType");
9921 const nsStyleDisplay
*disp
= aState
->mStyleDisplay
;
9923 if (aFrame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)
9924 printf(" out-of-flow");
9925 if (aFrame
->GetPrevInFlow())
9926 printf(" prev-in-flow");
9927 if (aFrame
->IsAbsolutelyPositioned())
9929 if (aFrame
->IsFloating())
9932 // This array must exactly match the NS_STYLE_DISPLAY constants.
9933 const char *const displayTypes
[] = {
9934 "none", "block", "inline", "inline-block", "list-item", "marker",
9935 "run-in", "compact", "table", "inline-table", "table-row-group",
9936 "table-column", "table-column-group", "table-header-group",
9937 "table-footer-group", "table-row", "table-cell", "table-caption",
9938 "box", "inline-box",
9940 "grid", "inline-grid", "grid-group", "grid-line", "stack",
9941 "inline-stack", "deck", "popup", "groupbox",
9944 if (disp
->mDisplay
>= ArrayLength(displayTypes
))
9945 printf(" display=%u", disp
->mDisplay
);
9947 printf(" display=%s", displayTypes
[disp
->mDisplay
]);
9949 // This array must exactly match the NS_CSS_FRAME_TYPE constants.
9950 const char *const cssFrameTypes
[] = {
9951 "unknown", "inline", "block", "floating", "absolute", "internal-table"
9953 nsCSSFrameType bareType
= NS_FRAME_GET_TYPE(aState
->mFrameType
);
9954 bool repNoBlock
= NS_FRAME_IS_REPLACED_NOBLOCK(aState
->mFrameType
);
9955 bool repBlock
= NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState
->mFrameType
);
9957 if (bareType
>= ArrayLength(cssFrameTypes
)) {
9958 printf(" result=type %u", bareType
);
9960 printf(" result=%s", cssFrameTypes
[bareType
]);
9962 printf("%s%s\n", repNoBlock
? " +rep" : "", repBlock
? " +repBlk" : "");
9964 DR_state
->DeleteTreeNode(*treeNode
);
9968 // End Display Reflow