michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: // vim:cindent:ts=2:et:sw=2: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* base class of all rendering objects */ michael@0: michael@0: #include "nsFrame.h" michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsFrameList.h" michael@0: #include "nsPlaceholderFrame.h" michael@0: #include "nsIContent.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsTableOuterFrame.h" michael@0: #include "nsView.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsIPresShell.h" michael@0: #include "prlog.h" michael@0: #include "prprf.h" michael@0: #include michael@0: #include "nsFrameManager.h" michael@0: #include "nsLayoutUtils.h" michael@0: michael@0: #include "nsIDOMNode.h" michael@0: #include "nsISelection.h" michael@0: #include "nsISelectionPrivate.h" michael@0: #include "nsFrameSelection.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsCSSAnonBoxes.h" michael@0: michael@0: #include "nsFrameTraversal.h" michael@0: #include "nsRange.h" michael@0: #include "nsITextControlFrame.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsIPercentHeightObserver.h" michael@0: #include "nsStyleStructInlines.h" michael@0: #include michael@0: michael@0: #include "nsBidiPresUtils.h" michael@0: michael@0: // For triple-click pref michael@0: #include "imgIContainer.h" michael@0: #include "imgIRequest.h" michael@0: #include "nsError.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "nsBoxLayoutState.h" michael@0: #include "nsBlockFrame.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsSVGIntegrationUtils.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "nsChangeHint.h" michael@0: #include "nsDeckFrame.h" michael@0: #include "nsSubDocumentFrame.h" michael@0: #include "SVGTextFrame.h" michael@0: michael@0: #include "gfxContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsAbsoluteContainingBlock.h" michael@0: #include "StickyScrollContainer.h" michael@0: #include "nsFontInflationData.h" michael@0: #include "gfxASurface.h" michael@0: #include "nsRegion.h" michael@0: #include "nsIFrameInlines.h" michael@0: michael@0: #include "mozilla/AsyncEventDispatcher.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/css/ImageLoader.h" michael@0: #include "mozilla/gfx/Tools.h" michael@0: #include "nsPrintfCString.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::css; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::layers; michael@0: using namespace mozilla::layout; michael@0: michael@0: // Struct containing cached metrics for box-wrapped frames. michael@0: struct nsBoxLayoutMetrics michael@0: { michael@0: nsSize mPrefSize; michael@0: nsSize mMinSize; michael@0: nsSize mMaxSize; michael@0: michael@0: nsSize mBlockMinSize; michael@0: nsSize mBlockPrefSize; michael@0: nscoord mBlockAscent; michael@0: michael@0: nscoord mFlex; michael@0: nscoord mAscent; michael@0: michael@0: nsSize mLastSize; michael@0: }; michael@0: michael@0: struct nsContentAndOffset michael@0: { michael@0: nsIContent* mContent; michael@0: int32_t mOffset; michael@0: }; michael@0: michael@0: // Some Misc #defines michael@0: #define SELECTION_DEBUG 0 michael@0: #define FORCE_SELECTION_UPDATE 1 michael@0: #define CALC_DEBUG 0 michael@0: michael@0: michael@0: #include "nsILineIterator.h" michael@0: michael@0: //non Hack prototypes michael@0: #if 0 michael@0: static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent); michael@0: #endif michael@0: michael@0: #include "prenv.h" michael@0: michael@0: // Formerly the nsIFrameDebug interface michael@0: michael@0: #ifdef DEBUG michael@0: static bool gShowFrameBorders = false; michael@0: michael@0: void nsFrame::ShowFrameBorders(bool aEnable) michael@0: { michael@0: gShowFrameBorders = aEnable; michael@0: } michael@0: michael@0: bool nsFrame::GetShowFrameBorders() michael@0: { michael@0: return gShowFrameBorders; michael@0: } michael@0: michael@0: static bool gShowEventTargetFrameBorder = false; michael@0: michael@0: void nsFrame::ShowEventTargetFrameBorder(bool aEnable) michael@0: { michael@0: gShowEventTargetFrameBorder = aEnable; michael@0: } michael@0: michael@0: bool nsFrame::GetShowEventTargetFrameBorder() michael@0: { michael@0: return gShowEventTargetFrameBorder; michael@0: } michael@0: michael@0: /** michael@0: * Note: the log module is created during library initialization which michael@0: * means that you cannot perform logging before then. michael@0: */ michael@0: static PRLogModuleInfo* gLogModule; michael@0: michael@0: static PRLogModuleInfo* gStyleVerifyTreeLogModuleInfo; michael@0: michael@0: static uint32_t gStyleVerifyTreeEnable = 0x55; michael@0: michael@0: bool michael@0: nsFrame::GetVerifyStyleTreeEnable() michael@0: { michael@0: if (gStyleVerifyTreeEnable == 0x55) { michael@0: if (nullptr == gStyleVerifyTreeLogModuleInfo) { michael@0: gStyleVerifyTreeLogModuleInfo = PR_NewLogModule("styleverifytree"); michael@0: gStyleVerifyTreeEnable = 0 != gStyleVerifyTreeLogModuleInfo->level; michael@0: } michael@0: } michael@0: return gStyleVerifyTreeEnable; michael@0: } michael@0: michael@0: void michael@0: nsFrame::SetVerifyStyleTreeEnable(bool aEnabled) michael@0: { michael@0: gStyleVerifyTreeEnable = aEnabled; michael@0: } michael@0: michael@0: PRLogModuleInfo* michael@0: nsFrame::GetLogModuleInfo() michael@0: { michael@0: if (nullptr == gLogModule) { michael@0: gLogModule = PR_NewLogModule("frame"); michael@0: } michael@0: return gLogModule; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: static void michael@0: DestroyAbsoluteContainingBlock(void* aPropertyValue) michael@0: { michael@0: delete static_cast(aPropertyValue); michael@0: } michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(AbsoluteContainingBlockProperty, DestroyAbsoluteContainingBlock) michael@0: michael@0: bool michael@0: nsIFrame::HasAbsolutelyPositionedChildren() const { michael@0: return IsAbsoluteContainer() && GetAbsoluteContainingBlock()->HasAbsoluteFrames(); michael@0: } michael@0: michael@0: nsAbsoluteContainingBlock* michael@0: nsIFrame::GetAbsoluteContainingBlock() const { michael@0: NS_ASSERTION(IsAbsoluteContainer(), "The frame is not marked as an abspos container correctly"); michael@0: nsAbsoluteContainingBlock* absCB = static_cast michael@0: (Properties().Get(AbsoluteContainingBlockProperty())); michael@0: NS_ASSERTION(absCB, "The frame is marked as an abspos container but doesn't have the property"); michael@0: return absCB; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::MarkAsAbsoluteContainingBlock() michael@0: { michael@0: MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: NS_ASSERTION(!Properties().Get(AbsoluteContainingBlockProperty()), michael@0: "Already has an abs-pos containing block property?"); michael@0: NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN), michael@0: "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?"); michael@0: AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN); michael@0: Properties().Set(AbsoluteContainingBlockProperty(), new nsAbsoluteContainingBlock(GetAbsoluteListID())); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::MarkAsNotAbsoluteContainingBlock() michael@0: { michael@0: NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!"); michael@0: NS_ASSERTION(Properties().Get(AbsoluteContainingBlockProperty()), michael@0: "Should have an abs-pos containing block property"); michael@0: NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN), michael@0: "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit"); michael@0: MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)); michael@0: RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN); michael@0: Properties().Delete(AbsoluteContainingBlockProperty()); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::CheckAndClearPaintedState() michael@0: { michael@0: bool result = (GetStateBits() & NS_FRAME_PAINTED_THEBES); michael@0: RemoveStateBits(NS_FRAME_PAINTED_THEBES); michael@0: michael@0: nsIFrame::ChildListIterator lists(this); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* child = childFrames.get(); michael@0: if (child->CheckAndClearPaintedState()) { michael@0: result = true; michael@0: } michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const michael@0: { michael@0: if (!StyleVisibility()->IsVisible()) { michael@0: return false; michael@0: } michael@0: michael@0: const nsIFrame* frame = this; michael@0: while (frame) { michael@0: nsView* view = frame->GetView(); michael@0: if (view && view->GetVisibility() == nsViewVisibility_kHide) michael@0: return false; michael@0: michael@0: nsIFrame* parent = frame->GetParent(); michael@0: nsDeckFrame* deck = do_QueryFrame(parent); michael@0: if (deck) { michael@0: if (deck->GetSelectedBox() != frame) michael@0: return false; michael@0: } michael@0: michael@0: if (parent) { michael@0: frame = parent; michael@0: } else { michael@0: parent = nsLayoutUtils::GetCrossDocParentFrame(frame); michael@0: if (!parent) michael@0: break; michael@0: michael@0: if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 && michael@0: parent->PresContext()->IsChrome() && !frame->PresContext()->IsChrome()) { michael@0: break; michael@0: } michael@0: michael@0: if (!parent->StyleVisibility()->IsVisible()) michael@0: return false; michael@0: michael@0: frame = parent; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::FindCloserFrameForSelection( michael@0: nsPoint aPoint, michael@0: nsIFrame::FrameWithDistance* aCurrentBestFrame) michael@0: { michael@0: if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect, michael@0: aCurrentBestFrame->mXDistance, michael@0: aCurrentBestFrame->mYDistance)) { michael@0: aCurrentBestFrame->mFrame = this; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIFrame::ContentStatesChanged(mozilla::EventStates aStates) michael@0: { michael@0: } michael@0: michael@0: void michael@0: NS_MergeReflowStatusInto(nsReflowStatus* aPrimary, nsReflowStatus aSecondary) michael@0: { michael@0: *aPrimary |= aSecondary & michael@0: (NS_FRAME_NOT_COMPLETE | NS_FRAME_OVERFLOW_INCOMPLETE | michael@0: NS_FRAME_TRUNCATED | NS_FRAME_REFLOW_NEXTINFLOW); michael@0: if (*aPrimary & NS_FRAME_NOT_COMPLETE) { michael@0: *aPrimary &= ~NS_FRAME_OVERFLOW_INCOMPLETE; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWeakFrame::Init(nsIFrame* aFrame) michael@0: { michael@0: Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr); michael@0: mFrame = aFrame; michael@0: if (mFrame) { michael@0: nsIPresShell* shell = mFrame->PresContext()->GetPresShell(); michael@0: NS_WARN_IF_FALSE(shell, "Null PresShell in nsWeakFrame!"); michael@0: if (shell) { michael@0: shell->AddWeakFrame(this); michael@0: } else { michael@0: mFrame = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: NS_NewEmptyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsFrame(aContext); michael@0: } michael@0: michael@0: nsFrame::nsFrame(nsStyleContext* aContext) michael@0: { michael@0: MOZ_COUNT_CTOR(nsFrame); michael@0: michael@0: mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY; michael@0: mStyleContext = aContext; michael@0: mStyleContext->AddRef(); michael@0: } michael@0: michael@0: nsFrame::~nsFrame() michael@0: { michael@0: MOZ_COUNT_DTOR(nsFrame); michael@0: michael@0: NS_IF_RELEASE(mContent); michael@0: mStyleContext->Release(); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsFrame) michael@0: michael@0: // Dummy operator delete. Will never be called, but must be defined michael@0: // to satisfy some C++ ABIs. michael@0: void michael@0: nsFrame::operator delete(void *, size_t) michael@0: { michael@0: NS_RUNTIMEABORT("nsFrame::operator delete should never be called"); michael@0: } michael@0: michael@0: NS_QUERYFRAME_HEAD(nsFrame) michael@0: NS_QUERYFRAME_ENTRY(nsIFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITANCE_ROOT michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////// michael@0: // nsIFrame michael@0: michael@0: static bool michael@0: IsFontSizeInflationContainer(nsIFrame* aFrame, michael@0: const nsStyleDisplay* aStyleDisplay) michael@0: { michael@0: /* michael@0: * Font size inflation is built around the idea that we're inflating michael@0: * the fonts for a pan-and-zoom UI so that when the user scales up a michael@0: * block or other container to fill the width of the device, the fonts michael@0: * will be readable. To do this, we need to pick what counts as a michael@0: * container. michael@0: * michael@0: * From a code perspective, the only hard requirement is that frames michael@0: * that are line participants michael@0: * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never michael@0: * containers, since line layout assumes that the inflation is michael@0: * consistent within a line. michael@0: * michael@0: * This is not an imposition, since we obviously want a bunch of text michael@0: * (possibly with inline elements) flowing within a block to count the michael@0: * block (or higher) as its container. michael@0: * michael@0: * We also want form controls, including the text in the anonymous michael@0: * content inside of them, to match each other and the text next to michael@0: * them, so they and their anonymous content should also not be a michael@0: * container. michael@0: * michael@0: * However, because we can't reliably compute sizes across XUL during michael@0: * reflow, any XUL frame with a XUL parent is always a container. michael@0: * michael@0: * There are contexts where it would be nice if some blocks didn't michael@0: * count as a container, so that, for example, an indented quotation michael@0: * didn't end up with a smaller font size. However, it's hard to michael@0: * distinguish these situations where we really do want the indented michael@0: * thing to count as a container, so we don't try, and blocks are michael@0: * always containers. michael@0: */ michael@0: michael@0: // The root frame should always be an inflation container. michael@0: if (!aFrame->GetParent()) { michael@0: return true; michael@0: } michael@0: michael@0: nsIContent *content = aFrame->GetContent(); michael@0: bool isInline = (aFrame->GetDisplay() == NS_STYLE_DISPLAY_INLINE || michael@0: (aFrame->IsFloating() && michael@0: aFrame->GetType() == nsGkAtoms::letterFrame) || michael@0: // Given multiple frames for the same node, only the michael@0: // outer one should be considered a container. michael@0: // (Important, e.g., for nsSelectsAreaFrame.) michael@0: (aFrame->GetParent()->GetContent() == content) || michael@0: (content && (content->IsHTML(nsGkAtoms::option) || michael@0: content->IsHTML(nsGkAtoms::optgroup) || michael@0: content->IsHTML(nsGkAtoms::select) || michael@0: content->IsInNativeAnonymousSubtree()))) && michael@0: !(aFrame->IsBoxFrame() && aFrame->GetParent()->IsBoxFrame()); michael@0: NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || michael@0: isInline || michael@0: // br frames and mathml frames report being line michael@0: // participants even when their position or display is michael@0: // set michael@0: aFrame->GetType() == nsGkAtoms::brFrame || michael@0: aFrame->IsFrameOfType(nsIFrame::eMathML), michael@0: "line participants must not be containers"); michael@0: NS_ASSERTION(aFrame->GetType() != nsGkAtoms::bulletFrame || isInline, michael@0: "bullets should not be containers"); michael@0: return !isInline; michael@0: } michael@0: michael@0: void michael@0: nsFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_PRECONDITION(!mContent, "Double-initing a frame?"); michael@0: NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) && michael@0: !IsFrameOfType(eDEBUGNoFrames), michael@0: "IsFrameOfType implementation that doesn't call base class"); michael@0: michael@0: mContent = aContent; michael@0: mParent = aParent; michael@0: michael@0: if (aContent) { michael@0: NS_ADDREF(aContent); michael@0: } michael@0: michael@0: if (aPrevInFlow) { michael@0: // Make sure the general flags bits are the same michael@0: nsFrameState state = aPrevInFlow->GetStateBits(); michael@0: michael@0: // Make bits that are currently off (see constructor) the same: michael@0: mState |= state & (NS_FRAME_INDEPENDENT_SELECTION | michael@0: NS_FRAME_PART_OF_IBSPLIT | michael@0: NS_FRAME_MAY_BE_TRANSFORMED | michael@0: NS_FRAME_MAY_HAVE_GENERATED_CONTENT | michael@0: NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: } michael@0: if (mParent) { michael@0: nsFrameState state = mParent->GetStateBits(); michael@0: michael@0: // Make bits that are currently off (see constructor) the same: michael@0: mState |= state & (NS_FRAME_INDEPENDENT_SELECTION | michael@0: NS_FRAME_GENERATED_CONTENT | michael@0: NS_FRAME_IS_SVG_TEXT | michael@0: NS_FRAME_IN_POPUP | michael@0: NS_FRAME_IS_NONDISPLAY); michael@0: } michael@0: const nsStyleDisplay *disp = StyleDisplay(); michael@0: if (disp->HasTransform(this)) { michael@0: // The frame gets reconstructed if we toggle the -moz-transform michael@0: // property, so we can set this bit here and then ignore it. michael@0: mState |= NS_FRAME_MAY_BE_TRANSFORMED; michael@0: } michael@0: if (disp->mPosition == NS_STYLE_POSITION_STICKY && michael@0: !aPrevInFlow && michael@0: !(mState & NS_FRAME_IS_NONDISPLAY) && michael@0: !disp->IsInnerTableStyle()) { michael@0: // Note that we only add first continuations, but we really only michael@0: // want to add first continuation-or-ib-split-siblings. But since we michael@0: // don't yet know if we're a later part of a block-in-inline split, michael@0: // we'll just add later members of a block-in-inline split here, and michael@0: // then StickyScrollContainer will remove them later. michael@0: // We don't currently support relative positioning of inner table michael@0: // elements (bug 35168), so exclude them from sticky positioning too. michael@0: StickyScrollContainer* ssc = michael@0: StickyScrollContainer::GetStickyScrollContainerForFrame(this); michael@0: if (ssc) { michael@0: ssc->AddFrame(this); michael@0: } michael@0: } michael@0: michael@0: if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent() michael@0: #ifdef DEBUG michael@0: // We have assertions that check inflation invariants even when michael@0: // font size inflation is not enabled. michael@0: || true michael@0: #endif michael@0: ) { michael@0: if (IsFontSizeInflationContainer(this, disp)) { michael@0: AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER); michael@0: if (!GetParent() || michael@0: // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet. michael@0: disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this)) { michael@0: AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); michael@0: } michael@0: } michael@0: NS_ASSERTION(GetParent() || michael@0: (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER), michael@0: "root frame should always be a container"); michael@0: } michael@0: michael@0: DidSetStyleContext(nullptr); michael@0: michael@0: if (IsBoxWrapped()) michael@0: InitBoxMetrics(false); michael@0: } michael@0: michael@0: nsresult nsFrame::SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: // XXX This shouldn't be getting called at all, but currently is for backwards michael@0: // compatility reasons... michael@0: #if 0 michael@0: NS_ERROR("not a container"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: #else michael@0: NS_ASSERTION(aChildList.IsEmpty(), "not a container"); michael@0: return NS_OK; michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: NS_PRECONDITION(false, "not a container"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: NS_PRECONDITION(false, "not a container"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) michael@0: { michael@0: NS_PRECONDITION(false, "not a container"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: void michael@0: nsFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), michael@0: "destroy called on frame while scripts not blocked"); michael@0: NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(), michael@0: "Frames should be removed before destruction."); michael@0: NS_ASSERTION(aDestructRoot, "Must specify destruct root"); michael@0: MOZ_ASSERT(!HasAbsolutelyPositionedChildren()); michael@0: michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: michael@0: if (StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) { michael@0: StickyScrollContainer* ssc = michael@0: StickyScrollContainer::GetStickyScrollContainerForFrame(this); michael@0: if (ssc) { michael@0: ssc->RemoveFrame(this); michael@0: } michael@0: } michael@0: michael@0: // Get the view pointer now before the frame properties disappear michael@0: // when we call NotifyDestroyingFrame() michael@0: nsView* view = GetView(); michael@0: nsPresContext* presContext = PresContext(); michael@0: michael@0: nsIPresShell *shell = presContext->GetPresShell(); michael@0: if (mState & NS_FRAME_OUT_OF_FLOW) { michael@0: nsPlaceholderFrame* placeholder = michael@0: shell->FrameManager()->GetPlaceholderFrameFor(this); michael@0: NS_ASSERTION(!placeholder || (aDestructRoot != this), michael@0: "Don't call Destroy() on OOFs, call Destroy() on the placeholder."); michael@0: NS_ASSERTION(!placeholder || michael@0: nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, placeholder), michael@0: "Placeholder relationship should have been torn down already; " michael@0: "this might mean we have a stray placeholder in the tree."); michael@0: if (placeholder) { michael@0: shell->FrameManager()->UnregisterPlaceholderFrame(placeholder); michael@0: placeholder->SetOutOfFlowFrame(nullptr); michael@0: } michael@0: } michael@0: michael@0: // If we have any IB split siblings, clear their references to us. michael@0: // (Note: This has to happen before we call shell->NotifyDestroyingFrame, michael@0: // because that clears our Properties() table.) michael@0: if (mState & NS_FRAME_PART_OF_IBSPLIT) { michael@0: // Delete previous sibling's reference to me. michael@0: nsIFrame* prevSib = static_cast michael@0: (Properties().Get(nsIFrame::IBSplitPrevSibling())); michael@0: if (prevSib) { michael@0: NS_WARN_IF_FALSE(this == michael@0: prevSib->Properties().Get(nsIFrame::IBSplitSibling()), michael@0: "IB sibling chain is inconsistent"); michael@0: prevSib->Properties().Delete(nsIFrame::IBSplitSibling()); michael@0: } michael@0: michael@0: // Delete next sibling's reference to me. michael@0: nsIFrame* nextSib = static_cast michael@0: (Properties().Get(nsIFrame::IBSplitSibling())); michael@0: if (nextSib) { michael@0: NS_WARN_IF_FALSE(this == michael@0: nextSib->Properties().Get(nsIFrame::IBSplitPrevSibling()), michael@0: "IB sibling chain is inconsistent"); michael@0: nextSib->Properties().Delete(nsIFrame::IBSplitPrevSibling()); michael@0: } michael@0: } michael@0: michael@0: shell->NotifyDestroyingFrame(this); michael@0: michael@0: if (mState & NS_FRAME_EXTERNAL_REFERENCE) { michael@0: shell->ClearFrameRefs(this); michael@0: } michael@0: michael@0: if (view) { michael@0: // Break association between view and frame michael@0: view->SetFrame(nullptr); michael@0: michael@0: // Destroy the view michael@0: view->Destroy(); michael@0: } michael@0: michael@0: // Make sure that our deleted frame can't be returned from GetPrimaryFrame() michael@0: if (mContent && mContent->GetPrimaryFrame() == this) { michael@0: mContent->SetPrimaryFrame(nullptr); michael@0: } michael@0: michael@0: // Must retrieve the object ID before calling destructors, so the michael@0: // vtable is still valid. michael@0: // michael@0: // Note to future tweakers: having the method that returns the michael@0: // object size call the destructor will not avoid an indirect call; michael@0: // the compiler cannot devirtualize the call to the destructor even michael@0: // if it's from a method defined in the same class. michael@0: michael@0: nsQueryFrame::FrameIID id = GetFrameId(); michael@0: this->~nsFrame(); michael@0: michael@0: // Now that we're totally cleaned out, we need to add ourselves to michael@0: // the presshell's recycler. michael@0: shell->FreeFrame(id, this); michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const michael@0: { michael@0: aStart = 0; michael@0: aEnd = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Subclass hook for style post processing michael@0: /* virtual */ void michael@0: nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) michael@0: { michael@0: if (IsSVGText()) { michael@0: SVGTextFrame* svgTextFrame = static_cast( michael@0: nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::svgTextFrame)); michael@0: nsIFrame* anonBlock = svgTextFrame->GetFirstPrincipalChild(); michael@0: // Just as in SVGTextFrame::DidSetStyleContext, we need to ensure that michael@0: // any non-display SVGTextFrames get reflowed when a child text frame michael@0: // gets new style. michael@0: // michael@0: // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's michael@0: // anonymous block frame rather than our self, since NS_FRAME_FIRST_REFLOW michael@0: // may be set on us if we're a new frame that has been inserted after the michael@0: // document's first reflow. (In which case this DidSetStyleContext call may michael@0: // be happening under frame construction under a Reflow() call.) michael@0: if (anonBlock && !(anonBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW) && michael@0: (svgTextFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) && michael@0: !(svgTextFrame->GetStateBits() & NS_STATE_SVG_TEXT_IN_REFLOW)) { michael@0: svgTextFrame->ScheduleReflowSVGNonDisplayText(); michael@0: } michael@0: } michael@0: michael@0: ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader(); michael@0: michael@0: // If the old context had a background image image and new context michael@0: // does not have the same image, clear the image load notifier michael@0: // (which keeps the image loading, if it still is) for the frame. michael@0: // We want to do this conservatively because some frames paint their michael@0: // backgrounds from some other frame's style data, and we don't want michael@0: // to clear those notifiers unless we have to. (They'll be reset michael@0: // when we paint, although we could miss a notification in that michael@0: // interval.) michael@0: const nsStyleBackground *oldBG = aOldStyleContext ? michael@0: aOldStyleContext->StyleBackground() : michael@0: nullptr; michael@0: const nsStyleBackground *newBG = StyleBackground(); michael@0: if (oldBG) { michael@0: NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, oldBG) { michael@0: // If there is an image in oldBG that's not in newBG, drop it. michael@0: if (i >= newBG->mImageCount || michael@0: !oldBG->mLayers[i].mImage.ImageDataEquals(newBG->mLayers[i].mImage)) { michael@0: const nsStyleImage& oldImage = oldBG->mLayers[i].mImage; michael@0: if (oldImage.GetType() != eStyleImageType_Image) { michael@0: continue; michael@0: } michael@0: michael@0: imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(), michael@0: this); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, newBG) { michael@0: // If there is an image in newBG that's not in oldBG, add it. michael@0: if (!oldBG || i >= oldBG->mImageCount || michael@0: !newBG->mLayers[i].mImage.ImageDataEquals(oldBG->mLayers[i].mImage)) { michael@0: const nsStyleImage& newImage = newBG->mLayers[i].mImage; michael@0: if (newImage.GetType() != eStyleImageType_Image) { michael@0: continue; michael@0: } michael@0: michael@0: imageLoader->AssociateRequestToFrame(newImage.GetImageData(), this); michael@0: } michael@0: } michael@0: michael@0: if (aOldStyleContext) { michael@0: // If we detect a change on margin, padding or border, we store the old michael@0: // values on the frame itself between now and reflow, so if someone michael@0: // calls GetUsed(Margin|Border|Padding)() before the next reflow, we michael@0: // can give an accurate answer. michael@0: // We don't want to set the property if one already exists. michael@0: FrameProperties props = Properties(); michael@0: nsMargin oldValue(0, 0, 0, 0); michael@0: nsMargin newValue(0, 0, 0, 0); michael@0: const nsStyleMargin* oldMargin = aOldStyleContext->PeekStyleMargin(); michael@0: if (oldMargin && oldMargin->GetMargin(oldValue)) { michael@0: if ((!StyleMargin()->GetMargin(newValue) || oldValue != newValue) && michael@0: !props.Get(UsedMarginProperty())) { michael@0: props.Set(UsedMarginProperty(), new nsMargin(oldValue)); michael@0: } michael@0: } michael@0: michael@0: const nsStylePadding* oldPadding = aOldStyleContext->PeekStylePadding(); michael@0: if (oldPadding && oldPadding->GetPadding(oldValue)) { michael@0: if ((!StylePadding()->GetPadding(newValue) || oldValue != newValue) && michael@0: !props.Get(UsedPaddingProperty())) { michael@0: props.Set(UsedPaddingProperty(), new nsMargin(oldValue)); michael@0: } michael@0: } michael@0: michael@0: const nsStyleBorder* oldBorder = aOldStyleContext->PeekStyleBorder(); michael@0: if (oldBorder) { michael@0: oldValue = oldBorder->GetComputedBorder(); michael@0: newValue = StyleBorder()->GetComputedBorder(); michael@0: if (oldValue != newValue && michael@0: !props.Get(UsedBorderProperty())) { michael@0: props.Set(UsedBorderProperty(), new nsMargin(oldValue)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: imgIRequest *oldBorderImage = aOldStyleContext michael@0: ? aOldStyleContext->StyleBorder()->GetBorderImageRequest() michael@0: : nullptr; michael@0: imgIRequest *newBorderImage = StyleBorder()->GetBorderImageRequest(); michael@0: // FIXME (Bug 759996): The following is no longer true. michael@0: // For border-images, we can't be as conservative (we need to set the michael@0: // new loaders if there has been any change) since the CalcDifference michael@0: // call depended on the result of GetComputedBorder() and that result michael@0: // depends on whether the image has loaded, start the image load now michael@0: // so that we'll get notified when it completes loading and can do a michael@0: // restyle. Otherwise, the image might finish loading from the michael@0: // network before we start listening to its notifications, and then michael@0: // we'll never know that it's finished loading. Likewise, we want to michael@0: // do this for freshly-created frames to prevent a similar race if the michael@0: // image loads between reflow (which can depend on whether the image michael@0: // is loaded) and paint. We also don't really care about any callers michael@0: // who try to paint borders with a different style context, because michael@0: // they won't have the correct size for the border either. michael@0: if (oldBorderImage != newBorderImage) { michael@0: // stop and restart the image loading/notification michael@0: if (oldBorderImage) { michael@0: imageLoader->DisassociateRequestFromFrame(oldBorderImage, this); michael@0: } michael@0: if (newBorderImage) { michael@0: imageLoader->AssociateRequestToFrame(newBorderImage, this); michael@0: } michael@0: } michael@0: michael@0: // If the page contains markup that overrides text direction, and michael@0: // does not contain any characters that would activate the Unicode michael@0: // bidi algorithm, we need to call |SetBidiEnabled| on the pres michael@0: // context before reflow starts. See bug 115921. michael@0: if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { michael@0: PresContext()->SetBidiEnabled(); michael@0: } michael@0: } michael@0: michael@0: // MSVC fails with link error "one or more multiply defined symbols found", michael@0: // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined" michael@0: // etc if they are not defined. michael@0: #ifndef _MSC_VER michael@0: // static nsIFrame constants; initialized in the header file. michael@0: const nsIFrame::ChildListID nsIFrame::kPrincipalList; michael@0: const nsIFrame::ChildListID nsIFrame::kAbsoluteList; michael@0: const nsIFrame::ChildListID nsIFrame::kBulletList; michael@0: const nsIFrame::ChildListID nsIFrame::kCaptionList; michael@0: const nsIFrame::ChildListID nsIFrame::kColGroupList; michael@0: const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList; michael@0: const nsIFrame::ChildListID nsIFrame::kFixedList; michael@0: const nsIFrame::ChildListID nsIFrame::kFloatList; michael@0: const nsIFrame::ChildListID nsIFrame::kOverflowContainersList; michael@0: const nsIFrame::ChildListID nsIFrame::kOverflowList; michael@0: const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList; michael@0: const nsIFrame::ChildListID nsIFrame::kPopupList; michael@0: const nsIFrame::ChildListID nsIFrame::kPushedFloatsList; michael@0: const nsIFrame::ChildListID nsIFrame::kSelectPopupList; michael@0: const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList; michael@0: #endif michael@0: michael@0: /* virtual */ nsMargin michael@0: nsIFrame::GetUsedMargin() const michael@0: { michael@0: nsMargin margin(0, 0, 0, 0); michael@0: if (((mState & NS_FRAME_FIRST_REFLOW) && michael@0: !(mState & NS_FRAME_IN_REFLOW)) || michael@0: IsSVGText()) michael@0: return margin; michael@0: michael@0: nsMargin *m = static_cast michael@0: (Properties().Get(UsedMarginProperty())); michael@0: if (m) { michael@0: margin = *m; michael@0: } else { michael@0: DebugOnly hasMargin = StyleMargin()->GetMargin(margin); michael@0: NS_ASSERTION(hasMargin, "We should have a margin here! (out of memory?)"); michael@0: } michael@0: return margin; michael@0: } michael@0: michael@0: /* virtual */ nsMargin michael@0: nsIFrame::GetUsedBorder() const michael@0: { michael@0: nsMargin border(0, 0, 0, 0); michael@0: if (((mState & NS_FRAME_FIRST_REFLOW) && michael@0: !(mState & NS_FRAME_IN_REFLOW)) || michael@0: IsSVGText()) michael@0: return border; michael@0: michael@0: // Theme methods don't use const-ness. michael@0: nsIFrame *mutable_this = const_cast(this); michael@0: michael@0: const nsStyleDisplay *disp = StyleDisplay(); michael@0: if (mutable_this->IsThemed(disp)) { michael@0: nsIntMargin result; michael@0: nsPresContext *presContext = PresContext(); michael@0: presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), michael@0: mutable_this, disp->mAppearance, michael@0: &result); michael@0: border.left = presContext->DevPixelsToAppUnits(result.left); michael@0: border.top = presContext->DevPixelsToAppUnits(result.top); michael@0: border.right = presContext->DevPixelsToAppUnits(result.right); michael@0: border.bottom = presContext->DevPixelsToAppUnits(result.bottom); michael@0: return border; michael@0: } michael@0: michael@0: nsMargin *b = static_cast michael@0: (Properties().Get(UsedBorderProperty())); michael@0: if (b) { michael@0: border = *b; michael@0: } else { michael@0: border = StyleBorder()->GetComputedBorder(); michael@0: } michael@0: return border; michael@0: } michael@0: michael@0: /* virtual */ nsMargin michael@0: nsIFrame::GetUsedPadding() const michael@0: { michael@0: nsMargin padding(0, 0, 0, 0); michael@0: if (((mState & NS_FRAME_FIRST_REFLOW) && michael@0: !(mState & NS_FRAME_IN_REFLOW)) || michael@0: IsSVGText()) michael@0: return padding; michael@0: michael@0: // Theme methods don't use const-ness. michael@0: nsIFrame *mutable_this = const_cast(this); michael@0: michael@0: const nsStyleDisplay *disp = StyleDisplay(); michael@0: if (mutable_this->IsThemed(disp)) { michael@0: nsPresContext *presContext = PresContext(); michael@0: nsIntMargin widget; michael@0: if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), michael@0: mutable_this, michael@0: disp->mAppearance, michael@0: &widget)) { michael@0: padding.top = presContext->DevPixelsToAppUnits(widget.top); michael@0: padding.right = presContext->DevPixelsToAppUnits(widget.right); michael@0: padding.bottom = presContext->DevPixelsToAppUnits(widget.bottom); michael@0: padding.left = presContext->DevPixelsToAppUnits(widget.left); michael@0: return padding; michael@0: } michael@0: } michael@0: michael@0: nsMargin *p = static_cast michael@0: (Properties().Get(UsedPaddingProperty())); michael@0: if (p) { michael@0: padding = *p; michael@0: } else { michael@0: DebugOnly hasPadding = StylePadding()->GetPadding(padding); michael@0: NS_ASSERTION(hasPadding, "We should have padding here! (out of memory?)"); michael@0: } michael@0: return padding; michael@0: } michael@0: michael@0: int michael@0: nsIFrame::GetSkipSides(const nsHTMLReflowState* aReflowState) const michael@0: { michael@0: // Convert the logical skip sides to physical sides using the frame's michael@0: // writing mode michael@0: WritingMode writingMode = GetWritingMode(); michael@0: int logicalSkip = GetLogicalSkipSides(aReflowState); michael@0: int skip = 0; michael@0: michael@0: if (logicalSkip & LOGICAL_SIDE_B_START) { michael@0: if (writingMode.IsVertical()) { michael@0: skip |= 1 << (writingMode.IsVerticalLR() ? NS_SIDE_LEFT : NS_SIDE_RIGHT); michael@0: } else { michael@0: skip |= 1 << NS_SIDE_TOP; michael@0: } michael@0: } michael@0: michael@0: if (logicalSkip & LOGICAL_SIDE_B_END) { michael@0: if (writingMode.IsVertical()) { michael@0: skip |= 1 << (writingMode.IsVerticalLR() ? NS_SIDE_RIGHT : NS_SIDE_LEFT); michael@0: } else { michael@0: skip |= 1 << NS_SIDE_BOTTOM; michael@0: } michael@0: } michael@0: michael@0: if (logicalSkip & LOGICAL_SIDE_I_START) { michael@0: if (writingMode.IsVertical()) { michael@0: skip |= 1 << NS_SIDE_TOP; michael@0: } else { michael@0: skip |= 1 << (writingMode.IsBidiLTR() ? NS_SIDE_LEFT : NS_SIDE_RIGHT); michael@0: } michael@0: } michael@0: michael@0: if (logicalSkip & LOGICAL_SIDE_I_END) { michael@0: if (writingMode.IsVertical()) { michael@0: skip |= 1 << NS_SIDE_BOTTOM; michael@0: } else { michael@0: skip |= 1 << (writingMode.IsBidiLTR() ? NS_SIDE_RIGHT : NS_SIDE_LEFT); michael@0: } michael@0: } michael@0: michael@0: return skip; michael@0: } michael@0: michael@0: michael@0: void michael@0: nsIFrame::ApplySkipSides(nsMargin& aMargin, michael@0: const nsHTMLReflowState* aReflowState) const michael@0: { michael@0: int skipSides = GetSkipSides(aReflowState); michael@0: if (skipSides & (1 << NS_SIDE_TOP)) { michael@0: aMargin.top = 0; michael@0: } michael@0: if (skipSides & (1 << NS_SIDE_RIGHT)) { michael@0: aMargin.right = 0; michael@0: } michael@0: if (skipSides & (1 << NS_SIDE_BOTTOM)) { michael@0: aMargin.bottom = 0; michael@0: } michael@0: if (skipSides & (1 << NS_SIDE_LEFT)) { michael@0: aMargin.left = 0; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIFrame::ApplyLogicalSkipSides(LogicalMargin& aMargin, michael@0: const nsHTMLReflowState* aReflowState) const michael@0: { michael@0: int skipSides = GetLogicalSkipSides(aReflowState); michael@0: if (skipSides & (LOGICAL_SIDE_B_START)) { michael@0: aMargin.BStart(GetWritingMode()) = 0; michael@0: } michael@0: if (skipSides & (LOGICAL_SIDE_I_END)) { michael@0: aMargin.IEnd(GetWritingMode()) = 0; michael@0: } michael@0: if (skipSides & (LOGICAL_SIDE_B_END)) { michael@0: aMargin.BEnd(GetWritingMode()) = 0; michael@0: } michael@0: if (skipSides & (LOGICAL_SIDE_I_START)) { michael@0: aMargin.IStart(GetWritingMode()) = 0; michael@0: } michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetPaddingRectRelativeToSelf() const michael@0: { michael@0: nsMargin border(GetUsedBorder()); michael@0: ApplySkipSides(border); michael@0: nsRect r(0, 0, mRect.width, mRect.height); michael@0: r.Deflate(border); michael@0: return r; michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetPaddingRect() const michael@0: { michael@0: return GetPaddingRectRelativeToSelf() + GetPosition(); michael@0: } michael@0: michael@0: WritingMode michael@0: nsIFrame::GetWritingMode(nsIFrame* aSubFrame) const michael@0: { michael@0: WritingMode writingMode = GetWritingMode(); michael@0: michael@0: if (!writingMode.IsVertical() && michael@0: (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT)) { michael@0: nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame); michael@0: writingMode.SetDirectionFromBidiLevel(frameLevel); michael@0: } michael@0: michael@0: return writingMode; michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetMarginRectRelativeToSelf() const michael@0: { michael@0: nsMargin m = GetUsedMargin(); michael@0: ApplySkipSides(m); michael@0: nsRect r(0, 0, mRect.width, mRect.height); michael@0: r.Inflate(m); michael@0: return r; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsTransformed() const michael@0: { michael@0: return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && michael@0: (StyleDisplay()->HasTransform(this) || michael@0: IsSVGTransformed() || michael@0: (mContent && michael@0: nsLayoutUtils::HasAnimationsForCompositor(mContent, michael@0: eCSSProperty_transform) && michael@0: IsFrameOfType(eSupportsCSSTransforms) && michael@0: mContent->GetPrimaryFrame() == this))); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::HasOpacityInternal(float aThreshold) const michael@0: { michael@0: MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument"); michael@0: const nsStyleDisplay* displayStyle = StyleDisplay(); michael@0: return StyleDisplay()->mOpacity < aThreshold || michael@0: (displayStyle->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) || michael@0: (mContent && michael@0: nsLayoutUtils::HasAnimationsForCompositor(mContent, michael@0: eCSSProperty_opacity) && michael@0: mContent->GetPrimaryFrame() == this); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsSVGTransformed(gfx::Matrix *aOwnTransforms, michael@0: gfx::Matrix *aFromParentTransforms) const michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::Preserves3DChildren() const michael@0: { michael@0: const nsStyleDisplay* disp = StyleDisplay(); michael@0: if (disp->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D || michael@0: !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) { michael@0: return false; michael@0: } michael@0: michael@0: // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d. michael@0: if (GetType() == nsGkAtoms::scrollFrame) { michael@0: return false; michael@0: } michael@0: michael@0: nsRect temp; michael@0: return !nsFrame::ShouldApplyOverflowClipping(this, disp) && michael@0: !GetClipPropClipRect(disp, &temp, GetSize()) && michael@0: !nsSVGIntegrationUtils::UsingEffectsForFrame(this); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::Preserves3D() const michael@0: { michael@0: if (!GetParent() || !GetParent()->Preserves3DChildren()) { michael@0: return false; michael@0: } michael@0: return StyleDisplay()->HasTransform(this) || StyleDisplay()->BackfaceIsHidden(); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::HasPerspective() const michael@0: { michael@0: if (!IsTransformed()) { michael@0: return false; michael@0: } michael@0: nsStyleContext* parentStyleContext = StyleContext()->GetParent(); michael@0: if (!parentStyleContext) { michael@0: return false; michael@0: } michael@0: const nsStyleDisplay* parentDisp = parentStyleContext->StyleDisplay(); michael@0: return parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::ChildrenHavePerspective() const michael@0: { michael@0: return StyleDisplay()->HasPerspectiveStyle(); michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetContentRectRelativeToSelf() const michael@0: { michael@0: nsMargin bp(GetUsedBorderAndPadding()); michael@0: ApplySkipSides(bp); michael@0: nsRect r(0, 0, mRect.width, mRect.height); michael@0: r.Deflate(bp); michael@0: return r; michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetContentRect() const michael@0: { michael@0: return GetContentRectRelativeToSelf() + GetPosition(); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::ComputeBorderRadii(const nsStyleCorners& aBorderRadius, michael@0: const nsSize& aFrameSize, michael@0: const nsSize& aBorderArea, michael@0: int aSkipSides, michael@0: nscoord aRadii[8]) michael@0: { michael@0: // Percentages are relative to whichever side they're on. michael@0: NS_FOR_CSS_HALF_CORNERS(i) { michael@0: const nsStyleCoord c = aBorderRadius.Get(i); michael@0: nscoord axis = michael@0: NS_HALF_CORNER_IS_X(i) ? aFrameSize.width : aFrameSize.height; michael@0: michael@0: if (c.IsCoordPercentCalcUnit()) { michael@0: aRadii[i] = nsRuleNode::ComputeCoordPercentCalc(c, axis); michael@0: if (aRadii[i] < 0) { michael@0: // clamp calc() michael@0: aRadii[i] = 0; michael@0: } michael@0: } else { michael@0: NS_NOTREACHED("ComputeBorderRadii: bad unit"); michael@0: aRadii[i] = 0; michael@0: } michael@0: } michael@0: michael@0: if (aSkipSides & (1 << NS_SIDE_TOP)) { michael@0: aRadii[NS_CORNER_TOP_LEFT_X] = 0; michael@0: aRadii[NS_CORNER_TOP_LEFT_Y] = 0; michael@0: aRadii[NS_CORNER_TOP_RIGHT_X] = 0; michael@0: aRadii[NS_CORNER_TOP_RIGHT_Y] = 0; michael@0: } michael@0: michael@0: if (aSkipSides & (1 << NS_SIDE_RIGHT)) { michael@0: aRadii[NS_CORNER_TOP_RIGHT_X] = 0; michael@0: aRadii[NS_CORNER_TOP_RIGHT_Y] = 0; michael@0: aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0; michael@0: aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0; michael@0: } michael@0: michael@0: if (aSkipSides & (1 << NS_SIDE_BOTTOM)) { michael@0: aRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0; michael@0: aRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0; michael@0: aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0; michael@0: aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0; michael@0: } michael@0: michael@0: if (aSkipSides & (1 << NS_SIDE_LEFT)) { michael@0: aRadii[NS_CORNER_BOTTOM_LEFT_X] = 0; michael@0: aRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0; michael@0: aRadii[NS_CORNER_TOP_LEFT_X] = 0; michael@0: aRadii[NS_CORNER_TOP_LEFT_Y] = 0; michael@0: } michael@0: michael@0: // css3-background specifies this algorithm for reducing michael@0: // corner radii when they are too big. michael@0: bool haveRadius = false; michael@0: double ratio = 1.0f; michael@0: NS_FOR_CSS_SIDES(side) { michael@0: uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, true); michael@0: uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, true); michael@0: nscoord length = michael@0: NS_SIDE_IS_VERTICAL(side) ? aBorderArea.height : aBorderArea.width; michael@0: nscoord sum = aRadii[hc1] + aRadii[hc2]; michael@0: if (sum) michael@0: haveRadius = true; michael@0: michael@0: // avoid floating point division in the normal case michael@0: if (length < sum) michael@0: ratio = std::min(ratio, double(length)/sum); michael@0: } michael@0: if (ratio < 1.0) { michael@0: NS_FOR_CSS_HALF_CORNERS(corner) { michael@0: aRadii[corner] *= ratio; michael@0: } michael@0: } michael@0: michael@0: return haveRadius; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets) michael@0: { michael@0: NS_FOR_CSS_SIDES(side) { michael@0: nscoord offset = aOffsets.Side(side); michael@0: uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false); michael@0: uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false); michael@0: aRadii[hc1] = std::max(0, aRadii[hc1] - offset); michael@0: aRadii[hc2] = std::max(0, aRadii[hc2] - offset); michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets) michael@0: { michael@0: NS_FOR_CSS_SIDES(side) { michael@0: nscoord offset = aOffsets.Side(side); michael@0: uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false); michael@0: uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false); michael@0: if (aRadii[hc1] > 0) michael@0: aRadii[hc1] += offset; michael@0: if (aRadii[hc2] > 0) michael@0: aRadii[hc2] += offset; michael@0: } michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsIFrame::GetBorderRadii(nscoord aRadii[8]) const michael@0: { michael@0: if (IsThemed()) { michael@0: // When we're themed, the native theme code draws the border and michael@0: // background, and therefore it doesn't make sense to tell other michael@0: // code that's interested in border-radius that we have any radii. michael@0: // michael@0: // In an ideal world, we might have a way for the them to tell us an michael@0: // border radius, but since we don't, we're better off assuming michael@0: // zero. michael@0: NS_FOR_CSS_HALF_CORNERS(corner) { michael@0: aRadii[corner] = 0; michael@0: } michael@0: return false; michael@0: } michael@0: nsSize size = GetSize(); michael@0: return ComputeBorderRadii(StyleBorder()->mBorderRadius, size, size, michael@0: GetSkipSides(), aRadii); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const michael@0: { michael@0: if (!GetBorderRadii(aRadii)) michael@0: return false; michael@0: InsetBorderRadii(aRadii, GetUsedBorder()); michael@0: NS_FOR_CSS_HALF_CORNERS(corner) { michael@0: if (aRadii[corner]) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const michael@0: { michael@0: if (!GetBorderRadii(aRadii)) michael@0: return false; michael@0: InsetBorderRadii(aRadii, GetUsedBorderAndPadding()); michael@0: NS_FOR_CSS_HALF_CORNERS(corner) { michael@0: if (aRadii[corner]) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsStyleContext* michael@0: nsFrame::GetAdditionalStyleContext(int32_t aIndex) const michael@0: { michael@0: NS_PRECONDITION(aIndex >= 0, "invalid index number"); michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsFrame::SetAdditionalStyleContext(int32_t aIndex, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: NS_PRECONDITION(aIndex >= 0, "invalid index number"); michael@0: } michael@0: michael@0: nscoord michael@0: nsFrame::GetBaseline() const michael@0: { michael@0: NS_ASSERTION(!NS_SUBTREE_DIRTY(this), michael@0: "frame must not be dirty"); michael@0: // Default to the bottom margin edge, per CSS2.1's definition of the michael@0: // 'baseline' value of 'vertical-align'. michael@0: return mRect.height + GetUsedMargin().bottom; michael@0: } michael@0: michael@0: const nsFrameList& michael@0: nsFrame::GetChildList(ChildListID aListID) const michael@0: { michael@0: if (IsAbsoluteContainer() && michael@0: aListID == GetAbsoluteListID()) { michael@0: return GetAbsoluteContainingBlock()->GetChildList(); michael@0: } else { michael@0: return nsFrameList::EmptyList(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrame::GetChildLists(nsTArray* aLists) const michael@0: { michael@0: if (IsAbsoluteContainer()) { michael@0: nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList(); michael@0: absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIFrame::GetCrossDocChildLists(nsTArray* aLists) michael@0: { michael@0: nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this); michael@0: if (subdocumentFrame) { michael@0: // Descend into the subdocument michael@0: nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame(); michael@0: if (root) { michael@0: aLists->AppendElement(nsIFrame::ChildList( michael@0: nsFrameList(root, nsLayoutUtils::GetLastSibling(root)), michael@0: nsIFrame::kPrincipalList)); michael@0: } michael@0: } michael@0: michael@0: GetChildLists(aLists); michael@0: } michael@0: michael@0: static nsIFrame* michael@0: GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame) michael@0: { michael@0: nsIContent* capturingContent = nsIPresShell::GetCapturingContent(); michael@0: if (capturingContent) { michael@0: nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent); michael@0: return activeFrame ? activeFrame : aFrame; michael@0: } michael@0: michael@0: return aFrame; michael@0: } michael@0: michael@0: int16_t michael@0: nsFrame::DisplaySelection(nsPresContext* aPresContext, bool isOkToTurnOn) michael@0: { michael@0: int16_t selType = nsISelectionController::SELECTION_OFF; michael@0: michael@0: nsCOMPtr selCon; michael@0: nsresult result = GetSelectionController(aPresContext, getter_AddRefs(selCon)); michael@0: if (NS_SUCCEEDED(result) && selCon) { michael@0: result = selCon->GetDisplaySelection(&selType); michael@0: if (NS_SUCCEEDED(result) && (selType != nsISelectionController::SELECTION_OFF)) { michael@0: // Check whether style allows selection. michael@0: bool selectable; michael@0: IsSelectable(&selectable, nullptr); michael@0: if (!selectable) { michael@0: selType = nsISelectionController::SELECTION_OFF; michael@0: isOkToTurnOn = false; michael@0: } michael@0: } michael@0: if (isOkToTurnOn && (selType == nsISelectionController::SELECTION_OFF)) { michael@0: selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); michael@0: selType = nsISelectionController::SELECTION_ON; michael@0: } michael@0: } michael@0: return selType; michael@0: } michael@0: michael@0: class nsDisplaySelectionOverlay : public nsDisplayItem { michael@0: public: michael@0: nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, michael@0: nsFrame* aFrame, int16_t aSelectionValue) michael@0: : nsDisplayItem(aBuilder, aFrame), mSelectionValue(aSelectionValue) { michael@0: MOZ_COUNT_CTOR(nsDisplaySelectionOverlay); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplaySelectionOverlay() { michael@0: MOZ_COUNT_DTOR(nsDisplaySelectionOverlay); michael@0: } michael@0: #endif michael@0: michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY) michael@0: private: michael@0: int16_t mSelectionValue; michael@0: }; michael@0: michael@0: void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: LookAndFeel::ColorID colorID; michael@0: if (mSelectionValue == nsISelectionController::SELECTION_ON) { michael@0: colorID = LookAndFeel::eColorID_TextSelectBackground; michael@0: } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) { michael@0: colorID = LookAndFeel::eColorID_TextSelectBackgroundAttention; michael@0: } else { michael@0: colorID = LookAndFeel::eColorID_TextSelectBackgroundDisabled; michael@0: } michael@0: michael@0: nscolor color = LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)); michael@0: michael@0: gfxRGBA c(color); michael@0: c.a = .5; michael@0: michael@0: gfxContext *ctx = aCtx->ThebesContext(); michael@0: ctx->SetColor(c); michael@0: michael@0: nsIntRect pxRect = michael@0: mVisibleRect.ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel()); michael@0: ctx->NewPath(); michael@0: ctx->Rectangle(gfxRect(pxRect.x, pxRect.y, pxRect.width, pxRect.height), true); michael@0: ctx->Fill(); michael@0: } michael@0: michael@0: /******************************************************** michael@0: * Refreshes each content's frame michael@0: *********************************************************/ michael@0: michael@0: void michael@0: nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayList* aList, michael@0: uint16_t aContentType) michael@0: { michael@0: if (!IsSelected() || !IsVisibleForPainting(aBuilder)) michael@0: return; michael@0: michael@0: nsPresContext* presContext = PresContext(); michael@0: nsIPresShell *shell = presContext->PresShell(); michael@0: if (!shell) michael@0: return; michael@0: michael@0: int16_t displaySelection = shell->GetSelectionFlags(); michael@0: if (!(displaySelection & aContentType)) michael@0: return; michael@0: michael@0: const nsFrameSelection* frameSelection = GetConstFrameSelection(); michael@0: int16_t selectionValue = frameSelection->GetDisplaySelection(); michael@0: michael@0: if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) michael@0: return; // selection is hidden or off michael@0: michael@0: nsIContent *newContent = mContent->GetParent(); michael@0: michael@0: //check to see if we are anonymous content michael@0: int32_t offset = 0; michael@0: if (newContent) { michael@0: // XXXbz there has GOT to be a better way of determining this! michael@0: offset = newContent->IndexOf(mContent); michael@0: } michael@0: michael@0: SelectionDetails *details; michael@0: //look up to see what selection(s) are on this frame michael@0: details = frameSelection->LookUpSelection(newContent, offset, 1, false); michael@0: if (!details) michael@0: return; michael@0: michael@0: bool normal = false; michael@0: while (details) { michael@0: if (details->mType == nsISelectionController::SELECTION_NORMAL) { michael@0: normal = true; michael@0: } michael@0: SelectionDetails *next = details->mNext; michael@0: delete details; michael@0: details = next; michael@0: } michael@0: michael@0: if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) { michael@0: // Don't overlay an image if it's not in the primary selection. michael@0: return; michael@0: } michael@0: michael@0: aList->AppendNewToTop(new (aBuilder) michael@0: nsDisplaySelectionOverlay(aBuilder, this, selectionValue)); michael@0: } michael@0: michael@0: void michael@0: nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: if (StyleOutline()->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) michael@0: return; michael@0: michael@0: aLists.Outlines()->AppendNewToTop( michael@0: new (aBuilder) nsDisplayOutline(aBuilder, this)); michael@0: } michael@0: michael@0: void michael@0: nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: if (!IsVisibleForPainting(aBuilder)) michael@0: return; michael@0: michael@0: DisplayOutlineUnconditional(aBuilder, aLists); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, nsDisplayList* aList) michael@0: { michael@0: if (!IsVisibleForPainting(aBuilder)) michael@0: return; michael@0: michael@0: aList->AppendNewToTop( michael@0: new (aBuilder) nsDisplayCaret(aBuilder, this, aBuilder->GetCaret())); michael@0: } michael@0: michael@0: nscolor michael@0: nsIFrame::GetCaretColorAt(int32_t aOffset) michael@0: { michael@0: nscolor color = NS_RGB(0, 0, 0); michael@0: if (nsLayoutUtils::GetNativeTextColor(this, color)) michael@0: return color; michael@0: michael@0: // Use CSS text color. michael@0: return StyleColor()->mColor; michael@0: } michael@0: michael@0: bool michael@0: nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists, michael@0: bool aForceBackground) michael@0: { michael@0: // Here we don't try to detect background propagation. Frames that might michael@0: // receive a propagated background should just set aForceBackground to michael@0: // true. michael@0: if (aBuilder->IsForEventDelivery() || aForceBackground || michael@0: !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) { michael@0: return nsDisplayBackgroundImage::AppendBackgroundItemsToTop( michael@0: aBuilder, this, aLists.BorderBackground()); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists, michael@0: bool aForceBackground) michael@0: { michael@0: // The visibility check belongs here since child elements have the michael@0: // opportunity to override the visibility property and display even if michael@0: // their parent is hidden. michael@0: if (!IsVisibleForPainting(aBuilder)) michael@0: return; michael@0: michael@0: nsCSSShadowArray* shadows = StyleBorder()->mBoxShadow; michael@0: if (shadows && shadows->HasShadowWithInset(false)) { michael@0: aLists.BorderBackground()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayBoxShadowOuter(aBuilder, this)); michael@0: } michael@0: michael@0: bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists, michael@0: aForceBackground); michael@0: michael@0: if (shadows && shadows->HasShadowWithInset(true)) { michael@0: aLists.BorderBackground()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayBoxShadowInner(aBuilder, this)); michael@0: } michael@0: michael@0: // If there's a themed background, we should not create a border item. michael@0: // It won't be rendered. michael@0: if (!bgIsThemed && StyleBorder()->HasBorder()) { michael@0: aLists.BorderBackground()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayBorder(aBuilder, this)); michael@0: } michael@0: michael@0: DisplayOutlineUnconditional(aBuilder, aLists); michael@0: } michael@0: michael@0: inline static bool IsSVGContentWithCSSClip(const nsIFrame *aFrame) michael@0: { michael@0: // The CSS spec says that the 'clip' property only applies to absolutely michael@0: // positioned elements, whereas the SVG spec says that it applies to SVG michael@0: // elements regardless of the value of the 'position' property. Here we obey michael@0: // the CSS spec for outer- (since that's what we generally do), but michael@0: // obey the SVG spec for other SVG elements to which 'clip' applies. michael@0: nsIAtom *tag = aFrame->GetContent()->Tag(); michael@0: return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) && michael@0: (tag == nsGkAtoms::svg || tag == nsGkAtoms::foreignObject); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, michael@0: const nsSize& aSize) const michael@0: { michael@0: NS_PRECONDITION(aRect, "Must have aRect out parameter"); michael@0: michael@0: if (!(aDisp->mClipFlags & NS_STYLE_CLIP_RECT) || michael@0: !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) { michael@0: return false; michael@0: } michael@0: michael@0: *aRect = aDisp->mClip; michael@0: if (NS_STYLE_CLIP_RIGHT_AUTO & aDisp->mClipFlags) { michael@0: aRect->width = aSize.width - aRect->x; michael@0: } michael@0: if (NS_STYLE_CLIP_BOTTOM_AUTO & aDisp->mClipFlags) { michael@0: aRect->height = aSize.height - aRect->y; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * If the CSS 'clip' property applies to this frame, set it up michael@0: * in aBuilder->ClipState() to clip all content descendants. Returns true michael@0: * if the property applies, and if so also returns the clip rect (relative michael@0: * to aFrame) in *aRect. michael@0: */ michael@0: static bool michael@0: ApplyClipPropClipping(nsDisplayListBuilder* aBuilder, michael@0: const nsIFrame* aFrame, michael@0: const nsStyleDisplay* aDisp, michael@0: nsRect* aRect, michael@0: DisplayListClipState::AutoSaveRestore& aClipState) michael@0: { michael@0: if (!aFrame->GetClipPropClipRect(aDisp, aRect, aFrame->GetSize())) michael@0: return false; michael@0: michael@0: nsRect clipRect = *aRect + aBuilder->ToReferenceFrame(aFrame); michael@0: aClipState.ClipContentDescendants(clipRect); michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * If the CSS 'overflow' property applies to this frame, and is not michael@0: * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping michael@0: * for that overflow in aBuilder->ClipState() to clip all containing-block michael@0: * descendants. michael@0: */ michael@0: static void michael@0: ApplyOverflowClipping(nsDisplayListBuilder* aBuilder, michael@0: const nsIFrame* aFrame, michael@0: const nsStyleDisplay* aDisp, michael@0: DisplayListClipState::AutoClipMultiple& aClipState) michael@0: { michael@0: // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table michael@0: // frames, and any non-visible value for blocks in a paginated context). michael@0: // We allow -moz-hidden-unscrollable to apply to any kind of frame. This michael@0: // is required by comboboxes which make their display text (an inline frame) michael@0: // have clipping. michael@0: if (!nsFrame::ShouldApplyOverflowClipping(aFrame, aDisp)) { michael@0: return; michael@0: } michael@0: nsRect clipRect; michael@0: bool haveRadii = false; michael@0: nscoord radii[8]; michael@0: if (aFrame->StyleDisplay()->mOverflowClipBox == michael@0: NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX) { michael@0: clipRect = aFrame->GetPaddingRectRelativeToSelf() + michael@0: aBuilder->ToReferenceFrame(aFrame); michael@0: haveRadii = aFrame->GetPaddingBoxBorderRadii(radii); michael@0: } else { michael@0: clipRect = aFrame->GetContentRectRelativeToSelf() + michael@0: aBuilder->ToReferenceFrame(aFrame); michael@0: // XXX border-radius michael@0: } michael@0: aClipState.ClipContainingBlockDescendantsExtra(clipRect, haveRadii ? radii : nullptr); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static void PaintDebugBorder(nsIFrame* aFrame, nsRenderingContext* aCtx, michael@0: const nsRect& aDirtyRect, nsPoint aPt) { michael@0: nsRect r(aPt, aFrame->GetSize()); michael@0: if (aFrame->HasView()) { michael@0: aCtx->SetColor(NS_RGB(0,0,255)); michael@0: } else { michael@0: aCtx->SetColor(NS_RGB(255,0,0)); michael@0: } michael@0: aCtx->DrawRect(r); michael@0: } michael@0: michael@0: static void PaintEventTargetBorder(nsIFrame* aFrame, nsRenderingContext* aCtx, michael@0: const nsRect& aDirtyRect, nsPoint aPt) { michael@0: nsRect r(aPt, aFrame->GetSize()); michael@0: aCtx->SetColor(NS_RGB(128,0,128)); michael@0: aCtx->DrawRect(r); michael@0: } michael@0: michael@0: static void michael@0: DisplayDebugBorders(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, michael@0: const nsDisplayListSet& aLists) { michael@0: // Draw a border around the child michael@0: // REVIEW: From nsContainerFrame::PaintChild michael@0: if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) { michael@0: aLists.Outlines()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayGeneric(aBuilder, aFrame, PaintDebugBorder, "DebugBorder", michael@0: nsDisplayItem::TYPE_DEBUG_BORDER)); michael@0: } michael@0: // Draw a border around the current event target michael@0: if (nsFrame::GetShowEventTargetFrameBorder() && michael@0: aFrame->PresContext()->PresShell()->GetDrawEventTargetFrame() == aFrame) { michael@0: aLists.Outlines()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayGeneric(aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder", michael@0: nsDisplayItem::TYPE_EVENT_TARGET_BORDER)); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: static nsresult michael@0: WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsDisplayList *aList, nsDisplayList *aOutput, uint32_t& aIndex, nsDisplayList* aTemp) michael@0: { michael@0: if (aIndex > nsDisplayTransform::INDEX_MAX) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: while (nsDisplayItem *item = aList->RemoveBottom()) { michael@0: nsIFrame *childFrame = item->Frame(); michael@0: michael@0: // We accumulate sequential items that aren't transforms into the 'temp' list michael@0: // and then flush this list into aOutput by wrapping the whole lot with a single michael@0: // nsDisplayTransform. michael@0: michael@0: if (childFrame->GetParent() && michael@0: (childFrame->GetParent()->Preserves3DChildren() || childFrame == aFrame)) { michael@0: switch (item->GetType()) { michael@0: case nsDisplayItem::TYPE_TRANSFORM: { michael@0: if (!aTemp->IsEmpty()) { michael@0: aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); michael@0: } michael@0: // Override item's clipping with our current clip state (if any). Since we're michael@0: // bubbling up a preserve-3d transformed child to a preserve-3d parent, michael@0: // we can be sure the child doesn't have clip state of its own. michael@0: NS_ASSERTION(!item->GetClip().HasClip(), "Unexpected clip on item"); michael@0: const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder); michael@0: if (clip) { michael@0: item->SetClip(aBuilder, *clip); michael@0: } michael@0: aOutput->AppendToTop(item); michael@0: break; michael@0: } michael@0: case nsDisplayItem::TYPE_WRAP_LIST: { michael@0: nsDisplayWrapList *list = static_cast(item); michael@0: rv = WrapPreserve3DListInternal(aFrame, aBuilder, michael@0: list->GetChildren(), aOutput, aIndex, aTemp); michael@0: list->~nsDisplayWrapList(); michael@0: break; michael@0: } michael@0: case nsDisplayItem::TYPE_OPACITY: { michael@0: if (!aTemp->IsEmpty()) { michael@0: aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); michael@0: } michael@0: nsDisplayOpacity *opacity = static_cast(item); michael@0: nsDisplayList output; michael@0: // Call GetChildren, not GetSameCoordinateSystemChildren, because michael@0: // the preserve-3d children of 'opacity' are temporarily not in the michael@0: // same coordinate system as the opacity --- until this wrapping is done. michael@0: rv = WrapPreserve3DListInternal(aFrame, aBuilder, michael@0: opacity->GetChildren(), &output, aIndex, aTemp); michael@0: if (!aTemp->IsEmpty()) { michael@0: output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); michael@0: } michael@0: opacity->GetChildren()->AppendToTop(&output); michael@0: opacity->UpdateBounds(aBuilder); michael@0: aOutput->AppendToTop(item); michael@0: break; michael@0: } michael@0: default: { michael@0: if (childFrame->StyleDisplay()->BackfaceIsHidden()) { michael@0: if (!aTemp->IsEmpty()) { michael@0: aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aTemp, aIndex++)); michael@0: } michael@0: michael@0: aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, childFrame, item, aIndex++)); michael@0: } else { michael@0: aTemp->AppendToTop(item); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } else { michael@0: aTemp->AppendToTop(item); michael@0: } michael@0: michael@0: if (NS_FAILED(rv) || !item || aIndex > nsDisplayTransform::INDEX_MAX) michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static bool michael@0: IsScrollFrameActive(nsIScrollableFrame* aScrollableFrame) michael@0: { michael@0: return aScrollableFrame && aScrollableFrame->IsScrollingActive(); michael@0: } michael@0: michael@0: static nsresult michael@0: WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsDisplayList *aList) michael@0: { michael@0: uint32_t index = 0; michael@0: nsDisplayList temp; michael@0: nsDisplayList output; michael@0: nsresult rv = WrapPreserve3DListInternal(aFrame, aBuilder, aList, &output, index, &temp); michael@0: michael@0: if (!temp.IsEmpty()) { michael@0: output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame, &temp, index++)); michael@0: } michael@0: michael@0: aList->AppendToTop(&output); michael@0: return rv; michael@0: } michael@0: michael@0: class AutoSaveRestoreBlendMode michael@0: { michael@0: nsDisplayListBuilder& mBuilder; michael@0: bool AutoResetContainsBlendMode; michael@0: public: michael@0: AutoSaveRestoreBlendMode(nsDisplayListBuilder& aBuilder) michael@0: : mBuilder(aBuilder), michael@0: AutoResetContainsBlendMode(aBuilder.ContainsBlendMode()) { michael@0: } michael@0: michael@0: ~AutoSaveRestoreBlendMode() { michael@0: mBuilder.SetContainsBlendMode(AutoResetContainsBlendMode); michael@0: } michael@0: }; michael@0: michael@0: static void michael@0: CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) michael@0: { michael@0: nsIContent* content = aFrame->GetContent(); michael@0: if (!content) { michael@0: return; michael@0: } michael@0: EventListenerManager* elm = nsContentUtils::GetExistingListenerManagerForNode(content); michael@0: if (!elm) { michael@0: return; michael@0: } michael@0: if (elm->HasListenersFor(nsGkAtoms::ontouchstart) || michael@0: elm->HasListenersFor(nsGkAtoms::ontouchmove)) { michael@0: aBuilder->SetAncestorHasTouchEventHandler(true); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: nsDisplayList* aList) { michael@0: if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) michael@0: return; michael@0: michael@0: // Replaced elements have their visibility handled here, because michael@0: // they're visually atomic michael@0: if (IsFrameOfType(eReplaced) && !IsVisibleForPainting(aBuilder)) michael@0: return; michael@0: michael@0: const nsStyleDisplay* disp = StyleDisplay(); michael@0: // We can stop right away if this is a zero-opacity stacking context and michael@0: // we're painting, and we're not animating opacity. Don't do this michael@0: // if we're going to compute plugin geometry, since opacity-0 plugins michael@0: // need to have display items built for them. michael@0: if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() && michael@0: !aBuilder->WillComputePluginGeometry() && michael@0: !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && michael@0: !nsLayoutUtils::HasAnimations(mContent, eCSSProperty_opacity)) { michael@0: return; michael@0: } michael@0: michael@0: nsRect dirtyRect = aDirtyRect; michael@0: michael@0: bool inTransform = aBuilder->IsInTransform(); michael@0: bool isTransformed = IsTransformed(); michael@0: // reset blend mode so we can keep track if this stacking context needs have michael@0: // a nsDisplayBlendContainer. Set the blend mode back when the routine exits michael@0: // so we keep track if the parent stacking context needs a container too. michael@0: AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder); michael@0: aBuilder->SetContainsBlendMode(false); michael@0: michael@0: if (isTransformed) { michael@0: const nsRect overflow = GetVisualOverflowRectRelativeToSelf(); michael@0: if (aBuilder->IsForPainting() && michael@0: nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) { michael@0: dirtyRect = overflow; michael@0: } else { michael@0: if (overflow.IsEmpty() && !Preserves3DChildren()) { michael@0: return; michael@0: } michael@0: michael@0: nsPoint offset = aBuilder->ToReferenceFrame(this); michael@0: dirtyRect += offset; michael@0: michael@0: nsRect untransformedDirtyRect; michael@0: if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this, offset, &untransformedDirtyRect)) { michael@0: dirtyRect = untransformedDirtyRect; michael@0: } else { michael@0: NS_WARNING("Unable to untransform dirty rect!"); michael@0: // This should only happen if the transform is singular, in which case nothing is visible anyway michael@0: dirtyRect.SetEmpty(); michael@0: } michael@0: } michael@0: inTransform = true; michael@0: } michael@0: michael@0: bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this); michael@0: bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL; michael@0: bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this); michael@0: bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY && michael@0: IsScrollFrameActive(nsLayoutUtils::GetNearestScrollableFrame(GetParent(), michael@0: nsLayoutUtils::SCROLLABLE_SAME_DOC | michael@0: nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN)); michael@0: michael@0: DisplayListClipState::AutoSaveRestore clipState(aBuilder); michael@0: michael@0: if (isTransformed || useOpacity || useBlendMode || usingSVGEffects || useStickyPosition) { michael@0: // We don't need to pass ancestor clipping down to our children; michael@0: // everything goes inside a display item's child list, and the display michael@0: // item itself will be clipped. michael@0: // For transforms we also need to clear ancestor clipping because it's michael@0: // relative to the wrong display item reference frame anyway. michael@0: clipState.Clear(); michael@0: } michael@0: michael@0: nsDisplayListCollection set; michael@0: { michael@0: nsDisplayListBuilder::AutoBuildingDisplayList rootSetter(aBuilder, true); michael@0: DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder); michael@0: nsDisplayListBuilder::AutoInTransformSetter michael@0: inTransformSetter(aBuilder, inTransform); michael@0: CheckForTouchEventHandler(aBuilder, this); michael@0: michael@0: if (usingSVGEffects) { michael@0: dirtyRect = michael@0: nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect); michael@0: } michael@0: michael@0: nsRect clipPropClip; michael@0: if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip, michael@0: nestedClipState)) { michael@0: dirtyRect.IntersectRect(dirtyRect, clipPropClip); michael@0: } michael@0: michael@0: MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect); michael@0: michael@0: // Preserve3DChildren() also guarantees that applyAbsPosClipping and usingSVGEffects are false michael@0: // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy michael@0: if (Preserves3DChildren()) { michael@0: aBuilder->MarkPreserve3DFramesForDisplayList(this, aDirtyRect); michael@0: } michael@0: michael@0: if (aBuilder->IsBuildingLayerEventRegions()) { michael@0: nsDisplayLayerEventRegions* eventRegions = michael@0: new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this); michael@0: aBuilder->SetLayerEventRegions(eventRegions); michael@0: set.BorderBackground()->AppendNewToTop(eventRegions); michael@0: } michael@0: BuildDisplayList(aBuilder, dirtyRect, set); michael@0: } michael@0: michael@0: if (aBuilder->IsBackgroundOnly()) { michael@0: set.BlockBorderBackgrounds()->DeleteAll(); michael@0: set.Floats()->DeleteAll(); michael@0: set.Content()->DeleteAll(); michael@0: set.PositionedDescendants()->DeleteAll(); michael@0: set.Outlines()->DeleteAll(); michael@0: } michael@0: michael@0: // This z-order sort also sorts secondarily by content order. We need to do michael@0: // this so that boxes produced by the same element are placed together michael@0: // in the sort. Consider a position:relative inline element that breaks michael@0: // across lines and has absolutely positioned children; all the abs-pos michael@0: // children should be z-ordered after all the boxes for the position:relative michael@0: // element itself. michael@0: set.PositionedDescendants()->SortByZOrder(aBuilder, GetContent()); michael@0: michael@0: nsDisplayList resultList; michael@0: // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html michael@0: // 1,2: backgrounds and borders michael@0: resultList.AppendToTop(set.BorderBackground()); michael@0: // 3: negative z-index children. michael@0: for (;;) { michael@0: nsDisplayItem* item = set.PositionedDescendants()->GetBottom(); michael@0: if (item && item->ZIndex() < 0) { michael@0: set.PositionedDescendants()->RemoveBottom(); michael@0: resultList.AppendToTop(item); michael@0: continue; michael@0: } michael@0: break; michael@0: } michael@0: // 4: block backgrounds michael@0: resultList.AppendToTop(set.BlockBorderBackgrounds()); michael@0: // 5: floats michael@0: resultList.AppendToTop(set.Floats()); michael@0: // 7: general content michael@0: resultList.AppendToTop(set.Content()); michael@0: // 7.5: outlines, in content tree order. We need to sort by content order michael@0: // because an element with outline that breaks and has children with outline michael@0: // might have placed child outline items between its own outline items. michael@0: // The element's outline items need to all come before any child outline michael@0: // items. michael@0: nsIContent* content = GetContent(); michael@0: if (!content) { michael@0: content = PresContext()->Document()->GetRootElement(); michael@0: } michael@0: if (content) { michael@0: set.Outlines()->SortByContentOrder(aBuilder, content); michael@0: } michael@0: #ifdef DEBUG michael@0: DisplayDebugBorders(aBuilder, this, set); michael@0: #endif michael@0: resultList.AppendToTop(set.Outlines()); michael@0: // 8, 9: non-negative z-index children michael@0: resultList.AppendToTop(set.PositionedDescendants()); michael@0: michael@0: if (!isTransformed) { michael@0: // Restore saved clip state now so that any display items we create below michael@0: // are clipped properly. michael@0: clipState.Restore(); michael@0: } michael@0: michael@0: /* If there are any SVG effects, wrap the list up in an SVG effects item michael@0: * (which also handles CSS group opacity). Note that we create an SVG effects michael@0: * item even if resultList is empty, since a filter can produce graphical michael@0: * output even if the element being filtered wouldn't otherwise do so. michael@0: */ michael@0: if (usingSVGEffects) { michael@0: /* List now emptied, so add the new list to the top. */ michael@0: resultList.AppendNewToTop( michael@0: new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList)); michael@0: } michael@0: /* Else, if the list is non-empty and there is CSS group opacity without SVG michael@0: * effects, wrap it up in an opacity item. michael@0: */ michael@0: else if (useOpacity && !resultList.IsEmpty()) { michael@0: resultList.AppendNewToTop( michael@0: new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList)); michael@0: } michael@0: /* If we have sticky positioning, wrap it in a sticky position item. michael@0: */ michael@0: if (useStickyPosition) { michael@0: resultList.AppendNewToTop( michael@0: new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList)); michael@0: } michael@0: michael@0: /* If we're going to apply a transformation and don't have preserve-3d set, wrap michael@0: * everything in an nsDisplayTransform. If there's nothing in the list, don't add michael@0: * anything. michael@0: * michael@0: * For the preserve-3d case we want to individually wrap every child in the list with michael@0: * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform, michael@0: * we can skip this step, as the computed transform will already include our own. michael@0: * michael@0: * We also traverse into sublists created by nsDisplayWrapList or nsDisplayOpacity, so that michael@0: * we find all the correct children. michael@0: */ michael@0: if (isTransformed && !resultList.IsEmpty()) { michael@0: // Restore clip state now so nsDisplayTransform is clipped properly. michael@0: clipState.Restore(); michael@0: michael@0: if (Preserves3DChildren()) { michael@0: WrapPreserve3DList(this, aBuilder, &resultList); michael@0: } else { michael@0: resultList.AppendNewToTop( michael@0: new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList)); michael@0: } michael@0: } michael@0: michael@0: /* If adding both a nsDisplayBlendContainer and a nsDisplayMixBlendMode to the michael@0: * same list, the nsDisplayBlendContainer should be added first. This only michael@0: * happens when the element creating this stacking context has mix-blend-mode michael@0: * and also contains a child which has mix-blend-mode. michael@0: * The nsDisplayBlendContainer must be added to the list first, so it does not michael@0: * isolate the containing element blending as well. michael@0: */ michael@0: michael@0: if (aBuilder->ContainsBlendMode()) { michael@0: resultList.AppendNewToTop( michael@0: new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList)); michael@0: } michael@0: michael@0: /* If there's blending, wrap up the list in a blend-mode item. Note michael@0: * that opacity can be applied before blending as the blend color is michael@0: * not affected by foreground opacity (only background alpha). michael@0: */ michael@0: michael@0: if (useBlendMode && !resultList.IsEmpty()) { michael@0: resultList.AppendNewToTop( michael@0: new (aBuilder) nsDisplayMixBlendMode(aBuilder, this, &resultList)); michael@0: } michael@0: michael@0: CreateOwnLayerIfNeeded(aBuilder, &resultList); michael@0: michael@0: aList->AppendToTop(&resultList); michael@0: } michael@0: michael@0: static nsDisplayItem* michael@0: WrapInWrapList(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList) michael@0: { michael@0: nsDisplayItem* item = aList->GetBottom(); michael@0: if (!item || item->GetAbove() || item->Frame() != aFrame) { michael@0: return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList); michael@0: } michael@0: aList->RemoveBottom(); michael@0: return item; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aChild, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists, michael@0: uint32_t aFlags) { michael@0: // If painting is restricted to just the background of the top level frame, michael@0: // then we have nothing to do here. michael@0: if (aBuilder->IsBackgroundOnly()) michael@0: return; michael@0: michael@0: nsIFrame* child = aChild; michael@0: if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) michael@0: return; michael@0: michael@0: bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT); michael@0: michael@0: // true if this is a real or pseudo stacking context michael@0: bool pseudoStackingContext = michael@0: (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0; michael@0: if (!isSVG && michael@0: (aFlags & DISPLAY_CHILD_INLINE) && michael@0: !child->IsFrameOfType(eLineParticipant)) { michael@0: // child is a non-inline frame in an inline context, i.e., michael@0: // it acts like inline-block or inline-table. Therefore it is a michael@0: // pseudo-stacking-context. michael@0: pseudoStackingContext = true; michael@0: } michael@0: michael@0: // dirty rect in child-relative coordinates michael@0: nsRect dirty = aDirtyRect - child->GetOffsetTo(this); michael@0: michael@0: nsIAtom* childType = child->GetType(); michael@0: nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr; michael@0: if (childType == nsGkAtoms::placeholderFrame) { michael@0: nsPlaceholderFrame* placeholder = static_cast(child); michael@0: child = placeholder->GetOutOfFlowFrame(); michael@0: NS_ASSERTION(child, "No out of flow frame?"); michael@0: // If 'child' is a pushed float then it's owned by a block that's not an michael@0: // ancestor of the placeholder, and it will be painted by that block and michael@0: // should not be painted through the placeholder. michael@0: if (!child || nsLayoutUtils::IsPopup(child) || michael@0: (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)) michael@0: return; michael@0: // Make sure that any attempt to use childType below is disappointed. We michael@0: // could call GetType again but since we don't currently need it, let's michael@0: // avoid the virtual call. michael@0: childType = nullptr; michael@0: // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE michael@0: if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) michael@0: return; michael@0: savedOutOfFlowData = static_cast michael@0: (child->Properties().Get(nsDisplayListBuilder::OutOfFlowDisplayDataProperty())); michael@0: if (savedOutOfFlowData) { michael@0: dirty = savedOutOfFlowData->mDirtyRect; michael@0: } else { michael@0: // The out-of-flow frame did not intersect the dirty area. We may still michael@0: // need to traverse into it, since it may contain placeholders we need michael@0: // to enter to reach other out-of-flow frames that are visible. michael@0: dirty.SetEmpty(); michael@0: } michael@0: pseudoStackingContext = true; michael@0: } michael@0: if (child->Preserves3D()) { michael@0: nsRect* savedDirty = static_cast michael@0: (child->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty())); michael@0: if (savedDirty) { michael@0: dirty = *savedDirty; michael@0: } else { michael@0: dirty.SetEmpty(); michael@0: } michael@0: } michael@0: michael@0: NS_ASSERTION(childType != nsGkAtoms::placeholderFrame, michael@0: "Should have dealt with placeholders already"); michael@0: if (aBuilder->GetSelectedFramesOnly() && michael@0: child->IsLeaf() && michael@0: !aChild->IsSelected()) { michael@0: return; michael@0: } michael@0: michael@0: if (aBuilder->GetIncludeAllOutOfFlows() && michael@0: (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { michael@0: dirty = child->GetVisualOverflowRect(); michael@0: } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { michael@0: // No need to descend into child to catch placeholders for visible michael@0: // positioned stuff. So see if we can short-circuit frame traversal here. michael@0: michael@0: // We can stop if child's frame subtree's intersection with the michael@0: // dirty area is empty. michael@0: // If the child is a scrollframe that we want to ignore, then we need michael@0: // to descend into it because its scrolled child may intersect the dirty michael@0: // area even if the scrollframe itself doesn't. michael@0: // There are cases where the "ignore scroll frame" on the builder is not set michael@0: // correctly, and so we additionally want to catch cases where the child is michael@0: // a root scrollframe and we are ignoring scrolling on the viewport. michael@0: nsIPresShell* shell = PresContext()->PresShell(); michael@0: bool keepDescending = child == aBuilder->GetIgnoreScrollFrame() || michael@0: (shell->IgnoringViewportScrolling() && child == shell->GetRootScrollFrame()); michael@0: if (!keepDescending) { michael@0: nsRect childDirty; michael@0: if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect())) michael@0: return; michael@0: // Usually we could set dirty to childDirty now but there's no michael@0: // benefit, and it can be confusing. It can especially confuse michael@0: // situations where we're going to ignore a scrollframe's clipping; michael@0: // we wouldn't want to clip the dirty area to the scrollframe's michael@0: // bounds in that case. michael@0: } michael@0: } michael@0: michael@0: // XXX need to have inline-block and inline-table set pseudoStackingContext michael@0: michael@0: const nsStyleDisplay* ourDisp = StyleDisplay(); michael@0: // REVIEW: Taken from nsBoxFrame::Paint michael@0: // Don't paint our children if the theme object is a leaf. michael@0: if (IsThemed(ourDisp) && michael@0: !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance)) michael@0: return; michael@0: michael@0: // Child is composited if it's transformed, partially transparent, or has michael@0: // SVG effects or a blend mode.. michael@0: const nsStyleDisplay* disp = child->StyleDisplay(); michael@0: const nsStylePosition* pos = child->StylePosition(); michael@0: bool isVisuallyAtomic = child->HasOpacity() michael@0: || child->IsTransformed() michael@0: // strictly speaking, 'perspective' doesn't require visual atomicity, michael@0: // but the spec says it acts like the rest of these michael@0: || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord michael@0: || disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL michael@0: || nsSVGIntegrationUtils::UsingEffectsForFrame(child); michael@0: michael@0: bool isPositioned = disp->IsPositioned(child); michael@0: bool isStackingContext = michael@0: (isPositioned && (disp->mPosition == NS_STYLE_POSITION_STICKY || michael@0: pos->mZIndex.GetUnit() == eStyleUnit_Integer)) || michael@0: (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || michael@0: isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT); michael@0: michael@0: if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) || michael@0: ((disp->mClipFlags & NS_STYLE_CLIP_RECT) && michael@0: IsSVGContentWithCSSClip(child)) || michael@0: (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || michael@0: (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { michael@0: // If you change this, also change IsPseudoStackingContextFromStyle() michael@0: pseudoStackingContext = true; michael@0: } michael@0: NS_ASSERTION(!isStackingContext || pseudoStackingContext, michael@0: "Stacking contexts must also be pseudo-stacking-contexts"); michael@0: michael@0: bool isInFixedPos = aBuilder->IsInFixedPos() || michael@0: (isPositioned && michael@0: disp->mPosition == NS_STYLE_POSITION_FIXED && michael@0: nsLayoutUtils::IsReallyFixedPos(child)); michael@0: nsDisplayListBuilder::AutoInFixedPosSetter michael@0: buildingInFixedPos(aBuilder, isInFixedPos); michael@0: michael@0: nsDisplayListBuilder::AutoBuildingDisplayList michael@0: buildingForChild(aBuilder, child, pseudoStackingContext); michael@0: DisplayListClipState::AutoClipMultiple clipState(aBuilder); michael@0: CheckForTouchEventHandler(aBuilder, child); michael@0: michael@0: if (savedOutOfFlowData) { michael@0: clipState.SetClipForContainingBlockDescendants( michael@0: &savedOutOfFlowData->mContainingBlockClip); michael@0: } michael@0: michael@0: // Setup clipping for the parent's overflow:-moz-hidden-unscrollable, michael@0: // or overflow:hidden on elements that don't support scrolling (and therefore michael@0: // don't create nsHTML/XULScrollFrame). This clipping needs to not clip michael@0: // anything directly rendered by the parent, only the rendering of its michael@0: // children. michael@0: // Don't use overflowClip to restrict the dirty rect, since some of the michael@0: // descendants may not be clipped by it. Even if we end up with unnecessary michael@0: // display items, they'll be pruned during ComputeVisibility. michael@0: nsIFrame* parent = child->GetParent(); michael@0: const nsStyleDisplay* parentDisp = michael@0: parent == this ? ourDisp : parent->StyleDisplay(); michael@0: ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState); michael@0: michael@0: nsDisplayList list; michael@0: nsDisplayList extraPositionedDescendants; michael@0: if (isStackingContext) { michael@0: if (disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { michael@0: aBuilder->SetContainsBlendMode(true); michael@0: } michael@0: // True stacking context. michael@0: // For stacking contexts, BuildDisplayListForStackingContext handles michael@0: // clipping and MarkAbsoluteFramesForDisplayList. michael@0: child->BuildDisplayListForStackingContext(aBuilder, dirty, &list); michael@0: aBuilder->DisplayCaret(child, dirty, &list); michael@0: } else { michael@0: nsRect clipRect; michael@0: if (ApplyClipPropClipping(aBuilder, child, disp, &clipRect, clipState)) { michael@0: // clipRect is in builder-reference-frame coordinates, michael@0: // dirty/clippedDirtyRect are in child coordinates michael@0: dirty.IntersectRect(dirty, clipRect); michael@0: } michael@0: michael@0: child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); michael@0: michael@0: if (!pseudoStackingContext) { michael@0: // THIS IS THE COMMON CASE. michael@0: // Not a pseudo or real stacking context. Do the simple thing and michael@0: // return early. michael@0: nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions(); michael@0: if (eventRegions) { michael@0: eventRegions->AddFrame(aBuilder, child); michael@0: } michael@0: child->BuildDisplayList(aBuilder, dirty, aLists); michael@0: aBuilder->DisplayCaret(child, dirty, aLists.Content()); michael@0: #ifdef DEBUG michael@0: DisplayDebugBorders(aBuilder, child, aLists); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: // A pseudo-stacking context (e.g., a positioned element with z-index auto). michael@0: // We allow positioned descendants of the child to escape to our parent michael@0: // stacking context's positioned descendant list, because they might be michael@0: // z-index:non-auto michael@0: nsDisplayListCollection pseudoStack; michael@0: if (aBuilder->IsBuildingLayerEventRegions()) { michael@0: nsDisplayLayerEventRegions* eventRegions = michael@0: new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this); michael@0: aBuilder->SetLayerEventRegions(eventRegions); michael@0: pseudoStack.BorderBackground()->AppendNewToTop(eventRegions); michael@0: } michael@0: child->BuildDisplayList(aBuilder, dirty, pseudoStack); michael@0: aBuilder->DisplayCaret(child, dirty, pseudoStack.Content()); michael@0: michael@0: list.AppendToTop(pseudoStack.BorderBackground()); michael@0: list.AppendToTop(pseudoStack.BlockBorderBackgrounds()); michael@0: list.AppendToTop(pseudoStack.Floats()); michael@0: list.AppendToTop(pseudoStack.Content()); michael@0: list.AppendToTop(pseudoStack.Outlines()); michael@0: extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants()); michael@0: #ifdef DEBUG michael@0: DisplayDebugBorders(aBuilder, child, aLists); michael@0: #endif michael@0: } michael@0: michael@0: // Clear clip rect for the construction of the items below. Since we're michael@0: // clipping all their contents, they themselves don't need to be clipped. michael@0: clipState.Clear(); michael@0: michael@0: if (isPositioned || isVisuallyAtomic || michael@0: (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { michael@0: // Genuine stacking contexts, and positioned pseudo-stacking-contexts, michael@0: // go in this level. michael@0: if (!list.IsEmpty()) { michael@0: nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list); michael@0: if (isSVG) { michael@0: aLists.Content()->AppendNewToTop(item); michael@0: } else { michael@0: aLists.PositionedDescendants()->AppendNewToTop(item); michael@0: } michael@0: } michael@0: } else if (!isSVG && disp->IsFloating(child)) { michael@0: if (!list.IsEmpty()) { michael@0: aLists.Floats()->AppendNewToTop(WrapInWrapList(aBuilder, child, &list)); michael@0: } michael@0: } else { michael@0: aLists.Content()->AppendToTop(&list); michael@0: } michael@0: // We delay placing the positioned descendants of positioned frames to here, michael@0: // because in the absence of z-index this is the correct order for them. michael@0: // This doesn't affect correctness because the positioned descendants list michael@0: // is sorted by z-order and content in BuildDisplayListForStackingContext, michael@0: // but it means that sort routine needs to do less work. michael@0: aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect) michael@0: { michael@0: if (IsAbsoluteContainer()) { michael@0: aBuilder->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList(), aDirtyRect); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::GetContentForEvent(WidgetEvent* aEvent, michael@0: nsIContent** aContent) michael@0: { michael@0: nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this); michael@0: *aContent = f->GetContent(); michael@0: NS_IF_ADDREF(*aContent); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsFrame::FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent) michael@0: { michael@0: nsIContent* target = aContent ? aContent : mContent; michael@0: michael@0: if (target) { michael@0: nsRefPtr asyncDispatcher = michael@0: new AsyncEventDispatcher(target, aDOMEventName, true, false); michael@0: DebugOnly rv = asyncDispatcher->PostDOMEvent(); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch"); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::HandleEvent(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: michael@0: if (aEvent->message == NS_MOUSE_MOVE) { michael@0: // XXX If the second argument of HandleDrag() is WidgetMouseEvent, michael@0: // the implementation becomes simpler. michael@0: return HandleDrag(aPresContext, aEvent, aEventStatus); michael@0: } michael@0: michael@0: if ((aEvent->eventStructType == NS_MOUSE_EVENT && michael@0: aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) || michael@0: aEvent->eventStructType == NS_TOUCH_EVENT) { michael@0: if (aEvent->message == NS_MOUSE_BUTTON_DOWN || aEvent->message == NS_TOUCH_START) { michael@0: HandlePress(aPresContext, aEvent, aEventStatus); michael@0: } else if (aEvent->message == NS_MOUSE_BUTTON_UP || aEvent->message == NS_TOUCH_END) { michael@0: HandleRelease(aPresContext, aEvent, aEventStatus); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrame::GetDataForTableSelection(const nsFrameSelection* aFrameSelection, michael@0: nsIPresShell* aPresShell, michael@0: WidgetMouseEvent* aMouseEvent, michael@0: nsIContent** aParentContent, michael@0: int32_t* aContentOffset, michael@0: int32_t* aTarget) michael@0: { michael@0: if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent || !aContentOffset || !aTarget) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *aParentContent = nullptr; michael@0: *aContentOffset = 0; michael@0: *aTarget = 0; michael@0: michael@0: int16_t displaySelection = aPresShell->GetSelectionFlags(); michael@0: michael@0: bool selectingTableCells = aFrameSelection->GetTableCellSelection(); michael@0: michael@0: // DISPLAY_ALL means we're in an editor. michael@0: // If already in cell selection mode, michael@0: // continue selecting with mouse drag or end on mouse up, michael@0: // or when using shift key to extend block of cells michael@0: // (Mouse down does normal selection unless Ctrl/Cmd is pressed) michael@0: bool doTableSelection = michael@0: displaySelection == nsISelectionDisplay::DISPLAY_ALL && selectingTableCells && michael@0: (aMouseEvent->message == NS_MOUSE_MOVE || michael@0: (aMouseEvent->message == NS_MOUSE_BUTTON_UP && michael@0: aMouseEvent->button == WidgetMouseEvent::eLeftButton) || michael@0: aMouseEvent->IsShift()); michael@0: michael@0: if (!doTableSelection) michael@0: { michael@0: // In Browser, special 'table selection' key must be pressed for table selection michael@0: // or when just Shift is pressed and we're already in table/cell selection mode michael@0: #ifdef XP_MACOSX michael@0: doTableSelection = aMouseEvent->IsMeta() || (aMouseEvent->IsShift() && selectingTableCells); michael@0: #else michael@0: doTableSelection = aMouseEvent->IsControl() || (aMouseEvent->IsShift() && selectingTableCells); michael@0: #endif michael@0: } michael@0: if (!doTableSelection) michael@0: return NS_OK; michael@0: michael@0: // Get the cell frame or table frame (or parent) of the current content node michael@0: nsIFrame *frame = this; michael@0: bool foundCell = false; michael@0: bool foundTable = false; michael@0: michael@0: // Get the limiting node to stop parent frame search michael@0: nsIContent* limiter = aFrameSelection->GetLimiter(); michael@0: michael@0: // If our content node is an ancestor of the limiting node, michael@0: // we should stop the search right now. michael@0: if (limiter && nsContentUtils::ContentIsDescendantOf(limiter, GetContent())) michael@0: return NS_OK; michael@0: michael@0: //We don't initiate row/col selection from here now, michael@0: // but we may in future michael@0: //bool selectColumn = false; michael@0: //bool selectRow = false; michael@0: michael@0: while (frame) michael@0: { michael@0: // Check for a table cell by querying to a known CellFrame interface michael@0: nsITableCellLayout *cellElement = do_QueryFrame(frame); michael@0: if (cellElement) michael@0: { michael@0: foundCell = true; michael@0: //TODO: If we want to use proximity to top or left border michael@0: // for row and column selection, this is the place to do it michael@0: break; michael@0: } michael@0: else michael@0: { michael@0: // If not a cell, check for table michael@0: // This will happen when starting frame is the table or child of a table, michael@0: // such as a row (we were inbetween cells or in table border) michael@0: nsTableOuterFrame *tableFrame = do_QueryFrame(frame); michael@0: if (tableFrame) michael@0: { michael@0: foundTable = true; michael@0: //TODO: How can we select row when along left table edge michael@0: // or select column when along top edge? michael@0: break; michael@0: } else { michael@0: frame = frame->GetParent(); michael@0: // Stop if we have hit the selection's limiting content node michael@0: if (frame && frame->GetContent() == limiter) michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: // We aren't in a cell or table michael@0: if (!foundCell && !foundTable) return NS_OK; michael@0: michael@0: nsIContent* tableOrCellContent = frame->GetContent(); michael@0: if (!tableOrCellContent) return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr parentContent = tableOrCellContent->GetParent(); michael@0: if (!parentContent) return NS_ERROR_FAILURE; michael@0: michael@0: int32_t offset = parentContent->IndexOf(tableOrCellContent); michael@0: // Not likely? michael@0: if (offset < 0) return NS_ERROR_FAILURE; michael@0: michael@0: // Everything is OK -- set the return values michael@0: *aParentContent = parentContent; michael@0: NS_ADDREF(*aParentContent); michael@0: michael@0: *aContentOffset = offset; michael@0: michael@0: #if 0 michael@0: if (selectRow) michael@0: *aTarget = nsISelectionPrivate::TABLESELECTION_ROW; michael@0: else if (selectColumn) michael@0: *aTarget = nsISelectionPrivate::TABLESELECTION_COLUMN; michael@0: else michael@0: #endif michael@0: if (foundCell) michael@0: *aTarget = nsISelectionPrivate::TABLESELECTION_CELL; michael@0: else if (foundTable) michael@0: *aTarget = nsISelectionPrivate::TABLESELECTION_TABLE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::IsSelectable(bool* aSelectable, uint8_t* aSelectStyle) const michael@0: { michael@0: if (!aSelectable) //it's ok if aSelectStyle is null michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: // Like 'visibility', we must check all the parents: if a parent michael@0: // is not selectable, none of its children is selectable. michael@0: // michael@0: // The -moz-all value acts similarly: if a frame has 'user-select:-moz-all', michael@0: // all its children are selectable, even those with 'user-select:none'. michael@0: // michael@0: // As a result, if 'none' and '-moz-all' are not present in the frame hierarchy, michael@0: // aSelectStyle returns the first style that is not AUTO. If these values michael@0: // are present in the frame hierarchy, aSelectStyle returns the style of the michael@0: // topmost parent that has either 'none' or '-moz-all'. michael@0: // michael@0: // For instance, if the frame hierarchy is: michael@0: // AUTO -> _MOZ_ALL -> NONE -> TEXT, the returned value is _MOZ_ALL michael@0: // TEXT -> NONE -> AUTO -> _MOZ_ALL, the returned value is TEXT michael@0: // _MOZ_ALL -> TEXT -> AUTO -> AUTO, the returned value is _MOZ_ALL michael@0: // AUTO -> CELL -> TEXT -> AUTO, the returned value is TEXT michael@0: // michael@0: uint8_t selectStyle = NS_STYLE_USER_SELECT_AUTO; michael@0: nsIFrame* frame = const_cast(this); michael@0: michael@0: while (frame) { michael@0: const nsStyleUIReset* userinterface = frame->StyleUIReset(); michael@0: switch (userinterface->mUserSelect) { michael@0: case NS_STYLE_USER_SELECT_ALL: michael@0: case NS_STYLE_USER_SELECT_MOZ_ALL: michael@0: // override the previous values michael@0: selectStyle = userinterface->mUserSelect; michael@0: break; michael@0: default: michael@0: // otherwise return the first value which is not 'auto' michael@0: if (selectStyle == NS_STYLE_USER_SELECT_AUTO) { michael@0: selectStyle = userinterface->mUserSelect; michael@0: } michael@0: break; michael@0: } michael@0: frame = frame->GetParent(); michael@0: } michael@0: michael@0: // convert internal values to standard values michael@0: if (selectStyle == NS_STYLE_USER_SELECT_AUTO) michael@0: selectStyle = NS_STYLE_USER_SELECT_TEXT; michael@0: else michael@0: if (selectStyle == NS_STYLE_USER_SELECT_MOZ_ALL) michael@0: selectStyle = NS_STYLE_USER_SELECT_ALL; michael@0: michael@0: // return stuff michael@0: if (aSelectStyle) michael@0: *aSelectStyle = selectStyle; michael@0: if (mState & NS_FRAME_GENERATED_CONTENT) michael@0: *aSelectable = false; michael@0: else michael@0: *aSelectable = (selectStyle != NS_STYLE_USER_SELECT_NONE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Handles the Mouse Press Event for the frame michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsFrame::HandlePress(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aEventStatus); michael@0: if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ENSURE_ARG_POINTER(aEvent); michael@0: if (aEvent->eventStructType == NS_TOUCH_EVENT) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: //We often get out of sync state issues with mousedown events that michael@0: //get interrupted by alerts/dialogs. michael@0: //Check with the ESM to see if we should process this one michael@0: if (!aPresContext->EventStateManager()->EventStatusOK(aEvent)) michael@0: return NS_OK; michael@0: michael@0: nsresult rv; michael@0: nsIPresShell *shell = aPresContext->GetPresShell(); michael@0: if (!shell) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // if we are in Navigator and the click is in a draggable node, we don't want michael@0: // to start selection because we don't want to interfere with a potential michael@0: // drag of said node and steal all its glory. michael@0: int16_t isEditor = shell->GetSelectionFlags(); michael@0: //weaaak. only the editor can display frame selection not just text and images michael@0: isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL; michael@0: michael@0: WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); michael@0: michael@0: if (!mouseEvent->IsAlt()) { michael@0: for (nsIContent* content = mContent; content; michael@0: content = content->GetParent()) { michael@0: if (nsContentUtils::ContentIsDraggable(content) && michael@0: !content->IsEditable()) { michael@0: // coordinate stuff is the fix for bug #55921 michael@0: if ((mRect - GetPosition()).Contains( michael@0: nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this))) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // check whether style allows selection michael@0: // if not, don't tell selection the mouse event even occurred. michael@0: bool selectable; michael@0: uint8_t selectStyle; michael@0: rv = IsSelectable(&selectable, &selectStyle); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // check for select: none michael@0: if (!selectable) michael@0: return NS_OK; michael@0: michael@0: // When implementing NS_STYLE_USER_SELECT_ELEMENT, NS_STYLE_USER_SELECT_ELEMENTS and michael@0: // NS_STYLE_USER_SELECT_TOGGLE, need to change this logic michael@0: bool useFrameSelection = (selectStyle == NS_STYLE_USER_SELECT_TEXT); michael@0: michael@0: // If the mouse is dragged outside the nearest enclosing scrollable area michael@0: // while making a selection, the area will be scrolled. To do this, capture michael@0: // the mouse on the nearest scrollable frame. If there isn't a scrollable michael@0: // frame, or something else is already capturing the mouse, there's no michael@0: // reason to capture. michael@0: bool hasCapturedContent = false; michael@0: if (!nsIPresShell::GetCapturingContent()) { michael@0: nsIScrollableFrame* scrollFrame = michael@0: nsLayoutUtils::GetNearestScrollableFrame(this, michael@0: nsLayoutUtils::SCROLLABLE_SAME_DOC | michael@0: nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); michael@0: if (scrollFrame) { michael@0: nsIFrame* capturingFrame = do_QueryFrame(scrollFrame); michael@0: nsIPresShell::SetCapturingContent(capturingFrame->GetContent(), michael@0: CAPTURE_IGNOREALLOWED); michael@0: hasCapturedContent = true; michael@0: } michael@0: } michael@0: michael@0: // XXX This is screwy; it really should use the selection frame, not the michael@0: // event frame michael@0: const nsFrameSelection* frameselection = nullptr; michael@0: if (useFrameSelection) michael@0: frameselection = GetConstFrameSelection(); michael@0: else michael@0: frameselection = shell->ConstFrameSelection(); michael@0: michael@0: if (!frameselection || frameselection->GetDisplaySelection() == nsISelectionController::SELECTION_OFF) michael@0: return NS_OK;//nothing to do we cannot affect selection from here michael@0: michael@0: #ifdef XP_MACOSX michael@0: if (mouseEvent->IsControl()) michael@0: return NS_OK;//short circuit. hard coded for mac due to time restraints. michael@0: bool control = mouseEvent->IsMeta(); michael@0: #else michael@0: bool control = mouseEvent->IsControl(); michael@0: #endif michael@0: michael@0: nsRefPtr fc = const_cast(frameselection); michael@0: if (mouseEvent->clickCount > 1) { michael@0: // These methods aren't const but can't actually delete anything, michael@0: // so no need for nsWeakFrame. michael@0: fc->SetMouseDownState(true); michael@0: fc->SetMouseDoubleDown(true); michael@0: return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control); michael@0: } michael@0: michael@0: nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); michael@0: ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN); michael@0: michael@0: if (!offsets.content) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // On touchables devices, touch the screen is usually a pan action, michael@0: // so let's reposition the caret if needed but do not select text michael@0: // if the touch did not happen over an editable element. Otherwise, michael@0: // let the user move the caret by tapping and dragging. michael@0: if (!offsets.content->IsEditable() && michael@0: Preferences::GetBool("browser.ignoreNativeFrameTextSelection", false)) { michael@0: // On touchables devices, mouse events are generated if the gesture is a tap. michael@0: // Such events are never going to generate a drag action, so let's release michael@0: // captured content if any. michael@0: if (hasCapturedContent) { michael@0: nsIPresShell::SetCapturingContent(nullptr, 0); michael@0: } michael@0: michael@0: return fc->HandleClick(offsets.content, offsets.StartOffset(), michael@0: offsets.EndOffset(), false, false, michael@0: offsets.associateWithNext); michael@0: } michael@0: michael@0: // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation michael@0: nsCOMPtrparentContent; michael@0: int32_t contentOffset; michael@0: int32_t target; michael@0: rv = GetDataForTableSelection(frameselection, shell, mouseEvent, michael@0: getter_AddRefs(parentContent), &contentOffset, michael@0: &target); michael@0: if (NS_SUCCEEDED(rv) && parentContent) michael@0: { michael@0: fc->SetMouseDownState(true); michael@0: return fc->HandleTableSelection(parentContent, contentOffset, target, michael@0: mouseEvent); michael@0: } michael@0: michael@0: fc->SetDelayedCaretData(0); michael@0: michael@0: // Check if any part of this frame is selected, and if the michael@0: // user clicked inside the selected region. If so, we delay michael@0: // starting a new selection since the user may be trying to michael@0: // drag the selected region to some other app. michael@0: michael@0: SelectionDetails *details = 0; michael@0: if (GetContent()->IsSelectionDescendant()) michael@0: { michael@0: bool inSelection = false; michael@0: details = frameselection->LookUpSelection(offsets.content, 0, michael@0: offsets.EndOffset(), false); michael@0: michael@0: // michael@0: // If there are any details, check to see if the user clicked michael@0: // within any selected region of the frame. michael@0: // michael@0: michael@0: SelectionDetails *curDetail = details; michael@0: michael@0: while (curDetail) michael@0: { michael@0: // michael@0: // If the user clicked inside a selection, then just michael@0: // return without doing anything. We will handle placing michael@0: // the caret later on when the mouse is released. We ignore michael@0: // the spellcheck, find and url formatting selections. michael@0: // michael@0: if (curDetail->mType != nsISelectionController::SELECTION_SPELLCHECK && michael@0: curDetail->mType != nsISelectionController::SELECTION_FIND && michael@0: curDetail->mType != nsISelectionController::SELECTION_URLSECONDARY && michael@0: curDetail->mStart <= offsets.StartOffset() && michael@0: offsets.EndOffset() <= curDetail->mEnd) michael@0: { michael@0: inSelection = true; michael@0: } michael@0: michael@0: SelectionDetails *nextDetail = curDetail->mNext; michael@0: delete curDetail; michael@0: curDetail = nextDetail; michael@0: } michael@0: michael@0: if (inSelection) { michael@0: fc->SetMouseDownState(false); michael@0: fc->SetDelayedCaretData(mouseEvent); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: fc->SetMouseDownState(true); michael@0: michael@0: // Do not touch any nsFrame members after this point without adding michael@0: // weakFrame checks. michael@0: rv = fc->HandleClick(offsets.content, offsets.StartOffset(), michael@0: offsets.EndOffset(), mouseEvent->IsShift(), control, michael@0: offsets.associateWithNext); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (offsets.offset != offsets.secondaryOffset) michael@0: fc->MaintainSelection(); michael@0: michael@0: if (isEditor && !mouseEvent->IsShift() && michael@0: (offsets.EndOffset() - offsets.StartOffset()) == 1) michael@0: { michael@0: // A single node is selected and we aren't extending an existing michael@0: // selection, which means the user clicked directly on an object (either michael@0: // -moz-user-select: all or a non-text node without children). michael@0: // Therefore, disable selection extension during mouse moves. michael@0: // XXX This is a bit hacky; shouldn't editor be able to deal with this? michael@0: fc->SetMouseDownState(false); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * SelectByTypeAtPoint michael@0: * michael@0: * Search for selectable content at point and attempt to select michael@0: * based on the start and end selection behaviours. michael@0: * michael@0: * @param aPresContext Presentation context michael@0: * @param aPoint Point at which selection will occur. Coordinates michael@0: * should be relaitve to this frame. michael@0: * @param aBeginAmountType, aEndAmountType Selection behavior, see michael@0: * nsIFrame for definitions. michael@0: * @param aSelectFlags Selection flags defined in nsFame.h. michael@0: * @return success or failure at finding suitable content to select. michael@0: */ michael@0: nsresult michael@0: nsFrame::SelectByTypeAtPoint(nsPresContext* aPresContext, michael@0: const nsPoint& aPoint, michael@0: nsSelectionAmount aBeginAmountType, michael@0: nsSelectionAmount aEndAmountType, michael@0: uint32_t aSelectFlags) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPresContext); michael@0: michael@0: // No point in selecting if selection is turned off michael@0: if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) michael@0: return NS_OK; michael@0: michael@0: ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN); michael@0: if (!offsets.content) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: int32_t offset; michael@0: const nsFrameSelection* frameSelection = michael@0: PresContext()->GetPresShell()->ConstFrameSelection(); michael@0: nsIFrame* theFrame = frameSelection-> michael@0: GetFrameForNodeOffset(offsets.content, offsets.offset, michael@0: nsFrameSelection::HINT(offsets.associateWithNext), michael@0: &offset); michael@0: if (!theFrame) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsFrame* frame = static_cast(theFrame); michael@0: return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType, michael@0: offset, aPresContext, michael@0: aBeginAmountType != eSelectWord, michael@0: aSelectFlags); michael@0: } michael@0: michael@0: /** michael@0: * Multiple Mouse Press -- line or paragraph selection -- for the frame. michael@0: * Wouldn't it be nice if this didn't have to be hardwired into Frame code? michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsFrame::HandleMultiplePress(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus, michael@0: bool aControlHeld) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aEvent); michael@0: NS_ENSURE_ARG_POINTER(aEventStatus); michael@0: michael@0: if (nsEventStatus_eConsumeNoDefault == *aEventStatus || michael@0: DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Find out whether we're doing line or paragraph selection. michael@0: // If browser.triple_click_selects_paragraph is true, triple-click selects paragraph. michael@0: // Otherwise, triple-click selects line, and quadruple-click selects paragraph michael@0: // (on platforms that support quadruple-click). michael@0: nsSelectionAmount beginAmount, endAmount; michael@0: WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); michael@0: if (!mouseEvent) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mouseEvent->clickCount == 4) { michael@0: beginAmount = endAmount = eSelectParagraph; michael@0: } else if (mouseEvent->clickCount == 3) { michael@0: if (Preferences::GetBool("browser.triple_click_selects_paragraph")) { michael@0: beginAmount = endAmount = eSelectParagraph; michael@0: } else { michael@0: beginAmount = eSelectBeginLine; michael@0: endAmount = eSelectEndLine; michael@0: } michael@0: } else if (mouseEvent->clickCount == 2) { michael@0: // We only want inline frames; PeekBackwardAndForward dislikes blocks michael@0: beginAmount = endAmount = eSelectWord; michael@0: } else { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsPoint relPoint = michael@0: nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); michael@0: return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount, michael@0: (aControlHeld ? SELECT_ACCUMULATE : 0)); michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack, michael@0: nsSelectionAmount aAmountForward, michael@0: int32_t aStartPos, michael@0: nsPresContext* aPresContext, michael@0: bool aJumpLines, michael@0: uint32_t aSelectFlags) michael@0: { michael@0: nsIFrame* baseFrame = this; michael@0: int32_t baseOffset = aStartPos; michael@0: nsresult rv; michael@0: michael@0: if (aAmountBack == eSelectWord) { michael@0: // To avoid selecting the previous word when at start of word, michael@0: // first move one character forward. michael@0: nsPeekOffsetStruct pos(eSelectCharacter, michael@0: eDirNext, michael@0: aStartPos, michael@0: 0, michael@0: aJumpLines, michael@0: true, //limit on scrolled views michael@0: false, michael@0: false); michael@0: rv = PeekOffset(&pos); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: baseFrame = pos.mResultFrame; michael@0: baseOffset = pos.mContentOffset; michael@0: } michael@0: } michael@0: michael@0: // Use peek offset one way then the other: michael@0: nsPeekOffsetStruct startpos(aAmountBack, michael@0: eDirPrevious, michael@0: baseOffset, michael@0: 0, michael@0: aJumpLines, michael@0: true, //limit on scrolled views michael@0: false, michael@0: false); michael@0: rv = baseFrame->PeekOffset(&startpos); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsPeekOffsetStruct endpos(aAmountForward, michael@0: eDirNext, michael@0: aStartPos, michael@0: 0, michael@0: aJumpLines, michael@0: true, //limit on scrolled views michael@0: false, michael@0: false); michael@0: rv = PeekOffset(&endpos); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // Keep frameSelection alive. michael@0: nsRefPtr frameSelection = GetFrameSelection(); michael@0: michael@0: rv = frameSelection->HandleClick(startpos.mResultContent, michael@0: startpos.mContentOffset, startpos.mContentOffset, michael@0: false, (aSelectFlags & SELECT_ACCUMULATE), michael@0: nsFrameSelection::HINTRIGHT); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = frameSelection->HandleClick(endpos.mResultContent, michael@0: endpos.mContentOffset, endpos.mContentOffset, michael@0: true, false, michael@0: nsFrameSelection::HINTLEFT); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // maintain selection michael@0: return frameSelection->MaintainSelection(aAmountBack); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFrame::HandleDrag(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: MOZ_ASSERT(aEvent->eventStructType == NS_MOUSE_EVENT, "HandleDrag can only handle mouse event"); michael@0: michael@0: bool selectable; michael@0: IsSelectable(&selectable, nullptr); michael@0: michael@0: // XXX Do we really need to exclude non-selectable content here? michael@0: // GetContentOffsetsFromPoint can handle it just fine, although some michael@0: // other stuff might not like it. michael@0: if (!selectable) michael@0: return NS_OK; michael@0: if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) { michael@0: return NS_OK; michael@0: } michael@0: nsIPresShell *presShell = aPresContext->PresShell(); michael@0: michael@0: nsRefPtr frameselection = GetFrameSelection(); michael@0: bool mouseDown = frameselection->GetMouseDownState(); michael@0: if (!mouseDown) michael@0: return NS_OK; michael@0: michael@0: frameselection->StopAutoScrollTimer(); michael@0: michael@0: // Check if we are dragging in a table cell michael@0: nsCOMPtr parentContent; michael@0: int32_t contentOffset; michael@0: int32_t target; michael@0: WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); michael@0: nsresult result; michael@0: result = GetDataForTableSelection(frameselection, presShell, mouseEvent, michael@0: getter_AddRefs(parentContent), michael@0: &contentOffset, &target); michael@0: michael@0: nsWeakFrame weakThis = this; michael@0: if (NS_SUCCEEDED(result) && parentContent) { michael@0: frameselection->HandleTableSelection(parentContent, contentOffset, target, michael@0: mouseEvent); michael@0: } else { michael@0: nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this); michael@0: frameselection->HandleDrag(this, pt); michael@0: } michael@0: michael@0: // The frameselection object notifies selection listeners synchronously above michael@0: // which might have killed us. michael@0: if (!weakThis.IsAlive()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // get the nearest scrollframe michael@0: nsIScrollableFrame* scrollFrame = michael@0: nsLayoutUtils::GetNearestScrollableFrame(this, michael@0: nsLayoutUtils::SCROLLABLE_SAME_DOC | michael@0: nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); michael@0: michael@0: if (scrollFrame) { michael@0: nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame(); michael@0: if (capturingFrame) { michael@0: nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, michael@0: capturingFrame); michael@0: frameselection->StartAutoScrollTimer(capturingFrame, pt, 30); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * This static method handles part of the nsFrame::HandleRelease in a way michael@0: * which doesn't rely on the nsFrame object to stay alive. michael@0: */ michael@0: static nsresult michael@0: HandleFrameSelection(nsFrameSelection* aFrameSelection, michael@0: nsIFrame::ContentOffsets& aOffsets, michael@0: bool aHandleTableSel, michael@0: int32_t aContentOffsetForTableSel, michael@0: int32_t aTargetForTableSel, michael@0: nsIContent* aParentContentForTableSel, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: if (!aFrameSelection) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (nsEventStatus_eConsumeNoDefault != *aEventStatus) { michael@0: if (!aHandleTableSel) { michael@0: if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // We are doing this to simulate what we would have done on HandlePress. michael@0: // We didn't do it there to give the user an opportunity to drag michael@0: // the text, but since they didn't drag, we want to place the michael@0: // caret. michael@0: // However, we'll use the mouse position from the release, since: michael@0: // * it's easier michael@0: // * that's the normal click position to use (although really, in michael@0: // the normal case, small movements that don't count as a drag michael@0: // can do selection) michael@0: aFrameSelection->SetMouseDownState(true); michael@0: michael@0: rv = aFrameSelection->HandleClick(aOffsets.content, michael@0: aOffsets.StartOffset(), michael@0: aOffsets.EndOffset(), michael@0: aFrameSelection->IsShiftDownInDelayedCaretData(), michael@0: false, michael@0: aOffsets.associateWithNext); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } else if (aParentContentForTableSel) { michael@0: aFrameSelection->SetMouseDownState(false); michael@0: rv = aFrameSelection->HandleTableSelection( michael@0: aParentContentForTableSel, michael@0: aContentOffsetForTableSel, michael@0: aTargetForTableSel, michael@0: aEvent->AsMouseEvent()); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: aFrameSelection->SetDelayedCaretData(0); michael@0: } michael@0: michael@0: aFrameSelection->SetMouseDownState(false); michael@0: aFrameSelection->StopAutoScrollTimer(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFrame::HandleRelease(nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: if (aEvent->eventStructType != NS_MOUSE_EVENT) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this); michael@0: michael@0: nsCOMPtr captureContent = nsIPresShell::GetCapturingContent(); michael@0: michael@0: // We can unconditionally stop capturing because michael@0: // we should never be capturing when the mouse button is up michael@0: nsIPresShell::SetCapturingContent(nullptr, 0); michael@0: michael@0: bool selectionOff = michael@0: (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF); michael@0: michael@0: nsRefPtr frameselection; michael@0: ContentOffsets offsets; michael@0: nsCOMPtr parentContent; michael@0: int32_t contentOffsetForTableSel = 0; michael@0: int32_t targetForTableSel = 0; michael@0: bool handleTableSelection = true; michael@0: michael@0: if (!selectionOff) { michael@0: frameselection = GetFrameSelection(); michael@0: if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) { michael@0: // Check if the frameselection recorded the mouse going down. michael@0: // If not, the user must have clicked in a part of the selection. michael@0: // Place the caret before continuing! michael@0: michael@0: bool mouseDown = frameselection->GetMouseDownState(); michael@0: michael@0: if (!mouseDown && frameselection->HasDelayedCaretData() && michael@0: frameselection->GetClickCountInDelayedCaretData() < 2) { michael@0: nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); michael@0: offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN); michael@0: handleTableSelection = false; michael@0: } else { michael@0: GetDataForTableSelection(frameselection, PresContext()->PresShell(), michael@0: aEvent->AsMouseEvent(), michael@0: getter_AddRefs(parentContent), michael@0: &contentOffsetForTableSel, michael@0: &targetForTableSel); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // We might be capturing in some other document and the event just happened to michael@0: // trickle down here. Make sure that document's frame selection is notified. michael@0: // Note, this may cause the current nsFrame object to be deleted, bug 336592. michael@0: nsRefPtr frameSelection; michael@0: if (activeFrame != this && michael@0: static_cast(activeFrame)->DisplaySelection(activeFrame->PresContext()) michael@0: != nsISelectionController::SELECTION_OFF) { michael@0: frameSelection = activeFrame->GetFrameSelection(); michael@0: } michael@0: michael@0: // Also check the selection of the capturing content which might be in a michael@0: // different document. michael@0: if (!frameSelection && captureContent) { michael@0: nsIDocument* doc = captureContent->GetCurrentDoc(); michael@0: if (doc) { michael@0: nsIPresShell* capturingShell = doc->GetShell(); michael@0: if (capturingShell && capturingShell != PresContext()->GetPresShell()) { michael@0: frameSelection = capturingShell->FrameSelection(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (frameSelection) { michael@0: frameSelection->SetMouseDownState(false); michael@0: frameSelection->StopAutoScrollTimer(); michael@0: } michael@0: michael@0: // Do not call any methods of the current object after this point!!! michael@0: // The object is perhaps dead! michael@0: michael@0: return selectionOff michael@0: ? NS_OK michael@0: : HandleFrameSelection(frameselection, offsets, handleTableSelection, michael@0: contentOffsetForTableSel, targetForTableSel, michael@0: parentContent, aEvent, aEventStatus); michael@0: } michael@0: michael@0: struct MOZ_STACK_CLASS FrameContentRange { michael@0: FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd) : michael@0: content(aContent), start(aStart), end(aEnd) { } michael@0: nsCOMPtr content; michael@0: int32_t start; michael@0: int32_t end; michael@0: }; michael@0: michael@0: // Retrieve the content offsets of a frame michael@0: static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) { michael@0: nsCOMPtr content, parent; michael@0: content = aFrame->GetContent(); michael@0: if (!content) { michael@0: NS_WARNING("Frame has no content"); michael@0: return FrameContentRange(nullptr, -1, -1); michael@0: } michael@0: nsIAtom* type = aFrame->GetType(); michael@0: if (type == nsGkAtoms::textFrame) { michael@0: int32_t offset, offsetEnd; michael@0: aFrame->GetOffsets(offset, offsetEnd); michael@0: return FrameContentRange(content, offset, offsetEnd); michael@0: } michael@0: if (type == nsGkAtoms::brFrame) { michael@0: parent = content->GetParent(); michael@0: int32_t beginOffset = parent->IndexOf(content); michael@0: return FrameContentRange(parent, beginOffset, beginOffset); michael@0: } michael@0: // Loop to deal with anonymous content, which has no index; this loop michael@0: // probably won't run more than twice under normal conditions michael@0: do { michael@0: parent = content->GetParent(); michael@0: if (parent) { michael@0: int32_t beginOffset = parent->IndexOf(content); michael@0: if (beginOffset >= 0) michael@0: return FrameContentRange(parent, beginOffset, beginOffset + 1); michael@0: content = parent; michael@0: } michael@0: } while (parent); michael@0: michael@0: // The root content node must act differently michael@0: return FrameContentRange(content, 0, content->GetChildCount()); michael@0: } michael@0: michael@0: // The FrameTarget represents the closest frame to a point that can be selected michael@0: // The frame is the frame represented, frameEdge says whether one end of the michael@0: // frame is the result (in which case different handling is needed), and michael@0: // afterFrame says which end is repersented if frameEdge is true michael@0: struct FrameTarget { michael@0: FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame, michael@0: bool aEmptyBlock = false) : michael@0: frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame), michael@0: emptyBlock(aEmptyBlock) { } michael@0: static FrameTarget Null() { michael@0: return FrameTarget(nullptr, false, false); michael@0: } michael@0: bool IsNull() { michael@0: return !frame; michael@0: } michael@0: nsIFrame* frame; michael@0: bool frameEdge; michael@0: bool afterFrame; michael@0: bool emptyBlock; michael@0: }; michael@0: michael@0: // See function implementation for information michael@0: static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint, michael@0: uint32_t aFlags); michael@0: michael@0: static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags) michael@0: { michael@0: if ((aFlags & nsIFrame::SKIP_HIDDEN) && michael@0: !aFrame->StyleVisibility()->IsVisible()) { michael@0: return false; michael@0: } michael@0: return !aFrame->IsGeneratedContentFrame() && michael@0: aFrame->StyleUIReset()->mUserSelect != NS_STYLE_USER_SELECT_NONE; michael@0: } michael@0: michael@0: static bool SelectionDescendToKids(nsIFrame* aFrame) { michael@0: uint8_t style = aFrame->StyleUIReset()->mUserSelect; michael@0: nsIFrame* parent = aFrame->GetParent(); michael@0: // If we are only near (not directly over) then don't traverse michael@0: // frames with independent selection (e.g. text and list controls) michael@0: // unless we're already inside such a frame (see bug 268497). Note that this michael@0: // prevents any of the users of this method from entering form controls. michael@0: // XXX We might want some way to allow using the up-arrow to go into a form michael@0: // control, but the focus didn't work right anyway; it'd probably be enough michael@0: // if the left and right arrows could enter textboxes (which I don't believe michael@0: // they can at the moment) michael@0: return !aFrame->IsGeneratedContentFrame() && michael@0: style != NS_STYLE_USER_SELECT_ALL && michael@0: style != NS_STYLE_USER_SELECT_NONE && michael@0: ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) || michael@0: !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)); michael@0: } michael@0: michael@0: static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild, michael@0: nsPoint aPoint, michael@0: uint32_t aFlags) michael@0: { michael@0: nsIFrame* parent = aChild->GetParent(); michael@0: if (SelectionDescendToKids(aChild)) { michael@0: nsPoint pt = aPoint - aChild->GetOffsetTo(parent); michael@0: return GetSelectionClosestFrame(aChild, pt, aFlags); michael@0: } michael@0: return FrameTarget(aChild, false, false); michael@0: } michael@0: michael@0: // When the cursor needs to be at the beginning of a block, it shouldn't be michael@0: // before the first child. A click on a block whose first child is a block michael@0: // should put the cursor in the child. The cursor shouldn't be between the michael@0: // blocks, because that's not where it's expected. michael@0: // Note that this method is guaranteed to succeed. michael@0: static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, michael@0: bool aEndFrame, uint32_t aFlags) { michael@0: if (SelectionDescendToKids(aFrame)) { michael@0: nsIFrame* result = nullptr; michael@0: nsIFrame *frame = aFrame->GetFirstPrincipalChild(); michael@0: if (!aEndFrame) { michael@0: while (frame && (!SelfIsSelectable(frame, aFlags) || michael@0: frame->IsEmpty())) michael@0: frame = frame->GetNextSibling(); michael@0: if (frame) michael@0: result = frame; michael@0: } else { michael@0: // Because the frame tree is singly linked, to find the last frame, michael@0: // we have to iterate through all the frames michael@0: // XXX I have a feeling this could be slow for long blocks, although michael@0: // I can't find any slowdowns michael@0: while (frame) { michael@0: if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags)) michael@0: result = frame; michael@0: frame = frame->GetNextSibling(); michael@0: } michael@0: } michael@0: if (result) michael@0: return DrillDownToSelectionFrame(result, aEndFrame, aFlags); michael@0: } michael@0: // If the current frame has no targetable children, target the current frame michael@0: return FrameTarget(aFrame, true, aEndFrame); michael@0: } michael@0: michael@0: // This method finds the closest valid FrameTarget on a given line; if there is michael@0: // no valid FrameTarget on the line, it returns a null FrameTarget michael@0: static FrameTarget GetSelectionClosestFrameForLine( michael@0: nsBlockFrame* aParent, michael@0: nsBlockFrame::line_iterator aLine, michael@0: nsPoint aPoint, michael@0: uint32_t aFlags) michael@0: { michael@0: nsIFrame *frame = aLine->mFirstChild; michael@0: // Account for end of lines (any iterator from the block is valid) michael@0: if (aLine == aParent->end_lines()) michael@0: return DrillDownToSelectionFrame(aParent, true, aFlags); michael@0: nsIFrame *closestFromIStart = nullptr, *closestFromIEnd = nullptr; michael@0: nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd(); michael@0: WritingMode wm = aLine->mWritingMode; michael@0: LogicalPoint pt(wm, aPoint, aLine->mContainerWidth); michael@0: for (int32_t n = aLine->GetChildCount(); n; michael@0: --n, frame = frame->GetNextSibling()) { michael@0: if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty()) michael@0: continue; michael@0: LogicalRect frameRect = LogicalRect(wm, frame->GetRect(), michael@0: aLine->mContainerWidth); michael@0: if (pt.I(wm) >= frameRect.IStart(wm)) { michael@0: if (pt.I(wm) < frameRect.IEnd(wm)) { michael@0: return GetSelectionClosestFrameForChild(frame, aPoint, aFlags); michael@0: } michael@0: if (frameRect.IEnd(wm) >= closestIStart) { michael@0: closestFromIStart = frame; michael@0: closestIStart = frameRect.IEnd(wm); michael@0: } michael@0: } else { michael@0: if (frameRect.IStart(wm) <= closestIEnd) { michael@0: closestFromIEnd = frame; michael@0: closestIEnd = frameRect.IStart(wm); michael@0: } michael@0: } michael@0: } michael@0: if (!closestFromIStart && !closestFromIEnd) { michael@0: // We should only get here if there are no selectable frames on a line michael@0: // XXX Do we need more elaborate handling here? michael@0: return FrameTarget::Null(); michael@0: } michael@0: if (closestFromIStart && michael@0: (!closestFromIEnd || michael@0: (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) { michael@0: return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, michael@0: aFlags); michael@0: } michael@0: return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags); michael@0: } michael@0: michael@0: // This method is for the special handling we do for block frames; they're michael@0: // special because they represent paragraphs and because they are organized michael@0: // into lines, which have bounds that are not stored elsewhere in the michael@0: // frame tree. Returns a null FrameTarget for frames which are not michael@0: // blocks or blocks with no lines except editable one. michael@0: static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame, michael@0: nsPoint aPoint, michael@0: uint32_t aFlags) michael@0: { michael@0: nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aFrame); // used only for QI michael@0: if (!bf) michael@0: return FrameTarget::Null(); michael@0: michael@0: // This code searches for the correct line michael@0: nsBlockFrame::line_iterator firstLine = bf->begin_lines(); michael@0: nsBlockFrame::line_iterator end = bf->end_lines(); michael@0: if (firstLine == end) { michael@0: nsIContent *blockContent = aFrame->GetContent(); michael@0: if (blockContent) { michael@0: // Return with empty flag true. michael@0: return FrameTarget(aFrame, false, false, true); michael@0: } michael@0: return FrameTarget::Null(); michael@0: } michael@0: nsBlockFrame::line_iterator curLine = firstLine; michael@0: nsBlockFrame::line_iterator closestLine = end; michael@0: // Convert aPoint into a LogicalPoint in the writing-mode of this block michael@0: WritingMode wm = curLine->mWritingMode; michael@0: LogicalPoint pt(wm, aPoint, curLine->mContainerWidth); michael@0: while (curLine != end) { michael@0: // Check to see if our point lies within the line's block-direction bounds michael@0: nscoord BCoord = pt.B(wm) - curLine->BStart(); michael@0: nscoord BSize = curLine->BSize(); michael@0: if (BCoord >= 0 && BCoord < BSize) { michael@0: closestLine = curLine; michael@0: break; // We found the line; stop looking michael@0: } michael@0: if (BCoord < 0) michael@0: break; michael@0: ++curLine; michael@0: } michael@0: michael@0: if (closestLine == end) { michael@0: nsBlockFrame::line_iterator prevLine = curLine.prev(); michael@0: nsBlockFrame::line_iterator nextLine = curLine; michael@0: // Avoid empty lines michael@0: while (nextLine != end && nextLine->IsEmpty()) michael@0: ++nextLine; michael@0: while (prevLine != end && prevLine->IsEmpty()) michael@0: --prevLine; michael@0: michael@0: // This hidden pref dictates whether a point above or below all lines comes michael@0: // up with a line or the beginning or end of the frame; 0 on Windows, michael@0: // 1 on other platforms by default at the writing of this code michael@0: int32_t dragOutOfFrame = michael@0: Preferences::GetInt("browser.drag_out_of_frame_style"); michael@0: michael@0: if (prevLine == end) { michael@0: if (dragOutOfFrame == 1 || nextLine == end) michael@0: return DrillDownToSelectionFrame(aFrame, false, aFlags); michael@0: closestLine = nextLine; michael@0: } else if (nextLine == end) { michael@0: if (dragOutOfFrame == 1) michael@0: return DrillDownToSelectionFrame(aFrame, true, aFlags); michael@0: closestLine = prevLine; michael@0: } else { // Figure out which line is closer michael@0: if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm)) michael@0: closestLine = prevLine; michael@0: else michael@0: closestLine = nextLine; michael@0: } michael@0: } michael@0: michael@0: do { michael@0: FrameTarget target = GetSelectionClosestFrameForLine(bf, closestLine, michael@0: aPoint, aFlags); michael@0: if (!target.IsNull()) michael@0: return target; michael@0: ++closestLine; michael@0: } while (closestLine != end); michael@0: // Fall back to just targeting the last targetable place michael@0: return DrillDownToSelectionFrame(aFrame, true, aFlags); michael@0: } michael@0: michael@0: // GetSelectionClosestFrame is the helper function that calculates the closest michael@0: // frame to the given point. michael@0: // It doesn't completely account for offset styles, so needs to be used in michael@0: // restricted environments. michael@0: // Cannot handle overlapping frames correctly, so it should receive the output michael@0: // of GetFrameForPoint michael@0: // Guaranteed to return a valid FrameTarget michael@0: static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, nsPoint aPoint, michael@0: uint32_t aFlags) michael@0: { michael@0: { michael@0: // Handle blocks; if the frame isn't a block, the method fails michael@0: FrameTarget target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags); michael@0: if (!target.IsNull()) michael@0: return target; michael@0: } michael@0: michael@0: nsIFrame *kid = aFrame->GetFirstPrincipalChild(); michael@0: michael@0: if (kid) { michael@0: // Go through all the child frames to find the closest one michael@0: nsIFrame::FrameWithDistance closest = { nullptr, nscoord_MAX, nscoord_MAX }; michael@0: for (; kid; kid = kid->GetNextSibling()) { michael@0: if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty()) michael@0: continue; michael@0: michael@0: kid->FindCloserFrameForSelection(aPoint, &closest); michael@0: } michael@0: if (closest.mFrame) { michael@0: if (closest.mFrame->IsSVGText()) michael@0: return FrameTarget(closest.mFrame, false, false); michael@0: return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags); michael@0: } michael@0: } michael@0: return FrameTarget(aFrame, false, false); michael@0: } michael@0: michael@0: nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame, nsPoint aPoint) michael@0: { michael@0: nsIFrame::ContentOffsets offsets; michael@0: FrameContentRange range = GetRangeForFrame(aFrame); michael@0: offsets.content = range.content; michael@0: // If there are continuations (meaning it's not one rectangle), this is the michael@0: // best this function can do michael@0: if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) { michael@0: offsets.offset = range.start; michael@0: offsets.secondaryOffset = range.end; michael@0: offsets.associateWithNext = true; michael@0: return offsets; michael@0: } michael@0: michael@0: // Figure out whether the offsets should be over, after, or before the frame michael@0: nsRect rect(nsPoint(0, 0), aFrame->GetSize()); michael@0: michael@0: bool isBlock = aFrame->GetDisplay() != NS_STYLE_DISPLAY_INLINE; michael@0: bool isRtl = (aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL); michael@0: if ((isBlock && rect.y < aPoint.y) || michael@0: (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) || michael@0: (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) { michael@0: offsets.offset = range.end; michael@0: if (rect.Contains(aPoint)) michael@0: offsets.secondaryOffset = range.start; michael@0: else michael@0: offsets.secondaryOffset = range.end; michael@0: } else { michael@0: offsets.offset = range.start; michael@0: if (rect.Contains(aPoint)) michael@0: offsets.secondaryOffset = range.end; michael@0: else michael@0: offsets.secondaryOffset = range.start; michael@0: } michael@0: offsets.associateWithNext = (offsets.offset == range.start); michael@0: return offsets; michael@0: } michael@0: michael@0: static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) { michael@0: nsIFrame* adjustedFrame = aFrame; michael@0: for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) michael@0: { michael@0: // These are the conditions that make all children not able to handle michael@0: // a cursor. michael@0: if (frame->StyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_ALL || michael@0: frame->IsGeneratedContentFrame()) { michael@0: adjustedFrame = frame; michael@0: } michael@0: } michael@0: return adjustedFrame; michael@0: } michael@0: michael@0: nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint, michael@0: uint32_t aFlags) michael@0: { michael@0: nsIFrame *adjustedFrame; michael@0: if (aFlags & IGNORE_SELECTION_STYLE) { michael@0: adjustedFrame = this; michael@0: } michael@0: else { michael@0: // This section of code deals with special selection styles. Note that michael@0: // -moz-all exists, even though it doesn't need to be explicitly handled. michael@0: // michael@0: // The offset is forced not to end up in generated content; content offsets michael@0: // cannot represent content outside of the document's content tree. michael@0: michael@0: adjustedFrame = AdjustFrameForSelectionStyles(this); michael@0: michael@0: // -moz-user-select: all needs special handling, because clicking on it michael@0: // should lead to the whole frame being selected michael@0: if (adjustedFrame && adjustedFrame->StyleUIReset()->mUserSelect == michael@0: NS_STYLE_USER_SELECT_ALL) { michael@0: nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame); michael@0: return OffsetsForSingleFrame(adjustedFrame, adjustedPoint); michael@0: } michael@0: michael@0: // For other cases, try to find a closest frame starting from the parent of michael@0: // the unselectable frame michael@0: if (adjustedFrame != this) michael@0: adjustedFrame = adjustedFrame->GetParent(); michael@0: } michael@0: michael@0: nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame); michael@0: michael@0: FrameTarget closest = michael@0: GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags); michael@0: michael@0: if (closest.emptyBlock) { michael@0: ContentOffsets offsets; michael@0: NS_ASSERTION(closest.frame, michael@0: "closest.frame must not be null when it's empty"); michael@0: offsets.content = closest.frame->GetContent(); michael@0: offsets.offset = 0; michael@0: offsets.secondaryOffset = 0; michael@0: offsets.associateWithNext = true; michael@0: return offsets; michael@0: } michael@0: michael@0: // If the correct offset is at one end of a frame, use offset-based michael@0: // calculation method michael@0: if (closest.frameEdge) { michael@0: ContentOffsets offsets; michael@0: FrameContentRange range = GetRangeForFrame(closest.frame); michael@0: offsets.content = range.content; michael@0: if (closest.afterFrame) michael@0: offsets.offset = range.end; michael@0: else michael@0: offsets.offset = range.start; michael@0: offsets.secondaryOffset = offsets.offset; michael@0: offsets.associateWithNext = (offsets.offset == range.start); michael@0: return offsets; michael@0: } michael@0: michael@0: nsPoint pt; michael@0: if (closest.frame != this) { michael@0: if (closest.frame->IsSVGText()) { michael@0: pt = nsLayoutUtils::TransformAncestorPointToFrame(closest.frame, michael@0: aPoint, this); michael@0: } else { michael@0: pt = aPoint - closest.frame->GetOffsetTo(this); michael@0: } michael@0: } else { michael@0: pt = aPoint; michael@0: } michael@0: return static_cast(closest.frame)->CalcContentOffsetsFromFramePoint(pt); michael@0: michael@0: // XXX should I add some kind of offset standardization? michael@0: // consider xxxxxzzzzz; should any click between the last michael@0: // x and first z put the cursor in the same logical position in addition michael@0: // to the same visual position? michael@0: } michael@0: michael@0: nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) michael@0: { michael@0: return OffsetsForSingleFrame(this, aPoint); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext) michael@0: { michael@0: if (aImage.GetType() != eStyleImageType_Image) { michael@0: return; michael@0: } michael@0: michael@0: imgIRequest *req = aImage.GetImageData(); michael@0: mozilla::css::ImageLoader* loader = michael@0: aPresContext->Document()->StyleImageLoader(); michael@0: michael@0: // If this fails there's not much we can do ... michael@0: loader->AssociateRequestToFrame(req, this); michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::GetCursor(const nsPoint& aPoint, michael@0: nsIFrame::Cursor& aCursor) michael@0: { michael@0: FillCursorInformationFromStyle(StyleUserInterface(), aCursor); michael@0: if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) { michael@0: // If this is editable, I-beam cursor is better for most elements. michael@0: aCursor.mCursor = michael@0: (mContent && mContent->IsEditable()) ? NS_STYLE_CURSOR_TEXT : michael@0: NS_STYLE_CURSOR_DEFAULT; michael@0: } michael@0: michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Resize and incremental reflow michael@0: michael@0: /* virtual */ void michael@0: nsFrame::MarkIntrinsicWidthsDirty() michael@0: { michael@0: // This version is meant only for what used to be box-to-block adaptors. michael@0: // It should not be called by other derived classes. michael@0: if (IsBoxWrapped()) { michael@0: nsBoxLayoutMetrics *metrics = BoxMetrics(); michael@0: michael@0: SizeNeedsRecalc(metrics->mPrefSize); michael@0: SizeNeedsRecalc(metrics->mMinSize); michael@0: SizeNeedsRecalc(metrics->mMaxSize); michael@0: SizeNeedsRecalc(metrics->mBlockPrefSize); michael@0: SizeNeedsRecalc(metrics->mBlockMinSize); michael@0: CoordNeedsRecalc(metrics->mFlex); michael@0: CoordNeedsRecalc(metrics->mAscent); michael@0: } michael@0: michael@0: if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) { michael@0: nsFontInflationData::MarkFontInflationDataTextDirty(this); michael@0: } michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsFrame::GetMinWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord result = 0; michael@0: DISPLAY_MIN_WIDTH(this, result); michael@0: return result; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord result = 0; michael@0: DISPLAY_PREF_WIDTH(this, result); michael@0: return result; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext, michael@0: nsIFrame::InlineMinWidthData *aData) michael@0: { michael@0: NS_ASSERTION(GetParent(), "Must have a parent if we get here!"); michael@0: nsIFrame* parent = GetParent(); michael@0: bool canBreak = !CanContinueTextRun() && michael@0: parent->StyleText()->WhiteSpaceCanWrap(parent); michael@0: michael@0: if (canBreak) michael@0: aData->OptionallyBreak(aRenderingContext); michael@0: aData->trailingWhitespace = 0; michael@0: aData->skipWhitespace = false; michael@0: aData->trailingTextFrame = nullptr; michael@0: aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: this, nsLayoutUtils::MIN_WIDTH); michael@0: aData->atStartOfLine = false; michael@0: if (canBreak) michael@0: aData->OptionallyBreak(aRenderingContext); michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext, michael@0: nsIFrame::InlinePrefWidthData *aData) michael@0: { michael@0: aData->trailingWhitespace = 0; michael@0: aData->skipWhitespace = false; michael@0: nscoord myPref = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: this, nsLayoutUtils::PREF_WIDTH); michael@0: aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, myPref); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::InlineMinWidthData::ForceBreak(nsRenderingContext *aRenderingContext) michael@0: { michael@0: currentLine -= trailingWhitespace; michael@0: prevLines = std::max(prevLines, currentLine); michael@0: currentLine = trailingWhitespace = 0; michael@0: michael@0: for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) { michael@0: nscoord float_min = floats[i].Width(); michael@0: if (float_min > prevLines) michael@0: prevLines = float_min; michael@0: } michael@0: floats.Clear(); michael@0: trailingTextFrame = nullptr; michael@0: skipWhitespace = true; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::InlineMinWidthData::OptionallyBreak(nsRenderingContext *aRenderingContext, michael@0: nscoord aHyphenWidth) michael@0: { michael@0: trailingTextFrame = nullptr; michael@0: michael@0: // If we can fit more content into a smaller width by staying on this michael@0: // line (because we're still at a negative offset due to negative michael@0: // text-indent or negative margin), don't break. Otherwise, do the michael@0: // same as ForceBreak. it doesn't really matter when we accumulate michael@0: // floats. michael@0: if (currentLine + aHyphenWidth < 0 || atStartOfLine) michael@0: return; michael@0: currentLine += aHyphenWidth; michael@0: ForceBreak(aRenderingContext); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::InlinePrefWidthData::ForceBreak(nsRenderingContext *aRenderingContext) michael@0: { michael@0: if (floats.Length() != 0) { michael@0: // preferred widths accumulated for floats that have already michael@0: // been cleared past michael@0: nscoord floats_done = 0, michael@0: // preferred widths accumulated for floats that have not yet michael@0: // been cleared past michael@0: floats_cur_left = 0, michael@0: floats_cur_right = 0; michael@0: michael@0: for (uint32_t i = 0, i_end = floats.Length(); i != i_end; ++i) { michael@0: const FloatInfo& floatInfo = floats[i]; michael@0: const nsStyleDisplay *floatDisp = floatInfo.Frame()->StyleDisplay(); michael@0: if (floatDisp->mBreakType == NS_STYLE_CLEAR_LEFT || michael@0: floatDisp->mBreakType == NS_STYLE_CLEAR_RIGHT || michael@0: floatDisp->mBreakType == NS_STYLE_CLEAR_BOTH) { michael@0: nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left, michael@0: floats_cur_right); michael@0: if (floats_cur > floats_done) michael@0: floats_done = floats_cur; michael@0: if (floatDisp->mBreakType != NS_STYLE_CLEAR_RIGHT) michael@0: floats_cur_left = 0; michael@0: if (floatDisp->mBreakType != NS_STYLE_CLEAR_LEFT) michael@0: floats_cur_right = 0; michael@0: } michael@0: michael@0: nscoord &floats_cur = floatDisp->mFloats == NS_STYLE_FLOAT_LEFT michael@0: ? floats_cur_left : floats_cur_right; michael@0: nscoord floatWidth = floatInfo.Width(); michael@0: // Negative-width floats don't change the available space so they michael@0: // shouldn't change our intrinsic line width either. michael@0: floats_cur = michael@0: NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth)); michael@0: } michael@0: michael@0: nscoord floats_cur = michael@0: NSCoordSaturatingAdd(floats_cur_left, floats_cur_right); michael@0: if (floats_cur > floats_done) michael@0: floats_done = floats_cur; michael@0: michael@0: currentLine = NSCoordSaturatingAdd(currentLine, floats_done); michael@0: michael@0: floats.Clear(); michael@0: } michael@0: michael@0: currentLine = michael@0: NSCoordSaturatingSubtract(currentLine, trailingWhitespace, nscoord_MAX); michael@0: prevLines = std::max(prevLines, currentLine); michael@0: currentLine = trailingWhitespace = 0; michael@0: skipWhitespace = true; michael@0: } michael@0: michael@0: static void michael@0: AddCoord(const nsStyleCoord& aStyle, michael@0: nsRenderingContext* aRenderingContext, michael@0: nsIFrame* aFrame, michael@0: nscoord* aCoord, float* aPercent, michael@0: bool aClampNegativeToZero) michael@0: { michael@0: switch (aStyle.GetUnit()) { michael@0: case eStyleUnit_Coord: { michael@0: NS_ASSERTION(!aClampNegativeToZero || aStyle.GetCoordValue() >= 0, michael@0: "unexpected negative value"); michael@0: *aCoord += aStyle.GetCoordValue(); michael@0: return; michael@0: } michael@0: case eStyleUnit_Percent: { michael@0: NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f, michael@0: "unexpected negative value"); michael@0: *aPercent += aStyle.GetPercentValue(); michael@0: return; michael@0: } michael@0: case eStyleUnit_Calc: { michael@0: const nsStyleCoord::Calc *calc = aStyle.GetCalcValue(); michael@0: if (aClampNegativeToZero) { michael@0: // This is far from ideal when one is negative and one is positive. michael@0: *aCoord += std::max(calc->mLength, 0); michael@0: *aPercent += std::max(calc->mPercent, 0.0f); michael@0: } else { michael@0: *aCoord += calc->mLength; michael@0: *aPercent += calc->mPercent; michael@0: } michael@0: return; michael@0: } michael@0: default: { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* virtual */ nsIFrame::IntrinsicWidthOffsetData michael@0: nsFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) michael@0: { michael@0: IntrinsicWidthOffsetData result; michael@0: michael@0: const nsStyleMargin *styleMargin = StyleMargin(); michael@0: AddCoord(styleMargin->mMargin.GetLeft(), aRenderingContext, this, michael@0: &result.hMargin, &result.hPctMargin, false); michael@0: AddCoord(styleMargin->mMargin.GetRight(), aRenderingContext, this, michael@0: &result.hMargin, &result.hPctMargin, false); michael@0: michael@0: const nsStylePadding *stylePadding = StylePadding(); michael@0: AddCoord(stylePadding->mPadding.GetLeft(), aRenderingContext, this, michael@0: &result.hPadding, &result.hPctPadding, true); michael@0: AddCoord(stylePadding->mPadding.GetRight(), aRenderingContext, this, michael@0: &result.hPadding, &result.hPctPadding, true); michael@0: michael@0: const nsStyleBorder *styleBorder = StyleBorder(); michael@0: result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_LEFT); michael@0: result.hBorder += styleBorder->GetComputedBorderWidth(NS_SIDE_RIGHT); michael@0: michael@0: const nsStyleDisplay *disp = StyleDisplay(); michael@0: if (IsThemed(disp)) { michael@0: nsPresContext *presContext = PresContext(); michael@0: michael@0: nsIntMargin border; michael@0: presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), michael@0: this, disp->mAppearance, michael@0: &border); michael@0: result.hBorder = presContext->DevPixelsToAppUnits(border.LeftRight()); michael@0: michael@0: nsIntMargin padding; michael@0: if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), michael@0: this, disp->mAppearance, michael@0: &padding)) { michael@0: result.hPadding = presContext->DevPixelsToAppUnits(padding.LeftRight()); michael@0: result.hPctPadding = 0; michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: /* virtual */ IntrinsicSize michael@0: nsFrame::GetIntrinsicSize() michael@0: { michael@0: return IntrinsicSize(); // default is width/height set to eStyleUnit_None michael@0: } michael@0: michael@0: /* virtual */ nsSize michael@0: nsFrame::GetIntrinsicRatio() michael@0: { michael@0: return nsSize(0, 0); michael@0: } michael@0: michael@0: /* virtual */ nsSize michael@0: nsFrame::ComputeSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, nsSize aPadding, michael@0: uint32_t aFlags) michael@0: { michael@0: nsSize result = ComputeAutoSize(aRenderingContext, aCBSize, aAvailableWidth, michael@0: aMargin, aBorder, aPadding, michael@0: aFlags & eShrinkWrap); michael@0: nsSize boxSizingAdjust(0,0); michael@0: const nsStylePosition *stylePos = StylePosition(); michael@0: michael@0: switch (stylePos->mBoxSizing) { michael@0: case NS_STYLE_BOX_SIZING_BORDER: michael@0: boxSizingAdjust += aBorder; michael@0: // fall through michael@0: case NS_STYLE_BOX_SIZING_PADDING: michael@0: boxSizingAdjust += aPadding; michael@0: } michael@0: nscoord boxSizingToMarginEdgeWidth = michael@0: aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width; michael@0: const nsStyleCoord* widthStyleCoord = &(stylePos->mWidth); michael@0: const nsStyleCoord* heightStyleCoord = &(stylePos->mHeight); michael@0: michael@0: bool isFlexItem = IsFlexItem(); michael@0: bool isHorizontalFlexItem = false; michael@0: michael@0: if (isFlexItem) { michael@0: // Flex items use their "flex-basis" property in place of their main-size michael@0: // property (e.g. "width") for sizing purposes, *unless* they have michael@0: // "flex-basis:auto", in which case they use their main-size property after michael@0: // all. michael@0: uint32_t flexDirection = mParent->StylePosition()->mFlexDirection; michael@0: isHorizontalFlexItem = michael@0: flexDirection == NS_STYLE_FLEX_DIRECTION_ROW || michael@0: flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE; michael@0: michael@0: // NOTE: The logic here should match the similar chunk for determining michael@0: // widthStyleCoord and heightStyleCoord in michael@0: // nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(). michael@0: const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis); michael@0: if (flexBasis->GetUnit() != eStyleUnit_Auto) { michael@0: if (isHorizontalFlexItem) { michael@0: widthStyleCoord = flexBasis; michael@0: } else { michael@0: // One caveat for vertical flex items: We don't support enumerated michael@0: // values (e.g. "max-content") for height properties yet. So, if our michael@0: // computed flex-basis is an enumerated value, we'll just behave as if michael@0: // it were "auto", which means "use the main-size property after all" michael@0: // (which is "height", in this case). michael@0: // NOTE: Once we support intrinsic sizing keywords for "height", michael@0: // we should remove this check. michael@0: if (flexBasis->GetUnit() != eStyleUnit_Enumerated) { michael@0: heightStyleCoord = flexBasis; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Compute width michael@0: michael@0: if (widthStyleCoord->GetUnit() != eStyleUnit_Auto) { michael@0: result.width = michael@0: nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, michael@0: aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, michael@0: *widthStyleCoord); michael@0: } michael@0: michael@0: // Flex items ignore their min & max sizing properties in their michael@0: // flex container's main-axis. (Those properties get applied later in michael@0: // the flexbox algorithm.) michael@0: if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None && michael@0: !(isFlexItem && isHorizontalFlexItem)) { michael@0: nscoord maxWidth = michael@0: nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, michael@0: aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, michael@0: stylePos->mMaxWidth); michael@0: result.width = std::min(maxWidth, result.width); michael@0: } michael@0: michael@0: nscoord minWidth; michael@0: if (!(isFlexItem && isHorizontalFlexItem)) { michael@0: minWidth = michael@0: nsLayoutUtils::ComputeWidthValue(aRenderingContext, this, michael@0: aCBSize.width, boxSizingAdjust.width, boxSizingToMarginEdgeWidth, michael@0: stylePos->mMinWidth); michael@0: } else { michael@0: minWidth = 0; michael@0: } michael@0: result.width = std::max(minWidth, result.width); michael@0: michael@0: // Compute height michael@0: // (but not if we're auto-height or if we recieved the "eUseAutoHeight" michael@0: // flag -- then, we'll just stick with the height that we already calculated michael@0: // in the initial ComputeAutoSize() call.) michael@0: if (!nsLayoutUtils::IsAutoHeight(*heightStyleCoord, aCBSize.height) && michael@0: !(aFlags & nsIFrame::eUseAutoHeight)) { michael@0: result.height = michael@0: nsLayoutUtils::ComputeHeightValue(aCBSize.height, michael@0: boxSizingAdjust.height, michael@0: *heightStyleCoord); michael@0: } michael@0: michael@0: if (result.height != NS_UNCONSTRAINEDSIZE) { michael@0: if (!nsLayoutUtils::IsAutoHeight(stylePos->mMaxHeight, aCBSize.height) && michael@0: !(isFlexItem && !isHorizontalFlexItem)) { michael@0: nscoord maxHeight = michael@0: nsLayoutUtils::ComputeHeightValue(aCBSize.height, michael@0: boxSizingAdjust.height, michael@0: stylePos->mMaxHeight); michael@0: result.height = std::min(maxHeight, result.height); michael@0: } michael@0: michael@0: if (!nsLayoutUtils::IsAutoHeight(stylePos->mMinHeight, aCBSize.height) && michael@0: !(isFlexItem && !isHorizontalFlexItem)) { michael@0: nscoord minHeight = michael@0: nsLayoutUtils::ComputeHeightValue(aCBSize.height, michael@0: boxSizingAdjust.height, michael@0: stylePos->mMinHeight); michael@0: result.height = std::max(minHeight, result.height); michael@0: } michael@0: } michael@0: michael@0: const nsStyleDisplay *disp = StyleDisplay(); michael@0: if (IsThemed(disp)) { michael@0: nsIntSize widget(0, 0); michael@0: bool canOverride = true; michael@0: nsPresContext *presContext = PresContext(); michael@0: presContext->GetTheme()-> michael@0: GetMinimumWidgetSize(aRenderingContext, this, disp->mAppearance, michael@0: &widget, &canOverride); michael@0: michael@0: nsSize size; michael@0: size.width = presContext->DevPixelsToAppUnits(widget.width); michael@0: size.height = presContext->DevPixelsToAppUnits(widget.height); michael@0: michael@0: // GMWS() returns border-box; we need content-box michael@0: size.width -= aBorder.width + aPadding.width; michael@0: size.height -= aBorder.height + aPadding.height; michael@0: michael@0: if (size.height > result.height || !canOverride) michael@0: result.height = size.height; michael@0: if (size.width > result.width || !canOverride) michael@0: result.width = size.width; michael@0: } michael@0: michael@0: result.width = std::max(0, result.width); michael@0: result.height = std::max(0, result.height); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::ComputeTightBounds(gfxContext* aContext) const michael@0: { michael@0: return GetVisualOverflowRect(); michael@0: } michael@0: michael@0: nsRect michael@0: nsFrame::ComputeSimpleTightBounds(gfxContext* aContext) const michael@0: { michael@0: if (StyleOutline()->GetOutlineStyle() != NS_STYLE_BORDER_STYLE_NONE || michael@0: StyleBorder()->HasBorder() || !StyleBackground()->IsTransparent() || michael@0: StyleDisplay()->mAppearance) { michael@0: // Not necessarily tight, due to clipping, negative michael@0: // outline-offset, and lots of other issues, but that's OK michael@0: return GetVisualOverflowRect(); michael@0: } michael@0: michael@0: nsRect r(0, 0, 0, 0); michael@0: ChildListIterator lists(this); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* child = childFrames.get(); michael@0: r.UnionRect(r, child->ComputeTightBounds(aContext) + child->GetPosition()); michael@0: } michael@0: } michael@0: return r; michael@0: } michael@0: michael@0: /* virtual */ nsresult michael@0: nsIFrame::GetPrefWidthTightBounds(nsRenderingContext* aContext, michael@0: nscoord* aX, michael@0: nscoord* aXMost) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /* virtual */ nsSize michael@0: nsFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, nsSize aPadding, michael@0: bool aShrinkWrap) michael@0: { michael@0: // Use basic shrink-wrapping as a default implementation. michael@0: nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE); michael@0: michael@0: // don't bother setting it if the result won't be used michael@0: if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { michael@0: nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width - michael@0: aPadding.width; michael@0: result.width = ShrinkWidthToFit(aRenderingContext, availBased); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nscoord michael@0: nsFrame::ShrinkWidthToFit(nsRenderingContext *aRenderingContext, michael@0: nscoord aWidthInCB) michael@0: { michael@0: // If we're a container for font size inflation, then shrink michael@0: // wrapping inside of us should not apply font size inflation. michael@0: AutoMaybeDisableFontInflation an(this); michael@0: michael@0: nscoord result; michael@0: nscoord minWidth = GetMinWidth(aRenderingContext); michael@0: if (minWidth > aWidthInCB) { michael@0: result = minWidth; michael@0: } else { michael@0: nscoord prefWidth = GetPrefWidth(aRenderingContext); michael@0: if (prefWidth > aWidthInCB) { michael@0: result = aWidthInCB; michael@0: } else { michael@0: result = prefWidth; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::WillReflow(nsPresContext* aPresContext) michael@0: { michael@0: #ifdef DEBUG_dbaron_off michael@0: // bug 81268 michael@0: NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), michael@0: "nsFrame::WillReflow: frame is already in reflow"); michael@0: #endif michael@0: michael@0: NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, michael@0: ("WillReflow: oldState=%x", mState)); michael@0: mState |= NS_FRAME_IN_REFLOW; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::DidReflow(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState* aReflowState, michael@0: nsDidReflowStatus aStatus) michael@0: { michael@0: NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS, michael@0: ("nsFrame::DidReflow: aStatus=%d", static_cast(aStatus))); michael@0: michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this, nsSVGEffects::INVALIDATE_REFLOW); michael@0: michael@0: if (nsDidReflowStatus::FINISHED == aStatus) { michael@0: mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: } michael@0: michael@0: // Notify the percent height observer if there is a percent height. michael@0: // The observer may be able to initiate another reflow with a computed michael@0: // height. This happens in the case where a table cell has no computed michael@0: // height but can fabricate one when the cell height is known. michael@0: if (aReflowState && aReflowState->mPercentHeightObserver && michael@0: !GetPrevInFlow()) { michael@0: const nsStyleCoord &height = aReflowState->mStylePosition->mHeight; michael@0: if (height.HasPercent()) { michael@0: aReflowState->mPercentHeightObserver->NotifyPercentHeight(*aReflowState); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus, michael@0: bool aConstrainHeight) michael@0: { michael@0: ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, aConstrainHeight); michael@0: michael@0: FinishAndStoreOverflow(&aDesiredSize); michael@0: } michael@0: michael@0: void michael@0: nsFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus, michael@0: bool aConstrainHeight) michael@0: { michael@0: if (HasAbsolutelyPositionedChildren()) { michael@0: nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock(); michael@0: michael@0: // Let the absolutely positioned container reflow any absolutely positioned michael@0: // child frames that need to be reflowed michael@0: michael@0: // The containing block for the abs pos kids is formed by our padding edge. michael@0: nsMargin computedBorder = michael@0: aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding(); michael@0: nscoord containingBlockWidth = michael@0: aDesiredSize.Width() - computedBorder.LeftRight(); michael@0: nscoord containingBlockHeight = michael@0: aDesiredSize.Height() - computedBorder.TopBottom(); michael@0: michael@0: nsContainerFrame* container = do_QueryFrame(this); michael@0: NS_ASSERTION(container, "Abs-pos children only supported on container frames for now"); michael@0: michael@0: nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight); michael@0: absoluteContainer->Reflow(container, aPresContext, aReflowState, aStatus, michael@0: containingBlock, michael@0: aConstrainHeight, true, true, // XXX could be optimized michael@0: &aDesiredSize.mOverflowAreas); michael@0: } michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsFrame::CanContinueTextRun() const michael@0: { michael@0: // By default, a frame will *not* allow a text run to be continued michael@0: // through it. michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsFrame"); michael@0: aDesiredSize.Width() = 0; michael@0: aDesiredSize.Height() = 0; michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo) michael@0: { michael@0: NS_NOTREACHED("should only be called for text frames"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Flow member functions michael@0: michael@0: nsSplittableType michael@0: nsFrame::GetSplittableType() const michael@0: { michael@0: return NS_FRAME_NOT_SPLITTABLE; michael@0: } michael@0: michael@0: nsIFrame* nsFrame::GetPrevContinuation() const michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsFrame::SetPrevContinuation(nsIFrame* aPrevContinuation) michael@0: { michael@0: MOZ_ASSERT(false, "not splittable"); michael@0: } michael@0: michael@0: nsIFrame* nsFrame::GetNextContinuation() const michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsFrame::SetNextContinuation(nsIFrame*) michael@0: { michael@0: MOZ_ASSERT(false, "not splittable"); michael@0: } michael@0: michael@0: nsIFrame* nsFrame::GetPrevInFlowVirtual() const michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsFrame::SetPrevInFlow(nsIFrame* aPrevInFlow) michael@0: { michael@0: MOZ_ASSERT(false, "not splittable"); michael@0: } michael@0: michael@0: nsIFrame* nsFrame::GetNextInFlowVirtual() const michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsFrame::SetNextInFlow(nsIFrame*) michael@0: { michael@0: MOZ_ASSERT(false, "not splittable"); michael@0: } michael@0: michael@0: nsIFrame* nsIFrame::GetTailContinuation() michael@0: { michael@0: nsIFrame* frame = this; michael@0: while (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { michael@0: frame = frame->GetPrevContinuation(); michael@0: NS_ASSERTION(frame, "first continuation can't be overflow container"); michael@0: } michael@0: for (nsIFrame* next = frame->GetNextContinuation(); michael@0: next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER); michael@0: next = frame->GetNextContinuation()) { michael@0: frame = next; michael@0: } michael@0: NS_POSTCONDITION(frame, "illegal state in continuation chain."); michael@0: return frame; michael@0: } michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(ViewProperty, nullptr) michael@0: michael@0: // Associated view object michael@0: nsView* michael@0: nsIFrame::GetView() const michael@0: { michael@0: // Check the frame state bit and see if the frame has a view michael@0: if (!(GetStateBits() & NS_FRAME_HAS_VIEW)) michael@0: return nullptr; michael@0: michael@0: // Check for a property on the frame michael@0: void* value = Properties().Get(ViewProperty()); michael@0: NS_ASSERTION(value, "frame state bit was set but frame has no view"); michael@0: return static_cast(value); michael@0: } michael@0: michael@0: /* virtual */ nsView* michael@0: nsIFrame::GetViewExternal() const michael@0: { michael@0: return GetView(); michael@0: } michael@0: michael@0: nsresult michael@0: nsIFrame::SetView(nsView* aView) michael@0: { michael@0: if (aView) { michael@0: aView->SetFrame(this); michael@0: michael@0: #ifdef DEBUG michael@0: nsIAtom* frameType = GetType(); michael@0: NS_ASSERTION(frameType == nsGkAtoms::scrollFrame || michael@0: frameType == nsGkAtoms::subDocumentFrame || michael@0: frameType == nsGkAtoms::listControlFrame || michael@0: frameType == nsGkAtoms::objectFrame || michael@0: frameType == nsGkAtoms::viewportFrame || michael@0: frameType == nsGkAtoms::menuPopupFrame, michael@0: "Only specific frame types can have an nsView"); michael@0: #endif michael@0: michael@0: // Set a property on the frame michael@0: Properties().Set(ViewProperty(), aView); michael@0: michael@0: // Set the frame state bit that says the frame has a view michael@0: AddStateBits(NS_FRAME_HAS_VIEW); michael@0: michael@0: // Let all of the ancestors know they have a descendant with a view. michael@0: for (nsIFrame* f = GetParent(); michael@0: f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW); michael@0: f = f->GetParent()) michael@0: f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame* nsIFrame::GetAncestorWithViewExternal() const michael@0: { michael@0: return GetAncestorWithView(); michael@0: } michael@0: michael@0: // Find the first geometric parent that has a view michael@0: nsIFrame* nsIFrame::GetAncestorWithView() const michael@0: { michael@0: for (nsIFrame* f = mParent; nullptr != f; f = f->GetParent()) { michael@0: if (f->HasView()) { michael@0: return f; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: // virtual michael@0: nsPoint nsIFrame::GetOffsetToExternal(const nsIFrame* aOther) const michael@0: { michael@0: return GetOffsetTo(aOther); michael@0: } michael@0: michael@0: nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const michael@0: { michael@0: NS_PRECONDITION(aOther, michael@0: "Must have frame for destination coordinate system!"); michael@0: michael@0: NS_ASSERTION(PresContext() == aOther->PresContext(), michael@0: "GetOffsetTo called on frames in different documents"); michael@0: michael@0: nsPoint offset(0, 0); michael@0: const nsIFrame* f; michael@0: for (f = this; f != aOther && f; f = f->GetParent()) { michael@0: offset += f->GetPosition(); michael@0: } michael@0: michael@0: if (f != aOther) { michael@0: // Looks like aOther wasn't an ancestor of |this|. So now we have michael@0: // the root-frame-relative position of |this| in |offset|. Convert back michael@0: // to the coordinates of aOther michael@0: while (aOther) { michael@0: offset -= aOther->GetPosition(); michael@0: aOther = aOther->GetParent(); michael@0: } michael@0: } michael@0: michael@0: return offset; michael@0: } michael@0: michael@0: nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const michael@0: { michael@0: return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel()); michael@0: } michael@0: michael@0: nsPoint michael@0: nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const michael@0: { michael@0: NS_PRECONDITION(aOther, michael@0: "Must have frame for destination coordinate system!"); michael@0: NS_ASSERTION(PresContext()->GetRootPresContext() == michael@0: aOther->PresContext()->GetRootPresContext(), michael@0: "trying to get the offset between frames in different document " michael@0: "hierarchies?"); michael@0: if (PresContext()->GetRootPresContext() != michael@0: aOther->PresContext()->GetRootPresContext()) { michael@0: // crash right away, we are almost certainly going to crash anyway. michael@0: NS_RUNTIMEABORT("trying to get the offset between frames in different " michael@0: "document hierarchies?"); michael@0: } michael@0: michael@0: const nsIFrame* root = nullptr; michael@0: // offset will hold the final offset michael@0: // docOffset holds the currently accumulated offset at the current APD, it michael@0: // will be converted and added to offset when the current APD changes. michael@0: nsPoint offset(0, 0), docOffset(0, 0); michael@0: const nsIFrame* f = this; michael@0: int32_t currAPD = PresContext()->AppUnitsPerDevPixel(); michael@0: while (f && f != aOther) { michael@0: docOffset += f->GetPosition(); michael@0: nsIFrame* parent = f->GetParent(); michael@0: if (parent) { michael@0: f = parent; michael@0: } else { michael@0: nsPoint newOffset(0, 0); michael@0: root = f; michael@0: f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset); michael@0: int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0; michael@0: if (!f || newAPD != currAPD) { michael@0: // Convert docOffset to the right APD and add it to offset. michael@0: offset += docOffset.ConvertAppUnits(currAPD, aAPD); michael@0: docOffset.x = docOffset.y = 0; michael@0: } michael@0: currAPD = newAPD; michael@0: docOffset += newOffset; michael@0: } michael@0: } michael@0: if (f == aOther) { michael@0: offset += docOffset.ConvertAppUnits(currAPD, aAPD); michael@0: } else { michael@0: // Looks like aOther wasn't an ancestor of |this|. So now we have michael@0: // the root-document-relative position of |this| in |offset|. Subtract the michael@0: // root-document-relative position of |aOther| from |offset|. michael@0: // This call won't try to recurse again because root is an ancestor of michael@0: // aOther. michael@0: nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD); michael@0: offset -= negOffset; michael@0: } michael@0: michael@0: return offset; michael@0: } michael@0: michael@0: // virtual michael@0: nsIntRect nsIFrame::GetScreenRectExternal() const michael@0: { michael@0: return GetScreenRect(); michael@0: } michael@0: michael@0: nsIntRect nsIFrame::GetScreenRect() const michael@0: { michael@0: return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel()); michael@0: } michael@0: michael@0: // virtual michael@0: nsRect nsIFrame::GetScreenRectInAppUnitsExternal() const michael@0: { michael@0: return GetScreenRectInAppUnits(); michael@0: } michael@0: michael@0: nsRect nsIFrame::GetScreenRectInAppUnits() const michael@0: { michael@0: nsPresContext* presContext = PresContext(); michael@0: nsIFrame* rootFrame = michael@0: presContext->PresShell()->FrameManager()->GetRootFrame(); michael@0: nsPoint rootScreenPos(0, 0); michael@0: nsPoint rootFrameOffsetInParent(0, 0); michael@0: nsIFrame* rootFrameParent = michael@0: nsLayoutUtils::GetCrossDocParentFrame(rootFrame, &rootFrameOffsetInParent); michael@0: if (rootFrameParent) { michael@0: nsRect parentScreenRectAppUnits = rootFrameParent->GetScreenRectInAppUnits(); michael@0: nsPresContext* parentPresContext = rootFrameParent->PresContext(); michael@0: double parentScale = double(presContext->AppUnitsPerDevPixel())/ michael@0: parentPresContext->AppUnitsPerDevPixel(); michael@0: nsPoint rootPt = parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent; michael@0: rootScreenPos.x = NS_round(parentScale*rootPt.x); michael@0: rootScreenPos.y = NS_round(parentScale*rootPt.y); michael@0: } else { michael@0: nsCOMPtr rootWidget; michael@0: presContext->PresShell()->GetViewManager()->GetRootWidget(getter_AddRefs(rootWidget)); michael@0: if (rootWidget) { michael@0: nsIntPoint rootDevPx = rootWidget->WidgetToScreenOffset(); michael@0: rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x); michael@0: rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y); michael@0: } michael@0: } michael@0: michael@0: return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize()); michael@0: } michael@0: michael@0: // Returns the offset from this frame to the closest geometric parent that michael@0: // has a view. Also returns the containing view or null in case of error michael@0: void michael@0: nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const michael@0: { michael@0: NS_PRECONDITION(nullptr != aView, "null OUT parameter pointer"); michael@0: nsIFrame* frame = const_cast(this); michael@0: michael@0: *aView = nullptr; michael@0: aOffset.MoveTo(0, 0); michael@0: do { michael@0: aOffset += frame->GetPosition(); michael@0: frame = frame->GetParent(); michael@0: } while (frame && !frame->HasView()); michael@0: michael@0: if (frame) { michael@0: *aView = frame->GetView(); michael@0: } michael@0: } michael@0: michael@0: nsIWidget* michael@0: nsIFrame::GetNearestWidget() const michael@0: { michael@0: return GetClosestView()->GetNearestWidget(nullptr); michael@0: } michael@0: michael@0: nsIWidget* michael@0: nsIFrame::GetNearestWidget(nsPoint& aOffset) const michael@0: { michael@0: nsPoint offsetToView; michael@0: nsPoint offsetToWidget; michael@0: nsIWidget* widget = michael@0: GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget); michael@0: aOffset = offsetToView + offsetToWidget; michael@0: return widget; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsFrame::GetType() const michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsLeaf() const michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: gfx3DMatrix michael@0: nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor, michael@0: nsIFrame** aOutAncestor) michael@0: { michael@0: NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!"); michael@0: michael@0: /* If we're transformed, we want to hand back the combination michael@0: * transform/translate matrix that will apply our current transform, then michael@0: * shift us to our parent. michael@0: */ michael@0: if (IsTransformed()) { michael@0: /* Compute the delta to the parent, which we need because we are converting michael@0: * coordinates to our parent. michael@0: */ michael@0: NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), michael@0: "Cannot transform the viewport frame!"); michael@0: int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel(); michael@0: michael@0: gfx3DMatrix result = michael@0: nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0), scaleFactor, nullptr, aOutAncestor); michael@0: // XXXjwatt: seems like this will double count offsets in the face of preserve-3d: michael@0: nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); michael@0: /* Combine the raw transform with a translation to our parent. */ michael@0: result *= gfx3DMatrix::Translation michael@0: (NSAppUnitsToFloatPixels(delta.x, scaleFactor), michael@0: NSAppUnitsToFloatPixels(delta.y, scaleFactor), michael@0: 0.0f); michael@0: return result; michael@0: } michael@0: michael@0: if (nsLayoutUtils::IsPopup(this) && michael@0: GetType() == nsGkAtoms::listControlFrame) { michael@0: nsPresContext* presContext = PresContext(); michael@0: nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame(); michael@0: michael@0: // Compute a matrix that transforms from the popup widget to the toplevel michael@0: // widget. We use the widgets because they're the simplest and most michael@0: // accurate approach --- this should work no matter how the widget position michael@0: // was chosen. michael@0: nsIWidget* widget = GetView()->GetWidget(); michael@0: nsPresContext* rootPresContext = PresContext()->GetRootPresContext(); michael@0: // Maybe the widget hasn't been created yet? Popups without widgets are michael@0: // treated as regular frames. That should work since they'll be rendered michael@0: // as part of the page if they're rendered at all. michael@0: if (widget && rootPresContext) { michael@0: nsIWidget* toplevel = rootPresContext->GetNearestWidget(); michael@0: if (toplevel) { michael@0: nsIntRect screenBounds; michael@0: widget->GetClientBounds(screenBounds); michael@0: nsIntRect toplevelScreenBounds; michael@0: toplevel->GetClientBounds(toplevelScreenBounds); michael@0: nsIntPoint translation = screenBounds.TopLeft() - toplevelScreenBounds.TopLeft(); michael@0: michael@0: gfx3DMatrix transformToTop; michael@0: transformToTop._41 = translation.x; michael@0: transformToTop._42 = translation.y; michael@0: michael@0: *aOutAncestor = docRootFrame; michael@0: gfx3DMatrix docRootTransformToTop = michael@0: nsLayoutUtils::GetTransformToAncestor(docRootFrame, nullptr); michael@0: if (docRootTransformToTop.IsSingular()) { michael@0: NS_WARNING("Containing document is invisible, we can't compute a valid transform"); michael@0: } else { michael@0: gfx3DMatrix topToDocRootTransform = docRootTransformToTop.Inverse(); michael@0: return transformToTop*topToDocRootTransform; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this); michael@0: michael@0: /* Otherwise, we're not transformed. In that case, we'll walk up the frame michael@0: * tree until we either hit the root frame or something that may be michael@0: * transformed. We'll then change coordinates into that frame, since we're michael@0: * guaranteed that nothing in-between can be transformed. First, however, michael@0: * we have to check to see if we have a parent. If not, we'll set the michael@0: * outparam to null (indicating that there's nothing left) and will hand back michael@0: * the identity matrix. michael@0: */ michael@0: if (!*aOutAncestor) michael@0: return gfx3DMatrix(); michael@0: michael@0: /* Keep iterating while the frame can't possibly be transformed. */ michael@0: while (!(*aOutAncestor)->IsTransformed() && michael@0: !nsLayoutUtils::IsPopup(*aOutAncestor) && michael@0: *aOutAncestor != aStopAtAncestor) { michael@0: /* If no parent, stop iterating. Otherwise, update the ancestor. */ michael@0: nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor); michael@0: if (!parent) michael@0: break; michael@0: michael@0: *aOutAncestor = parent; michael@0: } michael@0: michael@0: NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?"); michael@0: michael@0: /* Translate from this frame to our ancestor, if it exists. That's the michael@0: * entire transform, so we're done. michael@0: */ michael@0: nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); michael@0: int32_t scaleFactor = PresContext()->AppUnitsPerDevPixel(); michael@0: return gfx3DMatrix().Translation michael@0: (NSAppUnitsToFloatPixels(delta.x, scaleFactor), michael@0: NSAppUnitsToFloatPixels(delta.y, scaleFactor), michael@0: 0.0f); michael@0: } michael@0: michael@0: static void InvalidateFrameInternal(nsIFrame *aFrame, bool aHasDisplayItem = true) michael@0: { michael@0: if (aHasDisplayItem) { michael@0: aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT); michael@0: } michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(aFrame); michael@0: bool needsSchedulePaint = false; michael@0: if (nsLayoutUtils::IsPopup(aFrame)) { michael@0: needsSchedulePaint = true; michael@0: } else { michael@0: nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); michael@0: while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { michael@0: if (aHasDisplayItem) { michael@0: parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); michael@0: } michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(parent); michael@0: michael@0: // If we're inside a popup, then we need to make sure that we michael@0: // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE michael@0: // flag gets added to the popup display root frame. michael@0: if (nsLayoutUtils::IsPopup(parent)) { michael@0: needsSchedulePaint = true; michael@0: break; michael@0: } michael@0: parent = nsLayoutUtils::GetCrossDocParentFrame(parent); michael@0: } michael@0: if (!parent) { michael@0: needsSchedulePaint = true; michael@0: } michael@0: } michael@0: if (!aHasDisplayItem) { michael@0: return; michael@0: } michael@0: if (needsSchedulePaint) { michael@0: aFrame->SchedulePaint(); michael@0: } michael@0: if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) { michael@0: aFrame->Properties().Delete(nsIFrame::InvalidationRect()); michael@0: aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIFrame::InvalidateFrameSubtree(uint32_t aDisplayItemKey) michael@0: { michael@0: bool hasDisplayItem = michael@0: !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); michael@0: InvalidateFrame(aDisplayItemKey); michael@0: michael@0: if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT) || !hasDisplayItem) { michael@0: return; michael@0: } michael@0: michael@0: AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT); michael@0: michael@0: nsAutoTArray childListArray; michael@0: GetCrossDocChildLists(&childListArray); michael@0: michael@0: nsIFrame::ChildListArrayIterator lists(childListArray); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: childFrames.get()->InvalidateFrameSubtree(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIFrame::ClearInvalidationStateBits() michael@0: { michael@0: if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { michael@0: nsAutoTArray childListArray; michael@0: GetCrossDocChildLists(&childListArray); michael@0: michael@0: nsIFrame::ChildListArrayIterator lists(childListArray); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: childFrames.get()->ClearInvalidationStateBits(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: RemoveStateBits(NS_FRAME_NEEDS_PAINT | michael@0: NS_FRAME_DESCENDANT_NEEDS_PAINT | michael@0: NS_FRAME_ALL_DESCENDANTS_NEED_PAINT); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey) michael@0: { michael@0: bool hasDisplayItem = michael@0: !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); michael@0: InvalidateFrameInternal(this, hasDisplayItem); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) michael@0: { michael@0: bool hasDisplayItem = michael@0: !aDisplayItemKey || FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey); michael@0: bool alreadyInvalid = false; michael@0: if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) { michael@0: InvalidateFrameInternal(this, hasDisplayItem); michael@0: } else { michael@0: alreadyInvalid = true; michael@0: } michael@0: michael@0: if (!hasDisplayItem) { michael@0: return; michael@0: } michael@0: michael@0: nsRect *rect = static_cast(Properties().Get(InvalidationRect())); michael@0: if (!rect) { michael@0: if (alreadyInvalid) { michael@0: return; michael@0: } michael@0: rect = new nsRect(); michael@0: Properties().Set(InvalidationRect(), rect); michael@0: AddStateBits(NS_FRAME_HAS_INVALID_RECT); michael@0: } michael@0: michael@0: *rect = rect->Union(aRect); michael@0: } michael@0: michael@0: /*static*/ uint8_t nsIFrame::sLayerIsPrerenderedDataKey; michael@0: michael@0: bool michael@0: nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult) michael@0: { michael@0: Layer* layer = FrameLayerBuilder::GetDedicatedLayer( michael@0: this, nsDisplayItem::TYPE_TRANSFORM); michael@0: if (!layer || !layer->HasUserData(LayerIsPrerenderedDataKey())) { michael@0: // If this layer isn't prerendered or we clip composites to our OS michael@0: // window, then we can't correctly optimize to an empty michael@0: // transaction in general. michael@0: return false; michael@0: } michael@0: michael@0: gfx3DMatrix transform3d; michael@0: if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) { michael@0: // We're not able to compute a layer transform that we know would michael@0: // be used at the next layers transaction, so we can't only update michael@0: // the transform and will need to schedule an invalidating paint. michael@0: return false; michael@0: } michael@0: gfxMatrix transform; michael@0: gfx::Matrix previousTransform; michael@0: // FIXME/bug 796690 and 796705: in general, changes to 3D michael@0: // transforms, or transform changes to properties other than michael@0: // translation, may lead us to choose a different rendering michael@0: // resolution for our layer. So if the transform is 3D or has a michael@0: // non-translation change, bail and schedule an invalidating paint. michael@0: // (We can often do better than this, for example for scale-down michael@0: // changes.) michael@0: static const gfx::Float kError = 0.0001f; michael@0: if (!transform3d.Is2D(&transform) || michael@0: !layer->GetBaseTransform().Is2D(&previousTransform) || michael@0: !gfx::FuzzyEqual(transform.xx, previousTransform._11, kError) || michael@0: !gfx::FuzzyEqual(transform.yy, previousTransform._22, kError) || michael@0: !gfx::FuzzyEqual(transform.xy, previousTransform._21, kError) || michael@0: !gfx::FuzzyEqual(transform.yx, previousTransform._12, kError)) { michael@0: return false; michael@0: } michael@0: gfx::Matrix4x4 matrix; michael@0: gfx::ToMatrix4x4(transform3d, matrix); michael@0: layer->SetBaseTransformForNextTransaction(matrix); michael@0: *aLayerResult = layer; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsInvalid(nsRect& aRect) michael@0: { michael@0: if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) { michael@0: return false; michael@0: } michael@0: michael@0: if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) { michael@0: nsRect *rect = static_cast(Properties().Get(InvalidationRect())); michael@0: NS_ASSERTION(rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!"); michael@0: aRect = *rect; michael@0: } else { michael@0: aRect.SetEmpty(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::SchedulePaint(PaintType aType) michael@0: { michael@0: nsIFrame *displayRoot = nsLayoutUtils::GetDisplayRootFrame(this); michael@0: nsPresContext *pres = displayRoot->PresContext()->GetRootPresContext(); michael@0: michael@0: // No need to schedule a paint for an external document since they aren't michael@0: // painted directly. michael@0: if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) { michael@0: return; michael@0: } michael@0: if (!pres->GetContainerWeak()) { michael@0: NS_WARNING("Shouldn't call SchedulePaint in a detached pres context"); michael@0: return; michael@0: } michael@0: michael@0: pres->PresShell()->ScheduleViewManagerFlush(aType == PAINT_DELAYED_COMPRESS ? michael@0: nsIPresShell::PAINT_DELAYED_COMPRESS : michael@0: nsIPresShell::PAINT_DEFAULT); michael@0: michael@0: if (aType == PAINT_DELAYED_COMPRESS) { michael@0: return; michael@0: } michael@0: michael@0: if (aType == PAINT_DEFAULT) { michael@0: displayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE); michael@0: } michael@0: nsIPresShell* shell = PresContext()->PresShell(); michael@0: if (shell) { michael@0: shell->AddInvalidateHiddenPresShellObserver(pres->RefreshDriver()); michael@0: } michael@0: } michael@0: michael@0: Layer* michael@0: nsIFrame::InvalidateLayer(uint32_t aDisplayItemKey, michael@0: const nsIntRect* aDamageRect, michael@0: uint32_t aFlags /* = 0 */) michael@0: { michael@0: NS_ASSERTION(aDisplayItemKey > 0, "Need a key"); michael@0: michael@0: Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey); michael@0: michael@0: // If the layer is being updated asynchronously, and it's being forwarded michael@0: // to a compositor, then we don't need to invalidate. michael@0: if ((aFlags & UPDATE_IS_ASYNC) && layer && michael@0: layer->Manager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { michael@0: return layer; michael@0: } michael@0: michael@0: if (aDamageRect && aDamageRect->IsEmpty()) { michael@0: return layer; michael@0: } michael@0: michael@0: if (!layer) { michael@0: // Plugins can transition from not rendering anything to rendering, michael@0: // and still only call this. So always invalidate, with specifying michael@0: // the display item type just in case. michael@0: // michael@0: // In the bug 930056, dialer app startup but not shown on the michael@0: // screen because sometimes we don't have any retainned data michael@0: // for remote type displayitem and thus Repaint event is not michael@0: // triggered. So, always invalidate here as well. michael@0: if (aDisplayItemKey == nsDisplayItem::TYPE_PLUGIN || michael@0: aDisplayItemKey == nsDisplayItem::TYPE_REMOTE) { michael@0: InvalidateFrame(); michael@0: } else { michael@0: InvalidateFrame(aDisplayItemKey); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aDamageRect) { michael@0: layer->AddInvalidRect(*aDamageRect); michael@0: } else { michael@0: layer->SetInvalidRectToVisibleRegion(); michael@0: } michael@0: michael@0: SchedulePaint(PAINT_COMPOSITE_ONLY); michael@0: return layer; michael@0: } michael@0: michael@0: static nsRect michael@0: ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect, michael@0: const nsSize& aNewSize) michael@0: { michael@0: nsRect r = aOverflowRect; michael@0: michael@0: if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { michael@0: // For SVG frames, we only need to account for filters. michael@0: // TODO: We could also take account of clipPath and mask to reduce the michael@0: // visual overflow, but that's not essential. michael@0: if (aFrame->StyleSVGReset()->HasFilters()) { michael@0: aFrame->Properties(). michael@0: Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); michael@0: r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect); michael@0: } michael@0: return r; michael@0: } michael@0: michael@0: // box-shadow michael@0: r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize)); michael@0: michael@0: // border-image-outset. michael@0: // We need to include border-image-outset because it can cause the michael@0: // border image to be drawn beyond the border box. michael@0: michael@0: // (1) It's important we not check whether there's a border-image michael@0: // since the style hint for a change in border image doesn't cause michael@0: // reflow, and that's probably more important than optimizing the michael@0: // overflow areas for the silly case of border-image-outset without michael@0: // border-image michael@0: // (2) It's important that we not check whether the border-image michael@0: // is actually loaded, since that would require us to reflow when michael@0: // the image loads. michael@0: const nsStyleBorder* styleBorder = aFrame->StyleBorder(); michael@0: nsMargin outsetMargin = styleBorder->GetImageOutset(); michael@0: michael@0: if (outsetMargin != nsMargin(0, 0, 0, 0)) { michael@0: nsRect outsetRect(nsPoint(0, 0), aNewSize); michael@0: outsetRect.Inflate(outsetMargin); michael@0: r.UnionRect(r, outsetRect); michael@0: } michael@0: michael@0: // Note that we don't remove the outlineInnerRect if a frame loses outline michael@0: // style. That would require an extra property lookup for every frame, michael@0: // or a new frame state bit to track whether a property had been stored, michael@0: // or something like that. It's not worth doing that here. At most it's michael@0: // only one heap-allocated rect per frame and it will be cleaned up when michael@0: // the frame dies. michael@0: michael@0: if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) { michael@0: aFrame->Properties(). michael@0: Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); michael@0: r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r); michael@0: } michael@0: michael@0: return r; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::MovePositionBy(const nsPoint& aTranslation) michael@0: { michael@0: nsPoint position = GetNormalPosition() + aTranslation; michael@0: michael@0: const nsMargin* computedOffsets = nullptr; michael@0: if (IsRelativelyPositioned()) { michael@0: computedOffsets = static_cast michael@0: (Properties().Get(nsIFrame::ComputedOffsetProperty())); michael@0: } michael@0: nsHTMLReflowState::ApplyRelativePositioning(this, computedOffsets ? michael@0: *computedOffsets : nsMargin(), michael@0: &position); michael@0: NS_ASSERTION(StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY || michael@0: GetPosition() + aTranslation == position, michael@0: "MovePositionBy should always lead to the movement " michael@0: "specified, unless the frame is position:sticky"); michael@0: SetPosition(position); michael@0: } michael@0: michael@0: nsPoint michael@0: nsIFrame::GetNormalPosition() const michael@0: { michael@0: // It might be faster to first check michael@0: // StyleDisplay()->IsRelativelyPositionedStyle(). michael@0: nsPoint* normalPosition = static_cast michael@0: (Properties().Get(NormalPositionProperty())); michael@0: if (normalPosition) { michael@0: return *normalPosition; michael@0: } michael@0: return GetPosition(); michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetOverflowRect(nsOverflowType aType) const michael@0: { michael@0: NS_ABORT_IF_FALSE(aType == eVisualOverflow || aType == eScrollableOverflow, michael@0: "unexpected type"); michael@0: michael@0: // Note that in some cases the overflow area might not have been michael@0: // updated (yet) to reflect any outline set on the frame or the area michael@0: // of child frames. That's OK because any reflow that updates these michael@0: // areas will invalidate the appropriate area, so any (mis)uses of michael@0: // this method will be fixed up. michael@0: michael@0: if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { michael@0: // there is an overflow rect, and it's not stored as deltas but as michael@0: // a separately-allocated rect michael@0: return static_cast(const_cast(this)-> michael@0: GetOverflowAreasProperty())->Overflow(aType); michael@0: } michael@0: michael@0: if (aType == eVisualOverflow && michael@0: mOverflow.mType != NS_FRAME_OVERFLOW_NONE) { michael@0: return GetVisualOverflowFromDeltas(); michael@0: } michael@0: michael@0: return nsRect(nsPoint(0, 0), GetSize()); michael@0: } michael@0: michael@0: nsOverflowAreas michael@0: nsIFrame::GetOverflowAreas() const michael@0: { michael@0: if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { michael@0: // there is an overflow rect, and it's not stored as deltas but as michael@0: // a separately-allocated rect michael@0: return *const_cast(this)->GetOverflowAreasProperty(); michael@0: } michael@0: michael@0: return nsOverflowAreas(GetVisualOverflowFromDeltas(), michael@0: nsRect(nsPoint(0, 0), GetSize())); michael@0: } michael@0: michael@0: nsOverflowAreas michael@0: nsIFrame::GetOverflowAreasRelativeToSelf() const michael@0: { michael@0: if (IsTransformed()) { michael@0: nsOverflowAreas* preTransformOverflows = static_cast michael@0: (Properties().Get(PreTransformOverflowAreasProperty())); michael@0: if (preTransformOverflows) { michael@0: return nsOverflowAreas(preTransformOverflows->VisualOverflow(), michael@0: preTransformOverflows->ScrollableOverflow()); michael@0: } michael@0: } michael@0: return nsOverflowAreas(GetVisualOverflowRect(), michael@0: GetScrollableOverflowRect()); michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetScrollableOverflowRectRelativeToParent() const michael@0: { michael@0: return GetScrollableOverflowRect() + mRect.TopLeft(); michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetScrollableOverflowRectRelativeToSelf() const michael@0: { michael@0: if (IsTransformed()) { michael@0: nsOverflowAreas* preTransformOverflows = static_cast michael@0: (Properties().Get(PreTransformOverflowAreasProperty())); michael@0: if (preTransformOverflows) michael@0: return preTransformOverflows->ScrollableOverflow(); michael@0: } michael@0: return GetScrollableOverflowRect(); michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetVisualOverflowRectRelativeToSelf() const michael@0: { michael@0: if (IsTransformed()) { michael@0: nsOverflowAreas* preTransformOverflows = static_cast michael@0: (Properties().Get(PreTransformOverflowAreasProperty())); michael@0: if (preTransformOverflows) michael@0: return preTransformOverflows->VisualOverflow(); michael@0: } michael@0: return GetVisualOverflowRect(); michael@0: } michael@0: michael@0: nsRect michael@0: nsIFrame::GetPreEffectsVisualOverflowRect() const michael@0: { michael@0: nsRect* r = static_cast michael@0: (Properties().Get(nsIFrame::PreEffectsBBoxProperty())); michael@0: return r ? *r : GetVisualOverflowRectRelativeToSelf(); michael@0: } michael@0: michael@0: inline static bool michael@0: FrameMaintainsOverflow(nsIFrame* aFrame) michael@0: { michael@0: return (aFrame->GetStateBits() & michael@0: (NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY)) != michael@0: (NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY); michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsFrame::UpdateOverflow() michael@0: { michael@0: MOZ_ASSERT(FrameMaintainsOverflow(this), michael@0: "Non-display SVG do not maintain visual overflow rects"); michael@0: michael@0: nsRect rect(nsPoint(0, 0), GetSize()); michael@0: nsOverflowAreas overflowAreas(rect, rect); michael@0: michael@0: if (!DoesClipChildren() && michael@0: !(IsCollapsed() && (IsBoxFrame() || IsBoxWrapped()))) { michael@0: nsLayoutUtils::UnionChildOverflow(this, overflowAreas); michael@0: } michael@0: michael@0: if (FinishAndStoreOverflow(overflowAreas, GetSize())) { michael@0: nsView* view = GetView(); michael@0: if (view) { michael@0: uint32_t flags = 0; michael@0: GetLayoutFlags(flags); michael@0: michael@0: if ((flags & NS_FRAME_NO_SIZE_VIEW) == 0) { michael@0: // Make sure the frame's view is properly sized. michael@0: nsViewManager* vm = view->GetViewManager(); michael@0: vm->ResizeView(view, overflowAreas.VisualOverflow(), true); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus michael@0: // 4 for the frames above the document's frames: michael@0: // the Viewport, GFXScroll, ScrollPort, and Canvas michael@0: #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH+4) michael@0: michael@0: bool michael@0: nsFrame::IsFrameTreeTooDeep(const nsHTMLReflowState& aReflowState, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: if (aReflowState.mReflowDepth > MAX_FRAME_DEPTH) { michael@0: NS_WARNING("frame tree too deep; setting zero size and returning"); michael@0: mState |= NS_FRAME_TOO_DEEP_IN_FRAME_TREE; michael@0: ClearOverflowRects(); michael@0: aMetrics.Width() = 0; michael@0: aMetrics.Height() = 0; michael@0: aMetrics.SetTopAscent(0); michael@0: aMetrics.mCarriedOutBottomMargin.Zero(); michael@0: aMetrics.mOverflowAreas.Clear(); michael@0: michael@0: if (GetNextInFlow()) { michael@0: // Reflow depth might vary between reflows, so we might have michael@0: // successfully reflowed and split this frame before. If so, we michael@0: // shouldn't delete its continuations. michael@0: aStatus = NS_FRAME_NOT_COMPLETE; michael@0: } else { michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: mState &= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE; michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsBlockWrapper() const michael@0: { michael@0: nsIAtom *pseudoType = StyleContext()->GetPseudo(); michael@0: return (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || michael@0: pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || michael@0: pseudoType == nsCSSAnonBoxes::buttonContent || michael@0: pseudoType == nsCSSAnonBoxes::cellContent); michael@0: } michael@0: michael@0: static nsIFrame* michael@0: GetNearestBlockContainer(nsIFrame* frame) michael@0: { michael@0: // The block wrappers we use to wrap blocks inside inlines aren't michael@0: // described in the CSS spec. We need to make them not be containing michael@0: // blocks. michael@0: // Since the parent of such a block is either a normal block or michael@0: // another such pseudo, this shouldn't cause anything bad to happen. michael@0: // Also the anonymous blocks inside table cells are not containing blocks. michael@0: while (frame->IsFrameOfType(nsIFrame::eLineParticipant) || michael@0: frame->IsBlockWrapper() || michael@0: // Table rows are not containing blocks either michael@0: frame->GetType() == nsGkAtoms::tableRowFrame) { michael@0: frame = frame->GetParent(); michael@0: NS_ASSERTION(frame, "How come we got to the root frame without seeing a containing block?"); michael@0: } michael@0: return frame; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsIFrame::GetContainingBlock() const michael@0: { michael@0: // MathML frames might have absolute positioning style, but they would michael@0: // still be in-flow. So we have to check to make sure that the frame michael@0: // is really out-of-flow too. michael@0: if (IsAbsolutelyPositioned() && michael@0: (GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { michael@0: return GetParent(); // the parent is always the containing block michael@0: } michael@0: return GetNearestBlockContainer(GetParent()); michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: michael@0: int32_t nsFrame::ContentIndexInContainer(const nsIFrame* aFrame) michael@0: { michael@0: int32_t result = -1; michael@0: michael@0: nsIContent* content = aFrame->GetContent(); michael@0: if (content) { michael@0: nsIContent* parentContent = content->GetParent(); michael@0: if (parentContent) { michael@0: result = parentContent->IndexOf(content); michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * List a frame tree to stderr. Meant to be called from gdb. michael@0: */ michael@0: void michael@0: DebugListFrameTree(nsIFrame* aFrame) michael@0: { michael@0: ((nsFrame*)aFrame)->List(stderr); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::ListTag(nsACString& aTo) const michael@0: { michael@0: ListTag(aTo, this); michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: nsIFrame::ListTag(nsACString& aTo, const nsIFrame* aFrame) { michael@0: nsAutoString tmp; michael@0: aFrame->GetFrameName(tmp); michael@0: aTo += NS_ConvertUTF16toUTF8(tmp).get(); michael@0: aTo += nsPrintfCString("@%p", static_cast(aFrame)); michael@0: } michael@0: michael@0: // Debugging michael@0: void michael@0: nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) const michael@0: { michael@0: aTo =+ aPrefix; michael@0: ListTag(aTo); michael@0: if (HasView()) { michael@0: aTo += nsPrintfCString(" [view=%p]", static_cast(GetView())); michael@0: } michael@0: if (GetNextSibling()) { michael@0: aTo += nsPrintfCString(" next=%p", static_cast(GetNextSibling())); michael@0: } michael@0: if (GetPrevContinuation()) { michael@0: bool fluid = GetPrevInFlow() == GetPrevContinuation(); michael@0: aTo += nsPrintfCString(" prev-%s=%p", fluid?"in-flow":"continuation", michael@0: static_cast(GetPrevContinuation())); michael@0: } michael@0: if (GetNextContinuation()) { michael@0: bool fluid = GetNextInFlow() == GetNextContinuation(); michael@0: aTo += nsPrintfCString(" next-%s=%p", fluid?"in-flow":"continuation", michael@0: static_cast(GetNextContinuation())); michael@0: } michael@0: void* IBsibling = Properties().Get(IBSplitSibling()); michael@0: if (IBsibling) { michael@0: aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling); michael@0: } michael@0: void* IBprevsibling = Properties().Get(IBSplitPrevSibling()); michael@0: if (IBprevsibling) { michael@0: aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling); michael@0: } michael@0: aTo += nsPrintfCString(" {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); michael@0: nsIFrame* f = const_cast(this); michael@0: if (f->HasOverflowAreas()) { michael@0: nsRect vo = f->GetVisualOverflowRect(); michael@0: if (!vo.IsEqualEdges(mRect)) { michael@0: aTo += nsPrintfCString(" vis-overflow=%d,%d,%d,%d", vo.x, vo.y, vo.width, vo.height); michael@0: } michael@0: nsRect so = f->GetScrollableOverflowRect(); michael@0: if (!so.IsEqualEdges(mRect)) { michael@0: aTo += nsPrintfCString(" scr-overflow=%d,%d,%d,%d", so.x, so.y, so.width, so.height); michael@0: } michael@0: } michael@0: if (0 != mState) { michael@0: aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState); michael@0: } michael@0: if (IsTransformed()) { michael@0: aTo += nsPrintfCString(" transformed"); michael@0: } michael@0: if (ChildrenHavePerspective()) { michael@0: aTo += nsPrintfCString(" perspective"); michael@0: } michael@0: if (Preserves3DChildren()) { michael@0: aTo += nsPrintfCString(" preserves-3d-children"); michael@0: } michael@0: if (Preserves3D()) { michael@0: aTo += nsPrintfCString(" preserves-3d"); michael@0: } michael@0: if (mContent) { michael@0: aTo += nsPrintfCString(" [content=%p]", static_cast(mContent)); michael@0: } michael@0: aTo += nsPrintfCString(" [sc=%p", static_cast(mStyleContext)); michael@0: if (mStyleContext) { michael@0: nsIAtom* pseudoTag = mStyleContext->GetPseudo(); michael@0: if (pseudoTag) { michael@0: nsAutoString atomString; michael@0: pseudoTag->ToString(atomString); michael@0: aTo += nsPrintfCString("%s", NS_LossyConvertUTF16toASCII(atomString).get()); michael@0: } michael@0: if (mParent && mStyleContext->GetParent() != mParent->StyleContext()) { michael@0: aTo += nsPrintfCString(",parent=%p", mStyleContext->GetParent()); michael@0: } michael@0: } michael@0: aTo += "]"; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const michael@0: { michael@0: nsCString str; michael@0: ListGeneric(str, aPrefix, aFlags); michael@0: fprintf_stderr(out, "%s\n", str.get()); michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult); michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::MakeFrameName(const nsAString& aType, nsAString& aResult) const michael@0: { michael@0: aResult = aType; michael@0: if (mContent && !mContent->IsNodeOfType(nsINode::eTEXT)) { michael@0: nsAutoString buf; michael@0: mContent->Tag()->ToString(buf); michael@0: if (GetType() == nsGkAtoms::subDocumentFrame) { michael@0: nsAutoString src; michael@0: mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); michael@0: buf.Append(NS_LITERAL_STRING(" src=") + src); michael@0: } michael@0: aResult.Append(NS_LITERAL_STRING("(") + buf + NS_LITERAL_STRING(")")); michael@0: } michael@0: char buf[40]; michael@0: PR_snprintf(buf, sizeof(buf), "(%d)", ContentIndexInContainer(this)); michael@0: AppendASCIItoUTF16(buf, aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::DumpFrameTree() michael@0: { michael@0: RootFrameList(PresContext(), stderr); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::DumpFrameTreeLimited() michael@0: { michael@0: List(stderr); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::RootFrameList(nsPresContext* aPresContext, FILE* out, const char* aPrefix) michael@0: { michael@0: if (!aPresContext || !out) michael@0: return; michael@0: michael@0: nsIPresShell *shell = aPresContext->GetPresShell(); michael@0: if (shell) { michael@0: nsIFrame* frame = shell->FrameManager()->GetRootFrame(); michael@0: if(frame) { michael@0: frame->List(out, aPrefix); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: nsFrameState michael@0: nsFrame::GetDebugStateBits() const michael@0: { michael@0: // We'll ignore these flags for the purposes of comparing frame state: michael@0: // michael@0: // NS_FRAME_EXTERNAL_REFERENCE michael@0: // because this is set by the event state manager or the michael@0: // caret code when a frame is focused. Depending on whether michael@0: // or not the regression tests are run as the focused window michael@0: // will make this value vary randomly. michael@0: #define IRRELEVANT_FRAME_STATE_FLAGS NS_FRAME_EXTERNAL_REFERENCE michael@0: michael@0: #define FRAME_STATE_MASK (~(IRRELEVANT_FRAME_STATE_FLAGS)) michael@0: michael@0: return GetStateBits() & FRAME_STATE_MASK; michael@0: } michael@0: michael@0: void michael@0: nsFrame::XMLQuote(nsString& aString) michael@0: { michael@0: int32_t i, len = aString.Length(); michael@0: for (i = 0; i < len; i++) { michael@0: char16_t ch = aString.CharAt(i); michael@0: if (ch == '<') { michael@0: nsAutoString tmp(NS_LITERAL_STRING("<")); michael@0: aString.Cut(i, 1); michael@0: aString.Insert(tmp, i); michael@0: len += 3; michael@0: i += 3; michael@0: } michael@0: else if (ch == '>') { michael@0: nsAutoString tmp(NS_LITERAL_STRING(">")); michael@0: aString.Cut(i, 1); michael@0: aString.Insert(tmp, i); michael@0: len += 3; michael@0: i += 3; michael@0: } michael@0: else if (ch == '\"') { michael@0: nsAutoString tmp(NS_LITERAL_STRING(""")); michael@0: aString.Cut(i, 1); michael@0: aString.Insert(tmp, i); michael@0: len += 5; michael@0: i += 5; michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: nsIFrame::IsVisibleForPainting(nsDisplayListBuilder* aBuilder) { michael@0: if (!StyleVisibility()->IsVisible()) michael@0: return false; michael@0: nsISelection* sel = aBuilder->GetBoundingSelection(); michael@0: return !sel || IsVisibleInSelection(sel); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsVisibleForPainting() { michael@0: if (!StyleVisibility()->IsVisible()) michael@0: return false; michael@0: michael@0: nsPresContext* pc = PresContext(); michael@0: if (!pc->IsRenderingOnlySelection()) michael@0: return true; michael@0: michael@0: nsCOMPtr selcon(do_QueryInterface(pc->PresShell())); michael@0: if (selcon) { michael@0: nsCOMPtr sel; michael@0: selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, michael@0: getter_AddRefs(sel)); michael@0: if (sel) michael@0: return IsVisibleInSelection(sel); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsVisibleInSelection(nsDisplayListBuilder* aBuilder) { michael@0: nsISelection* sel = aBuilder->GetBoundingSelection(); michael@0: return !sel || IsVisibleInSelection(sel); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsVisibleOrCollapsedForPainting(nsDisplayListBuilder* aBuilder) { michael@0: if (!StyleVisibility()->IsVisibleOrCollapsed()) michael@0: return false; michael@0: nsISelection* sel = aBuilder->GetBoundingSelection(); michael@0: return !sel || IsVisibleInSelection(sel); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsVisibleInSelection(nsISelection* aSelection) michael@0: { michael@0: if (!GetContent() || !GetContent()->IsSelectionDescendant()) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr node(do_QueryInterface(mContent)); michael@0: bool vis; michael@0: nsresult rv = aSelection->ContainsNode(node, true, &vis); michael@0: return NS_FAILED(rv) || vis; michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsFrame::IsEmpty() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::CachedIsEmpty() michael@0: { michael@0: NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY), michael@0: "Must only be called on reflowed lines"); michael@0: return IsEmpty(); michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsFrame::IsSelfEmpty() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon) michael@0: { michael@0: if (!aPresContext || !aSelCon) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsIFrame *frame = this; michael@0: while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) { michael@0: nsITextControlFrame *tcf = do_QueryFrame(frame); michael@0: if (tcf) { michael@0: return tcf->GetOwnedSelectionController(aSelCon); michael@0: } michael@0: frame = frame->GetParent(); michael@0: } michael@0: michael@0: return CallQueryInterface(aPresContext->GetPresShell(), aSelCon); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsIFrame::GetFrameSelection() michael@0: { michael@0: nsRefPtr fs = michael@0: const_cast(GetConstFrameSelection()); michael@0: return fs.forget(); michael@0: } michael@0: michael@0: const nsFrameSelection* michael@0: nsIFrame::GetConstFrameSelection() const michael@0: { michael@0: nsIFrame* frame = const_cast(this); michael@0: while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) { michael@0: nsITextControlFrame* tcf = do_QueryFrame(frame); michael@0: if (tcf) { michael@0: return tcf->GetOwnedFrameSelection(); michael@0: } michael@0: frame = frame->GetParent(); michael@0: } michael@0: michael@0: return PresContext()->PresShell()->ConstFrameSelection(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: nsresult michael@0: nsFrame::DumpRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) michael@0: { michael@0: IndentBy(out, aIndent); michael@0: fprintf(out, "\n", michael@0: (unsigned long long)GetDebugStateBits(), (void*)mParent); michael@0: michael@0: aIndent++; michael@0: DumpBaseRegressionData(aPresContext, out, aIndent); michael@0: aIndent--; michael@0: michael@0: IndentBy(out, aIndent); michael@0: fprintf(out, "\n"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) michael@0: { michael@0: if (GetNextSibling()) { michael@0: IndentBy(out, aIndent); michael@0: fprintf(out, "\n", (void*)GetNextSibling()); michael@0: } michael@0: michael@0: if (HasView()) { michael@0: IndentBy(out, aIndent); michael@0: fprintf(out, "\n", (void*)GetView()); michael@0: aIndent++; michael@0: // XXX add in code to dump out view state too... michael@0: aIndent--; michael@0: IndentBy(out, aIndent); michael@0: fprintf(out, "\n"); michael@0: } michael@0: michael@0: IndentBy(out, aIndent); michael@0: fprintf(out, "\n", michael@0: mRect.x, mRect.y, mRect.width, mRect.height); michael@0: michael@0: // Now dump all of the children on all of the child lists michael@0: ChildListIterator lists(this); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: IndentBy(out, aIndent); michael@0: if (lists.CurrentID() != kPrincipalList) { michael@0: fprintf(out, "\n", mozilla::layout::ChildListName(lists.CurrentID())); michael@0: } michael@0: else { michael@0: fprintf(out, "\n"); michael@0: } michael@0: aIndent++; michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* kid = childFrames.get(); michael@0: kid->DumpRegressionData(aPresContext, out, aIndent); michael@0: } michael@0: aIndent--; michael@0: IndentBy(out, aIndent); michael@0: fprintf(out, "\n"); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: nsIFrame::IsFrameSelected() const michael@0: { michael@0: NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(), michael@0: "use the public IsSelected() instead"); michael@0: return nsRange::IsNodeSelected(GetContent(), 0, michael@0: GetContent()->GetChildCount()); michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) michael@0: { michael@0: NS_PRECONDITION(outPoint != nullptr, "Null parameter"); michael@0: nsRect contentRect = GetContentRect() - GetPosition(); michael@0: nsPoint pt = contentRect.TopLeft(); michael@0: if (mContent) michael@0: { michael@0: nsIContent* newContent = mContent->GetParent(); michael@0: if (newContent){ michael@0: int32_t newOffset = newContent->IndexOf(mContent); michael@0: michael@0: bool isRTL = (NS_GET_EMBEDDING_LEVEL(this) & 1) == 1; michael@0: if ((!isRTL && inOffset > newOffset) || michael@0: (isRTL && inOffset <= newOffset)) { michael@0: pt = contentRect.TopRight(); michael@0: } michael@0: } michael@0: } michael@0: *outPoint = pt; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::GetChildFrameContainingOffset(int32_t inContentOffset, bool inHint, int32_t* outFrameContentOffset, nsIFrame **outChildFrame) michael@0: { michael@0: NS_PRECONDITION(outChildFrame && outFrameContentOffset, "Null parameter"); michael@0: *outFrameContentOffset = (int32_t)inHint; michael@0: //the best frame to reflect any given offset would be a visible frame if possible michael@0: //i.e. we are looking for a valid frame to place the blinking caret michael@0: nsRect rect = GetRect(); michael@0: if (!rect.width || !rect.height) michael@0: { michael@0: //if we have a 0 width or height then lets look for another frame that possibly has michael@0: //the same content. If we have no frames in flow then just let us return 'this' frame michael@0: nsIFrame* nextFlow = GetNextInFlow(); michael@0: if (nextFlow) michael@0: return nextFlow->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame); michael@0: } michael@0: *outChildFrame = this; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // michael@0: // What I've pieced together about this routine: michael@0: // Starting with a block frame (from which a line frame can be gotten) michael@0: // and a line number, drill down and get the first/last selectable michael@0: // frame on that line, depending on aPos->mDirection. michael@0: // aOutSideLimit != 0 means ignore aLineStart, instead work from michael@0: // the end (if > 0) or beginning (if < 0). michael@0: // michael@0: nsresult michael@0: nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext, michael@0: nsPeekOffsetStruct *aPos, michael@0: nsIFrame *aBlockFrame, michael@0: int32_t aLineStart, michael@0: int8_t aOutSideLimit michael@0: ) michael@0: { michael@0: //magic numbers aLineStart will be -1 for end of block 0 will be start of block michael@0: if (!aBlockFrame || !aPos) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: aPos->mResultFrame = nullptr; michael@0: aPos->mResultContent = nullptr; michael@0: aPos->mAttachForward = (aPos->mDirection == eDirNext); michael@0: michael@0: nsAutoLineIterator it = aBlockFrame->GetLineIterator(); michael@0: if (!it) michael@0: return NS_ERROR_FAILURE; michael@0: int32_t searchingLine = aLineStart; michael@0: int32_t countLines = it->GetNumLines(); michael@0: if (aOutSideLimit > 0) //start at end michael@0: searchingLine = countLines; michael@0: else if (aOutSideLimit <0)//start at beginning michael@0: searchingLine = -1;//"next" will be 0 michael@0: else michael@0: if ((aPos->mDirection == eDirPrevious && searchingLine == 0) || michael@0: (aPos->mDirection == eDirNext && searchingLine >= (countLines -1) )){ michael@0: //we need to jump to new block frame. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: int32_t lineFrameCount; michael@0: nsIFrame *resultFrame = nullptr; michael@0: nsIFrame *farStoppingFrame = nullptr; //we keep searching until we find a "this" frame then we go to next line michael@0: nsIFrame *nearStoppingFrame = nullptr; //if we are backing up from edge, stop here michael@0: nsIFrame *firstFrame; michael@0: nsIFrame *lastFrame; michael@0: nsRect rect; michael@0: bool isBeforeFirstFrame, isAfterLastFrame; michael@0: bool found = false; michael@0: michael@0: nsresult result = NS_OK; michael@0: while (!found) michael@0: { michael@0: if (aPos->mDirection == eDirPrevious) michael@0: searchingLine --; michael@0: else michael@0: searchingLine ++; michael@0: if ((aPos->mDirection == eDirPrevious && searchingLine < 0) || michael@0: (aPos->mDirection == eDirNext && searchingLine >= countLines )) michael@0: { michael@0: //we need to jump to new block frame. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: uint32_t lineFlags; michael@0: result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount, michael@0: rect, &lineFlags); michael@0: if (!lineFrameCount) michael@0: continue; michael@0: if (NS_SUCCEEDED(result)){ michael@0: lastFrame = firstFrame; michael@0: for (;lineFrameCount > 1;lineFrameCount --){ michael@0: //result = lastFrame->GetNextSibling(&lastFrame, searchingLine); michael@0: result = it->GetNextSiblingOnLine(lastFrame, searchingLine); michael@0: if (NS_FAILED(result) || !lastFrame){ michael@0: NS_ERROR("GetLine promised more frames than could be found"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: GetLastLeaf(aPresContext, &lastFrame); michael@0: michael@0: if (aPos->mDirection == eDirNext){ michael@0: nearStoppingFrame = firstFrame; michael@0: farStoppingFrame = lastFrame; michael@0: } michael@0: else{ michael@0: nearStoppingFrame = lastFrame; michael@0: farStoppingFrame = firstFrame; michael@0: } michael@0: nsPoint offset; michael@0: nsView * view; //used for call of get offset from view michael@0: aBlockFrame->GetOffsetFromView(offset,&view); michael@0: nscoord newDesiredX = aPos->mDesiredX - offset.x;//get desired x into blockframe coordinates! michael@0: result = it->FindFrameAt(searchingLine, newDesiredX, &resultFrame, &isBeforeFirstFrame, &isAfterLastFrame); michael@0: if(NS_FAILED(result)) michael@0: continue; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(result) && resultFrame) michael@0: { michael@0: //check to see if this is ANOTHER blockframe inside the other one if so then call into its lines michael@0: nsAutoLineIterator newIt = resultFrame->GetLineIterator(); michael@0: if (newIt) michael@0: { michael@0: aPos->mResultFrame = resultFrame; michael@0: return NS_OK; michael@0: } michael@0: //resultFrame is not a block frame michael@0: result = NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr frameTraversal; michael@0: result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), michael@0: aPresContext, resultFrame, michael@0: ePostOrder, michael@0: false, // aVisual michael@0: aPos->mScrollViewStop, michael@0: false // aFollowOOFs michael@0: ); michael@0: if (NS_FAILED(result)) michael@0: return result; michael@0: michael@0: nsIFrame *storeOldResultFrame = resultFrame; michael@0: while ( !found ){ michael@0: nsPoint point; michael@0: point.x = aPos->mDesiredX; michael@0: michael@0: nsRect tempRect = resultFrame->GetRect(); michael@0: nsPoint offset; michael@0: nsView * view; //used for call of get offset from view michael@0: resultFrame->GetOffsetFromView(offset, &view); michael@0: if (!view) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: point.y = tempRect.height + offset.y; michael@0: michael@0: //special check. if we allow non-text selection then we can allow a hit location to fall before a table. michael@0: //otherwise there is no way to get and click signal to fall before a table (it being a line iterator itself) michael@0: nsIPresShell *shell = aPresContext->GetPresShell(); michael@0: if (!shell) michael@0: return NS_ERROR_FAILURE; michael@0: int16_t isEditor = shell->GetSelectionFlags(); michael@0: isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL; michael@0: if ( isEditor ) michael@0: { michael@0: if (resultFrame->GetType() == nsGkAtoms::tableOuterFrame) michael@0: { michael@0: if (((point.x - offset.x + tempRect.x)<0) || ((point.x - offset.x+ tempRect.x)>tempRect.width))//off left/right side michael@0: { michael@0: nsIContent* content = resultFrame->GetContent(); michael@0: if (content) michael@0: { michael@0: nsIContent* parent = content->GetParent(); michael@0: if (parent) michael@0: { michael@0: aPos->mResultContent = parent; michael@0: aPos->mContentOffset = parent->IndexOf(content); michael@0: aPos->mAttachForward = false; michael@0: if ((point.x - offset.x+ tempRect.x)>tempRect.width) michael@0: { michael@0: aPos->mContentOffset++;//go to end of this frame michael@0: aPos->mAttachForward = true; michael@0: } michael@0: //result frame is the result frames parent. michael@0: aPos->mResultFrame = resultFrame->GetParent(); michael@0: return NS_POSITION_BEFORE_TABLE; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!resultFrame->HasView()) michael@0: { michael@0: nsView* view; michael@0: nsPoint offset; michael@0: resultFrame->GetOffsetFromView(offset, &view); michael@0: ContentOffsets offsets = michael@0: resultFrame->GetContentOffsetsFromPoint(point - offset); michael@0: aPos->mResultContent = offsets.content; michael@0: aPos->mContentOffset = offsets.offset; michael@0: aPos->mAttachForward = offsets.associateWithNext; michael@0: if (offsets.content) michael@0: { michael@0: bool selectable; michael@0: resultFrame->IsSelectable(&selectable, nullptr); michael@0: if (selectable) michael@0: { michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (aPos->mDirection == eDirPrevious && (resultFrame == farStoppingFrame)) michael@0: break; michael@0: if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame)) michael@0: break; michael@0: //always try previous on THAT line if that fails go the other way michael@0: frameTraversal->Prev(); michael@0: resultFrame = frameTraversal->CurrentItem(); michael@0: if (!resultFrame) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (!found){ michael@0: resultFrame = storeOldResultFrame; michael@0: michael@0: result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), michael@0: aPresContext, resultFrame, michael@0: eLeaf, michael@0: false, // aVisual michael@0: aPos->mScrollViewStop, michael@0: false // aFollowOOFs michael@0: ); michael@0: } michael@0: while ( !found ){ michael@0: nsPoint point(aPos->mDesiredX, 0); michael@0: nsView* view; michael@0: nsPoint offset; michael@0: resultFrame->GetOffsetFromView(offset, &view); michael@0: ContentOffsets offsets = michael@0: resultFrame->GetContentOffsetsFromPoint(point - offset); michael@0: aPos->mResultContent = offsets.content; michael@0: aPos->mContentOffset = offsets.offset; michael@0: aPos->mAttachForward = offsets.associateWithNext; michael@0: if (offsets.content) michael@0: { michael@0: bool selectable; michael@0: resultFrame->IsSelectable(&selectable, nullptr); michael@0: if (selectable) michael@0: { michael@0: found = true; michael@0: if (resultFrame == farStoppingFrame) michael@0: aPos->mAttachForward = false; michael@0: else michael@0: aPos->mAttachForward = true; michael@0: break; michael@0: } michael@0: } michael@0: if (aPos->mDirection == eDirPrevious && (resultFrame == nearStoppingFrame)) michael@0: break; michael@0: if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame)) michael@0: break; michael@0: //previous didnt work now we try "next" michael@0: frameTraversal->Next(); michael@0: nsIFrame *tempFrame = frameTraversal->CurrentItem(); michael@0: if (!tempFrame) michael@0: break; michael@0: resultFrame = tempFrame; michael@0: } michael@0: aPos->mResultFrame = resultFrame; michael@0: } michael@0: else { michael@0: //we need to jump to new block frame. michael@0: aPos->mAmount = eSelectLine; michael@0: aPos->mStartOffset = 0; michael@0: aPos->mAttachForward = !(aPos->mDirection == eDirNext); michael@0: if (aPos->mDirection == eDirPrevious) michael@0: aPos->mStartOffset = -1;//start from end michael@0: return aBlockFrame->PeekOffset(aPos); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame::CaretPosition michael@0: nsIFrame::GetExtremeCaretPosition(bool aStart) michael@0: { michael@0: CaretPosition result; michael@0: michael@0: FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0); michael@0: FrameContentRange range = GetRangeForFrame(targetFrame.frame); michael@0: result.mResultContent = range.content; michael@0: result.mContentOffset = aStart ? range.start : range.end; michael@0: return result; michael@0: } michael@0: michael@0: // Find the first (or last) descendant of the given frame michael@0: // which is either a block frame or a BRFrame. michael@0: static nsContentAndOffset michael@0: FindBlockFrameOrBR(nsIFrame* aFrame, nsDirection aDirection) michael@0: { michael@0: nsContentAndOffset result; michael@0: result.mContent = nullptr; michael@0: result.mOffset = 0; michael@0: michael@0: if (aFrame->IsGeneratedContentFrame()) michael@0: return result; michael@0: michael@0: // Treat form controls as inline leaves michael@0: // XXX we really need a way to determine whether a frame is inline-level michael@0: nsIFormControlFrame* fcf = do_QueryFrame(aFrame); michael@0: if (fcf) michael@0: return result; michael@0: michael@0: // Check the frame itself michael@0: // Fall through block-in-inline split frames because their mContent is michael@0: // the content of the inline frames they were created from. The michael@0: // first/last child of such frames is the real block frame we're michael@0: // looking for. michael@0: if ((nsLayoutUtils::GetAsBlock(aFrame) && michael@0: !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) || michael@0: aFrame->GetType() == nsGkAtoms::brFrame) { michael@0: nsIContent* content = aFrame->GetContent(); michael@0: result.mContent = content->GetParent(); michael@0: // In some cases (bug 310589, bug 370174) we end up here with a null content. michael@0: // This probably shouldn't ever happen, but since it sometimes does, we want michael@0: // to avoid crashing here. michael@0: NS_ASSERTION(result.mContent, "Unexpected orphan content"); michael@0: if (result.mContent) michael@0: result.mOffset = result.mContent->IndexOf(content) + michael@0: (aDirection == eDirPrevious ? 1 : 0); michael@0: return result; michael@0: } michael@0: michael@0: // If this is a preformatted text frame, see if it ends with a newline michael@0: if (aFrame->HasSignificantTerminalNewline()) { michael@0: int32_t startOffset, endOffset; michael@0: aFrame->GetOffsets(startOffset, endOffset); michael@0: result.mContent = aFrame->GetContent(); michael@0: result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1); michael@0: return result; michael@0: } michael@0: michael@0: // Iterate over children and call ourselves recursively michael@0: if (aDirection == eDirPrevious) { michael@0: nsIFrame* child = aFrame->GetLastChild(nsIFrame::kPrincipalList); michael@0: while(child && !result.mContent) { michael@0: result = FindBlockFrameOrBR(child, aDirection); michael@0: child = child->GetPrevSibling(); michael@0: } michael@0: } else { // eDirNext michael@0: nsIFrame* child = aFrame->GetFirstPrincipalChild(); michael@0: while(child && !result.mContent) { michael@0: result = FindBlockFrameOrBR(child, aDirection); michael@0: child = child->GetNextSibling(); michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos) michael@0: { michael@0: nsIFrame* frame = this; michael@0: nsContentAndOffset blockFrameOrBR; michael@0: blockFrameOrBR.mContent = nullptr; michael@0: bool reachedBlockAncestor = false; michael@0: michael@0: // Go through containing frames until reaching a block frame. michael@0: // In each step, search the previous (or next) siblings for the closest michael@0: // "stop frame" (a block frame or a BRFrame). michael@0: // If found, set it to be the selection boundray and abort. michael@0: michael@0: if (aPos->mDirection == eDirPrevious) { michael@0: while (!reachedBlockAncestor) { michael@0: nsIFrame* parent = frame->GetParent(); michael@0: // Treat a frame associated with the root content as if it were a block frame. michael@0: if (!frame->mContent || !frame->mContent->GetParent()) { michael@0: reachedBlockAncestor = true; michael@0: break; michael@0: } michael@0: nsIFrame* sibling = frame->GetPrevSibling(); michael@0: while (sibling && !blockFrameOrBR.mContent) { michael@0: blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirPrevious); michael@0: sibling = sibling->GetPrevSibling(); michael@0: } michael@0: if (blockFrameOrBR.mContent) { michael@0: aPos->mResultContent = blockFrameOrBR.mContent; michael@0: aPos->mContentOffset = blockFrameOrBR.mOffset; michael@0: break; michael@0: } michael@0: frame = parent; michael@0: reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr); michael@0: } michael@0: if (reachedBlockAncestor) { // no "stop frame" found michael@0: aPos->mResultContent = frame->GetContent(); michael@0: aPos->mContentOffset = 0; michael@0: } michael@0: } else { // eDirNext michael@0: while (!reachedBlockAncestor) { michael@0: nsIFrame* parent = frame->GetParent(); michael@0: // Treat a frame associated with the root content as if it were a block frame. michael@0: if (!frame->mContent || !frame->mContent->GetParent()) { michael@0: reachedBlockAncestor = true; michael@0: break; michael@0: } michael@0: nsIFrame* sibling = frame; michael@0: while (sibling && !blockFrameOrBR.mContent) { michael@0: blockFrameOrBR = FindBlockFrameOrBR(sibling, eDirNext); michael@0: sibling = sibling->GetNextSibling(); michael@0: } michael@0: if (blockFrameOrBR.mContent) { michael@0: aPos->mResultContent = blockFrameOrBR.mContent; michael@0: aPos->mContentOffset = blockFrameOrBR.mOffset; michael@0: break; michael@0: } michael@0: frame = parent; michael@0: reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr); michael@0: } michael@0: if (reachedBlockAncestor) { // no "stop frame" found michael@0: aPos->mResultContent = frame->GetContent(); michael@0: if (aPos->mResultContent) michael@0: aPos->mContentOffset = aPos->mResultContent->GetChildCount(); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Determine movement direction relative to frame michael@0: static bool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection, bool aVisual) michael@0: { michael@0: bool isReverseDirection = aVisual ? michael@0: (NS_GET_EMBEDDING_LEVEL(frame) & 1) != (NS_GET_BASE_LEVEL(frame) & 1) : false; michael@0: return aDirection == (isReverseDirection ? eDirPrevious : eDirNext); michael@0: } michael@0: michael@0: nsresult michael@0: nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) michael@0: { michael@0: if (!aPos) michael@0: return NS_ERROR_NULL_POINTER; michael@0: nsresult result = NS_ERROR_FAILURE; michael@0: michael@0: if (mState & NS_FRAME_IS_DIRTY) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Translate content offset to be relative to frame michael@0: FrameContentRange range = GetRangeForFrame(this); michael@0: int32_t offset = aPos->mStartOffset - range.start; michael@0: nsIFrame* current = this; michael@0: michael@0: switch (aPos->mAmount) { michael@0: case eSelectCharacter: michael@0: case eSelectCluster: michael@0: { michael@0: bool eatingNonRenderableWS = false; michael@0: nsIFrame::FrameSearchResult peekSearchState = CONTINUE; michael@0: bool jumpedLine = false; michael@0: bool movedOverNonSelectableText = false; michael@0: michael@0: while (peekSearchState != FOUND) { michael@0: bool movingInFrameDirection = michael@0: IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual); michael@0: michael@0: if (eatingNonRenderableWS) michael@0: peekSearchState = current->PeekOffsetNoAmount(movingInFrameDirection, &offset); michael@0: else michael@0: peekSearchState = current->PeekOffsetCharacter(movingInFrameDirection, &offset, michael@0: aPos->mAmount == eSelectCluster); michael@0: michael@0: movedOverNonSelectableText |= (peekSearchState == CONTINUE_UNSELECTABLE); michael@0: michael@0: if (peekSearchState != FOUND) { michael@0: result = michael@0: current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, michael@0: aPos->mJumpLines, aPos->mScrollViewStop, michael@0: ¤t, &offset, &jumpedLine); michael@0: if (NS_FAILED(result)) michael@0: return result; michael@0: michael@0: // If we jumped lines, it's as if we found a character, but we still need michael@0: // to eat non-renderable content on the new line. michael@0: if (jumpedLine) michael@0: eatingNonRenderableWS = true; michael@0: } michael@0: michael@0: // Found frame, but because we moved over non selectable text we want the offset michael@0: // to be at the frame edge. michael@0: if (peekSearchState == FOUND && movedOverNonSelectableText) michael@0: { michael@0: int32_t start, end; michael@0: current->GetOffsets(start, end); michael@0: offset = aPos->mDirection == eDirNext ? 0 : end - start; michael@0: } michael@0: } michael@0: michael@0: // Set outputs michael@0: range = GetRangeForFrame(current); michael@0: aPos->mResultFrame = current; michael@0: aPos->mResultContent = range.content; michael@0: // Output offset is relative to content, not frame michael@0: aPos->mContentOffset = offset < 0 ? range.end : range.start + offset; michael@0: // If we're dealing with a text frame and moving backward positions us at michael@0: // the end of that line, decrease the offset by one to make sure that michael@0: // we're placed before the linefeed character on the previous line. michael@0: if (offset < 0 && jumpedLine && michael@0: aPos->mDirection == eDirPrevious && michael@0: current->HasSignificantTerminalNewline()) { michael@0: --aPos->mContentOffset; michael@0: } michael@0: michael@0: break; michael@0: } michael@0: case eSelectWordNoSpace: michael@0: // eSelectWordNoSpace means that we should not be eating any whitespace when michael@0: // moving to the adjacent word. This means that we should set aPos-> michael@0: // mWordMovementType to eEndWord if we're moving forwards, and to eStartWord michael@0: // if we're moving backwards. michael@0: if (aPos->mDirection == eDirPrevious) { michael@0: aPos->mWordMovementType = eStartWord; michael@0: } else { michael@0: aPos->mWordMovementType = eEndWord; michael@0: } michael@0: // Intentionally fall through the eSelectWord case. michael@0: case eSelectWord: michael@0: { michael@0: // wordSelectEatSpace means "are we looking for a boundary between whitespace michael@0: // and non-whitespace (in the direction we're moving in)". michael@0: // It is true when moving forward and looking for a beginning of a word, or michael@0: // when moving backwards and looking for an end of a word. michael@0: bool wordSelectEatSpace; michael@0: if (aPos->mWordMovementType != eDefaultBehavior) { michael@0: // aPos->mWordMovementType possible values: michael@0: // eEndWord: eat the space if we're moving backwards michael@0: // eStartWord: eat the space if we're moving forwards michael@0: wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) == (aPos->mDirection == eDirPrevious)); michael@0: } michael@0: else { michael@0: // Use the hidden preference which is based on operating system behavior. michael@0: // This pref only affects whether moving forward by word should go to the end of this word or start of the next word. michael@0: // When going backwards, the start of the word is always used, on every operating system. michael@0: wordSelectEatSpace = aPos->mDirection == eDirNext && michael@0: Preferences::GetBool("layout.word_select.eat_space_to_next_word"); michael@0: } michael@0: michael@0: // mSawBeforeType means "we already saw characters of the type michael@0: // before the boundary we're looking for". Examples: michael@0: // 1. If we're moving forward, looking for a word beginning (i.e. a boundary michael@0: // between whitespace and non-whitespace), then eatingWS==true means michael@0: // "we already saw some whitespace". michael@0: // 2. If we're moving backward, looking for a word beginning (i.e. a boundary michael@0: // between non-whitespace and whitespace), then eatingWS==true means michael@0: // "we already saw some non-whitespace". michael@0: PeekWordState state; michael@0: int32_t offsetAdjustment = 0; michael@0: bool done = false; michael@0: while (!done) { michael@0: bool movingInFrameDirection = michael@0: IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual); michael@0: michael@0: done = current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace, michael@0: aPos->mIsKeyboardSelect, &offset, &state) == FOUND; michael@0: michael@0: if (!done) { michael@0: nsIFrame* nextFrame; michael@0: int32_t nextFrameOffset; michael@0: bool jumpedLine; michael@0: result = michael@0: current->GetFrameFromDirection(aPos->mDirection, aPos->mVisual, michael@0: aPos->mJumpLines, aPos->mScrollViewStop, michael@0: &nextFrame, &nextFrameOffset, &jumpedLine); michael@0: // We can't jump lines if we're looking for whitespace following michael@0: // non-whitespace, and we already encountered non-whitespace. michael@0: if (NS_FAILED(result) || michael@0: (jumpedLine && !wordSelectEatSpace && state.mSawBeforeType)) { michael@0: done = true; michael@0: // If we've crossed the line boundary, check to make sure that we michael@0: // have not consumed a trailing newline as whitesapce if it's significant. michael@0: if (jumpedLine && wordSelectEatSpace && michael@0: current->HasSignificantTerminalNewline()) { michael@0: offsetAdjustment = -1; michael@0: } michael@0: } else { michael@0: if (jumpedLine) { michael@0: state.mContext.Truncate(); michael@0: } michael@0: current = nextFrame; michael@0: offset = nextFrameOffset; michael@0: // Jumping a line is equivalent to encountering whitespace michael@0: if (wordSelectEatSpace && jumpedLine) michael@0: state.SetSawBeforeType(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Set outputs michael@0: range = GetRangeForFrame(current); michael@0: aPos->mResultFrame = current; michael@0: aPos->mResultContent = range.content; michael@0: // Output offset is relative to content, not frame michael@0: aPos->mContentOffset = (offset < 0 ? range.end : range.start + offset) + offsetAdjustment; michael@0: break; michael@0: } michael@0: case eSelectLine : michael@0: { michael@0: nsAutoLineIterator iter; michael@0: nsIFrame *blockFrame = this; michael@0: michael@0: while (NS_FAILED(result)){ michael@0: int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame); michael@0: if (thisLine < 0) michael@0: return NS_ERROR_FAILURE; michael@0: iter = blockFrame->GetLineIterator(); michael@0: NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?"); michael@0: result = NS_OK; michael@0: michael@0: int edgeCase = 0;//no edge case. this should look at thisLine michael@0: michael@0: bool doneLooping = false;//tells us when no more block frames hit. michael@0: //this part will find a frame or a block frame. if it's a block frame michael@0: //it will "drill down" to find a viable frame or it will return an error. michael@0: nsIFrame *lastFrame = this; michael@0: do { michael@0: result = nsFrame::GetNextPrevLineFromeBlockFrame(PresContext(), michael@0: aPos, michael@0: blockFrame, michael@0: thisLine, michael@0: edgeCase //start from thisLine michael@0: ); michael@0: if (NS_SUCCEEDED(result) && (!aPos->mResultFrame || aPos->mResultFrame == lastFrame))//we came back to same spot! keep going michael@0: { michael@0: aPos->mResultFrame = nullptr; michael@0: if (aPos->mDirection == eDirPrevious) michael@0: thisLine--; michael@0: else michael@0: thisLine++; michael@0: } michael@0: else //if failure or success with different frame. michael@0: doneLooping = true; //do not continue with while loop michael@0: michael@0: lastFrame = aPos->mResultFrame; //set last frame michael@0: michael@0: if (NS_SUCCEEDED(result) && aPos->mResultFrame michael@0: && blockFrame != aPos->mResultFrame)// make sure block element is not the same as the one we had before michael@0: { michael@0: /* SPECIAL CHECK FOR TABLE NAVIGATION michael@0: tables need to navigate also and the frame that supports it is nsTableRowGroupFrame which is INSIDE michael@0: nsTableOuterFrame. if we have stumbled onto an nsTableOuter we need to drill into nsTableRowGroup michael@0: if we hit a header or footer that's ok just go into them, michael@0: */ michael@0: bool searchTableBool = false; michael@0: if (aPos->mResultFrame->GetType() == nsGkAtoms::tableOuterFrame || michael@0: aPos->mResultFrame->GetType() == nsGkAtoms::tableCellFrame) michael@0: { michael@0: nsIFrame *frame = aPos->mResultFrame->GetFirstPrincipalChild(); michael@0: //got the table frame now michael@0: while(frame) //ok time to drill down to find iterator michael@0: { michael@0: iter = frame->GetLineIterator(); michael@0: if (iter) michael@0: { michael@0: aPos->mResultFrame = frame; michael@0: searchTableBool = true; michael@0: result = NS_OK; michael@0: break; //while(frame) michael@0: } michael@0: result = NS_ERROR_FAILURE; michael@0: frame = frame->GetFirstPrincipalChild(); michael@0: } michael@0: } michael@0: michael@0: if (!searchTableBool) { michael@0: iter = aPos->mResultFrame->GetLineIterator(); michael@0: result = iter ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: if (NS_SUCCEEDED(result) && iter)//we've struck another block element! michael@0: { michael@0: doneLooping = false; michael@0: if (aPos->mDirection == eDirPrevious) michael@0: edgeCase = 1;//far edge, search from end backwards michael@0: else michael@0: edgeCase = -1;//near edge search from beginning onwards michael@0: thisLine=0;//this line means nothing now. michael@0: //everything else means something so keep looking "inside" the block michael@0: blockFrame = aPos->mResultFrame; michael@0: michael@0: } michael@0: else michael@0: { michael@0: result = NS_OK;//THIS is to mean that everything is ok to the containing while loop michael@0: break; michael@0: } michael@0: } michael@0: } while (!doneLooping); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: case eSelectParagraph: michael@0: return PeekOffsetParagraph(aPos); michael@0: michael@0: case eSelectBeginLine: michael@0: case eSelectEndLine: michael@0: { michael@0: // Adjusted so that the caret can't get confused when content changes michael@0: nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this); michael@0: int32_t thisLine = nsFrame::GetLineNumber(blockFrame, aPos->mScrollViewStop, &blockFrame); michael@0: if (thisLine < 0) michael@0: return NS_ERROR_FAILURE; michael@0: nsAutoLineIterator it = blockFrame->GetLineIterator(); michael@0: NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?"); michael@0: michael@0: int32_t lineFrameCount; michael@0: nsIFrame *firstFrame; michael@0: nsRect usedRect; michael@0: uint32_t lineFlags; michael@0: nsIFrame* baseFrame = nullptr; michael@0: bool endOfLine = (eSelectEndLine == aPos->mAmount); michael@0: michael@0: if (aPos->mVisual && PresContext()->BidiEnabled()) { michael@0: bool lineIsRTL = it->GetDirection(); michael@0: bool isReordered; michael@0: nsIFrame *lastFrame; michael@0: result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame); michael@0: baseFrame = endOfLine ? lastFrame : firstFrame; michael@0: if (baseFrame) { michael@0: nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(baseFrame); michael@0: // If the direction of the frame on the edge is opposite to that of the line, michael@0: // we'll need to drill down to its opposite end, so reverse endOfLine. michael@0: if ((embeddingLevel & 1) == !lineIsRTL) michael@0: endOfLine = !endOfLine; michael@0: } michael@0: } else { michael@0: it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect, &lineFlags); michael@0: michael@0: nsIFrame* frame = firstFrame; michael@0: for (int32_t count = lineFrameCount; count; michael@0: --count, frame = frame->GetNextSibling()) { michael@0: if (!frame->IsGeneratedContentFrame()) { michael@0: baseFrame = frame; michael@0: if (!endOfLine) michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: if (!baseFrame) michael@0: return NS_ERROR_FAILURE; michael@0: FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame, michael@0: endOfLine, 0); michael@0: FrameContentRange range = GetRangeForFrame(targetFrame.frame); michael@0: aPos->mResultContent = range.content; michael@0: aPos->mContentOffset = endOfLine ? range.end : range.start; michael@0: if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) { michael@0: // Do not position the caret after the terminating newline if we're michael@0: // trying to move to the end of line (see bug 596506) michael@0: --aPos->mContentOffset; michael@0: } michael@0: aPos->mResultFrame = targetFrame.frame; michael@0: aPos->mAttachForward = (aPos->mContentOffset == range.start); michael@0: if (!range.content) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: default: michael@0: { michael@0: NS_ASSERTION(false, "Invalid amount"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame::FrameSearchResult michael@0: nsFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset) michael@0: { michael@0: NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); michael@0: // Sure, we can stop right here. michael@0: return FOUND; michael@0: } michael@0: michael@0: nsIFrame::FrameSearchResult michael@0: nsFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset, michael@0: bool aRespectClusters) michael@0: { michael@0: NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); michael@0: int32_t startOffset = *aOffset; michael@0: // A negative offset means "end of frame", which in our case means offset 1. michael@0: if (startOffset < 0) michael@0: startOffset = 1; michael@0: if (aForward == (startOffset == 0)) { michael@0: // We're before the frame and moving forward, or after it and moving backwards: michael@0: // skip to the other side and we're done. michael@0: *aOffset = 1 - startOffset; michael@0: return FOUND; michael@0: } michael@0: return CONTINUE; michael@0: } michael@0: michael@0: nsIFrame::FrameSearchResult michael@0: nsFrame::PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect, michael@0: int32_t* aOffset, PeekWordState* aState) michael@0: { michael@0: NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); michael@0: int32_t startOffset = *aOffset; michael@0: // This isn't text, so truncate the context michael@0: aState->mContext.Truncate(); michael@0: if (startOffset < 0) michael@0: startOffset = 1; michael@0: if (aForward == (startOffset == 0)) { michael@0: // We're before the frame and moving forward, or after it and moving backwards. michael@0: // If we're looking for non-whitespace, we found it (without skipping this frame). michael@0: if (!aState->mAtStart) { michael@0: if (aState->mLastCharWasPunctuation) { michael@0: // We're not punctuation, so this is a punctuation boundary. michael@0: if (BreakWordBetweenPunctuation(aState, aForward, false, false, aIsKeyboardSelect)) michael@0: return FOUND; michael@0: } else { michael@0: // This is not a punctuation boundary. michael@0: if (aWordSelectEatSpace && aState->mSawBeforeType) michael@0: return FOUND; michael@0: } michael@0: } michael@0: // Otherwise skip to the other side and note that we encountered non-whitespace. michael@0: *aOffset = 1 - startOffset; michael@0: aState->Update(false, // not punctuation michael@0: false // not whitespace michael@0: ); michael@0: if (!aWordSelectEatSpace) michael@0: aState->SetSawBeforeType(); michael@0: } michael@0: return CONTINUE; michael@0: } michael@0: michael@0: bool michael@0: nsFrame::BreakWordBetweenPunctuation(const PeekWordState* aState, michael@0: bool aForward, michael@0: bool aPunctAfter, bool aWhitespaceAfter, michael@0: bool aIsKeyboardSelect) michael@0: { michael@0: NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation, michael@0: "Call this only at punctuation boundaries"); michael@0: if (aState->mLastCharWasWhitespace) { michael@0: // We always stop between whitespace and punctuation michael@0: return true; michael@0: } michael@0: if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) { michael@0: // When this pref is false, we never stop at a punctuation boundary unless michael@0: // it's followed by whitespace (in the relevant direction). michael@0: return aWhitespaceAfter; michael@0: } michael@0: if (!aIsKeyboardSelect) { michael@0: // mouse caret movement (e.g. word selection) always stops at every punctuation boundary michael@0: return true; michael@0: } michael@0: bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter; michael@0: if (!afterPunct) { michael@0: // keyboard caret movement only stops after punctuation (in content order) michael@0: return false; michael@0: } michael@0: // Stop only if we've seen some non-punctuation since the last whitespace; michael@0: // don't stop after punctuation that follows whitespace. michael@0: return aState->mSeenNonPunctuationSinceWhitespace; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::CheckVisibility(nsPresContext* , int32_t , int32_t , bool , bool *, bool *) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: michael@0: int32_t michael@0: nsFrame::GetLineNumber(nsIFrame *aFrame, bool aLockScroll, nsIFrame** aContainingBlock) michael@0: { michael@0: NS_ASSERTION(aFrame, "null aFrame"); michael@0: nsFrameManager* frameManager = aFrame->PresContext()->FrameManager(); michael@0: nsIFrame *blockFrame = aFrame; michael@0: nsIFrame *thisBlock; michael@0: nsAutoLineIterator it; michael@0: nsresult result = NS_ERROR_FAILURE; michael@0: while (NS_FAILED(result) && blockFrame) michael@0: { michael@0: thisBlock = blockFrame; michael@0: if (thisBlock->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { michael@0: //if we are searching for a frame that is not in flow we will not find it. michael@0: //we must instead look for its placeholder michael@0: if (thisBlock->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { michael@0: // abspos continuations don't have placeholders, get the fif michael@0: thisBlock = thisBlock->FirstInFlow(); michael@0: } michael@0: thisBlock = frameManager->GetPlaceholderFrameFor(thisBlock); michael@0: if (!thisBlock) michael@0: return -1; michael@0: } michael@0: blockFrame = thisBlock->GetParent(); michael@0: result = NS_OK; michael@0: if (blockFrame) { michael@0: if (aLockScroll && blockFrame->GetType() == nsGkAtoms::scrollFrame) michael@0: return -1; michael@0: it = blockFrame->GetLineIterator(); michael@0: if (!it) michael@0: result = NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: if (!blockFrame || !it) michael@0: return -1; michael@0: michael@0: if (aContainingBlock) michael@0: *aContainingBlock = blockFrame; michael@0: return it->FindLineContaining(thisBlock); michael@0: } michael@0: michael@0: nsresult michael@0: nsIFrame::GetFrameFromDirection(nsDirection aDirection, bool aVisual, michael@0: bool aJumpLines, bool aScrollViewStop, michael@0: nsIFrame** aOutFrame, int32_t* aOutOffset, bool* aOutJumpedLine) michael@0: { michael@0: nsresult result; michael@0: michael@0: if (!aOutFrame || !aOutOffset || !aOutJumpedLine) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsPresContext* presContext = PresContext(); michael@0: *aOutFrame = nullptr; michael@0: *aOutOffset = 0; michael@0: *aOutJumpedLine = false; michael@0: michael@0: // Find the prev/next selectable frame michael@0: bool selectable = false; michael@0: nsIFrame *traversedFrame = this; michael@0: while (!selectable) { michael@0: nsIFrame *blockFrame; michael@0: michael@0: int32_t thisLine = nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &blockFrame); michael@0: if (thisLine < 0) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsAutoLineIterator it = blockFrame->GetLineIterator(); michael@0: NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?"); michael@0: michael@0: bool atLineEdge; michael@0: nsIFrame *firstFrame; michael@0: nsIFrame *lastFrame; michael@0: if (aVisual && presContext->BidiEnabled()) { michael@0: bool lineIsRTL = it->GetDirection(); michael@0: bool isReordered; michael@0: result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame); michael@0: nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame; michael@0: if (*framePtr) { michael@0: nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr); michael@0: if ((((embeddingLevel & 1) && lineIsRTL) || (!(embeddingLevel & 1) && !lineIsRTL)) == michael@0: (aDirection == eDirPrevious)) { michael@0: nsFrame::GetFirstLeaf(presContext, framePtr); michael@0: } else { michael@0: nsFrame::GetLastLeaf(presContext, framePtr); michael@0: } michael@0: atLineEdge = *framePtr == traversedFrame; michael@0: } else { michael@0: atLineEdge = true; michael@0: } michael@0: } else { michael@0: nsRect nonUsedRect; michael@0: int32_t lineFrameCount; michael@0: uint32_t lineFlags; michael@0: result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect, michael@0: &lineFlags); michael@0: if (NS_FAILED(result)) michael@0: return result; michael@0: michael@0: if (aDirection == eDirPrevious) { michael@0: nsFrame::GetFirstLeaf(presContext, &firstFrame); michael@0: atLineEdge = firstFrame == traversedFrame; michael@0: } else { // eDirNext michael@0: lastFrame = firstFrame; michael@0: for (;lineFrameCount > 1;lineFrameCount --){ michael@0: result = it->GetNextSiblingOnLine(lastFrame, thisLine); michael@0: if (NS_FAILED(result) || !lastFrame){ michael@0: NS_ERROR("should not be reached nsFrame"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: nsFrame::GetLastLeaf(presContext, &lastFrame); michael@0: atLineEdge = lastFrame == traversedFrame; michael@0: } michael@0: } michael@0: michael@0: if (atLineEdge) { michael@0: *aOutJumpedLine = true; michael@0: if (!aJumpLines) michael@0: return NS_ERROR_FAILURE; //we are done. cannot jump lines michael@0: } michael@0: michael@0: nsCOMPtr frameTraversal; michael@0: result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal), michael@0: presContext, traversedFrame, michael@0: eLeaf, michael@0: aVisual && presContext->BidiEnabled(), michael@0: aScrollViewStop, michael@0: true // aFollowOOFs michael@0: ); michael@0: if (NS_FAILED(result)) michael@0: return result; michael@0: michael@0: if (aDirection == eDirNext) michael@0: frameTraversal->Next(); michael@0: else michael@0: frameTraversal->Prev(); michael@0: michael@0: traversedFrame = frameTraversal->CurrentItem(); michael@0: if (!traversedFrame) michael@0: return NS_ERROR_FAILURE; michael@0: traversedFrame->IsSelectable(&selectable, nullptr); michael@0: } // while (!selectable) michael@0: michael@0: *aOutOffset = (aDirection == eDirNext) ? 0 : -1; michael@0: michael@0: if (aVisual) { michael@0: uint8_t newLevel = NS_GET_EMBEDDING_LEVEL(traversedFrame); michael@0: uint8_t newBaseLevel = NS_GET_BASE_LEVEL(traversedFrame); michael@0: if ((newLevel & 1) != (newBaseLevel & 1)) // The new frame is reverse-direction, go to the other end michael@0: *aOutOffset = -1 - *aOutOffset; michael@0: } michael@0: *aOutFrame = traversedFrame; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const michael@0: { michael@0: nsPoint offset(0,0); michael@0: for (const nsIFrame *f = this; f; f = f->GetParent()) { michael@0: if (f->HasView()) { michael@0: if (aOffset) michael@0: *aOffset = offset; michael@0: return f->GetView(); michael@0: } michael@0: offset += f->GetPosition(); michael@0: } michael@0: michael@0: NS_NOTREACHED("No view on any parent? How did that happen?"); michael@0: return nullptr; michael@0: } michael@0: michael@0: michael@0: /* virtual */ void michael@0: nsFrame::ChildIsDirty(nsIFrame* aChild) michael@0: { michael@0: NS_NOTREACHED("should never be called on a frame that doesn't inherit from " michael@0: "nsContainerFrame"); michael@0: } michael@0: michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: a11y::AccType michael@0: nsFrame::AccessibleType() michael@0: { michael@0: return a11y::eNoType; michael@0: } michael@0: #endif michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty, michael@0: nsIFrame::DestroyOverflowAreas) michael@0: michael@0: bool michael@0: nsIFrame::ClearOverflowRects() michael@0: { michael@0: if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) { michael@0: return false; michael@0: } michael@0: if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { michael@0: Properties().Delete(OverflowAreasProperty()); michael@0: } michael@0: mOverflow.mType = NS_FRAME_OVERFLOW_NONE; michael@0: return true; michael@0: } michael@0: michael@0: /** Create or retrieve the previously stored overflow area, if the frame does michael@0: * not overflow and no creation is required return nullptr. michael@0: * @return pointer to the overflow area rectangle michael@0: */ michael@0: nsOverflowAreas* michael@0: nsIFrame::GetOverflowAreasProperty() michael@0: { michael@0: FrameProperties props = Properties(); michael@0: nsOverflowAreas *overflow = michael@0: static_cast(props.Get(OverflowAreasProperty())); michael@0: michael@0: if (overflow) { michael@0: return overflow; // the property already exists michael@0: } michael@0: michael@0: // The property isn't set yet, so allocate a new rect, set the property, michael@0: // and return the newly allocated rect michael@0: overflow = new nsOverflowAreas; michael@0: props.Set(OverflowAreasProperty(), overflow); michael@0: return overflow; michael@0: } michael@0: michael@0: /** Set the overflowArea rect, storing it as deltas or a separate rect michael@0: * depending on its size in relation to the primary frame rect. michael@0: */ michael@0: bool michael@0: nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas) michael@0: { michael@0: if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) { michael@0: nsOverflowAreas *overflow = michael@0: static_cast(Properties().Get(OverflowAreasProperty())); michael@0: bool changed = *overflow != aOverflowAreas; michael@0: *overflow = aOverflowAreas; michael@0: michael@0: // Don't bother with converting to the deltas form if we already michael@0: // have a property. michael@0: return changed; michael@0: } michael@0: michael@0: const nsRect& vis = aOverflowAreas.VisualOverflow(); michael@0: uint32_t l = -vis.x, // left edge: positive delta is leftwards michael@0: t = -vis.y, // top: positive is upwards michael@0: r = vis.XMost() - mRect.width, // right: positive is rightwards michael@0: b = vis.YMost() - mRect.height; // bottom: positive is downwards michael@0: if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) && michael@0: l <= NS_FRAME_OVERFLOW_DELTA_MAX && michael@0: t <= NS_FRAME_OVERFLOW_DELTA_MAX && michael@0: r <= NS_FRAME_OVERFLOW_DELTA_MAX && michael@0: b <= NS_FRAME_OVERFLOW_DELTA_MAX && michael@0: // we have to check these against zero because we *never* want to michael@0: // set a frame as having no overflow in this function. This is michael@0: // because FinishAndStoreOverflow calls this function prior to michael@0: // SetRect based on whether the overflow areas match aNewSize. michael@0: // In the case where the overflow areas exactly match mRect but michael@0: // do not match aNewSize, we need to store overflow in a property michael@0: // so that our eventual SetRect/SetSize will know that it has to michael@0: // reset our overflow areas. michael@0: (l | t | r | b) != 0) { michael@0: VisualDeltas oldDeltas = mOverflow.mVisualDeltas; michael@0: // It's a "small" overflow area so we store the deltas for each edge michael@0: // directly in the frame, rather than allocating a separate rect. michael@0: // If they're all zero, that's fine; we're setting things to michael@0: // no-overflow. michael@0: mOverflow.mVisualDeltas.mLeft = l; michael@0: mOverflow.mVisualDeltas.mTop = t; michael@0: mOverflow.mVisualDeltas.mRight = r; michael@0: mOverflow.mVisualDeltas.mBottom = b; michael@0: // There was no scrollable overflow before, and there isn't now. michael@0: return oldDeltas != mOverflow.mVisualDeltas; michael@0: } else { michael@0: bool changed = !aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) || michael@0: !aOverflowAreas.VisualOverflow().IsEqualEdges(GetVisualOverflowFromDeltas()); michael@0: michael@0: // it's a large overflow area that we need to store as a property michael@0: mOverflow.mType = NS_FRAME_OVERFLOW_LARGE; michael@0: nsOverflowAreas* overflow = GetOverflowAreasProperty(); michael@0: NS_ASSERTION(overflow, "should have created areas"); michael@0: *overflow = aOverflowAreas; michael@0: return changed; michael@0: } michael@0: } michael@0: michael@0: inline bool michael@0: IsInlineFrame(nsIFrame *aFrame) michael@0: { michael@0: nsIAtom *type = aFrame->GetType(); michael@0: return type == nsGkAtoms::inlineFrame; michael@0: } michael@0: michael@0: /** michael@0: * Compute the union of the border boxes of aFrame and its descendants, michael@0: * in aFrame's coordinate space (if aApplyTransform is false) or its michael@0: * post-transform coordinate space (if aApplyTransform is true). michael@0: */ michael@0: static nsRect michael@0: UnionBorderBoxes(nsIFrame* aFrame, bool aApplyTransform, michael@0: const nsSize* aSizeOverride = nullptr, michael@0: const nsOverflowAreas* aOverflowOverride = nullptr) michael@0: { michael@0: const nsRect bounds(nsPoint(0, 0), michael@0: aSizeOverride ? *aSizeOverride : aFrame->GetSize()); michael@0: michael@0: // Start from our border-box, transformed. See comment below about michael@0: // transform of children. michael@0: nsRect u; michael@0: bool doTransform = aApplyTransform && aFrame->IsTransformed(); michael@0: if (doTransform) { michael@0: u = nsDisplayTransform::TransformRect(bounds, aFrame, michael@0: nsPoint(0, 0), &bounds); michael@0: } else { michael@0: u = bounds; michael@0: } michael@0: michael@0: // Only iterate through the children if the overflow areas suggest michael@0: // that we might need to, and if the frame doesn't clip its overflow michael@0: // anyway. michael@0: if (aOverflowOverride) { michael@0: if (!doTransform && michael@0: bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) && michael@0: bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) { michael@0: return u; michael@0: } michael@0: } else { michael@0: if (!doTransform && michael@0: bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) && michael@0: bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) { michael@0: return u; michael@0: } michael@0: } michael@0: const nsStyleDisplay* disp = aFrame->StyleDisplay(); michael@0: nsIAtom* fType = aFrame->GetType(); michael@0: if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) || michael@0: fType == nsGkAtoms::scrollFrame || michael@0: fType == nsGkAtoms::svgOuterSVGFrame) { michael@0: return u; michael@0: } michael@0: michael@0: nsRect clipPropClipRect; michael@0: bool hasClipPropClip = michael@0: aFrame->GetClipPropClipRect(disp, &clipPropClipRect, bounds.Size()); michael@0: michael@0: // Iterate over all children except pop-ups. michael@0: const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList | michael@0: nsIFrame::kSelectPopupList); michael@0: for (nsIFrame::ChildListIterator childLists(aFrame); michael@0: !childLists.IsDone(); childLists.Next()) { michael@0: if (skip.Contains(childLists.CurrentID())) { michael@0: continue; michael@0: } michael@0: michael@0: nsFrameList children = childLists.CurrentList(); michael@0: for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { michael@0: nsIFrame* child = e.get(); michael@0: // Note that passing |true| for aApplyTransform when michael@0: // child->Preserves3D() is incorrect if our aApplyTransform is michael@0: // false... but the opposite would be as well. This is because michael@0: // elements within a preserve-3d scene are always transformed up michael@0: // to the top of the scene. This means we don't have a michael@0: // mechanism for getting a transform up to an intermediate point michael@0: // within the scene. We choose to over-transform rather than michael@0: // under-transform because this is consistent with other michael@0: // overflow areas. michael@0: nsRect childRect = UnionBorderBoxes(child, true) + michael@0: child->GetPosition(); michael@0: michael@0: if (hasClipPropClip) { michael@0: // Intersect with the clip before transforming. michael@0: childRect.IntersectRect(childRect, clipPropClipRect); michael@0: } michael@0: michael@0: // Note that we transform each child separately according to michael@0: // aFrame's transform, and then union, which gives a different michael@0: // (smaller) result from unioning and then transforming the michael@0: // union. This doesn't match the way we handle overflow areas michael@0: // with 2-D transforms, though it does match the way we handle michael@0: // overflow areas in preserve-3d 3-D scenes. michael@0: if (doTransform && !child->Preserves3D()) { michael@0: childRect = nsDisplayTransform::TransformRect(childRect, aFrame, michael@0: nsPoint(0, 0), &bounds); michael@0: } michael@0: u.UnionRectEdges(u, childRect); michael@0: } michael@0: } michael@0: michael@0: return u; michael@0: } michael@0: michael@0: static void michael@0: ComputeAndIncludeOutlineArea(nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas, michael@0: const nsSize& aNewSize) michael@0: { michael@0: const nsStyleOutline* outline = aFrame->StyleOutline(); michael@0: if (outline->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) { michael@0: return; michael@0: } michael@0: michael@0: nscoord width; michael@0: DebugOnly result = outline->GetOutlineWidth(width); michael@0: NS_ASSERTION(result, "GetOutlineWidth had no cached outline width"); michael@0: if (width <= 0) { michael@0: return; michael@0: } michael@0: michael@0: // When the outline property is set on :-moz-anonymous-block or michael@0: // :-moz-anonymous-positioned-block pseudo-elements, it inherited michael@0: // that outline from the inline that was broken because it michael@0: // contained a block. In that case, we don't want a really wide michael@0: // outline if the block inside the inline is narrow, so union the michael@0: // actual contents of the anonymous blocks. michael@0: nsIFrame *frameForArea = aFrame; michael@0: do { michael@0: nsIAtom *pseudoType = frameForArea->StyleContext()->GetPseudo(); michael@0: if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock && michael@0: pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock) michael@0: break; michael@0: // If we're done, we really want it and all its later siblings. michael@0: frameForArea = frameForArea->GetFirstPrincipalChild(); michael@0: NS_ASSERTION(frameForArea, "anonymous block with no children?"); michael@0: } while (frameForArea); michael@0: michael@0: // Find the union of the border boxes of all descendants, or in michael@0: // the block-in-inline case, all descendants we care about. michael@0: // michael@0: // Note that the interesting perspective-related cases are taken michael@0: // care of by the code that handles those issues for overflow michael@0: // calling FinishAndStoreOverflow again, which in turn calls this michael@0: // function again. We still need to deal with preserve-3d a bit. michael@0: nsRect innerRect; michael@0: if (frameForArea == aFrame) { michael@0: innerRect = UnionBorderBoxes(aFrame, false, &aNewSize, &aOverflowAreas); michael@0: } else { michael@0: for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) { michael@0: nsRect r(UnionBorderBoxes(frameForArea, true)); michael@0: michael@0: // Adjust for offsets transforms up to aFrame's pre-transform michael@0: // (i.e., normal) coordinate space; see comments in michael@0: // UnionBorderBoxes for some of the subtlety here. michael@0: for (nsIFrame *f = frameForArea, *parent = f->GetParent(); michael@0: /* see middle of loop */; michael@0: f = parent, parent = f->GetParent()) { michael@0: r += f->GetPosition(); michael@0: if (parent == aFrame) { michael@0: break; michael@0: } michael@0: if (parent->IsTransformed() && !f->Preserves3D()) { michael@0: r = nsDisplayTransform::TransformRect(r, parent, nsPoint(0, 0)); michael@0: } michael@0: } michael@0: michael@0: innerRect.UnionRect(innerRect, r); michael@0: } michael@0: } michael@0: michael@0: aFrame->Properties().Set(nsIFrame::OutlineInnerRectProperty(), michael@0: new nsRect(innerRect)); michael@0: michael@0: nscoord offset = outline->mOutlineOffset; michael@0: nscoord inflateBy = std::max(width + offset, 0); michael@0: michael@0: // Keep this code (and the storing of properties just above) in michael@0: // sync with GetOutlineInnerRect in nsCSSRendering.cpp. michael@0: nsRect outerRect(innerRect); michael@0: outerRect.Inflate(inflateBy, inflateBy); michael@0: michael@0: nsRect& vo = aOverflowAreas.VisualOverflow(); michael@0: vo.UnionRectEdges(vo, outerRect); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas, michael@0: nsSize aNewSize, nsSize* aOldSize) michael@0: { michael@0: NS_ASSERTION(FrameMaintainsOverflow(this), michael@0: "Don't call - overflow rects not maintained on these SVG frames"); michael@0: michael@0: nsRect bounds(nsPoint(0, 0), aNewSize); michael@0: // Store the passed in overflow area if we are a preserve-3d frame or we have michael@0: // a transform, and it's not just the frame bounds. michael@0: if (Preserves3D() || HasPerspective() || IsTransformed()) { michael@0: if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) || michael@0: !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) { michael@0: michael@0: nsOverflowAreas* initial = michael@0: static_cast(Properties().Get(nsIFrame::InitialOverflowProperty())); michael@0: if (!initial) { michael@0: Properties().Set(nsIFrame::InitialOverflowProperty(), michael@0: new nsOverflowAreas(aOverflowAreas)); michael@0: } else if (initial != &aOverflowAreas) { michael@0: *initial = aOverflowAreas; michael@0: } michael@0: } michael@0: #ifdef DEBUG michael@0: Properties().Set(nsIFrame::DebugInitialOverflowPropertyApplied(), nullptr); michael@0: #endif michael@0: } else { michael@0: #ifdef DEBUG michael@0: Properties().Delete(nsIFrame::DebugInitialOverflowPropertyApplied()); michael@0: #endif michael@0: } michael@0: michael@0: // This is now called FinishAndStoreOverflow() instead of michael@0: // StoreOverflow() because frame-generic ways of adding overflow michael@0: // can happen here, e.g. CSS2 outline and native theme. michael@0: // If the overflow area width or height is nscoord_MAX, then a michael@0: // saturating union may have encounted an overflow, so the overflow may not michael@0: // contain the frame border-box. Don't warn in that case. michael@0: // Don't warn for SVG either, since SVG doesn't need the overflow area michael@0: // to contain the frame bounds. michael@0: NS_FOR_FRAME_OVERFLOW_TYPES(otype) { michael@0: DebugOnly r = &aOverflowAreas.Overflow(otype); michael@0: NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 || michael@0: r->width == nscoord_MAX || r->height == nscoord_MAX || michael@0: (mState & NS_FRAME_SVG_LAYOUT) || michael@0: r->Contains(nsRect(nsPoint(0,0), aNewSize)), michael@0: "Computed overflow area must contain frame bounds"); michael@0: } michael@0: michael@0: // If we clip our children, clear accumulated overflow area. The michael@0: // children are actually clipped to the padding-box, but since the michael@0: // overflow area should include the entire border-box, just set it to michael@0: // the border-box here. michael@0: const nsStyleDisplay* disp = StyleDisplay(); michael@0: NS_ASSERTION((disp->mOverflowY == NS_STYLE_OVERFLOW_CLIP) == michael@0: (disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP), michael@0: "If one overflow is clip, the other should be too"); michael@0: if (nsFrame::ShouldApplyOverflowClipping(this, disp)) { michael@0: // The contents are actually clipped to the padding area michael@0: aOverflowAreas.SetAllTo(bounds); michael@0: } michael@0: michael@0: // Overflow area must always include the frame's top-left and bottom-right, michael@0: // even if the frame rect is empty (so we can scroll to those positions). michael@0: // Pending a real fix for bug 426879, don't do this for inline frames michael@0: // with zero width. michael@0: // Do not do this for SVG either, since it will usually massively increase michael@0: // the area unnecessarily. michael@0: if ((aNewSize.width != 0 || !IsInlineFrame(this)) && michael@0: !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) { michael@0: NS_FOR_FRAME_OVERFLOW_TYPES(otype) { michael@0: nsRect& o = aOverflowAreas.Overflow(otype); michael@0: o.UnionRectEdges(o, bounds); michael@0: } michael@0: } michael@0: michael@0: // Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background, michael@0: // so we add theme background overflow here so it's not clipped. michael@0: if (!IsBoxWrapped() && IsThemed(disp)) { michael@0: nsRect r(bounds); michael@0: nsPresContext *presContext = PresContext(); michael@0: if (presContext->GetTheme()-> michael@0: GetWidgetOverflow(presContext->DeviceContext(), this, michael@0: disp->mAppearance, &r)) { michael@0: nsRect& vo = aOverflowAreas.VisualOverflow(); michael@0: vo.UnionRectEdges(vo, r); michael@0: } michael@0: } michael@0: michael@0: ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize); michael@0: michael@0: // Nothing in here should affect scrollable overflow. michael@0: aOverflowAreas.VisualOverflow() = michael@0: ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize); michael@0: michael@0: // Absolute position clipping michael@0: nsRect clipPropClipRect; michael@0: bool hasClipPropClip = GetClipPropClipRect(disp, &clipPropClipRect, aNewSize); michael@0: if (hasClipPropClip) { michael@0: NS_FOR_FRAME_OVERFLOW_TYPES(otype) { michael@0: nsRect& o = aOverflowAreas.Overflow(otype); michael@0: o.IntersectRect(o, clipPropClipRect); michael@0: } michael@0: } michael@0: michael@0: /* If we're transformed, transform the overflow rect by the current transformation. */ michael@0: bool hasTransform = IsTransformed(); michael@0: nsSize oldSize = aOldSize ? *aOldSize : mRect.Size(); michael@0: bool sizeChanged = (oldSize != aNewSize); michael@0: if (hasTransform) { michael@0: Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(), michael@0: new nsOverflowAreas(aOverflowAreas)); michael@0: /* Since our size might not actually have been computed yet, we need to make sure that we use the michael@0: * correct dimensions by overriding the stored bounding rectangle with the value the caller has michael@0: * ensured us we'll use. michael@0: */ michael@0: nsRect newBounds(nsPoint(0, 0), aNewSize); michael@0: // Transform affects both overflow areas. michael@0: NS_FOR_FRAME_OVERFLOW_TYPES(otype) { michael@0: nsRect& o = aOverflowAreas.Overflow(otype); michael@0: o = nsDisplayTransform::TransformRect(o, this, nsPoint(0, 0), &newBounds); michael@0: } michael@0: if (Preserves3DChildren()) { michael@0: ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds); michael@0: } else if (sizeChanged && ChildrenHavePerspective()) { michael@0: RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds); michael@0: } michael@0: } else { michael@0: Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty()); michael@0: if (ChildrenHavePerspective() && sizeChanged) { michael@0: nsRect newBounds(nsPoint(0, 0), aNewSize); michael@0: RecomputePerspectiveChildrenOverflow(this->StyleContext(), &newBounds); michael@0: } michael@0: } michael@0: michael@0: bool anyOverflowChanged; michael@0: if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) { michael@0: anyOverflowChanged = SetOverflowAreas(aOverflowAreas); michael@0: } else { michael@0: anyOverflowChanged = ClearOverflowRects(); michael@0: } michael@0: michael@0: if (anyOverflowChanged) { michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } michael@0: return anyOverflowChanged; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::RecomputePerspectiveChildrenOverflow(const nsStyleContext* aStartStyle, const nsRect* aBounds) michael@0: { michael@0: // Children may check our size when getting our transform, make sure it's valid. michael@0: nsSize oldSize = GetSize(); michael@0: if (aBounds) { michael@0: SetSize(aBounds->Size()); michael@0: } michael@0: nsIFrame::ChildListIterator lists(this); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* child = childFrames.get(); michael@0: if (!FrameMaintainsOverflow(child)) { michael@0: continue; // frame does not maintain overflow rects michael@0: } michael@0: if (child->HasPerspective()) { michael@0: nsOverflowAreas* overflow = michael@0: static_cast(child->Properties().Get(nsIFrame::InitialOverflowProperty())); michael@0: nsRect bounds(nsPoint(0, 0), child->GetSize()); michael@0: if (overflow) { michael@0: nsOverflowAreas overflowCopy = *overflow; michael@0: child->FinishAndStoreOverflow(overflowCopy, bounds.Size()); michael@0: } else { michael@0: nsOverflowAreas boundsOverflow; michael@0: boundsOverflow.SetAllTo(bounds); michael@0: child->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); michael@0: } michael@0: } else if (child->StyleContext()->GetParent() == aStartStyle || michael@0: child->StyleContext() == aStartStyle) { michael@0: // If a frame is using perspective, then the size used to compute michael@0: // perspective-origin is the size of the frame belonging to its parent michael@0: // style context. We must find any descendant frames using our size michael@0: // (by recurse into frames with the same style context, or a direct michael@0: // child style context) to update their overflow rects too. michael@0: child->RecomputePerspectiveChildrenOverflow(aStartStyle, nullptr); michael@0: } michael@0: } michael@0: } michael@0: // Restore our old size just in case something depends on this elesewhere. michael@0: SetSize(oldSize); michael@0: } michael@0: michael@0: /* The overflow rects for leaf nodes in a preserve-3d hierarchy depends on michael@0: * the mRect value for their parents (since we use their transform, and transform michael@0: * depends on this for transform-origin etc). These weren't necessarily correct michael@0: * when we reflowed initially, so walk over all preserve-3d children and repeat the michael@0: * overflow calculation. michael@0: */ michael@0: static void michael@0: RecomputePreserve3DChildrenOverflow(nsIFrame* aFrame, const nsRect* aBounds) michael@0: { michael@0: // Children may check our size when getting our transform, make sure it's valid. michael@0: nsSize oldSize = aFrame->GetSize(); michael@0: if (aBounds) { michael@0: aFrame->SetSize(aBounds->Size()); michael@0: } michael@0: nsIFrame::ChildListIterator lists(aFrame); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* child = childFrames.get(); michael@0: if (!FrameMaintainsOverflow(child)) { michael@0: continue; // frame does not maintain overflow rects michael@0: } michael@0: if (child->Preserves3DChildren()) { michael@0: RecomputePreserve3DChildrenOverflow(child, nullptr); michael@0: } else if (child->Preserves3D()) { michael@0: nsOverflowAreas* overflow = michael@0: static_cast(child->Properties().Get(nsIFrame::InitialOverflowProperty())); michael@0: nsRect bounds(nsPoint(0, 0), child->GetSize()); michael@0: if (overflow) { michael@0: nsOverflowAreas overflowCopy = *overflow; michael@0: child->FinishAndStoreOverflow(overflowCopy, bounds.Size()); michael@0: } else { michael@0: nsOverflowAreas boundsOverflow; michael@0: boundsOverflow.SetAllTo(bounds); michael@0: child->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: // Restore our old size just in case something depends on this elesewhere. michael@0: aFrame->SetSize(oldSize); michael@0: michael@0: // Only repeat computing our overflow in recursive calls since the initial caller is still michael@0: // in the middle of doing this and we don't want an infinite loop. michael@0: if (!aBounds) { michael@0: nsOverflowAreas* overflow = michael@0: static_cast(aFrame->Properties().Get(nsIFrame::InitialOverflowProperty())); michael@0: nsRect bounds(nsPoint(0, 0), aFrame->GetSize()); michael@0: if (overflow) { michael@0: nsOverflowAreas overflowCopy = *overflow; michael@0: overflowCopy.UnionAllWith(bounds); michael@0: aFrame->FinishAndStoreOverflow(overflowCopy, bounds.Size()); michael@0: } else { michael@0: nsOverflowAreas boundsOverflow; michael@0: boundsOverflow.SetAllTo(bounds); michael@0: aFrame->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds) michael@0: { michael@0: // When we are preserving 3d we need to iterate over all children separately. michael@0: // If the child also preserves 3d then their overflow will already been in our michael@0: // coordinate space, otherwise we need to transform. michael@0: michael@0: // If we're the top frame in a preserve 3d chain then we need to recalculate the overflow michael@0: // areas of all our children since they will have used our size/offset which was invalid at michael@0: // the time. michael@0: if (!Preserves3D()) { michael@0: RecomputePreserve3DChildrenOverflow(this, &aBounds); michael@0: } michael@0: michael@0: nsRect childVisual; michael@0: nsRect childScrollable; michael@0: nsIFrame::ChildListIterator lists(this); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* child = childFrames.get(); michael@0: nsPoint offset = child->GetPosition(); michael@0: nsRect visual = child->GetVisualOverflowRect(); michael@0: nsRect scrollable = child->GetScrollableOverflowRect(); michael@0: visual.MoveBy(offset); michael@0: scrollable.MoveBy(offset); michael@0: if (child->Preserves3D()) { michael@0: childVisual = childVisual.Union(visual); michael@0: childScrollable = childScrollable.Union(scrollable); michael@0: } else { michael@0: childVisual = michael@0: childVisual.Union(nsDisplayTransform::TransformRect(visual, michael@0: this, nsPoint(0,0), &aBounds)); michael@0: childScrollable = michael@0: childScrollable.Union(nsDisplayTransform::TransformRect(scrollable, michael@0: this, nsPoint(0,0), &aBounds)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: aOverflowAreas.Overflow(eVisualOverflow) = aOverflowAreas.Overflow(eVisualOverflow).Union(childVisual); michael@0: aOverflowAreas.Overflow(eScrollableOverflow) = aOverflowAreas.Overflow(eScrollableOverflow).Union(childScrollable); michael@0: } michael@0: michael@0: void michael@0: nsFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas, michael@0: nsIFrame* aChildFrame) michael@0: { michael@0: aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() + michael@0: aChildFrame->GetPosition()); michael@0: } michael@0: michael@0: /** michael@0: * This function takes a frame that is part of a block-in-inline split, michael@0: * and _if_ that frame is an anonymous block created by an ib split it michael@0: * returns the block's preceding inline. This is needed because the michael@0: * split inline's style context is the parent of the anonymous block's michael@0: * style context. michael@0: * michael@0: * If aFrame is not an anonymous block, null is returned. michael@0: */ michael@0: static nsIFrame* michael@0: GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Must have a non-null frame!"); michael@0: NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT, michael@0: "GetIBSplitSibling should only be called on ib-split frames"); michael@0: michael@0: nsIAtom* type = aFrame->StyleContext()->GetPseudo(); michael@0: if (type != nsCSSAnonBoxes::mozAnonymousBlock && michael@0: type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) { michael@0: // it's not an anonymous block michael@0: return nullptr; michael@0: } michael@0: michael@0: // Find the first continuation of the frame. (Ugh. This ends up michael@0: // being O(N^2) when it is called O(N) times.) michael@0: aFrame = aFrame->FirstContinuation(); michael@0: michael@0: /* michael@0: * Now look up the nsGkAtoms::IBSplitPrevSibling michael@0: * property. michael@0: */ michael@0: nsIFrame *ibSplitSibling = static_cast michael@0: (aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())); michael@0: NS_ASSERTION(ibSplitSibling, "Broken frame tree?"); michael@0: return ibSplitSibling; michael@0: } michael@0: michael@0: /** michael@0: * Get the parent, corrected for the mangled frame tree resulting from michael@0: * having a block within an inline. The result only differs from the michael@0: * result of |GetParent| when |GetParent| returns an anonymous block michael@0: * that was created for an element that was 'display: inline' because michael@0: * that element contained a block. michael@0: * michael@0: * Also skip anonymous scrolled-content parents; inherit directly from the michael@0: * outer scroll frame. michael@0: */ michael@0: static nsIFrame* michael@0: GetCorrectedParent(const nsIFrame* aFrame) michael@0: { michael@0: nsIFrame *parent = aFrame->GetParent(); michael@0: if (!parent) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Outer tables are always anon boxes; if we're in here for an outer michael@0: // table, that actually means its the _inner_ table that wants to michael@0: // know its parent. So get the pseudo of the inner in that case. michael@0: nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo(); michael@0: if (pseudo == nsCSSAnonBoxes::tableOuter) { michael@0: pseudo = aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo(); michael@0: } michael@0: return nsFrame::CorrectStyleParentFrame(parent, pseudo); michael@0: } michael@0: michael@0: /* static */ michael@0: nsIFrame* michael@0: nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent, michael@0: nsIAtom* aChildPseudo) michael@0: { michael@0: NS_PRECONDITION(aProspectiveParent, "Must have a prospective parent"); michael@0: michael@0: // Anon boxes are parented to their actual parent already, except michael@0: // for non-elements. Those should not be treated as an anon box. michael@0: if (aChildPseudo && aChildPseudo != nsCSSAnonBoxes::mozNonElement && michael@0: nsCSSAnonBoxes::IsAnonBox(aChildPseudo)) { michael@0: NS_ASSERTION(aChildPseudo != nsCSSAnonBoxes::mozAnonymousBlock && michael@0: aChildPseudo != nsCSSAnonBoxes::mozAnonymousPositionedBlock, michael@0: "Should have dealt with kids that have " michael@0: "NS_FRAME_PART_OF_IBSPLIT elsewhere"); michael@0: return aProspectiveParent; michael@0: } michael@0: michael@0: // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out michael@0: // of all pseudo-elements as well. Otherwise ReparentStyleContext could cause michael@0: // style data to be out of sync with the frame tree. michael@0: nsIFrame* parent = aProspectiveParent; michael@0: do { michael@0: if (parent->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) { michael@0: nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent); michael@0: michael@0: if (sibling) { michael@0: // |parent| was a block in an {ib} split; use the inline as michael@0: // |the style parent. michael@0: parent = sibling; michael@0: } michael@0: } michael@0: michael@0: nsIAtom* parentPseudo = parent->StyleContext()->GetPseudo(); michael@0: if (!parentPseudo || michael@0: (!nsCSSAnonBoxes::IsAnonBox(parentPseudo) && michael@0: // nsPlaceholderFrame pases in nsGkAtoms::placeholderFrame for michael@0: // aChildPseudo (even though that's not a valid pseudo-type) just to michael@0: // trigger this behavior of walking up to the nearest non-pseudo michael@0: // ancestor. michael@0: aChildPseudo != nsGkAtoms::placeholderFrame)) { michael@0: return parent; michael@0: } michael@0: michael@0: parent = parent->GetParent(); michael@0: } while (parent); michael@0: michael@0: if (aProspectiveParent->StyleContext()->GetPseudo() == michael@0: nsCSSAnonBoxes::viewportScroll) { michael@0: // aProspectiveParent is the scrollframe for a viewport michael@0: // and the kids are the anonymous scrollbars michael@0: return aProspectiveParent; michael@0: } michael@0: michael@0: // We can get here if the root element is absolutely positioned. michael@0: // We can't test for this very accurately, but it can only happen michael@0: // when the prospective parent is a canvas frame. michael@0: NS_ASSERTION(aProspectiveParent->GetType() == nsGkAtoms::canvasFrame, michael@0: "Should have found a parent before this"); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrame::DoGetParentStyleContextFrame() const michael@0: { michael@0: if (mContent && !mContent->GetParent() && michael@0: !StyleContext()->GetPseudo()) { michael@0: // we're a frame for the root. We have no style context parent. michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!(mState & NS_FRAME_OUT_OF_FLOW)) { michael@0: /* michael@0: * If this frame is an anonymous block created when an inline with a block michael@0: * inside it got split, then the parent style context is on its preceding michael@0: * inline. We can get to it using GetIBSplitSiblingForAnonymousBlock. michael@0: */ michael@0: if (mState & NS_FRAME_PART_OF_IBSPLIT) { michael@0: nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this); michael@0: if (ibSplitSibling) { michael@0: return ibSplitSibling; michael@0: } michael@0: } michael@0: michael@0: // If this frame is one of the blocks that split an inline, we must michael@0: // return the "special" inline parent, i.e., the parent that this michael@0: // frame would have if we didn't mangle the frame structure. michael@0: return GetCorrectedParent(this); michael@0: } michael@0: michael@0: // We're an out-of-flow frame. For out-of-flow frames, we must michael@0: // resolve underneath the placeholder's parent. The placeholder is michael@0: // reached from the first-in-flow. michael@0: nsIFrame* placeholder = PresContext()->FrameManager()-> michael@0: GetPlaceholderFrameFor(FirstInFlow()); michael@0: if (!placeholder) { michael@0: NS_NOTREACHED("no placeholder frame for out-of-flow frame"); michael@0: return GetCorrectedParent(this); michael@0: } michael@0: return placeholder->GetParentStyleContextFrame(); michael@0: } michael@0: michael@0: void michael@0: nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame) michael@0: { michael@0: if (!aFrame || !*aFrame) michael@0: return; michael@0: nsIFrame *child = *aFrame; michael@0: //if we are a block frame then go for the last line of 'this' michael@0: while (1){ michael@0: child = child->GetFirstPrincipalChild(); michael@0: if (!child) michael@0: return;//nothing to do michael@0: nsIFrame* siblingFrame; michael@0: nsIContent* content; michael@0: //ignore anonymous elements, e.g. mozTableAdd* mozTableRemove* michael@0: //see bug 278197 comment #12 #13 for details michael@0: while ((siblingFrame = child->GetNextSibling()) && michael@0: (content = siblingFrame->GetContent()) && michael@0: !content->IsRootOfNativeAnonymousSubtree()) michael@0: child = siblingFrame; michael@0: *aFrame = child; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame) michael@0: { michael@0: if (!aFrame || !*aFrame) michael@0: return; michael@0: nsIFrame *child = *aFrame; michael@0: while (1){ michael@0: child = child->GetFirstPrincipalChild(); michael@0: if (!child) michael@0: return;//nothing to do michael@0: *aFrame = child; michael@0: } michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsIFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse) michael@0: { michael@0: int32_t tabIndex = -1; michael@0: if (aTabIndex) { michael@0: *aTabIndex = -1; // Default for early return is not focusable michael@0: } michael@0: bool isFocusable = false; michael@0: michael@0: if (mContent && mContent->IsElement() && IsVisibleConsideringAncestors()) { michael@0: const nsStyleUserInterface* ui = StyleUserInterface(); michael@0: if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE && michael@0: ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) { michael@0: // Pass in default tabindex of -1 for nonfocusable and 0 for focusable michael@0: tabIndex = 0; michael@0: } michael@0: isFocusable = mContent->IsFocusable(&tabIndex, aWithMouse); michael@0: if (!isFocusable && !aWithMouse && michael@0: GetType() == nsGkAtoms::scrollFrame && michael@0: mContent->IsHTML() && michael@0: !mContent->IsRootOfNativeAnonymousSubtree() && michael@0: mContent->GetParent() && michael@0: !mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) { michael@0: // Elements with scrollable view are focusable with script & tabbable michael@0: // Otherwise you couldn't scroll them with keyboard, which is michael@0: // an accessibility issue (e.g. Section 508 rules) michael@0: // However, we don't make them to be focusable with the mouse, michael@0: // because the extra focus outlines are considered unnecessarily ugly. michael@0: // When clicked on, the selection position within the element michael@0: // will be enough to make them keyboard scrollable. michael@0: nsIScrollableFrame *scrollFrame = do_QueryFrame(this); michael@0: if (scrollFrame && michael@0: scrollFrame->GetScrollbarStyles() != ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN) && michael@0: !scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) { michael@0: // Scroll bars will be used for overflow michael@0: isFocusable = true; michael@0: tabIndex = 0; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (aTabIndex) { michael@0: *aTabIndex = tabIndex; michael@0: } michael@0: return isFocusable; michael@0: } michael@0: michael@0: /** michael@0: * @return true if this text frame ends with a newline character which is michael@0: * treated as preformatted. It should return false if this is not a text frame. michael@0: */ michael@0: bool michael@0: nsIFrame::HasSignificantTerminalNewline() const michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: static uint8_t michael@0: ConvertSVGDominantBaselineToVerticalAlign(uint8_t aDominantBaseline) michael@0: { michael@0: // Most of these are approximate mappings. michael@0: switch (aDominantBaseline) { michael@0: case NS_STYLE_DOMINANT_BASELINE_HANGING: michael@0: case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE: michael@0: return NS_STYLE_VERTICAL_ALIGN_TEXT_TOP; michael@0: case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE: michael@0: case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC: michael@0: return NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM; michael@0: case NS_STYLE_DOMINANT_BASELINE_CENTRAL: michael@0: case NS_STYLE_DOMINANT_BASELINE_MIDDLE: michael@0: case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL: michael@0: return NS_STYLE_VERTICAL_ALIGN_MIDDLE; michael@0: case NS_STYLE_DOMINANT_BASELINE_AUTO: michael@0: case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC: michael@0: return NS_STYLE_VERTICAL_ALIGN_BASELINE; michael@0: case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT: michael@0: case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE: michael@0: case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE: michael@0: // These three should not simply map to 'baseline', but we don't michael@0: // support the complex baseline model that SVG 1.1 has and which michael@0: // css3-linebox now defines. michael@0: return NS_STYLE_VERTICAL_ALIGN_BASELINE; michael@0: default: michael@0: NS_NOTREACHED("unexpected aDominantBaseline value"); michael@0: return NS_STYLE_VERTICAL_ALIGN_BASELINE; michael@0: } michael@0: } michael@0: michael@0: uint8_t michael@0: nsIFrame::VerticalAlignEnum() const michael@0: { michael@0: if (IsSVGText()) { michael@0: uint8_t dominantBaseline; michael@0: for (const nsIFrame* frame = this; frame; frame = frame->GetParent()) { michael@0: dominantBaseline = frame->StyleSVGReset()->mDominantBaseline; michael@0: if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO || michael@0: frame->GetType() == nsGkAtoms::svgTextFrame) { michael@0: break; michael@0: } michael@0: } michael@0: return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline); michael@0: } michael@0: michael@0: const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign; michael@0: if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) { michael@0: return verticalAlign.GetIntValue(); michael@0: } michael@0: michael@0: return eInvalidVerticalAlign; michael@0: } michael@0: michael@0: /* static */ michael@0: void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface* ui, michael@0: nsIFrame::Cursor& aCursor) michael@0: { michael@0: aCursor.mCursor = ui->mCursor; michael@0: aCursor.mHaveHotspot = false; michael@0: aCursor.mHotspotX = aCursor.mHotspotY = 0.0f; michael@0: michael@0: for (nsCursorImage *item = ui->mCursorArray, michael@0: *item_end = ui->mCursorArray + ui->mCursorArrayLength; michael@0: item < item_end; ++item) { michael@0: uint32_t status; michael@0: nsresult rv = item->GetImage()->GetImageStatus(&status); michael@0: if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_LOAD_COMPLETE)) { michael@0: // This is the one we want michael@0: item->GetImage()->GetImage(getter_AddRefs(aCursor.mContainer)); michael@0: aCursor.mHaveHotspot = item->mHaveHotspot; michael@0: aCursor.mHotspotX = item->mHotspotX; michael@0: aCursor.mHotspotY = item->mHotspotY; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrame::RefreshSizeCache(nsBoxLayoutState& aState) michael@0: { michael@0: // XXXbz this comment needs some rewriting to make sense in the michael@0: // post-reflow-branch world. michael@0: michael@0: // Ok we need to compute our minimum, preferred, and maximum sizes. michael@0: // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS. michael@0: // 2) Preferred size. This is a little harder. This is the size the block would be michael@0: // if it were laid out on an infinite canvas. So we can get this by reflowing michael@0: // the block with and INTRINSIC width and height. We can also do a nice optimization michael@0: // for incremental reflow. If the reflow is incremental then we can pass a flag to michael@0: // have the block compute the preferred width for us! Preferred height can just be michael@0: // the minimum height; michael@0: // 3) Minimum size. This is a toughy. We can pass the block a flag asking for the max element michael@0: // size. That would give us the width. Unfortunately you can only ask for a maxElementSize michael@0: // during an incremental reflow. So on other reflows we will just have to use 0. michael@0: // The min height on the other hand is fairly easy we need to get the largest michael@0: // line height. This can be done with the line iterator. michael@0: michael@0: // if we do have a rendering context michael@0: nsresult rv = NS_OK; michael@0: nsRenderingContext* rendContext = aState.GetRenderingContext(); michael@0: if (rendContext) { michael@0: nsPresContext* presContext = aState.PresContext(); michael@0: michael@0: // If we don't have any HTML constraints and it's a resize, then nothing in the block michael@0: // could have changed, so no refresh is necessary. michael@0: nsBoxLayoutMetrics* metrics = BoxMetrics(); michael@0: if (!DoesNeedRecalc(metrics->mBlockPrefSize)) michael@0: return NS_OK; michael@0: michael@0: // the rect we plan to size to. michael@0: nsRect rect = GetRect(); michael@0: michael@0: nsMargin bp(0,0,0,0); michael@0: GetBorderAndPadding(bp); michael@0: michael@0: { michael@0: // If we're a container for font size inflation, then shrink michael@0: // wrapping inside of us should not apply font size inflation. michael@0: AutoMaybeDisableFontInflation an(this); michael@0: michael@0: metrics->mBlockPrefSize.width = michael@0: GetPrefWidth(rendContext) + bp.LeftRight(); michael@0: metrics->mBlockMinSize.width = michael@0: GetMinWidth(rendContext) + bp.LeftRight(); michael@0: } michael@0: michael@0: // do the nasty. michael@0: const WritingMode wm = aState.OuterReflowState() ? michael@0: aState.OuterReflowState()->GetWritingMode() : GetWritingMode(); michael@0: nsHTMLReflowMetrics desiredSize(wm); michael@0: rv = BoxReflow(aState, presContext, desiredSize, rendContext, michael@0: rect.x, rect.y, michael@0: metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE); michael@0: michael@0: metrics->mBlockMinSize.height = 0; michael@0: // ok we need the max ascent of the items on the line. So to do this michael@0: // ask the block for its line iterator. Get the max ascent. michael@0: nsAutoLineIterator lines = GetLineIterator(); michael@0: if (lines) michael@0: { michael@0: metrics->mBlockMinSize.height = 0; michael@0: int count = 0; michael@0: nsIFrame* firstFrame = nullptr; michael@0: int32_t framesOnLine; michael@0: nsRect lineBounds; michael@0: uint32_t lineFlags; michael@0: michael@0: do { michael@0: lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds, &lineFlags); michael@0: michael@0: if (lineBounds.height > metrics->mBlockMinSize.height) michael@0: metrics->mBlockMinSize.height = lineBounds.height; michael@0: michael@0: count++; michael@0: } while(firstFrame); michael@0: } else { michael@0: metrics->mBlockMinSize.height = desiredSize.Height(); michael@0: } michael@0: michael@0: metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height; michael@0: michael@0: if (desiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { michael@0: if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mBlockAscent)) michael@0: metrics->mBlockAscent = GetBaseline(); michael@0: } else { michael@0: metrics->mBlockAscent = desiredSize.TopAscent(); michael@0: } michael@0: michael@0: #ifdef DEBUG_adaptor michael@0: printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n", metrics->mBlockMinSize.width, michael@0: metrics->mBlockMinSize.height, michael@0: metrics->mBlockPrefSize.width, michael@0: metrics->mBlockPrefSize.height, michael@0: metrics->mBlockAscent); michael@0: #endif michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* virtual */ nsILineIterator* michael@0: nsFrame::GetLineIterator() michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsSize michael@0: nsFrame::GetPrefSize(nsBoxLayoutState& aState) michael@0: { michael@0: nsSize size(0,0); michael@0: DISPLAY_PREF_SIZE(this, size); michael@0: // If the size is cached, and there are no HTML constraints that we might michael@0: // be depending on, then we just return the cached size. michael@0: nsBoxLayoutMetrics *metrics = BoxMetrics(); michael@0: if (!DoesNeedRecalc(metrics->mPrefSize)) { michael@0: return metrics->mPrefSize; michael@0: } michael@0: michael@0: if (IsCollapsed()) michael@0: return size; michael@0: michael@0: // get our size in CSS. michael@0: bool widthSet, heightSet; michael@0: bool completelyRedefined = nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet); michael@0: michael@0: // Refresh our caches with new sizes. michael@0: if (!completelyRedefined) { michael@0: RefreshSizeCache(aState); michael@0: nsSize blockSize = metrics->mBlockPrefSize; michael@0: michael@0: // notice we don't need to add our borders or padding michael@0: // in. That's because the block did it for us. michael@0: if (!widthSet) michael@0: size.width = blockSize.width; michael@0: if (!heightSet) michael@0: size.height = blockSize.height; michael@0: } michael@0: michael@0: metrics->mPrefSize = size; michael@0: return size; michael@0: } michael@0: michael@0: nsSize michael@0: nsFrame::GetMinSize(nsBoxLayoutState& aState) michael@0: { michael@0: nsSize size(0,0); michael@0: DISPLAY_MIN_SIZE(this, size); michael@0: // Don't use the cache if we have HTMLReflowState constraints --- they might have changed michael@0: nsBoxLayoutMetrics *metrics = BoxMetrics(); michael@0: if (!DoesNeedRecalc(metrics->mMinSize)) { michael@0: size = metrics->mMinSize; michael@0: return size; michael@0: } michael@0: michael@0: if (IsCollapsed()) michael@0: return size; michael@0: michael@0: // get our size in CSS. michael@0: bool widthSet, heightSet; michael@0: bool completelyRedefined = michael@0: nsIFrame::AddCSSMinSize(aState, this, size, widthSet, heightSet); michael@0: michael@0: // Refresh our caches with new sizes. michael@0: if (!completelyRedefined) { michael@0: RefreshSizeCache(aState); michael@0: nsSize blockSize = metrics->mBlockMinSize; michael@0: michael@0: if (!widthSet) michael@0: size.width = blockSize.width; michael@0: if (!heightSet) michael@0: size.height = blockSize.height; michael@0: } michael@0: michael@0: metrics->mMinSize = size; michael@0: return size; michael@0: } michael@0: michael@0: nsSize michael@0: nsFrame::GetMaxSize(nsBoxLayoutState& aState) michael@0: { michael@0: nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); michael@0: DISPLAY_MAX_SIZE(this, size); michael@0: // Don't use the cache if we have HTMLReflowState constraints --- they might have changed michael@0: nsBoxLayoutMetrics *metrics = BoxMetrics(); michael@0: if (!DoesNeedRecalc(metrics->mMaxSize)) { michael@0: size = metrics->mMaxSize; michael@0: return size; michael@0: } michael@0: michael@0: if (IsCollapsed()) michael@0: return size; michael@0: michael@0: size = nsBox::GetMaxSize(aState); michael@0: metrics->mMaxSize = size; michael@0: michael@0: return size; michael@0: } michael@0: michael@0: nscoord michael@0: nsFrame::GetFlex(nsBoxLayoutState& aState) michael@0: { michael@0: nsBoxLayoutMetrics *metrics = BoxMetrics(); michael@0: if (!DoesNeedRecalc(metrics->mFlex)) michael@0: return metrics->mFlex; michael@0: michael@0: metrics->mFlex = nsBox::GetFlex(aState); michael@0: michael@0: return metrics->mFlex; michael@0: } michael@0: michael@0: nscoord michael@0: nsFrame::GetBoxAscent(nsBoxLayoutState& aState) michael@0: { michael@0: nsBoxLayoutMetrics *metrics = BoxMetrics(); michael@0: if (!DoesNeedRecalc(metrics->mAscent)) michael@0: return metrics->mAscent; michael@0: michael@0: if (IsCollapsed()) { michael@0: metrics->mAscent = 0; michael@0: } else { michael@0: // Refresh our caches with new sizes. michael@0: RefreshSizeCache(aState); michael@0: metrics->mAscent = metrics->mBlockAscent; michael@0: } michael@0: michael@0: return metrics->mAscent; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::DoLayout(nsBoxLayoutState& aState) michael@0: { michael@0: nsRect ourRect(mRect); michael@0: michael@0: nsRenderingContext* rendContext = aState.GetRenderingContext(); michael@0: nsPresContext* presContext = aState.PresContext(); michael@0: const WritingMode wm = aState.OuterReflowState() ? michael@0: aState.OuterReflowState()->GetWritingMode() : GetWritingMode(); michael@0: nsHTMLReflowMetrics desiredSize(wm); michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (rendContext) { michael@0: michael@0: rv = BoxReflow(aState, presContext, desiredSize, rendContext, michael@0: ourRect.x, ourRect.y, ourRect.width, ourRect.height); michael@0: michael@0: if (IsCollapsed()) { michael@0: SetSize(nsSize(0, 0)); michael@0: } else { michael@0: michael@0: // if our child needs to be bigger. This might happend with michael@0: // wrapping text. There is no way to predict its height until we michael@0: // reflow it. Now that we know the height reshuffle upward. michael@0: if (desiredSize.Width() > ourRect.width || michael@0: desiredSize.Height() > ourRect.height) { michael@0: michael@0: #ifdef DEBUG_GROW michael@0: DumpBox(stdout); michael@0: printf(" GREW from (%d,%d) -> (%d,%d)\n", michael@0: ourRect.width, ourRect.height, michael@0: desiredSize.Width(), desiredSize.Height()); michael@0: #endif michael@0: michael@0: if (desiredSize.Width() > ourRect.width) michael@0: ourRect.width = desiredSize.Width(); michael@0: michael@0: if (desiredSize.Height() > ourRect.height) michael@0: ourRect.height = desiredSize.Height(); michael@0: } michael@0: michael@0: // ensure our size is what we think is should be. Someone could have michael@0: // reset the frame to be smaller or something dumb like that. michael@0: SetSize(ourRect.Size()); michael@0: } michael@0: } michael@0: michael@0: // Should we do this if IsCollapsed() is true? michael@0: nsSize size(GetSize()); michael@0: desiredSize.Width() = size.width; michael@0: desiredSize.Height() = size.height; michael@0: desiredSize.UnionOverflowAreasWithDesiredBounds(); michael@0: michael@0: if (HasAbsolutelyPositionedChildren()) { michael@0: // Set up a |reflowState| to pass into ReflowAbsoluteFrames michael@0: nsHTMLReflowState reflowState(aState.PresContext(), this, michael@0: aState.GetRenderingContext(), michael@0: nsSize(size.width, NS_UNCONSTRAINEDSIZE), michael@0: nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); michael@0: michael@0: AddStateBits(NS_FRAME_IN_REFLOW); michael@0: // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames michael@0: // (just a dummy value; hopefully that's OK) michael@0: nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; michael@0: ReflowAbsoluteFrames(aState.PresContext(), desiredSize, michael@0: reflowState, reflowStatus); michael@0: RemoveStateBits(NS_FRAME_IN_REFLOW); michael@0: } michael@0: michael@0: nsSize oldSize(ourRect.Size()); michael@0: FinishAndStoreOverflow(desiredSize.mOverflowAreas, size, &oldSize); michael@0: michael@0: SyncLayout(aState); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrame::BoxReflow(nsBoxLayoutState& aState, michael@0: nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: nsRenderingContext* aRenderingContext, michael@0: nscoord aX, michael@0: nscoord aY, michael@0: nscoord aWidth, michael@0: nscoord aHeight, michael@0: bool aMoveFrame) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor"); michael@0: michael@0: #ifdef DEBUG_REFLOW michael@0: nsAdaptorAddIndents(); michael@0: printf("Reflowing: "); michael@0: nsFrame::ListTag(stdout, mFrame); michael@0: printf("\n"); michael@0: gIndent2++; michael@0: #endif michael@0: michael@0: nsBoxLayoutMetrics *metrics = BoxMetrics(); michael@0: nsReflowStatus status = NS_FRAME_COMPLETE; michael@0: michael@0: bool needsReflow = NS_SUBTREE_DIRTY(this); michael@0: michael@0: // if we don't need a reflow then michael@0: // lets see if we are already that size. Yes? then don't even reflow. We are done. michael@0: if (!needsReflow) { michael@0: michael@0: if (aWidth != NS_INTRINSICSIZE && aHeight != NS_INTRINSICSIZE) { michael@0: michael@0: // if the new calculated size has a 0 width or a 0 height michael@0: if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) && (aWidth == 0 || aHeight == 0)) { michael@0: needsReflow = false; michael@0: aDesiredSize.Width() = aWidth; michael@0: aDesiredSize.Height() = aHeight; michael@0: SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height())); michael@0: } else { michael@0: aDesiredSize.Width() = metrics->mLastSize.width; michael@0: aDesiredSize.Height() = metrics->mLastSize.height; michael@0: michael@0: // remove the margin. The rect of our child does not include it but our calculated size does. michael@0: // don't reflow if we are already the right size michael@0: if (metrics->mLastSize.width == aWidth && metrics->mLastSize.height == aHeight) michael@0: needsReflow = false; michael@0: else michael@0: needsReflow = true; michael@0: michael@0: } michael@0: } else { michael@0: // if the width or height are intrinsic alway reflow because michael@0: // we don't know what it should be. michael@0: needsReflow = true; michael@0: } michael@0: } michael@0: michael@0: // ok now reflow the child into the spacers calculated space michael@0: if (needsReflow) { michael@0: michael@0: aDesiredSize.Width() = 0; michael@0: aDesiredSize.Height() = 0; michael@0: michael@0: // create a reflow state to tell our child to flow at the given size. michael@0: michael@0: // Construct a bogus parent reflow state so that there's a usable michael@0: // containing block reflow state. michael@0: nsMargin margin(0,0,0,0); michael@0: GetMargin(margin); michael@0: michael@0: nsSize parentSize(aWidth, aHeight); michael@0: if (parentSize.height != NS_INTRINSICSIZE) michael@0: parentSize.height += margin.TopBottom(); michael@0: if (parentSize.width != NS_INTRINSICSIZE) michael@0: parentSize.width += margin.LeftRight(); michael@0: michael@0: nsIFrame *parentFrame = GetParent(); michael@0: nsFrameState savedState = parentFrame->GetStateBits(); michael@0: nsHTMLReflowState parentReflowState(aPresContext, parentFrame, michael@0: aRenderingContext, michael@0: parentSize, michael@0: nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); michael@0: parentFrame->RemoveStateBits(~nsFrameState(0)); michael@0: parentFrame->AddStateBits(savedState); michael@0: michael@0: // This may not do very much useful, but it's probably worth trying. michael@0: if (parentSize.width != NS_INTRINSICSIZE) michael@0: parentReflowState.SetComputedWidth(std::max(parentSize.width, 0)); michael@0: if (parentSize.height != NS_INTRINSICSIZE) michael@0: parentReflowState.SetComputedHeight(std::max(parentSize.height, 0)); michael@0: parentReflowState.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); michael@0: // XXX use box methods michael@0: parentFrame->GetPadding(parentReflowState.ComputedPhysicalPadding()); michael@0: parentFrame->GetBorder(parentReflowState.ComputedPhysicalBorderPadding()); michael@0: parentReflowState.ComputedPhysicalBorderPadding() += michael@0: parentReflowState.ComputedPhysicalPadding(); michael@0: michael@0: // Construct the parent chain manually since constructing it normally michael@0: // messes up dimensions. michael@0: const nsHTMLReflowState *outerReflowState = aState.OuterReflowState(); michael@0: NS_ASSERTION(!outerReflowState || outerReflowState->frame != this, michael@0: "in and out of XUL on a single frame?"); michael@0: const nsHTMLReflowState* parentRS; michael@0: if (outerReflowState && outerReflowState->frame == parentFrame) { michael@0: // We're a frame (such as a text control frame) that jumps into michael@0: // box reflow and then straight out of it on the child frame. michael@0: // This means we actually have a real parent reflow state. michael@0: // nsLayoutUtils::InflationMinFontSizeFor used to need this to be michael@0: // linked up correctly for text control frames, so do so here). michael@0: parentRS = outerReflowState; michael@0: } else { michael@0: parentRS = &parentReflowState; michael@0: } michael@0: michael@0: // XXX Is it OK that this reflow state has only one ancestor? michael@0: // (It used to have a bogus parent, skipping all the boxes). michael@0: nsSize availSize(aWidth, NS_INTRINSICSIZE); michael@0: nsHTMLReflowState reflowState(aPresContext, *parentRS, this, michael@0: availSize, -1, -1, michael@0: nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); michael@0: michael@0: // XXX_jwir3: This is somewhat fishy. If this is actually changing the value michael@0: // here (which it might be), then we should make sure that it's michael@0: // correct the first time around, rather than changing it later. michael@0: reflowState.mCBReflowState = parentRS; michael@0: michael@0: reflowState.mReflowDepth = aState.GetReflowDepth(); michael@0: michael@0: // mComputedWidth and mComputedHeight are content-box, not michael@0: // border-box michael@0: if (aWidth != NS_INTRINSICSIZE) { michael@0: nscoord computedWidth = michael@0: aWidth - reflowState.ComputedPhysicalBorderPadding().LeftRight(); michael@0: computedWidth = std::max(computedWidth, 0); michael@0: reflowState.SetComputedWidth(computedWidth); michael@0: } michael@0: michael@0: // Most child frames of box frames (e.g. subdocument or scroll frames) michael@0: // need to be constrained to the provided size and overflow as necessary. michael@0: // The one exception are block frames, because we need to know their michael@0: // natural height excluding any overflow area which may be caused by michael@0: // various CSS effects such as shadow or outline. michael@0: if (!IsFrameOfType(eBlockFrame)) { michael@0: if (aHeight != NS_INTRINSICSIZE) { michael@0: nscoord computedHeight = michael@0: aHeight - reflowState.ComputedPhysicalBorderPadding().TopBottom(); michael@0: computedHeight = std::max(computedHeight, 0); michael@0: reflowState.SetComputedHeight(computedHeight); michael@0: } else { michael@0: reflowState.SetComputedHeight( michael@0: ComputeSize(aRenderingContext, availSize, availSize.width, michael@0: nsSize(reflowState.ComputedPhysicalMargin().LeftRight(), michael@0: reflowState.ComputedPhysicalMargin().TopBottom()), michael@0: nsSize(reflowState.ComputedPhysicalBorderPadding().LeftRight() - michael@0: reflowState.ComputedPhysicalPadding().LeftRight(), michael@0: reflowState.ComputedPhysicalBorderPadding().TopBottom() - michael@0: reflowState.ComputedPhysicalPadding().TopBottom()), michael@0: nsSize(reflowState.ComputedPhysicalPadding().LeftRight(), michael@0: reflowState.ComputedPhysicalPadding().TopBottom()), michael@0: false).height michael@0: ); michael@0: } michael@0: } michael@0: michael@0: // Box layout calls SetRect before Layout, whereas non-box layout michael@0: // calls SetRect after Reflow. michael@0: // XXX Perhaps we should be doing this by twiddling the rect back to michael@0: // mLastSize before calling Reflow and then switching it back, but michael@0: // However, mLastSize can also be the size passed to BoxReflow by michael@0: // RefreshSizeCache, so that doesn't really make sense. michael@0: if (metrics->mLastSize.width != aWidth) { michael@0: reflowState.mFlags.mHResize = true; michael@0: michael@0: // When font size inflation is enabled, a horizontal resize michael@0: // requires a full reflow. See nsHTMLReflowState::InitResizeFlags michael@0: // for more details. michael@0: if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { michael@0: AddStateBits(NS_FRAME_IS_DIRTY); michael@0: } michael@0: } michael@0: if (metrics->mLastSize.height != aHeight) michael@0: reflowState.mFlags.mVResize = true; michael@0: michael@0: #ifdef DEBUG_REFLOW michael@0: nsAdaptorAddIndents(); michael@0: printf("Size=(%d,%d)\n",reflowState.ComputedWidth(), michael@0: reflowState.ComputedHeight()); michael@0: nsAdaptorAddIndents(); michael@0: nsAdaptorPrintReason(reflowState); michael@0: printf("\n"); michael@0: #endif michael@0: michael@0: // place the child and reflow michael@0: WillReflow(aPresContext); michael@0: michael@0: Reflow(aPresContext, aDesiredSize, reflowState, status); michael@0: michael@0: NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status"); michael@0: michael@0: uint32_t layoutFlags = aState.LayoutFlags(); michael@0: nsContainerFrame::FinishReflowChild(this, aPresContext, aDesiredSize, michael@0: &reflowState, aX, aY, layoutFlags | NS_FRAME_NO_MOVE_FRAME); michael@0: michael@0: // Save the ascent. (bug 103925) michael@0: if (IsCollapsed()) { michael@0: metrics->mAscent = 0; michael@0: } else { michael@0: if (aDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { michael@0: if (!nsLayoutUtils::GetFirstLineBaseline(this, &metrics->mAscent)) michael@0: metrics->mAscent = GetBaseline(); michael@0: } else michael@0: metrics->mAscent = aDesiredSize.TopAscent(); michael@0: } michael@0: michael@0: } else { michael@0: aDesiredSize.SetTopAscent(metrics->mBlockAscent); michael@0: } michael@0: michael@0: #ifdef DEBUG_REFLOW michael@0: if (aHeight != NS_INTRINSICSIZE && aDesiredSize.Height() != aHeight) michael@0: { michael@0: nsAdaptorAddIndents(); michael@0: printf("*****got taller!*****\n"); michael@0: michael@0: } michael@0: if (aWidth != NS_INTRINSICSIZE && aDesiredSize.Width() != aWidth) michael@0: { michael@0: nsAdaptorAddIndents(); michael@0: printf("*****got wider!******\n"); michael@0: michael@0: } michael@0: #endif michael@0: michael@0: if (aWidth == NS_INTRINSICSIZE) michael@0: aWidth = aDesiredSize.Width(); michael@0: michael@0: if (aHeight == NS_INTRINSICSIZE) michael@0: aHeight = aDesiredSize.Height(); michael@0: michael@0: metrics->mLastSize.width = aDesiredSize.Width(); michael@0: metrics->mLastSize.height = aDesiredSize.Height(); michael@0: michael@0: #ifdef DEBUG_REFLOW michael@0: gIndent2--; michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: DestroyBoxMetrics(void* aPropertyValue) michael@0: { michael@0: delete static_cast(aPropertyValue); michael@0: } michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(BoxMetricsProperty, DestroyBoxMetrics) michael@0: michael@0: nsBoxLayoutMetrics* michael@0: nsFrame::BoxMetrics() const michael@0: { michael@0: nsBoxLayoutMetrics* metrics = michael@0: static_cast(Properties().Get(BoxMetricsProperty())); michael@0: NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called"); michael@0: return metrics; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) michael@0: { michael@0: aFrame->AddStateBits(NS_FRAME_IN_POPUP); michael@0: michael@0: nsAutoTArray childListArray; michael@0: aFrame->GetCrossDocChildLists(&childListArray); michael@0: michael@0: nsIFrame::ChildListArrayIterator lists(childListArray); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: AddInPopupStateBitToDescendants(childFrames.get()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) michael@0: { michael@0: if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) || michael@0: nsLayoutUtils::IsPopup(aFrame)) { michael@0: return; michael@0: } michael@0: michael@0: aFrame->RemoveStateBits(NS_FRAME_IN_POPUP); michael@0: michael@0: nsAutoTArray childListArray; michael@0: aFrame->GetCrossDocChildLists(&childListArray); michael@0: michael@0: nsIFrame::ChildListArrayIterator lists(childListArray); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: RemoveInPopupStateBitFromDescendants(childFrames.get()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrame::SetParent(nsIFrame* aParent) michael@0: { michael@0: bool wasBoxWrapped = IsBoxWrapped(); michael@0: mParent = aParent; michael@0: if (!wasBoxWrapped && IsBoxWrapped()) { michael@0: InitBoxMetrics(true); michael@0: } else if (wasBoxWrapped && !IsBoxWrapped()) { michael@0: Properties().Delete(BoxMetricsProperty()); michael@0: } michael@0: michael@0: if (GetStateBits() & (NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) { michael@0: for (nsIFrame* f = aParent; michael@0: f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW); michael@0: f = f->GetParent()) { michael@0: f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); michael@0: } michael@0: } michael@0: michael@0: if (HasInvalidFrameInSubtree()) { michael@0: for (nsIFrame* f = aParent; michael@0: f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); michael@0: f = nsLayoutUtils::GetCrossDocParentFrame(f)) { michael@0: f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT); michael@0: } michael@0: } michael@0: michael@0: if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) { michael@0: AddInPopupStateBitToDescendants(this); michael@0: } else { michael@0: RemoveInPopupStateBitFromDescendants(this); michael@0: } michael@0: michael@0: // If our new parent only has invalid children, then we just invalidate michael@0: // ourselves too. This is probably faster than clearing the flag all michael@0: // the way up the frame tree. michael@0: if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) { michael@0: InvalidateFrame(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrame::InitBoxMetrics(bool aClear) michael@0: { michael@0: FrameProperties props = Properties(); michael@0: if (aClear) { michael@0: props.Delete(BoxMetricsProperty()); michael@0: } michael@0: michael@0: nsBoxLayoutMetrics *metrics = new nsBoxLayoutMetrics(); michael@0: props.Set(BoxMetricsProperty(), metrics); michael@0: michael@0: nsFrame::MarkIntrinsicWidthsDirty(); michael@0: metrics->mBlockAscent = 0; michael@0: metrics->mLastSize.SizeTo(0, 0); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayList* aList) michael@0: { michael@0: if (GetContent() && michael@0: GetContent()->IsXUL() && michael@0: GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) { michael@0: aList->AppendNewToTop(new (aBuilder) michael@0: nsDisplayOwnLayer(aBuilder, this, aList)); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsSelected() const michael@0: { michael@0: return (GetContent() && GetContent()->IsSelectionDescendant()) ? michael@0: IsFrameSelected() : false; michael@0: } michael@0: michael@0: void michael@0: nsIFrame::DestroySurface(void* aPropertyValue) michael@0: { michael@0: static_cast(aPropertyValue)->Release(); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::DestroyDT(void* aPropertyValue) michael@0: { michael@0: static_cast(aPropertyValue)->Release(); michael@0: } michael@0: michael@0: void michael@0: nsIFrame::DestroyRegion(void* aPropertyValue) michael@0: { michael@0: delete static_cast(aPropertyValue); michael@0: } michael@0: michael@0: bool michael@0: nsIFrame::IsPseudoStackingContextFromStyle() { michael@0: const nsStyleDisplay* disp = StyleDisplay(); michael@0: // If you change this, also change the computation of pseudoStackingContext michael@0: // in BuildDisplayListForChild() michael@0: return disp->mOpacity != 1.0f || michael@0: disp->IsPositioned(this) || michael@0: disp->IsFloating(this) || michael@0: (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT); michael@0: } michael@0: michael@0: Element* michael@0: nsIFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) michael@0: { michael@0: nsIFrame* frame = nullptr; michael@0: michael@0: if (aType == nsCSSPseudoElements::ePseudo_before) { michael@0: frame = nsLayoutUtils::GetBeforeFrame(this); michael@0: } else if (aType == nsCSSPseudoElements::ePseudo_after) { michael@0: frame = nsLayoutUtils::GetAfterFrame(this); michael@0: } michael@0: michael@0: if (frame) { michael@0: nsIContent* content = frame->GetContent(); michael@0: if (content->IsElement()) { michael@0: return content->AsElement(); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame::ContentOffsets::ContentOffsets() michael@0: { michael@0: } michael@0: michael@0: nsIFrame::ContentOffsets::ContentOffsets(const ContentOffsets& rhs) michael@0: : content(rhs.content), michael@0: offset(rhs.offset), michael@0: secondaryOffset(rhs.secondaryOffset), michael@0: associateWithNext(rhs.associateWithNext) michael@0: { michael@0: } michael@0: michael@0: nsIFrame::ContentOffsets::~ContentOffsets() michael@0: { michael@0: } michael@0: michael@0: nsIFrame::CaretPosition::CaretPosition() michael@0: : mContentOffset(0) michael@0: { michael@0: } michael@0: michael@0: nsIFrame::CaretPosition::~CaretPosition() michael@0: { michael@0: } michael@0: michael@0: // Box layout debugging michael@0: #ifdef DEBUG_REFLOW michael@0: int32_t gIndent2 = 0; michael@0: michael@0: void michael@0: nsAdaptorAddIndents() michael@0: { michael@0: for(int32_t i=0; i < gIndent2; i++) michael@0: { michael@0: printf(" "); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsAdaptorPrintReason(nsHTMLReflowState& aReflowState) michael@0: { michael@0: char* reflowReasonString; michael@0: michael@0: switch(aReflowState.reason) michael@0: { michael@0: case eReflowReason_Initial: michael@0: reflowReasonString = "initial"; michael@0: break; michael@0: michael@0: case eReflowReason_Resize: michael@0: reflowReasonString = "resize"; michael@0: break; michael@0: case eReflowReason_Dirty: michael@0: reflowReasonString = "dirty"; michael@0: break; michael@0: case eReflowReason_StyleChange: michael@0: reflowReasonString = "stylechange"; michael@0: break; michael@0: case eReflowReason_Incremental: michael@0: { michael@0: switch (aReflowState.reflowCommand->Type()) { michael@0: case eReflowType_StyleChanged: michael@0: reflowReasonString = "incremental (StyleChanged)"; michael@0: break; michael@0: case eReflowType_ReflowDirty: michael@0: reflowReasonString = "incremental (ReflowDirty)"; michael@0: break; michael@0: default: michael@0: reflowReasonString = "incremental (Unknown)"; michael@0: } michael@0: } michael@0: break; michael@0: default: michael@0: reflowReasonString = "unknown"; michael@0: break; michael@0: } michael@0: michael@0: printf("%s",reflowReasonString); michael@0: } michael@0: michael@0: #endif michael@0: #ifdef DEBUG_LAYOUT michael@0: void michael@0: nsFrame::GetBoxName(nsAutoString& aName) michael@0: { michael@0: GetFrameName(aName); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: static void michael@0: GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize, michael@0: char* aResult) michael@0: { michael@0: if (aContent) { michael@0: PR_snprintf(aResult, aResultSize, "%s@%p", michael@0: nsAtomCString(aContent->Tag()).get(), aFrame); michael@0: } michael@0: else { michael@0: PR_snprintf(aResult, aResultSize, "@%p", aFrame); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrame::Trace(const char* aMethod, bool aEnter) michael@0: { michael@0: if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { michael@0: char tagbuf[40]; michael@0: GetTagName(this, mContent, sizeof(tagbuf), tagbuf); michael@0: PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus) michael@0: { michael@0: if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { michael@0: char tagbuf[40]; michael@0: GetTagName(this, mContent, sizeof(tagbuf), tagbuf); michael@0: PR_LogPrint("%s: %s %s, status=%scomplete%s", michael@0: tagbuf, aEnter ? "enter" : "exit", aMethod, michael@0: NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "", michael@0: (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : ""); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrame::TraceMsg(const char* aFormatString, ...) michael@0: { michael@0: if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) { michael@0: // Format arguments into a buffer michael@0: char argbuf[200]; michael@0: va_list ap; michael@0: va_start(ap, aFormatString); michael@0: PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap); michael@0: va_end(ap); michael@0: michael@0: char tagbuf[40]; michael@0: GetTagName(this, mContent, sizeof(tagbuf), tagbuf); michael@0: PR_LogPrint("%s: %s", tagbuf, argbuf); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) michael@0: { michael@0: for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { michael@0: NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY, michael@0: "dirty bit not set"); michael@0: } michael@0: } michael@0: michael@0: // Start Display Reflow michael@0: #ifdef DEBUG michael@0: michael@0: DR_cookie::DR_cookie(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nsReflowStatus& aStatus) michael@0: :mPresContext(aPresContext), mFrame(aFrame), mReflowState(aReflowState), mMetrics(aMetrics), mStatus(aStatus) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_cookie); michael@0: mValue = nsFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowState); michael@0: } michael@0: michael@0: DR_cookie::~DR_cookie() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_cookie); michael@0: nsFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue); michael@0: } michael@0: michael@0: DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame) michael@0: : mFrame(aFrame) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_layout_cookie); michael@0: mValue = nsFrame::DisplayLayoutEnter(mFrame); michael@0: } michael@0: michael@0: DR_layout_cookie::~DR_layout_cookie() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_layout_cookie); michael@0: nsFrame::DisplayLayoutExit(mFrame, mValue); michael@0: } michael@0: michael@0: DR_intrinsic_width_cookie::DR_intrinsic_width_cookie( michael@0: nsIFrame* aFrame, michael@0: const char* aType, michael@0: nscoord& aResult) michael@0: : mFrame(aFrame) michael@0: , mType(aType) michael@0: , mResult(aResult) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_intrinsic_width_cookie); michael@0: mValue = nsFrame::DisplayIntrinsicWidthEnter(mFrame, mType); michael@0: } michael@0: michael@0: DR_intrinsic_width_cookie::~DR_intrinsic_width_cookie() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_intrinsic_width_cookie); michael@0: nsFrame::DisplayIntrinsicWidthExit(mFrame, mType, mResult, mValue); michael@0: } michael@0: michael@0: DR_intrinsic_size_cookie::DR_intrinsic_size_cookie( michael@0: nsIFrame* aFrame, michael@0: const char* aType, michael@0: nsSize& aResult) michael@0: : mFrame(aFrame) michael@0: , mType(aType) michael@0: , mResult(aResult) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_intrinsic_size_cookie); michael@0: mValue = nsFrame::DisplayIntrinsicSizeEnter(mFrame, mType); michael@0: } michael@0: michael@0: DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_intrinsic_size_cookie); michael@0: nsFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue); michael@0: } michael@0: michael@0: DR_init_constraints_cookie::DR_init_constraints_cookie( michael@0: nsIFrame* aFrame, michael@0: nsHTMLReflowState* aState, michael@0: nscoord aCBWidth, michael@0: nscoord aCBHeight, michael@0: const nsMargin* aMargin, michael@0: const nsMargin* aPadding) michael@0: : mFrame(aFrame) michael@0: , mState(aState) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_init_constraints_cookie); michael@0: mValue = nsHTMLReflowState::DisplayInitConstraintsEnter(mFrame, mState, michael@0: aCBWidth, aCBHeight, michael@0: aMargin, aPadding); michael@0: } michael@0: michael@0: DR_init_constraints_cookie::~DR_init_constraints_cookie() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_init_constraints_cookie); michael@0: nsHTMLReflowState::DisplayInitConstraintsExit(mFrame, mState, mValue); michael@0: } michael@0: michael@0: DR_init_offsets_cookie::DR_init_offsets_cookie( michael@0: nsIFrame* aFrame, michael@0: nsCSSOffsetState* aState, michael@0: nscoord aHorizontalPercentBasis, michael@0: nscoord aVerticalPercentBasis, michael@0: const nsMargin* aMargin, michael@0: const nsMargin* aPadding) michael@0: : mFrame(aFrame) michael@0: , mState(aState) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_init_offsets_cookie); michael@0: mValue = nsCSSOffsetState::DisplayInitOffsetsEnter(mFrame, mState, michael@0: aHorizontalPercentBasis, michael@0: aVerticalPercentBasis, michael@0: aMargin, aPadding); michael@0: } michael@0: michael@0: DR_init_offsets_cookie::~DR_init_offsets_cookie() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_init_offsets_cookie); michael@0: nsCSSOffsetState::DisplayInitOffsetsExit(mFrame, mState, mValue); michael@0: } michael@0: michael@0: DR_init_type_cookie::DR_init_type_cookie( michael@0: nsIFrame* aFrame, michael@0: nsHTMLReflowState* aState) michael@0: : mFrame(aFrame) michael@0: , mState(aState) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_init_type_cookie); michael@0: mValue = nsHTMLReflowState::DisplayInitFrameTypeEnter(mFrame, mState); michael@0: } michael@0: michael@0: DR_init_type_cookie::~DR_init_type_cookie() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_init_type_cookie); michael@0: nsHTMLReflowState::DisplayInitFrameTypeExit(mFrame, mState, mValue); michael@0: } michael@0: michael@0: struct DR_FrameTypeInfo; michael@0: struct DR_FrameTreeNode; michael@0: struct DR_Rule; michael@0: michael@0: struct DR_State michael@0: { michael@0: DR_State(); michael@0: ~DR_State(); michael@0: void Init(); michael@0: void AddFrameTypeInfo(nsIAtom* aFrameType, michael@0: const char* aFrameNameAbbrev, michael@0: const char* aFrameName); michael@0: DR_FrameTypeInfo* GetFrameTypeInfo(nsIAtom* aFrameType); michael@0: DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName); michael@0: void InitFrameTypeTable(); michael@0: DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame, michael@0: const nsHTMLReflowState* aReflowState); michael@0: void FindMatchingRule(DR_FrameTreeNode& aNode); michael@0: bool RuleMatches(DR_Rule& aRule, michael@0: DR_FrameTreeNode& aNode); michael@0: bool GetToken(FILE* aFile, michael@0: char* aBuf, michael@0: size_t aBufSize); michael@0: DR_Rule* ParseRule(FILE* aFile); michael@0: void ParseRulesFile(); michael@0: void AddRule(nsTArray& aRules, michael@0: DR_Rule& aRule); michael@0: bool IsWhiteSpace(int c); michael@0: bool GetNumber(char* aBuf, michael@0: int32_t& aNumber); michael@0: void PrettyUC(nscoord aSize, michael@0: char* aBuf); michael@0: void PrintMargin(const char* tag, const nsMargin* aMargin); michael@0: void DisplayFrameTypeInfo(nsIFrame* aFrame, michael@0: int32_t aIndent); michael@0: void DeleteTreeNode(DR_FrameTreeNode& aNode); michael@0: michael@0: bool mInited; michael@0: bool mActive; michael@0: int32_t mCount; michael@0: int32_t mAssert; michael@0: int32_t mIndent; michael@0: bool mIndentUndisplayedFrames; michael@0: bool mDisplayPixelErrors; michael@0: nsTArray mWildRules; michael@0: nsTArray mFrameTypeTable; michael@0: // reflow specific state michael@0: nsTArray mFrameTreeLeaves; michael@0: }; michael@0: michael@0: static DR_State *DR_state; // the one and only DR_State michael@0: michael@0: struct DR_RulePart michael@0: { michael@0: DR_RulePart(nsIAtom* aFrameType) : mFrameType(aFrameType), mNext(0) {} michael@0: void Destroy(); michael@0: michael@0: nsIAtom* mFrameType; michael@0: DR_RulePart* mNext; michael@0: }; michael@0: michael@0: void DR_RulePart::Destroy() michael@0: { michael@0: if (mNext) { michael@0: mNext->Destroy(); michael@0: } michael@0: delete this; michael@0: } michael@0: michael@0: struct DR_Rule michael@0: { michael@0: DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) { michael@0: MOZ_COUNT_CTOR(DR_Rule); michael@0: } michael@0: ~DR_Rule() { michael@0: if (mTarget) mTarget->Destroy(); michael@0: MOZ_COUNT_DTOR(DR_Rule); michael@0: } michael@0: void AddPart(nsIAtom* aFrameType); michael@0: michael@0: uint32_t mLength; michael@0: DR_RulePart* mTarget; michael@0: bool mDisplay; michael@0: }; michael@0: michael@0: void DR_Rule::AddPart(nsIAtom* aFrameType) michael@0: { michael@0: DR_RulePart* newPart = new DR_RulePart(aFrameType); michael@0: newPart->mNext = mTarget; michael@0: mTarget = newPart; michael@0: mLength++; michael@0: } michael@0: michael@0: struct DR_FrameTypeInfo michael@0: { michael@0: DR_FrameTypeInfo(nsIAtom* aFrmeType, const char* aFrameNameAbbrev, const char* aFrameName); michael@0: ~DR_FrameTypeInfo() { michael@0: int32_t numElements; michael@0: numElements = mRules.Length(); michael@0: for (int32_t i = numElements - 1; i >= 0; i--) { michael@0: delete mRules.ElementAt(i); michael@0: } michael@0: } michael@0: michael@0: nsIAtom* mType; michael@0: char mNameAbbrev[16]; michael@0: char mName[32]; michael@0: nsTArray mRules; michael@0: private: michael@0: DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) MOZ_DELETE; michael@0: }; michael@0: michael@0: DR_FrameTypeInfo::DR_FrameTypeInfo(nsIAtom* aFrameType, michael@0: const char* aFrameNameAbbrev, michael@0: const char* aFrameName) michael@0: { michael@0: mType = aFrameType; michael@0: PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev)); michael@0: PL_strncpyz(mName, aFrameName, sizeof(mName)); michael@0: } michael@0: michael@0: struct DR_FrameTreeNode michael@0: { michael@0: DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent) : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_FrameTreeNode); michael@0: } michael@0: michael@0: ~DR_FrameTreeNode() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_FrameTreeNode); michael@0: } michael@0: michael@0: nsIFrame* mFrame; michael@0: DR_FrameTreeNode* mParent; michael@0: bool mDisplay; michael@0: uint32_t mIndent; michael@0: }; michael@0: michael@0: // DR_State implementation michael@0: michael@0: DR_State::DR_State() michael@0: : mInited(false), mActive(false), mCount(0), mAssert(-1), mIndent(0), michael@0: mIndentUndisplayedFrames(false), mDisplayPixelErrors(false) michael@0: { michael@0: MOZ_COUNT_CTOR(DR_State); michael@0: } michael@0: michael@0: void DR_State::Init() michael@0: { michael@0: char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT"); michael@0: int32_t num; michael@0: if (env) { michael@0: if (GetNumber(env, num)) michael@0: mAssert = num; michael@0: else michael@0: printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env); michael@0: } michael@0: michael@0: env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START"); michael@0: if (env) { michael@0: if (GetNumber(env, num)) michael@0: mIndent = num; michael@0: else michael@0: printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env); michael@0: } michael@0: michael@0: env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES"); michael@0: if (env) { michael@0: if (GetNumber(env, num)) michael@0: mIndentUndisplayedFrames = num; michael@0: else michael@0: printf("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", env); michael@0: } michael@0: michael@0: env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS"); michael@0: if (env) { michael@0: if (GetNumber(env, num)) michael@0: mDisplayPixelErrors = num; michael@0: else michael@0: printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", env); michael@0: } michael@0: michael@0: InitFrameTypeTable(); michael@0: ParseRulesFile(); michael@0: mInited = true; michael@0: } michael@0: michael@0: DR_State::~DR_State() michael@0: { michael@0: MOZ_COUNT_DTOR(DR_State); michael@0: int32_t numElements, i; michael@0: numElements = mWildRules.Length(); michael@0: for (i = numElements - 1; i >= 0; i--) { michael@0: delete mWildRules.ElementAt(i); michael@0: } michael@0: numElements = mFrameTreeLeaves.Length(); michael@0: for (i = numElements - 1; i >= 0; i--) { michael@0: delete mFrameTreeLeaves.ElementAt(i); michael@0: } michael@0: } michael@0: michael@0: bool DR_State::GetNumber(char* aBuf, michael@0: int32_t& aNumber) michael@0: { michael@0: if (sscanf(aBuf, "%d", &aNumber) > 0) michael@0: return true; michael@0: else michael@0: return false; michael@0: } michael@0: michael@0: bool DR_State::IsWhiteSpace(int c) { michael@0: return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'); michael@0: } michael@0: michael@0: bool DR_State::GetToken(FILE* aFile, michael@0: char* aBuf, michael@0: size_t aBufSize) michael@0: { michael@0: bool haveToken = false; michael@0: aBuf[0] = 0; michael@0: // get the 1st non whitespace char michael@0: int c = -1; michael@0: for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) { michael@0: } michael@0: michael@0: if (c > 0) { michael@0: haveToken = true; michael@0: aBuf[0] = c; michael@0: // get everything up to the next whitespace char michael@0: size_t cX; michael@0: for (cX = 1; cX + 1 < aBufSize ; cX++) { michael@0: c = getc(aFile); michael@0: if (c < 0) { // EOF michael@0: ungetc(' ', aFile); michael@0: break; michael@0: } michael@0: else { michael@0: if (IsWhiteSpace(c)) { michael@0: break; michael@0: } michael@0: else { michael@0: aBuf[cX] = c; michael@0: } michael@0: } michael@0: } michael@0: aBuf[cX] = 0; michael@0: } michael@0: return haveToken; michael@0: } michael@0: michael@0: DR_Rule* DR_State::ParseRule(FILE* aFile) michael@0: { michael@0: char buf[128]; michael@0: int32_t doDisplay; michael@0: DR_Rule* rule = nullptr; michael@0: while (GetToken(aFile, buf, sizeof(buf))) { michael@0: if (GetNumber(buf, doDisplay)) { michael@0: if (rule) { michael@0: rule->mDisplay = !!doDisplay; michael@0: break; michael@0: } michael@0: else { michael@0: printf("unexpected token - %s \n", buf); michael@0: } michael@0: } michael@0: else { michael@0: if (!rule) { michael@0: rule = new DR_Rule; michael@0: } michael@0: if (strcmp(buf, "*") == 0) { michael@0: rule->AddPart(nullptr); michael@0: } michael@0: else { michael@0: DR_FrameTypeInfo* info = GetFrameTypeInfo(buf); michael@0: if (info) { michael@0: rule->AddPart(info->mType); michael@0: } michael@0: else { michael@0: printf("invalid frame type - %s \n", buf); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return rule; michael@0: } michael@0: michael@0: void DR_State::AddRule(nsTArray& aRules, michael@0: DR_Rule& aRule) michael@0: { michael@0: int32_t numRules = aRules.Length(); michael@0: for (int32_t ruleX = 0; ruleX < numRules; ruleX++) { michael@0: DR_Rule* rule = aRules.ElementAt(ruleX); michael@0: NS_ASSERTION(rule, "program error"); michael@0: if (aRule.mLength > rule->mLength) { michael@0: aRules.InsertElementAt(ruleX, &aRule); michael@0: return; michael@0: } michael@0: } michael@0: aRules.AppendElement(&aRule); michael@0: } michael@0: michael@0: void DR_State::ParseRulesFile() michael@0: { michael@0: char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE"); michael@0: if (path) { michael@0: FILE* inFile = fopen(path, "r"); michael@0: if (inFile) { michael@0: for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) { michael@0: if (rule->mTarget) { michael@0: nsIAtom* fType = rule->mTarget->mFrameType; michael@0: if (fType) { michael@0: DR_FrameTypeInfo* info = GetFrameTypeInfo(fType); michael@0: if (info) { michael@0: AddRule(info->mRules, *rule); michael@0: } michael@0: } michael@0: else { michael@0: AddRule(mWildRules, *rule); michael@0: } michael@0: mActive = true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: void DR_State::AddFrameTypeInfo(nsIAtom* aFrameType, michael@0: const char* aFrameNameAbbrev, michael@0: const char* aFrameName) michael@0: { michael@0: mFrameTypeTable.AppendElement(DR_FrameTypeInfo(aFrameType, aFrameNameAbbrev, aFrameName)); michael@0: } michael@0: michael@0: DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(nsIAtom* aFrameType) michael@0: { michael@0: int32_t numEntries = mFrameTypeTable.Length(); michael@0: NS_ASSERTION(numEntries != 0, "empty FrameTypeTable"); michael@0: for (int32_t i = 0; i < numEntries; i++) { michael@0: DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i); michael@0: if (info.mType == aFrameType) { michael@0: return &info; michael@0: } michael@0: } michael@0: return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type michael@0: } michael@0: michael@0: DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName) michael@0: { michael@0: int32_t numEntries = mFrameTypeTable.Length(); michael@0: NS_ASSERTION(numEntries != 0, "empty FrameTypeTable"); michael@0: for (int32_t i = 0; i < numEntries; i++) { michael@0: DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i); michael@0: if ((strcmp(aFrameName, info.mName) == 0) || (strcmp(aFrameName, info.mNameAbbrev) == 0)) { michael@0: return &info; michael@0: } michael@0: } michael@0: return &mFrameTypeTable.ElementAt(numEntries - 1); // return unknown frame type michael@0: } michael@0: michael@0: void DR_State::InitFrameTypeTable() michael@0: { michael@0: AddFrameTypeInfo(nsGkAtoms::blockFrame, "block", "block"); michael@0: AddFrameTypeInfo(nsGkAtoms::brFrame, "br", "br"); michael@0: AddFrameTypeInfo(nsGkAtoms::bulletFrame, "bullet", "bullet"); michael@0: AddFrameTypeInfo(nsGkAtoms::colorControlFrame, "color", "colorControl"); michael@0: AddFrameTypeInfo(nsGkAtoms::gfxButtonControlFrame, "button", "gfxButtonControl"); michael@0: AddFrameTypeInfo(nsGkAtoms::HTMLButtonControlFrame, "HTMLbutton", "HTMLButtonControl"); michael@0: AddFrameTypeInfo(nsGkAtoms::HTMLCanvasFrame, "HTMLCanvas","HTMLCanvas"); michael@0: AddFrameTypeInfo(nsGkAtoms::subDocumentFrame, "subdoc", "subDocument"); michael@0: AddFrameTypeInfo(nsGkAtoms::imageFrame, "img", "image"); michael@0: AddFrameTypeInfo(nsGkAtoms::inlineFrame, "inline", "inline"); michael@0: AddFrameTypeInfo(nsGkAtoms::letterFrame, "letter", "letter"); michael@0: AddFrameTypeInfo(nsGkAtoms::lineFrame, "line", "line"); michael@0: AddFrameTypeInfo(nsGkAtoms::listControlFrame, "select", "select"); michael@0: AddFrameTypeInfo(nsGkAtoms::objectFrame, "obj", "object"); michael@0: AddFrameTypeInfo(nsGkAtoms::pageFrame, "page", "page"); michael@0: AddFrameTypeInfo(nsGkAtoms::placeholderFrame, "place", "placeholder"); michael@0: AddFrameTypeInfo(nsGkAtoms::canvasFrame, "canvas", "canvas"); michael@0: AddFrameTypeInfo(nsGkAtoms::rootFrame, "root", "root"); michael@0: AddFrameTypeInfo(nsGkAtoms::scrollFrame, "scroll", "scroll"); michael@0: AddFrameTypeInfo(nsGkAtoms::tableCaptionFrame, "caption", "tableCaption"); michael@0: AddFrameTypeInfo(nsGkAtoms::tableCellFrame, "cell", "tableCell"); michael@0: AddFrameTypeInfo(nsGkAtoms::bcTableCellFrame, "bcCell", "bcTableCell"); michael@0: AddFrameTypeInfo(nsGkAtoms::tableColFrame, "col", "tableCol"); michael@0: AddFrameTypeInfo(nsGkAtoms::tableColGroupFrame, "colG", "tableColGroup"); michael@0: AddFrameTypeInfo(nsGkAtoms::tableFrame, "tbl", "table"); michael@0: AddFrameTypeInfo(nsGkAtoms::tableOuterFrame, "tblO", "tableOuter"); michael@0: AddFrameTypeInfo(nsGkAtoms::tableRowGroupFrame, "rowG", "tableRowGroup"); michael@0: AddFrameTypeInfo(nsGkAtoms::tableRowFrame, "row", "tableRow"); michael@0: AddFrameTypeInfo(nsGkAtoms::textInputFrame, "textCtl", "textInput"); michael@0: AddFrameTypeInfo(nsGkAtoms::textFrame, "text", "text"); michael@0: AddFrameTypeInfo(nsGkAtoms::viewportFrame, "VP", "viewport"); michael@0: #ifdef MOZ_XUL michael@0: AddFrameTypeInfo(nsGkAtoms::XULLabelFrame, "XULLabel", "XULLabel"); michael@0: AddFrameTypeInfo(nsGkAtoms::boxFrame, "Box", "Box"); michael@0: AddFrameTypeInfo(nsGkAtoms::sliderFrame, "Slider", "Slider"); michael@0: AddFrameTypeInfo(nsGkAtoms::popupSetFrame, "PopupSet", "PopupSet"); michael@0: #endif michael@0: AddFrameTypeInfo(nullptr, "unknown", "unknown"); michael@0: } michael@0: michael@0: michael@0: void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame, michael@0: int32_t aIndent) michael@0: { michael@0: DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->GetType()); michael@0: if (frameTypeInfo) { michael@0: for (int32_t i = 0; i < aIndent; i++) { michael@0: printf(" "); michael@0: } michael@0: if(!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) { michael@0: if (aFrame) { michael@0: nsAutoString name; michael@0: aFrame->GetFrameName(name); michael@0: printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)aFrame); michael@0: } michael@0: else { michael@0: printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame); michael@0: } michael@0: } michael@0: else { michael@0: printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool DR_State::RuleMatches(DR_Rule& aRule, michael@0: DR_FrameTreeNode& aNode) michael@0: { michael@0: NS_ASSERTION(aRule.mTarget, "program error"); michael@0: michael@0: DR_RulePart* rulePart; michael@0: DR_FrameTreeNode* parentNode; michael@0: for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent; michael@0: rulePart && parentNode; michael@0: rulePart = rulePart->mNext, parentNode = parentNode->mParent) { michael@0: if (rulePart->mFrameType) { michael@0: if (parentNode->mFrame) { michael@0: if (rulePart->mFrameType != parentNode->mFrame->GetType()) { michael@0: return false; michael@0: } michael@0: } michael@0: else NS_ASSERTION(false, "program error"); michael@0: } michael@0: // else wild card match michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode) michael@0: { michael@0: if (!aNode.mFrame) { michael@0: NS_ASSERTION(false, "invalid DR_FrameTreeNode \n"); michael@0: return; michael@0: } michael@0: michael@0: bool matchingRule = false; michael@0: michael@0: DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->GetType()); michael@0: NS_ASSERTION(info, "program error"); michael@0: int32_t numRules = info->mRules.Length(); michael@0: for (int32_t ruleX = 0; ruleX < numRules; ruleX++) { michael@0: DR_Rule* rule = info->mRules.ElementAt(ruleX); michael@0: if (rule && RuleMatches(*rule, aNode)) { michael@0: aNode.mDisplay = rule->mDisplay; michael@0: matchingRule = true; michael@0: break; michael@0: } michael@0: } michael@0: if (!matchingRule) { michael@0: int32_t numWildRules = mWildRules.Length(); michael@0: for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) { michael@0: DR_Rule* rule = mWildRules.ElementAt(ruleX); michael@0: if (rule && RuleMatches(*rule, aNode)) { michael@0: aNode.mDisplay = rule->mDisplay; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame, michael@0: const nsHTMLReflowState* aReflowState) michael@0: { michael@0: // find the frame of the parent reflow state (usually just the parent of aFrame) michael@0: nsIFrame* parentFrame; michael@0: if (aReflowState) { michael@0: const nsHTMLReflowState* parentRS = aReflowState->parentReflowState; michael@0: parentFrame = (parentRS) ? parentRS->frame : nullptr; michael@0: } else { michael@0: parentFrame = aFrame->GetParent(); michael@0: } michael@0: michael@0: // find the parent tree node leaf michael@0: DR_FrameTreeNode* parentNode = nullptr; michael@0: michael@0: DR_FrameTreeNode* lastLeaf = nullptr; michael@0: if(mFrameTreeLeaves.Length()) michael@0: lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1); michael@0: if (lastLeaf) { michael@0: for (parentNode = lastLeaf; parentNode && (parentNode->mFrame != parentFrame); parentNode = parentNode->mParent) { michael@0: } michael@0: } michael@0: DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode); michael@0: FindMatchingRule(*newNode); michael@0: michael@0: newNode->mIndent = mIndent; michael@0: if (newNode->mDisplay || mIndentUndisplayedFrames) { michael@0: ++mIndent; michael@0: } michael@0: michael@0: if (lastLeaf && (lastLeaf == parentNode)) { michael@0: mFrameTreeLeaves.RemoveElementAt(mFrameTreeLeaves.Length() - 1); michael@0: } michael@0: mFrameTreeLeaves.AppendElement(newNode); michael@0: mCount++; michael@0: michael@0: return newNode; michael@0: } michael@0: michael@0: void DR_State::PrettyUC(nscoord aSize, michael@0: char* aBuf) michael@0: { michael@0: if (NS_UNCONSTRAINEDSIZE == aSize) { michael@0: strcpy(aBuf, "UC"); michael@0: } michael@0: else { michael@0: if ((nscoord)0xdeadbeefU == aSize) michael@0: { michael@0: strcpy(aBuf, "deadbeef"); michael@0: } michael@0: else { michael@0: sprintf(aBuf, "%d", aSize); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void DR_State::PrintMargin(const char *tag, const nsMargin* aMargin) michael@0: { michael@0: if (aMargin) { michael@0: char t[16], r[16], b[16], l[16]; michael@0: PrettyUC(aMargin->top, t); michael@0: PrettyUC(aMargin->right, r); michael@0: PrettyUC(aMargin->bottom, b); michael@0: PrettyUC(aMargin->left, l); michael@0: printf(" %s=%s,%s,%s,%s", tag, t, r, b, l); michael@0: } else { michael@0: // use %p here for consistency with other null-pointer printouts michael@0: printf(" %s=%p", tag, (void*)aMargin); michael@0: } michael@0: } michael@0: michael@0: void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode) michael@0: { michael@0: mFrameTreeLeaves.RemoveElement(&aNode); michael@0: int32_t numLeaves = mFrameTreeLeaves.Length(); michael@0: if ((0 == numLeaves) || (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) { michael@0: mFrameTreeLeaves.AppendElement(aNode.mParent); michael@0: } michael@0: michael@0: if (aNode.mDisplay || mIndentUndisplayedFrames) { michael@0: --mIndent; michael@0: } michael@0: // delete the tree node michael@0: delete &aNode; michael@0: } michael@0: michael@0: static void michael@0: CheckPixelError(nscoord aSize, michael@0: int32_t aPixelToTwips) michael@0: { michael@0: if (NS_UNCONSTRAINEDSIZE != aSize) { michael@0: if ((aSize % aPixelToTwips) > 0) { michael@0: printf("VALUE %d is not a whole pixel \n", aSize); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void DisplayReflowEnterPrint(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: const nsHTMLReflowState& aReflowState, michael@0: DR_FrameTreeNode& aTreeNode, michael@0: bool aChanged) michael@0: { michael@0: if (aTreeNode.mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent); michael@0: michael@0: char width[16]; michael@0: char height[16]; michael@0: michael@0: DR_state->PrettyUC(aReflowState.AvailableWidth(), width); michael@0: DR_state->PrettyUC(aReflowState.AvailableHeight(), height); michael@0: printf("Reflow a=%s,%s ", width, height); michael@0: michael@0: DR_state->PrettyUC(aReflowState.ComputedWidth(), width); michael@0: DR_state->PrettyUC(aReflowState.ComputedHeight(), height); michael@0: printf("c=%s,%s ", width, height); michael@0: michael@0: if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY) michael@0: printf("dirty "); michael@0: michael@0: if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) michael@0: printf("dirty-children "); michael@0: michael@0: if (aReflowState.mFlags.mSpecialHeightReflow) michael@0: printf("special-height "); michael@0: michael@0: if (aReflowState.mFlags.mHResize) michael@0: printf("h-resize "); michael@0: michael@0: if (aReflowState.mFlags.mVResize) michael@0: printf("v-resize "); michael@0: michael@0: nsIFrame* inFlow = aFrame->GetPrevInFlow(); michael@0: if (inFlow) { michael@0: printf("pif=%p ", (void*)inFlow); michael@0: } michael@0: inFlow = aFrame->GetNextInFlow(); michael@0: if (inFlow) { michael@0: printf("nif=%p ", (void*)inFlow); michael@0: } michael@0: if (aChanged) michael@0: printf("CHANGED \n"); michael@0: else michael@0: printf("cnt=%d \n", DR_state->mCount); michael@0: if (DR_state->mDisplayPixelErrors) { michael@0: int32_t p2t = aPresContext->AppUnitsPerDevPixel(); michael@0: CheckPixelError(aReflowState.AvailableWidth(), p2t); michael@0: CheckPixelError(aReflowState.AvailableHeight(), p2t); michael@0: CheckPixelError(aReflowState.ComputedWidth(), p2t); michael@0: CheckPixelError(aReflowState.ComputedHeight(), p2t); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void* nsFrame::DisplayReflowEnter(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: const nsHTMLReflowState& aReflowState) michael@0: { michael@0: if (!DR_state->mInited) DR_state->Init(); michael@0: if (!DR_state->mActive) return nullptr; michael@0: michael@0: NS_ASSERTION(aFrame, "invalid call"); michael@0: michael@0: DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowState); michael@0: if (treeNode) { michael@0: DisplayReflowEnterPrint(aPresContext, aFrame, aReflowState, *treeNode, false); michael@0: } michael@0: return treeNode; michael@0: } michael@0: michael@0: void* nsFrame::DisplayLayoutEnter(nsIFrame* aFrame) michael@0: { michael@0: if (!DR_state->mInited) DR_state->Init(); michael@0: if (!DR_state->mActive) return nullptr; michael@0: michael@0: NS_ASSERTION(aFrame, "invalid call"); michael@0: michael@0: DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); michael@0: if (treeNode && treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: printf("Layout\n"); michael@0: } michael@0: return treeNode; michael@0: } michael@0: michael@0: void* nsFrame::DisplayIntrinsicWidthEnter(nsIFrame* aFrame, michael@0: const char* aType) michael@0: { michael@0: if (!DR_state->mInited) DR_state->Init(); michael@0: if (!DR_state->mActive) return nullptr; michael@0: michael@0: NS_ASSERTION(aFrame, "invalid call"); michael@0: michael@0: DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); michael@0: if (treeNode && treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: printf("Get%sWidth\n", aType); michael@0: } michael@0: return treeNode; michael@0: } michael@0: michael@0: void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame, michael@0: const char* aType) michael@0: { michael@0: if (!DR_state->mInited) DR_state->Init(); michael@0: if (!DR_state->mActive) return nullptr; michael@0: michael@0: NS_ASSERTION(aFrame, "invalid call"); michael@0: michael@0: DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); michael@0: if (treeNode && treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: printf("Get%sSize\n", aType); michael@0: } michael@0: return treeNode; michael@0: } michael@0: michael@0: void nsFrame::DisplayReflowExit(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nsReflowStatus aStatus, michael@0: void* aFrameTreeNode) michael@0: { michael@0: if (!DR_state->mActive) return; michael@0: michael@0: NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call"); michael@0: if (!aFrameTreeNode) return; michael@0: michael@0: DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; michael@0: if (treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: michael@0: char width[16]; michael@0: char height[16]; michael@0: char x[16]; michael@0: char y[16]; michael@0: DR_state->PrettyUC(aMetrics.Width(), width); michael@0: DR_state->PrettyUC(aMetrics.Height(), height); michael@0: printf("Reflow d=%s,%s", width, height); michael@0: michael@0: if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { michael@0: printf(" status=0x%x", aStatus); michael@0: } michael@0: if (aFrame->HasOverflowAreas()) { michael@0: DR_state->PrettyUC(aMetrics.VisualOverflow().x, x); michael@0: DR_state->PrettyUC(aMetrics.VisualOverflow().y, y); michael@0: DR_state->PrettyUC(aMetrics.VisualOverflow().width, width); michael@0: DR_state->PrettyUC(aMetrics.VisualOverflow().height, height); michael@0: printf(" vis-o=(%s,%s) %s x %s", x, y, width, height); michael@0: michael@0: nsRect storedOverflow = aFrame->GetVisualOverflowRect(); michael@0: DR_state->PrettyUC(storedOverflow.x, x); michael@0: DR_state->PrettyUC(storedOverflow.y, y); michael@0: DR_state->PrettyUC(storedOverflow.width, width); michael@0: DR_state->PrettyUC(storedOverflow.height, height); michael@0: printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height); michael@0: michael@0: DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x); michael@0: DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y); michael@0: DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width); michael@0: DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height); michael@0: printf(" scr-o=(%s,%s) %s x %s", x, y, width, height); michael@0: michael@0: storedOverflow = aFrame->GetScrollableOverflowRect(); michael@0: DR_state->PrettyUC(storedOverflow.x, x); michael@0: DR_state->PrettyUC(storedOverflow.y, y); michael@0: DR_state->PrettyUC(storedOverflow.width, width); michael@0: DR_state->PrettyUC(storedOverflow.height, height); michael@0: printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height); michael@0: } michael@0: printf("\n"); michael@0: if (DR_state->mDisplayPixelErrors) { michael@0: int32_t p2t = aPresContext->AppUnitsPerDevPixel(); michael@0: CheckPixelError(aMetrics.Width(), p2t); michael@0: CheckPixelError(aMetrics.Height(), p2t); michael@0: } michael@0: } michael@0: DR_state->DeleteTreeNode(*treeNode); michael@0: } michael@0: michael@0: void nsFrame::DisplayLayoutExit(nsIFrame* aFrame, michael@0: void* aFrameTreeNode) michael@0: { michael@0: if (!DR_state->mActive) return; michael@0: michael@0: NS_ASSERTION(aFrame, "non-null frame required"); michael@0: if (!aFrameTreeNode) return; michael@0: michael@0: DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; michael@0: if (treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: nsRect rect = aFrame->GetRect(); michael@0: printf("Layout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height); michael@0: } michael@0: DR_state->DeleteTreeNode(*treeNode); michael@0: } michael@0: michael@0: void nsFrame::DisplayIntrinsicWidthExit(nsIFrame* aFrame, michael@0: const char* aType, michael@0: nscoord aResult, michael@0: void* aFrameTreeNode) michael@0: { michael@0: if (!DR_state->mActive) return; michael@0: michael@0: NS_ASSERTION(aFrame, "non-null frame required"); michael@0: if (!aFrameTreeNode) return; michael@0: michael@0: DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; michael@0: if (treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: char width[16]; michael@0: DR_state->PrettyUC(aResult, width); michael@0: printf("Get%sWidth=%s\n", aType, width); michael@0: } michael@0: DR_state->DeleteTreeNode(*treeNode); michael@0: } michael@0: michael@0: void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, michael@0: const char* aType, michael@0: nsSize aResult, michael@0: void* aFrameTreeNode) michael@0: { michael@0: if (!DR_state->mActive) return; michael@0: michael@0: NS_ASSERTION(aFrame, "non-null frame required"); michael@0: if (!aFrameTreeNode) return; michael@0: michael@0: DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; michael@0: if (treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: michael@0: char width[16]; michael@0: char height[16]; michael@0: DR_state->PrettyUC(aResult.width, width); michael@0: DR_state->PrettyUC(aResult.height, height); michael@0: printf("Get%sSize=%s,%s\n", aType, width, height); michael@0: } michael@0: DR_state->DeleteTreeNode(*treeNode); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsFrame::DisplayReflowStartup() michael@0: { michael@0: DR_state = new DR_State(); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsFrame::DisplayReflowShutdown() michael@0: { michael@0: delete DR_state; michael@0: DR_state = nullptr; michael@0: } michael@0: michael@0: void DR_cookie::Change() const michael@0: { michael@0: DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue; michael@0: if (treeNode && treeNode->mDisplay) { michael@0: DisplayReflowEnterPrint(mPresContext, mFrame, mReflowState, *treeNode, true); michael@0: } michael@0: } michael@0: michael@0: /* static */ void* michael@0: nsHTMLReflowState::DisplayInitConstraintsEnter(nsIFrame* aFrame, michael@0: nsHTMLReflowState* aState, michael@0: nscoord aContainingBlockWidth, michael@0: nscoord aContainingBlockHeight, michael@0: const nsMargin* aBorder, michael@0: const nsMargin* aPadding) michael@0: { michael@0: NS_PRECONDITION(aFrame, "non-null frame required"); michael@0: NS_PRECONDITION(aState, "non-null state required"); michael@0: michael@0: if (!DR_state->mInited) DR_state->Init(); michael@0: if (!DR_state->mActive) return nullptr; michael@0: michael@0: DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState); michael@0: if (treeNode && treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: michael@0: printf("InitConstraints parent=%p", michael@0: (void*)aState->parentReflowState); michael@0: michael@0: char width[16]; michael@0: char height[16]; michael@0: michael@0: DR_state->PrettyUC(aContainingBlockWidth, width); michael@0: DR_state->PrettyUC(aContainingBlockHeight, height); michael@0: printf(" cb=%s,%s", width, height); michael@0: michael@0: DR_state->PrettyUC(aState->AvailableWidth(), width); michael@0: DR_state->PrettyUC(aState->AvailableHeight(), height); michael@0: printf(" as=%s,%s", width, height); michael@0: michael@0: DR_state->PrintMargin("b", aBorder); michael@0: DR_state->PrintMargin("p", aPadding); michael@0: putchar('\n'); michael@0: } michael@0: return treeNode; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsHTMLReflowState::DisplayInitConstraintsExit(nsIFrame* aFrame, michael@0: nsHTMLReflowState* aState, michael@0: void* aValue) michael@0: { michael@0: NS_PRECONDITION(aFrame, "non-null frame required"); michael@0: NS_PRECONDITION(aState, "non-null state required"); michael@0: michael@0: if (!DR_state->mActive) return; michael@0: if (!aValue) return; michael@0: michael@0: DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; michael@0: if (treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16]; michael@0: DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw); michael@0: DR_state->PrettyUC(aState->ComputedWidth(), cw); michael@0: DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw); michael@0: DR_state->PrettyUC(aState->ComputedMinHeight(), cmih); michael@0: DR_state->PrettyUC(aState->ComputedHeight(), ch); michael@0: DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh); michael@0: printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", michael@0: cmiw, cw, cmxw, cmih, ch, cmxh); michael@0: DR_state->PrintMargin("co", &aState->ComputedPhysicalOffsets()); michael@0: putchar('\n'); michael@0: } michael@0: DR_state->DeleteTreeNode(*treeNode); michael@0: } michael@0: michael@0: michael@0: /* static */ void* michael@0: nsCSSOffsetState::DisplayInitOffsetsEnter(nsIFrame* aFrame, michael@0: nsCSSOffsetState* aState, michael@0: nscoord aHorizontalPercentBasis, michael@0: nscoord aVerticalPercentBasis, michael@0: const nsMargin* aBorder, michael@0: const nsMargin* aPadding) michael@0: { michael@0: NS_PRECONDITION(aFrame, "non-null frame required"); michael@0: NS_PRECONDITION(aState, "non-null state required"); michael@0: michael@0: if (!DR_state->mInited) DR_state->Init(); michael@0: if (!DR_state->mActive) return nullptr; michael@0: michael@0: // aState is not necessarily a nsHTMLReflowState michael@0: DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); michael@0: if (treeNode && treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: michael@0: char horizPctBasisStr[16]; michael@0: char vertPctBasisStr[16]; michael@0: DR_state->PrettyUC(aHorizontalPercentBasis, horizPctBasisStr); michael@0: DR_state->PrettyUC(aVerticalPercentBasis, vertPctBasisStr); michael@0: printf("InitOffsets pct_basis=%s,%s", horizPctBasisStr, vertPctBasisStr); michael@0: michael@0: DR_state->PrintMargin("b", aBorder); michael@0: DR_state->PrintMargin("p", aPadding); michael@0: putchar('\n'); michael@0: } michael@0: return treeNode; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsCSSOffsetState::DisplayInitOffsetsExit(nsIFrame* aFrame, michael@0: nsCSSOffsetState* aState, michael@0: void* aValue) michael@0: { michael@0: NS_PRECONDITION(aFrame, "non-null frame required"); michael@0: NS_PRECONDITION(aState, "non-null state required"); michael@0: michael@0: if (!DR_state->mActive) return; michael@0: if (!aValue) return; michael@0: michael@0: DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; michael@0: if (treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: printf("InitOffsets="); michael@0: DR_state->PrintMargin("m", &aState->ComputedPhysicalMargin()); michael@0: DR_state->PrintMargin("p", &aState->ComputedPhysicalPadding()); michael@0: DR_state->PrintMargin("p+b", &aState->ComputedPhysicalBorderPadding()); michael@0: putchar('\n'); michael@0: } michael@0: DR_state->DeleteTreeNode(*treeNode); michael@0: } michael@0: michael@0: /* static */ void* michael@0: nsHTMLReflowState::DisplayInitFrameTypeEnter(nsIFrame* aFrame, michael@0: nsHTMLReflowState* aState) michael@0: { michael@0: NS_PRECONDITION(aFrame, "non-null frame required"); michael@0: NS_PRECONDITION(aState, "non-null state required"); michael@0: michael@0: if (!DR_state->mInited) DR_state->Init(); michael@0: if (!DR_state->mActive) return nullptr; michael@0: michael@0: // we don't print anything here michael@0: return DR_state->CreateTreeNode(aFrame, aState); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsHTMLReflowState::DisplayInitFrameTypeExit(nsIFrame* aFrame, michael@0: nsHTMLReflowState* aState, michael@0: void* aValue) michael@0: { michael@0: NS_PRECONDITION(aFrame, "non-null frame required"); michael@0: NS_PRECONDITION(aState, "non-null state required"); michael@0: michael@0: if (!DR_state->mActive) return; michael@0: if (!aValue) return; michael@0: michael@0: DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; michael@0: if (treeNode->mDisplay) { michael@0: DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); michael@0: printf("InitFrameType"); michael@0: michael@0: const nsStyleDisplay *disp = aState->mStyleDisplay; michael@0: michael@0: if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) michael@0: printf(" out-of-flow"); michael@0: if (aFrame->GetPrevInFlow()) michael@0: printf(" prev-in-flow"); michael@0: if (aFrame->IsAbsolutelyPositioned()) michael@0: printf(" abspos"); michael@0: if (aFrame->IsFloating()) michael@0: printf(" float"); michael@0: michael@0: // This array must exactly match the NS_STYLE_DISPLAY constants. michael@0: const char *const displayTypes[] = { michael@0: "none", "block", "inline", "inline-block", "list-item", "marker", michael@0: "run-in", "compact", "table", "inline-table", "table-row-group", michael@0: "table-column", "table-column-group", "table-header-group", michael@0: "table-footer-group", "table-row", "table-cell", "table-caption", michael@0: "box", "inline-box", michael@0: #ifdef MOZ_XUL michael@0: "grid", "inline-grid", "grid-group", "grid-line", "stack", michael@0: "inline-stack", "deck", "popup", "groupbox", michael@0: #endif michael@0: }; michael@0: if (disp->mDisplay >= ArrayLength(displayTypes)) michael@0: printf(" display=%u", disp->mDisplay); michael@0: else michael@0: printf(" display=%s", displayTypes[disp->mDisplay]); michael@0: michael@0: // This array must exactly match the NS_CSS_FRAME_TYPE constants. michael@0: const char *const cssFrameTypes[] = { michael@0: "unknown", "inline", "block", "floating", "absolute", "internal-table" michael@0: }; michael@0: nsCSSFrameType bareType = NS_FRAME_GET_TYPE(aState->mFrameType); michael@0: bool repNoBlock = NS_FRAME_IS_REPLACED_NOBLOCK(aState->mFrameType); michael@0: bool repBlock = NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState->mFrameType); michael@0: michael@0: if (bareType >= ArrayLength(cssFrameTypes)) { michael@0: printf(" result=type %u", bareType); michael@0: } else { michael@0: printf(" result=%s", cssFrameTypes[bareType]); michael@0: } michael@0: printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : ""); michael@0: } michael@0: DR_state->DeleteTreeNode(*treeNode); michael@0: } michael@0: michael@0: #endif michael@0: // End Display Reflow michael@0: michael@0: #endif