Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsFrame.cpp
blobc82da083befae7980c06a5fd4115b11c4c0468f2
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 */
9 #include "nsFrame.h"
11 #include <stdarg.h>
12 #include <algorithm>
14 #include "mozilla/Attributes.h"
15 #include "mozilla/DebugOnly.h"
17 #include "nsCOMPtr.h"
18 #include "nsFrameList.h"
19 #include "nsPlaceholderFrame.h"
20 #include "nsIContent.h"
21 #include "nsContentUtils.h"
22 #include "nsIAtom.h"
23 #include "nsString.h"
24 #include "nsReadableUtils.h"
25 #include "nsStyleContext.h"
26 #include "nsTableOuterFrame.h"
27 #include "nsView.h"
28 #include "nsViewManager.h"
29 #include "nsIScrollableFrame.h"
30 #include "nsPresContext.h"
31 #include "nsStyleConsts.h"
32 #include "nsIPresShell.h"
33 #include "prlog.h"
34 #include "prprf.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"
47 #include "nsRange.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"
59 #include "nsError.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"
77 #include "nsRegion.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"
92 #include "nsITheme.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
105 nsSize mPrefSize;
106 nsSize mMinSize;
107 nsSize mMaxSize;
109 nsSize mBlockMinSize;
110 nsSize mBlockPrefSize;
111 nscoord mBlockAscent;
113 nscoord mFlex;
114 nscoord mAscent;
116 nsSize mLastSize;
119 struct nsContentAndOffset
121 nsIContent* mContent;
122 int32_t mOffset;
125 // Some Misc #defines
126 #define SELECTION_DEBUG 0
127 #define FORCE_SELECTION_UPDATE 1
128 #define CALC_DEBUG 0
131 #include "nsILineIterator.h"
133 //non Hack prototypes
134 #if 0
135 static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent);
136 #endif
138 #include "prenv.h"
140 static void
141 DestroyBoxMetrics(void* aPropertyValue)
143 delete static_cast<nsBoxLayoutMetrics*>(aPropertyValue);
146 NS_DECLARE_FRAME_PROPERTY(BoxMetricsProperty, DestroyBoxMetrics)
148 static void
149 InitBoxMetrics(nsIFrame* aFrame, bool aClear)
151 FrameProperties props = aFrame->Properties();
152 if (aClear) {
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);
164 static bool
165 IsBoxWrapped(const nsIFrame* aFrame)
167 return aFrame->GetParent() &&
168 aFrame->GetParent()->IsBoxFrame() &&
169 !aFrame->IsBoxFrame();
172 // Formerly the nsIFrameDebug interface
174 #ifdef DEBUG
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;
209 bool
210 nsFrame::GetVerifyStyleTreeEnable()
212 if (gStyleVerifyTreeEnable == 0x55) {
213 if (nullptr == gStyleVerifyTreeLogModuleInfo) {
214 gStyleVerifyTreeLogModuleInfo = PR_NewLogModule("styleverifytree");
215 gStyleVerifyTreeEnable = 0 != gStyleVerifyTreeLogModuleInfo->level;
218 return gStyleVerifyTreeEnable;
221 void
222 nsFrame::SetVerifyStyleTreeEnable(bool aEnabled)
224 gStyleVerifyTreeEnable = aEnabled;
227 PRLogModuleInfo*
228 nsFrame::GetLogModuleInfo()
230 if (nullptr == gLogModule) {
231 gLogModule = PR_NewLogModule("frame");
233 return gLogModule;
236 #endif
238 static void
239 DestroyAbsoluteContainingBlock(void* aPropertyValue)
241 delete static_cast<nsAbsoluteContainingBlock*>(aPropertyValue);
244 NS_DECLARE_FRAME_PROPERTY(AbsoluteContainingBlockProperty, DestroyAbsoluteContainingBlock)
246 bool
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");
257 return absCB;
260 void
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()));
272 void
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());
285 bool
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()) {
297 result = true;
301 return result;
304 bool
305 nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const
307 if (!StyleVisibility()->IsVisible()) {
308 return false;
311 const nsIFrame* frame = this;
312 while (frame) {
313 nsView* view = frame->GetView();
314 if (view && view->GetVisibility() == nsViewVisibility_kHide)
315 return false;
317 nsIFrame* parent = frame->GetParent();
318 nsDeckFrame* deck = do_QueryFrame(parent);
319 if (deck) {
320 if (deck->GetSelectedBox() != frame)
321 return false;
324 if (parent) {
325 frame = parent;
326 } else {
327 parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
328 if (!parent)
329 break;
331 if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
332 parent->PresContext()->IsChrome() && !frame->PresContext()->IsChrome()) {
333 break;
336 if (!parent->StyleVisibility()->IsVisible())
337 return false;
339 frame = parent;
343 return true;
346 void
347 nsIFrame::FindCloserFrameForSelection(
348 nsPoint aPoint,
349 nsIFrame::FrameWithDistance* aCurrentBestFrame)
351 if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
352 aCurrentBestFrame->mXDistance,
353 aCurrentBestFrame->mYDistance)) {
354 aCurrentBestFrame->mFrame = this;
358 void
359 nsIFrame::ContentStatesChanged(mozilla::EventStates aStates)
363 void
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;
374 void
375 nsWeakFrame::Init(nsIFrame* aFrame)
377 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
378 mFrame = aFrame;
379 if (mFrame) {
380 nsIPresShell* shell = mFrame->PresContext()->GetPresShell();
381 NS_WARN_IF_FALSE(shell, "Null PresShell in nsWeakFrame!");
382 if (shell) {
383 shell->AddWeakFrame(this);
384 } else {
385 mFrame = nullptr;
390 nsIFrame*
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();
405 nsFrame::~nsFrame()
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.
417 void
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 /////////////////////////////////////////////////////////////////////////////
428 // nsIFrame
430 static bool
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
439 * container.
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
454 * container.
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
464 * always containers.
467 // The root frame should always be an inflation container.
468 if (!aFrame->GetParent()) {
469 return true;
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) ||
491 isInline ||
492 // br frames and mathml frames report being line
493 // participants even when their position or display is
494 // set
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");
500 return !isInline;
503 void
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");
513 mContent = aContent;
514 mParent = aParent;
516 if (aContent) {
517 NS_ADDREF(aContent);
520 if (aPrevInFlow) {
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);
531 if (GetParent()) {
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 |
538 NS_FRAME_IN_POPUP |
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 &&
548 !aPrevInFlow &&
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);
560 if (ssc) {
561 ssc->AddFrame(this);
565 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent()
566 #ifdef DEBUG
567 // We have assertions that check inflation invariants even when
568 // font size inflation is not enabled.
569 || true
570 #endif
572 if (IsFontSizeInflationContainer(this, disp)) {
573 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
574 if (!GetParent() ||
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);
591 void
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);
606 if (ssc) {
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.");
626 if (placeholder) {
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()));
639 if (prevSib) {
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()));
649 if (nextSib) {
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();
669 if (rsc) {
670 rsc->Put(mContent, mStyleContext);
674 shell->NotifyDestroyingFrame(this);
676 if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
677 shell->ClearFrameRefs(this);
680 if (view) {
681 // Break association between view and frame
682 view->SetFrame(nullptr);
684 // Destroy the view
685 view->Destroy();
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();
702 this->~nsFrame();
704 // Now that we're totally cleaned out, we need to add ourselves to
705 // the presshell's recycler.
706 shell->FreeFrame(id, this);
709 nsresult
710 nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const
712 aStart = 0;
713 aEnd = 0;
714 return NS_OK;
717 // Subclass hook for style post processing
718 /* virtual */ void
719 nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
721 if (IsSVGText()) {
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
727 // gets new style.
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
750 // interval.)
751 const nsStyleBackground *oldBG = aOldStyleContext ?
752 aOldStyleContext->StyleBackground() :
753 nullptr;
754 const nsStyleBackground *newBG = StyleBackground();
755 if (oldBG) {
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) {
762 continue;
765 imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(),
766 this);
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) {
777 continue;
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();
810 if (oldBorder) {
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()
822 : nullptr;
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.
860 #ifndef _MSC_VER
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;
877 #endif
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)) ||
885 IsSVGText())
886 return margin;
888 nsMargin *m = static_cast<nsMargin*>
889 (Properties().Get(UsedMarginProperty()));
890 if (m) {
891 margin = *m;
892 } else {
893 DebugOnly<bool> hasMargin = StyleMargin()->GetMargin(margin);
894 NS_ASSERTION(hasMargin, "We should have a margin here! (out of memory?)");
896 return margin;
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)) ||
905 IsSVGText())
906 return border;
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)) {
913 nsIntMargin result;
914 nsPresContext *presContext = PresContext();
915 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
916 mutable_this, disp->mAppearance,
917 &result);
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);
922 return border;
925 nsMargin *b = static_cast<nsMargin*>
926 (Properties().Get(UsedBorderProperty()));
927 if (b) {
928 border = *b;
929 } else {
930 border = StyleBorder()->GetComputedBorder();
932 return border;
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)) ||
941 IsSVGText())
942 return padding;
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();
950 nsIntMargin widget;
951 if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
952 mutable_this,
953 disp->mAppearance,
954 &widget)) {
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);
959 return padding;
963 nsMargin *p = static_cast<nsMargin*>
964 (Properties().Get(UsedPaddingProperty()));
965 if (p) {
966 padding = *p;
967 } else {
968 DebugOnly<bool> hasPadding = StylePadding()->GetPadding(padding);
969 NS_ASSERTION(hasPadding, "We should have padding here! (out of memory?)");
971 return padding;
974 nsIFrame::Sides
975 nsIFrame::GetSkipSides(const nsHTMLReflowState* aReflowState) const
977 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
978 NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
979 return Sides();
982 // Convert the logical skip sides to physical sides using the frame's
983 // writing mode
984 WritingMode writingMode = GetWritingMode();
985 LogicalSides logicalSkip = GetLogicalSkipSides(aReflowState);
986 Sides skip;
988 if (logicalSkip.BStart()) {
989 if (writingMode.IsVertical()) {
990 skip |= writingMode.IsVerticalLR() ? eSideBitsLeft : eSideBitsRight;
991 } else {
992 skip |= eSideBitsTop;
996 if (logicalSkip.BEnd()) {
997 if (writingMode.IsVertical()) {
998 skip |= writingMode.IsVerticalLR() ? eSideBitsRight : eSideBitsLeft;
999 } else {
1000 skip |= eSideBitsBottom;
1004 if (logicalSkip.IStart()) {
1005 if (writingMode.IsVertical()) {
1006 skip |= eSideBitsTop;
1007 } else {
1008 skip |= writingMode.IsBidiLTR() ? eSideBitsLeft : eSideBitsRight;
1012 if (logicalSkip.IEnd()) {
1013 if (writingMode.IsVertical()) {
1014 skip |= eSideBitsBottom;
1015 } else {
1016 skip |= writingMode.IsBidiLTR() ? eSideBitsRight : eSideBitsLeft;
1019 return skip;
1022 nsRect
1023 nsIFrame::GetPaddingRectRelativeToSelf() const
1025 nsMargin border(GetUsedBorder());
1026 border.ApplySkipSides(GetSkipSides());
1027 nsRect r(0, 0, mRect.width, mRect.height);
1028 r.Deflate(border);
1029 return r;
1032 nsRect
1033 nsIFrame::GetPaddingRect() const
1035 return GetPaddingRectRelativeToSelf() + GetPosition();
1038 WritingMode
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);
1049 return writingMode;
1052 nsRect
1053 nsIFrame::GetMarginRectRelativeToSelf() const
1055 nsMargin m = GetUsedMargin();
1056 m.ApplySkipSides(GetSkipSides());
1057 nsRect r(0, 0, mRect.width, mRect.height);
1058 r.Inflate(m);
1059 return r;
1062 bool
1063 nsIFrame::IsTransformed() const
1065 return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
1066 (StyleDisplay()->HasTransform(this) ||
1067 IsSVGTransformed() ||
1068 (mContent &&
1069 nsLayoutUtils::HasAnimationsForCompositor(mContent,
1070 eCSSProperty_transform) &&
1071 IsFrameOfType(eSupportsCSSTransforms) &&
1072 mContent->GetPrimaryFrame() == this)));
1075 bool
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) ||
1082 (mContent &&
1083 nsLayoutUtils::HasAnimationsForCompositor(mContent,
1084 eCSSProperty_opacity) &&
1085 mContent->GetPrimaryFrame() == this);
1088 bool
1089 nsIFrame::IsSVGTransformed(gfx::Matrix *aOwnTransforms,
1090 gfx::Matrix *aFromParentTransforms) const
1092 return false;
1095 bool
1096 nsIFrame::Preserves3DChildren() const
1098 const nsStyleDisplay* disp = StyleDisplay();
1099 if (disp->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
1100 !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
1101 return false;
1104 // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d.
1105 if (GetType() == nsGkAtoms::scrollFrame) {
1106 return false;
1109 nsRect temp;
1110 return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
1111 !GetClipPropClipRect(disp, &temp, GetSize()) &&
1112 !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1115 bool
1116 nsIFrame::Preserves3D() const
1118 if (!GetParent() || !GetParent()->Preserves3DChildren()) {
1119 return false;
1121 return StyleDisplay()->HasTransform(this) || StyleDisplay()->BackfaceIsHidden();
1124 bool
1125 nsIFrame::HasPerspective() const
1127 if (!IsTransformed()) {
1128 return false;
1130 nsStyleContext* parentStyleContext = StyleContext()->GetParent();
1131 if (!parentStyleContext) {
1132 return false;
1134 const nsStyleDisplay* parentDisp = parentStyleContext->StyleDisplay();
1135 return parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord;
1138 bool
1139 nsIFrame::ChildrenHavePerspective() const
1141 return StyleDisplay()->HasPerspectiveStyle();
1144 nsRect
1145 nsIFrame::GetContentRectRelativeToSelf() const
1147 nsMargin bp(GetUsedBorderAndPadding());
1148 bp.ApplySkipSides(GetSkipSides());
1149 nsRect r(0, 0, mRect.width, mRect.height);
1150 r.Deflate(bp);
1151 return r;
1154 nsRect
1155 nsIFrame::GetContentRect() const
1157 return GetContentRectRelativeToSelf() + GetPosition();
1160 bool
1161 nsIFrame::ComputeBorderRadii(const nsStyleCorners& aBorderRadius,
1162 const nsSize& aFrameSize,
1163 const nsSize& aBorderArea,
1164 Sides aSkipSides,
1165 nscoord aRadii[8])
1167 // Percentages are relative to whichever side they're on.
1168 NS_FOR_CSS_HALF_CORNERS(i) {
1169 const nsStyleCoord c = aBorderRadius.Get(i);
1170 nscoord axis =
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) {
1176 // clamp calc()
1177 aRadii[i] = 0;
1179 } else {
1180 NS_NOTREACHED("ComputeBorderRadii: bad unit");
1181 aRadii[i] = 0;
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);
1220 nscoord length =
1221 NS_SIDE_IS_VERTICAL(side) ? aBorderArea.height : aBorderArea.width;
1222 nscoord sum = aRadii[hc1] + aRadii[hc2];
1223 if (sum)
1224 haveRadius = true;
1226 // avoid floating point division in the normal case
1227 if (length < sum)
1228 ratio = std::min(ratio, double(length)/sum);
1230 if (ratio < 1.0) {
1231 NS_FOR_CSS_HALF_CORNERS(corner) {
1232 aRadii[corner] *= ratio;
1236 return haveRadius;
1239 /* static */ void
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);
1251 /* static */ void
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;
1265 /* virtual */ bool
1266 nsIFrame::GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea,
1267 Sides aSkipSides, nscoord aRadii[8]) const
1269 if (IsThemed()) {
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
1276 // zero.
1277 NS_FOR_CSS_HALF_CORNERS(corner) {
1278 aRadii[corner] = 0;
1280 return false;
1282 return ComputeBorderRadii(StyleBorder()->mBorderRadius,
1283 aFrameSize, aBorderArea,
1284 aSkipSides, aRadii);
1287 bool
1288 nsIFrame::GetBorderRadii(nscoord aRadii[8]) const
1290 nsSize sz = GetSize();
1291 return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
1294 bool
1295 nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const
1297 if (!GetBorderRadii(aRadii))
1298 return false;
1299 InsetBorderRadii(aRadii, GetUsedBorder());
1300 NS_FOR_CSS_HALF_CORNERS(corner) {
1301 if (aRadii[corner])
1302 return true;
1304 return false;
1307 bool
1308 nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const
1310 if (!GetBorderRadii(aRadii))
1311 return false;
1312 InsetBorderRadii(aRadii, GetUsedBorderAndPadding());
1313 NS_FOR_CSS_HALF_CORNERS(corner) {
1314 if (aRadii[corner])
1315 return true;
1317 return false;
1320 nsStyleContext*
1321 nsFrame::GetAdditionalStyleContext(int32_t aIndex) const
1323 NS_PRECONDITION(aIndex >= 0, "invalid index number");
1324 return nullptr;
1327 void
1328 nsFrame::SetAdditionalStyleContext(int32_t aIndex,
1329 nsStyleContext* aStyleContext)
1331 NS_PRECONDITION(aIndex >= 0, "invalid index number");
1334 nscoord
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);
1345 const nsFrameList&
1346 nsFrame::GetChildList(ChildListID aListID) const
1348 if (IsAbsoluteContainer() &&
1349 aListID == GetAbsoluteListID()) {
1350 return GetAbsoluteContainingBlock()->GetChildList();
1351 } else {
1352 return nsFrameList::EmptyList();
1356 void
1357 nsFrame::GetChildLists(nsTArray<ChildList>* aLists) const
1359 if (IsAbsoluteContainer()) {
1360 nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList();
1361 absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID());
1365 void
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();
1372 if (root) {
1373 aLists->AppendElement(nsIFrame::ChildList(
1374 nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
1375 nsIFrame::kPrincipalList));
1379 GetChildLists(aLists);
1382 static nsIFrame*
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;
1391 return aFrame;
1394 int16_t
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.
1405 bool selectable;
1406 IsSelectable(&selectable, nullptr);
1407 if (!selectable) {
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;
1417 return selType;
1420 class nsDisplaySelectionOverlay : public nsDisplayItem {
1421 public:
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);
1431 #endif
1433 virtual void Paint(nsDisplayListBuilder* aBuilder,
1434 nsRenderingContext* aCtx) MOZ_OVERRIDE;
1435 NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
1436 private:
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;
1448 } else {
1449 colorID = LookAndFeel::eColorID_TextSelectBackgroundDisabled;
1452 nscolor color = LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255));
1454 gfxRGBA c(color);
1455 c.a = .5;
1457 gfxContext *ctx = aCtx->ThebesContext();
1458 ctx->SetColor(c);
1460 nsIntRect pxRect =
1461 mVisibleRect.ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel());
1462 ctx->NewPath();
1463 ctx->Rectangle(gfxRect(pxRect.x, pxRect.y, pxRect.width, pxRect.height), true);
1464 ctx->Fill();
1467 /********************************************************
1468 * Refreshes each content's frame
1469 *********************************************************/
1471 void
1472 nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
1473 nsDisplayList* aList,
1474 uint16_t aContentType)
1476 if (!IsSelected() || !IsVisibleForPainting(aBuilder))
1477 return;
1479 nsPresContext* presContext = PresContext();
1480 nsIPresShell *shell = presContext->PresShell();
1481 if (!shell)
1482 return;
1484 int16_t displaySelection = shell->GetSelectionFlags();
1485 if (!(displaySelection & aContentType))
1486 return;
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
1497 int32_t offset = 0;
1498 if (newContent) {
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);
1506 if (!details)
1507 return;
1509 bool normal = false;
1510 while (details) {
1511 if (details->mType == nsISelectionController::SELECTION_NORMAL) {
1512 normal = true;
1514 SelectionDetails *next = details->mNext;
1515 delete details;
1516 details = next;
1519 if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) {
1520 // Don't overlay an image if it's not in the primary selection.
1521 return;
1524 aList->AppendNewToTop(new (aBuilder)
1525 nsDisplaySelectionOverlay(aBuilder, this, selectionValue));
1528 void
1529 nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
1530 const nsDisplayListSet& aLists)
1532 if (StyleOutline()->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE)
1533 return;
1535 aLists.Outlines()->AppendNewToTop(
1536 new (aBuilder) nsDisplayOutline(aBuilder, this));
1539 void
1540 nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
1541 const nsDisplayListSet& aLists)
1543 if (!IsVisibleForPainting(aBuilder))
1544 return;
1546 DisplayOutlineUnconditional(aBuilder, aLists);
1549 void
1550 nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
1551 const nsRect& aDirtyRect, nsDisplayList* aList)
1553 if (!IsVisibleForPainting(aBuilder))
1554 return;
1556 aList->AppendNewToTop(new (aBuilder) nsDisplayCaret(aBuilder, this));
1559 nscolor
1560 nsIFrame::GetCaretColorAt(int32_t aOffset)
1562 // Use text color.
1563 return StyleColor()->mColor;
1566 bool
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
1573 // true.
1574 if (aBuilder->IsForEventDelivery() || aForceBackground ||
1575 !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) {
1576 return nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1577 aBuilder, this, aLists.BorderBackground());
1579 return false;
1582 void
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))
1591 return;
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,
1600 aForceBackground);
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);
1629 bool
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))) {
1637 return false;
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
1644 // continuation.
1645 nscoord y = 0;
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;
1658 return true;
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.
1667 static bool
1668 ApplyClipPropClipping(nsDisplayListBuilder* aBuilder,
1669 const nsIFrame* aFrame,
1670 const nsStyleDisplay* aDisp,
1671 nsRect* aRect,
1672 DisplayListClipState::AutoSaveRestore& aClipState)
1674 if (!aFrame->GetClipPropClipRect(aDisp, aRect, aFrame->GetSize()))
1675 return false;
1677 nsRect clipRect = *aRect + aBuilder->ToReferenceFrame(aFrame);
1678 aClipState.ClipContentDescendants(clipRect);
1679 return true;
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
1686 * descendants.
1688 static void
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)
1698 // have clipping.
1699 if (!nsFrame::ShouldApplyOverflowClipping(aFrame, aDisp)) {
1700 return;
1702 nsRect clipRect;
1703 bool haveRadii = false;
1704 nscoord radii[8];
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);
1710 } else {
1711 clipRect = aFrame->GetContentRectRelativeToSelf() +
1712 aBuilder->ToReferenceFrame(aFrame);
1713 // XXX border-radius
1715 aClipState.ClipContainingBlockDescendantsExtra(clipRect, haveRadii ? radii : nullptr);
1718 #ifdef DEBUG
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));
1724 } else {
1725 aCtx->SetColor(NS_RGB(255,0,0));
1727 aCtx->DrawRect(r);
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));
1734 aCtx->DrawRect(r);
1737 static void
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));
1755 #endif
1757 static nsresult
1758 WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder,
1759 nsDisplayList *aList, nsDisplayList *aOutput,
1760 uint32_t& aIndex, nsDisplayList* aTemp)
1762 if (aIndex > nsDisplayTransform::INDEX_MAX) {
1763 return NS_OK;
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);
1787 if (clip) {
1788 item->SetClip(aBuilder, *clip);
1790 aOutput->AppendToTop(item);
1791 break;
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();
1798 break;
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);
1819 break;
1821 default: {
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++));
1830 } else {
1831 aTemp->AppendToTop(item);
1833 break;
1836 } else {
1837 aTemp->AppendToTop(item);
1840 if (NS_FAILED(rv) || !item || aIndex > nsDisplayTransform::INDEX_MAX)
1841 return rv;
1844 return NS_OK;
1847 static bool
1848 IsScrollFrameActive(nsIScrollableFrame* aScrollableFrame)
1850 return aScrollableFrame && aScrollableFrame->IsScrollingActive();
1853 static nsresult
1854 WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
1855 nsDisplayList *aList)
1857 uint32_t index = 0;
1858 nsDisplayList temp;
1859 nsDisplayList output;
1860 nsresult rv = WrapPreserve3DListInternal(aFrame, aBuilder, aList, &output,
1861 index, &temp);
1863 if (!temp.IsEmpty()) {
1864 output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame,
1865 &temp, temp.GetVisibleRect(), index++));
1868 aList->AppendToTop(&output);
1869 return rv;
1872 class AutoSaveRestoreBlendMode
1874 nsDisplayListBuilder& mBuilder;
1875 EnumSet<gfx::CompositionOp> mSavedBlendModes;
1876 public:
1877 explicit AutoSaveRestoreBlendMode(nsDisplayListBuilder& aBuilder)
1878 : mBuilder(aBuilder)
1879 , mSavedBlendModes(aBuilder.ContainedBlendModes())
1882 ~AutoSaveRestoreBlendMode() {
1883 mBuilder.SetContainsBlendModes(mSavedBlendModes);
1887 static void
1888 CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1890 nsIContent* content = aFrame->GetContent();
1891 if (!content) {
1892 return;
1894 EventListenerManager* elm = nsContentUtils::GetExistingListenerManagerForNode(content);
1895 if (!elm) {
1896 return;
1898 if (elm->HasListenersFor(nsGkAtoms::ontouchstart) ||
1899 elm->HasListenersFor(nsGkAtoms::ontouchmove)) {
1900 aBuilder->SetAncestorHasTouchEventHandler(true);
1904 void
1905 nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
1906 const nsRect& aDirtyRect,
1907 nsDisplayList* aList) {
1908 if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
1909 return;
1911 // Replaced elements have their visibility handled here, because
1912 // they're visually atomic
1913 if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder))
1914 return;
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)) {
1925 return;
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;
1944 } else {
1945 if (overflow.IsEmpty() && !Preserves3DChildren()) {
1946 return;
1949 nsRect untransformedDirtyRect;
1950 if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this,
1951 nsPoint(0,0), &untransformedDirtyRect)) {
1952 dirtyRect = untransformedDirtyRect;
1953 } else {
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();
1959 inTransform = true;
1962 bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1963 nsRect dirtyRectOutsideSVGEffects = dirtyRect;
1964 if (usingSVGEffects) {
1965 dirtyRect =
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.
1986 clipState.Clear();
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,
1998 nestedClipState)) {
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
2032 // element itself.
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.
2040 for (;;) {
2041 nsDisplayItem* item = set.PositionedDescendants()->GetBottom();
2042 if (item && item->ZIndex() < 0) {
2043 set.PositionedDescendants()->RemoveBottom();
2044 resultList.AppendToTop(item);
2045 continue;
2047 break;
2049 // 4: block backgrounds
2050 resultList.AppendToTop(set.BlockBorderBackgrounds());
2051 // 5: floats
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
2059 // items.
2060 nsIContent* content = GetContent();
2061 if (!content) {
2062 content = PresContext()->Document()->GetRootElement();
2064 if (content) {
2065 set.Outlines()->SortByContentOrder(aBuilder, content);
2067 #ifdef DEBUG
2068 DisplayDebugBorders(aBuilder, this, set);
2069 #endif
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
2108 * anything.
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);
2132 } else {
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();
2175 return item;
2178 void
2179 nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
2180 nsIFrame* aChild,
2181 const nsRect& aDirtyRect,
2182 const nsDisplayListSet& aLists,
2183 uint32_t aFlags) {
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())
2187 return;
2189 nsIFrame* child = aChild;
2190 if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
2191 return;
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;
2198 if (!isSVG &&
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))
2221 return;
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)
2228 return;
2229 savedOutOfFlowData = static_cast<nsDisplayListBuilder::OutOfFlowDisplayData*>
2230 (child->Properties().Get(nsDisplayListBuilder::OutOfFlowDisplayDataProperty()));
2231 if (savedOutOfFlowData) {
2232 dirty = savedOutOfFlowData->mDirtyRect;
2233 } else {
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.
2237 dirty.SetEmpty();
2239 pseudoStackingContext = true;
2241 if (child->Preserves3D()) {
2242 nsRect* savedDirty = static_cast<nsRect*>
2243 (child->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty()));
2244 if (savedDirty) {
2245 dirty = *savedDirty;
2246 } else {
2247 dirty.SetEmpty();
2251 NS_ASSERTION(childType != nsGkAtoms::placeholderFrame,
2252 "Should have dealt with placeholders already");
2253 if (aBuilder->GetSelectedFramesOnly() &&
2254 child->IsLeaf() &&
2255 !aChild->IsSelected()) {
2256 return;
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) {
2278 nsRect childDirty;
2279 if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect()))
2280 return;
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))
2296 return;
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
2342 // children.
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);
2351 nsDisplayList list;
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);
2362 } else {
2363 nsRect clipRect;
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
2375 // return early.
2376 nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions();
2377 if (eventRegions) {
2378 eventRegions->AddFrame(aBuilder, child);
2380 child->BuildDisplayList(aBuilder, dirty, aLists);
2381 aBuilder->DisplayCaret(child, dirty, aLists.Content());
2382 #ifdef DEBUG
2383 DisplayDebugBorders(aBuilder, child, aLists);
2384 #endif
2385 return;
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
2391 // z-index:non-auto
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());
2408 #ifdef DEBUG
2409 DisplayDebugBorders(aBuilder, child, aLists);
2410 #endif
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.
2415 clipState.Clear();
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);
2423 if (isSVG) {
2424 aLists.Content()->AppendNewToTop(item);
2425 } else {
2426 aLists.PositionedDescendants()->AppendNewToTop(item);
2429 } else if (!isSVG && disp->IsFloating(child)) {
2430 if (!list.IsEmpty()) {
2431 aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list));
2433 } else {
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);
2444 void
2445 nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder,
2446 const nsRect& aDirtyRect)
2448 if (IsAbsoluteContainer()) {
2449 aBuilder->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList(), aDirtyRect);
2453 nsresult
2454 nsFrame::GetContentForEvent(WidgetEvent* aEvent,
2455 nsIContent** aContent)
2457 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
2458 *aContent = f->GetContent();
2459 NS_IF_ADDREF(*aContent);
2460 return NS_OK;
2463 void
2464 nsFrame::FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent)
2466 nsIContent* target = aContent ? aContent : mContent;
2468 if (target) {
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");
2476 nsresult
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);
2497 return NS_OK;
2500 NS_IMETHODIMP
2501 nsFrame::GetDataForTableSelection(const nsFrameSelection* aFrameSelection,
2502 nsIPresShell* aPresShell,
2503 WidgetMouseEvent* aMouseEvent,
2504 nsIContent** aParentContent,
2505 int32_t* aContentOffset,
2506 int32_t* aTarget)
2508 if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget)
2509 return NS_ERROR_NULL_POINTER;
2511 *aParentContent = nullptr;
2512 *aContentOffset = 0;
2513 *aTarget = 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
2535 #ifdef XP_MACOSX
2536 doTableSelection = aMouseEvent->IsMeta() || (aMouseEvent->IsShift() && selectingTableCells);
2537 #else
2538 doTableSelection = aMouseEvent->IsControl() || (aMouseEvent->IsShift() && selectingTableCells);
2539 #endif
2541 if (!doTableSelection)
2542 return NS_OK;
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()))
2555 return NS_OK;
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;
2562 while (frame)
2564 // Check for a table cell by querying to a known CellFrame interface
2565 nsITableCellLayout *cellElement = do_QueryFrame(frame);
2566 if (cellElement)
2568 foundCell = true;
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
2571 break;
2573 else
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);
2579 if (tableFrame)
2581 foundTable = true;
2582 //TODO: How can we select row when along left table edge
2583 // or select column when along top edge?
2584 break;
2585 } else {
2586 frame = frame->GetParent();
2587 // Stop if we have hit the selection's limiting content node
2588 if (frame && frame->GetContent() == limiter)
2589 break;
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);
2603 // Not likely?
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;
2612 #if 0
2613 if (selectRow)
2614 *aTarget = nsISelectionPrivate::TABLESELECTION_ROW;
2615 else if (selectColumn)
2616 *aTarget = nsISelectionPrivate::TABLESELECTION_COLUMN;
2617 else
2618 #endif
2619 if (foundCell)
2620 *aTarget = nsISelectionPrivate::TABLESELECTION_CELL;
2621 else if (foundTable)
2622 *aTarget = nsISelectionPrivate::TABLESELECTION_TABLE;
2624 return NS_OK;
2627 nsresult
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);
2653 while (frame) {
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;
2660 break;
2661 default:
2662 // otherwise return the first value which is not 'auto'
2663 if (selectStyle == NS_STYLE_USER_SELECT_AUTO) {
2664 selectStyle = userinterface->mUserSelect;
2666 break;
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;
2674 else
2675 if (selectStyle == NS_STYLE_USER_SELECT_MOZ_ALL)
2676 selectStyle = NS_STYLE_USER_SELECT_ALL;
2678 // return stuff
2679 if (aSelectStyle)
2680 *aSelectStyle = selectStyle;
2681 if (mState & NS_FRAME_GENERATED_CONTENT)
2682 *aSelectable = false;
2683 else
2684 *aSelectable = (selectStyle != NS_STYLE_USER_SELECT_NONE);
2685 return NS_OK;
2689 * Handles the Mouse Press Event for the frame
2691 NS_IMETHODIMP
2692 nsFrame::HandlePress(nsPresContext* aPresContext,
2693 WidgetGUIEvent* aEvent,
2694 nsEventStatus* aEventStatus)
2696 NS_ENSURE_ARG_POINTER(aEventStatus);
2697 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
2698 return NS_OK;
2701 NS_ENSURE_ARG_POINTER(aEvent);
2702 if (aEvent->mClass == eTouchEventClass) {
2703 return NS_OK;
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))
2710 return NS_OK;
2712 nsresult rv;
2713 nsIPresShell *shell = aPresContext->GetPresShell();
2714 if (!shell)
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))) {
2734 return NS_OK;
2740 // check whether style allows selection
2741 // if not, don't tell selection the mouse event even occurred.
2742 bool selectable;
2743 uint8_t selectStyle;
2744 rv = IsSelectable(&selectable, &selectStyle);
2745 if (NS_FAILED(rv)) return rv;
2747 // check for select: none
2748 if (!selectable)
2749 return NS_OK;
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);
2766 if (scrollFrame) {
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
2775 // event frame
2776 const nsFrameSelection* frameselection = nullptr;
2777 if (useFrameSelection)
2778 frameselection = GetConstFrameSelection();
2779 else
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
2785 #ifdef XP_MACOSX
2786 if (mouseEvent->IsControl())
2787 return NS_OK;//short circuit. hard coded for mac due to time restraints.
2788 bool control = mouseEvent->IsMeta();
2789 #else
2790 bool control = mouseEvent->IsControl();
2791 #endif
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,
2823 offsets.associate);
2826 // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
2827 nsCOMPtr<nsIContent>parentContent;
2828 int32_t contentOffset;
2829 int32_t target;
2830 rv = GetDataForTableSelection(frameselection, shell, mouseEvent,
2831 getter_AddRefs(parentContent), &contentOffset,
2832 &target);
2833 if (NS_SUCCEEDED(rv) && parentContent)
2835 fc->SetDragState(true);
2836 return fc->HandleTableSelection(parentContent, contentOffset, target,
2837 mouseEvent);
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;
2861 while (curDetail)
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)
2875 inSelection = true;
2878 SelectionDetails *nextDetail = curDetail->mNext;
2879 delete curDetail;
2880 curDetail = nextDetail;
2883 if (inSelection) {
2884 fc->SetDragState(false);
2885 fc->SetDelayedCaretData(mouseEvent);
2886 return NS_OK;
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,
2896 offsets.associate);
2898 if (NS_FAILED(rv))
2899 return rv;
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);
2915 return rv;
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.
2932 nsresult
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)
2943 return NS_OK;
2945 ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN);
2946 if (!offsets.content)
2947 return NS_ERROR_FAILURE;
2949 int32_t offset;
2950 const nsFrameSelection* frameSelection =
2951 PresContext()->GetPresShell()->ConstFrameSelection();
2952 nsIFrame* theFrame = frameSelection->
2953 GetFrameForNodeOffset(offsets.content, offsets.offset,
2954 offsets.associate, &offset);
2955 if (!theFrame)
2956 return NS_ERROR_FAILURE;
2958 nsFrame* frame = static_cast<nsFrame*>(theFrame);
2959 return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType,
2960 offset, aPresContext,
2961 aBeginAmountType != eSelectWord,
2962 aSelectFlags);
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?
2969 NS_IMETHODIMP
2970 nsFrame::HandleMultiplePress(nsPresContext* aPresContext,
2971 WidgetGUIEvent* aEvent,
2972 nsEventStatus* aEventStatus,
2973 bool aControlHeld)
2975 NS_ENSURE_ARG_POINTER(aEvent);
2976 NS_ENSURE_ARG_POINTER(aEventStatus);
2978 if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
2979 DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
2980 return NS_OK;
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();
2989 if (!mouseEvent) {
2990 return NS_OK;
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;
2998 } else {
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;
3005 } else {
3006 return NS_OK;
3009 nsPoint relPoint =
3010 nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
3011 return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount,
3012 (aControlHeld ? SELECT_ACCUMULATE : 0));
3015 nsresult
3016 nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack,
3017 nsSelectionAmount aAmountForward,
3018 int32_t aStartPos,
3019 nsPresContext* aPresContext,
3020 bool aJumpLines,
3021 uint32_t aSelectFlags)
3023 nsIFrame* baseFrame = this;
3024 int32_t baseOffset = aStartPos;
3025 nsresult rv;
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,
3031 eDirNext,
3032 aStartPos,
3034 aJumpLines,
3035 true, //limit on scrolled views
3036 false,
3037 false);
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,
3047 eDirPrevious,
3048 baseOffset,
3050 aJumpLines,
3051 true, //limit on scrolled views
3052 false,
3053 false);
3054 rv = baseFrame->PeekOffset(&startpos);
3055 if (NS_FAILED(rv))
3056 return rv;
3058 nsPeekOffsetStruct endpos(aAmountForward,
3059 eDirNext,
3060 aStartPos,
3062 aJumpLines,
3063 true, //limit on scrolled views
3064 false,
3065 false);
3066 rv = PeekOffset(&endpos);
3067 if (NS_FAILED(rv))
3068 return rv;
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);
3077 if (NS_FAILED(rv))
3078 return rv;
3080 rv = frameSelection->HandleClick(endpos.mResultContent,
3081 endpos.mContentOffset, endpos.mContentOffset,
3082 true, false,
3083 CARET_ASSOCIATE_BEFORE);
3084 if (NS_FAILED(rv))
3085 return rv;
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");
3098 bool selectable;
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.
3104 if (!selectable)
3105 return NS_OK;
3106 if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
3107 return NS_OK;
3109 nsIPresShell *presShell = aPresContext->PresShell();
3111 nsRefPtr<nsFrameSelection> frameselection = GetFrameSelection();
3112 bool mouseDown = frameselection->GetDragState();
3113 if (!mouseDown)
3114 return NS_OK;
3116 frameselection->StopAutoScrollTimer();
3118 // Check if we are dragging in a table cell
3119 nsCOMPtr<nsIContent> parentContent;
3120 int32_t contentOffset;
3121 int32_t target;
3122 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
3123 nsresult result;
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,
3131 mouseEvent);
3132 } else {
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()) {
3140 return NS_OK;
3143 // get the nearest scrollframe
3144 nsIScrollableFrame* scrollFrame =
3145 nsLayoutUtils::GetNearestScrollableFrame(this,
3146 nsLayoutUtils::SCROLLABLE_SAME_DOC |
3147 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
3149 if (scrollFrame) {
3150 nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
3151 if (capturingFrame) {
3152 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
3153 capturingFrame);
3154 frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
3158 return NS_OK;
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.
3165 static nsresult
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) {
3176 return NS_OK;
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
3190 // caret.
3191 // However, we'll use the mouse position from the release, since:
3192 // * it's easier
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(),
3202 false,
3203 aOffsets.associate);
3204 if (NS_FAILED(rv)) {
3205 return rv;
3207 } else if (aParentContentForTableSel) {
3208 aFrameSelection->SetDragState(false);
3209 rv = aFrameSelection->HandleTableSelection(
3210 aParentContentForTableSel,
3211 aContentOffsetForTableSel,
3212 aTargetForTableSel,
3213 aEvent->AsMouseEvent());
3214 if (NS_FAILED(rv)) {
3215 return rv;
3218 aFrameSelection->SetDelayedCaretData(0);
3221 aFrameSelection->SetDragState(false);
3222 aFrameSelection->StopAutoScrollTimer();
3224 return NS_OK;
3227 NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext,
3228 WidgetGUIEvent* aEvent,
3229 nsEventStatus* aEventStatus)
3231 if (aEvent->mClass != eMouseEventClass) {
3232 return NS_OK;
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);
3243 bool selectionOff =
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;
3267 } else {
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();
3291 if (doc) {
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!
3307 return selectionOff
3308 ? NS_OK
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;
3318 int32_t start;
3319 int32_t end;
3322 // Retrieve the content offsets of a frame
3323 static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) {
3324 nsCOMPtr<nsIContent> content, parent;
3325 content = aFrame->GetContent();
3326 if (!content) {
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
3343 do {
3344 parent = content->GetParent();
3345 if (parent) {
3346 int32_t beginOffset = parent->IndexOf(content);
3347 if (beginOffset >= 0)
3348 return FrameContentRange(parent, beginOffset, beginOffset + 1);
3349 content = parent;
3351 } while (parent);
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);
3369 bool IsNull() {
3370 return !frame;
3372 nsIFrame* frame;
3373 bool frameEdge;
3374 bool afterFrame;
3375 bool emptyBlock;
3378 // See function implementation for information
3379 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint,
3380 uint32_t aFlags);
3382 static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags)
3384 if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
3385 !aFrame->StyleVisibility()->IsVisible()) {
3386 return false;
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,
3411 nsPoint aPoint,
3412 uint32_t aFlags)
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();
3432 if (!aEndFrame) {
3433 while (frame && (!SelfIsSelectable(frame, aFlags) ||
3434 frame->IsEmpty()))
3435 frame = frame->GetNextSibling();
3436 if (frame)
3437 result = frame;
3438 } else {
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
3443 while (frame) {
3444 if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags))
3445 result = frame;
3446 frame = frame->GetNextSibling();
3449 if (result)
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,
3461 nsPoint aPoint,
3462 uint32_t aFlags)
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())
3475 continue;
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);
3486 } else {
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,
3502 aFlags);
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,
3513 nsPoint aPoint,
3514 uint32_t aFlags)
3516 nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aFrame); // used only for QI
3517 if (!bf)
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();
3525 if (blockContent) {
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
3544 if (BCoord < 0)
3545 break;
3546 ++curLine;
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())
3554 ++nextLine;
3555 while (prevLine != end && prevLine->IsEmpty())
3556 --prevLine;
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;
3575 else
3576 closestLine = nextLine;
3580 do {
3581 FrameTarget target = GetSelectionClosestFrameForLine(bf, closestLine,
3582 aPoint, aFlags);
3583 if (!target.IsNull())
3584 return target;
3585 ++closestLine;
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,
3599 uint32_t aFlags)
3602 // Handle blocks; if the frame isn't a block, the method fails
3603 FrameTarget target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags);
3604 if (!target.IsNull())
3605 return target;
3608 nsIFrame *kid = aFrame->GetFirstPrincipalChild();
3610 if (kid) {
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())
3615 continue;
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;
3639 return offsets;
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;
3653 else
3654 offsets.secondaryOffset = range.end;
3655 } else {
3656 offsets.offset = range.start;
3657 if (rect.Contains(aPoint))
3658 offsets.secondaryOffset = range.end;
3659 else
3660 offsets.secondaryOffset = range.start;
3662 offsets.associate =
3663 offsets.offset == range.start ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
3664 return offsets;
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
3672 // a cursor.
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,
3682 uint32_t aFlags)
3684 nsIFrame *adjustedFrame;
3685 if (aFlags & IGNORE_SELECTION_STYLE) {
3686 adjustedFrame = this;
3688 else {
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();
3721 offsets.offset = 0;
3722 offsets.secondaryOffset = 0;
3723 offsets.associate = CARET_ASSOCIATE_AFTER;
3724 return offsets;
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;
3735 else
3736 offsets.offset = range.start;
3737 offsets.secondaryOffset = offsets.offset;
3738 offsets.associate = offsets.offset == range.start ?
3739 CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
3740 return offsets;
3743 nsPoint pt;
3744 if (closest.frame != this) {
3745 if (closest.frame->IsSVGText()) {
3746 pt = nsLayoutUtils::TransformAncestorPointToFrame(closest.frame,
3747 aPoint, this);
3748 } else {
3749 pt = aPoint - closest.frame->GetOffsetTo(this);
3751 } else {
3752 pt = aPoint;
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);
3767 void
3768 nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext)
3770 if (aImage.GetType() != eStyleImageType_Image) {
3771 return;
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);
3782 nsresult
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.
3789 aCursor.mCursor =
3790 (mContent && mContent->IsEditable()) ? NS_STYLE_CURSOR_TEXT :
3791 NS_STYLE_CURSOR_DEFAULT;
3795 return NS_OK;
3798 // Resize and incremental reflow
3800 /* virtual */ void
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)
3825 nscoord result = 0;
3826 DISPLAY_MIN_WIDTH(this, result);
3827 return result;
3830 /* virtual */ nscoord
3831 nsFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
3833 nscoord result = 0;
3834 DISPLAY_PREF_WIDTH(this, result);
3835 return result;
3838 /* virtual */ void
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);
3847 if (canBreak)
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;
3855 if (canBreak)
3856 aData->OptionallyBreak(aRenderingContext);
3859 /* virtual */ void
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);
3870 void
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;
3882 floats.Clear();
3883 trailingTextFrame = nullptr;
3884 skipWhitespace = true;
3887 void
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
3897 // floats.
3898 if (currentLine + aHyphenWidth < 0 || atStartOfLine)
3899 return;
3900 currentLine += aHyphenWidth;
3901 ForceBreak(aRenderingContext);
3904 void
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,
3923 floats_cur_right);
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.
3937 floats_cur =
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);
3948 floats.Clear();
3951 currentLine =
3952 NSCoordSaturatingSubtract(currentLine, trailingWhitespace, nscoord_MAX);
3953 prevLines = std::max(prevLines, currentLine);
3954 currentLine = trailingWhitespace = 0;
3955 skipWhitespace = true;
3958 static void
3959 AddCoord(const nsStyleCoord& aStyle,
3960 nsRenderingContext* aRenderingContext,
3961 nsIFrame* aFrame,
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();
3970 return;
3972 case eStyleUnit_Percent: {
3973 NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f,
3974 "unexpected negative value");
3975 *aPercent += aStyle.GetPercentValue();
3976 return;
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);
3984 } else {
3985 *aCoord += calc->mLength;
3986 *aPercent += calc->mPercent;
3988 return;
3990 default: {
3991 return;
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();
4021 nsIntMargin border;
4022 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
4023 this, disp->mAppearance,
4024 &border);
4025 result.hBorder = presContext->DevPixelsToAppUnits(border.LeftRight());
4027 nsIntMargin padding;
4028 if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
4029 this, disp->mAppearance,
4030 &padding)) {
4031 result.hPadding = presContext->DevPixelsToAppUnits(padding.LeftRight());
4032 result.hPctPadding = 0;
4036 return result;
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);
4051 /* virtual */
4052 LogicalSize
4053 nsFrame::ComputeSize(nsRenderingContext *aRenderingContext,
4054 WritingMode aWM,
4055 const LogicalSize& aCBSize,
4056 nscoord aAvailableISize,
4057 const LogicalSize& aMargin,
4058 const LogicalSize& aBorder,
4059 const LogicalSize& aPadding,
4060 uint32_t aFlags)
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;
4072 // fall through
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);
4085 } else {
4086 inlineStyleCoord = &(stylePos->mWidth);
4087 blockStyleCoord = &(stylePos->mHeight);
4090 bool isFlexItem = IsFlexItem();
4091 bool isInlineFlexItem = false;
4093 if (isFlexItem) {
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
4097 // all.
4098 uint32_t flexDirection = GetParent()->StylePosition()->mFlexDirection;
4099 isInlineFlexItem =
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;
4110 } else {
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) {
4128 result.ISize(aWM) =
4129 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
4130 aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
4131 *inlineStyleCoord);
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)) {
4139 nscoord maxISize =
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));
4146 nscoord minISize;
4147 if (stylePos->mMinWidth.GetUnit() != eStyleUnit_Auto &&
4148 !(isFlexItem && isInlineFlexItem)) {
4149 minISize =
4150 nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
4151 aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
4152 stylePos->mMinWidth);
4153 } else {
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.
4159 minISize = 0;
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)) {
4169 result.BSize(aWM) =
4170 nsLayoutUtils::ComputeHeightValue(aCBSize.BSize(aWM),
4171 boxSizingAdjust.BSize(aWM),
4172 *blockStyleCoord);
4175 if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
4176 if (!nsLayoutUtils::IsAutoHeight(stylePos->mMaxHeight, aCBSize.BSize(aWM)) &&
4177 !(isFlexItem && !isInlineFlexItem)) {
4178 nscoord maxBSize =
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)) {
4187 nscoord minBSize =
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...
4205 nsSize size;
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));
4224 return result;
4227 nsRect
4228 nsIFrame::ComputeTightBounds(gfxContext* aContext) const
4230 return GetVisualOverflowRect();
4233 nsRect
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());
4253 return r;
4256 /* virtual */ nsresult
4257 nsIFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext,
4258 nscoord* aX,
4259 nscoord* aXMost)
4261 return NS_ERROR_NOT_IMPLEMENTED;
4264 /* virtual */
4265 LogicalSize
4266 nsFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
4267 WritingMode aWM,
4268 const mozilla::LogicalSize& aCBSize,
4269 nscoord aAvailableISize,
4270 const mozilla::LogicalSize& aMargin,
4271 const mozilla::LogicalSize& aBorder,
4272 const mozilla::LogicalSize& aPadding,
4273 bool aShrinkWrap)
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);
4286 return result;
4289 nscoord
4290 nsFrame::ShrinkWidthToFit(nsRenderingContext *aRenderingContext,
4291 nscoord aWidthInCB)
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);
4297 nscoord result;
4298 nscoord minWidth = GetMinISize(aRenderingContext);
4299 if (minWidth > aWidthInCB) {
4300 result = minWidth;
4301 } else {
4302 nscoord prefWidth = GetPrefISize(aRenderingContext);
4303 if (prefWidth > aWidthInCB) {
4304 result = aWidthInCB;
4305 } else {
4306 result = prefWidth;
4309 return result;
4312 void
4313 nsFrame::WillReflow(nsPresContext* aPresContext)
4315 #ifdef DEBUG_dbaron_off
4316 // bug 81268
4317 NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
4318 "nsFrame::WillReflow: frame is already in reflow");
4319 #endif
4321 NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
4322 ("WillReflow: oldState=%x", mState));
4323 mState |= NS_FRAME_IN_REFLOW;
4326 void
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 &&
4346 !GetPrevInFlow()) {
4347 const nsStyleCoord &height = aReflowState->mStylePosition->mHeight;
4348 if (height.HasPercent()) {
4349 aReflowState->mPercentHeightObserver->NotifyPercentHeight(*aReflowState);
4354 void
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);
4366 void
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,
4392 containingBlock,
4393 aConstrainHeight, true, true, // XXX could be optimized
4394 &aDesiredSize.mOverflowAreas);
4398 void
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();
4410 /* virtual */ bool
4411 nsFrame::CanContinueTextRun() const
4413 // By default, a frame will *not* allow a text run to be continued
4414 // through it.
4415 return false;
4418 void
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);
4430 nsresult
4431 nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
4433 NS_NOTREACHED("should only be called for text frames");
4434 return NS_OK;
4437 nsresult
4438 nsFrame::AttributeChanged(int32_t aNameSpaceID,
4439 nsIAtom* aAttribute,
4440 int32_t aModType)
4442 return NS_OK;
4445 // Flow member functions
4447 nsSplittableType
4448 nsFrame::GetSplittableType() const
4450 return NS_FRAME_NOT_SPLITTABLE;
4453 nsIFrame* nsFrame::GetPrevContinuation() const
4455 return nullptr;
4458 void
4459 nsFrame::SetPrevContinuation(nsIFrame* aPrevContinuation)
4461 MOZ_ASSERT(false, "not splittable");
4464 nsIFrame* nsFrame::GetNextContinuation() const
4466 return nullptr;
4469 void
4470 nsFrame::SetNextContinuation(nsIFrame*)
4472 MOZ_ASSERT(false, "not splittable");
4475 nsIFrame* nsFrame::GetPrevInFlowVirtual() const
4477 return nullptr;
4480 void
4481 nsFrame::SetPrevInFlow(nsIFrame* aPrevInFlow)
4483 MOZ_ASSERT(false, "not splittable");
4486 nsIFrame* nsFrame::GetNextInFlowVirtual() const
4488 return nullptr;
4491 void
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()) {
4507 frame = next;
4509 NS_POSTCONDITION(frame, "illegal state in continuation chain.");
4510 return frame;
4513 NS_DECLARE_FRAME_PROPERTY(ViewProperty, nullptr)
4515 // Associated view object
4516 nsView*
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))
4521 return nullptr;
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
4532 return GetView();
4535 nsresult
4536 nsIFrame::SetView(nsView* aView)
4538 if (aView) {
4539 aView->SetFrame(this);
4541 #ifdef DEBUG
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");
4550 #endif
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);
4561 f = f->GetParent())
4562 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
4565 return NS_OK;
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()) {
4577 if (f->HasView()) {
4578 return f;
4581 return nullptr;
4584 // virtual
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);
4599 const nsIFrame* f;
4600 for (f = this; f != aOther && f; f = f->GetParent()) {
4601 offset += f->GetPosition();
4604 if (f != aOther) {
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
4608 while (aOther) {
4609 offset -= aOther->GetPosition();
4610 aOther = aOther->GetParent();
4614 return offset;
4617 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const
4619 return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
4622 nsPoint
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 "
4630 "hierarchies?");
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();
4648 if (parent) {
4649 f = parent;
4650 } else {
4651 nsPoint newOffset(0, 0);
4652 root = f;
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;
4660 currAPD = newAPD;
4661 docOffset += newOffset;
4664 if (f == aOther) {
4665 offset += docOffset.ConvertAppUnits(currAPD, aAPD);
4666 } else {
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
4671 // aOther.
4672 nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
4673 offset -= negOffset;
4676 return offset;
4679 // virtual
4680 nsIntRect nsIFrame::GetScreenRectExternal() const
4682 return GetScreenRect();
4685 nsIntRect nsIFrame::GetScreenRect() const
4687 return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel());
4690 // virtual
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);
4713 } else {
4714 nsCOMPtr<nsIWidget> rootWidget;
4715 presContext->PresShell()->GetViewManager()->GetRootWidget(getter_AddRefs(rootWidget));
4716 if (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
4728 void
4729 nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const
4731 NS_PRECONDITION(nullptr != aView, "null OUT parameter pointer");
4732 nsIFrame* frame = const_cast<nsIFrame*>(this);
4734 *aView = nullptr;
4735 aOffset.MoveTo(0, 0);
4736 do {
4737 aOffset += frame->GetPosition();
4738 frame = frame->GetParent();
4739 } while (frame && !frame->HasView());
4741 if (frame) {
4742 *aView = frame->GetView();
4746 nsIWidget*
4747 nsIFrame::GetNearestWidget() const
4749 return GetClosestView()->GetNearestWidget(nullptr);
4752 nsIWidget*
4753 nsIFrame::GetNearestWidget(nsPoint& aOffset) const
4755 nsPoint offsetToView;
4756 nsPoint offsetToWidget;
4757 nsIWidget* widget =
4758 GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
4759 aOffset = offsetToView + offsetToWidget;
4760 return widget;
4763 nsIAtom*
4764 nsFrame::GetType() const
4766 return nullptr;
4769 bool
4770 nsIFrame::IsLeaf() const
4772 return true;
4775 Matrix4x4
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),
4801 0.0f);
4802 return result;
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
4813 // was chosen.
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();
4821 if (toplevel) {
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");
4837 } else {
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.
4855 if (!*aOutAncestor)
4856 return Matrix4x4();
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);
4864 if (!parent)
4865 break;
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),
4880 0.0f);
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;
4892 } else {
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;
4905 break;
4907 parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
4909 if (!parent) {
4910 needsSchedulePaint = true;
4913 if (!aHasDisplayItem) {
4914 return;
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);
4925 void
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) {
4933 return;
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();
4950 void
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);
4971 void
4972 nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey)
4974 bool hasDisplayItem =
4975 !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
4976 InvalidateFrameInternal(this, hasDisplayItem);
4979 void
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);
4987 } else {
4988 alreadyInvalid = true;
4991 if (!hasDisplayItem) {
4992 return;
4995 nsRect *rect = static_cast<nsRect*>(Properties().Get(InvalidationRect()));
4996 if (!rect) {
4997 if (alreadyInvalid) {
4998 return;
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;
5010 bool
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.
5019 return false;
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.
5027 return false;
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
5037 // changes.)
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)) {
5045 return false;
5047 layer->SetBaseTransformForNextTransaction(transform3d);
5048 *aLayerResult = layer;
5049 return true;
5052 bool
5053 nsIFrame::IsInvalid(nsRect& aRect)
5055 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
5056 return false;
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!");
5062 aRect = *rect;
5063 } else {
5064 aRect.SetEmpty();
5066 return true;
5069 void
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())) {
5078 return;
5080 if (!pres->GetContainerWeak()) {
5081 NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
5082 return;
5085 pres->PresShell()->ScheduleViewManagerFlush(aType == PAINT_DELAYED_COMPRESS ?
5086 nsIPresShell::PAINT_DELAYED_COMPRESS :
5087 nsIPresShell::PAINT_DEFAULT);
5089 if (aType == PAINT_DELAYED_COMPRESS) {
5090 return;
5093 if (aType == PAINT_DEFAULT) {
5094 displayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
5096 nsIPresShell* shell = PresContext()->PresShell();
5097 if (shell) {
5098 shell->AddInvalidateHiddenPresShellObserver(pres->RefreshDriver());
5102 Layer*
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) {
5115 return layer;
5118 if (aDamageRect && aDamageRect->IsEmpty()) {
5119 return layer;
5122 if (!layer) {
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) {
5133 InvalidateFrame();
5134 } else {
5135 InvalidateFrame(aDisplayItemKey);
5137 return nullptr;
5140 if (aDamageRect) {
5141 layer->AddInvalidRect(*aDamageRect);
5142 } else {
5143 layer->SetInvalidRectToVisibleRegion();
5146 SchedulePaint(PAINT_COMPOSITE_ONLY);
5147 return layer;
5150 static nsRect
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);
5165 return r;
5168 // box-shadow
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
5179 // border-image
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
5182 // the image loads.
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
5197 // the frame dies.
5199 if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
5200 aFrame->Properties().
5201 Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
5202 r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r);
5205 return r;
5208 void
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(),
5220 &position);
5221 SetPosition(position);
5224 nsPoint
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();
5237 nsPoint
5238 nsIFrame::GetPositionIgnoringScrolling()
5240 return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
5241 : GetPosition();
5244 nsRect
5245 nsIFrame::GetOverflowRect(nsOverflowType aType) const
5247 NS_ABORT_IF_FALSE(aType == eVisualOverflow || aType == eScrollableOverflow,
5248 "unexpected type");
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());
5271 nsOverflowAreas
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()));
5284 nsOverflowAreas
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());
5299 nsRect
5300 nsIFrame::GetScrollableOverflowRectRelativeToParent() const
5302 return GetScrollableOverflowRect() + mRect.TopLeft();
5305 nsRect
5306 nsIFrame::GetVisualOverflowRectRelativeToParent() const
5308 return GetVisualOverflowRect() + mRect.TopLeft();
5311 nsRect
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();
5323 nsRect
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();
5335 nsRect
5336 nsIFrame::GetPreEffectsVisualOverflowRect() const
5338 nsRect* r = static_cast<nsRect*>
5339 (Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
5340 return r ? *r : GetVisualOverflowRectRelativeToSelf();
5343 inline static bool
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);
5351 /* virtual */ bool
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();
5367 if (view) {
5368 uint32_t flags = 0;
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);
5378 return true;
5381 return false;
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)
5389 bool
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;
5408 } else {
5409 aStatus = NS_FRAME_COMPLETE;
5412 return true;
5414 mState &= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
5415 return false;
5418 bool
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);
5428 static nsIFrame*
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
5433 // blocks.
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?");
5444 return frame;
5447 nsIFrame*
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();
5467 if (content) {
5468 nsIContent* parentContent = content->GetParent();
5469 if (parentContent) {
5470 result = parentContent->IndexOf(content);
5474 return result;
5478 * List a frame tree to stderr. Meant to be called from gdb.
5480 void
5481 DebugListFrameTree(nsIFrame* aFrame)
5483 ((nsFrame*)aFrame)->List(stderr);
5486 void
5487 nsIFrame::ListTag(nsACString& aTo) const
5489 ListTag(aTo, this);
5492 /* static */
5493 void
5494 nsIFrame::ListTag(nsACString& aTo, const nsIFrame* aFrame) {
5495 nsAutoString tmp;
5496 aFrame->GetFrameName(tmp);
5497 aTo += NS_ConvertUTF16toUTF8(tmp).get();
5498 aTo += nsPrintfCString("@%p", static_cast<const void*>(aFrame));
5501 // Debugging
5502 void
5503 nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) const
5505 aTo =+ aPrefix;
5506 ListTag(aTo);
5507 if (HasView()) {
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());
5524 if (IBsibling) {
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);
5543 if (0 != mState) {
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");
5558 if (mContent) {
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();
5564 if (pseudoTag) {
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());
5573 aTo += "]";
5576 void
5577 nsIFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
5579 nsCString str;
5580 ListGeneric(str, aPrefix, aFlags);
5581 fprintf_stderr(out, "%s\n", str.get());
5584 nsresult
5585 nsFrame::GetFrameName(nsAString& aResult) const
5587 return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult);
5590 nsresult
5591 nsFrame::MakeFrameName(const nsAString& aType, nsAString& aResult) const
5593 aResult = aType;
5594 if (mContent && !mContent->IsNodeOfType(nsINode::eTEXT)) {
5595 nsAutoString buf;
5596 mContent->Tag()->ToString(buf);
5597 if (GetType() == nsGkAtoms::subDocumentFrame) {
5598 nsAutoString src;
5599 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
5600 buf.AppendLiteral(" src=");
5601 buf.Append(src);
5603 aResult.Append('(');
5604 aResult.Append(buf);
5605 aResult.Append(')');
5607 char buf[40];
5608 PR_snprintf(buf, sizeof(buf), "(%d)", ContentIndexInContainer(this));
5609 AppendASCIItoUTF16(buf, aResult);
5610 return NS_OK;
5613 void
5614 nsIFrame::DumpFrameTree()
5616 RootFrameList(PresContext(), stderr);
5619 void
5620 nsIFrame::DumpFrameTreeLimited()
5622 List(stderr);
5625 void
5626 nsIFrame::RootFrameList(nsPresContext* aPresContext, FILE* out, const char* aPrefix)
5628 if (!aPresContext || !out)
5629 return;
5631 nsIPresShell *shell = aPresContext->GetPresShell();
5632 if (shell) {
5633 nsIFrame* frame = shell->FrameManager()->GetRootFrame();
5634 if(frame) {
5635 frame->List(out, aPrefix);
5639 #endif
5641 #ifdef DEBUG
5642 nsFrameState
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;
5659 void
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);
5665 if (ch == '<') {
5666 nsAutoString tmp(NS_LITERAL_STRING("&lt;"));
5667 aString.Cut(i, 1);
5668 aString.Insert(tmp, i);
5669 len += 3;
5670 i += 3;
5672 else if (ch == '>') {
5673 nsAutoString tmp(NS_LITERAL_STRING("&gt;"));
5674 aString.Cut(i, 1);
5675 aString.Insert(tmp, i);
5676 len += 3;
5677 i += 3;
5679 else if (ch == '\"') {
5680 nsAutoString tmp(NS_LITERAL_STRING("&quot;"));
5681 aString.Cut(i, 1);
5682 aString.Insert(tmp, i);
5683 len += 5;
5684 i += 5;
5688 #endif
5690 bool
5691 nsIFrame::IsVisibleForPainting(nsDisplayListBuilder* aBuilder) {
5692 if (!StyleVisibility()->IsVisible())
5693 return false;
5694 nsISelection* sel = aBuilder->GetBoundingSelection();
5695 return !sel || IsVisibleInSelection(sel);
5698 bool
5699 nsIFrame::IsVisibleForPainting() {
5700 if (!StyleVisibility()->IsVisible())
5701 return false;
5703 nsPresContext* pc = PresContext();
5704 if (!pc->IsRenderingOnlySelection())
5705 return true;
5707 nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(pc->PresShell()));
5708 if (selcon) {
5709 nsCOMPtr<nsISelection> sel;
5710 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
5711 getter_AddRefs(sel));
5712 if (sel)
5713 return IsVisibleInSelection(sel);
5715 return true;
5718 bool
5719 nsIFrame::IsVisibleInSelection(nsDisplayListBuilder* aBuilder) {
5720 nsISelection* sel = aBuilder->GetBoundingSelection();
5721 return !sel || IsVisibleInSelection(sel);
5724 bool
5725 nsIFrame::IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder) {
5726 if (!StyleVisibility()->IsVisibleOrCollapsed())
5727 return false;
5728 nsISelection* sel = aBuilder->GetBoundingSelection();
5729 return !sel || IsVisibleInSelection(sel);
5732 bool
5733 nsIFrame::IsVisibleInSelection(nsISelection* aSelection)
5735 if (!GetContent() || !GetContent()->IsSelectionDescendant()) {
5736 return false;
5739 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
5740 bool vis;
5741 nsresult rv = aSelection->ContainsNode(node, true, &vis);
5742 return NS_FAILED(rv) || vis;
5745 /* virtual */ bool
5746 nsFrame::IsEmpty()
5748 return false;
5751 bool
5752 nsIFrame::CachedIsEmpty()
5754 NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY),
5755 "Must only be called on reflowed lines");
5756 return IsEmpty();
5759 /* virtual */ bool
5760 nsFrame::IsSelfEmpty()
5762 return false;
5765 nsresult
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);
5774 if (tcf) {
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());
5788 return fs.forget();
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);
5797 if (tcf) {
5798 return tcf->GetOwnedFrameSelection();
5800 frame = frame->GetParent();
5803 return PresContext()->PresShell()->ConstFrameSelection();
5806 #ifdef DEBUG
5807 nsresult
5808 nsFrame::DumpRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
5810 IndentBy(out, aIndent);
5811 fprintf(out, "<frame va=\"%p\" type=\"", (void*)this);
5812 nsAutoString name;
5813 GetFrameName(name);
5814 XMLQuote(name);
5815 fputs(NS_LossyConvertUTF16toASCII(name).get(), out);
5816 fprintf(out, "\" state=\"%016llx\" parent=\"%p\">\n",
5817 (unsigned long long)GetDebugStateBits(), (void*)GetParent());
5819 aIndent++;
5820 DumpBaseRegressionData(aPresContext, out, aIndent);
5821 aIndent--;
5823 IndentBy(out, aIndent);
5824 fprintf(out, "</frame>\n");
5826 return NS_OK;
5829 void
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());
5837 if (HasView()) {
5838 IndentBy(out, aIndent);
5839 fprintf(out, "<view va=\"%p\">\n", (void*)GetView());
5840 aIndent++;
5841 // XXX add in code to dump out view state too...
5842 aIndent--;
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()));
5858 else {
5859 fprintf(out, "<child-list>\n");
5861 aIndent++;
5862 nsFrameList::Enumerator childFrames(lists.CurrentList());
5863 for (; !childFrames.AtEnd(); childFrames.Next()) {
5864 nsIFrame* kid = childFrames.get();
5865 kid->DumpRegressionData(aPresContext, out, aIndent);
5867 aIndent--;
5868 IndentBy(out, aIndent);
5869 fprintf(out, "</child-list>\n");
5872 #endif
5874 bool
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());
5883 nsresult
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();
5889 if (mContent)
5891 nsIContent* newContent = mContent->GetParent();
5892 if (newContent){
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();
5902 *outPoint = pt;
5903 return NS_OK;
5906 nsresult
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();
5919 if (nextFlow)
5920 return nextFlow->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
5922 *outChildFrame = this;
5923 return NS_OK;
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).
5934 nsresult
5935 nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
5936 nsPeekOffsetStruct *aPos,
5937 nsIFrame *aBlockFrame,
5938 int32_t aLineStart,
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;
5948 aPos->mAttach =
5949 aPos->mDirection == eDirNext ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
5951 nsAutoLineIterator it = aBlockFrame->GetLineIterator();
5952 if (!it)
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
5960 else
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;
5972 nsRect rect;
5973 bool isBeforeFirstFrame, isAfterLastFrame;
5974 bool found = false;
5976 nsresult result = NS_OK;
5977 while (!found)
5979 if (aPos->mDirection == eDirPrevious)
5980 searchingLine --;
5981 else
5982 searchingLine ++;
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;
5989 uint32_t lineFlags;
5990 result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount,
5991 rect, &lineFlags);
5992 if (!lineFrameCount)
5993 continue;
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;
6010 else{
6011 nearStoppingFrame = lastFrame;
6012 farStoppingFrame = firstFrame;
6014 nsPoint offset;
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))
6020 continue;
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();
6027 if (newIt)
6029 aPos->mResultFrame = resultFrame;
6030 return NS_OK;
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,
6038 ePostOrder,
6039 false, // aVisual
6040 aPos->mScrollViewStop,
6041 false // aFollowOOFs
6043 if (NS_FAILED(result))
6044 return result;
6046 nsIFrame *storeOldResultFrame = resultFrame;
6047 while ( !found ){
6048 nsPoint point;
6049 point.x = aPos->mDesiredX;
6051 nsRect tempRect = resultFrame->GetRect();
6052 nsPoint offset;
6053 nsView * view; //used for call of get offset from view
6054 resultFrame->GetOffsetFromView(offset, &view);
6055 if (!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();
6063 if (!shell)
6064 return NS_ERROR_FAILURE;
6065 int16_t isEditor = shell->GetSelectionFlags();
6066 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
6067 if ( isEditor )
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();
6074 if (content)
6076 nsIContent* parent = content->GetParent();
6077 if (parent)
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())
6098 nsView* view;
6099 nsPoint offset;
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)
6108 bool selectable;
6109 resultFrame->IsSelectable(&selectable, nullptr);
6110 if (selectable)
6112 found = true;
6113 break;
6118 if (aPos->mDirection == eDirPrevious && (resultFrame == farStoppingFrame))
6119 break;
6120 if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame))
6121 break;
6122 //always try previous on THAT line if that fails go the other way
6123 frameTraversal->Prev();
6124 resultFrame = frameTraversal->CurrentItem();
6125 if (!resultFrame)
6126 return NS_ERROR_FAILURE;
6129 if (!found){
6130 resultFrame = storeOldResultFrame;
6132 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
6133 aPresContext, resultFrame,
6134 eLeaf,
6135 false, // aVisual
6136 aPos->mScrollViewStop,
6137 false // aFollowOOFs
6140 while ( !found ){
6141 nsPoint point(aPos->mDesiredX, 0);
6142 nsView* view;
6143 nsPoint offset;
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)
6152 bool selectable;
6153 resultFrame->IsSelectable(&selectable, nullptr);
6154 if (selectable)
6156 found = true;
6157 if (resultFrame == farStoppingFrame)
6158 aPos->mAttach = CARET_ASSOCIATE_BEFORE;
6159 else
6160 aPos->mAttach = CARET_ASSOCIATE_AFTER;
6161 break;
6164 if (aPos->mDirection == eDirPrevious && (resultFrame == nearStoppingFrame))
6165 break;
6166 if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame))
6167 break;
6168 //previous didnt work now we try "next"
6169 frameTraversal->Next();
6170 nsIFrame *tempFrame = frameTraversal->CurrentItem();
6171 if (!tempFrame)
6172 break;
6173 resultFrame = tempFrame;
6175 aPos->mResultFrame = resultFrame;
6177 else {
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);
6188 return NS_OK;
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;
6200 return result;
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;
6210 result.mOffset = 0;
6212 if (aFrame->IsGeneratedContentFrame())
6213 return result;
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);
6218 if (fcf)
6219 return result;
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
6225 // looking for.
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);
6238 return result;
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);
6247 return result;
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();
6264 return result;
6267 nsresult
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;
6286 break;
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;
6296 break;
6298 frame = parent;
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;
6311 break;
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;
6321 break;
6323 frame = parent;
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();
6332 return NS_OK;
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);
6343 nsresult
6344 nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
6346 if (!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);
6373 else
6374 peekSearchState = current->PeekOffsetCharacter(movingInFrameDirection, &offset,
6375 aPos->mAmount == eSelectCluster);
6377 movedOverNonSelectableText |= (peekSearchState == CONTINUE_UNSELECTABLE);
6379 if (peekSearchState != FOUND) {
6380 result =
6381 current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual,
6382 aPos->mJumpLines, aPos->mScrollViewStop,
6383 &current, &offset, &jumpedLine);
6384 if (NS_FAILED(result))
6385 return 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.
6389 if (jumpedLine)
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)
6397 int32_t start, end;
6398 current->GetOffsets(start, end);
6399 offset = aPos->mDirection == eDirNext ? 0 : end - start;
6403 // Set outputs
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;
6418 break;
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;
6427 } else {
6428 aPos->mWordMovementType = eEndWord;
6430 // Intentionally fall through the eSelectWord case.
6431 case eSelectWord:
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));
6444 else {
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;
6462 bool done = false;
6463 while (!done) {
6464 bool movingInFrameDirection =
6465 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
6467 done = current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace,
6468 aPos->mIsKeyboardSelect, &offset, &state) == FOUND;
6470 if (!done) {
6471 nsIFrame* nextFrame;
6472 int32_t nextFrameOffset;
6473 bool jumpedLine;
6474 result =
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)) {
6482 done = true;
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;
6489 } else {
6490 if (jumpedLine) {
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();
6502 // Set outputs
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;
6508 break;
6510 case eSelectLine :
6512 nsAutoLineIterator iter;
6513 nsIFrame *blockFrame = this;
6515 while (NS_FAILED(result)){
6516 int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame);
6517 if (thisLine < 0)
6518 return NS_ERROR_FAILURE;
6519 iter = blockFrame->GetLineIterator();
6520 NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?");
6521 result = NS_OK;
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;
6529 do {
6530 result = nsFrame::GetNextPrevLineFromeBlockFrame(PresContext(),
6531 aPos,
6532 blockFrame,
6533 thisLine,
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)
6540 thisLine--;
6541 else
6542 thisLine++;
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();
6566 if (iter)
6568 aPos->mResultFrame = frame;
6569 searchTableBool = true;
6570 result = NS_OK;
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
6587 else
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;
6594 else
6596 result = NS_OK;//THIS is to mean that everything is ok to the containing while loop
6597 break;
6600 } while (!doneLooping);
6602 return result;
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);
6614 if (thisLine < 0)
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;
6621 nsRect usedRect;
6622 uint32_t lineFlags;
6623 nsIFrame* baseFrame = nullptr;
6624 bool endOfLine = (eSelectEndLine == aPos->mAmount);
6626 if (aPos->mVisual && PresContext()->BidiEnabled()) {
6627 bool lineIsRTL = it->GetDirection();
6628 bool isReordered;
6629 nsIFrame *lastFrame;
6630 result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
6631 baseFrame = endOfLine ? lastFrame : firstFrame;
6632 if (baseFrame) {
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;
6639 } else {
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()) {
6646 baseFrame = frame;
6647 if (!endOfLine)
6648 break;
6652 if (!baseFrame)
6653 return NS_ERROR_FAILURE;
6654 FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame,
6655 endOfLine, 0);
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;
6667 if (!range.content)
6668 return NS_ERROR_FAILURE;
6669 return NS_OK;
6672 default:
6674 NS_ASSERTION(false, "Invalid amount");
6675 return NS_ERROR_FAILURE;
6678 return NS_OK;
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.
6686 return FOUND;
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)
6697 startOffset = 1;
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;
6702 return FOUND;
6704 return CONTINUE;
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)
6716 startOffset = 1;
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))
6724 return FOUND;
6725 } else {
6726 // This is not a punctuation boundary.
6727 if (aWordSelectEatSpace && aState->mSawBeforeType)
6728 return FOUND;
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();
6739 return CONTINUE;
6742 bool
6743 nsFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
6744 bool aForward,
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
6752 return true;
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
6761 return true;
6763 bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
6764 if (!afterPunct) {
6765 // keyboard caret movement only stops after punctuation (in content order)
6766 return false;
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;
6773 nsresult
6774 nsFrame::CheckVisibility(nsPresContext* , int32_t , int32_t , bool , bool *, bool *)
6776 return NS_ERROR_NOT_IMPLEMENTED;
6780 int32_t
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);
6800 if (!thisBlock)
6801 return -1;
6803 blockFrame = thisBlock->GetParent();
6804 result = NS_OK;
6805 if (blockFrame) {
6806 if (aLockScroll && blockFrame->GetType() == nsGkAtoms::scrollFrame)
6807 return -1;
6808 it = blockFrame->GetLineIterator();
6809 if (!it)
6810 result = NS_ERROR_FAILURE;
6813 if (!blockFrame || !it)
6814 return -1;
6816 if (aContainingBlock)
6817 *aContainingBlock = blockFrame;
6818 return it->FindLineContaining(thisBlock);
6821 nsresult
6822 nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual,
6823 bool aJumpLines, bool aScrollViewStop,
6824 nsIFrame** aOutFrame, int32_t* aOutOffset, bool* aOutJumpedLine)
6826 nsresult result;
6828 if (!aOutFrame || !aOutOffset || !aOutJumpedLine)
6829 return NS_ERROR_NULL_POINTER;
6831 nsPresContext* presContext = PresContext();
6832 *aOutFrame = nullptr;
6833 *aOutOffset = 0;
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);
6843 if (thisLine < 0)
6844 return NS_ERROR_FAILURE;
6846 nsAutoLineIterator it = blockFrame->GetLineIterator();
6847 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
6849 bool atLineEdge;
6850 nsIFrame *firstFrame;
6851 nsIFrame *lastFrame;
6852 if (aVisual && presContext->BidiEnabled()) {
6853 bool lineIsRTL = it->GetDirection();
6854 bool isReordered;
6855 result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
6856 nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
6857 if (*framePtr) {
6858 nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr);
6859 if ((((embeddingLevel & 1) && lineIsRTL) || (!(embeddingLevel & 1) && !lineIsRTL)) ==
6860 (aDirection == eDirPrevious)) {
6861 nsFrame::GetFirstLeaf(presContext, framePtr);
6862 } else {
6863 nsFrame::GetLastLeaf(presContext, framePtr);
6865 atLineEdge = *framePtr == traversedFrame;
6866 } else {
6867 atLineEdge = true;
6869 } else {
6870 nsRect nonUsedRect;
6871 int32_t lineFrameCount;
6872 uint32_t lineFlags;
6873 result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
6874 &lineFlags);
6875 if (NS_FAILED(result))
6876 return 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;
6895 if (atLineEdge) {
6896 *aOutJumpedLine = true;
6897 if (!aJumpLines)
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,
6904 eLeaf,
6905 aVisual && presContext->BidiEnabled(),
6906 aScrollViewStop,
6907 true // aFollowOOFs
6909 if (NS_FAILED(result))
6910 return result;
6912 if (aDirection == eDirNext)
6913 frameTraversal->Next();
6914 else
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;
6925 if (aVisual) {
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;
6932 return NS_OK;
6935 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const
6937 nsPoint offset(0,0);
6938 for (const nsIFrame *f = this; f; f = f->GetParent()) {
6939 if (f->HasView()) {
6940 if (aOffset)
6941 *aOffset = offset;
6942 return f->GetView();
6944 offset += f->GetPosition();
6947 NS_NOTREACHED("No view on any parent? How did that happen?");
6948 return nullptr;
6952 /* virtual */ void
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
6961 a11y::AccType
6962 nsFrame::AccessibleType()
6964 return a11y::eNoType;
6966 #endif
6968 NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty,
6969 nsIFrame::DestroyOverflowAreas)
6971 bool
6972 nsIFrame::ClearOverflowRects()
6974 if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) {
6975 return false;
6977 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
6978 Properties().Delete(OverflowAreasProperty());
6980 mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
6981 return true;
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
6988 nsOverflowAreas*
6989 nsIFrame::GetOverflowAreasProperty()
6991 FrameProperties props = Properties();
6992 nsOverflowAreas *overflow =
6993 static_cast<nsOverflowAreas*>(props.Get(OverflowAreasProperty()));
6995 if (overflow) {
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);
7003 return 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.
7009 bool
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
7019 // have a property.
7020 return changed;
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
7046 // no-overflow.
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;
7053 } else {
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;
7062 return changed;
7066 inline bool
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).
7078 static nsRect
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.
7088 nsRect u;
7089 bool doTransform = aApplyTransform && aFrame->IsTransformed();
7090 if (doTransform) {
7091 u = nsDisplayTransform::TransformRect(bounds, aFrame,
7092 nsPoint(0, 0), &bounds);
7093 } else {
7094 u = 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
7099 // anyway.
7100 if (aOverflowOverride) {
7101 if (!doTransform &&
7102 bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) &&
7103 bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
7104 return u;
7106 } else {
7107 if (!doTransform &&
7108 bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) &&
7109 bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) {
7110 return u;
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) {
7118 return u;
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())) {
7131 continue;
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
7145 // overflow areas.
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);
7168 return u;
7171 static void
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) {
7178 return;
7181 nscoord width;
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) {
7185 return;
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;
7195 do {
7196 nsIAtom *pseudoType = frameForArea->StyleContext()->GetPseudo();
7197 if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
7198 pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
7199 break;
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.
7212 nsRect innerRect;
7213 if (frameForArea == aFrame) {
7214 innerRect = UnionBorderBoxes(aFrame, false, &aNewSize, &aOverflowAreas);
7215 } else {
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) {
7227 break;
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);
7254 } else {
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));
7267 bool
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()));
7282 if (!initial) {
7283 Properties().Set(nsIFrame::InitialOverflowProperty(),
7284 new nsOverflowAreas(aOverflowAreas));
7285 } else if (initial != &aOverflowAreas) {
7286 *initial = aOverflowAreas;
7288 } else {
7289 Properties().Delete(nsIFrame::InitialOverflowProperty());
7291 #ifdef DEBUG
7292 Properties().Set(nsIFrame::DebugInitialOverflowPropertyApplied(), nullptr);
7293 #endif
7294 } else {
7295 #ifdef DEBUG
7296 Properties().Delete(nsIFrame::DebugInitialOverflowPropertyApplied());
7297 #endif
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
7333 // with zero width.
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)) {
7347 nsRect r(bounds);
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);
7377 if (hasTransform) {
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);
7395 } else {
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);
7406 } else {
7407 anyOverflowChanged = ClearOverflowRects();
7410 if (anyOverflowChanged) {
7411 nsSVGEffects::InvalidateDirectRenderingObservers(this);
7413 return anyOverflowChanged;
7416 void
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();
7421 if (aBounds) {
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());
7436 if (overflow) {
7437 nsOverflowAreas overflowCopy = *overflow;
7438 child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
7439 } else {
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.
7456 SetSize(oldSize);
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.
7465 static void
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();
7470 if (aBounds) {
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());
7487 if (overflow) {
7488 nsOverflowAreas overflowCopy = *overflow;
7489 child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
7490 } else {
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.
7503 if (!aBounds) {
7504 nsOverflowAreas* overflow =
7505 static_cast<nsOverflowAreas*>(aFrame->Properties().Get(nsIFrame::InitialOverflowProperty()));
7506 nsRect bounds(nsPoint(0, 0), aFrame->GetSize());
7507 if (overflow) {
7508 nsOverflowAreas overflowCopy = *overflow;
7509 overflowCopy.UnionAllWith(bounds);
7510 aFrame->FinishAndStoreOverflow(overflowCopy, bounds.Size());
7511 } else {
7512 nsOverflowAreas boundsOverflow;
7513 boundsOverflow.SetAllTo(bounds);
7514 aFrame->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
7519 void
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
7528 // the time.
7529 if (!Preserves3D()) {
7530 RecomputePreserve3DChildrenOverflow(this, &aBounds);
7533 nsRect childVisual;
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);
7548 } else {
7549 childVisual =
7550 childVisual.Union(nsDisplayTransform::TransformRect(visual,
7551 this, nsPoint(0,0), &aBounds));
7552 childScrollable =
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);
7563 uint32_t
7564 nsIFrame::GetDepthInFrameTree() const
7566 uint32_t result = 0;
7567 for (nsContainerFrame* ancestor = GetParent(); ancestor;
7568 ancestor = ancestor->GetParent()) {
7569 result++;
7571 return result;
7574 void
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
7587 * style context.
7589 * If aFrame is not an anonymous block, null is returned.
7591 static nsIFrame*
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
7602 return nullptr;
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
7611 * property.
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.
7629 static nsIFrame*
7630 GetCorrectedParent(const nsIFrame* aFrame)
7632 nsIFrame *parent = aFrame->GetParent();
7633 if (!parent) {
7634 return nullptr;
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);
7647 /* static */
7648 nsIFrame*
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;
7669 do {
7670 if (parent->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
7671 nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
7673 if (sibling) {
7674 // |parent| was a block in an {ib} split; use the inline as
7675 // |the style parent.
7676 parent = sibling;
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
7686 // ancestor.
7687 aChildPseudo != nsGkAtoms::placeholderFrame)) {
7688 return parent;
7691 parent = parent->GetParent();
7692 } while (parent);
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");
7706 return nullptr;
7709 nsIFrame*
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.
7715 return nullptr;
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());
7742 if (!placeholder) {
7743 NS_NOTREACHED("no placeholder frame for out-of-flow frame");
7744 return GetCorrectedParent(this);
7746 return placeholder->GetParentStyleContextFrame();
7749 void
7750 nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame)
7752 if (!aFrame || !*aFrame)
7753 return;
7754 nsIFrame *child = *aFrame;
7755 //if we are a block frame then go for the last line of 'this'
7756 while (1){
7757 child = child->GetFirstPrincipalChild();
7758 if (!child)
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;
7768 *aFrame = child;
7772 void
7773 nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame)
7775 if (!aFrame || !*aFrame)
7776 return;
7777 nsIFrame *child = *aFrame;
7778 while (1){
7779 child = child->GetFirstPrincipalChild();
7780 if (!child)
7781 return;//nothing to do
7782 *aFrame = child;
7786 /* virtual */ bool
7787 nsIFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse)
7789 int32_t tabIndex = -1;
7790 if (aTabIndex) {
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
7800 tabIndex = 0;
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);
7817 if (scrollFrame &&
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
7821 isFocusable = true;
7822 tabIndex = 0;
7827 if (aTabIndex) {
7828 *aTabIndex = tabIndex;
7830 return isFocusable;
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.
7837 bool
7838 nsIFrame::HasSignificantTerminalNewline() const
7840 return false;
7843 static uint8_t
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;
7868 default:
7869 NS_NOTREACHED("unexpected aDominantBaseline value");
7870 return NS_STYLE_VERTICAL_ALIGN_BASELINE;
7874 uint8_t
7875 nsIFrame::VerticalAlignEnum() const
7877 if (IsSVGText()) {
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) {
7883 break;
7886 return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline);
7889 const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign;
7890 if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
7891 return verticalAlign.GetIntValue();
7894 return eInvalidVerticalAlign;
7897 /* static */
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) {
7908 uint32_t status;
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;
7916 break;
7921 NS_IMETHODIMP
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();
7943 if (rendContext) {
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))
7950 return NS_OK;
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();
7969 // do the nasty.
7970 const WritingMode wm = aState.OuterReflowState() ?
7971 aState.OuterReflowState()->GetWritingMode() : GetWritingMode();
7972 nsHTMLReflowMetrics desiredSize(wm);
7973 BoxReflow(aState, presContext, desiredSize, rendContext,
7974 rect.x, rect.y,
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();
7981 if (lines)
7983 metrics->mBlockMinSize.height = 0;
7984 int count = 0;
7985 nsIFrame* firstFrame = nullptr;
7986 int32_t framesOnLine;
7987 nsRect lineBounds;
7988 uint32_t lineFlags;
7990 do {
7991 lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds, &lineFlags);
7993 if (lineBounds.height > metrics->mBlockMinSize.height)
7994 metrics->mBlockMinSize.height = lineBounds.height;
7996 count++;
7997 } while(firstFrame);
7998 } else {
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);
8009 } else {
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);
8019 #endif
8022 return NS_OK;
8025 /* virtual */ nsILineIterator*
8026 nsFrame::GetLineIterator()
8028 return nullptr;
8031 nsSize
8032 nsFrame::GetPrefSize(nsBoxLayoutState& aState)
8034 nsSize size(0,0);
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;
8043 if (IsCollapsed())
8044 return size;
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.
8057 if (!widthSet)
8058 size.width = blockSize.width;
8059 if (!heightSet)
8060 size.height = blockSize.height;
8063 metrics->mPrefSize = size;
8064 return size;
8067 nsSize
8068 nsFrame::GetMinSize(nsBoxLayoutState& aState)
8070 nsSize size(0,0);
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;
8076 return size;
8079 if (IsCollapsed())
8080 return size;
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;
8092 if (!widthSet)
8093 size.width = blockSize.width;
8094 if (!heightSet)
8095 size.height = blockSize.height;
8098 metrics->mMinSize = size;
8099 return size;
8102 nsSize
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;
8111 return size;
8114 if (IsCollapsed())
8115 return size;
8117 size = nsBox::GetMaxSize(aState);
8118 metrics->mMaxSize = size;
8120 return size;
8123 nscoord
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;
8135 nscoord
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;
8144 } else {
8145 // Refresh our caches with new sizes.
8146 RefreshSizeCache(aState);
8147 metrics->mAscent = metrics->mBlockAscent;
8150 return metrics->mAscent;
8153 nsresult
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);
8166 if (rendContext) {
8168 BoxReflow(aState, presContext, desiredSize, rendContext,
8169 ourRect.x, ourRect.y, ourRect.width, ourRect.height);
8171 if (IsCollapsed()) {
8172 SetSize(nsSize(0, 0));
8173 } else {
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)) {
8181 #ifdef DEBUG_GROW
8182 DumpBox(stdout);
8183 printf(" GREW from (%d,%d) -> (%d,%d)\n",
8184 ourSize.ISize(outerWM), ourSize.BSize(outerWM),
8185 desiredSize.ISize(outerWM), desiredSize.BSize(outerWM));
8186 #endif
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);
8230 SyncLayout(aState);
8232 return NS_OK;
8235 void
8236 nsFrame::BoxReflow(nsBoxLayoutState& aState,
8237 nsPresContext* aPresContext,
8238 nsHTMLReflowMetrics& aDesiredSize,
8239 nsRenderingContext* aRenderingContext,
8240 nscoord aX,
8241 nscoord aY,
8242 nscoord aWidth,
8243 nscoord aHeight,
8244 bool aMoveFrame)
8246 DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
8248 #ifdef DEBUG_REFLOW
8249 nsAdaptorAddIndents();
8250 printf("Reflowing: ");
8251 nsFrame::ListTag(stdout, mFrame);
8252 printf("\n");
8253 gIndent2++;
8254 #endif
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.
8264 if (!needsReflow) {
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));
8274 } else {
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;
8282 else
8283 needsReflow = true;
8286 } else {
8287 // if the width or height are intrinsic alway reflow because
8288 // we don't know what it should be.
8289 needsReflow = true;
8293 // ok now reflow the child into the spacers calculated space
8294 if (needsReflow) {
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);
8303 GetMargin(margin);
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();
8314 nsHTMLReflowState
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;
8346 } else {
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
8367 // border-box
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);
8386 } else {
8387 reflowState.SetComputedHeight(
8388 ComputeSize(aRenderingContext, wm,
8389 logicalSize,
8390 logicalSize.ISize(wm),
8391 reflowState.ComputedLogicalMargin().Size(wm),
8392 reflowState.ComputedLogicalBorderPadding().Size(wm) -
8393 reflowState.ComputedLogicalPadding().Size(wm),
8394 reflowState.ComputedLogicalPadding().Size(wm),
8395 false).Height(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;
8418 #ifdef DEBUG_REFLOW
8419 nsAdaptorAddIndents();
8420 printf("Size=(%d,%d)\n",reflowState.ComputedWidth(),
8421 reflowState.ComputedHeight());
8422 nsAdaptorAddIndents();
8423 nsAdaptorPrintReason(reflowState);
8424 printf("\n");
8425 #endif
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;
8441 } else {
8442 if (aDesiredSize.BlockStartAscent() ==
8443 nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
8444 if (!nsLayoutUtils::GetFirstLineBaseline(wm, this, &metrics->mAscent))
8445 metrics->mAscent = GetLogicalBaseline(wm);
8446 } else
8447 metrics->mAscent = aDesiredSize.BlockStartAscent();
8450 } else {
8451 aDesiredSize.SetBlockStartAscent(metrics->mBlockAscent);
8454 #ifdef DEBUG_REFLOW
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");
8467 #endif
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();
8478 #ifdef DEBUG_REFLOW
8479 gIndent2--;
8480 #endif
8483 nsBoxLayoutMetrics*
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");
8489 return metrics;
8492 /* static */ void
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());
8509 /* static */ void
8510 nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame)
8512 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
8513 nsLayoutUtils::IsPopup(aFrame)) {
8514 return;
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());
8531 void
8532 nsIFrame::SetParent(nsContainerFrame* aParent)
8534 bool wasBoxWrapped = ::IsBoxWrapped(this);
8535 mParent = aParent;
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);
8560 } else {
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)) {
8568 InvalidateFrame();
8572 void
8573 nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
8574 nsDisplayList* aList)
8576 if (GetContent() &&
8577 GetContent()->IsXUL() &&
8578 GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
8579 aList->AppendNewToTop(new (aBuilder)
8580 nsDisplayOwnLayer(aBuilder, this, aList));
8584 bool
8585 nsIFrame::IsSelected() const
8587 return (GetContent() && GetContent()->IsSelectionDescendant()) ?
8588 IsFrameSelected() : false;
8591 void
8592 nsIFrame::DestroySurface(void* aPropertyValue)
8594 static_cast<gfxASurface*>(aPropertyValue)->Release();
8597 void
8598 nsIFrame::DestroyDT(void* aPropertyValue)
8600 static_cast<mozilla::gfx::DrawTarget*>(aPropertyValue)->Release();
8603 void
8604 nsIFrame::DestroyRegion(void* aPropertyValue)
8606 delete static_cast<nsRegion*>(aPropertyValue);
8609 bool
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);
8620 Element*
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);
8631 if (frame) {
8632 nsIContent* content = frame->GetContent();
8633 if (content->IsElement()) {
8634 return content->AsElement();
8638 return nullptr;
8641 nsIFrame::ContentOffsets::ContentOffsets()
8645 nsIFrame::ContentOffsets::ContentOffsets(const ContentOffsets& rhs)
8646 : content(rhs.content),
8647 offset(rhs.offset),
8648 secondaryOffset(rhs.secondaryOffset),
8649 associate(rhs.associate)
8653 nsIFrame::ContentOffsets::~ContentOffsets()
8657 nsIFrame::CaretPosition::CaretPosition()
8658 : mContentOffset(0)
8662 nsIFrame::CaretPosition::~CaretPosition()
8666 // Box layout debugging
8667 #ifdef DEBUG_REFLOW
8668 int32_t gIndent2 = 0;
8670 void
8671 nsAdaptorAddIndents()
8673 for(int32_t i=0; i < gIndent2; i++)
8675 printf(" ");
8679 void
8680 nsAdaptorPrintReason(nsHTMLReflowState& aReflowState)
8682 char* reflowReasonString;
8684 switch(aReflowState.reason)
8686 case eReflowReason_Initial:
8687 reflowReasonString = "initial";
8688 break;
8690 case eReflowReason_Resize:
8691 reflowReasonString = "resize";
8692 break;
8693 case eReflowReason_Dirty:
8694 reflowReasonString = "dirty";
8695 break;
8696 case eReflowReason_StyleChange:
8697 reflowReasonString = "stylechange";
8698 break;
8699 case eReflowReason_Incremental:
8701 switch (aReflowState.reflowCommand->Type()) {
8702 case eReflowType_StyleChanged:
8703 reflowReasonString = "incremental (StyleChanged)";
8704 break;
8705 case eReflowType_ReflowDirty:
8706 reflowReasonString = "incremental (ReflowDirty)";
8707 break;
8708 default:
8709 reflowReasonString = "incremental (Unknown)";
8712 break;
8713 default:
8714 reflowReasonString = "unknown";
8715 break;
8718 printf("%s",reflowReasonString);
8721 #endif
8722 #ifdef DEBUG_LAYOUT
8723 void
8724 nsFrame::GetBoxName(nsAutoString& aName)
8726 GetFrameName(aName);
8728 #endif
8730 #ifdef DEBUG
8731 static void
8732 GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize,
8733 char* aResult)
8735 if (aContent) {
8736 PR_snprintf(aResult, aResultSize, "%s@%p",
8737 nsAtomCString(aContent->Tag()).get(), aFrame);
8739 else {
8740 PR_snprintf(aResult, aResultSize, "@%p", aFrame);
8744 void
8745 nsFrame::Trace(const char* aMethod, bool aEnter)
8747 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
8748 char tagbuf[40];
8749 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
8750 PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
8754 void
8755 nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
8757 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
8758 char tagbuf[40];
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" : "");
8767 void
8768 nsFrame::TraceMsg(const char* aFormatString, ...)
8770 if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
8771 // Format arguments into a buffer
8772 char argbuf[200];
8773 va_list ap;
8774 va_start(ap, aFormatString);
8775 PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap);
8776 va_end(ap);
8778 char tagbuf[40];
8779 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
8780 PR_LogPrint("%s: %s", tagbuf, argbuf);
8784 void
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
8794 #ifdef DEBUG
8796 DR_cookie::DR_cookie(nsPresContext* aPresContext,
8797 nsIFrame* aFrame,
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)
8814 : mFrame(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(
8827 nsIFrame* aFrame,
8828 const char* aType,
8829 nscoord& aResult)
8830 : mFrame(aFrame)
8831 , mType(aType)
8832 , mResult(aResult)
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(
8845 nsIFrame* aFrame,
8846 const char* aType,
8847 nsSize& aResult)
8848 : mFrame(aFrame)
8849 , mType(aType)
8850 , mResult(aResult)
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(
8863 nsIFrame* aFrame,
8864 nsHTMLReflowState* aState,
8865 nscoord aCBWidth,
8866 nscoord aCBHeight,
8867 const nsMargin* aMargin,
8868 const nsMargin* aPadding)
8869 : mFrame(aFrame)
8870 , mState(aState)
8872 MOZ_COUNT_CTOR(DR_init_constraints_cookie);
8873 mValue = nsHTMLReflowState::DisplayInitConstraintsEnter(mFrame, mState,
8874 aCBWidth, aCBHeight,
8875 aMargin, aPadding);
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(
8885 nsIFrame* aFrame,
8886 nsCSSOffsetState* aState,
8887 nscoord aHorizontalPercentBasis,
8888 nscoord aVerticalPercentBasis,
8889 const nsMargin* aMargin,
8890 const nsMargin* aPadding)
8891 : mFrame(aFrame)
8892 , mState(aState)
8894 MOZ_COUNT_CTOR(DR_init_offsets_cookie);
8895 mValue = nsCSSOffsetState::DisplayInitOffsetsEnter(mFrame, mState,
8896 aHorizontalPercentBasis,
8897 aVerticalPercentBasis,
8898 aMargin, aPadding);
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(
8908 nsIFrame* aFrame,
8909 nsHTMLReflowState* aState)
8910 : mFrame(aFrame)
8911 , mState(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;
8925 struct DR_Rule;
8927 struct DR_State
8929 DR_State();
8930 ~DR_State();
8931 void Init();
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,
8944 char* aBuf,
8945 size_t aBufSize);
8946 DR_Rule* ParseRule(FILE* aFile);
8947 void ParseRulesFile();
8948 void AddRule(nsTArray<DR_Rule*>& aRules,
8949 DR_Rule& aRule);
8950 bool IsWhiteSpace(int c);
8951 bool GetNumber(char* aBuf,
8952 int32_t& aNumber);
8953 void PrettyUC(nscoord aSize,
8954 char* aBuf);
8955 void PrintMargin(const char* tag, const nsMargin* aMargin);
8956 void DisplayFrameTypeInfo(nsIFrame* aFrame,
8957 int32_t aIndent);
8958 void DeleteTreeNode(DR_FrameTreeNode& aNode);
8960 bool mInited;
8961 bool mActive;
8962 int32_t mCount;
8963 int32_t mAssert;
8964 int32_t mIndent;
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
8975 struct DR_RulePart
8977 explicit DR_RulePart(nsIAtom* aFrameType) : mFrameType(aFrameType), mNext(0) {}
8978 void Destroy();
8980 nsIAtom* mFrameType;
8981 DR_RulePart* mNext;
8984 void DR_RulePart::Destroy()
8986 if (mNext) {
8987 mNext->Destroy();
8989 delete this;
8992 struct DR_Rule
8994 DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) {
8995 MOZ_COUNT_CTOR(DR_Rule);
8997 ~DR_Rule() {
8998 if (mTarget) mTarget->Destroy();
8999 MOZ_COUNT_DTOR(DR_Rule);
9001 void AddPart(nsIAtom* aFrameType);
9003 uint32_t mLength;
9004 DR_RulePart* mTarget;
9005 bool mDisplay;
9008 void DR_Rule::AddPart(nsIAtom* aFrameType)
9010 DR_RulePart* newPart = new DR_RulePart(aFrameType);
9011 newPart->mNext = mTarget;
9012 mTarget = newPart;
9013 mLength++;
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);
9027 nsIAtom* mType;
9028 char mNameAbbrev[16];
9029 char mName[32];
9030 nsTArray<DR_Rule*> mRules;
9031 private:
9032 DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) MOZ_DELETE;
9035 DR_FrameTypeInfo::DR_FrameTypeInfo(nsIAtom* aFrameType,
9036 const char* aFrameNameAbbrev,
9037 const char* aFrameName)
9039 mType = aFrameType;
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);
9051 ~DR_FrameTreeNode()
9053 MOZ_COUNT_DTOR(DR_FrameTreeNode);
9056 nsIFrame* mFrame;
9057 DR_FrameTreeNode* mParent;
9058 bool mDisplay;
9059 uint32_t mIndent;
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");
9074 int32_t num;
9075 if (env) {
9076 if (GetNumber(env, num))
9077 mAssert = num;
9078 else
9079 printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env);
9082 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
9083 if (env) {
9084 if (GetNumber(env, num))
9085 mIndent = num;
9086 else
9087 printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env);
9090 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
9091 if (env) {
9092 if (GetNumber(env, num))
9093 mIndentUndisplayedFrames = num;
9094 else
9095 printf("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", env);
9098 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
9099 if (env) {
9100 if (GetNumber(env, num))
9101 mDisplayPixelErrors = num;
9102 else
9103 printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", env);
9106 InitFrameTypeTable();
9107 ParseRulesFile();
9108 mInited = true;
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,
9126 int32_t& aNumber)
9128 if (sscanf(aBuf, "%d", &aNumber) > 0)
9129 return true;
9130 else
9131 return false;
9134 bool DR_State::IsWhiteSpace(int c) {
9135 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
9138 bool DR_State::GetToken(FILE* aFile,
9139 char* aBuf,
9140 size_t aBufSize)
9142 bool haveToken = false;
9143 aBuf[0] = 0;
9144 // get the 1st non whitespace char
9145 int c = -1;
9146 for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) {
9149 if (c > 0) {
9150 haveToken = true;
9151 aBuf[0] = c;
9152 // get everything up to the next whitespace char
9153 size_t cX;
9154 for (cX = 1; cX + 1 < aBufSize ; cX++) {
9155 c = getc(aFile);
9156 if (c < 0) { // EOF
9157 ungetc(' ', aFile);
9158 break;
9160 else {
9161 if (IsWhiteSpace(c)) {
9162 break;
9164 else {
9165 aBuf[cX] = c;
9169 aBuf[cX] = 0;
9171 return haveToken;
9174 DR_Rule* DR_State::ParseRule(FILE* aFile)
9176 char buf[128];
9177 int32_t doDisplay;
9178 DR_Rule* rule = nullptr;
9179 while (GetToken(aFile, buf, sizeof(buf))) {
9180 if (GetNumber(buf, doDisplay)) {
9181 if (rule) {
9182 rule->mDisplay = !!doDisplay;
9183 break;
9185 else {
9186 printf("unexpected token - %s \n", buf);
9189 else {
9190 if (!rule) {
9191 rule = new DR_Rule;
9193 if (strcmp(buf, "*") == 0) {
9194 rule->AddPart(nullptr);
9196 else {
9197 DR_FrameTypeInfo* info = GetFrameTypeInfo(buf);
9198 if (info) {
9199 rule->AddPart(info->mType);
9201 else {
9202 printf("invalid frame type - %s \n", buf);
9207 return rule;
9210 void DR_State::AddRule(nsTArray<DR_Rule*>& aRules,
9211 DR_Rule& aRule)
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);
9219 return;
9222 aRules.AppendElement(&aRule);
9225 void DR_State::ParseRulesFile()
9227 char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
9228 if (path) {
9229 FILE* inFile = fopen(path, "r");
9230 if (inFile) {
9231 for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) {
9232 if (rule->mTarget) {
9233 nsIAtom* fType = rule->mTarget->mFrameType;
9234 if (fType) {
9235 DR_FrameTypeInfo* info = GetFrameTypeInfo(fType);
9236 if (info) {
9237 AddRule(info->mRules, *rule);
9240 else {
9241 AddRule(mWildRules, *rule);
9243 mActive = true;
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) {
9265 return &info;
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)) {
9278 return &info;
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");
9317 #ifdef MOZ_XUL
9318 AddFrameTypeInfo(nsGkAtoms::XULLabelFrame, "XULLabel", "XULLabel");
9319 AddFrameTypeInfo(nsGkAtoms::boxFrame, "Box", "Box");
9320 AddFrameTypeInfo(nsGkAtoms::sliderFrame, "Slider", "Slider");
9321 AddFrameTypeInfo(nsGkAtoms::popupSetFrame, "PopupSet", "PopupSet");
9322 #endif
9323 AddFrameTypeInfo(nullptr, "unknown", "unknown");
9327 void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame,
9328 int32_t aIndent)
9330 DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->GetType());
9331 if (frameTypeInfo) {
9332 for (int32_t i = 0; i < aIndent; i++) {
9333 printf(" ");
9335 if(!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) {
9336 if (aFrame) {
9337 nsAutoString name;
9338 aFrame->GetFrameName(name);
9339 printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)aFrame);
9341 else {
9342 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
9345 else {
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()) {
9364 return false;
9367 else NS_ASSERTION(false, "program error");
9369 // else wild card match
9371 return true;
9374 void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode)
9376 if (!aNode.mFrame) {
9377 NS_ASSERTION(false, "invalid DR_FrameTreeNode \n");
9378 return;
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;
9391 break;
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;
9400 break;
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;
9411 if (aReflowState) {
9412 const nsHTMLReflowState* parentRS = aReflowState->parentReflowState;
9413 parentFrame = (parentRS) ? parentRS->frame : nullptr;
9414 } else {
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);
9424 if (lastLeaf) {
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) {
9433 ++mIndent;
9436 if (lastLeaf && (lastLeaf == parentNode)) {
9437 mFrameTreeLeaves.RemoveElementAt(mFrameTreeLeaves.Length() - 1);
9439 mFrameTreeLeaves.AppendElement(newNode);
9440 mCount++;
9442 return newNode;
9445 void DR_State::PrettyUC(nscoord aSize,
9446 char* aBuf)
9448 if (NS_UNCONSTRAINEDSIZE == aSize) {
9449 strcpy(aBuf, "UC");
9451 else {
9452 if ((nscoord)0xdeadbeefU == aSize)
9454 strcpy(aBuf, "deadbeef");
9456 else {
9457 sprintf(aBuf, "%d", aSize);
9462 void DR_State::PrintMargin(const char *tag, const nsMargin* aMargin)
9464 if (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);
9471 } else {
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) {
9486 --mIndent;
9488 // delete the tree node
9489 delete &aNode;
9492 static void
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,
9504 nsIFrame* aFrame,
9505 const nsHTMLReflowState& aReflowState,
9506 DR_FrameTreeNode& aTreeNode,
9507 bool aChanged)
9509 if (aTreeNode.mDisplay) {
9510 DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent);
9512 char width[16];
9513 char height[16];
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)
9524 printf("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();
9539 if (inFlow) {
9540 printf("pif=%p ", (void*)inFlow);
9542 inFlow = aFrame->GetNextInFlow();
9543 if (inFlow) {
9544 printf("nif=%p ", (void*)inFlow);
9546 if (aChanged)
9547 printf("CHANGED \n");
9548 else
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,
9561 nsIFrame* aFrame,
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);
9570 if (treeNode) {
9571 DisplayReflowEnterPrint(aPresContext, aFrame, aReflowState, *treeNode, false);
9573 return treeNode;
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);
9586 printf("Layout\n");
9588 return treeNode;
9591 void* nsFrame::DisplayIntrinsicISizeEnter(nsIFrame* aFrame,
9592 const char* aType)
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);
9604 return treeNode;
9607 void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame,
9608 const char* aType)
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);
9620 return treeNode;
9623 void nsFrame::DisplayReflowExit(nsPresContext* aPresContext,
9624 nsIFrame* aFrame,
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);
9638 char width[16];
9639 char height[16];
9640 char x[16];
9641 char y[16];
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);
9676 printf("\n");
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,
9704 const char* aType,
9705 nscoord aResult,
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);
9716 char width[16];
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,
9724 const char* aType,
9725 nsSize aResult,
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);
9737 char width[16];
9738 char height[16];
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);
9746 /* static */ void
9747 nsFrame::DisplayReflowStartup()
9749 DR_state = new DR_State();
9752 /* static */ void
9753 nsFrame::DisplayReflowShutdown()
9755 delete DR_state;
9756 DR_state = nullptr;
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);
9767 /* static */ void*
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);
9788 char width[16];
9789 char height[16];
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);
9801 putchar('\n');
9803 return treeNode;
9806 /* static */ void
9807 nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame* aFrame,
9808 nsHTMLReflowState* aState,
9809 void* aValue)
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());
9830 putchar('\n');
9832 DR_state->DeleteTreeNode(*treeNode);
9836 /* static */ void*
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);
9863 putchar('\n');
9865 return treeNode;
9868 /* static */ void
9869 nsCSSOffsetState::DisplayInitOffsetsExit(nsIFrame* aFrame,
9870 nsCSSOffsetState* aState,
9871 void* aValue)
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());
9886 putchar('\n');
9888 DR_state->DeleteTreeNode(*treeNode);
9891 /* static */ void*
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);
9905 /* static */ void
9906 nsHTMLReflowState::DisplayInitFrameTypeExit(nsIFrame* aFrame,
9907 nsHTMLReflowState* aState,
9908 void* aValue)
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())
9928 printf(" abspos");
9929 if (aFrame->IsFloating())
9930 printf(" float");
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",
9939 #ifdef MOZ_XUL
9940 "grid", "inline-grid", "grid-group", "grid-line", "stack",
9941 "inline-stack", "deck", "popup", "groupbox",
9942 #endif
9944 if (disp->mDisplay >= ArrayLength(displayTypes))
9945 printf(" display=%u", disp->mDisplay);
9946 else
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);
9959 } else {
9960 printf(" result=%s", cssFrameTypes[bareType]);
9962 printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : "");
9964 DR_state->DeleteTreeNode(*treeNode);
9967 #endif
9968 // End Display Reflow
9970 #endif