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: /* michael@0: * construction of a frame tree that is nearly isomorphic to the content michael@0: * tree and updating of that tree in response to dynamic changes michael@0: */ michael@0: michael@0: #include "nsCSSFrameConstructor.h" michael@0: michael@0: #include "mozilla/AutoRestore.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/dom/HTMLSelectElement.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/LinkedList.h" michael@0: #include "nsAbsoluteContainingBlock.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsIFrameInlines.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsTableFrame.h" michael@0: #include "nsTableColFrame.h" michael@0: #include "nsIDOMHTMLDocument.h" michael@0: #include "nsHTMLParts.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsIDOMXULElement.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsIComboboxControlFrame.h" michael@0: #include "nsIListControlFrame.h" michael@0: #include "nsIDOMCharacterData.h" michael@0: #include "nsPlaceholderFrame.h" michael@0: #include "nsTableRowGroupFrame.h" michael@0: #include "nsIFormControl.h" michael@0: #include "nsCSSAnonBoxes.h" michael@0: #include "nsTextFragment.h" michael@0: #include "nsIAnonymousContentCreator.h" michael@0: #include "nsBindingManager.h" michael@0: #include "nsXBLBinding.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIScriptError.h" michael@0: #ifdef XP_MACOSX michael@0: #include "nsIDocShell.h" michael@0: #endif michael@0: #include "ChildIterator.h" michael@0: #include "nsError.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsBoxFrame.h" michael@0: #include "nsBoxLayout.h" michael@0: #include "nsFlexContainerFrame.h" michael@0: #include "nsGridContainerFrame.h" michael@0: #include "nsImageFrame.h" michael@0: #include "nsIObjectLoadingContent.h" michael@0: #include "nsTArray.h" michael@0: #include "nsGenericDOMDataNode.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsAutoLayoutPhase.h" michael@0: #include "nsStyleStructInlines.h" michael@0: #include "nsPageContentFrame.h" michael@0: #include "RestyleManager.h" michael@0: #include "StickyScrollContainer.h" michael@0: #include "nsFieldSetFrame.h" michael@0: michael@0: #ifdef MOZ_XUL michael@0: #include "nsIRootBox.h" michael@0: #endif michael@0: #ifdef ACCESSIBILITY michael@0: #include "nsAccessibilityService.h" michael@0: #endif michael@0: michael@0: #include "nsBlockFrame.h" michael@0: michael@0: #include "nsIScrollableFrame.h" michael@0: michael@0: #include "nsXBLService.h" michael@0: michael@0: #undef NOISY_FIRST_LETTER michael@0: michael@0: #include "nsMathMLParts.h" michael@0: #include "mozilla/dom/SVGTests.h" michael@0: #include "nsSVGUtils.h" michael@0: michael@0: #include "nsRefreshDriver.h" michael@0: #include "nsRuleProcessorData.h" michael@0: #include "nsTextNode.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: // An alias for convenience. michael@0: static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList; michael@0: michael@0: nsIFrame* michael@0: NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewHTMLVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGGenericContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGAFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGViewFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: extern nsIFrame* michael@0: NS_NewSVGLinearGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext); michael@0: extern nsIFrame* michael@0: NS_NewSVGRadialGradientFrame(nsIPresShell *aPresShell, nsStyleContext* aContext); michael@0: extern nsIFrame* michael@0: NS_NewSVGStopFrame(nsIPresShell *aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: extern nsIFrame* michael@0: NS_NewSVGImageFrame(nsIPresShell *aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGFilterFrame(nsIPresShell *aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGPatternFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGFEContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGFELeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGFEImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: #include "nsINodeInfo.h" michael@0: #include "prenv.h" michael@0: #include "nsNodeInfoManager.h" michael@0: #include "nsContentCreatorFunctions.h" michael@0: michael@0: #ifdef DEBUG michael@0: // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or michael@0: // more of the following flags (comma separated) for handy debug michael@0: // output. michael@0: static bool gNoisyContentUpdates = false; michael@0: static bool gReallyNoisyContentUpdates = false; michael@0: static bool gNoisyInlineConstruction = false; michael@0: michael@0: struct FrameCtorDebugFlags { michael@0: const char* name; michael@0: bool* on; michael@0: }; michael@0: michael@0: static FrameCtorDebugFlags gFlags[] = { michael@0: { "content-updates", &gNoisyContentUpdates }, michael@0: { "really-noisy-content-updates", &gReallyNoisyContentUpdates }, michael@0: { "noisy-inline", &gNoisyInlineConstruction } michael@0: }; michael@0: michael@0: #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0])) michael@0: #endif michael@0: michael@0: michael@0: #ifdef MOZ_XUL michael@0: #include "nsMenuFrame.h" michael@0: #include "nsPopupSetFrame.h" michael@0: #include "nsTreeColFrame.h" michael@0: #include "nsIBoxObject.h" michael@0: #include "nsPIListBoxObject.h" michael@0: #include "nsListBoxBodyFrame.h" michael@0: #include "nsListItemFrame.h" michael@0: #include "nsXULLabelFrame.h" michael@0: michael@0: //------------------------------------------------------------------ michael@0: michael@0: nsIFrame* michael@0: NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewRootBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewDocElementBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewThumbFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewDeckFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewLeafBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewStackFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewProgressMeterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewRangeFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewGroupBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewButtonBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewMenuPopupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewMenuFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, uint32_t aFlags); michael@0: michael@0: nsIFrame* michael@0: NS_NewMenuBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewTreeBodyFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: // grid michael@0: nsresult michael@0: NS_NewGridLayout2 ( nsIPresShell* aPresShell, nsBoxLayout** aNewLayout ); michael@0: nsIFrame* michael@0: NS_NewGridRowLeafFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: nsIFrame* michael@0: NS_NewGridRowGroupFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: // end grid michael@0: michael@0: nsIFrame* michael@0: NS_NewTitleBarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewResizerFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: michael@0: #endif michael@0: michael@0: nsIFrame* michael@0: NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot); michael@0: michael@0: nsIFrame* michael@0: NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, michael@0: bool aIsRoot, bool aClipAllDescendants); michael@0: michael@0: nsIFrame* michael@0: NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: nsIFrame* michael@0: NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: michael@0: #ifdef NOISY_FINDFRAME michael@0: static int32_t FFWC_totalCount=0; michael@0: static int32_t FFWC_doLoop=0; michael@0: static int32_t FFWC_doSibling=0; michael@0: static int32_t FFWC_recursions=0; michael@0: static int32_t FFWC_nextInFlows=0; michael@0: #endif michael@0: michael@0: // Returns true if aFrame is an anonymous flex item michael@0: static inline bool michael@0: IsAnonymousFlexItem(const nsIFrame* aFrame) michael@0: { michael@0: const nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo(); michael@0: return pseudoType == nsCSSAnonBoxes::anonymousFlexItem; michael@0: } michael@0: michael@0: static inline nsIFrame* michael@0: GetFieldSetBlockFrame(nsIFrame* aFieldsetFrame) michael@0: { michael@0: // Depends on the fieldset child frame order - see ConstructFieldSetFrame() below. michael@0: nsIFrame* firstChild = aFieldsetFrame->GetFirstPrincipalChild(); michael@0: nsIFrame* inner = firstChild && firstChild->GetNextSibling() ? firstChild->GetNextSibling() : firstChild; michael@0: return inner ? inner->GetContentInsertionFrame() : nullptr; michael@0: } michael@0: michael@0: #define FCDATA_DECL(_flags, _func) \ michael@0: { _flags, { (FrameCreationFunc)_func }, nullptr, nullptr } michael@0: #define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \ michael@0: { _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS, \ michael@0: { (FrameCreationFunc)_func }, nullptr, &_anon_box } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * True if aFrame is an actual inline frame in the sense of non-replaced michael@0: * display:inline CSS boxes. In other words, it can be affected by {ib} michael@0: * splitting and can contain first-letter frames. Basically, this is either an michael@0: * inline frame (positioned or otherwise) or an line frame (this last because michael@0: * it can contain first-letter and because inserting blocks in the middle of it michael@0: * needs to terminate it). michael@0: */ michael@0: static bool michael@0: IsInlineFrame(const nsIFrame* aFrame) michael@0: { michael@0: return aFrame->IsFrameOfType(nsIFrame::eLineParticipant); michael@0: } michael@0: michael@0: /** michael@0: * True if aFrame is an instance of an SVG frame class or is an inline/block michael@0: * frame being used for SVG text. michael@0: */ michael@0: static bool michael@0: IsFrameForSVG(const nsIFrame* aFrame) michael@0: { michael@0: return aFrame->IsFrameOfType(nsIFrame::eSVG) || michael@0: aFrame->IsSVGText(); michael@0: } michael@0: michael@0: /** michael@0: * Returns true iff aFrame explicitly prevents its descendants from floating michael@0: * (at least, down to the level of descendants which themselves are michael@0: * float-containing blocks -- those will manage the floating status of any michael@0: * lower-level descendents inside them, of course). michael@0: */ michael@0: static bool michael@0: ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) michael@0: { michael@0: return aFrame->IsFrameOfType(nsIFrame::eMathML) || michael@0: aFrame->IsBoxFrame() || michael@0: aFrame->GetType() == nsGkAtoms::flexContainerFrame || michael@0: aFrame->GetType() == nsGkAtoms::gridContainerFrame; michael@0: } michael@0: michael@0: /** michael@0: * If any children require a block parent, return the first such child. michael@0: * Otherwise return null. michael@0: */ michael@0: static nsIContent* michael@0: AnyKidsNeedBlockParent(nsIFrame *aFrameList) michael@0: { michael@0: for (nsIFrame *k = aFrameList; k; k = k->GetNextSibling()) { michael@0: // Line participants, such as text and inline frames, can't be michael@0: // directly inside a XUL box; they must be wrapped in an michael@0: // intermediate block. michael@0: if (k->IsFrameOfType(nsIFrame::eLineParticipant)) { michael@0: return k->GetContent(); michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: // Reparent a frame into a wrapper frame that is a child of its old parent. michael@0: static void michael@0: ReparentFrame(RestyleManager* aRestyleManager, michael@0: nsIFrame* aNewParentFrame, michael@0: nsIFrame* aFrame) michael@0: { michael@0: aFrame->SetParent(aNewParentFrame); michael@0: aRestyleManager->ReparentStyleContext(aFrame); michael@0: } michael@0: michael@0: static void michael@0: ReparentFrames(nsCSSFrameConstructor* aFrameConstructor, michael@0: nsIFrame* aNewParentFrame, michael@0: const nsFrameList& aFrameList) michael@0: { michael@0: RestyleManager* restyleManager = aFrameConstructor->RestyleManager(); michael@0: for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { michael@0: ReparentFrame(restyleManager, aNewParentFrame, e.get()); michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // When inline frames get weird and have block frames in them, we michael@0: // annotate them to help us respond to incremental content changes michael@0: // more easily. michael@0: michael@0: static inline bool michael@0: IsFramePartOfIBSplit(nsIFrame* aFrame) michael@0: { michael@0: return (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0; michael@0: } michael@0: michael@0: static nsIFrame* GetIBSplitSibling(nsIFrame* aFrame) michael@0: { michael@0: NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this"); michael@0: michael@0: // We only store the "ib-split sibling" annotation with the first michael@0: // frame in the continuation chain. Walk back to find that frame now. michael@0: return static_cast michael@0: (aFrame->FirstContinuation()-> michael@0: Properties().Get(nsIFrame::IBSplitSibling())); michael@0: } michael@0: michael@0: static nsIFrame* GetIBSplitPrevSibling(nsIFrame* aFrame) michael@0: { michael@0: NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), "Shouldn't call this"); michael@0: michael@0: // We only store the ib-split sibling annotation with the first michael@0: // frame in the continuation chain. Walk back to find that frame now. michael@0: return static_cast michael@0: (aFrame->FirstContinuation()-> michael@0: Properties().Get(nsIFrame::IBSplitPrevSibling())); michael@0: } michael@0: michael@0: static nsIFrame* michael@0: GetLastIBSplitSibling(nsIFrame* aFrame, bool aReturnEmptyTrailingInline) michael@0: { michael@0: for (nsIFrame *frame = aFrame, *next; ; frame = next) { michael@0: next = GetIBSplitSibling(frame); michael@0: if (!next || michael@0: (!aReturnEmptyTrailingInline && !next->GetFirstPrincipalChild() && michael@0: !GetIBSplitSibling(next))) { michael@0: NS_ASSERTION(!next || !frame->IsInlineOutside(), michael@0: "Should have a block here!"); michael@0: return frame; michael@0: } michael@0: } michael@0: NS_NOTREACHED("unreachable code"); michael@0: return nullptr; michael@0: } michael@0: michael@0: static void michael@0: SetFrameIsIBSplit(nsIFrame* aFrame, nsIFrame* aIBSplitSibling) michael@0: { michael@0: NS_PRECONDITION(aFrame, "bad args!"); michael@0: michael@0: // We should be the only continuation michael@0: NS_ASSERTION(!aFrame->GetPrevContinuation(), michael@0: "assigning ib-split sibling to other than first continuation!"); michael@0: NS_ASSERTION(!aFrame->GetNextContinuation() || michael@0: IsFramePartOfIBSplit(aFrame->GetNextContinuation()), michael@0: "should have no non-ib-split continuations here"); michael@0: michael@0: // Mark the frame as ib-split. michael@0: aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT); michael@0: michael@0: if (aIBSplitSibling) { michael@0: NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(), michael@0: "assigning something other than the first continuation as the " michael@0: "ib-split sibling"); michael@0: michael@0: // Store the ib-split sibling (if we were given one) with the michael@0: // first frame in the flow. michael@0: FramePropertyTable* props = aFrame->PresContext()->PropertyTable(); michael@0: props->Set(aFrame, nsIFrame::IBSplitSibling(), aIBSplitSibling); michael@0: props->Set(aIBSplitSibling, nsIFrame::IBSplitPrevSibling(), aFrame); michael@0: } michael@0: } michael@0: michael@0: static nsIFrame* michael@0: GetIBContainingBlockFor(nsIFrame* aFrame) michael@0: { michael@0: NS_PRECONDITION(IsFramePartOfIBSplit(aFrame), michael@0: "GetIBContainingBlockFor() should only be called on known IB frames"); michael@0: michael@0: // Get the first "normal" ancestor of the target frame. michael@0: nsIFrame* parentFrame; michael@0: do { michael@0: parentFrame = aFrame->GetParent(); michael@0: michael@0: if (! parentFrame) { michael@0: NS_ERROR("no unsplit block frame in IB hierarchy"); michael@0: return aFrame; michael@0: } michael@0: michael@0: // Note that we ignore non-ib-split frames which have a pseudo on their michael@0: // style context -- they're not the frames we're looking for! In michael@0: // particular, they may be hiding a real parent that _is_ in an ib-split. michael@0: if (!IsFramePartOfIBSplit(parentFrame) && michael@0: !parentFrame->StyleContext()->GetPseudo()) michael@0: break; michael@0: michael@0: aFrame = parentFrame; michael@0: } while (1); michael@0: michael@0: // post-conditions michael@0: NS_ASSERTION(parentFrame, "no normal ancestor found for ib-split frame " michael@0: "in GetIBContainingBlockFor"); michael@0: NS_ASSERTION(parentFrame != aFrame, "parentFrame is actually the child frame - bogus reslt"); michael@0: michael@0: return parentFrame; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: // Block/inline frame construction logic. We maintain a few invariants here: michael@0: // michael@0: // 1. Block frames contain block and inline frames. michael@0: // michael@0: // 2. Inline frames only contain inline frames. If an inline parent has a block michael@0: // child then the block child is migrated upward until it lands in a block michael@0: // parent (the inline frames containing block is where it will end up). michael@0: michael@0: // After this function returns, aLink is pointing to the first link at or michael@0: // after its starting position for which the next frame is a block. If there michael@0: // is no such link, it points to the end of the list. michael@0: static void michael@0: FindFirstBlock(nsFrameList::FrameLinkEnumerator& aLink) michael@0: { michael@0: for ( ; !aLink.AtEnd(); aLink.Next()) { michael@0: if (!aLink.NextFrame()->IsInlineOutside()) { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // This function returns a frame link enumerator pointing to the first link in michael@0: // the list for which the next frame is not block. If there is no such link, michael@0: // it points to the end of the list. michael@0: static nsFrameList::FrameLinkEnumerator michael@0: FindFirstNonBlock(const nsFrameList& aList) michael@0: { michael@0: nsFrameList::FrameLinkEnumerator link(aList); michael@0: for (; !link.AtEnd(); link.Next()) { michael@0: if (link.NextFrame()->IsInlineOutside()) { michael@0: break; michael@0: } michael@0: } michael@0: return link; michael@0: } michael@0: michael@0: inline void michael@0: SetInitialSingleChild(nsIFrame* aParent, nsIFrame* aFrame) michael@0: { michael@0: NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list"); michael@0: nsFrameList temp(aFrame, aFrame); michael@0: aParent->SetInitialChildList(kPrincipalList, temp); michael@0: } michael@0: michael@0: // ----------------------------------------------------------- michael@0: michael@0: // Structure used when constructing formatting object trees. michael@0: struct nsFrameItems : public nsFrameList michael@0: { michael@0: // Appends the frame to the end of the list michael@0: void AddChild(nsIFrame* aChild); michael@0: }; michael@0: michael@0: void michael@0: nsFrameItems::AddChild(nsIFrame* aChild) michael@0: { michael@0: NS_PRECONDITION(aChild, "nsFrameItems::AddChild"); michael@0: michael@0: // It'd be really nice if we could just AppendFrames(kPrincipalList, aChild) here, michael@0: // but some of our callers put frames that have different michael@0: // parents (caption, I'm looking at you) on the same framelist, and michael@0: // nsFrameList asserts if you try to do that. michael@0: if (IsEmpty()) { michael@0: SetFrames(aChild); michael@0: } michael@0: else { michael@0: NS_ASSERTION(aChild != mLastChild, michael@0: "Same frame being added to frame list twice?"); michael@0: mLastChild->SetNextSibling(aChild); michael@0: mLastChild = nsLayoutUtils::GetLastSibling(aChild); michael@0: } michael@0: } michael@0: michael@0: // ----------------------------------------------------------- michael@0: michael@0: // Structure used when constructing formatting object trees. Contains michael@0: // state information needed for absolutely positioned elements michael@0: struct nsAbsoluteItems : nsFrameItems { michael@0: // containing block for absolutely positioned elements michael@0: nsIFrame* containingBlock; michael@0: michael@0: nsAbsoluteItems(nsIFrame* aContainingBlock); michael@0: #ifdef DEBUG michael@0: // XXXbz Does this need a debug-only assignment operator that nulls out the michael@0: // childList in the nsAbsoluteItems we're copying? Introducing a difference michael@0: // between debug and non-debug behavior seems bad, so I guess not... michael@0: ~nsAbsoluteItems() { michael@0: NS_ASSERTION(!FirstChild(), michael@0: "Dangling child list. Someone forgot to insert it?"); michael@0: } michael@0: #endif michael@0: michael@0: // Appends the frame to the end of the list michael@0: void AddChild(nsIFrame* aChild); michael@0: }; michael@0: michael@0: nsAbsoluteItems::nsAbsoluteItems(nsIFrame* aContainingBlock) michael@0: : containingBlock(aContainingBlock) michael@0: { michael@0: } michael@0: michael@0: // Additional behavior is that it sets the frame's NS_FRAME_OUT_OF_FLOW flag michael@0: void michael@0: nsAbsoluteItems::AddChild(nsIFrame* aChild) michael@0: { michael@0: NS_ASSERTION(aChild->PresContext()->FrameManager()-> michael@0: GetPlaceholderFrameFor(aChild), michael@0: "Child without placeholder being added to nsAbsoluteItems?"); michael@0: aChild->AddStateBits(NS_FRAME_OUT_OF_FLOW); michael@0: nsFrameItems::AddChild(aChild); michael@0: } michael@0: michael@0: // ----------------------------------------------------------- michael@0: michael@0: // Structure for saving the existing state when pushing/poping containing michael@0: // blocks. The destructor restores the state to its previous state michael@0: class MOZ_STACK_CLASS nsFrameConstructorSaveState { michael@0: public: michael@0: typedef nsIFrame::ChildListID ChildListID; michael@0: nsFrameConstructorSaveState(); michael@0: ~nsFrameConstructorSaveState(); michael@0: michael@0: private: michael@0: nsAbsoluteItems* mItems; // pointer to struct whose data we save/restore michael@0: nsAbsoluteItems mSavedItems; // copy of original data michael@0: michael@0: // The name of the child list in which our frames would belong michael@0: ChildListID mChildListID; michael@0: nsFrameConstructorState* mState; michael@0: michael@0: // State used only when we're saving the abs-pos state for a transformed michael@0: // element. michael@0: nsAbsoluteItems mSavedFixedItems; michael@0: michael@0: bool mSavedFixedPosIsAbsPos; michael@0: michael@0: friend class nsFrameConstructorState; michael@0: }; michael@0: michael@0: // Structure used to keep track of a list of bindings we need to call michael@0: // AddToAttachedQueue on. These should be in post-order depth-first michael@0: // flattened tree traversal order. michael@0: struct PendingBinding : public LinkedListElement michael@0: { michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: PendingBinding() { michael@0: MOZ_COUNT_CTOR(PendingBinding); michael@0: } michael@0: ~PendingBinding() { michael@0: MOZ_COUNT_DTOR(PendingBinding); michael@0: } michael@0: #endif michael@0: michael@0: nsRefPtr mBinding; michael@0: }; michael@0: michael@0: // Structure used for maintaining state information during the michael@0: // frame construction process michael@0: class MOZ_STACK_CLASS nsFrameConstructorState { michael@0: public: michael@0: typedef nsIFrame::ChildListID ChildListID; michael@0: michael@0: nsPresContext *mPresContext; michael@0: nsIPresShell *mPresShell; michael@0: nsFrameManager *mFrameManager; michael@0: michael@0: #ifdef MOZ_XUL michael@0: // Frames destined for the kPopupList. michael@0: nsAbsoluteItems mPopupItems; michael@0: #endif michael@0: michael@0: // Containing block information for out-of-flow frames. michael@0: nsAbsoluteItems mFixedItems; michael@0: nsAbsoluteItems mAbsoluteItems; michael@0: nsAbsoluteItems mFloatedItems; michael@0: michael@0: nsCOMPtr mFrameState; michael@0: // These bits will be added to the state bits of any frame we construct michael@0: // using this state. michael@0: nsFrameState mAdditionalStateBits; michael@0: michael@0: // When working with the -moz-transform property, we want to hook michael@0: // the abs-pos and fixed-pos lists together, since transformed michael@0: // elements are fixed-pos containing blocks. This flag determines michael@0: // whether or not we want to wire the fixed-pos and abs-pos lists michael@0: // together. michael@0: bool mFixedPosIsAbsPos; michael@0: michael@0: // A boolean to indicate whether we have a "pending" popupgroup. That is, we michael@0: // have already created the FrameConstructionItem for the root popupgroup but michael@0: // we have not yet created the relevant frame. michael@0: bool mHavePendingPopupgroup; michael@0: michael@0: // If false (which is the default) then call SetPrimaryFrame() as needed michael@0: // during frame construction. If true, don't make any SetPrimaryFrame() michael@0: // calls, except for generated content which doesn't have a primary frame michael@0: // yet. The mCreatingExtraFrames == true mode is meant to be used for michael@0: // construction of random "extra" frames for elements via normal frame michael@0: // construction APIs (e.g. replication of things across pages in paginated michael@0: // mode). michael@0: bool mCreatingExtraFrames; michael@0: michael@0: nsCOMArray mGeneratedTextNodesWithInitializer; michael@0: michael@0: TreeMatchContext mTreeMatchContext; michael@0: michael@0: // Constructor michael@0: // Use the passed-in history state. michael@0: nsFrameConstructorState(nsIPresShell* aPresShell, michael@0: nsIFrame* aFixedContainingBlock, michael@0: nsIFrame* aAbsoluteContainingBlock, michael@0: nsIFrame* aFloatContainingBlock, michael@0: nsILayoutHistoryState* aHistoryState); michael@0: // Get the history state from the pres context's pres shell. michael@0: nsFrameConstructorState(nsIPresShell* aPresShell, michael@0: nsIFrame* aFixedContainingBlock, michael@0: nsIFrame* aAbsoluteContainingBlock, michael@0: nsIFrame* aFloatContainingBlock); michael@0: michael@0: ~nsFrameConstructorState(); michael@0: michael@0: // Function to push the existing absolute containing block state and michael@0: // create a new scope. Code that uses this function should get matching michael@0: // logic in GetAbsoluteContainingBlock. michael@0: // Also makes aNewAbsoluteContainingBlock the containing block for michael@0: // fixed-pos elements if necessary. michael@0: // aPositionedFrame is the frame whose style actually makes michael@0: // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable element michael@0: // aPositionedFrame is the element's primary frame and michael@0: // aNewAbsoluteContainingBlock is the scrolled frame. michael@0: void PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock, michael@0: nsIFrame* aPositionedFrame, michael@0: nsFrameConstructorSaveState& aSaveState); michael@0: michael@0: // Function to push the existing float containing block state and michael@0: // create a new scope. Code that uses this function should get matching michael@0: // logic in GetFloatContainingBlock. michael@0: // Pushing a null float containing block forbids any frames from being michael@0: // floated until a new float containing block is pushed. michael@0: // XXX we should get rid of null float containing blocks and teach the michael@0: // various frame classes to deal with floats instead. michael@0: void PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock, michael@0: nsFrameConstructorSaveState& aSaveState); michael@0: michael@0: // Function to return the proper geometric parent for a frame with display michael@0: // struct given by aStyleDisplay and parent's frame given by michael@0: // aContentParentFrame. michael@0: nsIFrame* GetGeometricParent(const nsStyleDisplay* aStyleDisplay, michael@0: nsIFrame* aContentParentFrame) const; michael@0: michael@0: /** michael@0: * Function to add a new frame to the right frame list. This MUST be called michael@0: * on frames before their children have been processed if the frames might michael@0: * conceivably be out-of-flow; otherwise cleanup in error cases won't work michael@0: * right. Also, this MUST be called on frames after they have been michael@0: * initialized. michael@0: * @param aNewFrame the frame to add michael@0: * @param aFrameItems the list to add in-flow frames to michael@0: * @param aContent the content pointer for aNewFrame michael@0: * @param aStyleContext the style context resolved for aContent michael@0: * @param aParentFrame the parent frame for the content if it were in-flow michael@0: * @param aCanBePositioned pass false if the frame isn't allowed to be michael@0: * positioned michael@0: * @param aCanBeFloated pass false if the frame isn't allowed to be michael@0: * floated michael@0: * @param aIsOutOfFlowPopup pass true if the frame is an out-of-flow popup michael@0: * (XUL-only) michael@0: */ michael@0: void AddChild(nsIFrame* aNewFrame, michael@0: nsFrameItems& aFrameItems, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame* aParentFrame, michael@0: bool aCanBePositioned = true, michael@0: bool aCanBeFloated = true, michael@0: bool aIsOutOfFlowPopup = false, michael@0: bool aInsertAfter = false, michael@0: nsIFrame* aInsertAfterFrame = nullptr); michael@0: michael@0: /** michael@0: * Function to return the fixed-pos element list. Normally this will just hand back the michael@0: * fixed-pos element list, but in case we're dealing with a transformed element that's michael@0: * acting as an abs-pos and fixed-pos container, we'll hand back the abs-pos list. Callers should michael@0: * use this function if they want to get the list acting as the fixed-pos item parent. michael@0: */ michael@0: nsAbsoluteItems& GetFixedItems() michael@0: { michael@0: return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems; michael@0: } michael@0: const nsAbsoluteItems& GetFixedItems() const michael@0: { michael@0: return mFixedPosIsAbsPos ? mAbsoluteItems : mFixedItems; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * class to automatically push and pop a pending binding in the frame michael@0: * constructor state. See nsCSSFrameConstructor::FrameConstructionItem michael@0: * mPendingBinding documentation. michael@0: */ michael@0: class PendingBindingAutoPusher; michael@0: friend class PendingBindingAutoPusher; michael@0: class MOZ_STACK_CLASS PendingBindingAutoPusher { michael@0: public: michael@0: PendingBindingAutoPusher(nsFrameConstructorState& aState, michael@0: PendingBinding* aPendingBinding) : michael@0: mState(aState), michael@0: mPendingBinding(aState.mCurrentPendingBindingInsertionPoint) michael@0: { michael@0: if (aPendingBinding) { michael@0: aState.mCurrentPendingBindingInsertionPoint = aPendingBinding; michael@0: } michael@0: } michael@0: michael@0: ~PendingBindingAutoPusher() michael@0: { michael@0: mState.mCurrentPendingBindingInsertionPoint = mPendingBinding; michael@0: } michael@0: michael@0: private: michael@0: nsFrameConstructorState& mState; michael@0: PendingBinding* mPendingBinding; michael@0: }; michael@0: michael@0: /** michael@0: * Add a new pending binding to the list michael@0: */ michael@0: void AddPendingBinding(PendingBinding* aPendingBinding) { michael@0: if (mCurrentPendingBindingInsertionPoint) { michael@0: mCurrentPendingBindingInsertionPoint->setPrevious(aPendingBinding); michael@0: } else { michael@0: mPendingBindings.insertBack(aPendingBinding); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: friend class nsFrameConstructorSaveState; michael@0: michael@0: /** michael@0: * ProcessFrameInsertions takes the frames in aFrameItems and adds them as michael@0: * kids to the aChildListID child list of |aFrameItems.containingBlock|. michael@0: */ michael@0: void ProcessFrameInsertions(nsAbsoluteItems& aFrameItems, michael@0: ChildListID aChildListID); michael@0: michael@0: // Our list of all pending bindings. When we're done, we need to call michael@0: // AddToAttachedQueue on all of them, in order. michael@0: LinkedList mPendingBindings; michael@0: michael@0: PendingBinding* mCurrentPendingBindingInsertionPoint; michael@0: }; michael@0: michael@0: nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell, michael@0: nsIFrame* aFixedContainingBlock, michael@0: nsIFrame* aAbsoluteContainingBlock, michael@0: nsIFrame* aFloatContainingBlock, michael@0: nsILayoutHistoryState* aHistoryState) michael@0: : mPresContext(aPresShell->GetPresContext()), michael@0: mPresShell(aPresShell), michael@0: mFrameManager(aPresShell->FrameManager()), michael@0: #ifdef MOZ_XUL michael@0: mPopupItems(nullptr), michael@0: #endif michael@0: mFixedItems(aFixedContainingBlock), michael@0: mAbsoluteItems(aAbsoluteContainingBlock), michael@0: mFloatedItems(aFloatContainingBlock), michael@0: // See PushAbsoluteContaningBlock below michael@0: mFrameState(aHistoryState), michael@0: mAdditionalStateBits(nsFrameState(0)), michael@0: // If the fixed-pos containing block is equal to the abs-pos containing michael@0: // block, use the abs-pos containing block's abs-pos list for fixed-pos michael@0: // frames. michael@0: mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock), michael@0: mHavePendingPopupgroup(false), michael@0: mCreatingExtraFrames(false), michael@0: mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited, michael@0: aPresShell->GetDocument()), michael@0: mCurrentPendingBindingInsertionPoint(nullptr) michael@0: { michael@0: #ifdef MOZ_XUL michael@0: nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell); michael@0: if (rootBox) { michael@0: mPopupItems.containingBlock = rootBox->GetPopupSetFrame(); michael@0: } michael@0: #endif michael@0: MOZ_COUNT_CTOR(nsFrameConstructorState); michael@0: } michael@0: michael@0: nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell, michael@0: nsIFrame* aFixedContainingBlock, michael@0: nsIFrame* aAbsoluteContainingBlock, michael@0: nsIFrame* aFloatContainingBlock) michael@0: : mPresContext(aPresShell->GetPresContext()), michael@0: mPresShell(aPresShell), michael@0: mFrameManager(aPresShell->FrameManager()), michael@0: #ifdef MOZ_XUL michael@0: mPopupItems(nullptr), michael@0: #endif michael@0: mFixedItems(aFixedContainingBlock), michael@0: mAbsoluteItems(aAbsoluteContainingBlock), michael@0: mFloatedItems(aFloatContainingBlock), michael@0: // See PushAbsoluteContaningBlock below michael@0: mAdditionalStateBits(nsFrameState(0)), michael@0: // If the fixed-pos containing block is equal to the abs-pos containing michael@0: // block, use the abs-pos containing block's abs-pos list for fixed-pos michael@0: // frames. michael@0: mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock), michael@0: mHavePendingPopupgroup(false), michael@0: mCreatingExtraFrames(false), michael@0: mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited, michael@0: aPresShell->GetDocument()), michael@0: mCurrentPendingBindingInsertionPoint(nullptr) michael@0: { michael@0: #ifdef MOZ_XUL michael@0: nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell); michael@0: if (rootBox) { michael@0: mPopupItems.containingBlock = rootBox->GetPopupSetFrame(); michael@0: } michael@0: #endif michael@0: MOZ_COUNT_CTOR(nsFrameConstructorState); michael@0: mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState(); michael@0: } michael@0: michael@0: nsFrameConstructorState::~nsFrameConstructorState() michael@0: { michael@0: // Frame order comparison functions only work properly when the placeholders michael@0: // have been inserted into the frame tree. So for example if we have a new float michael@0: // containing the placeholder for a new abs-pos frame, and we process the abs-pos michael@0: // insertion first, then we won't be able to find the right place to insert in michael@0: // in the abs-pos list. So put floats in first, because they can contain placeholders michael@0: // for abs-pos and fixed-pos items whose containing blocks are outside the floats. michael@0: // Then put abs-pos frames in, because they can contain placeholders for fixed-pos michael@0: // items whose containing block is outside the abs-pos frames. michael@0: MOZ_COUNT_DTOR(nsFrameConstructorState); michael@0: ProcessFrameInsertions(mFloatedItems, nsIFrame::kFloatList); michael@0: ProcessFrameInsertions(mAbsoluteItems, nsIFrame::kAbsoluteList); michael@0: ProcessFrameInsertions(mFixedItems, nsIFrame::kFixedList); michael@0: #ifdef MOZ_XUL michael@0: ProcessFrameInsertions(mPopupItems, nsIFrame::kPopupList); michael@0: #endif michael@0: for (int32_t i = mGeneratedTextNodesWithInitializer.Count() - 1; i >= 0; --i) { michael@0: mGeneratedTextNodesWithInitializer[i]-> michael@0: DeleteProperty(nsGkAtoms::genConInitializerProperty); michael@0: } michael@0: if (!mPendingBindings.isEmpty()) { michael@0: nsBindingManager* bindingManager = mPresShell->GetDocument()->BindingManager(); michael@0: do { michael@0: nsAutoPtr pendingBinding; michael@0: pendingBinding = mPendingBindings.popFirst(); michael@0: bindingManager->AddToAttachedQueue(pendingBinding->mBinding); michael@0: } while (!mPendingBindings.isEmpty()); michael@0: mCurrentPendingBindingInsertionPoint = nullptr; michael@0: } michael@0: } michael@0: michael@0: static nsIFrame* michael@0: AdjustAbsoluteContainingBlock(nsIFrame* aContainingBlockIn) michael@0: { michael@0: if (!aContainingBlockIn) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Always use the container's first continuation. (Inline frames can have michael@0: // non-fluid bidi continuations...) michael@0: return aContainingBlockIn->FirstContinuation(); michael@0: } michael@0: michael@0: void michael@0: nsFrameConstructorState::PushAbsoluteContainingBlock(nsIFrame* aNewAbsoluteContainingBlock, michael@0: nsIFrame* aPositionedFrame, michael@0: nsFrameConstructorSaveState& aSaveState) michael@0: { michael@0: aSaveState.mItems = &mAbsoluteItems; michael@0: aSaveState.mSavedItems = mAbsoluteItems; michael@0: aSaveState.mChildListID = nsIFrame::kAbsoluteList; michael@0: aSaveState.mState = this; michael@0: aSaveState.mSavedFixedPosIsAbsPos = mFixedPosIsAbsPos; michael@0: michael@0: if (mFixedPosIsAbsPos) { michael@0: // Since we're going to replace mAbsoluteItems, we need to save it into michael@0: // mFixedItems now (and save the current value of mFixedItems). michael@0: aSaveState.mSavedFixedItems = mFixedItems; michael@0: mFixedItems = mAbsoluteItems; michael@0: } michael@0: michael@0: mAbsoluteItems = michael@0: nsAbsoluteItems(AdjustAbsoluteContainingBlock(aNewAbsoluteContainingBlock)); michael@0: michael@0: /* See if we're wiring the fixed-pos and abs-pos lists together. This happens iff michael@0: * we're a transformed element. michael@0: */ michael@0: mFixedPosIsAbsPos = aPositionedFrame && michael@0: (aPositionedFrame->StyleDisplay()->HasTransform(aPositionedFrame) || michael@0: aPositionedFrame->StyleDisplay()->HasPerspectiveStyle()); michael@0: michael@0: if (aNewAbsoluteContainingBlock) { michael@0: aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameConstructorState::PushFloatContainingBlock(nsIFrame* aNewFloatContainingBlock, michael@0: nsFrameConstructorSaveState& aSaveState) michael@0: { michael@0: NS_PRECONDITION(!aNewFloatContainingBlock || michael@0: aNewFloatContainingBlock->IsFloatContainingBlock(), michael@0: "Please push a real float containing block!"); michael@0: NS_ASSERTION(!aNewFloatContainingBlock || michael@0: !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock), michael@0: "We should not push a frame that is supposed to _suppress_ " michael@0: "floats as a float containing block!"); michael@0: aSaveState.mItems = &mFloatedItems; michael@0: aSaveState.mSavedItems = mFloatedItems; michael@0: aSaveState.mChildListID = nsIFrame::kFloatList; michael@0: aSaveState.mState = this; michael@0: mFloatedItems = nsAbsoluteItems(aNewFloatContainingBlock); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameConstructorState::GetGeometricParent(const nsStyleDisplay* aStyleDisplay, michael@0: nsIFrame* aContentParentFrame) const michael@0: { michael@0: NS_PRECONDITION(aStyleDisplay, "Must have display struct!"); michael@0: michael@0: // If there is no container for a fixed, absolute, or floating root michael@0: // frame, we will ignore the positioning. This hack is originally michael@0: // brought to you by the letter T: tables, since other roots don't michael@0: // even call into this code. See bug 178855. michael@0: // michael@0: // XXX Disabling positioning in this case is a hack. If one was so inclined, michael@0: // one could support this either by (1) inserting a dummy block between the michael@0: // table and the canvas or (2) teaching the canvas how to reflow positioned michael@0: // elements. (1) has the usual problems when multiple frames share the same michael@0: // content (notice all the special cases in this file dealing with inner michael@0: // tables and outer tables which share the same content). (2) requires some michael@0: // work and possible factoring. michael@0: // michael@0: // XXXbz couldn't we just force position to "static" on roots and michael@0: // float to "none"? That's OK per CSS 2.1, as far as I can tell. michael@0: michael@0: if (aContentParentFrame && aContentParentFrame->IsSVGText()) { michael@0: return aContentParentFrame; michael@0: } michael@0: michael@0: if (aStyleDisplay->IsFloatingStyle() && mFloatedItems.containingBlock) { michael@0: NS_ASSERTION(!aStyleDisplay->IsAbsolutelyPositionedStyle(), michael@0: "Absolutely positioned _and_ floating?"); michael@0: return mFloatedItems.containingBlock; michael@0: } michael@0: michael@0: if (aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE && michael@0: mAbsoluteItems.containingBlock) { michael@0: return mAbsoluteItems.containingBlock; michael@0: } michael@0: michael@0: if (aStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED && michael@0: GetFixedItems().containingBlock) { michael@0: return GetFixedItems().containingBlock; michael@0: } michael@0: michael@0: return aContentParentFrame; michael@0: } michael@0: michael@0: void michael@0: nsFrameConstructorState::AddChild(nsIFrame* aNewFrame, michael@0: nsFrameItems& aFrameItems, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame* aParentFrame, michael@0: bool aCanBePositioned, michael@0: bool aCanBeFloated, michael@0: bool aIsOutOfFlowPopup, michael@0: bool aInsertAfter, michael@0: nsIFrame* aInsertAfterFrame) michael@0: { michael@0: NS_PRECONDITION(!aNewFrame->GetNextSibling(), "Shouldn't happen"); michael@0: michael@0: const nsStyleDisplay* disp = aNewFrame->StyleDisplay(); michael@0: michael@0: // The comments in GetGeometricParent regarding root table frames michael@0: // all apply here, unfortunately. michael@0: michael@0: bool needPlaceholder = false; michael@0: nsFrameState placeholderType; michael@0: nsFrameItems* frameItems = &aFrameItems; michael@0: #ifdef MOZ_XUL michael@0: if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) { michael@0: NS_ASSERTION(aNewFrame->GetParent() == mPopupItems.containingBlock, michael@0: "Popup whose parent is not the popup containing block?"); michael@0: NS_ASSERTION(mPopupItems.containingBlock, "Must have a popup set frame!"); michael@0: needPlaceholder = true; michael@0: frameItems = &mPopupItems; michael@0: placeholderType = PLACEHOLDER_FOR_POPUP; michael@0: } michael@0: else michael@0: #endif // MOZ_XUL michael@0: if (aCanBeFloated && aNewFrame->IsFloating() && michael@0: mFloatedItems.containingBlock) { michael@0: NS_ASSERTION(aNewFrame->GetParent() == mFloatedItems.containingBlock, michael@0: "Float whose parent is not the float containing block?"); michael@0: needPlaceholder = true; michael@0: frameItems = &mFloatedItems; michael@0: placeholderType = PLACEHOLDER_FOR_FLOAT; michael@0: } michael@0: else if (aCanBePositioned) { michael@0: if (disp->mPosition == NS_STYLE_POSITION_ABSOLUTE && michael@0: mAbsoluteItems.containingBlock) { michael@0: NS_ASSERTION(aNewFrame->GetParent() == mAbsoluteItems.containingBlock, michael@0: "Abs pos whose parent is not the abs pos containing block?"); michael@0: needPlaceholder = true; michael@0: frameItems = &mAbsoluteItems; michael@0: placeholderType = PLACEHOLDER_FOR_ABSPOS; michael@0: } michael@0: if (disp->mPosition == NS_STYLE_POSITION_FIXED && michael@0: GetFixedItems().containingBlock) { michael@0: NS_ASSERTION(aNewFrame->GetParent() == GetFixedItems().containingBlock, michael@0: "Fixed pos whose parent is not the fixed pos containing block?"); michael@0: needPlaceholder = true; michael@0: frameItems = &GetFixedItems(); michael@0: placeholderType = PLACEHOLDER_FOR_FIXEDPOS; michael@0: } michael@0: } michael@0: michael@0: if (needPlaceholder) { michael@0: NS_ASSERTION(frameItems != &aFrameItems, michael@0: "Putting frame in-flow _and_ want a placeholder?"); michael@0: nsIFrame* placeholderFrame = michael@0: nsCSSFrameConstructor::CreatePlaceholderFrameFor(mPresShell, michael@0: aContent, michael@0: aNewFrame, michael@0: aStyleContext, michael@0: aParentFrame, michael@0: nullptr, michael@0: placeholderType); michael@0: michael@0: placeholderFrame->AddStateBits(mAdditionalStateBits); michael@0: // Add the placeholder frame to the flow michael@0: aFrameItems.AddChild(placeholderFrame); michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: NS_ASSERTION(aNewFrame->GetParent() == aParentFrame, michael@0: "In-flow frame has wrong parent"); michael@0: } michael@0: #endif michael@0: michael@0: if (aInsertAfter) { michael@0: frameItems->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame); michael@0: } else { michael@0: frameItems->AddChild(aNewFrame); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems, michael@0: ChildListID aChildListID) michael@0: { michael@0: #define NS_NONXUL_LIST_TEST (&aFrameItems == &mFloatedItems && \ michael@0: aChildListID == nsIFrame::kFloatList) || \ michael@0: (&aFrameItems == &mAbsoluteItems && \ michael@0: aChildListID == nsIFrame::kAbsoluteList) || \ michael@0: (&aFrameItems == &mFixedItems && \ michael@0: aChildListID == nsIFrame::kFixedList) michael@0: #ifdef MOZ_XUL michael@0: NS_PRECONDITION(NS_NONXUL_LIST_TEST || michael@0: (&aFrameItems == &mPopupItems && michael@0: aChildListID == nsIFrame::kPopupList), michael@0: "Unexpected aFrameItems/aChildListID combination"); michael@0: #else michael@0: NS_PRECONDITION(NS_NONXUL_LIST_TEST, michael@0: "Unexpected aFrameItems/aChildListID combination"); michael@0: #endif michael@0: michael@0: if (aFrameItems.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: nsIFrame* containingBlock = aFrameItems.containingBlock; michael@0: michael@0: NS_ASSERTION(containingBlock, michael@0: "Child list without containing block?"); michael@0: michael@0: if (aChildListID == nsIFrame::kFixedList) { michael@0: // Put this frame on the transformed-frame's abs-pos list instead, if michael@0: // it has abs-pos children instead of fixed-pos children. michael@0: aChildListID = containingBlock->GetAbsoluteListID(); michael@0: } michael@0: michael@0: // Insert the frames hanging out in aItems. We can use SetInitialChildList() michael@0: // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW michael@0: // is set) and doesn't have any frames in the aChildListID child list yet. michael@0: const nsFrameList& childList = containingBlock->GetChildList(aChildListID); michael@0: DebugOnly rv = NS_OK; michael@0: if (childList.IsEmpty() && michael@0: (containingBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: // If we're injecting absolutely positioned frames, inject them on the michael@0: // absolute containing block michael@0: if (aChildListID == containingBlock->GetAbsoluteListID()) { michael@0: rv = containingBlock->GetAbsoluteContainingBlock()-> michael@0: SetInitialChildList(containingBlock, aChildListID, aFrameItems); michael@0: } else { michael@0: rv = containingBlock->SetInitialChildList(aChildListID, aFrameItems); michael@0: } michael@0: } else { michael@0: // Note that whether the frame construction context is doing an append or michael@0: // not is not helpful here, since it could be appending to some frame in michael@0: // the middle of the document, which means we're not necessarily michael@0: // appending to the children of the containing block. michael@0: // michael@0: // We need to make sure the 'append to the end of document' case is fast. michael@0: // So first test the last child of the containing block michael@0: nsIFrame* lastChild = childList.LastChild(); michael@0: michael@0: // CompareTreePosition uses placeholder hierarchy for out of flow frames, michael@0: // so this will make out-of-flows respect the ordering of placeholders, michael@0: // which is great because it takes care of anonymous content. michael@0: nsIFrame* firstNewFrame = aFrameItems.FirstChild(); michael@0: michael@0: // Cache the ancestor chain so that we can reuse it if needed. michael@0: nsAutoTArray firstNewFrameAncestors; michael@0: nsIFrame* notCommonAncestor = nullptr; michael@0: if (lastChild) { michael@0: notCommonAncestor = nsLayoutUtils::FillAncestors(firstNewFrame, michael@0: containingBlock, michael@0: &firstNewFrameAncestors); michael@0: } michael@0: michael@0: if (!lastChild || michael@0: nsLayoutUtils::CompareTreePosition(lastChild, firstNewFrame, michael@0: firstNewFrameAncestors, michael@0: notCommonAncestor ? michael@0: containingBlock : nullptr) < 0) { michael@0: // no lastChild, or lastChild comes before the new children, so just append michael@0: rv = mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameItems); michael@0: } else { michael@0: // Try the other children. First collect them to an array so that a michael@0: // reasonable fast binary search can be used to find the insertion point. michael@0: nsAutoTArray children; michael@0: for (nsIFrame* f = childList.FirstChild(); f != lastChild; michael@0: f = f->GetNextSibling()) { michael@0: children.AppendElement(f); michael@0: } michael@0: michael@0: nsIFrame* insertionPoint = nullptr; michael@0: int32_t imin = 0; michael@0: int32_t max = children.Length(); michael@0: while (max > imin) { michael@0: int32_t imid = imin + ((max - imin) / 2); michael@0: nsIFrame* f = children[imid]; michael@0: int32_t compare = michael@0: nsLayoutUtils::CompareTreePosition(f, firstNewFrame, firstNewFrameAncestors, michael@0: notCommonAncestor ? containingBlock : nullptr); michael@0: if (compare > 0) { michael@0: // f is after the new frame. michael@0: max = imid; michael@0: insertionPoint = imid > 0 ? children[imid - 1] : nullptr; michael@0: } else if (compare < 0) { michael@0: // f is before the new frame. michael@0: imin = imid + 1; michael@0: insertionPoint = f; michael@0: } else { michael@0: // This is for the old behavior. Should be removed once it is michael@0: // guaranteed that CompareTreePosition can't return 0! michael@0: // See bug 928645. michael@0: NS_WARNING("Something odd happening???"); michael@0: insertionPoint = nullptr; michael@0: for (uint32_t i = 0; i < children.Length(); ++i) { michael@0: nsIFrame* f = children[i]; michael@0: if (nsLayoutUtils::CompareTreePosition(f, firstNewFrame, michael@0: firstNewFrameAncestors, michael@0: notCommonAncestor ? michael@0: containingBlock : nullptr) > 0) { michael@0: break; michael@0: } michael@0: insertionPoint = f; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: rv = mFrameManager->InsertFrames(containingBlock, aChildListID, michael@0: insertionPoint, aFrameItems); michael@0: } michael@0: } michael@0: michael@0: NS_POSTCONDITION(aFrameItems.IsEmpty(), "How did that happen?"); michael@0: michael@0: // XXXbz And if NS_FAILED(rv), what? I guess we need to clean up the list michael@0: // and deal with all the placeholders... but what if the placeholders aren't michael@0: // in the document yet? Could that happen? michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Frames getting lost!"); michael@0: } michael@0: michael@0: michael@0: nsFrameConstructorSaveState::nsFrameConstructorSaveState() michael@0: : mItems(nullptr), michael@0: mSavedItems(nullptr), michael@0: mChildListID(kPrincipalList), michael@0: mState(nullptr), michael@0: mSavedFixedItems(nullptr), michael@0: mSavedFixedPosIsAbsPos(false) michael@0: { michael@0: } michael@0: michael@0: nsFrameConstructorSaveState::~nsFrameConstructorSaveState() michael@0: { michael@0: // Restore the state michael@0: if (mItems) { michael@0: NS_ASSERTION(mState, "Can't have mItems set without having a state!"); michael@0: mState->ProcessFrameInsertions(*mItems, mChildListID); michael@0: *mItems = mSavedItems; michael@0: #ifdef DEBUG michael@0: // We've transferred the child list, so drop the pointer we held to it. michael@0: // Note that this only matters for the assert in ~nsAbsoluteItems. michael@0: mSavedItems.Clear(); michael@0: #endif michael@0: if (mItems == &mState->mAbsoluteItems) { michael@0: mState->mFixedPosIsAbsPos = mSavedFixedPosIsAbsPos; michael@0: if (mSavedFixedPosIsAbsPos) { michael@0: // mAbsoluteItems was moved to mFixedItems, so move mFixedItems back michael@0: // and repair the old mFixedItems now. michael@0: mState->mAbsoluteItems = mState->mFixedItems; michael@0: mState->mFixedItems = mSavedFixedItems; michael@0: #ifdef DEBUG michael@0: mSavedFixedItems.Clear(); michael@0: #endif michael@0: } michael@0: } michael@0: NS_ASSERTION(!mItems->LastChild() || !mItems->LastChild()->GetNextSibling(), michael@0: "Something corrupted our list"); michael@0: } michael@0: } michael@0: michael@0: static michael@0: bool IsBorderCollapse(nsIFrame* aFrame) michael@0: { michael@0: for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) { michael@0: if (nsGkAtoms::tableFrame == frame->GetType()) { michael@0: return ((nsTableFrame*)frame)->IsBorderCollapse(); michael@0: } michael@0: } michael@0: NS_ASSERTION(false, "program error"); michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Moves aFrameList from aOldParent to aNewParent. This updates the parent michael@0: * pointer of the frames in the list, and reparents their views as needed. michael@0: * nsFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its michael@0: * ancestors as needed. Then it sets the list as the initial child list michael@0: * on aNewParent, unless aNewParent either already has kids or has been michael@0: * reflowed; in that case it appends the new frames. Note that this michael@0: * method differs from ReparentFrames in that it doesn't change the kids' michael@0: * style contexts. michael@0: */ michael@0: // XXXbz Since this is only used for {ib} splits, could we just copy the view michael@0: // bits from aOldParent to aNewParent and then use the michael@0: // nsFrameList::ApplySetParent? That would still leave us doing two passes michael@0: // over the list, of course; if we really wanted to we could factor out the michael@0: // relevant part of ReparentFrameViewList, I suppose... Or just get rid of michael@0: // views, which would make most of this function go away. michael@0: static void michael@0: MoveChildrenTo(nsPresContext* aPresContext, michael@0: nsIFrame* aOldParent, michael@0: nsIFrame* aNewParent, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: bool sameGrandParent = aOldParent->GetParent() == aNewParent->GetParent(); michael@0: michael@0: if (aNewParent->HasView() || aOldParent->HasView() || !sameGrandParent) { michael@0: // Move the frames into the new view michael@0: nsContainerFrame::ReparentFrameViewList(aFrameList, aOldParent, aNewParent); michael@0: } michael@0: michael@0: for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { michael@0: e.get()->SetParent(aNewParent); michael@0: } michael@0: michael@0: if (aNewParent->PrincipalChildList().IsEmpty() && michael@0: (aNewParent->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: aNewParent->SetInitialChildList(kPrincipalList, aFrameList); michael@0: } else { michael@0: aNewParent->AppendFrames(kPrincipalList, aFrameList); michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument, michael@0: nsIPresShell *aPresShell, michael@0: nsStyleSet* aStyleSet) michael@0: : nsFrameManager(aPresShell, aStyleSet) michael@0: , mDocument(aDocument) michael@0: , mRootElementFrame(nullptr) michael@0: , mRootElementStyleFrame(nullptr) michael@0: , mFixedContainingBlock(nullptr) michael@0: , mDocElementContainingBlock(nullptr) michael@0: , mGfxScrollFrame(nullptr) michael@0: , mPageSequenceFrame(nullptr) michael@0: , mCurrentDepth(0) michael@0: , mUpdateCount(0) michael@0: , mQuotesDirty(false) michael@0: , mCountersDirty(false) michael@0: , mIsDestroyingFrameTree(false) michael@0: , mHasRootAbsPosContainingBlock(false) michael@0: , mAlwaysCreateFramesForIgnorableWhitespace(false) michael@0: { michael@0: #ifdef DEBUG michael@0: static bool gFirstTime = true; michael@0: if (gFirstTime) { michael@0: gFirstTime = false; michael@0: char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS"); michael@0: if (flags) { michael@0: bool error = false; michael@0: for (;;) { michael@0: char* comma = PL_strchr(flags, ','); michael@0: if (comma) michael@0: *comma = '\0'; michael@0: michael@0: bool found = false; michael@0: FrameCtorDebugFlags* flag = gFlags; michael@0: FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS; michael@0: while (flag < limit) { michael@0: if (PL_strcasecmp(flag->name, flags) == 0) { michael@0: *(flag->on) = true; michael@0: printf("nsCSSFrameConstructor: setting %s debug flag on\n", flag->name); michael@0: found = true; michael@0: break; michael@0: } michael@0: ++flag; michael@0: } michael@0: michael@0: if (! found) michael@0: error = true; michael@0: michael@0: if (! comma) michael@0: break; michael@0: michael@0: *comma = ','; michael@0: flags = comma + 1; michael@0: } michael@0: michael@0: if (error) { michael@0: printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n"); michael@0: FrameCtorDebugFlags* flag = gFlags; michael@0: FrameCtorDebugFlags* limit = gFlags + NUM_DEBUG_FLAGS; michael@0: while (flag < limit) { michael@0: printf(" %s\n", flag->name); michael@0: ++flag; michael@0: } michael@0: printf("Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of flag\n"); michael@0: printf("names (no whitespace)\n"); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) michael@0: { michael@0: NS_PRECONDITION(mUpdateCount != 0, michael@0: "Should be in an update while destroying frames"); michael@0: michael@0: if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) { michael@0: if (mQuoteList.DestroyNodesFor(aFrame)) michael@0: QuotesDirty(); michael@0: } michael@0: michael@0: if (mCounterManager.DestroyNodesFor(aFrame)) { michael@0: // Technically we don't need to update anything if we destroyed only michael@0: // USE nodes. However, this is unlikely to happen in the real world michael@0: // since USE nodes generally go along with INCREMENT nodes. michael@0: CountersDirty(); michael@0: } michael@0: michael@0: RestyleManager()->NotifyDestroyingFrame(aFrame); michael@0: michael@0: nsFrameManager::NotifyDestroyingFrame(aFrame); michael@0: } michael@0: michael@0: struct nsGenConInitializer { michael@0: nsAutoPtr mNode; michael@0: nsGenConList* mList; michael@0: void (nsCSSFrameConstructor::*mDirtyAll)(); michael@0: michael@0: nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList, michael@0: void (nsCSSFrameConstructor::*aDirtyAll)()) michael@0: : mNode(aNode), mList(aList), mDirtyAll(aDirtyAll) {} michael@0: }; michael@0: michael@0: already_AddRefed michael@0: nsCSSFrameConstructor::CreateGenConTextNode(nsFrameConstructorState& aState, michael@0: const nsString& aString, michael@0: nsCOMPtr* aText, michael@0: nsGenConInitializer* aInitializer) michael@0: { michael@0: nsRefPtr content = new nsTextNode(mDocument->NodeInfoManager()); michael@0: content->SetText(aString, false); michael@0: if (aText) { michael@0: *aText = content; michael@0: } michael@0: if (aInitializer) { michael@0: content->SetProperty(nsGkAtoms::genConInitializerProperty, aInitializer, michael@0: nsINode::DeleteProperty); michael@0: aState.mGeneratedTextNodesWithInitializer.AppendObject(content); michael@0: } michael@0: return content.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsCSSFrameConstructor::CreateGeneratedContent(nsFrameConstructorState& aState, michael@0: nsIContent* aParentContent, michael@0: nsStyleContext* aStyleContext, michael@0: uint32_t aContentIndex) michael@0: { michael@0: // Get the content value michael@0: const nsStyleContentData &data = michael@0: aStyleContext->StyleContent()->ContentAt(aContentIndex); michael@0: nsStyleContentType type = data.mType; michael@0: michael@0: if (eStyleContentType_Image == type) { michael@0: if (!data.mContent.mImage) { michael@0: // CSS had something specified that couldn't be converted to an michael@0: // image object michael@0: return nullptr; michael@0: } michael@0: michael@0: // Create an image content object and pass it the image request. michael@0: // XXX Check if it's an image type we can handle... michael@0: michael@0: nsCOMPtr nodeInfo; michael@0: nodeInfo = mDocument->NodeInfoManager()-> michael@0: GetNodeInfo(nsGkAtoms::mozgeneratedcontentimage, nullptr, michael@0: kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE); michael@0: michael@0: nsCOMPtr content; michael@0: NS_NewGenConImageContent(getter_AddRefs(content), nodeInfo.forget(), michael@0: data.mContent.mImage); michael@0: return content.forget(); michael@0: } michael@0: michael@0: switch (type) { michael@0: case eStyleContentType_String: michael@0: return CreateGenConTextNode(aState, michael@0: nsDependentString(data.mContent.mString), michael@0: nullptr, nullptr); michael@0: michael@0: case eStyleContentType_Attr: michael@0: { michael@0: nsCOMPtr attrName; michael@0: int32_t attrNameSpace = kNameSpaceID_None; michael@0: nsAutoString contentString(data.mContent.mString); michael@0: michael@0: int32_t barIndex = contentString.FindChar('|'); // CSS namespace delimiter michael@0: if (-1 != barIndex) { michael@0: nsAutoString nameSpaceVal; michael@0: contentString.Left(nameSpaceVal, barIndex); michael@0: nsresult error; michael@0: attrNameSpace = nameSpaceVal.ToInteger(&error); michael@0: contentString.Cut(0, barIndex + 1); michael@0: if (contentString.Length()) { michael@0: if (mDocument->IsHTML() && aParentContent->IsHTML()) { michael@0: ToLowerCase(contentString); michael@0: } michael@0: attrName = do_GetAtom(contentString); michael@0: } michael@0: } michael@0: else { michael@0: if (mDocument->IsHTML() && aParentContent->IsHTML()) { michael@0: ToLowerCase(contentString); michael@0: } michael@0: attrName = do_GetAtom(contentString); michael@0: } michael@0: michael@0: if (!attrName) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr content; michael@0: NS_NewAttributeContent(mDocument->NodeInfoManager(), michael@0: attrNameSpace, attrName, getter_AddRefs(content)); michael@0: return content.forget(); michael@0: } michael@0: michael@0: case eStyleContentType_Counter: michael@0: case eStyleContentType_Counters: michael@0: { michael@0: nsCSSValue::Array* counters = data.mContent.mCounters; michael@0: nsCounterList* counterList = mCounterManager.CounterListFor( michael@0: nsDependentString(counters->Item(0).GetStringBufferValue())); michael@0: if (!counterList) michael@0: return nullptr; michael@0: michael@0: nsCounterUseNode* node = michael@0: new nsCounterUseNode(counters, aContentIndex, michael@0: type == eStyleContentType_Counters); michael@0: michael@0: nsGenConInitializer* initializer = michael@0: new nsGenConInitializer(node, counterList, michael@0: &nsCSSFrameConstructor::CountersDirty); michael@0: return CreateGenConTextNode(aState, EmptyString(), &node->mText, michael@0: initializer); michael@0: } michael@0: michael@0: case eStyleContentType_Image: michael@0: NS_NOTREACHED("handled by if above"); michael@0: return nullptr; michael@0: michael@0: case eStyleContentType_OpenQuote: michael@0: case eStyleContentType_CloseQuote: michael@0: case eStyleContentType_NoOpenQuote: michael@0: case eStyleContentType_NoCloseQuote: michael@0: { michael@0: nsQuoteNode* node = michael@0: new nsQuoteNode(type, aContentIndex); michael@0: michael@0: nsGenConInitializer* initializer = michael@0: new nsGenConInitializer(node, &mQuoteList, michael@0: &nsCSSFrameConstructor::QuotesDirty); michael@0: return CreateGenConTextNode(aState, EmptyString(), &node->mText, michael@0: initializer); michael@0: } michael@0: michael@0: case eStyleContentType_AltContent: michael@0: { michael@0: // Use the "alt" attribute; if that fails and the node is an HTML michael@0: // , try the value attribute and then fall back to some default michael@0: // localized text we have. michael@0: // XXX what if the 'alt' attribute is added later, how will we michael@0: // detect that and do the right thing here? michael@0: if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) { michael@0: nsCOMPtr content; michael@0: NS_NewAttributeContent(mDocument->NodeInfoManager(), michael@0: kNameSpaceID_None, nsGkAtoms::alt, getter_AddRefs(content)); michael@0: return content.forget(); michael@0: } michael@0: michael@0: if (aParentContent->IsHTML() && michael@0: aParentContent->NodeInfo()->Equals(nsGkAtoms::input)) { michael@0: if (aParentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) { michael@0: nsCOMPtr content; michael@0: NS_NewAttributeContent(mDocument->NodeInfoManager(), michael@0: kNameSpaceID_None, nsGkAtoms::value, getter_AddRefs(content)); michael@0: return content.forget(); michael@0: } michael@0: michael@0: nsXPIDLString temp; michael@0: nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, michael@0: "Submit", temp); michael@0: return CreateGenConTextNode(aState, temp, nullptr, nullptr); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: michael@0: case eStyleContentType_Uninitialized: michael@0: NS_NOTREACHED("uninitialized content type"); michael@0: return nullptr; michael@0: } // switch michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * aParentFrame - the frame that should be the parent of the generated michael@0: * content. This is the frame for the corresponding content node, michael@0: * which must not be a leaf frame. michael@0: * michael@0: * Any items created are added to aItems. michael@0: * michael@0: * We create an XML element (tag _moz_generated_content_before or michael@0: * _moz_generated_content_after) representing the pseudoelement. We michael@0: * create a DOM node for each 'content' item and make those nodes the michael@0: * children of the XML element. Then we create a frame subtree for michael@0: * the XML element as if it were a regular child of michael@0: * aParentFrame/aParentContent, giving the XML element the ::before or michael@0: * ::after style. michael@0: */ michael@0: void michael@0: nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aState, michael@0: nsIFrame* aParentFrame, michael@0: nsIContent* aParentContent, michael@0: nsStyleContext* aStyleContext, michael@0: nsCSSPseudoElements::Type aPseudoElement, michael@0: FrameConstructionItemList& aItems) michael@0: { michael@0: // XXXbz is this ever true? michael@0: if (!aParentContent->IsElement()) { michael@0: NS_ERROR("Bogus generated content parent"); michael@0: return; michael@0: } michael@0: michael@0: nsStyleSet *styleSet = mPresShell->StyleSet(); michael@0: michael@0: // Probe for the existence of the pseudo-element michael@0: nsRefPtr pseudoStyleContext; michael@0: pseudoStyleContext = michael@0: styleSet->ProbePseudoElementStyle(aParentContent->AsElement(), michael@0: aPseudoElement, michael@0: aStyleContext, michael@0: aState.mTreeMatchContext); michael@0: if (!pseudoStyleContext) michael@0: return; michael@0: // |ProbePseudoStyleFor| checked the 'display' property and the michael@0: // |ContentCount()| of the 'content' property for us. michael@0: nsCOMPtr nodeInfo; michael@0: nsIAtom* elemName = aPseudoElement == nsCSSPseudoElements::ePseudo_before ? michael@0: nsGkAtoms::mozgeneratedcontentbefore : nsGkAtoms::mozgeneratedcontentafter; michael@0: nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(elemName, nullptr, michael@0: kNameSpaceID_None, michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: nsCOMPtr container; michael@0: nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget()); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: container->SetIsNativeAnonymousRoot(); michael@0: michael@0: rv = container->BindToTree(mDocument, aParentContent, aParentContent, true); michael@0: if (NS_FAILED(rv)) { michael@0: container->UnbindFromTree(); michael@0: return; michael@0: } michael@0: michael@0: uint32_t contentCount = pseudoStyleContext->StyleContent()->ContentCount(); michael@0: for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) { michael@0: nsCOMPtr content = michael@0: CreateGeneratedContent(aState, aParentContent, pseudoStyleContext, michael@0: contentIndex); michael@0: if (content) { michael@0: container->AppendChildTo(content, false); michael@0: } michael@0: } michael@0: michael@0: AddFrameConstructionItemsInternal(aState, container, aParentFrame, elemName, michael@0: kNameSpaceID_None, true, michael@0: pseudoStyleContext, michael@0: ITEM_IS_GENERATED_CONTENT, nullptr, michael@0: aItems); michael@0: } michael@0: michael@0: /**************************************************** michael@0: ** BEGIN TABLE SECTION michael@0: ****************************************************/ michael@0: michael@0: // The term pseudo frame is being used instead of anonymous frame, since anonymous michael@0: // frame has been used elsewhere to refer to frames that have generated content michael@0: michael@0: // Return whether the given frame is a table pseudo-frame. Note that michael@0: // cell-content and table-outer frames have pseudo-types, but are always michael@0: // created, even for non-anonymous cells and tables respectively. So for those michael@0: // we have to examine the cell or table frame to see whether it's a pseudo michael@0: // frame. In particular, a lone table caption will have an outer table as its michael@0: // parent, but will also trigger construction of an empty inner table, which michael@0: // will be the one we can examine to see whether the outer was a pseudo-frame. michael@0: static bool michael@0: IsTablePseudo(nsIFrame* aFrame) michael@0: { michael@0: nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo(); michael@0: return pseudoType && michael@0: (pseudoType == nsCSSAnonBoxes::table || michael@0: pseudoType == nsCSSAnonBoxes::inlineTable || michael@0: pseudoType == nsCSSAnonBoxes::tableColGroup || michael@0: pseudoType == nsCSSAnonBoxes::tableRowGroup || michael@0: pseudoType == nsCSSAnonBoxes::tableRow || michael@0: pseudoType == nsCSSAnonBoxes::tableCell || michael@0: (pseudoType == nsCSSAnonBoxes::cellContent && michael@0: aFrame->GetParent()->StyleContext()->GetPseudo() == michael@0: nsCSSAnonBoxes::tableCell) || michael@0: (pseudoType == nsCSSAnonBoxes::tableOuter && michael@0: (aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo() == michael@0: nsCSSAnonBoxes::table || michael@0: aFrame->GetFirstPrincipalChild()->StyleContext()->GetPseudo() == michael@0: nsCSSAnonBoxes::inlineTable))); michael@0: } michael@0: michael@0: /* static */ michael@0: nsCSSFrameConstructor::ParentType michael@0: nsCSSFrameConstructor::GetParentType(nsIAtom* aFrameType) michael@0: { michael@0: if (aFrameType == nsGkAtoms::tableFrame) { michael@0: return eTypeTable; michael@0: } michael@0: if (aFrameType == nsGkAtoms::tableRowGroupFrame) { michael@0: return eTypeRowGroup; michael@0: } michael@0: if (aFrameType == nsGkAtoms::tableRowFrame) { michael@0: return eTypeRow; michael@0: } michael@0: if (aFrameType == nsGkAtoms::tableColGroupFrame) { michael@0: return eTypeColGroup; michael@0: } michael@0: michael@0: return eTypeBlock; michael@0: } michael@0: michael@0: static nsIFrame* michael@0: AdjustCaptionParentFrame(nsIFrame* aParentFrame) michael@0: { michael@0: if (nsGkAtoms::tableFrame == aParentFrame->GetType()) { michael@0: return aParentFrame->GetParent();; michael@0: } michael@0: return aParentFrame; michael@0: } michael@0: michael@0: /** michael@0: * If the parent frame is a |tableFrame| and the child is a michael@0: * |captionFrame|, then we want to insert the frames beneath the michael@0: * |tableFrame|'s parent frame. Returns |true| if the parent frame michael@0: * needed to be fixed up. michael@0: */ michael@0: static bool michael@0: GetCaptionAdjustedParent(nsIFrame* aParentFrame, michael@0: const nsIFrame* aChildFrame, michael@0: nsIFrame** aAdjParentFrame) michael@0: { michael@0: *aAdjParentFrame = aParentFrame; michael@0: bool haveCaption = false; michael@0: michael@0: if (nsGkAtoms::tableCaptionFrame == aChildFrame->GetType()) { michael@0: haveCaption = true; michael@0: *aAdjParentFrame = AdjustCaptionParentFrame(aParentFrame); michael@0: } michael@0: return haveCaption; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::AdjustParentFrame(nsIFrame* & aParentFrame, michael@0: const FrameConstructionData* aFCData, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: NS_PRECONDITION(aStyleContext, "Must have child's style context"); michael@0: NS_PRECONDITION(aFCData, "Must have frame construction data"); michael@0: michael@0: bool tablePart = ((aFCData->mBits & FCDATA_IS_TABLE_PART) != 0); michael@0: michael@0: if (tablePart && aStyleContext->StyleDisplay()->mDisplay == michael@0: NS_STYLE_DISPLAY_TABLE_CAPTION) { michael@0: aParentFrame = AdjustCaptionParentFrame(aParentFrame); michael@0: } michael@0: } michael@0: michael@0: // Pull all the captions present in aItems out into aCaptions michael@0: static void michael@0: PullOutCaptionFrames(nsFrameItems& aItems, nsFrameItems& aCaptions) michael@0: { michael@0: nsIFrame *child = aItems.FirstChild(); michael@0: while (child) { michael@0: nsIFrame *nextSibling = child->GetNextSibling(); michael@0: if (nsGkAtoms::tableCaptionFrame == child->GetType()) { michael@0: aItems.RemoveFrame(child); michael@0: aCaptions.AddChild(child); michael@0: } michael@0: child = nextSibling; michael@0: } michael@0: } michael@0: michael@0: michael@0: // Construct the outer, inner table frames and the children frames for the table. michael@0: // XXX Page break frames for pseudo table frames are not constructed to avoid the risk michael@0: // associated with revising the pseudo frame mechanism. The long term solution michael@0: // of having frames handle page-break-before/after will solve the problem. michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_TABLE, michael@0: "Unexpected call"); michael@0: michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: const uint32_t nameSpaceID = aItem.mNameSpaceID; michael@0: michael@0: // create the pseudo SC for the outer table as a child of the inner SC michael@0: nsRefPtr outerStyleContext; michael@0: outerStyleContext = mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableOuter, styleContext); michael@0: michael@0: // Create the outer table frame which holds the caption and inner table frame michael@0: nsIFrame* newFrame; michael@0: if (kNameSpaceID_MathML == nameSpaceID) michael@0: newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerStyleContext); michael@0: else michael@0: newFrame = NS_NewTableOuterFrame(mPresShell, outerStyleContext); michael@0: michael@0: nsIFrame* geometricParent = michael@0: aState.GetGeometricParent(outerStyleContext->StyleDisplay(), michael@0: aParentFrame); michael@0: michael@0: // Init the table outer frame michael@0: InitAndRestoreFrame(aState, content, geometricParent, newFrame); michael@0: michael@0: // Create the inner table frame michael@0: nsIFrame* innerFrame; michael@0: if (kNameSpaceID_MathML == nameSpaceID) michael@0: innerFrame = NS_NewMathMLmtableFrame(mPresShell, styleContext); michael@0: else michael@0: innerFrame = NS_NewTableFrame(mPresShell, styleContext); michael@0: michael@0: InitAndRestoreFrame(aState, content, newFrame, innerFrame); michael@0: michael@0: // Put the newly created frames into the right child list michael@0: SetInitialSingleChild(newFrame, innerFrame); michael@0: michael@0: aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame); michael@0: michael@0: if (!mRootElementFrame) { michael@0: // The frame we're constructing will be the root element frame. michael@0: // Set mRootElementFrame before processing children. michael@0: mRootElementFrame = newFrame; michael@0: } michael@0: michael@0: nsFrameItems childItems; michael@0: michael@0: // Process children michael@0: nsFrameConstructorSaveState absoluteSaveState; michael@0: const nsStyleDisplay* display = outerStyleContext->StyleDisplay(); michael@0: michael@0: // Mark the table frame as an absolute container if needed michael@0: newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: if (display->IsPositioned(newFrame)) { michael@0: aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState); michael@0: } michael@0: NS_ASSERTION(aItem.mAnonChildren.IsEmpty(), michael@0: "nsIAnonymousContentCreator::CreateAnonymousContent " michael@0: "implementations for table frames are not currently expected " michael@0: "to output a list where the items have their own children"); michael@0: if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) { michael@0: ConstructFramesFromItemList(aState, aItem.mChildItems, michael@0: innerFrame, childItems); michael@0: } else { michael@0: ProcessChildren(aState, content, styleContext, innerFrame, michael@0: true, childItems, false, aItem.mPendingBinding); michael@0: } michael@0: michael@0: nsFrameItems captionItems; michael@0: PullOutCaptionFrames(childItems, captionItems); michael@0: michael@0: // Set the inner table frame's initial primary list michael@0: innerFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: michael@0: // Set the outer table frame's secondary childlist lists michael@0: if (captionItems.NotEmpty()) { michael@0: newFrame->SetInitialChildList(nsIFrame::kCaptionList, captionItems); michael@0: } michael@0: michael@0: return newFrame; michael@0: } michael@0: michael@0: static void michael@0: MakeTablePartAbsoluteContainingBlockIfNeeded(nsFrameConstructorState& aState, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameConstructorSaveState& aAbsSaveState, michael@0: nsIFrame* aFrame) michael@0: { michael@0: // If we're positioned, then we need to become an absolute containing block michael@0: // for any absolutely positioned children and register for post-reflow fixup. michael@0: // michael@0: // Note that usually if a frame type can be an absolute containing block, we michael@0: // always set NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN, whether it actually is or not. michael@0: // However, in this case flag serves the additional purpose of indicating that michael@0: // the frame was registered with its table frame. This allows us to avoid the michael@0: // overhead of unregistering the frame in most cases. michael@0: if (aDisplay->IsPositioned(aFrame)) { michael@0: aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState); michael@0: nsTableFrame::RegisterPositionedTablePart(aFrame); michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructTableRowOrRowGroup(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW_GROUP || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_HEADER_GROUP, michael@0: "Unexpected call"); michael@0: MOZ_ASSERT(aItem.mStyleContext->StyleDisplay() == aDisplay, michael@0: "Display style doesn't match style context"); michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: const uint32_t nameSpaceID = aItem.mNameSpaceID; michael@0: michael@0: nsIFrame* newFrame; michael@0: if (aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW) { michael@0: if (kNameSpaceID_MathML == nameSpaceID) michael@0: newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext); michael@0: else michael@0: newFrame = NS_NewTableRowFrame(mPresShell, styleContext); michael@0: } else { michael@0: newFrame = NS_NewTableRowGroupFrame(mPresShell, styleContext); michael@0: } michael@0: michael@0: InitAndRestoreFrame(aState, content, aParentFrame, newFrame); michael@0: michael@0: nsFrameConstructorSaveState absoluteSaveState; michael@0: MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay, michael@0: absoluteSaveState, michael@0: newFrame); michael@0: michael@0: nsFrameItems childItems; michael@0: NS_ASSERTION(aItem.mAnonChildren.IsEmpty(), michael@0: "nsIAnonymousContentCreator::CreateAnonymousContent " michael@0: "implementations for table frames are not currently expected " michael@0: "to output a list where the items have their own children"); michael@0: if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) { michael@0: ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame, michael@0: childItems); michael@0: } else { michael@0: ProcessChildren(aState, content, styleContext, newFrame, michael@0: true, childItems, false, aItem.mPendingBinding); michael@0: } michael@0: michael@0: newFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: aFrameItems.AddChild(newFrame); michael@0: return newFrame; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructTableCol(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: michael@0: nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, styleContext); michael@0: InitAndRestoreFrame(aState, content, aParentFrame, colFrame); michael@0: michael@0: NS_ASSERTION(colFrame->StyleContext() == styleContext, michael@0: "Unexpected style context"); michael@0: michael@0: aFrameItems.AddChild(colFrame); michael@0: michael@0: // construct additional col frames if the col frame has a span > 1 michael@0: int32_t span = colFrame->GetSpan(); michael@0: for (int32_t spanX = 1; spanX < span; spanX++) { michael@0: nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, styleContext); michael@0: InitAndRestoreFrame(aState, content, aParentFrame, newCol, false); michael@0: aFrameItems.LastChild()->SetNextContinuation(newCol); michael@0: newCol->SetPrevContinuation(aFrameItems.LastChild()); michael@0: aFrameItems.AddChild(newCol); michael@0: newCol->SetColType(eColAnonymousCol); michael@0: } michael@0: michael@0: return colFrame; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructTableCell(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL, michael@0: "Unexpected call"); michael@0: michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: const uint32_t nameSpaceID = aItem.mNameSpaceID; michael@0: michael@0: bool borderCollapse = IsBorderCollapse(aParentFrame); michael@0: nsIFrame* newFrame; michael@0: // is border separate in mathml.css and the MathML code doesn't implement michael@0: // border collapse. For those users who style with border collapse, michael@0: // give them the default non-MathML table frames that understand border collapse. michael@0: // This won't break us because MathML table frames are all subclasses of the default michael@0: // table code, and so we can freely mix with or , or . michael@0: // What will happen is just that non-MathML frames won't understand MathML attributes michael@0: // and will therefore miss the special handling that the MathML code does. michael@0: if (kNameSpaceID_MathML == nameSpaceID && !borderCollapse) michael@0: newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext); michael@0: else michael@0: // Warning: If you change this and add a wrapper frame around table cell michael@0: // frames, make sure Bug 368554 doesn't regress! michael@0: // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp. michael@0: newFrame = NS_NewTableCellFrame(mPresShell, styleContext, borderCollapse); michael@0: michael@0: // Initialize the table cell frame michael@0: InitAndRestoreFrame(aState, content, aParentFrame, newFrame); michael@0: michael@0: // Resolve pseudo style and initialize the body cell frame michael@0: nsRefPtr innerPseudoStyle; michael@0: innerPseudoStyle = mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(nsCSSAnonBoxes::cellContent, styleContext); michael@0: michael@0: // Create a block frame that will format the cell's content michael@0: bool isBlock; michael@0: nsIFrame* cellInnerFrame; michael@0: if (kNameSpaceID_MathML == nameSpaceID) { michael@0: cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle); michael@0: isBlock = false; michael@0: } else { michael@0: cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle); michael@0: isBlock = true; michael@0: } michael@0: michael@0: InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame); michael@0: michael@0: nsFrameConstructorSaveState absoluteSaveState; michael@0: MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay, michael@0: absoluteSaveState, michael@0: newFrame); michael@0: michael@0: nsFrameItems childItems; michael@0: NS_ASSERTION(aItem.mAnonChildren.IsEmpty(), michael@0: "nsIAnonymousContentCreator::CreateAnonymousContent " michael@0: "implementations for table frames are not currently expected " michael@0: "to output a list where the items have their own children"); michael@0: if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) { michael@0: // Need to push ourselves as a float containing block. michael@0: // XXXbz it might be nice to work on getting the parent michael@0: // FrameConstructionItem down into ProcessChildren and just making use of michael@0: // the push there, but that's a bit of work. michael@0: nsFrameConstructorSaveState floatSaveState; michael@0: if (!isBlock) { /* MathML case */ michael@0: aState.PushFloatContainingBlock(nullptr, floatSaveState); michael@0: } else { michael@0: aState.PushFloatContainingBlock(cellInnerFrame, floatSaveState); michael@0: } michael@0: michael@0: ConstructFramesFromItemList(aState, aItem.mChildItems, cellInnerFrame, michael@0: childItems); michael@0: } else { michael@0: // Process the child content michael@0: ProcessChildren(aState, content, styleContext, cellInnerFrame, michael@0: true, childItems, isBlock, aItem.mPendingBinding); michael@0: } michael@0: michael@0: cellInnerFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: SetInitialSingleChild(newFrame, cellInnerFrame); michael@0: aFrameItems.AddChild(newFrame); michael@0: return newFrame; michael@0: } michael@0: michael@0: static inline bool michael@0: NeedFrameFor(const nsFrameConstructorState& aState, michael@0: nsIFrame* aParentFrame, michael@0: nsIContent* aChildContent) michael@0: { michael@0: // XXX the GetContent() != aChildContent check is needed due to bug 135040. michael@0: // Remove it once that's fixed. michael@0: NS_PRECONDITION(!aChildContent->GetPrimaryFrame() || michael@0: aState.mCreatingExtraFrames || michael@0: aChildContent->GetPrimaryFrame()->GetContent() != aChildContent, michael@0: "Why did we get called?"); michael@0: michael@0: // don't create a whitespace frame if aParentFrame doesn't want it. michael@0: // always create frames for children in generated content. counter(), michael@0: // quotes, and attr() content can easily change dynamically and we don't michael@0: // want to be reconstructing frames. It's not even clear that these michael@0: // should be considered ignorable just because they evaluate to michael@0: // whitespace. michael@0: michael@0: // We could handle all this in CreateNeededTablePseudos or some other place michael@0: // after we build our frame construction items, but that would involve michael@0: // creating frame construction items for whitespace kids of michael@0: // eExcludesIgnorableWhitespace frames, where we know we'll be dropping them michael@0: // all anyway, and involve an extra walk down the frame construction item michael@0: // list. michael@0: if (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) || michael@0: aParentFrame->IsGeneratedContentFrame() || michael@0: !aChildContent->IsNodeOfType(nsINode::eTEXT)) { michael@0: return true; michael@0: } michael@0: michael@0: aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | michael@0: NS_REFRAME_IF_WHITESPACE); michael@0: return !aChildContent->TextIsOnlyWhitespace(); michael@0: } michael@0: michael@0: /*********************************************** michael@0: * END TABLE SECTION michael@0: ***********************************************/ michael@0: michael@0: static bool CheckOverflow(nsPresContext* aPresContext, michael@0: const nsStyleDisplay* aDisplay) michael@0: { michael@0: if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) michael@0: return false; michael@0: michael@0: if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP) michael@0: aPresContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_HIDDEN, michael@0: NS_STYLE_OVERFLOW_HIDDEN); michael@0: else michael@0: aPresContext->SetViewportOverflowOverride(aDisplay->mOverflowX, michael@0: aDisplay->mOverflowY); michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * This checks the root element and the HTML BODY, if any, for an "overflow" property michael@0: * that should be applied to the viewport. If one is found then we return the michael@0: * element that we took the overflow from (which should then be treated as michael@0: * "overflow:visible"), and we store the overflow style in the prescontext. michael@0: * @return if scroll was propagated from some content node, the content node it michael@0: * was propagated from. michael@0: */ michael@0: nsIContent* michael@0: nsCSSFrameConstructor::PropagateScrollToViewport() michael@0: { michael@0: // Set default michael@0: nsPresContext* presContext = mPresShell->GetPresContext(); michael@0: presContext->SetViewportOverflowOverride(NS_STYLE_OVERFLOW_AUTO, michael@0: NS_STYLE_OVERFLOW_AUTO); michael@0: michael@0: // We never mess with the viewport scroll state michael@0: // when printing or in print preview michael@0: if (presContext->IsPaginated()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: Element* docElement = mDocument->GetRootElement(); michael@0: michael@0: // Check the style on the document root element michael@0: nsStyleSet *styleSet = mPresShell->StyleSet(); michael@0: nsRefPtr rootStyle; michael@0: rootStyle = styleSet->ResolveStyleFor(docElement, nullptr); michael@0: if (CheckOverflow(presContext, rootStyle->StyleDisplay())) { michael@0: // tell caller we stole the overflow style from the root element michael@0: return docElement; michael@0: } michael@0: michael@0: // Don't look in the BODY for non-HTML documents or HTML documents michael@0: // with non-HTML roots michael@0: // XXX this should be earlier; we shouldn't even look at the document root michael@0: // for non-HTML documents. Fix this once we support explicit CSS styling michael@0: // of the viewport michael@0: // XXX what about XHTML? michael@0: nsCOMPtr htmlDoc(do_QueryInterface(mDocument)); michael@0: if (!htmlDoc || !docElement->IsHTML()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr body; michael@0: htmlDoc->GetBody(getter_AddRefs(body)); michael@0: nsCOMPtr bodyElement = do_QueryInterface(body); michael@0: michael@0: if (!bodyElement || michael@0: !bodyElement->NodeInfo()->Equals(nsGkAtoms::body)) { michael@0: // The body is not a tag, it's a . michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr bodyStyle; michael@0: bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle); michael@0: michael@0: if (CheckOverflow(presContext, bodyStyle->StyleDisplay())) { michael@0: // tell caller we stole the overflow style from the body element michael@0: return bodyElement; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocElement, michael@0: nsILayoutHistoryState* aFrameState) michael@0: { michael@0: NS_PRECONDITION(mFixedContainingBlock, michael@0: "No viewport? Someone forgot to call ConstructRootFrame!"); michael@0: NS_PRECONDITION(mFixedContainingBlock == GetRootFrame(), michael@0: "Unexpected mFixedContainingBlock"); michael@0: NS_PRECONDITION(!mDocElementContainingBlock, michael@0: "Shouldn't have a doc element containing block here"); michael@0: michael@0: // Make sure to call PropagateScrollToViewport before michael@0: // SetUpDocElementContainingBlock, since it sets up our scrollbar state michael@0: // properly. michael@0: #ifdef DEBUG michael@0: nsIContent* propagatedScrollFrom = michael@0: #endif michael@0: PropagateScrollToViewport(); michael@0: michael@0: SetUpDocElementContainingBlock(aDocElement); michael@0: michael@0: NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now"); michael@0: michael@0: nsFrameConstructorState state(mPresShell, mFixedContainingBlock, nullptr, michael@0: nullptr, aFrameState); michael@0: // Initialize the ancestor filter with null for now; we'll push michael@0: // aDocElement once we finish resolving style for it. michael@0: state.mTreeMatchContext.InitAncestors(nullptr); michael@0: michael@0: // XXXbz why, exactly? michael@0: if (!mTempFrameTreeState) michael@0: state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState)); michael@0: michael@0: // Make sure that we'll handle restyles for this document element in michael@0: // the future. We need this, because the document element might michael@0: // have stale restyle bits from a previous frame constructor for michael@0: // this document. Unlike in AddFrameConstructionItems, it's safe to michael@0: // unset all element restyle flags, since we don't have any michael@0: // siblings. michael@0: aDocElement->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS); michael@0: michael@0: // --------- CREATE AREA OR BOX FRAME ------- michael@0: nsRefPtr styleContext; michael@0: styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement, michael@0: nullptr); michael@0: michael@0: const nsStyleDisplay* display = styleContext->StyleDisplay(); michael@0: michael@0: // Ensure that our XBL bindings are installed. michael@0: if (display->mBinding) { michael@0: // Get the XBL loader. michael@0: nsresult rv; michael@0: bool resolveStyle; michael@0: michael@0: nsXBLService* xblService = nsXBLService::GetInstance(); michael@0: if (!xblService) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr binding; michael@0: rv = xblService->LoadBindings(aDocElement, display->mBinding->GetURI(), michael@0: display->mBinding->mOriginPrincipal, michael@0: getter_AddRefs(binding), &resolveStyle); michael@0: if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED) michael@0: return nullptr; // Binding will load asynchronously. michael@0: michael@0: if (binding) { michael@0: // For backwards compat, keep firing the root's constructor michael@0: // after all of its kids' constructors. So tell the binding michael@0: // manager about it right now. michael@0: mDocument->BindingManager()->AddToAttachedQueue(binding); michael@0: } michael@0: michael@0: if (resolveStyle) { michael@0: styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement, michael@0: nullptr); michael@0: display = styleContext->StyleDisplay(); michael@0: } michael@0: } michael@0: michael@0: // --------- IF SCROLLABLE WRAP IN SCROLLFRAME -------- michael@0: michael@0: #ifdef DEBUG michael@0: NS_ASSERTION(!display->IsScrollableOverflow() || michael@0: state.mPresContext->IsPaginated() || michael@0: propagatedScrollFrom == aDocElement, michael@0: "Scrollbars should have been propagated to the viewport"); michael@0: #endif michael@0: michael@0: if (MOZ_UNLIKELY(display->mDisplay == NS_STYLE_DISPLAY_NONE)) { michael@0: SetUndisplayedContent(aDocElement, styleContext); michael@0: return nullptr; michael@0: } michael@0: michael@0: TreeMatchContext::AutoAncestorPusher ancestorPusher(state.mTreeMatchContext); michael@0: ancestorPusher.PushAncestorAndStyleScope(aDocElement); michael@0: michael@0: // Make sure to start any background image loads for the root element now. michael@0: styleContext->StartBackgroundImageLoads(); michael@0: michael@0: nsFrameConstructorSaveState absoluteSaveState; michael@0: if (mHasRootAbsPosContainingBlock) { michael@0: // Push the absolute containing block now so we can absolutely position michael@0: // the root element michael@0: mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: state.PushAbsoluteContainingBlock(mDocElementContainingBlock, michael@0: mDocElementContainingBlock, michael@0: absoluteSaveState); michael@0: } michael@0: michael@0: // The rules from CSS 2.1, section 9.2.4, have already been applied michael@0: // by the style system, so we can assume that display->mDisplay is michael@0: // either NONE, BLOCK, or TABLE. michael@0: michael@0: // contentFrame is the primary frame for the root element. newFrame michael@0: // is the frame that will be the child of the initial containing block. michael@0: // These are usually the same frame but they can be different, in michael@0: // particular if the root frame is positioned, in which case michael@0: // contentFrame is the out-of-flow frame and newFrame is the michael@0: // placeholder. michael@0: nsIFrame* contentFrame; michael@0: nsIFrame* newFrame; michael@0: bool processChildren = false; michael@0: michael@0: // Check whether we need to build a XUL box or SVG root frame michael@0: #ifdef MOZ_XUL michael@0: if (aDocElement->IsXUL()) { michael@0: contentFrame = NS_NewDocElementBoxFrame(mPresShell, styleContext); michael@0: InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock, michael@0: contentFrame); michael@0: newFrame = contentFrame; michael@0: processChildren = true; michael@0: } michael@0: else michael@0: #endif michael@0: if (aDocElement->IsSVG()) { michael@0: if (aDocElement->Tag() != nsGkAtoms::svg) { michael@0: return nullptr; michael@0: } michael@0: // We're going to call the right function ourselves, so no need to give a michael@0: // function to this FrameConstructionData. michael@0: michael@0: // XXXbz on the other hand, if we converted this whole function to michael@0: // FrameConstructionData/Item, then we'd need the right function michael@0: // here... but would probably be able to get away with less code in this michael@0: // function in general. michael@0: // Use a null PendingBinding, since our binding is not in fact pending. michael@0: static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr); michael@0: already_AddRefed extraRef = michael@0: nsRefPtr(styleContext).forget(); michael@0: FrameConstructionItem item(&rootSVGData, aDocElement, michael@0: aDocElement->Tag(), kNameSpaceID_SVG, michael@0: nullptr, extraRef, true, nullptr); michael@0: michael@0: nsFrameItems frameItems; michael@0: contentFrame = ConstructOuterSVG(state, item, mDocElementContainingBlock, michael@0: styleContext->StyleDisplay(), michael@0: frameItems); michael@0: newFrame = frameItems.FirstChild(); michael@0: NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames"); michael@0: } else if (display->mDisplay == NS_STYLE_DISPLAY_FLEX) { michael@0: contentFrame = NS_NewFlexContainerFrame(mPresShell, styleContext); michael@0: InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock, michael@0: contentFrame); michael@0: newFrame = contentFrame; michael@0: processChildren = true; michael@0: } else if (display->mDisplay == NS_STYLE_DISPLAY_GRID) { michael@0: contentFrame = NS_NewGridContainerFrame(mPresShell, styleContext); michael@0: InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock, michael@0: contentFrame); michael@0: newFrame = contentFrame; michael@0: processChildren = true; michael@0: } else if (display->mDisplay == NS_STYLE_DISPLAY_TABLE) { michael@0: // We're going to call the right function ourselves, so no need to give a michael@0: // function to this FrameConstructionData. michael@0: michael@0: // XXXbz on the other hand, if we converted this whole function to michael@0: // FrameConstructionData/Item, then we'd need the right function michael@0: // here... but would probably be able to get away with less code in this michael@0: // function in general. michael@0: // Use a null PendingBinding, since our binding is not in fact pending. michael@0: static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr); michael@0: already_AddRefed extraRef = michael@0: nsRefPtr(styleContext).forget(); michael@0: FrameConstructionItem item(&rootTableData, aDocElement, michael@0: aDocElement->Tag(), kNameSpaceID_None, michael@0: nullptr, extraRef, true, nullptr); michael@0: michael@0: nsFrameItems frameItems; michael@0: // if the document is a table then just populate it. michael@0: contentFrame = ConstructTable(state, item, mDocElementContainingBlock, michael@0: styleContext->StyleDisplay(), michael@0: frameItems); michael@0: newFrame = frameItems.FirstChild(); michael@0: NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames"); michael@0: } else { michael@0: MOZ_ASSERT(display->mDisplay == NS_STYLE_DISPLAY_BLOCK, michael@0: "Unhandled display type for root element"); michael@0: contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext); michael@0: nsFrameItems frameItems; michael@0: // Use a null PendingBinding, since our binding is not in fact pending. michael@0: ConstructBlock(state, display, aDocElement, michael@0: state.GetGeometricParent(display, michael@0: mDocElementContainingBlock), michael@0: mDocElementContainingBlock, styleContext, michael@0: &contentFrame, frameItems, michael@0: display->IsPositioned(contentFrame) ? contentFrame : nullptr, michael@0: nullptr); michael@0: newFrame = frameItems.FirstChild(); michael@0: NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames"); michael@0: } michael@0: michael@0: MOZ_ASSERT(newFrame); michael@0: MOZ_ASSERT(contentFrame); michael@0: michael@0: NS_ASSERTION(processChildren ? !mRootElementFrame : michael@0: mRootElementFrame == contentFrame, michael@0: "unexpected mRootElementFrame"); michael@0: mRootElementFrame = contentFrame; michael@0: michael@0: // Figure out which frame has the main style for the document element, michael@0: // assigning it to mRootElementStyleFrame. michael@0: // Backgrounds should be propagated from that frame to the viewport. michael@0: mRootElementStyleFrame = contentFrame->GetParentStyleContextFrame(); michael@0: bool isChild = mRootElementStyleFrame && michael@0: mRootElementStyleFrame->GetParent() == contentFrame; michael@0: if (!isChild) { michael@0: mRootElementStyleFrame = mRootElementFrame; michael@0: } michael@0: michael@0: if (processChildren) { michael@0: // Still need to process the child content michael@0: nsFrameItems childItems; michael@0: michael@0: NS_ASSERTION(!nsLayoutUtils::GetAsBlock(contentFrame) && michael@0: !contentFrame->IsFrameOfType(nsIFrame::eSVG), michael@0: "Only XUL frames should reach here"); michael@0: // Use a null PendingBinding, since our binding is not in fact pending. michael@0: ProcessChildren(state, aDocElement, styleContext, contentFrame, true, michael@0: childItems, false, nullptr); michael@0: michael@0: // Set the initial child lists michael@0: contentFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: } michael@0: michael@0: // set the primary frame michael@0: aDocElement->SetPrimaryFrame(contentFrame); michael@0: michael@0: SetInitialSingleChild(mDocElementContainingBlock, newFrame); michael@0: michael@0: return newFrame; michael@0: } michael@0: michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructRootFrame() michael@0: { michael@0: AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); michael@0: michael@0: nsStyleSet *styleSet = mPresShell->StyleSet(); michael@0: michael@0: // Set up our style rule observer. michael@0: // XXXbz wouldn't this make more sense as part of presshell init? michael@0: { michael@0: styleSet->SetBindingManager(mDocument->BindingManager()); michael@0: } michael@0: michael@0: // --------- BUILD VIEWPORT ----------- michael@0: nsIFrame* viewportFrame = nullptr; michael@0: nsRefPtr viewportPseudoStyle; michael@0: michael@0: viewportPseudoStyle = michael@0: styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewport, nullptr); michael@0: michael@0: viewportFrame = NS_NewViewportFrame(mPresShell, viewportPseudoStyle); michael@0: michael@0: // XXXbz do we _have_ to pass a null content pointer to that frame? michael@0: // Would it really kill us to pass in the root element or something? michael@0: // What would that break? michael@0: viewportFrame->Init(nullptr, nullptr, nullptr); michael@0: michael@0: // Bind the viewport frame to the root view michael@0: nsView* rootView = mPresShell->GetViewManager()->GetRootView(); michael@0: viewportFrame->SetView(rootView); michael@0: michael@0: nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame, michael@0: viewportPseudoStyle, rootView); michael@0: nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame, michael@0: rootView); michael@0: michael@0: // The viewport is the containing block for 'fixed' elements michael@0: mFixedContainingBlock = viewportFrame; michael@0: // Make it an absolute container for fixed-pos elements michael@0: mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: mFixedContainingBlock->MarkAsAbsoluteContainingBlock(); michael@0: michael@0: return viewportFrame; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::SetUpDocElementContainingBlock(nsIContent* aDocElement) michael@0: { michael@0: NS_PRECONDITION(aDocElement, "No element?"); michael@0: NS_PRECONDITION(!aDocElement->GetParent(), "Not root content?"); michael@0: NS_PRECONDITION(aDocElement->GetCurrentDoc(), "Not in a document?"); michael@0: NS_PRECONDITION(aDocElement->GetCurrentDoc()->GetRootElement() == michael@0: aDocElement, "Not the root of the document?"); michael@0: michael@0: /* michael@0: how the root frame hierarchy should look michael@0: michael@0: Galley presentation, non-XUL, with scrolling: michael@0: michael@0: ViewportFrame [fixed-cb] michael@0: nsHTMLScrollFrame michael@0: nsCanvasFrame [abs-cb] michael@0: root element frame (nsBlockFrame, nsSVGOuterSVGFrame, michael@0: nsTableOuterFrame, nsPlaceholderFrame) michael@0: michael@0: Galley presentation, XUL michael@0: michael@0: ViewportFrame [fixed-cb] michael@0: nsRootBoxFrame michael@0: root element frame (nsDocElementBoxFrame) michael@0: michael@0: Print presentation, non-XUL michael@0: michael@0: ViewportFrame michael@0: nsSimplePageSequenceFrame michael@0: nsPageFrame [fixed-cb] michael@0: nsPageContentFrame michael@0: nsCanvasFrame [abs-cb] michael@0: root element frame (nsBlockFrame, nsSVGOuterSVGFrame, michael@0: nsTableOuterFrame, nsPlaceholderFrame) michael@0: michael@0: Print-preview presentation, non-XUL michael@0: michael@0: ViewportFrame michael@0: nsHTMLScrollFrame michael@0: nsSimplePageSequenceFrame michael@0: nsPageFrame [fixed-cb] michael@0: nsPageContentFrame michael@0: nsCanvasFrame [abs-cb] michael@0: root element frame (nsBlockFrame, nsSVGOuterSVGFrame, michael@0: nsTableOuterFrame, nsPlaceholderFrame) michael@0: michael@0: Print/print preview of XUL is not supported. michael@0: [fixed-cb]: the default containing block for fixed-pos content michael@0: [abs-cb]: the default containing block for abs-pos content michael@0: michael@0: Meaning of nsCSSFrameConstructor fields: michael@0: mRootElementFrame is "root element frame". This is the primary frame for michael@0: the root element. michael@0: mDocElementContainingBlock is the parent of mRootElementFrame michael@0: (i.e. nsCanvasFrame or nsRootBoxFrame) michael@0: mFixedContainingBlock is the [fixed-cb] michael@0: mGfxScrollFrame is the nsHTMLScrollFrame mentioned above, or null if there isn't one michael@0: mPageSequenceFrame is the nsSimplePageSequenceFrame, or null if there isn't one michael@0: */ michael@0: michael@0: // --------- CREATE ROOT FRAME ------- michael@0: michael@0: michael@0: // Create the root frame. The document element's frame is a child of the michael@0: // root frame. michael@0: // michael@0: // The root frame serves two purposes: michael@0: // - reserves space for any margins needed for the document element's frame michael@0: // - renders the document element's background. This ensures the background covers michael@0: // the entire canvas as specified by the CSS2 spec michael@0: michael@0: nsPresContext* presContext = mPresShell->GetPresContext(); michael@0: bool isPaginated = presContext->IsRootPaginatedDocument(); michael@0: nsIFrame* viewportFrame = mFixedContainingBlock; michael@0: nsStyleContext* viewportPseudoStyle = viewportFrame->StyleContext(); michael@0: michael@0: nsIFrame* rootFrame = nullptr; michael@0: nsIAtom* rootPseudo; michael@0: michael@0: if (!isPaginated) { michael@0: #ifdef MOZ_XUL michael@0: if (aDocElement->IsXUL()) michael@0: { michael@0: // pass a temporary stylecontext, the correct one will be set later michael@0: rootFrame = NS_NewRootBoxFrame(mPresShell, viewportPseudoStyle); michael@0: } else michael@0: #endif michael@0: { michael@0: // pass a temporary stylecontext, the correct one will be set later michael@0: rootFrame = NS_NewCanvasFrame(mPresShell, viewportPseudoStyle); michael@0: mHasRootAbsPosContainingBlock = true; michael@0: } michael@0: michael@0: rootPseudo = nsCSSAnonBoxes::canvas; michael@0: mDocElementContainingBlock = rootFrame; michael@0: } else { michael@0: // Create a page sequence frame michael@0: rootFrame = NS_NewSimplePageSequenceFrame(mPresShell, viewportPseudoStyle); michael@0: mPageSequenceFrame = rootFrame; michael@0: rootPseudo = nsCSSAnonBoxes::pageSequence; michael@0: } michael@0: michael@0: michael@0: // --------- IF SCROLLABLE WRAP IN SCROLLFRAME -------- michael@0: michael@0: // If the device supports scrolling (e.g., in galley mode on the screen and michael@0: // for print-preview, but not when printing), then create a scroll frame that michael@0: // will act as the scrolling mechanism for the viewport. michael@0: // XXX Do we even need a viewport when printing to a printer? michael@0: michael@0: bool isHTML = aDocElement->IsHTML(); michael@0: bool isXUL = false; michael@0: michael@0: if (!isHTML) { michael@0: isXUL = aDocElement->IsXUL(); michael@0: } michael@0: michael@0: // Never create scrollbars for XUL documents michael@0: bool isScrollable = isPaginated ? presContext->HasPaginatedScrolling() : !isXUL; michael@0: michael@0: // We no longer need to do overflow propagation here. It's taken care of michael@0: // when we construct frames for the element whose overflow might be michael@0: // propagated michael@0: NS_ASSERTION(!isScrollable || !isXUL, michael@0: "XUL documents should never be scrollable - see above"); michael@0: michael@0: nsIFrame* newFrame = rootFrame; michael@0: nsRefPtr rootPseudoStyle; michael@0: // we must create a state because if the scrollbars are GFX it needs the michael@0: // state to build the scrollbar frames. michael@0: nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr); michael@0: michael@0: // Start off with the viewport as parent; we'll adjust it as needed. michael@0: nsIFrame* parentFrame = viewportFrame; michael@0: michael@0: nsStyleSet* styleSet = mPresShell->StyleSet(); michael@0: // If paginated, make sure we don't put scrollbars in michael@0: if (!isScrollable) { michael@0: rootPseudoStyle = styleSet->ResolveAnonymousBoxStyle(rootPseudo, michael@0: viewportPseudoStyle); michael@0: } else { michael@0: if (rootPseudo == nsCSSAnonBoxes::canvas) { michael@0: rootPseudo = nsCSSAnonBoxes::scrolledCanvas; michael@0: } else { michael@0: NS_ASSERTION(rootPseudo == nsCSSAnonBoxes::pageSequence, michael@0: "Unknown root pseudo"); michael@0: rootPseudo = nsCSSAnonBoxes::scrolledPageSequence; michael@0: } michael@0: michael@0: // Build the frame. We give it the content we are wrapping which is the michael@0: // document element, the root frame, the parent view port frame, and we michael@0: // should get back the new frame and the scrollable view if one was michael@0: // created. michael@0: michael@0: // resolve a context for the scrollframe michael@0: nsRefPtr styleContext; michael@0: styleContext = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::viewportScroll, michael@0: viewportPseudoStyle); michael@0: michael@0: // Note that the viewport scrollframe is always built with michael@0: // overflow:auto style. This forces the scroll frame to create michael@0: // anonymous content for both scrollbars. This is necessary even michael@0: // if the HTML or BODY elements are overriding the viewport michael@0: // scroll style to 'hidden' --- dynamic style changes might put michael@0: // scrollbars back on the viewport and we don't want to have to michael@0: // reframe the viewport to create the scrollbar content. michael@0: newFrame = nullptr; michael@0: rootPseudoStyle = BeginBuildingScrollFrame( state, michael@0: aDocElement, michael@0: styleContext, michael@0: viewportFrame, michael@0: rootPseudo, michael@0: true, michael@0: newFrame); michael@0: parentFrame = newFrame; michael@0: mGfxScrollFrame = newFrame; michael@0: } michael@0: michael@0: rootFrame->SetStyleContextWithoutNotification(rootPseudoStyle); michael@0: rootFrame->Init(aDocElement, parentFrame, nullptr); michael@0: michael@0: if (isScrollable) { michael@0: FinishBuildingScrollFrame(parentFrame, rootFrame); michael@0: } michael@0: michael@0: if (isPaginated) { // paginated michael@0: // Create the first page michael@0: // Set the initial child lists michael@0: nsIFrame* canvasFrame; michael@0: nsIFrame* pageFrame = michael@0: ConstructPageFrame(mPresShell, presContext, rootFrame, nullptr, michael@0: canvasFrame); michael@0: SetInitialSingleChild(rootFrame, pageFrame); michael@0: michael@0: // The eventual parent of the document element frame. michael@0: // XXX should this be set for every new page (in ConstructPageFrame)? michael@0: mDocElementContainingBlock = canvasFrame; michael@0: mHasRootAbsPosContainingBlock = true; michael@0: } michael@0: michael@0: if (viewportFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) { michael@0: SetInitialSingleChild(viewportFrame, newFrame); michael@0: } else { michael@0: nsFrameList newFrameList(newFrame, newFrame); michael@0: viewportFrame->AppendFrames(kPrincipalList, newFrameList); michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell* aPresShell, michael@0: nsPresContext* aPresContext, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aPrevPageFrame, michael@0: nsIFrame*& aCanvasFrame) michael@0: { michael@0: nsStyleContext* parentStyleContext = aParentFrame->StyleContext(); michael@0: nsStyleSet *styleSet = aPresShell->StyleSet(); michael@0: michael@0: nsRefPtr pagePseudoStyle; michael@0: pagePseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::page, michael@0: parentStyleContext); michael@0: michael@0: nsIFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle); michael@0: michael@0: // Initialize the page frame and force it to have a view. This makes printing of michael@0: // the pages easier and faster. michael@0: pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame); michael@0: michael@0: nsRefPtr pageContentPseudoStyle; michael@0: pageContentPseudoStyle = michael@0: styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageContent, michael@0: pagePseudoStyle); michael@0: michael@0: nsIFrame* pageContentFrame = NS_NewPageContentFrame(aPresShell, pageContentPseudoStyle); michael@0: michael@0: // Initialize the page content frame and force it to have a view. Also make it the michael@0: // containing block for fixed elements which are repeated on every page. michael@0: nsIFrame* prevPageContentFrame = nullptr; michael@0: if (aPrevPageFrame) { michael@0: prevPageContentFrame = aPrevPageFrame->GetFirstPrincipalChild(); michael@0: NS_ASSERTION(prevPageContentFrame, "missing page content frame"); michael@0: } michael@0: pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame); michael@0: SetInitialSingleChild(pageFrame, pageContentFrame); michael@0: mFixedContainingBlock = pageContentFrame; michael@0: // Make it an absolute container for fixed-pos elements michael@0: mFixedContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: mFixedContainingBlock->MarkAsAbsoluteContainingBlock(); michael@0: michael@0: nsRefPtr canvasPseudoStyle; michael@0: canvasPseudoStyle = styleSet->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::canvas, michael@0: pageContentPseudoStyle); michael@0: michael@0: aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle); michael@0: michael@0: nsIFrame* prevCanvasFrame = nullptr; michael@0: if (prevPageContentFrame) { michael@0: prevCanvasFrame = prevPageContentFrame->GetFirstPrincipalChild(); michael@0: NS_ASSERTION(prevCanvasFrame, "missing canvas frame"); michael@0: } michael@0: aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame); michael@0: SetInitialSingleChild(pageContentFrame, aCanvasFrame); michael@0: return pageFrame; michael@0: } michael@0: michael@0: /* static */ michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::CreatePlaceholderFrameFor(nsIPresShell* aPresShell, michael@0: nsIContent* aContent, michael@0: nsIFrame* aFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aPrevInFlow, michael@0: nsFrameState aTypeBit) michael@0: { michael@0: nsRefPtr placeholderStyle = aPresShell->StyleSet()-> michael@0: ResolveStyleForNonElement(aStyleContext->GetParent()); michael@0: michael@0: // The placeholder frame gets a pseudo style context michael@0: nsPlaceholderFrame* placeholderFrame = michael@0: (nsPlaceholderFrame*)NS_NewPlaceholderFrame(aPresShell, placeholderStyle, michael@0: aTypeBit); michael@0: michael@0: placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow); michael@0: michael@0: // The placeholder frame has a pointer back to the out-of-flow frame michael@0: placeholderFrame->SetOutOfFlowFrame(aFrame); michael@0: michael@0: aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW); michael@0: michael@0: // Add mapping from absolutely positioned frame to its placeholder frame michael@0: aPresShell->FrameManager()->RegisterPlaceholderFrame(placeholderFrame); michael@0: michael@0: return placeholderFrame; michael@0: } michael@0: michael@0: // Clears any lazy bits set in the range [aStartContent, aEndContent). If michael@0: // aEndContent is null, that means to clear bits in all siblings starting with michael@0: // aStartContent. aStartContent must not be null unless aEndContent is also michael@0: // null. We do this so that when new children are inserted under elements whose michael@0: // frame is a leaf the new children don't cause us to try to construct frames michael@0: // for the existing children again. michael@0: static inline void michael@0: ClearLazyBits(nsIContent* aStartContent, nsIContent* aEndContent) michael@0: { michael@0: NS_PRECONDITION(aStartContent || !aEndContent, michael@0: "Must have start child if we have an end child"); michael@0: for (nsIContent* cur = aStartContent; cur != aEndContent; michael@0: cur = cur->GetNextSibling()) { michael@0: cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: michael@0: // Construct a frame-based listbox or combobox michael@0: dom::HTMLSelectElement* sel = dom::HTMLSelectElement::FromContent(content); michael@0: MOZ_ASSERT(sel); michael@0: if (sel->IsCombobox()) { michael@0: // Construct a frame-based combo box. michael@0: // The frame-based combo box is built out of three parts. A display area, a button and michael@0: // a dropdown list. The display area and button are created through anonymous content. michael@0: // The drop-down list's frame is created explicitly. The combobox frame shares its content michael@0: // with the drop-down list. michael@0: nsFrameState flags = NS_BLOCK_FLOAT_MGR; michael@0: nsIFrame* comboboxFrame = NS_NewComboboxControlFrame(mPresShell, styleContext, flags); michael@0: michael@0: // Save the history state so we don't restore during construction michael@0: // since the complete tree is required before we restore. michael@0: nsILayoutHistoryState *historyState = aState.mFrameState; michael@0: aState.mFrameState = nullptr; michael@0: // Initialize the combobox frame michael@0: InitAndRestoreFrame(aState, content, michael@0: aState.GetGeometricParent(aStyleDisplay, aParentFrame), michael@0: comboboxFrame); michael@0: michael@0: aState.AddChild(comboboxFrame, aFrameItems, content, styleContext, michael@0: aParentFrame); michael@0: michael@0: nsIComboboxControlFrame* comboBox = do_QueryFrame(comboboxFrame); michael@0: NS_ASSERTION(comboBox, "NS_NewComboboxControlFrame returned frame that " michael@0: "doesn't implement nsIComboboxControlFrame"); michael@0: michael@0: // Resolve pseudo element style for the dropdown list michael@0: nsRefPtr listStyle; michael@0: listStyle = mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(nsCSSAnonBoxes::dropDownList, styleContext); michael@0: michael@0: // Create a listbox michael@0: nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, listStyle); michael@0: michael@0: // Notify the listbox that it is being used as a dropdown list. michael@0: nsIListControlFrame * listControlFrame = do_QueryFrame(listFrame); michael@0: if (listControlFrame) { michael@0: listControlFrame->SetComboboxFrame(comboboxFrame); michael@0: } michael@0: // Notify combobox that it should use the listbox as it's popup michael@0: comboBox->SetDropDown(listFrame); michael@0: michael@0: NS_ASSERTION(!listFrame->IsPositioned(), michael@0: "Ended up with positioned dropdown list somehow."); michael@0: NS_ASSERTION(!listFrame->IsFloating(), michael@0: "Ended up with floating dropdown list somehow."); michael@0: michael@0: // Initialize the scroll frame positioned. Note that it is NOT michael@0: // initialized as absolutely positioned. michael@0: nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(mPresShell, styleContext, flags); michael@0: michael@0: InitializeSelectFrame(aState, listFrame, scrolledFrame, content, michael@0: comboboxFrame, listStyle, true, michael@0: aItem.mPendingBinding, aFrameItems); michael@0: michael@0: NS_ASSERTION(listFrame->GetView(), "ListFrame's view is nullptr"); michael@0: michael@0: // Create display and button frames from the combobox's anonymous content. michael@0: // The anonymous content is appended to existing anonymous content for this michael@0: // element (the scrollbars). michael@0: michael@0: nsFrameItems childItems; michael@0: CreateAnonymousFrames(aState, content, comboboxFrame, michael@0: aItem.mPendingBinding, childItems); michael@0: michael@0: comboboxFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: michael@0: // Initialize the additional popup child list which contains the michael@0: // dropdown list frame. michael@0: nsFrameItems popupItems; michael@0: popupItems.AddChild(listFrame); michael@0: comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList, michael@0: popupItems); michael@0: michael@0: aState.mFrameState = historyState; michael@0: if (aState.mFrameState) { michael@0: // Restore frame state for the entire subtree of |comboboxFrame|. michael@0: RestoreFrameState(comboboxFrame, aState.mFrameState); michael@0: } michael@0: return comboboxFrame; michael@0: } michael@0: michael@0: // Listbox, not combobox michael@0: nsIFrame* listFrame = NS_NewListControlFrame(mPresShell, styleContext); michael@0: michael@0: nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame( michael@0: mPresShell, styleContext, NS_BLOCK_FLOAT_MGR); michael@0: michael@0: // ******* this code stolen from Initialze ScrollFrame ******** michael@0: // please adjust this code to use BuildScrollFrame. michael@0: michael@0: InitializeSelectFrame(aState, listFrame, scrolledFrame, content, michael@0: aParentFrame, styleContext, false, michael@0: aItem.mPendingBinding, aFrameItems); michael@0: michael@0: return listFrame; michael@0: } michael@0: michael@0: /** michael@0: * Used to be InitializeScrollFrame but now it's only used for the select tag michael@0: * But the select tag should really be fixed to use GFX scrollbars that can michael@0: * be create with BuildScrollFrame. michael@0: */ michael@0: nsresult michael@0: nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState, michael@0: nsIFrame* scrollFrame, michael@0: nsIFrame* scrolledFrame, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext, michael@0: bool aBuildCombobox, michael@0: PendingBinding* aPendingBinding, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: const nsStyleDisplay* display = aStyleContext->StyleDisplay(); michael@0: michael@0: // Initialize it michael@0: nsIFrame* geometricParent = aState.GetGeometricParent(display, aParentFrame); michael@0: michael@0: // We don't call InitAndRestoreFrame for scrollFrame because we can only michael@0: // restore the frame state after its parts have been created (in particular, michael@0: // the scrollable view). So we have to split Init and Restore. michael@0: michael@0: // Initialize the frame michael@0: scrollFrame->Init(aContent, geometricParent, nullptr); michael@0: michael@0: if (!aBuildCombobox) { michael@0: aState.AddChild(scrollFrame, aFrameItems, aContent, michael@0: aStyleContext, aParentFrame); michael@0: } michael@0: michael@0: if (aBuildCombobox) { michael@0: nsContainerFrame::CreateViewForFrame(scrollFrame, true); michael@0: } michael@0: michael@0: BuildScrollFrame(aState, aContent, aStyleContext, scrolledFrame, michael@0: geometricParent, scrollFrame); michael@0: michael@0: if (aState.mFrameState) { michael@0: // Restore frame state for the scroll frame michael@0: RestoreFrameStateFor(scrollFrame, aState.mFrameState); michael@0: } michael@0: michael@0: // Process children michael@0: nsFrameItems childItems; michael@0: michael@0: ProcessChildren(aState, aContent, aStyleContext, scrolledFrame, false, michael@0: childItems, false, aPendingBinding); michael@0: michael@0: // Set the scrolled frame's initial child lists michael@0: scrolledFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aStyleDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: michael@0: nsIFrame* fieldsetFrame = NS_NewFieldSetFrame(mPresShell, styleContext); michael@0: michael@0: // Initialize it michael@0: InitAndRestoreFrame(aState, content, michael@0: aState.GetGeometricParent(aStyleDisplay, aParentFrame), michael@0: fieldsetFrame); michael@0: michael@0: // Resolve style and initialize the frame michael@0: nsRefPtr fieldsetContentStyle; michael@0: fieldsetContentStyle = mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(nsCSSAnonBoxes::fieldsetContent, styleContext); michael@0: michael@0: const nsStyleDisplay* fieldsetContentDisplay = fieldsetContentStyle->StyleDisplay(); michael@0: bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow(); michael@0: nsIFrame* scrollFrame = nullptr; michael@0: if (isScrollable) { michael@0: fieldsetContentStyle = michael@0: BeginBuildingScrollFrame(aState, content, fieldsetContentStyle, michael@0: fieldsetFrame, nsCSSAnonBoxes::scrolledContent, michael@0: false, scrollFrame); michael@0: } michael@0: michael@0: nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle, michael@0: NS_BLOCK_FLOAT_MGR | michael@0: NS_BLOCK_MARGIN_ROOT); michael@0: InitAndRestoreFrame(aState, content, michael@0: scrollFrame ? scrollFrame : fieldsetFrame, blockFrame); michael@0: michael@0: aState.AddChild(fieldsetFrame, aFrameItems, content, styleContext, aParentFrame); michael@0: michael@0: // Process children michael@0: nsFrameConstructorSaveState absoluteSaveState; michael@0: nsFrameItems childItems; michael@0: michael@0: blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: if (fieldsetFrame->IsPositioned()) { michael@0: aState.PushAbsoluteContainingBlock(blockFrame, fieldsetFrame, absoluteSaveState); michael@0: } michael@0: michael@0: ProcessChildren(aState, content, styleContext, blockFrame, true, michael@0: childItems, true, aItem.mPendingBinding); michael@0: michael@0: nsFrameItems fieldsetKids; michael@0: fieldsetKids.AddChild(scrollFrame ? scrollFrame : blockFrame); michael@0: michael@0: for (nsFrameList::Enumerator e(childItems); !e.AtEnd(); e.Next()) { michael@0: nsIFrame* child = e.get(); michael@0: if (child->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) { michael@0: // We want the legend to be the first frame in the fieldset child list. michael@0: // That way the EventStateManager will do the right thing when tabbing michael@0: // from a selection point within the legend (bug 236071), which is michael@0: // used for implementing legend access keys (bug 81481). michael@0: // GetAdjustedParentFrame() below depends on this frame order. michael@0: childItems.RemoveFrame(child); michael@0: // Make sure to reparent the legend so it has the fieldset as the parent. michael@0: fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child); michael@0: if (scrollFrame) { michael@0: StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary( michael@0: child, blockFrame); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (isScrollable) { michael@0: FinishBuildingScrollFrame(scrollFrame, blockFrame); michael@0: } michael@0: michael@0: // Set the inner frame's initial child lists michael@0: blockFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: michael@0: // Set the outer frame's initial child list michael@0: fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids); michael@0: michael@0: fieldsetFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT); michael@0: michael@0: // Our new frame returned is the outer frame, which is the fieldset frame. michael@0: return fieldsetFrame; michael@0: } michael@0: michael@0: static nsIFrame* michael@0: FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame) michael@0: { michael@0: for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) { michael@0: NS_ASSERTION(f->IsGeneratedContentFrame(), michael@0: "should not have exited generated content"); michael@0: nsIAtom* pseudo = f->StyleContext()->GetPseudo(); michael@0: if (pseudo == nsCSSPseudoElements::before || michael@0: pseudo == nsCSSPseudoElements::after) michael@0: return f; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func) michael@0: #define FULL_CTOR_FCDATA(_flags, _func) \ michael@0: { _flags | FCDATA_FUNC_IS_FULL_CTOR, { nullptr }, _func, nullptr } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindTextData(nsIFrame* aParentFrame) michael@0: { michael@0: if (aParentFrame && IsFrameForSVG(aParentFrame)) { michael@0: nsIFrame *ancestorFrame = michael@0: nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame); michael@0: if (ancestorFrame) { michael@0: static const FrameConstructionData sSVGTextData = michael@0: FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT, michael@0: NS_NewTextFrame); michael@0: if (ancestorFrame->IsSVGText()) { michael@0: return &sSVGTextData; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionData sTextData = michael@0: FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewTextFrame); michael@0: return &sTextData; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::ConstructTextFrame(const FrameConstructionData* aData, michael@0: nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: NS_PRECONDITION(aData, "Must have frame construction data"); michael@0: michael@0: nsIFrame* newFrame = (*aData->mFunc.mCreationFunc)(mPresShell, aStyleContext); michael@0: michael@0: InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame); michael@0: michael@0: // We never need to create a view for a text frame. michael@0: michael@0: if (newFrame->IsGeneratedContentFrame()) { michael@0: nsAutoPtr initializer; michael@0: initializer = michael@0: static_cast( michael@0: aContent->UnsetProperty(nsGkAtoms::genConInitializerProperty)); michael@0: if (initializer) { michael@0: if (initializer->mNode->InitTextFrame(initializer->mList, michael@0: FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) { michael@0: (this->*(initializer->mDirtyAll))(); michael@0: } michael@0: initializer->mNode.forget(); michael@0: } michael@0: } michael@0: michael@0: // Add the newly constructed frame to the flow michael@0: aFrameItems.AddChild(newFrame); michael@0: michael@0: if (!aState.mCreatingExtraFrames) michael@0: aContent->SetPrimaryFrame(newFrame); michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindDataByInt(int32_t aInt, michael@0: Element* aElement, michael@0: nsStyleContext* aStyleContext, michael@0: const FrameConstructionDataByInt* aDataPtr, michael@0: uint32_t aDataLength) michael@0: { michael@0: for (const FrameConstructionDataByInt *curData = aDataPtr, michael@0: *endData = aDataPtr + aDataLength; michael@0: curData != endData; michael@0: ++curData) { michael@0: if (curData->mInt == aInt) { michael@0: const FrameConstructionData* data = &curData->mData; michael@0: if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) { michael@0: return data->mFunc.mDataGetter(aElement, aStyleContext); michael@0: } michael@0: michael@0: return data; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindDataByTag(nsIAtom* aTag, michael@0: Element* aElement, michael@0: nsStyleContext* aStyleContext, michael@0: const FrameConstructionDataByTag* aDataPtr, michael@0: uint32_t aDataLength) michael@0: { michael@0: for (const FrameConstructionDataByTag *curData = aDataPtr, michael@0: *endData = aDataPtr + aDataLength; michael@0: curData != endData; michael@0: ++curData) { michael@0: if (*curData->mTag == aTag) { michael@0: const FrameConstructionData* data = &curData->mData; michael@0: if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) { michael@0: return data->mFunc.mDataGetter(aElement, aStyleContext); michael@0: } michael@0: michael@0: return data; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: #define SUPPRESS_FCDATA() FCDATA_DECL(FCDATA_SUPPRESS_FRAME, nullptr) michael@0: #define SIMPLE_INT_CREATE(_int, _func) { _int, SIMPLE_FCDATA(_func) } michael@0: #define SIMPLE_INT_CHAIN(_int, _func) \ michael@0: { _int, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) } michael@0: #define COMPLEX_INT_CREATE(_int, _func) \ michael@0: { _int, FULL_CTOR_FCDATA(0, _func) } michael@0: michael@0: #define SIMPLE_TAG_CREATE(_tag, _func) \ michael@0: { &nsGkAtoms::_tag, SIMPLE_FCDATA(_func) } michael@0: #define SIMPLE_TAG_CHAIN(_tag, _func) \ michael@0: { &nsGkAtoms::_tag, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER, _func) } michael@0: #define COMPLEX_TAG_CREATE(_tag, _func) \ michael@0: { &nsGkAtoms::_tag, FULL_CTOR_FCDATA(0, _func) } michael@0: michael@0: static bool michael@0: IsFrameForFieldSet(nsIFrame* aFrame, nsIAtom* aFrameType) michael@0: { michael@0: nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo(); michael@0: if (pseudo == nsCSSAnonBoxes::fieldsetContent || michael@0: pseudo == nsCSSAnonBoxes::scrolledContent) { michael@0: return IsFrameForFieldSet(aFrame->GetParent(), aFrame->GetParent()->GetType()); michael@0: } michael@0: return aFrameType == nsGkAtoms::fieldSetFrame; michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindHTMLData(Element* aElement, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: // Ignore the tag if it's not HTML content and if it doesn't extend (via XBL) michael@0: // a valid HTML namespace. This check must match the one in michael@0: // ShouldHaveFirstLineStyle. michael@0: if (aNameSpaceID != kNameSpaceID_XHTML) { michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_ASSERTION(!aParentFrame || michael@0: aParentFrame->StyleContext()->GetPseudo() != michael@0: nsCSSAnonBoxes::fieldsetContent || michael@0: aParentFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame, michael@0: "Unexpected parent for fieldset content anon box"); michael@0: if (aTag == nsGkAtoms::legend && michael@0: (!aParentFrame || michael@0: !IsFrameForFieldSet(aParentFrame, aParentFrame->GetType()) || michael@0: !aElement->GetParent() || michael@0: !aElement->GetParent()->IsHTML(nsGkAtoms::fieldset) || michael@0: aStyleContext->StyleDisplay()->IsFloatingStyle() || michael@0: aStyleContext->StyleDisplay()->IsAbsolutelyPositionedStyle())) { michael@0: // is only special inside fieldset, check both the frame tree michael@0: // parent and content tree parent due to XBL issues. For floated or michael@0: // absolutely positioned legends we want to construct by display type and michael@0: // not do special legend stuff. michael@0: // XXXbz it would be nice if we could just decide this based on the parent michael@0: // tag, and hence just use a SIMPLE_TAG_CHAIN for legend below, but the michael@0: // fact that with XBL we could end up with this legend element in some michael@0: // totally weird insertion point makes that chancy, I think. michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionDataByTag sHTMLData[] = { michael@0: SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData), michael@0: SIMPLE_TAG_CHAIN(mozgeneratedcontentimage, michael@0: nsCSSFrameConstructor::FindImgData), michael@0: { &nsGkAtoms::br, michael@0: FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK, michael@0: NS_NewBRFrame) }, michael@0: SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame), michael@0: SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData), michael@0: SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame), michael@0: COMPLEX_TAG_CREATE(select, &nsCSSFrameConstructor::ConstructSelectFrame), michael@0: SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData), michael@0: SIMPLE_TAG_CHAIN(applet, nsCSSFrameConstructor::FindObjectData), michael@0: SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData), michael@0: COMPLEX_TAG_CREATE(fieldset, michael@0: &nsCSSFrameConstructor::ConstructFieldSetFrame), michael@0: { &nsGkAtoms::legend, michael@0: FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES | FCDATA_MAY_NEED_SCROLLFRAME, michael@0: NS_NewLegendFrame) }, michael@0: SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame), michael@0: SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame), michael@0: { &nsGkAtoms::button, michael@0: FCDATA_WITH_WRAPPING_BLOCK(FCDATA_ALLOW_BLOCK_STYLES, michael@0: NS_NewHTMLButtonControlFrame, michael@0: nsCSSAnonBoxes::buttonContent) }, michael@0: SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData), michael@0: SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame), michael@0: SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame), michael@0: SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame), michael@0: SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame) michael@0: }; michael@0: michael@0: return FindDataByTag(aTag, aElement, aStyleContext, sHTMLData, michael@0: ArrayLength(sHTMLData)); michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindImgData(Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionData sImgData = SIMPLE_FCDATA(NS_NewImageFrame); michael@0: return &sImgData; michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindImgControlData(Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyleContext)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionData sImgControlData = michael@0: SIMPLE_FCDATA(NS_NewImageControlFrame); michael@0: return &sImgControlData; michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindInputData(Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: static const FrameConstructionDataByInt sInputData[] = { michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_CHECKBOX, NS_NewGfxCheckboxControlFrame), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_RADIO, NS_NewGfxRadioControlFrame), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_FILE, NS_NewFileControlFrame), michael@0: SIMPLE_INT_CHAIN(NS_FORM_INPUT_IMAGE, michael@0: nsCSSFrameConstructor::FindImgControlData), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_EMAIL, NS_NewTextControlFrame), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_SEARCH, NS_NewTextControlFrame), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_TEXT, NS_NewTextControlFrame), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_TEL, NS_NewTextControlFrame), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_URL, NS_NewTextControlFrame), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_RANGE, NS_NewRangeFrame), michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame), michael@0: { NS_FORM_INPUT_COLOR, michael@0: FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewColorControlFrame, michael@0: nsCSSAnonBoxes::buttonContent) }, michael@0: // TODO: this is temporary until a frame is written: bug 635240. michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_NUMBER, NS_NewNumberControlFrame), michael@0: // TODO: this is temporary until a frame is written: bug 773205. michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_DATE, NS_NewTextControlFrame), michael@0: // TODO: this is temporary until a frame is written: bug 773205 michael@0: SIMPLE_INT_CREATE(NS_FORM_INPUT_TIME, NS_NewTextControlFrame), michael@0: { NS_FORM_INPUT_SUBMIT, michael@0: FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame, michael@0: nsCSSAnonBoxes::buttonContent) }, michael@0: { NS_FORM_INPUT_RESET, michael@0: FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame, michael@0: nsCSSAnonBoxes::buttonContent) }, michael@0: { NS_FORM_INPUT_BUTTON, michael@0: FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame, michael@0: nsCSSAnonBoxes::buttonContent) } michael@0: // Keeping hidden inputs out of here on purpose for so they get frames by michael@0: // display (in practice, none). michael@0: }; michael@0: michael@0: nsCOMPtr control = do_QueryInterface(aElement); michael@0: NS_ASSERTION(control, "input doesn't implement nsIFormControl?"); michael@0: michael@0: return FindDataByInt(control->GetType(), aElement, aStyleContext, michael@0: sInputData, ArrayLength(sInputData)); michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindObjectData(Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: // GetDisplayedType isn't necessarily nsIObjectLoadingContent::TYPE_NULL for michael@0: // cases when the object is broken/suppressed/etc (e.g. a broken image), but michael@0: // we want to treat those cases as TYPE_NULL michael@0: uint32_t type; michael@0: if (aElement->State().HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | michael@0: NS_EVENT_STATE_USERDISABLED | michael@0: NS_EVENT_STATE_SUPPRESSED)) { michael@0: type = nsIObjectLoadingContent::TYPE_NULL; michael@0: } else { michael@0: nsCOMPtr objContent(do_QueryInterface(aElement)); michael@0: NS_ASSERTION(objContent, michael@0: "applet, embed and object must implement " michael@0: "nsIObjectLoadingContent!"); michael@0: michael@0: objContent->GetDisplayedType(&type); michael@0: } michael@0: michael@0: static const FrameConstructionDataByInt sObjectData[] = { michael@0: SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING, michael@0: NS_NewEmptyFrame), michael@0: SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_PLUGIN, michael@0: NS_NewObjectFrame), michael@0: SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE, michael@0: NS_NewImageFrame), michael@0: SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT, michael@0: NS_NewSubDocumentFrame) michael@0: // Nothing for TYPE_NULL so we'll construct frames by display there michael@0: }; michael@0: michael@0: return FindDataByInt((int32_t)type, aElement, aStyleContext, michael@0: sObjectData, ArrayLength(sObjectData)); michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindCanvasData(Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: // We want to check whether script is enabled on the document that michael@0: // could be painting to the canvas. That's the owner document of michael@0: // the canvas, except when the owner document is a static document, michael@0: // in which case it's the original document it was cloned from. michael@0: nsIDocument* doc = aElement->OwnerDoc(); michael@0: if (doc->IsStaticDocument()) { michael@0: doc = doc->GetOriginalDocument(); michael@0: } michael@0: if (!doc->IsScriptEnabled()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionData sCanvasData = michael@0: FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewHTMLCanvasFrame, michael@0: nsCSSAnonBoxes::htmlCanvasContent); michael@0: return &sCanvasData; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aItem, michael@0: nsFrameConstructorState& aState, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: const FrameConstructionData* data = aItem.mFCData; michael@0: NS_ASSERTION(data, "Must have frame construction data"); michael@0: michael@0: uint32_t bits = data->mBits; michael@0: michael@0: NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER), michael@0: "Should have dealt with this inside the data finder"); michael@0: michael@0: // Some sets of bits are not compatible with each other michael@0: #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \ michael@0: NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \ michael@0: "Only one of these bits should be set") michael@0: CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_FORCE_NULL_ABSPOS_CONTAINER); michael@0: CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS); michael@0: CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_MAY_NEED_SCROLLFRAME); michael@0: CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP); michael@0: CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH); michael@0: CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, michael@0: FCDATA_DISALLOW_GENERATED_CONTENT); michael@0: CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES); michael@0: CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, michael@0: FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS); michael@0: CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS, michael@0: FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS); michael@0: #undef CHECK_ONLY_ONE_BIT michael@0: NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) || michael@0: ((bits & FCDATA_FUNC_IS_FULL_CTOR) && michael@0: data->mFullConstructor == michael@0: &nsCSSFrameConstructor::ConstructNonScrollableBlock), michael@0: "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag"); michael@0: michael@0: // Don't create a subdocument frame for iframes if we're creating extra frames michael@0: if (aState.mCreatingExtraFrames && aItem.mContent->IsHTML() && michael@0: aItem.mContent->Tag() == nsGkAtoms::iframe) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: const nsStyleDisplay* display = styleContext->StyleDisplay(); michael@0: nsIContent* const content = aItem.mContent; michael@0: michael@0: // Get the parent of the content and check if it is a XBL children element. michael@0: // Push the children element as an ancestor here because it does michael@0: // not have a frame and would not otherwise be pushed as an ancestor. It is michael@0: // necessary to do so in order to correctly handle style resolution on michael@0: // descendants. michael@0: nsIContent* parent = content->GetParent(); michael@0: TreeMatchContext::AutoAncestorPusher michael@0: insertionPointPusher(aState.mTreeMatchContext); michael@0: if (parent && nsContentUtils::IsContentInsertionPoint(parent)) { michael@0: if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { michael@0: insertionPointPusher.PushAncestorAndStyleScope(parent); michael@0: } else { michael@0: insertionPointPusher.PushStyleScope(parent); michael@0: } michael@0: } michael@0: michael@0: // Push the content as a style ancestor now, so we don't have to do michael@0: // it in our various full-constructor functions. In particular, michael@0: // since a number of full-constructor functions don't actually call michael@0: // ProcessChildren in some cases (e.g. for CSS anonymous table boxes michael@0: // or for situations where only anonymouse children are having michael@0: // frames constructed), this is the best place to bottleneck the michael@0: // pushing of the content instead of having to do it in multiple michael@0: // places. michael@0: TreeMatchContext::AutoAncestorPusher michael@0: ancestorPusher(aState.mTreeMatchContext); michael@0: if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { michael@0: ancestorPusher.PushAncestorAndStyleScope(content); michael@0: } else { michael@0: ancestorPusher.PushStyleScope(content); michael@0: } michael@0: michael@0: nsIFrame* newFrame; michael@0: nsIFrame* primaryFrame; michael@0: if (bits & FCDATA_FUNC_IS_FULL_CTOR) { michael@0: newFrame = michael@0: (this->*(data->mFullConstructor))(aState, aItem, aParentFrame, michael@0: display, aFrameItems); michael@0: MOZ_ASSERT(newFrame, "Full constructor failed"); michael@0: primaryFrame = newFrame; michael@0: } else { michael@0: newFrame = michael@0: (*data->mFunc.mCreationFunc)(mPresShell, styleContext); michael@0: michael@0: bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW); michael@0: bool isPopup = aItem.mIsPopup; michael@0: NS_ASSERTION(!isPopup || michael@0: (aState.mPopupItems.containingBlock && michael@0: aState.mPopupItems.containingBlock->GetType() == michael@0: nsGkAtoms::popupSetFrame), michael@0: "Should have a containing block here!"); michael@0: michael@0: nsIFrame* geometricParent = michael@0: isPopup ? aState.mPopupItems.containingBlock : michael@0: (allowOutOfFlow ? aState.GetGeometricParent(display, aParentFrame) michael@0: : aParentFrame); michael@0: michael@0: // Must init frameToAddToList to null, since it's inout michael@0: nsIFrame* frameToAddToList = nullptr; michael@0: if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) && michael@0: display->IsScrollableOverflow()) { michael@0: BuildScrollFrame(aState, content, styleContext, newFrame, michael@0: geometricParent, frameToAddToList); michael@0: } else { michael@0: InitAndRestoreFrame(aState, content, geometricParent, newFrame); michael@0: // See whether we need to create a view michael@0: nsContainerFrame::CreateViewForFrame(newFrame, false); michael@0: frameToAddToList = newFrame; michael@0: } michael@0: michael@0: // Use frameToAddToList as the primary frame. In the non-scrollframe case michael@0: // they're equal, but in the scrollframe case newFrame is the scrolled michael@0: // frame, while frameToAddToList is the scrollframe (and should be the michael@0: // primary frame). michael@0: primaryFrame = frameToAddToList; michael@0: michael@0: // If we need to create a block formatting context to wrap our michael@0: // kids, do it now. michael@0: const nsStyleDisplay* maybeAbsoluteContainingBlockDisplay = display; michael@0: nsIFrame* maybeAbsoluteContainingBlock = newFrame; michael@0: nsIFrame* possiblyLeafFrame = newFrame; michael@0: if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) { michael@0: nsRefPtr blockContext; michael@0: blockContext = michael@0: mPresShell->StyleSet()->ResolveAnonymousBoxStyle(*data->mAnonBoxPseudo, michael@0: styleContext); michael@0: nsIFrame* blockFrame = michael@0: NS_NewBlockFormattingContext(mPresShell, blockContext); michael@0: michael@0: InitAndRestoreFrame(aState, content, newFrame, blockFrame); michael@0: michael@0: SetInitialSingleChild(newFrame, blockFrame); michael@0: michael@0: // Now figure out whether newFrame or blockFrame should be the michael@0: // absolute container. It should be the latter if it's michael@0: // positioned, otherwise the former. michael@0: const nsStyleDisplay* blockDisplay = blockContext->StyleDisplay(); michael@0: if (blockDisplay->IsPositioned(blockFrame)) { michael@0: maybeAbsoluteContainingBlockDisplay = blockDisplay; michael@0: maybeAbsoluteContainingBlock = blockFrame; michael@0: } michael@0: michael@0: // Our kids should go into the blockFrame michael@0: newFrame = blockFrame; michael@0: } michael@0: michael@0: aState.AddChild(frameToAddToList, aFrameItems, content, styleContext, michael@0: aParentFrame, allowOutOfFlow, allowOutOfFlow, isPopup); michael@0: michael@0: #ifdef MOZ_XUL michael@0: // Icky XUL stuff, sadly michael@0: michael@0: if (aItem.mIsRootPopupgroup) { michael@0: NS_ASSERTION(nsIRootBox::GetRootBox(mPresShell) && michael@0: nsIRootBox::GetRootBox(mPresShell)->GetPopupSetFrame() == michael@0: newFrame, michael@0: "Unexpected PopupSetFrame"); michael@0: aState.mPopupItems.containingBlock = newFrame; michael@0: aState.mHavePendingPopupgroup = false; michael@0: } michael@0: #endif /* MOZ_XUL */ michael@0: michael@0: // Process the child content if requested michael@0: nsFrameItems childItems; michael@0: nsFrameConstructorSaveState absoluteSaveState; michael@0: michael@0: if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) { michael@0: aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState); michael@0: } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) { michael@0: nsIFrame* cb = maybeAbsoluteContainingBlock; michael@0: cb->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: // This check is identical to nsStyleDisplay::IsPositioned except without michael@0: // the assertion that the style display and frame match. When constructing michael@0: // scroll frames we intentionally use the style display for the outer, but michael@0: // make the inner the containing block. michael@0: if ((maybeAbsoluteContainingBlockDisplay->IsAbsolutelyPositionedStyle() || michael@0: maybeAbsoluteContainingBlockDisplay->IsRelativelyPositionedStyle() || michael@0: (maybeAbsoluteContainingBlockDisplay->HasTransformStyle() && michael@0: cb->IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) || michael@0: maybeAbsoluteContainingBlockDisplay->HasPerspectiveStyle()) && michael@0: !cb->IsSVGText()) { michael@0: aState.PushAbsoluteContainingBlock(cb, cb, absoluteSaveState); michael@0: } michael@0: } michael@0: michael@0: if (!aItem.mAnonChildren.IsEmpty()) { michael@0: NS_ASSERTION(!(bits & FCDATA_USE_CHILD_ITEMS), michael@0: "We should not have both anonymous and non-anonymous " michael@0: "children in a given FrameConstructorItem"); michael@0: AddFCItemsForAnonymousContent(aState, newFrame, aItem.mAnonChildren, michael@0: aItem.mChildItems); michael@0: bits |= FCDATA_USE_CHILD_ITEMS; michael@0: } michael@0: michael@0: if (bits & FCDATA_USE_CHILD_ITEMS) { michael@0: nsFrameConstructorSaveState floatSaveState; michael@0: michael@0: if (ShouldSuppressFloatingOfDescendants(newFrame)) { michael@0: aState.PushFloatContainingBlock(nullptr, floatSaveState); michael@0: } else if (newFrame->IsFloatContainingBlock()) { michael@0: aState.PushFloatContainingBlock(newFrame, floatSaveState); michael@0: } michael@0: ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame, michael@0: childItems); michael@0: } else { michael@0: // Process the child frames. michael@0: ProcessChildren(aState, content, styleContext, newFrame, michael@0: !(bits & FCDATA_DISALLOW_GENERATED_CONTENT), michael@0: childItems, michael@0: (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0, michael@0: aItem.mPendingBinding, possiblyLeafFrame); michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: // More icky XUL stuff michael@0: if (aItem.mNameSpaceID == kNameSpaceID_XUL && michael@0: (aItem.mTag == nsGkAtoms::treechildren || // trees always need titletips michael@0: content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext) || michael@0: content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltip))) { michael@0: nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell); michael@0: if (rootBox) { michael@0: rootBox->AddTooltipSupport(content); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) { michael@0: nsFrameItems newItems; michael@0: nsFrameItems currentBlockItems; michael@0: nsIFrame* f; michael@0: while ((f = childItems.FirstChild()) != nullptr) { michael@0: bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f); michael@0: if (!wrapFrame) { michael@0: FlushAccumulatedBlock(aState, content, newFrame, michael@0: currentBlockItems, newItems); michael@0: } michael@0: michael@0: childItems.RemoveFrame(f); michael@0: if (wrapFrame) { michael@0: currentBlockItems.AddChild(f); michael@0: } else { michael@0: newItems.AddChild(f); michael@0: } michael@0: } michael@0: FlushAccumulatedBlock(aState, content, newFrame, michael@0: currentBlockItems, newItems); michael@0: michael@0: if (childItems.NotEmpty()) { michael@0: // an error must have occurred, delete unprocessed frames michael@0: childItems.DestroyFrames(); michael@0: } michael@0: michael@0: childItems = newItems; michael@0: } michael@0: michael@0: // Set the frame's initial child list michael@0: // Note that MathML depends on this being called even if michael@0: // childItems is empty! michael@0: newFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: } michael@0: michael@0: NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) == michael@0: ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0), michael@0: "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits"); michael@0: michael@0: if (aItem.mIsAnonymousContentCreatorContent) { michael@0: primaryFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT); michael@0: } michael@0: michael@0: // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for michael@0: // generated content that doesn't have one yet. Note that we have to examine michael@0: // the frame bit, because by this point mIsGeneratedContent has been cleared michael@0: // on aItem. michael@0: if ((!aState.mCreatingExtraFrames || michael@0: ((primaryFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) && michael@0: !aItem.mContent->GetPrimaryFrame())) && michael@0: !(bits & FCDATA_SKIP_FRAMESET)) { michael@0: aItem.mContent->SetPrimaryFrame(primaryFrame); michael@0: } michael@0: } michael@0: michael@0: // after the node has been constructed and initialized create any michael@0: // anonymous content a node needs. michael@0: nsresult michael@0: nsCSSFrameConstructor::CreateAnonymousFrames(nsFrameConstructorState& aState, michael@0: nsIContent* aParent, michael@0: nsIFrame* aParentFrame, michael@0: PendingBinding* aPendingBinding, michael@0: nsFrameItems& aChildItems) michael@0: { michael@0: nsAutoTArray newAnonymousItems; michael@0: nsresult rv = GetAnonymousContent(aParent, aParentFrame, newAnonymousItems); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t count = newAnonymousItems.Length(); michael@0: if (count == 0) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsFrameConstructorState::PendingBindingAutoPusher pusher(aState, michael@0: aPendingBinding); michael@0: TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext); michael@0: if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { michael@0: ancestorPusher.PushAncestorAndStyleScope(aParent->AsElement()); michael@0: } else { michael@0: ancestorPusher.PushStyleScope(aParent->AsElement()); michael@0: } michael@0: michael@0: nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame); michael@0: NS_ASSERTION(creator, michael@0: "How can that happen if we have nodes to construct frames for?"); michael@0: michael@0: for (uint32_t i=0; i < count; i++) { michael@0: nsIContent* content = newAnonymousItems[i].mContent; michael@0: NS_ASSERTION(content, "null anonymous content?"); michael@0: NS_ASSERTION(!newAnonymousItems[i].mStyleContext, "Unexpected style context"); michael@0: NS_ASSERTION(newAnonymousItems[i].mChildren.IsEmpty(), michael@0: "This method is not currently used with frames that implement " michael@0: "nsIAnonymousContentCreator::CreateAnonymousContent to " michael@0: "output a list where the items have their own children"); michael@0: michael@0: nsIFrame* newFrame = creator->CreateFrameFor(content); michael@0: if (newFrame) { michael@0: NS_ASSERTION(content->GetPrimaryFrame(), michael@0: "Content must have a primary frame now"); michael@0: newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT); michael@0: aChildItems.AddChild(newFrame); michael@0: } else { michael@0: FrameConstructionItemList items; michael@0: { michael@0: // Skip flex item style-fixup during our AddFrameConstructionItems() call: michael@0: TreeMatchContext::AutoFlexItemStyleFixupSkipper michael@0: flexItemStyleFixupSkipper(aState.mTreeMatchContext); michael@0: michael@0: AddFrameConstructionItems(aState, content, true, aParentFrame, items); michael@0: } michael@0: ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: SetFlagsOnSubtree(nsIContent *aNode, uintptr_t aFlagsToSet) michael@0: { michael@0: #ifdef DEBUG michael@0: // Make sure that the node passed to us doesn't have any XBL children michael@0: { michael@0: FlattenedChildIterator iter(aNode); michael@0: NS_ASSERTION(!iter.XBLInvolved() || !iter.GetNextChild(), michael@0: "The node should not have any XBL children"); michael@0: } michael@0: #endif michael@0: michael@0: // Set the flag on the node itself michael@0: aNode->SetFlags(aFlagsToSet); michael@0: michael@0: // Set the flag on all of its children recursively michael@0: uint32_t count; michael@0: nsIContent * const *children = aNode->GetChildArray(&count); michael@0: michael@0: for (uint32_t index = 0; index < count; ++index) { michael@0: SetFlagsOnSubtree(children[index], aFlagsToSet); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * This function takes a tree of nsIAnonymousContentCreator::ContentInfo michael@0: * objects where the nsIContent nodes have just been created, and appends the michael@0: * nsIContent children in the tree to their parent. The leaf nsIContent objects michael@0: * are appended first to minimize the number of notifications that are sent michael@0: * out (i.e. by appending as many descendants as posible while their parent is michael@0: * not yet in the document tree). michael@0: * michael@0: * This function is used simply as a convenience so that implementations of michael@0: * nsIAnonymousContentCreator::CreateAnonymousContent don't all have to have michael@0: * their own code to connect the elements that they create. michael@0: */ michael@0: static void michael@0: ConnectAnonymousTreeDescendants(nsIContent* aParent, michael@0: nsTArray& aContent) michael@0: { michael@0: uint32_t count = aContent.Length(); michael@0: for (uint32_t i=0; i < count; i++) { michael@0: nsIContent* content = aContent[i].mContent; michael@0: NS_ASSERTION(content, "null anonymous content?"); michael@0: michael@0: ConnectAnonymousTreeDescendants(content, aContent[i].mChildren); michael@0: michael@0: aParent->AppendChildTo(content, false); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, michael@0: nsIFrame* aParentFrame, michael@0: nsTArray& aContent) michael@0: { michael@0: nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame); michael@0: if (!creator) michael@0: return NS_OK; michael@0: michael@0: nsresult rv = creator->CreateAnonymousContent(aContent); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t count = aContent.Length(); michael@0: for (uint32_t i=0; i < count; i++) { michael@0: // get our child's content and set its parent to our content michael@0: nsIContent* content = aContent[i].mContent; michael@0: NS_ASSERTION(content, "null anonymous content?"); michael@0: michael@0: // least-surprise CSS binding until we do the SVG specified michael@0: // cascading rules for - bug 265894 michael@0: if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) { michael@0: content->SetFlags(NODE_IS_ANONYMOUS_ROOT); michael@0: } else { michael@0: content->SetIsNativeAnonymousRoot(); michael@0: } michael@0: michael@0: ConnectAnonymousTreeDescendants(content, aContent[i].mChildren); michael@0: michael@0: bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE); michael@0: rv = content->BindToTree(mDocument, aParent, aParent, true); michael@0: // If the anonymous content creator requested that the content should be michael@0: // editable, honor its request. michael@0: // We need to set the flag on the whole subtree, because existing michael@0: // children's flags have already been set as part of the BindToTree operation. michael@0: if (anonContentIsEditable) { michael@0: NS_ASSERTION(aParentFrame->GetType() == nsGkAtoms::textInputFrame, michael@0: "We only expect this for anonymous content under a text control frame"); michael@0: SetFlagsOnSubtree(content, NODE_IS_EDITABLE); michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: content->UnbindFromTree(); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static michael@0: bool IsXULDisplayType(const nsStyleDisplay* aDisplay) michael@0: { michael@0: return (aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX || michael@0: #ifdef MOZ_XUL michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_XUL_GRID || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK || michael@0: #endif michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_BOX michael@0: #ifdef MOZ_XUL michael@0: || aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_STACK || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID_GROUP || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_XUL_GRID_LINE || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_DECK || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_POPUP || michael@0: aDisplay->mDisplay == NS_STYLE_DISPLAY_GROUPBOX michael@0: #endif michael@0: ); michael@0: } michael@0: michael@0: michael@0: // XUL frames are not allowed to be out of flow. michael@0: #define SIMPLE_XUL_FCDATA(_func) \ michael@0: FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH, \ michael@0: _func) michael@0: #define SCROLLABLE_XUL_FCDATA(_func) \ michael@0: FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | \ michael@0: FCDATA_MAY_NEED_SCROLLFRAME, _func) michael@0: // .. but we allow some XUL frames to be _containers_ for out-of-flow content michael@0: // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH) michael@0: #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \ michael@0: FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \ michael@0: FCDATA_MAY_NEED_SCROLLFRAME, _func) michael@0: michael@0: #define SIMPLE_XUL_CREATE(_tag, _func) \ michael@0: { &nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) } michael@0: #define SCROLLABLE_XUL_CREATE(_tag, _func) \ michael@0: { &nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) } michael@0: #define SIMPLE_XUL_INT_CREATE(_int, _func) \ michael@0: { _int, SIMPLE_XUL_FCDATA(_func) } michael@0: #define SCROLLABLE_XUL_INT_CREATE(_int, _func) \ michael@0: { _int, SCROLLABLE_XUL_FCDATA(_func) } michael@0: #define SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(_int, _func) \ michael@0: { _int, SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) } michael@0: michael@0: static michael@0: nsIFrame* NS_NewGridBoxFrame(nsIPresShell* aPresShell, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: nsCOMPtr layout; michael@0: NS_NewGridLayout2(aPresShell, getter_AddRefs(layout)); michael@0: return NS_NewBoxFrame(aPresShell, aStyleContext, false, layout); michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindXULTagData(Element* aElement, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (aNameSpaceID != kNameSpaceID_XUL) { michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionDataByTag sXULTagData[] = { michael@0: #ifdef MOZ_XUL michael@0: SCROLLABLE_XUL_CREATE(button, NS_NewButtonBoxFrame), michael@0: SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame), michael@0: SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame), michael@0: SCROLLABLE_XUL_CREATE(autorepeatbutton, NS_NewAutoRepeatBoxFrame), michael@0: SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame), michael@0: SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame), michael@0: SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame), michael@0: SIMPLE_XUL_CREATE(spring, NS_NewLeafBoxFrame), michael@0: SIMPLE_XUL_CREATE(spacer, NS_NewLeafBoxFrame), michael@0: SIMPLE_XUL_CREATE(treechildren, NS_NewTreeBodyFrame), michael@0: SIMPLE_XUL_CREATE(treecol, NS_NewTreeColFrame), michael@0: SIMPLE_XUL_CREATE(text, NS_NewTextBoxFrame), michael@0: SIMPLE_TAG_CHAIN(label, nsCSSFrameConstructor::FindXULLabelData), michael@0: SIMPLE_TAG_CHAIN(description, nsCSSFrameConstructor::FindXULDescriptionData), michael@0: SIMPLE_XUL_CREATE(menu, NS_NewMenuFrame), michael@0: SIMPLE_XUL_CREATE(menubutton, NS_NewMenuFrame), michael@0: SIMPLE_XUL_CREATE(menuitem, NS_NewMenuItemFrame), michael@0: #ifdef XP_MACOSX michael@0: SIMPLE_TAG_CHAIN(menubar, nsCSSFrameConstructor::FindXULMenubarData), michael@0: #else michael@0: SIMPLE_XUL_CREATE(menubar, NS_NewMenuBarFrame), michael@0: #endif /* XP_MACOSX */ michael@0: SIMPLE_TAG_CHAIN(popupgroup, nsCSSFrameConstructor::FindPopupGroupData), michael@0: SIMPLE_XUL_CREATE(iframe, NS_NewSubDocumentFrame), michael@0: SIMPLE_XUL_CREATE(editor, NS_NewSubDocumentFrame), michael@0: SIMPLE_XUL_CREATE(browser, NS_NewSubDocumentFrame), michael@0: SIMPLE_XUL_CREATE(progressmeter, NS_NewProgressMeterFrame), michael@0: SIMPLE_XUL_CREATE(splitter, NS_NewSplitterFrame), michael@0: SIMPLE_TAG_CHAIN(listboxbody, michael@0: nsCSSFrameConstructor::FindXULListBoxBodyData), michael@0: SIMPLE_TAG_CHAIN(listitem, nsCSSFrameConstructor::FindXULListItemData), michael@0: #endif /* MOZ_XUL */ michael@0: SIMPLE_XUL_CREATE(slider, NS_NewSliderFrame), michael@0: SIMPLE_XUL_CREATE(scrollbar, NS_NewScrollbarFrame), michael@0: SIMPLE_XUL_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame) michael@0: }; michael@0: michael@0: return FindDataByTag(aTag, aElement, aStyleContext, sXULTagData, michael@0: ArrayLength(sXULTagData)); michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindPopupGroupData(Element* aElement, michael@0: nsStyleContext* /* unused */) michael@0: { michael@0: if (!aElement->IsRootOfNativeAnonymousSubtree()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionData sPopupSetData = michael@0: SIMPLE_XUL_FCDATA(NS_NewPopupSetFrame); michael@0: return &sPopupSetData; michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData michael@0: nsCSSFrameConstructor::sXULTextBoxData = SIMPLE_XUL_FCDATA(NS_NewTextBoxFrame); michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindXULLabelData(Element* aElement, michael@0: nsStyleContext* /* unused */) michael@0: { michael@0: if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) { michael@0: return &sXULTextBoxData; michael@0: } michael@0: michael@0: static const FrameConstructionData sLabelData = michael@0: SIMPLE_XUL_FCDATA(NS_NewXULLabelFrame); michael@0: return &sLabelData; michael@0: } michael@0: michael@0: static nsIFrame* michael@0: NS_NewXULDescriptionFrame(nsIPresShell* aPresShell, nsStyleContext *aContext) michael@0: { michael@0: // XXXbz do we really need to set those flags? If the parent is not michael@0: // a block we'll get them anyway, and if it is, do we want them? michael@0: return NS_NewBlockFrame(aPresShell, aContext, michael@0: NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT); michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindXULDescriptionData(Element* aElement, michael@0: nsStyleContext* /* unused */) michael@0: { michael@0: if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::value)) { michael@0: return &sXULTextBoxData; michael@0: } michael@0: michael@0: static const FrameConstructionData sDescriptionData = michael@0: SIMPLE_XUL_FCDATA(NS_NewXULDescriptionFrame); michael@0: return &sDescriptionData; michael@0: } michael@0: michael@0: #ifdef XP_MACOSX michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindXULMenubarData(Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: nsCOMPtr treeItem = michael@0: aStyleContext->PresContext()->GetDocShell(); michael@0: if (treeItem && nsIDocShellTreeItem::typeChrome == treeItem->ItemType()) { michael@0: nsCOMPtr parent; michael@0: treeItem->GetParent(getter_AddRefs(parent)); michael@0: if (!parent) { michael@0: // This is the root. Suppress the menubar, since on Mac michael@0: // window menus are not attached to the window. michael@0: static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA(); michael@0: return &sSuppressData; michael@0: } michael@0: } michael@0: michael@0: static const FrameConstructionData sMenubarData = michael@0: SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame); michael@0: return &sMenubarData; michael@0: } michael@0: #endif /* XP_MACOSX */ michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindXULListBoxBodyData(Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (aStyleContext->StyleDisplay()->mDisplay != michael@0: NS_STYLE_DISPLAY_XUL_GRID_GROUP) { michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionData sListBoxBodyData = michael@0: SCROLLABLE_XUL_FCDATA(NS_NewListBoxBodyFrame); michael@0: return &sListBoxBodyData; michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindXULListItemData(Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (aStyleContext->StyleDisplay()->mDisplay != michael@0: NS_STYLE_DISPLAY_XUL_GRID_LINE) { michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionData sListItemData = michael@0: SCROLLABLE_XUL_FCDATA(NS_NewListItemFrame); michael@0: return &sListItemData; michael@0: } michael@0: michael@0: #endif /* MOZ_XUL */ michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay* aDisplay, michael@0: Element* aElement, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: static const FrameConstructionDataByInt sXULDisplayData[] = { michael@0: SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_BOX, michael@0: NS_NewBoxFrame), michael@0: SCROLLABLE_ABSPOS_CONTAINER_XUL_INT_CREATE(NS_STYLE_DISPLAY_BOX, michael@0: NS_NewBoxFrame), michael@0: #ifdef MOZ_XUL michael@0: SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_XUL_GRID, NS_NewGridBoxFrame), michael@0: SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID, NS_NewGridBoxFrame), michael@0: SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID_GROUP, michael@0: NS_NewGridRowGroupFrame), michael@0: SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_XUL_GRID_LINE, michael@0: NS_NewGridRowLeafFrame), michael@0: SIMPLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_DECK, NS_NewDeckFrame), michael@0: SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_GROUPBOX, NS_NewGroupBoxFrame), michael@0: SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_INLINE_STACK, NS_NewStackFrame), michael@0: SCROLLABLE_XUL_INT_CREATE(NS_STYLE_DISPLAY_STACK, NS_NewStackFrame), michael@0: { NS_STYLE_DISPLAY_POPUP, michael@0: FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP | michael@0: FCDATA_SKIP_ABSPOS_PUSH, NS_NewMenuPopupFrame) } michael@0: #endif /* MOZ_XUL */ michael@0: }; michael@0: michael@0: // Processing by display here: michael@0: return FindDataByInt(aDisplay->mDisplay, aElement, aStyleContext, michael@0: sXULDisplayData, ArrayLength(sXULDisplayData)); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aContentStyle, michael@0: nsIFrame* aParentFrame, michael@0: nsIAtom* aScrolledPseudo, michael@0: bool aIsRoot, michael@0: nsIFrame*& aNewFrame) michael@0: { michael@0: nsIFrame* gfxScrollFrame = aNewFrame; michael@0: michael@0: nsFrameItems anonymousItems; michael@0: michael@0: nsRefPtr contentStyle = aContentStyle; michael@0: michael@0: if (!gfxScrollFrame) { michael@0: // Build a XULScrollFrame when the child is a box, otherwise an michael@0: // HTMLScrollFrame michael@0: // XXXbz this is the lone remaining consumer of IsXULDisplayType. michael@0: // I wonder whether we can eliminate that somehow. michael@0: const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay(); michael@0: if (IsXULDisplayType(displayStyle)) { michael@0: gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot, michael@0: displayStyle->mDisplay == NS_STYLE_DISPLAY_STACK || michael@0: displayStyle->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK); michael@0: } else { michael@0: gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot); michael@0: } michael@0: michael@0: InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame); michael@0: } michael@0: michael@0: // if there are any anonymous children for the scroll frame, create michael@0: // frames for them. michael@0: // Pass a null pending binding: we don't care how constructors for any of michael@0: // this anonymous content order with anything else. It's never been michael@0: // consistent anyway. michael@0: CreateAnonymousFrames(aState, aContent, gfxScrollFrame, nullptr, michael@0: anonymousItems); michael@0: michael@0: aNewFrame = gfxScrollFrame; michael@0: michael@0: // we used the style that was passed in. So resolve another one. michael@0: nsStyleSet *styleSet = mPresShell->StyleSet(); michael@0: nsRefPtr scrolledChildStyle = michael@0: styleSet->ResolveAnonymousBoxStyle(aScrolledPseudo, contentStyle); michael@0: michael@0: if (gfxScrollFrame) { michael@0: gfxScrollFrame->SetInitialChildList(kPrincipalList, anonymousItems); michael@0: } michael@0: michael@0: return scrolledChildStyle.forget(); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::FinishBuildingScrollFrame(nsIFrame* aScrollFrame, michael@0: nsIFrame* aScrolledFrame) michael@0: { michael@0: nsFrameList scrolled(aScrolledFrame, aScrolledFrame); michael@0: aScrollFrame->AppendFrames(kPrincipalList, scrolled); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Called to wrap a gfx scrollframe around a frame. The hierarchy will look like this michael@0: * michael@0: * ------- for gfx scrollbars ------ michael@0: * michael@0: * michael@0: * ScrollFrame michael@0: * ^ michael@0: * | michael@0: * Frame (scrolled frame you passed in) michael@0: * michael@0: * michael@0: *----------------------------------- michael@0: * LEGEND: michael@0: * michael@0: * ScrollFrame: This is a frame that manages gfx cross platform frame based scrollbars. michael@0: * michael@0: * @param aContent the content node of the child to wrap. michael@0: * @param aScrolledFrame The frame of the content to wrap. This should not be michael@0: * Initialized. This method will initialize it with a scrolled pseudo michael@0: * and no nsIContent. The content will be attached to the scrollframe michael@0: * returned. michael@0: * @param aContentStyle the style context that has already been resolved for the content being passed in. michael@0: * michael@0: * @param aParentFrame The parent to attach the scroll frame to michael@0: * michael@0: * @param aNewFrame The new scrollframe or gfx scrollframe that we create. It will contain the michael@0: * scrolled frame you passed in. (returned) michael@0: * If this is not null, we'll just use it michael@0: * @param aScrolledContentStyle the style that was resolved for the scrolled frame. (returned) michael@0: */ michael@0: nsresult michael@0: nsCSSFrameConstructor::BuildScrollFrame(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aContentStyle, michael@0: nsIFrame* aScrolledFrame, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame*& aNewFrame) michael@0: { michael@0: nsRefPtr scrolledContentStyle = michael@0: BeginBuildingScrollFrame(aState, aContent, aContentStyle, aParentFrame, michael@0: nsCSSAnonBoxes::scrolledContent, michael@0: false, aNewFrame); michael@0: michael@0: aScrolledFrame->SetStyleContextWithoutNotification(scrolledContentStyle); michael@0: InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame); michael@0: michael@0: FinishBuildingScrollFrame(aNewFrame, aScrolledFrame); michael@0: return NS_OK; michael@0: } michael@0: michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay, michael@0: Element* aElement, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: PR_STATIC_ASSERT(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET))); michael@0: michael@0: // The style system ensures that floated and positioned frames are michael@0: // block-level. michael@0: NS_ASSERTION(!(aDisplay->IsFloatingStyle() || michael@0: aDisplay->IsAbsolutelyPositionedStyle()) || michael@0: aDisplay->IsBlockOutsideStyle(), michael@0: "Style system did not apply CSS2.1 section 9.7 fixups"); michael@0: michael@0: // If this is "body", try propagating its scroll style to the viewport michael@0: // Note that we need to do this even if the body is NOT scrollable; michael@0: // it might have dynamically changed from scrollable to not scrollable, michael@0: // and that might need to be propagated. michael@0: // XXXbz is this the right place to do this? If this code moves, michael@0: // make this function static. michael@0: bool propagatedScrollToViewport = false; michael@0: if (aElement->IsHTML(nsGkAtoms::body)) { michael@0: propagatedScrollToViewport = michael@0: PropagateScrollToViewport() == aElement; michael@0: } michael@0: michael@0: NS_ASSERTION(!propagatedScrollToViewport || michael@0: !mPresShell->GetPresContext()->IsPaginated(), michael@0: "Shouldn't propagate scroll in paginated contexts"); michael@0: michael@0: // If the frame is a block-level frame and is scrollable, then wrap it in a michael@0: // scroll frame. michael@0: // XXX Ignore tables for the time being michael@0: // XXXbz it would be nice to combine this with the other block michael@0: // case... Think about how do do this? michael@0: if (aDisplay->IsBlockInsideStyle() && michael@0: aDisplay->IsScrollableOverflow() && michael@0: !propagatedScrollToViewport) { michael@0: // Except we don't want to do that for paginated contexts for michael@0: // frames that are block-outside and aren't frames for native michael@0: // anonymous stuff. michael@0: if (mPresShell->GetPresContext()->IsPaginated() && michael@0: aDisplay->IsBlockOutsideStyle() && michael@0: !aElement->IsInNativeAnonymousSubtree()) { michael@0: static const FrameConstructionData sForcedNonScrollableBlockData = michael@0: FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK, michael@0: &nsCSSFrameConstructor::ConstructNonScrollableBlock); michael@0: return &sForcedNonScrollableBlockData; michael@0: } michael@0: michael@0: static const FrameConstructionData sScrollableBlockData = michael@0: FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructScrollableBlock); michael@0: return &sScrollableBlockData; michael@0: } michael@0: michael@0: // Handle various non-scrollable blocks michael@0: if (aDisplay->IsBlockInsideStyle()) { michael@0: static const FrameConstructionData sNonScrollableBlockData = michael@0: FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructNonScrollableBlock); michael@0: return &sNonScrollableBlockData; michael@0: } michael@0: michael@0: static const FrameConstructionDataByInt sDisplayData[] = { michael@0: // To keep the hash table small don't add inline frames (they're michael@0: // typically things like FONT and B), because we can quickly michael@0: // find them if we need to. michael@0: // XXXbz the "quickly" part is a bald-faced lie! michael@0: { NS_STYLE_DISPLAY_INLINE, michael@0: FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT, michael@0: &nsCSSFrameConstructor::ConstructInline) }, michael@0: { NS_STYLE_DISPLAY_FLEX, michael@0: FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) }, michael@0: { NS_STYLE_DISPLAY_INLINE_FLEX, michael@0: FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) }, michael@0: { NS_STYLE_DISPLAY_GRID, michael@0: FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) }, michael@0: { NS_STYLE_DISPLAY_INLINE_GRID, michael@0: FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) }, michael@0: { NS_STYLE_DISPLAY_TABLE, michael@0: FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) }, michael@0: { NS_STYLE_DISPLAY_INLINE_TABLE, michael@0: FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) }, michael@0: // NOTE: In the unlikely event that we add another table-part here that has michael@0: // a desired-parent-type (& hence triggers table fixup), we'll need to also michael@0: // update the flexbox chunk in nsStyleContext::ApplyStyleFixups(). michael@0: { NS_STYLE_DISPLAY_TABLE_CAPTION, michael@0: FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_ALLOW_BLOCK_STYLES | michael@0: FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), michael@0: NS_NewTableCaptionFrame) }, michael@0: { NS_STYLE_DISPLAY_TABLE_ROW_GROUP, michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), michael@0: &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) }, michael@0: { NS_STYLE_DISPLAY_TABLE_HEADER_GROUP, michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), michael@0: &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) }, michael@0: { NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP, michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), michael@0: &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) }, michael@0: { NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP, michael@0: FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW | michael@0: FCDATA_SKIP_ABSPOS_PUSH | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), michael@0: NS_NewTableColGroupFrame) }, michael@0: { NS_STYLE_DISPLAY_TABLE_COLUMN, michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup), michael@0: &nsCSSFrameConstructor::ConstructTableCol) }, michael@0: { NS_STYLE_DISPLAY_TABLE_ROW, michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup), michael@0: &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) }, michael@0: { NS_STYLE_DISPLAY_TABLE_CELL, michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow), michael@0: &nsCSSFrameConstructor::ConstructTableCell) } michael@0: }; michael@0: michael@0: return FindDataByInt(aDisplay->mDisplay, michael@0: aElement, aStyleContext, sDisplayData, michael@0: ArrayLength(sDisplayData)); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructScrollableBlock(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: michael@0: nsIFrame* newFrame = nullptr; michael@0: nsRefPtr scrolledContentStyle michael@0: = BeginBuildingScrollFrame(aState, content, styleContext, michael@0: aState.GetGeometricParent(aDisplay, aParentFrame), michael@0: nsCSSAnonBoxes::scrolledContent, michael@0: false, newFrame); michael@0: michael@0: // Create our block frame michael@0: // pass a temporary stylecontext, the correct one will be set later michael@0: nsIFrame* scrolledFrame = michael@0: NS_NewBlockFormattingContext(mPresShell, styleContext); michael@0: michael@0: // Make sure to AddChild before we call ConstructBlock so that we michael@0: // end up before our descendants in fixed-pos lists as needed. michael@0: aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame); michael@0: michael@0: nsFrameItems blockItem; michael@0: ConstructBlock(aState, scrolledContentStyle->StyleDisplay(), content, michael@0: newFrame, newFrame, scrolledContentStyle, michael@0: &scrolledFrame, blockItem, michael@0: aDisplay->IsPositioned(newFrame) ? newFrame : nullptr, michael@0: aItem.mPendingBinding); michael@0: michael@0: NS_ASSERTION(blockItem.FirstChild() == scrolledFrame, michael@0: "Scrollframe's frameItems should be exactly the scrolled frame"); michael@0: FinishBuildingScrollFrame(newFrame, scrolledFrame); michael@0: michael@0: return newFrame; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructNonScrollableBlock(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: michael@0: // We want a block formatting context root in paginated contexts for michael@0: // every block that would be scrollable in a non-paginated context. michael@0: // We mark our blocks with a bit here if this condition is true, so michael@0: // we can check it later in nsFrame::ApplyPaginatedOverflowClipping. michael@0: bool clipPaginatedOverflow = michael@0: (aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0; michael@0: nsIFrame* newFrame; michael@0: if ((aDisplay->IsAbsolutelyPositionedStyle() || michael@0: aDisplay->IsFloatingStyle() || michael@0: NS_STYLE_DISPLAY_INLINE_BLOCK == aDisplay->mDisplay || michael@0: clipPaginatedOverflow) && michael@0: !aParentFrame->IsSVGText()) { michael@0: newFrame = NS_NewBlockFormattingContext(mPresShell, styleContext); michael@0: if (clipPaginatedOverflow) { michael@0: newFrame->AddStateBits(NS_BLOCK_CLIP_PAGINATED_OVERFLOW); michael@0: } michael@0: } else { michael@0: newFrame = NS_NewBlockFrame(mPresShell, styleContext); michael@0: } michael@0: michael@0: ConstructBlock(aState, aDisplay, aItem.mContent, michael@0: aState.GetGeometricParent(aDisplay, aParentFrame), michael@0: aParentFrame, styleContext, &newFrame, michael@0: aFrameItems, michael@0: aDisplay->IsPositioned(newFrame) ? newFrame : nullptr, michael@0: aItem.mPendingBinding); michael@0: return newFrame; michael@0: } michael@0: michael@0: michael@0: void michael@0: nsCSSFrameConstructor::InitAndRestoreFrame(const nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aNewFrame, michael@0: bool aAllowCounters) michael@0: { michael@0: NS_PRECONDITION(mUpdateCount != 0, michael@0: "Should be in an update while creating frames"); michael@0: michael@0: MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized"); michael@0: michael@0: // Initialize the frame michael@0: aNewFrame->Init(aContent, aParentFrame, nullptr); michael@0: aNewFrame->AddStateBits(aState.mAdditionalStateBits); michael@0: michael@0: if (aState.mFrameState) { michael@0: // Restore frame state for just the newly created frame. michael@0: RestoreFrameStateFor(aNewFrame, aState.mFrameState); michael@0: } michael@0: michael@0: if (aAllowCounters && michael@0: mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) { michael@0: CountersDirty(); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsCSSFrameConstructor::ResolveStyleContext(nsIFrame* aParentFrame, michael@0: nsIContent* aContent, michael@0: nsFrameConstructorState* aState) michael@0: { michael@0: nsStyleContext* parentStyleContext = nullptr; michael@0: NS_ASSERTION(aContent->GetParent(), "Must have parent here"); michael@0: michael@0: aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nullptr); michael@0: michael@0: if (aParentFrame) { michael@0: // Resolve the style context based on the content object and the parent michael@0: // style context michael@0: parentStyleContext = aParentFrame->StyleContext(); michael@0: } else { michael@0: // Perhaps aParentFrame is a canvasFrame and we're replicating michael@0: // fixed-pos frames. michael@0: // XXX should we create a way to tell ConstructFrame which style michael@0: // context to use, and pass it the style context for the michael@0: // previous page's fixed-pos frame? michael@0: } michael@0: michael@0: return ResolveStyleContext(parentStyleContext, aContent, aState); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext, michael@0: nsIContent* aContent, michael@0: nsFrameConstructorState* aState) michael@0: { michael@0: nsStyleSet *styleSet = mPresShell->StyleSet(); michael@0: aContent->OwnerDoc()->FlushPendingLinkUpdates(); michael@0: michael@0: if (aContent->IsElement()) { michael@0: if (aState) { michael@0: return styleSet->ResolveStyleFor(aContent->AsElement(), michael@0: aParentStyleContext, michael@0: aState->mTreeMatchContext); michael@0: } michael@0: return styleSet->ResolveStyleFor(aContent->AsElement(), aParentStyleContext); michael@0: michael@0: } michael@0: michael@0: NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), michael@0: "shouldn't waste time creating style contexts for " michael@0: "comments and processing instructions"); michael@0: michael@0: return styleSet->ResolveStyleForNonElement(aParentStyleContext); michael@0: } michael@0: michael@0: // MathML Mod - RBS michael@0: void michael@0: nsCSSFrameConstructor::FlushAccumulatedBlock(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aBlockItems, michael@0: nsFrameItems& aNewItems) michael@0: { michael@0: if (aBlockItems.IsEmpty()) { michael@0: // Nothing to do michael@0: return; michael@0: } michael@0: michael@0: nsIAtom* anonPseudo = nsCSSAnonBoxes::mozMathMLAnonymousBlock; michael@0: michael@0: nsStyleContext* parentContext = michael@0: nsFrame::CorrectStyleParentFrame(aParentFrame, michael@0: anonPseudo)->StyleContext(); michael@0: nsStyleSet* styleSet = mPresShell->StyleSet(); michael@0: nsRefPtr blockContext; michael@0: blockContext = styleSet-> michael@0: ResolveAnonymousBoxStyle(anonPseudo, parentContext); michael@0: michael@0: michael@0: // then, create a block frame that will wrap the child frames. Make it a michael@0: // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this michael@0: // is not a suitable block. michael@0: nsIFrame* blockFrame = michael@0: NS_NewMathMLmathBlockFrame(mPresShell, blockContext, michael@0: NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT); michael@0: michael@0: InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame); michael@0: ReparentFrames(this, blockFrame, aBlockItems); michael@0: // abs-pos and floats are disabled in MathML children so we don't have to michael@0: // worry about messing up those. michael@0: blockFrame->SetInitialChildList(kPrincipalList, aBlockItems); michael@0: NS_ASSERTION(aBlockItems.IsEmpty(), "What happened?"); michael@0: aBlockItems.Clear(); michael@0: aNewItems.AddChild(blockFrame); michael@0: } michael@0: michael@0: // Only elements can be floated or positioned. All other MathML michael@0: // should be in-flow. michael@0: #define SIMPLE_MATHML_CREATE(_tag, _func) \ michael@0: { &nsGkAtoms::_tag, \ michael@0: FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \ michael@0: FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \ michael@0: FCDATA_WRAP_KIDS_IN_BLOCKS, _func) } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindMathMLData(Element* aElement, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: // Make sure that we remain confined in the MathML world michael@0: if (aNameSpaceID != kNameSpaceID_MathML) michael@0: return nullptr; michael@0: michael@0: // Handle specially, because it sometimes produces inlines michael@0: if (aTag == nsGkAtoms::math) { michael@0: // This needs to match the test in EnsureBlockDisplay in michael@0: // nsRuleNode.cpp. Though the behavior here for the display:table michael@0: // case is pretty weird... michael@0: if (aStyleContext->StyleDisplay()->IsBlockOutsideStyle()) { michael@0: static const FrameConstructionData sBlockMathData = michael@0: FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER | michael@0: FCDATA_WRAP_KIDS_IN_BLOCKS, michael@0: NS_CreateNewMathMLmathBlockFrame); michael@0: return &sBlockMathData; michael@0: } michael@0: michael@0: static const FrameConstructionData sInlineMathData = michael@0: FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER | michael@0: FCDATA_IS_LINE_PARTICIPANT | michael@0: FCDATA_WRAP_KIDS_IN_BLOCKS, michael@0: NS_NewMathMLmathInlineFrame); michael@0: return &sInlineMathData; michael@0: } michael@0: michael@0: michael@0: static const FrameConstructionDataByTag sMathMLData[] = { michael@0: SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame), michael@0: SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame), michael@0: SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame), michael@0: SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame), michael@0: SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame), michael@0: SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame), michael@0: SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame), michael@0: SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame), michael@0: SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmmultiscriptsFrame), michael@0: SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmmultiscriptsFrame), michael@0: SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmmultiscriptsFrame), michael@0: SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame), michael@0: SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame), michael@0: SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame), michael@0: SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmphantomFrame), michael@0: SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame), michael@0: SIMPLE_MATHML_CREATE(mspace_, NS_NewMathMLmspaceFrame), michael@0: SIMPLE_MATHML_CREATE(none, NS_NewMathMLmspaceFrame), michael@0: SIMPLE_MATHML_CREATE(mprescripts_, NS_NewMathMLmspaceFrame), michael@0: SIMPLE_MATHML_CREATE(mfenced_, NS_NewMathMLmfencedFrame), michael@0: SIMPLE_MATHML_CREATE(mmultiscripts_, NS_NewMathMLmmultiscriptsFrame), michael@0: SIMPLE_MATHML_CREATE(mstyle_, NS_NewMathMLmrowFrame), michael@0: SIMPLE_MATHML_CREATE(msqrt_, NS_NewMathMLmsqrtFrame), michael@0: SIMPLE_MATHML_CREATE(mroot_, NS_NewMathMLmrootFrame), michael@0: SIMPLE_MATHML_CREATE(maction_, NS_NewMathMLmactionFrame), michael@0: SIMPLE_MATHML_CREATE(mrow_, NS_NewMathMLmrowFrame), michael@0: SIMPLE_MATHML_CREATE(merror_, NS_NewMathMLmrowFrame), michael@0: SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame), michael@0: SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame) michael@0: }; michael@0: michael@0: return FindDataByTag(aTag, aElement, aStyleContext, sMathMLData, michael@0: ArrayLength(sMathMLData)); michael@0: } michael@0: michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructFrameWithAnonymousChild( michael@0: nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems, michael@0: FrameCreationFunc aConstructor, michael@0: FrameCreationFunc aInnerConstructor, michael@0: nsICSSAnonBoxPseudo* aInnerPseudo, michael@0: bool aCandidateRootFrame) michael@0: { michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: michael@0: // Create the outer frame: michael@0: nsIFrame* newFrame = aConstructor(mPresShell, styleContext); michael@0: michael@0: InitAndRestoreFrame(aState, content, michael@0: aCandidateRootFrame ? michael@0: aState.GetGeometricParent(styleContext->StyleDisplay(), michael@0: aParentFrame) : michael@0: aParentFrame, michael@0: newFrame); michael@0: michael@0: // Create the pseudo SC for the anonymous wrapper child as a child of the SC: michael@0: nsRefPtr scForAnon; michael@0: scForAnon = mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(aInnerPseudo, styleContext); michael@0: michael@0: // Create the anonymous inner wrapper frame michael@0: nsIFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon); michael@0: michael@0: InitAndRestoreFrame(aState, content, newFrame, innerFrame); michael@0: michael@0: // Put the newly created frames into the right child list michael@0: SetInitialSingleChild(newFrame, innerFrame); michael@0: michael@0: aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame, michael@0: aCandidateRootFrame, aCandidateRootFrame); michael@0: michael@0: if (!mRootElementFrame && aCandidateRootFrame) { michael@0: // The frame we're constructing will be the root element frame. michael@0: // Set mRootElementFrame before processing children. michael@0: mRootElementFrame = newFrame; michael@0: } michael@0: michael@0: nsFrameItems childItems; michael@0: michael@0: // Process children michael@0: NS_ASSERTION(aItem.mAnonChildren.IsEmpty(), michael@0: "nsIAnonymousContentCreator::CreateAnonymousContent should not " michael@0: "be implemented for frames for which we explicitly create an " michael@0: "anonymous child to wrap its child frames"); michael@0: if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) { michael@0: ConstructFramesFromItemList(aState, aItem.mChildItems, michael@0: innerFrame, childItems); michael@0: } else { michael@0: ProcessChildren(aState, content, styleContext, innerFrame, michael@0: true, childItems, false, aItem.mPendingBinding); michael@0: } michael@0: michael@0: // Set the inner wrapper frame's initial primary list michael@0: innerFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: michael@0: return newFrame; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructOuterSVG(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: return ConstructFrameWithAnonymousChild( michael@0: aState, aItem, aParentFrame, aDisplay, aFrameItems, michael@0: NS_NewSVGOuterSVGFrame, NS_NewSVGOuterSVGAnonChildFrame, michael@0: nsCSSAnonBoxes::mozSVGOuterSVGAnonChild, true); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructMarker(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: return ConstructFrameWithAnonymousChild( michael@0: aState, aItem, aParentFrame, aDisplay, aFrameItems, michael@0: NS_NewSVGMarkerFrame, NS_NewSVGMarkerAnonChildFrame, michael@0: nsCSSAnonBoxes::mozSVGMarkerAnonChild, false); michael@0: } michael@0: michael@0: // Only outer elements can be floated or positioned. All other SVG michael@0: // should be in-flow. michael@0: #define SIMPLE_SVG_FCDATA(_func) \ michael@0: FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | \ michael@0: FCDATA_SKIP_ABSPOS_PUSH | \ michael@0: FCDATA_DISALLOW_GENERATED_CONTENT, _func) michael@0: #define SIMPLE_SVG_CREATE(_tag, _func) \ michael@0: { &nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) } michael@0: michael@0: static bool michael@0: IsFilterPrimitiveChildTag(const nsIAtom* aTag) michael@0: { michael@0: return aTag == nsGkAtoms::feDistantLight || michael@0: aTag == nsGkAtoms::fePointLight || michael@0: aTag == nsGkAtoms::feSpotLight || michael@0: aTag == nsGkAtoms::feFuncR || michael@0: aTag == nsGkAtoms::feFuncG || michael@0: aTag == nsGkAtoms::feFuncB || michael@0: aTag == nsGkAtoms::feFuncA || michael@0: aTag == nsGkAtoms::feMergeNode; michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::FrameConstructionData* michael@0: nsCSSFrameConstructor::FindSVGData(Element* aElement, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: nsIFrame* aParentFrame, michael@0: bool aIsWithinSVGText, michael@0: bool aAllowsTextPathChild, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (aNameSpaceID != kNameSpaceID_SVG) { michael@0: return nullptr; michael@0: } michael@0: michael@0: static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA(); michael@0: static const FrameConstructionData sContainerData = michael@0: SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame); michael@0: michael@0: bool parentIsSVG = aIsWithinSVGText; michael@0: nsIContent* parentContent = michael@0: aParentFrame ? aParentFrame->GetContent() : nullptr; michael@0: // XXXbz should this really be based on the XBL-resolved tag of the parent michael@0: // frame's content? Should it not be based on the type of the parent frame michael@0: // (e.g. whether it's an SVG frame)? michael@0: if (parentContent) { michael@0: int32_t parentNSID; michael@0: nsIAtom* parentTag = michael@0: parentContent->OwnerDoc()->BindingManager()-> michael@0: ResolveTag(parentContent, &parentNSID); michael@0: michael@0: // It's not clear whether the SVG spec intends to allow any SVG michael@0: // content within svg:foreignObject at all (SVG 1.1, section michael@0: // 23.2), but if it does, it better be svg:svg. So given that michael@0: // we're allowing it, treat it as a non-SVG parent. michael@0: parentIsSVG = parentNSID == kNameSpaceID_SVG && michael@0: parentTag != nsGkAtoms::foreignObject; michael@0: } michael@0: michael@0: if ((aTag != nsGkAtoms::svg && !parentIsSVG) || michael@0: (aTag == nsGkAtoms::desc || aTag == nsGkAtoms::title)) { michael@0: // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than michael@0: // svg:svg not contained within svg:svg are incorrect, although they michael@0: // don't seem to specify error handling. Ignore them, since many of michael@0: // our frame classes can't deal. It *may* be that the document michael@0: // should at that point be considered in error according to F.2, but michael@0: // it's hard to tell. michael@0: // michael@0: // Style mutation can't change this situation, so don't bother michael@0: // adding to the undisplayed content map. michael@0: // michael@0: // We don't currently handle any UI for desc/title michael@0: return &sSuppressData; michael@0: } michael@0: michael@0: // We don't need frames for animation elements michael@0: if (aElement->IsNodeOfType(nsINode::eANIMATION)) { michael@0: return &sSuppressData; michael@0: } michael@0: michael@0: if (aTag == nsGkAtoms::svg && !parentIsSVG) { michael@0: // We need outer elements to have an nsSVGOuterSVGFrame regardless michael@0: // of whether they fail conditional processing attributes, since various michael@0: // SVG frames assume that one exists. We handle the non-rendering michael@0: // of failing outer element contents like statements, michael@0: // and do the PassesConditionalProcessingTests call in michael@0: // nsSVGOuterSVGFrame::Init. michael@0: static const FrameConstructionData sOuterSVGData = michael@0: FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructOuterSVG); michael@0: return &sOuterSVGData; michael@0: } michael@0: michael@0: if (aTag == nsGkAtoms::marker) { michael@0: static const FrameConstructionData sMarkerSVGData = michael@0: FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructMarker); michael@0: return &sMarkerSVGData; michael@0: } michael@0: michael@0: nsCOMPtr tests(do_QueryInterface(aElement)); michael@0: if (tests && !tests->PassesConditionalProcessingTests()) { michael@0: // Elements with failing conditional processing attributes never get michael@0: // rendered. Note that this is not where we select which frame in a michael@0: // to render! That happens in nsSVGSwitchFrame::PaintSVG. michael@0: return &sContainerData; michael@0: } michael@0: michael@0: // Prevent bad frame types being children of filters or parents of filter michael@0: // primitives. If aParentFrame is null, we know that the frame that will michael@0: // be created will be an nsInlineFrame, so it can never be a filter. michael@0: bool parentIsFilter = aParentFrame && michael@0: aParentFrame->GetType() == nsGkAtoms::svgFilterFrame; michael@0: bool filterPrimitive = aElement->IsNodeOfType(nsINode::eFILTER); michael@0: if ((parentIsFilter && !filterPrimitive) || michael@0: (!parentIsFilter && filterPrimitive)) { michael@0: return &sSuppressData; michael@0: } michael@0: michael@0: // Prevent bad frame types being children of filter primitives or parents of michael@0: // filter primitive children. If aParentFrame is null, we know that the frame michael@0: // that will be created will be an nsInlineFrame, so it can never be a filter michael@0: // primitive. michael@0: bool parentIsFEContainerFrame = aParentFrame && michael@0: aParentFrame->GetType() == nsGkAtoms::svgFEContainerFrame; michael@0: if ((parentIsFEContainerFrame && !IsFilterPrimitiveChildTag(aTag)) || michael@0: (!parentIsFEContainerFrame && IsFilterPrimitiveChildTag(aTag))) { michael@0: return &sSuppressData; michael@0: } michael@0: michael@0: // Special cases for text/tspan/textPath, because the kind of frame michael@0: // they get depends on the parent frame. We ignore 'a' elements when michael@0: // determining the parent, however. michael@0: if (aIsWithinSVGText) { michael@0: // If aIsWithinSVGText is true, then we know that the "SVG text uses michael@0: // CSS frames" pref was true when this SVG fragment was first constructed. michael@0: michael@0: // We don't use ConstructInline because we want different behavior michael@0: // for generated content. michael@0: static const FrameConstructionData sTSpanData = michael@0: FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | michael@0: FCDATA_SKIP_ABSPOS_PUSH | michael@0: FCDATA_DISALLOW_GENERATED_CONTENT | michael@0: FCDATA_IS_LINE_PARTICIPANT | michael@0: FCDATA_IS_INLINE | michael@0: FCDATA_USE_CHILD_ITEMS, michael@0: NS_NewInlineFrame); michael@0: if (aTag == nsGkAtoms::textPath) { michael@0: if (aAllowsTextPathChild) { michael@0: return &sTSpanData; michael@0: } michael@0: } else if (aTag == nsGkAtoms::tspan || michael@0: aTag == nsGkAtoms::altGlyph || michael@0: aTag == nsGkAtoms::a) { michael@0: return &sTSpanData; michael@0: } michael@0: return &sSuppressData; michael@0: } else if (aTag == nsGkAtoms::text) { michael@0: static const FrameConstructionData sTextData = michael@0: FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW | michael@0: FCDATA_ALLOW_BLOCK_STYLES, michael@0: NS_NewSVGTextFrame, michael@0: nsCSSAnonBoxes::mozSVGText); michael@0: return &sTextData; michael@0: } else if (aTag == nsGkAtoms::tspan || michael@0: aTag == nsGkAtoms::altGlyph || michael@0: aTag == nsGkAtoms::textPath) { michael@0: return &sSuppressData; michael@0: } michael@0: michael@0: static const FrameConstructionDataByTag sSVGData[] = { michael@0: SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame), michael@0: SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame), michael@0: SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame), michael@0: SIMPLE_SVG_CREATE(polygon, NS_NewSVGPathGeometryFrame), michael@0: SIMPLE_SVG_CREATE(polyline, NS_NewSVGPathGeometryFrame), michael@0: SIMPLE_SVG_CREATE(circle, NS_NewSVGPathGeometryFrame), michael@0: SIMPLE_SVG_CREATE(ellipse, NS_NewSVGPathGeometryFrame), michael@0: SIMPLE_SVG_CREATE(line, NS_NewSVGPathGeometryFrame), michael@0: SIMPLE_SVG_CREATE(rect, NS_NewSVGPathGeometryFrame), michael@0: SIMPLE_SVG_CREATE(path, NS_NewSVGPathGeometryFrame), michael@0: SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame), michael@0: SIMPLE_SVG_CREATE(generic_, NS_NewSVGGenericContainerFrame), michael@0: { &nsGkAtoms::foreignObject, michael@0: FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW, michael@0: NS_NewSVGForeignObjectFrame, michael@0: nsCSSAnonBoxes::mozSVGForeignContent) }, michael@0: SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame), michael@0: SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame), michael@0: SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame), michael@0: SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame), michael@0: SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame), michael@0: SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame), michael@0: SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame), michael@0: SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame), michael@0: SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame), michael@0: SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame), michael@0: SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame), michael@0: SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame), michael@0: SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame), michael@0: SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame), michael@0: SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame), michael@0: SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame), michael@0: SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame), michael@0: SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame), michael@0: SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame), michael@0: SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame), michael@0: SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame), michael@0: SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame), michael@0: SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame), michael@0: SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame), michael@0: SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame), michael@0: SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame) michael@0: }; michael@0: michael@0: const FrameConstructionData* data = michael@0: FindDataByTag(aTag, aElement, aStyleContext, sSVGData, michael@0: ArrayLength(sSVGData)); michael@0: michael@0: if (!data) { michael@0: data = &sContainerData; michael@0: } michael@0: michael@0: return data; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::AddPageBreakItem(nsIContent* aContent, michael@0: nsStyleContext* aMainStyleContext, michael@0: FrameConstructionItemList& aItems) michael@0: { michael@0: // Use the same parent style context that |aMainStyleContext| has, since michael@0: // that's easier to re-resolve and it doesn't matter in practice. michael@0: // (Getting different parents can result in framechange hints, e.g., michael@0: // for user-modify.) michael@0: nsRefPtr pseudoStyle = michael@0: mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(nsCSSAnonBoxes::pageBreak, michael@0: aMainStyleContext->GetParent()); michael@0: michael@0: NS_ASSERTION(pseudoStyle->StyleDisplay()->mDisplay == michael@0: NS_STYLE_DISPLAY_BLOCK, "Unexpected display"); michael@0: michael@0: static const FrameConstructionData sPageBreakData = michael@0: FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame); michael@0: michael@0: // Lie about the tag and namespace so we don't trigger anything michael@0: // interesting during frame construction. michael@0: aItems.AppendItem(&sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak, michael@0: kNameSpaceID_None, nullptr, pseudoStyle.forget(), michael@0: true, nullptr); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: bool aSuppressWhiteSpaceOptimizations, michael@0: nsIFrame* aParentFrame, michael@0: FrameConstructionItemList& aItems) michael@0: { michael@0: aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); michael@0: if (aContent->IsElement()) { michael@0: // We can't just remove our pending restyle flags, since we may michael@0: // have restyle-later-siblings set on us. But we _can_ remove the michael@0: // "is possible restyle root" flags, and need to. Otherwise we can michael@0: // end up with stale such flags (e.g. if we used to have a michael@0: // display:none parent when our last restyle was posted and michael@0: // processed and now no longer do). michael@0: aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS & michael@0: ~ELEMENT_PENDING_RESTYLE_FLAGS); michael@0: } michael@0: michael@0: // XXX the GetContent() != aContent check is needed due to bug 135040. michael@0: // Remove it once that's fixed. michael@0: if (aContent->GetPrimaryFrame() && michael@0: aContent->GetPrimaryFrame()->GetContent() == aContent && michael@0: !aState.mCreatingExtraFrames) { michael@0: NS_ERROR("asked to create frame construction item for a node that already " michael@0: "has a frame"); michael@0: return; michael@0: } michael@0: michael@0: // don't create a whitespace frame if aParent doesn't want it michael@0: if (!NeedFrameFor(aState, aParentFrame, aContent)) { michael@0: return; michael@0: } michael@0: michael@0: // never create frames for comments or PIs michael@0: if (aContent->IsNodeOfType(nsINode::eCOMMENT) || michael@0: aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) michael@0: return; michael@0: michael@0: nsRefPtr styleContext; michael@0: styleContext = ResolveStyleContext(aParentFrame, aContent, &aState); michael@0: michael@0: uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK; michael@0: if (aParentFrame->IsSVGText()) { michael@0: flags |= ITEM_IS_WITHIN_SVG_TEXT; michael@0: } michael@0: if (aParentFrame->GetType() == nsGkAtoms::blockFrame && michael@0: aParentFrame->GetParent() && michael@0: aParentFrame->GetParent()->GetType() == nsGkAtoms::svgTextFrame) { michael@0: flags |= ITEM_ALLOWS_TEXT_PATH_CHILD; michael@0: } michael@0: AddFrameConstructionItemsInternal(aState, aContent, aParentFrame, michael@0: aContent->Tag(), aContent->GetNameSpaceID(), michael@0: aSuppressWhiteSpaceOptimizations, michael@0: styleContext, michael@0: flags, nullptr, michael@0: aItems); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsCSSFrameConstructor::SetAsUndisplayedContent(FrameConstructionItemList& aList, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: bool aIsGeneratedContent) michael@0: { michael@0: if (aStyleContext->GetPseudo()) { michael@0: if (aIsGeneratedContent) { michael@0: aContent->UnbindFromTree(); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type"); michael@0: aList.AppendUndisplayedItem(aContent, aStyleContext); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsIAtom* aTag, michael@0: int32_t aNameSpaceID, michael@0: bool aSuppressWhiteSpaceOptimizations, michael@0: nsStyleContext* aStyleContext, michael@0: uint32_t aFlags, michael@0: nsTArray* aAnonChildren, michael@0: FrameConstructionItemList& aItems) michael@0: { michael@0: NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT) || michael@0: aContent->IsElement(), michael@0: "Shouldn't get anything else here!"); michael@0: michael@0: // The following code allows the user to specify the base tag michael@0: // of an element using XBL. XUL and HTML objects (like boxes, menus, etc.) michael@0: // can then be extended arbitrarily. michael@0: const nsStyleDisplay* display = aStyleContext->StyleDisplay(); michael@0: nsRefPtr styleContext(aStyleContext); michael@0: PendingBinding* pendingBinding = nullptr; michael@0: if ((aFlags & ITEM_ALLOW_XBL_BASE) && display->mBinding) michael@0: { michael@0: // Ensure that our XBL bindings are installed. michael@0: michael@0: nsXBLService* xblService = nsXBLService::GetInstance(); michael@0: if (!xblService) michael@0: return; michael@0: michael@0: bool resolveStyle; michael@0: michael@0: nsAutoPtr newPendingBinding(new PendingBinding()); michael@0: michael@0: nsresult rv = xblService->LoadBindings(aContent, display->mBinding->GetURI(), michael@0: display->mBinding->mOriginPrincipal, michael@0: getter_AddRefs(newPendingBinding->mBinding), michael@0: &resolveStyle); michael@0: if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED) michael@0: return; michael@0: michael@0: if (newPendingBinding->mBinding) { michael@0: pendingBinding = newPendingBinding; michael@0: // aState takes over owning newPendingBinding michael@0: aState.AddPendingBinding(newPendingBinding.forget()); michael@0: } michael@0: michael@0: if (resolveStyle) { michael@0: styleContext = michael@0: ResolveStyleContext(styleContext->GetParent(), aContent, &aState); michael@0: display = styleContext->StyleDisplay(); michael@0: aStyleContext = styleContext; michael@0: } michael@0: michael@0: aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID); michael@0: } michael@0: michael@0: bool isGeneratedContent = ((aFlags & ITEM_IS_GENERATED_CONTENT) != 0); michael@0: michael@0: // Pre-check for display "none" - if we find that, don't create michael@0: // any frame at all michael@0: if (NS_STYLE_DISPLAY_NONE == display->mDisplay) { michael@0: SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent); michael@0: return; michael@0: } michael@0: michael@0: bool isText = !aContent->IsElement(); michael@0: michael@0: // never create frames for non-option/optgroup kids of
so no need to check it michael@0: items.SetLineBoundaryAtEnd(!parentAfterFrame || michael@0: !parentAfterFrame->IsInlineOutside()); michael@0: } michael@0: // To suppress whitespace-only text frames, we have to verify that michael@0: // our container's DOM child list matches its flattened tree child list. michael@0: items.SetParentHasNoXBLChildren(haveNoXBLChildren); michael@0: michael@0: nsFrameItems frameItems; michael@0: ConstructFramesFromItemList(state, items, parentFrame, frameItems); michael@0: michael@0: for (nsIContent* child = aFirstNewContent; michael@0: child; michael@0: child = child->GetNextSibling()) { michael@0: // Invalidate now instead of before the WipeContainingBlock call, just in michael@0: // case we do wipe; in that case we don't need to do this walk at all. michael@0: // XXXbz does that matter? Would it make more sense to save some virtual michael@0: // GetChildAt calls instead and do this during construction of our michael@0: // FrameConstructionItemList? michael@0: InvalidateCanvasIfNeeded(mPresShell, child); michael@0: } michael@0: michael@0: // if the container is a table and a caption was appended, it needs to be put michael@0: // in the outer table frame's additional child list. michael@0: nsFrameItems captionItems; michael@0: if (nsGkAtoms::tableFrame == frameType) { michael@0: // Pull out the captions. Note that we don't want to do that as we go, michael@0: // because processing a single caption can add a whole bunch of things to michael@0: // the frame items due to pseudoframe processing. So we'd have to pull michael@0: // captions from a list anyway; might as well do that here. michael@0: // XXXbz this is no longer true; we could pull captions directly out of the michael@0: // FrameConstructionItemList now. michael@0: PullOutCaptionFrames(frameItems, captionItems); michael@0: } michael@0: michael@0: if (haveFirstLineStyle && parentFrame == containingBlock) { michael@0: // It's possible that some of the new frames go into a michael@0: // first-line frame. Look at them and see... michael@0: AppendFirstLineFrames(state, containingBlock->GetContent(), michael@0: containingBlock, frameItems); michael@0: } michael@0: michael@0: // Notify the parent frame passing it the list of new frames michael@0: // Append the flowed frames to the principal child list; captions michael@0: // need special treatment michael@0: if (captionItems.NotEmpty()) { // append the caption to the outer table michael@0: NS_ASSERTION(nsGkAtoms::tableFrame == frameType, "how did that happen?"); michael@0: nsIFrame* outerTable = parentFrame->GetParent(); michael@0: AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems); michael@0: } michael@0: michael@0: if (frameItems.NotEmpty()) { // append the in-flow kids michael@0: AppendFramesToParent(state, parentFrame, frameItems, prevSibling); michael@0: } michael@0: michael@0: // Recover first-letter frames michael@0: if (haveFirstLetterStyle) { michael@0: RecoverLetterFrames(containingBlock); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: if (gReallyNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::ContentAppended: resulting frame model:\n"); michael@0: parentFrame->List(stdout, 0); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: nsAccessibilityService* accService = nsIPresShell::AccService(); michael@0: if (accService) { michael@0: accService->ContentRangeInserted(mPresShell, aContainer, michael@0: aFirstNewContent, nullptr); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: michael@0: enum content_operation michael@0: { michael@0: CONTENT_INSERTED, michael@0: CONTENT_REMOVED michael@0: }; michael@0: michael@0: // Helper function to lookup the listbox body frame and send a notification michael@0: // for insertion or removal of content michael@0: static michael@0: bool NotifyListBoxBody(nsPresContext* aPresContext, michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: // Only used for the removed notification michael@0: nsIContent* aOldNextSibling, michael@0: nsIDocument* aDocument, michael@0: nsIFrame* aChildFrame, michael@0: content_operation aOperation) michael@0: { michael@0: nsListBoxBodyFrame* listBoxBodyFrame = michael@0: MaybeGetListBoxBodyFrame(aContainer, aChild); michael@0: if (listBoxBodyFrame) { michael@0: if (aOperation == CONTENT_REMOVED) { michael@0: // Except if we have an aChildFrame and its parent is not the right michael@0: // thing, then we don't do this. Pseudo frames are so much fun.... michael@0: if (!aChildFrame || aChildFrame->GetParent() == listBoxBodyFrame) { michael@0: listBoxBodyFrame->OnContentRemoved(aPresContext, aContainer, michael@0: aChildFrame, aOldNextSibling); michael@0: return true; michael@0: } michael@0: } else { michael@0: listBoxBodyFrame->OnContentInserted(aPresContext, aChild); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: #endif // MOZ_XUL michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: nsILayoutHistoryState* aFrameState, michael@0: bool aAllowLazyConstruction) michael@0: { michael@0: return ContentRangeInserted(aContainer, michael@0: aChild, michael@0: aChild->GetNextSibling(), michael@0: aFrameState, michael@0: aAllowLazyConstruction); michael@0: } michael@0: michael@0: // ContentRangeInserted handles creating frames for a range of nodes that michael@0: // aren't at the end of their childlist. ContentRangeInserted isn't a real michael@0: // content notification, but rather it handles regular ContentInserted calls michael@0: // for a single node as well as the lazy construction of frames for a range of michael@0: // nodes when called from CreateNeededFrames. For a range of nodes to be michael@0: // suitable to have its frames constructed all at once they must meet the same michael@0: // conditions that ContentAppended imposes (GetRangeInsertionPoint checks michael@0: // these), plus more. Namely when finding the insertion prevsibling we must not michael@0: // need to consult something specific to any one node in the range, so that the michael@0: // insertion prevsibling would be the same for each node in the range. So we michael@0: // pass the first node in the range to GetInsertionPrevSibling, and if michael@0: // IsValidSibling (the only place GetInsertionPrevSibling might look at the michael@0: // passed in node itself) needs to resolve style on the node we record this and michael@0: // return that this range needs to be split up and inserted separately. Table michael@0: // captions need extra attention as we need to determine where to insert them michael@0: // in the caption list, while skipping any nodes in the range being inserted michael@0: // (because when we treat the caption frames the other nodes have had their michael@0: // frames constructed but not yet inserted into the frame tree). michael@0: nsresult michael@0: nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, michael@0: nsIContent* aStartChild, michael@0: nsIContent* aEndChild, michael@0: nsILayoutHistoryState* aFrameState, michael@0: bool aAllowLazyConstruction) michael@0: { michael@0: AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); michael@0: NS_PRECONDITION(mUpdateCount != 0, michael@0: "Should be in an update while creating frames"); michael@0: michael@0: NS_PRECONDITION(aStartChild, "must always pass a child"); michael@0: michael@0: // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and michael@0: // the :empty pseudo-class? michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::ContentRangeInserted container=%p " michael@0: "start-child=%p end-child=%p lazy=%d\n", michael@0: static_cast(aContainer), michael@0: static_cast(aStartChild), static_cast(aEndChild), michael@0: aAllowLazyConstruction); michael@0: if (gReallyNoisyContentUpdates) { michael@0: if (aContainer) { michael@0: aContainer->List(stdout,0); michael@0: } else { michael@0: aStartChild->List(stdout, 0); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: for (nsIContent* child = aStartChild; michael@0: child != aEndChild; michael@0: child = child->GetNextSibling()) { michael@0: // XXX the GetContent() != child check is needed due to bug 135040. michael@0: // Remove it once that's fixed. michael@0: NS_ASSERTION(!child->GetPrimaryFrame() || michael@0: child->GetPrimaryFrame()->GetContent() != child, michael@0: "asked to construct a frame for a node that already has a frame"); michael@0: } michael@0: #endif michael@0: michael@0: bool isSingleInsert = (aStartChild->GetNextSibling() == aEndChild); michael@0: NS_ASSERTION(isSingleInsert || !aAllowLazyConstruction, michael@0: "range insert shouldn't be lazy"); michael@0: NS_ASSERTION(isSingleInsert || aEndChild, michael@0: "range should not include all nodes after aStartChild"); michael@0: michael@0: #ifdef MOZ_XUL michael@0: if (aContainer && IsXULListBox(aContainer)) { michael@0: if (isSingleInsert) { michael@0: if (NotifyListBoxBody(mPresShell->GetPresContext(), aContainer, michael@0: // The insert case in NotifyListBoxBody michael@0: // doesn't use "old next sibling". michael@0: aStartChild, nullptr, michael@0: mDocument, nullptr, CONTENT_INSERTED)) { michael@0: return NS_OK; michael@0: } michael@0: } else { michael@0: // We don't handle a range insert to a listbox parent, issue single michael@0: // ContertInserted calls for each node inserted. michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, michael@0: aAllowLazyConstruction); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: #endif // MOZ_XUL michael@0: michael@0: // If we have a null parent, then this must be the document element being michael@0: // inserted, or some other child of the document in the DOM (might be a PI, michael@0: // say). michael@0: if (! aContainer) { michael@0: NS_ASSERTION(isSingleInsert, michael@0: "root node insertion should be a single insertion"); michael@0: Element *docElement = mDocument->GetRootElement(); michael@0: michael@0: if (aStartChild != docElement) { michael@0: // Not the root element; just bail out michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_PRECONDITION(nullptr == mRootElementFrame, michael@0: "root element frame already created"); michael@0: michael@0: // Create frames for the document element and its child elements michael@0: nsIFrame* docElementFrame = michael@0: ConstructDocElementFrame(docElement, aFrameState); michael@0: michael@0: if (docElementFrame) { michael@0: InvalidateCanvasIfNeeded(mPresShell, aStartChild); michael@0: #ifdef DEBUG michael@0: if (gReallyNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame " michael@0: "model:\n"); michael@0: mFixedContainingBlock->List(stdout, 0); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: if (aFrameState) { michael@0: // Restore frame state for the root scroll frame if there is one michael@0: nsIFrame* rootScrollFrame = mPresShell->GetRootScrollFrame(); michael@0: if (rootScrollFrame) { michael@0: RestoreFrameStateFor(rootScrollFrame, aFrameState); michael@0: } michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: nsAccessibilityService* accService = nsIPresShell::AccService(); michael@0: if (accService) { michael@0: accService->ContentRangeInserted(mPresShell, aContainer, michael@0: aStartChild, aEndChild); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) { michael@0: // Recreate frames if content is inserted into a ShadowRoot michael@0: // because children of ShadowRoot are rendered in place of michael@0: // the children of the host. michael@0: nsIContent* bindingParent = aContainer->GetBindingParent(); michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: nsresult rv = RecreateFramesForContent(bindingParent, false); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return rv; michael@0: } michael@0: michael@0: nsIFrame* parentFrame = GetFrameFor(aContainer); michael@0: // The xbl:children element won't have a frame, but default content can have the children as michael@0: // a parent. While its uncommon to change the structure of the default content itself, a label, michael@0: // for example, can be reframed by having its value attribute set or removed. michael@0: if (!parentFrame && !aContainer->IsActiveChildrenElement()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Otherwise, we've got parent content. Find its frame. michael@0: NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer, "New XBL code is possibly wrong!"); michael@0: michael@0: if (aAllowLazyConstruction && michael@0: MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (isSingleInsert) { michael@0: // See if we have an XBL insertion point. If so, then that's our michael@0: // real parent frame; if not, then the frame hasn't been built yet michael@0: // and we just bail. michael@0: parentFrame = GetInsertionPoint(aContainer, aStartChild); michael@0: } else { michael@0: // Get our insertion point. If we need to issue single ContentInserted's michael@0: // GetRangeInsertionPoint will take care of that for us. michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: parentFrame = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild, michael@0: aAllowLazyConstruction); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: } michael@0: michael@0: if (!parentFrame) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool isAppend, isRangeInsertSafe; michael@0: nsIFrame* prevSibling = michael@0: GetInsertionPrevSibling(parentFrame, aContainer, aStartChild, michael@0: &isAppend, &isRangeInsertSafe); michael@0: michael@0: // check if range insert is safe michael@0: if (!isSingleInsert && !isRangeInsertSafe) { michael@0: // must fall back to a single ContertInserted for each child in the range michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, michael@0: aAllowLazyConstruction); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIContent* container = parentFrame->GetContent(); michael@0: michael@0: nsIAtom* frameType = parentFrame->GetType(); michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: if (MaybeRecreateForFrameset(parentFrame, aStartChild, aEndChild)) { michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return NS_OK; michael@0: } michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: michael@0: // We should only get here with fieldsets when doing a single insert, because michael@0: // fieldsets have multiple insertion points. michael@0: NS_ASSERTION(isSingleInsert || frameType != nsGkAtoms::fieldSetFrame, michael@0: "Unexpected parent"); michael@0: if (IsFrameForFieldSet(parentFrame, frameType) && michael@0: aStartChild->Tag() == nsGkAtoms::legend) { michael@0: // Just reframe the parent, since figuring out whether this michael@0: // should be the new legend and then handling it is too complex. michael@0: // We could do a little better here --- check if the fieldset already michael@0: // has a legend which occurs earlier in its child list than this node, michael@0: // and if so, proceed. But we'd have to extend nsFieldSetFrame michael@0: // to locate this legend in the inserted frames and extract it. michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return rv; michael@0: } michael@0: michael@0: // Don't construct kids of leaves michael@0: if (parentFrame->IsLeaf()) { michael@0: // Clear lazy bits so we don't try to construct again. michael@0: ClearLazyBits(aStartChild, aEndChild); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) { michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return rv; michael@0: } michael@0: michael@0: nsFrameConstructorState state(mPresShell, michael@0: GetAbsoluteContainingBlock(parentFrame, FIXED_POS), michael@0: GetAbsoluteContainingBlock(parentFrame, ABS_POS), michael@0: GetFloatContainingBlock(parentFrame), michael@0: aFrameState); michael@0: state.mTreeMatchContext.InitAncestors(aContainer ? michael@0: aContainer->AsElement() : michael@0: nullptr); michael@0: michael@0: // Recover state for the containing block - we need to know if michael@0: // it has :first-letter or :first-line style applied to it. The michael@0: // reason we care is that the internal structure in these cases michael@0: // is not the normal structure and requires custom updating michael@0: // logic. michael@0: nsIFrame* containingBlock = state.mFloatedItems.containingBlock; michael@0: bool haveFirstLetterStyle = false; michael@0: bool haveFirstLineStyle = false; michael@0: michael@0: // In order to shave off some cycles, we only dig up the michael@0: // containing block haveFirst* flags if the parent frame where michael@0: // the insertion/append is occurring is an inline or block michael@0: // container. For other types of containers this isn't relevant. michael@0: uint8_t parentDisplay = parentFrame->GetDisplay(); michael@0: michael@0: // Examine the parentFrame where the insertion is taking michael@0: // place. If it's a certain kind of container then some special michael@0: // processing is done. michael@0: if ((NS_STYLE_DISPLAY_BLOCK == parentDisplay) || michael@0: (NS_STYLE_DISPLAY_LIST_ITEM == parentDisplay) || michael@0: (NS_STYLE_DISPLAY_INLINE == parentDisplay) || michael@0: (NS_STYLE_DISPLAY_INLINE_BLOCK == parentDisplay)) { michael@0: // Recover the special style flags for the containing block michael@0: if (containingBlock) { michael@0: haveFirstLetterStyle = HasFirstLetterStyle(containingBlock); michael@0: haveFirstLineStyle = michael@0: ShouldHaveFirstLineStyle(containingBlock->GetContent(), michael@0: containingBlock->StyleContext()); michael@0: } michael@0: michael@0: if (haveFirstLetterStyle) { michael@0: // If our current parentFrame is a Letter frame, use its parent as our michael@0: // new parent hint michael@0: if (parentFrame->GetType() == nsGkAtoms::letterFrame) { michael@0: // If parentFrame is out of flow, then we actually want the parent of michael@0: // the placeholder frame. michael@0: if (parentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { michael@0: nsPlaceholderFrame* placeholderFrame = michael@0: GetPlaceholderFrameFor(parentFrame); michael@0: NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?"); michael@0: parentFrame = placeholderFrame->GetParent(); michael@0: } else { michael@0: parentFrame = parentFrame->GetParent(); michael@0: } michael@0: } michael@0: michael@0: // Remove the old letter frames before doing the insertion michael@0: RemoveLetterFrames(state.mPresContext, mPresShell, michael@0: state.mFloatedItems.containingBlock); michael@0: michael@0: // Removing the letterframes messes around with the frame tree, removing michael@0: // and creating frames. We need to reget our prevsibling, parent frame, michael@0: // etc. michael@0: prevSibling = GetInsertionPrevSibling(parentFrame, aContainer, michael@0: aStartChild, &isAppend, michael@0: &isRangeInsertSafe); michael@0: michael@0: // Need check whether a range insert is still safe. michael@0: if (!isSingleInsert && !isRangeInsertSafe) { michael@0: // Need to recover the letter frames first. michael@0: RecoverLetterFrames(state.mFloatedItems.containingBlock); michael@0: michael@0: // must fall back to a single ContertInserted for each child in the range michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, michael@0: aAllowLazyConstruction); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: container = parentFrame->GetContent(); michael@0: frameType = parentFrame->GetType(); michael@0: } michael@0: } michael@0: michael@0: if (!prevSibling) { michael@0: // We're inserting the new frames as the first child. See if the michael@0: // parent has a :before pseudo-element michael@0: nsIFrame* firstChild = parentFrame->GetFirstPrincipalChild(); michael@0: michael@0: if (firstChild && michael@0: nsLayoutUtils::IsGeneratedContentFor(container, firstChild, michael@0: nsCSSPseudoElements::before)) { michael@0: // Insert the new frames after the last continuation of the :before michael@0: prevSibling = firstChild->GetTailContinuation(); michael@0: parentFrame = prevSibling->GetParent()->GetContentInsertionFrame(); michael@0: // Don't change isAppend here; we'll can call AppendFrames as needed, and michael@0: // the change to our prevSibling doesn't affect that. michael@0: } michael@0: } michael@0: michael@0: FrameConstructionItemList items; michael@0: ParentType parentType = GetParentType(frameType); michael@0: FlattenedChildIterator iter(aContainer); michael@0: bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild()); michael@0: if (aStartChild->GetPreviousSibling() && michael@0: parentType == eTypeBlock && haveNoXBLChildren) { michael@0: // If there's a text node in the normal content list just before the michael@0: // new nodes, and it has no frame, make a frame construction item for michael@0: // it, because it might need a frame now. No need to do this if our michael@0: // parent type is not block, though, since WipeContainingBlock michael@0: // already handles that sitation. michael@0: AddTextItemIfNeeded(state, parentFrame, aStartChild->GetPreviousSibling(), michael@0: items); michael@0: } michael@0: michael@0: if (isSingleInsert) { michael@0: AddFrameConstructionItems(state, aStartChild, michael@0: aStartChild->IsRootOfAnonymousSubtree(), michael@0: parentFrame, items); michael@0: } else { michael@0: for (nsIContent* child = aStartChild; michael@0: child != aEndChild; michael@0: child = child->GetNextSibling()){ michael@0: AddFrameConstructionItems(state, child, false, parentFrame, items); michael@0: } michael@0: } michael@0: michael@0: if (aEndChild && parentType == eTypeBlock && haveNoXBLChildren) { michael@0: // If there's a text node in the normal content list just after the michael@0: // new nodes, and it has no frame, make a frame construction item for michael@0: // it, because it might need a frame now. No need to do this if our michael@0: // parent type is not block, though, since WipeContainingBlock michael@0: // already handles that sitation. michael@0: AddTextItemIfNeeded(state, parentFrame, aEndChild, items); michael@0: } michael@0: michael@0: // Perform special check for diddling around with the frames in michael@0: // a special inline frame. michael@0: // If we're appending before :after content, then we're not really michael@0: // appending, so let WipeContainingBlock know that. michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: if (WipeContainingBlock(state, containingBlock, parentFrame, items, michael@0: isAppend, prevSibling)) { michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return NS_OK; michael@0: } michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: michael@0: // If the container is a table and a caption will be appended, it needs to be michael@0: // put in the outer table frame's additional child list. michael@0: // We make no attempt here to set flags to indicate whether the list michael@0: // will be at the start or end of a block. It doesn't seem worthwhile. michael@0: nsFrameItems frameItems, captionItems; michael@0: ConstructFramesFromItemList(state, items, parentFrame, frameItems); michael@0: michael@0: if (frameItems.NotEmpty()) { michael@0: for (nsIContent* child = aStartChild; michael@0: child != aEndChild; michael@0: child = child->GetNextSibling()){ michael@0: InvalidateCanvasIfNeeded(mPresShell, child); michael@0: } michael@0: michael@0: if (nsGkAtoms::tableFrame == frameType || michael@0: nsGkAtoms::tableOuterFrame == frameType) { michael@0: PullOutCaptionFrames(frameItems, captionItems); michael@0: } michael@0: } michael@0: michael@0: // If the parent of our current prevSibling is different from the frame we'll michael@0: // actually use as the parent, then the calculated insertion point is now michael@0: // invalid and as it is unknown where to insert correctly we append instead michael@0: // (bug 341858). michael@0: // This can affect our prevSibling and isAppend, but should not have any michael@0: // effect on the WipeContainingBlock above, since this should only happen michael@0: // when neither parent is a ib-split frame and should not affect whitespace michael@0: // handling inside table-related frames (and in fact, can only happen when michael@0: // one of the parents is an outer table and one is an inner table or when the michael@0: // parent is a fieldset or fieldset content frame). So it won't affect the michael@0: // {ib} or XUL box cases in WipeContainingBlock(), and the table pseudo michael@0: // handling will only be affected by us maybe thinking we're not inserting michael@0: // at the beginning, whereas we really are. That would have made us reframe michael@0: // unnecessarily, but that's ok. michael@0: // XXXbz we should push our frame construction item code up higher, so we michael@0: // know what our items are by the time we start figuring out previous michael@0: // siblings michael@0: if (prevSibling && frameItems.NotEmpty() && michael@0: frameItems.FirstChild()->GetParent() != prevSibling->GetParent()) { michael@0: #ifdef DEBUG michael@0: nsIFrame* frame1 = frameItems.FirstChild()->GetParent(); michael@0: nsIFrame* frame2 = prevSibling->GetParent(); michael@0: NS_ASSERTION(!IsFramePartOfIBSplit(frame1) && michael@0: !IsFramePartOfIBSplit(frame2), michael@0: "Neither should be ib-split"); michael@0: NS_ASSERTION((frame1->GetType() == nsGkAtoms::tableFrame && michael@0: frame2->GetType() == nsGkAtoms::tableOuterFrame) || michael@0: (frame1->GetType() == nsGkAtoms::tableOuterFrame && michael@0: frame2->GetType() == nsGkAtoms::tableFrame) || michael@0: frame1->GetType() == nsGkAtoms::fieldSetFrame || michael@0: (frame1->GetParent() && michael@0: frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame), michael@0: "Unexpected frame types"); michael@0: #endif michael@0: isAppend = true; michael@0: nsIFrame* appendAfterFrame; michael@0: parentFrame = michael@0: ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(), michael@0: container, michael@0: frameItems.FirstChild()->GetParent(), michael@0: &appendAfterFrame); michael@0: prevSibling = ::FindAppendPrevSibling(parentFrame, appendAfterFrame); michael@0: } michael@0: michael@0: if (haveFirstLineStyle && parentFrame == containingBlock) { michael@0: // It's possible that the new frame goes into a first-line michael@0: // frame. Look at it and see... michael@0: if (isAppend) { michael@0: // Use append logic when appending michael@0: AppendFirstLineFrames(state, containingBlock->GetContent(), michael@0: containingBlock, frameItems); michael@0: } michael@0: else { michael@0: // Use more complicated insert logic when inserting michael@0: // XXXbz this method is a no-op, so it's easy for the args being passed michael@0: // here to make no sense without anyone noticing... If it ever stops michael@0: // being a no-op, vet them carefully! michael@0: InsertFirstLineFrames(state, container, containingBlock, &parentFrame, michael@0: prevSibling, frameItems); michael@0: } michael@0: } michael@0: michael@0: // We might have captions; put them into the caption list of the michael@0: // outer table frame. michael@0: if (captionItems.NotEmpty()) { michael@0: NS_ASSERTION(nsGkAtoms::tableFrame == frameType || michael@0: nsGkAtoms::tableOuterFrame == frameType, michael@0: "parent for caption is not table?"); michael@0: // We need to determine where to put the caption items; start with the michael@0: // the parent frame that has already been determined and get the insertion michael@0: // prevsibling of the first caption item. michael@0: nsIFrame* captionParent = parentFrame; michael@0: bool captionIsAppend; michael@0: nsIFrame* captionPrevSibling = nullptr; michael@0: michael@0: // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here. michael@0: bool ignored; michael@0: if (isSingleInsert) { michael@0: captionPrevSibling = michael@0: GetInsertionPrevSibling(captionParent, aContainer, aStartChild, michael@0: &captionIsAppend, &ignored); michael@0: } else { michael@0: nsIContent* firstCaption = captionItems.FirstChild()->GetContent(); michael@0: // It is very important here that we skip the children in michael@0: // [aStartChild,aEndChild) when looking for a michael@0: // prevsibling. michael@0: captionPrevSibling = michael@0: GetInsertionPrevSibling(captionParent, aContainer, firstCaption, michael@0: &captionIsAppend, &ignored, michael@0: aStartChild, aEndChild); michael@0: } michael@0: michael@0: nsIFrame* outerTable = nullptr; michael@0: if (GetCaptionAdjustedParent(captionParent, captionItems.FirstChild(), michael@0: &outerTable)) { michael@0: // If the parent is not an outer table frame we will try to add frames michael@0: // to a named child list that the parent does not honour and the frames michael@0: // will get lost michael@0: NS_ASSERTION(nsGkAtoms::tableOuterFrame == outerTable->GetType(), michael@0: "Pseudo frame construction failure; " michael@0: "a caption can be only a child of an outer table frame"); michael@0: michael@0: // If the parent of our current prevSibling is different from the frame michael@0: // we'll actually use as the parent, then the calculated insertion michael@0: // point is now invalid (bug 341382). michael@0: if (captionPrevSibling && michael@0: captionPrevSibling->GetParent() != outerTable) { michael@0: captionPrevSibling = nullptr; michael@0: } michael@0: if (captionIsAppend) { michael@0: AppendFrames(outerTable, nsIFrame::kCaptionList, captionItems); michael@0: } else { michael@0: InsertFrames(outerTable, nsIFrame::kCaptionList, michael@0: captionPrevSibling, captionItems); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (frameItems.NotEmpty()) { michael@0: // Notify the parent frame michael@0: if (isAppend) { michael@0: AppendFramesToParent(state, parentFrame, frameItems, prevSibling); michael@0: } else { michael@0: InsertFrames(parentFrame, kPrincipalList, prevSibling, frameItems); michael@0: } michael@0: } michael@0: michael@0: if (haveFirstLetterStyle) { michael@0: // Recover the letter frames for the containing block when michael@0: // it has first-letter style. michael@0: RecoverLetterFrames(state.mFloatedItems.containingBlock); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: if (gReallyNoisyContentUpdates && parentFrame) { michael@0: printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame model:\n"); michael@0: parentFrame->List(stdout, 0); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: nsAccessibilityService* accService = nsIPresShell::AccService(); michael@0: if (accService) { michael@0: accService->ContentRangeInserted(mPresShell, aContainer, michael@0: aStartChild, aEndChild); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: nsIContent* aOldNextSibling, michael@0: RemoveFlags aFlags, michael@0: bool* aDidReconstruct) michael@0: { michael@0: AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); michael@0: NS_PRECONDITION(mUpdateCount != 0, michael@0: "Should be in an update while destroying frames"); michael@0: michael@0: *aDidReconstruct = false; michael@0: michael@0: // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and michael@0: // the :empty pseudo-class? michael@0: michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::ContentRemoved container=%p child=%p " michael@0: "old-next-sibling=%p\n", michael@0: static_cast(aContainer), michael@0: static_cast(aChild), michael@0: static_cast(aOldNextSibling)); michael@0: if (gReallyNoisyContentUpdates) { michael@0: aContainer->List(stdout, 0); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: nsPresContext *presContext = mPresShell->GetPresContext(); michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Find the child frame that maps the content michael@0: nsIFrame* childFrame = aChild->GetPrimaryFrame(); michael@0: michael@0: if (!childFrame || childFrame->GetContent() != aChild) { michael@0: // XXXbz the GetContent() != aChild check is needed due to bug 135040. michael@0: // Remove it once that's fixed. michael@0: ClearUndisplayedContentIn(aChild, aContainer); michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling, michael@0: mDocument, childFrame, CONTENT_REMOVED)) michael@0: return NS_OK; michael@0: michael@0: #endif // MOZ_XUL michael@0: michael@0: // If we're removing the root, then make sure to remove things starting at michael@0: // the viewport's child instead of the primary frame (which might even be michael@0: // null if the root had an XBL binding or display:none, even though the michael@0: // frames above it got created). We do the adjustment after the childFrame michael@0: // check above, because we do want to clear any undisplayed content we might michael@0: // have for the root. Detecting removal of a root is a little exciting; in michael@0: // particular, having a null aContainer is necessary but NOT sufficient. Due michael@0: // to how we process reframes, the content node might not even be in our michael@0: // document by now. So explicitly check whether the viewport's first kid's michael@0: // content node is aChild. michael@0: bool isRoot = false; michael@0: if (!aContainer) { michael@0: nsIFrame* viewport = GetRootFrame(); michael@0: if (viewport) { michael@0: nsIFrame* firstChild = viewport->GetFirstPrincipalChild(); michael@0: if (firstChild && firstChild->GetContent() == aChild) { michael@0: isRoot = true; michael@0: childFrame = firstChild; michael@0: NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) { michael@0: // Recreate frames if content is removed from a ShadowRoot michael@0: // because it may contain an insertion point which can change michael@0: // how the host is rendered. michael@0: nsIContent* bindingParent = aContainer->GetBindingParent(); michael@0: *aDidReconstruct = true; michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: nsresult rv = RecreateFramesForContent(bindingParent, false); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return rv; michael@0: } michael@0: michael@0: if (childFrame) { michael@0: InvalidateCanvasIfNeeded(mPresShell, aChild); michael@0: michael@0: // See whether we need to remove more than just childFrame michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: if (MaybeRecreateContainerForFrameRemoval(childFrame, &rv)) { michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: *aDidReconstruct = true; michael@0: return rv; michael@0: } michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: michael@0: // Get the childFrame's parent frame michael@0: nsIFrame* parentFrame = childFrame->GetParent(); michael@0: nsIAtom* parentType = parentFrame->GetType(); michael@0: michael@0: if (parentType == nsGkAtoms::frameSetFrame && michael@0: IsSpecialFramesetChild(aChild)) { michael@0: // Just reframe the parent, since framesets are weird like that. michael@0: *aDidReconstruct = true; michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return rv; michael@0: } michael@0: michael@0: // If we're a child of MathML, then we should reframe the MathML content. michael@0: // If we're non-MathML, then we would be wrapped in a block so we need to michael@0: // check our grandparent in that case. michael@0: nsIFrame* possibleMathMLAncestor = parentType == nsGkAtoms::blockFrame ? michael@0: parentFrame->GetParent() : parentFrame; michael@0: if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) { michael@0: *aDidReconstruct = true; michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: nsresult rv = RecreateFramesForContent(possibleMathMLAncestor->GetContent(), false); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return rv; michael@0: } michael@0: michael@0: // Undo XUL wrapping if it's no longer needed. michael@0: // (If we're in the XUL block-wrapping situation, parentFrame is the michael@0: // wrapper frame.) michael@0: nsIFrame* grandparentFrame = parentFrame->GetParent(); michael@0: if (grandparentFrame && grandparentFrame->IsBoxFrame() && michael@0: (grandparentFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) && michael@0: // check if this frame is the only one needing wrapping michael@0: aChild == AnyKidsNeedBlockParent(parentFrame->GetFirstPrincipalChild()) && michael@0: !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) { michael@0: *aDidReconstruct = true; michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: nsresult rv = RecreateFramesForContent(grandparentFrame->GetContent(), true); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return rv; michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: nsAccessibilityService* accService = nsIPresShell::AccService(); michael@0: if (accService) { michael@0: accService->ContentRemoved(mPresShell, aContainer, aChild); michael@0: } michael@0: #endif michael@0: michael@0: // Examine the containing-block for the removed content and see if michael@0: // :first-letter style applies. michael@0: nsIFrame* inflowChild = childFrame; michael@0: if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { michael@0: inflowChild = GetPlaceholderFrameFor(childFrame); michael@0: NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?"); michael@0: } michael@0: nsIFrame* containingBlock = michael@0: GetFloatContainingBlock(inflowChild->GetParent()); michael@0: bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock); michael@0: if (haveFLS) { michael@0: // Trap out to special routine that handles adjusting a blocks michael@0: // frame tree when first-letter style is present. michael@0: #ifdef NOISY_FIRST_LETTER michael@0: printf("ContentRemoved: containingBlock="); michael@0: nsFrame::ListTag(stdout, containingBlock); michael@0: printf(" parentFrame="); michael@0: nsFrame::ListTag(stdout, parentFrame); michael@0: printf(" childFrame="); michael@0: nsFrame::ListTag(stdout, childFrame); michael@0: printf("\n"); michael@0: #endif michael@0: michael@0: // First update the containing blocks structure by removing the michael@0: // existing letter frames. This makes the subsequent logic michael@0: // simpler. michael@0: RemoveLetterFrames(presContext, mPresShell, containingBlock); michael@0: michael@0: // Recover childFrame and parentFrame michael@0: childFrame = aChild->GetPrimaryFrame(); michael@0: if (!childFrame || childFrame->GetContent() != aChild) { michael@0: // XXXbz the GetContent() != aChild check is needed due to bug 135040. michael@0: // Remove it once that's fixed. michael@0: ClearUndisplayedContentIn(aChild, aContainer); michael@0: return NS_OK; michael@0: } michael@0: parentFrame = childFrame->GetParent(); michael@0: parentType = parentFrame->GetType(); michael@0: michael@0: #ifdef NOISY_FIRST_LETTER michael@0: printf(" ==> revised parentFrame="); michael@0: nsFrame::ListTag(stdout, parentFrame); michael@0: printf(" childFrame="); michael@0: nsFrame::ListTag(stdout, childFrame); michael@0: printf("\n"); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: if (gReallyNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::ContentRemoved: childFrame="); michael@0: nsFrame::ListTag(stdout, childFrame); michael@0: putchar('\n'); michael@0: parentFrame->List(stdout, 0); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: // Notify the parent frame that it should delete the frame michael@0: if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { michael@0: childFrame = GetPlaceholderFrameFor(childFrame); michael@0: NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow."); michael@0: parentFrame = childFrame->GetParent(); michael@0: } michael@0: rv = RemoveFrame(nsLayoutUtils::GetChildListNameFor(childFrame), michael@0: childFrame); michael@0: //XXXfr NS_ENSURE_SUCCESS(rv, rv) ? michael@0: michael@0: if (isRoot) { michael@0: mRootElementFrame = nullptr; michael@0: mRootElementStyleFrame = nullptr; michael@0: mDocElementContainingBlock = nullptr; michael@0: mPageSequenceFrame = nullptr; michael@0: mGfxScrollFrame = nullptr; michael@0: mHasRootAbsPosContainingBlock = false; michael@0: mFixedContainingBlock = GetRootFrame(); michael@0: } michael@0: michael@0: if (haveFLS && mRootElementFrame) { michael@0: RecoverLetterFrames(containingBlock); michael@0: } michael@0: michael@0: // If we're just reconstructing frames for the element, then the michael@0: // following ContentInserted notification on the element will michael@0: // take care of fixing up any adjacent text nodes. We don't need michael@0: // to do this if the table parent type of our parent type is not michael@0: // eTypeBlock, though, because in that case the whitespace isn't michael@0: // being suppressed due to us anyway. michael@0: if (aContainer && !aChild->IsRootOfAnonymousSubtree() && michael@0: aFlags != REMOVE_FOR_RECONSTRUCTION && michael@0: GetParentType(parentType) == eTypeBlock) { michael@0: // Adjacent whitespace-only text nodes might have been suppressed if michael@0: // this node does not have inline ends. Create frames for them now michael@0: // if necessary. michael@0: // Reframe any text node just before the node being removed, if there is michael@0: // one, and if it's not the last child or the first child. If a whitespace michael@0: // textframe was being suppressed and it's now the last child or first michael@0: // child then it can stay suppressed since the parent must be a block michael@0: // and hence it's adjacent to a block end. michael@0: // If aOldNextSibling is null, then the text node before the node being michael@0: // removed is the last node, and we don't need to worry about it. michael@0: if (aOldNextSibling) { michael@0: nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling(); michael@0: if (prevSibling && prevSibling->GetPreviousSibling()) { michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: ReframeTextIfNeeded(aContainer, prevSibling); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: } michael@0: } michael@0: // Reframe any text node just after the node being removed, if there is michael@0: // one, and if it's not the last child or the first child. michael@0: if (aOldNextSibling && aOldNextSibling->GetNextSibling() && michael@0: aOldNextSibling->GetPreviousSibling()) { michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: ReframeTextIfNeeded(aContainer, aOldNextSibling); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: if (gReallyNoisyContentUpdates && parentFrame) { michael@0: printf("nsCSSFrameConstructor::ContentRemoved: resulting frame model:\n"); michael@0: parentFrame->List(stdout, 0); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * This method invalidates the canvas when frames are removed or added for a michael@0: * node that might have its background propagated to the canvas, i.e., a michael@0: * document root node or an HTML BODY which is a child of the root node. michael@0: * michael@0: * @param aFrame a frame for a content node about to be removed or a frame that michael@0: * was just created for a content node that was inserted. michael@0: */ michael@0: static void michael@0: InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node) michael@0: { michael@0: NS_PRECONDITION(presShell->GetRootFrame(), "What happened here?"); michael@0: NS_PRECONDITION(presShell->GetPresContext(), "Say what?"); michael@0: michael@0: // Note that both in ContentRemoved and ContentInserted the content node michael@0: // will still have the right parent pointer, so looking at that is ok. michael@0: michael@0: nsIContent* parent = node->GetParent(); michael@0: if (parent) { michael@0: // Has a parent; might not be what we want michael@0: nsIContent* grandParent = parent->GetParent(); michael@0: if (grandParent) { michael@0: // Has a grandparent, so not what we want michael@0: return; michael@0: } michael@0: michael@0: // Check whether it's an HTML body michael@0: if (node->Tag() != nsGkAtoms::body || michael@0: !node->IsHTML()) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // At this point the node has no parent or it's an HTML child of the michael@0: // root. We might not need to invalidate in this case (eg we might be in michael@0: // XHTML or something), but chances are we want to. Play it safe. michael@0: // Invalidate the viewport. michael@0: michael@0: nsIFrame* rootFrame = presShell->GetRootFrame(); michael@0: rootFrame->InvalidateFrameSubtree(); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent) michael@0: { michael@0: if (aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) && michael@0: !mAlwaysCreateFramesForIgnorableWhitespace) { michael@0: // Text frame may have been suppressed. Disable suppression and signal michael@0: // that a flush should be performed. We do this on a document-wide michael@0: // basis so that pages that repeatedly query metrics for michael@0: // collapsed-whitespace text nodes don't trigger pathological behavior. michael@0: mAlwaysCreateFramesForIgnorableWhitespace = true; michael@0: nsAutoScriptBlocker blocker; michael@0: BeginUpdate(); michael@0: ReconstructDocElementHierarchy(); michael@0: EndUpdate(); michael@0: } michael@0: return aContent->GetPrimaryFrame(); michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent, michael@0: CharacterDataChangeInfo* aInfo) michael@0: { michael@0: AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); michael@0: nsresult rv = NS_OK; michael@0: michael@0: if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) && michael@0: !aContent->TextIsOnlyWhitespace()) || michael@0: (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) && michael@0: aContent->TextIsOnlyWhitespace())) { michael@0: #ifdef DEBUG michael@0: nsIFrame* frame = aContent->GetPrimaryFrame(); michael@0: NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(), michael@0: "Bit should never be set on generated content"); michael@0: #endif michael@0: LAYOUT_PHASE_TEMP_EXIT(); michael@0: nsresult rv = RecreateFramesForContent(aContent, false); michael@0: LAYOUT_PHASE_TEMP_REENTER(); michael@0: return rv; michael@0: } michael@0: michael@0: // Find the child frame michael@0: nsIFrame* frame = aContent->GetPrimaryFrame(); michael@0: michael@0: // Notify the first frame that maps the content. It will generate a reflow michael@0: // command michael@0: michael@0: // It's possible the frame whose content changed isn't inserted into the michael@0: // frame hierarchy yet, or that there is no frame that maps the content michael@0: if (nullptr != frame) { michael@0: #if 0 michael@0: NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, michael@0: ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p", michael@0: aContent, ContentTag(aContent, 0), michael@0: aSubContent, frame)); michael@0: #endif michael@0: michael@0: // Special check for text content that is a child of a letter frame. If michael@0: // this happens, we should remove the letter frame, do whatever we're michael@0: // planning to do with this notification, then put the letter frame back. michael@0: // Note that this is basically what RecreateFramesForContent ends up doing; michael@0: // the reason we dont' want to call that here is that our text content michael@0: // could be native anonymous, in which case RecreateFramesForContent would michael@0: // completely barf on it. And recreating the non-anonymous ancestor would michael@0: // just lead us to come back into this notification (e.g. if quotes or michael@0: // counters are involved), leading to a loop. michael@0: nsIFrame* block = GetFloatContainingBlock(frame); michael@0: bool haveFirstLetterStyle = false; michael@0: if (block) { michael@0: // See if the block has first-letter style applied to it. michael@0: haveFirstLetterStyle = HasFirstLetterStyle(block); michael@0: if (haveFirstLetterStyle) { michael@0: RemoveLetterFrames(mPresShell->GetPresContext(), mPresShell, michael@0: block); michael@0: // Reget |frame|, since we might have killed it. michael@0: // Do we really need to call CharacterDataChanged in this case, though? michael@0: frame = aContent->GetPrimaryFrame(); michael@0: NS_ASSERTION(frame, "Should have frame here!"); michael@0: } michael@0: } michael@0: michael@0: frame->CharacterDataChanged(aInfo); michael@0: michael@0: if (haveFirstLetterStyle) { michael@0: RecoverLetterFrames(block); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::BeginUpdate() { michael@0: NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), michael@0: "Someone forgot a script blocker"); michael@0: michael@0: nsRootPresContext* rootPresContext = michael@0: mPresShell->GetPresContext()->GetRootPresContext(); michael@0: if (rootPresContext) { michael@0: rootPresContext->IncrementDOMGeneration(); michael@0: } michael@0: michael@0: ++sGlobalGenerationNumber; michael@0: ++mUpdateCount; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::EndUpdate() michael@0: { michael@0: if (mUpdateCount == 1) { michael@0: // This is the end of our last update. Before we decrement michael@0: // mUpdateCount, recalc quotes and counters as needed. michael@0: michael@0: RecalcQuotesAndCounters(); michael@0: NS_ASSERTION(mUpdateCount == 1, "Odd update count"); michael@0: } michael@0: NS_ASSERTION(mUpdateCount, "Negative mUpdateCount!"); michael@0: --mUpdateCount; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::RecalcQuotesAndCounters() michael@0: { michael@0: if (mQuotesDirty) { michael@0: mQuotesDirty = false; michael@0: mQuoteList.RecalcAll(); michael@0: } michael@0: michael@0: if (mCountersDirty) { michael@0: mCountersDirty = false; michael@0: mCounterManager.RecalcAll(); michael@0: } michael@0: michael@0: NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost"); michael@0: NS_ASSERTION(!mCountersDirty, "Counter updates will be lost"); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::WillDestroyFrameTree() michael@0: { michael@0: #if defined(DEBUG_dbaron_off) michael@0: mCounterManager.Dump(); michael@0: #endif michael@0: michael@0: mIsDestroyingFrameTree = true; michael@0: michael@0: // Prevent frame tree destruction from being O(N^2) michael@0: mQuoteList.Clear(); michael@0: mCounterManager.Clear(); michael@0: michael@0: // Remove our presshell as a style flush observer. But leave michael@0: // RestyleManager::mObservingRefreshDriver true so we don't readd to michael@0: // it even if someone tries to post restyle events on us from this michael@0: // point on for some reason. michael@0: mPresShell->GetPresContext()->RefreshDriver()-> michael@0: RemoveStyleFlushObserver(mPresShell); michael@0: michael@0: nsFrameManager::Destroy(); michael@0: } michael@0: michael@0: //STATIC michael@0: michael@0: // XXXbz I'd really like this method to go away. Once we have inline-block and michael@0: // I can just use that for sized broken images, that can happen, maybe. michael@0: void nsCSSFrameConstructor::GetAlternateTextFor(nsIContent* aContent, michael@0: nsIAtom* aTag, // content object's tag michael@0: nsXPIDLString& aAltText) michael@0: { michael@0: // The "alt" attribute specifies alternate text that is rendered michael@0: // when the image can not be displayed michael@0: michael@0: // If there's no "alt" attribute, and aContent is an input michael@0: // element, then use the value of the "value" attribute michael@0: if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aAltText) && michael@0: nsGkAtoms::input == aTag) { michael@0: // If there's no "value" attribute either, then use the localized string michael@0: // for "Submit" as the alternate text. michael@0: if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aAltText)) { michael@0: nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, michael@0: "Submit", aAltText); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::CreateContinuingOuterTableFrame(nsIPresShell* aPresShell, michael@0: nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsIFrame* aParentFrame, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: nsIFrame* newFrame = NS_NewTableOuterFrame(aPresShell, aStyleContext); michael@0: michael@0: newFrame->Init(aContent, aParentFrame, aFrame); michael@0: michael@0: // Create a continuing inner table frame, and if there's a caption then michael@0: // replicate the caption michael@0: nsFrameItems newChildFrames; michael@0: michael@0: nsIFrame* childFrame = aFrame->GetFirstPrincipalChild(); michael@0: if (childFrame) { michael@0: nsIFrame* continuingTableFrame = michael@0: CreateContinuingFrame(aPresContext, childFrame, newFrame); michael@0: newChildFrames.AddChild(continuingTableFrame); michael@0: michael@0: NS_ASSERTION(!childFrame->GetNextSibling(),"there can be only one inner table frame"); michael@0: } michael@0: michael@0: // Set the outer table's initial child list michael@0: newFrame->SetInitialChildList(kPrincipalList, newChildFrames); michael@0: michael@0: return newFrame; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::CreateContinuingTableFrame(nsIPresShell* aPresShell, michael@0: nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsIFrame* aParentFrame, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: nsIFrame* newFrame = NS_NewTableFrame(aPresShell, aStyleContext); michael@0: michael@0: newFrame->Init(aContent, aParentFrame, aFrame); michael@0: michael@0: // Replicate any header/footer frames michael@0: nsFrameItems childFrames; michael@0: nsIFrame* childFrame = aFrame->GetFirstPrincipalChild(); michael@0: for ( ; childFrame; childFrame = childFrame->GetNextSibling()) { michael@0: // See if it's a header/footer, possibly wrapped in a scroll frame. michael@0: nsTableRowGroupFrame* rowGroupFrame = michael@0: static_cast(childFrame); michael@0: // If the row group was continued, then don't replicate it. michael@0: nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow(); michael@0: if (rgNextInFlow) { michael@0: rowGroupFrame->SetRepeatable(false); michael@0: } michael@0: else if (rowGroupFrame->IsRepeatable()) { michael@0: // Replicate the header/footer frame. michael@0: nsTableRowGroupFrame* headerFooterFrame; michael@0: nsFrameItems childItems; michael@0: nsFrameConstructorState state(mPresShell, michael@0: GetAbsoluteContainingBlock(newFrame, FIXED_POS), michael@0: GetAbsoluteContainingBlock(newFrame, ABS_POS), michael@0: nullptr); michael@0: state.mCreatingExtraFrames = true; michael@0: michael@0: nsStyleContext* const headerFooterStyleContext = rowGroupFrame->StyleContext(); michael@0: headerFooterFrame = static_cast michael@0: (NS_NewTableRowGroupFrame(aPresShell, headerFooterStyleContext)); michael@0: michael@0: nsIContent* headerFooter = rowGroupFrame->GetContent(); michael@0: headerFooterFrame->Init(headerFooter, newFrame, nullptr); michael@0: michael@0: nsFrameConstructorSaveState absoluteSaveState; michael@0: MakeTablePartAbsoluteContainingBlockIfNeeded(state, michael@0: headerFooterStyleContext->StyleDisplay(), michael@0: absoluteSaveState, michael@0: headerFooterFrame); michael@0: michael@0: ProcessChildren(state, headerFooter, rowGroupFrame->StyleContext(), michael@0: headerFooterFrame, true, childItems, false, michael@0: nullptr); michael@0: NS_ASSERTION(state.mFloatedItems.IsEmpty(), "unexpected floated element"); michael@0: headerFooterFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: headerFooterFrame->SetRepeatable(true); michael@0: michael@0: // Table specific initialization michael@0: headerFooterFrame->InitRepeatedFrame(aPresContext, rowGroupFrame); michael@0: michael@0: // XXX Deal with absolute and fixed frames... michael@0: childFrames.AddChild(headerFooterFrame); michael@0: } michael@0: } michael@0: michael@0: // Set the table frame's initial child list michael@0: newFrame->SetInitialChildList(kPrincipalList, childFrames); michael@0: michael@0: return newFrame; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext, michael@0: nsIFrame* aFrame, michael@0: nsIFrame* aParentFrame, michael@0: bool aIsFluid) michael@0: { michael@0: nsIPresShell* shell = aPresContext->PresShell(); michael@0: nsStyleContext* styleContext = aFrame->StyleContext(); michael@0: nsIFrame* newFrame = nullptr; michael@0: nsIFrame* nextContinuation = aFrame->GetNextContinuation(); michael@0: nsIFrame* nextInFlow = aFrame->GetNextInFlow(); michael@0: michael@0: // Use the frame type to determine what type of frame to create michael@0: nsIAtom* frameType = aFrame->GetType(); michael@0: nsIContent* content = aFrame->GetContent(); michael@0: michael@0: NS_ASSERTION(aFrame->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE, michael@0: "why CreateContinuingFrame for a non-splittable frame?"); michael@0: michael@0: if (nsGkAtoms::textFrame == frameType) { michael@0: newFrame = NS_NewContinuingTextFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else if (nsGkAtoms::inlineFrame == frameType) { michael@0: newFrame = NS_NewInlineFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else if (nsGkAtoms::blockFrame == frameType) { michael@0: newFrame = NS_NewBlockFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: #ifdef MOZ_XUL michael@0: } else if (nsGkAtoms::XULLabelFrame == frameType) { michael@0: newFrame = NS_NewXULLabelFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: #endif michael@0: } else if (nsGkAtoms::columnSetFrame == frameType) { michael@0: newFrame = NS_NewColumnSetFrame(shell, styleContext, nsFrameState(0)); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else if (nsGkAtoms::pageFrame == frameType) { michael@0: nsIFrame* canvasFrame; michael@0: newFrame = ConstructPageFrame(shell, aPresContext, aParentFrame, aFrame, michael@0: canvasFrame); michael@0: } else if (nsGkAtoms::tableOuterFrame == frameType) { michael@0: newFrame = michael@0: CreateContinuingOuterTableFrame(shell, aPresContext, aFrame, aParentFrame, michael@0: content, styleContext); michael@0: michael@0: } else if (nsGkAtoms::tableFrame == frameType) { michael@0: newFrame = michael@0: CreateContinuingTableFrame(shell, aPresContext, aFrame, aParentFrame, michael@0: content, styleContext); michael@0: michael@0: } else if (nsGkAtoms::tableRowGroupFrame == frameType) { michael@0: newFrame = NS_NewTableRowGroupFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { michael@0: nsTableFrame::RegisterPositionedTablePart(newFrame); michael@0: } michael@0: } else if (nsGkAtoms::tableRowFrame == frameType) { michael@0: newFrame = NS_NewTableRowFrame(shell, styleContext); michael@0: michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { michael@0: nsTableFrame::RegisterPositionedTablePart(newFrame); michael@0: } michael@0: michael@0: // Create a continuing frame for each table cell frame michael@0: nsFrameItems newChildList; michael@0: nsIFrame* cellFrame = aFrame->GetFirstPrincipalChild(); michael@0: while (cellFrame) { michael@0: // See if it's a table cell frame michael@0: if (IS_TABLE_CELL(cellFrame->GetType())) { michael@0: nsIFrame* continuingCellFrame = michael@0: CreateContinuingFrame(aPresContext, cellFrame, newFrame); michael@0: newChildList.AddChild(continuingCellFrame); michael@0: } michael@0: cellFrame = cellFrame->GetNextSibling(); michael@0: } michael@0: michael@0: // Set the table cell's initial child list michael@0: newFrame->SetInitialChildList(kPrincipalList, newChildList); michael@0: michael@0: } else if (IS_TABLE_CELL(frameType)) { michael@0: // Warning: If you change this and add a wrapper frame around table cell michael@0: // frames, make sure Bug 368554 doesn't regress! michael@0: // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp. michael@0: newFrame = NS_NewTableCellFrame(shell, styleContext, IsBorderCollapse(aParentFrame)); michael@0: michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: if (newFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { michael@0: nsTableFrame::RegisterPositionedTablePart(newFrame); michael@0: } michael@0: michael@0: // Create a continuing area frame michael@0: nsIFrame* blockFrame = aFrame->GetFirstPrincipalChild(); michael@0: nsIFrame* continuingBlockFrame = michael@0: CreateContinuingFrame(aPresContext, blockFrame, newFrame); michael@0: michael@0: // Set the table cell's initial child list michael@0: SetInitialSingleChild(newFrame, continuingBlockFrame); michael@0: } else if (nsGkAtoms::lineFrame == frameType) { michael@0: newFrame = NS_NewFirstLineFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else if (nsGkAtoms::letterFrame == frameType) { michael@0: newFrame = NS_NewFirstLetterFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else if (nsGkAtoms::imageFrame == frameType) { michael@0: newFrame = NS_NewImageFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else if (nsGkAtoms::imageControlFrame == frameType) { michael@0: newFrame = NS_NewImageControlFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else if (nsGkAtoms::placeholderFrame == frameType) { michael@0: // create a continuing out of flow frame michael@0: nsIFrame* oofFrame = nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame); michael@0: nsIFrame* oofContFrame = michael@0: CreateContinuingFrame(aPresContext, oofFrame, aParentFrame); michael@0: newFrame = michael@0: CreatePlaceholderFrameFor(shell, content, oofContFrame, styleContext, michael@0: aParentFrame, aFrame, michael@0: aFrame->GetStateBits() & PLACEHOLDER_TYPE_MASK); michael@0: } else if (nsGkAtoms::fieldSetFrame == frameType) { michael@0: newFrame = NS_NewFieldSetFrame(shell, styleContext); michael@0: michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: michael@0: // Create a continuing area frame michael@0: // XXXbz we really shouldn't have to do this by hand! michael@0: nsIFrame* blockFrame = GetFieldSetBlockFrame(aFrame); michael@0: if (blockFrame) { michael@0: nsIFrame* continuingBlockFrame = michael@0: CreateContinuingFrame(aPresContext, blockFrame, newFrame); michael@0: // Set the fieldset's initial child list michael@0: SetInitialSingleChild(newFrame, continuingBlockFrame); michael@0: } else { michael@0: MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER, michael@0: "FieldSet block may only be null for overflow containers"); michael@0: } michael@0: } else if (nsGkAtoms::legendFrame == frameType) { michael@0: newFrame = NS_NewLegendFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else if (nsGkAtoms::flexContainerFrame == frameType) { michael@0: newFrame = NS_NewFlexContainerFrame(shell, styleContext); michael@0: newFrame->Init(content, aParentFrame, aFrame); michael@0: } else { michael@0: NS_RUNTIMEABORT("unexpected frame type"); michael@0: } michael@0: michael@0: // Init() set newFrame to be a fluid continuation of aFrame. michael@0: // If we want a non-fluid continuation, we need to call SetPrevContinuation() michael@0: // to reset NS_FRAME_IS_FLUID_CONTINUATION. michael@0: if (!aIsFluid) { michael@0: newFrame->SetPrevContinuation(aFrame); michael@0: } michael@0: michael@0: // A continuation of generated content is also generated content michael@0: if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) { michael@0: newFrame->AddStateBits(NS_FRAME_GENERATED_CONTENT); michael@0: } michael@0: michael@0: // A continuation of nsIAnonymousContentCreator content is also michael@0: // nsIAnonymousContentCreator created content michael@0: if (aFrame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) { michael@0: newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT); michael@0: } michael@0: michael@0: // A continuation of an out-of-flow is also an out-of-flow michael@0: if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { michael@0: newFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW); michael@0: } michael@0: michael@0: if (nextInFlow) { michael@0: nextInFlow->SetPrevInFlow(newFrame); michael@0: newFrame->SetNextInFlow(nextInFlow); michael@0: } else if (nextContinuation) { michael@0: nextContinuation->SetPrevContinuation(newFrame); michael@0: newFrame->SetNextContinuation(nextContinuation); michael@0: } michael@0: michael@0: NS_POSTCONDITION(!newFrame->GetNextSibling(), "unexpected sibling"); michael@0: return newFrame; michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame) michael@0: { michael@0: // Now deal with fixed-pos things.... They should appear on all pages, michael@0: // so we want to move over the placeholders when processing the child michael@0: // of the pageContentFrame. michael@0: michael@0: nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow(); michael@0: if (!prevPageContentFrame) { michael@0: return NS_OK; michael@0: } michael@0: nsIFrame* canvasFrame = aParentFrame->GetFirstPrincipalChild(); michael@0: nsIFrame* prevCanvasFrame = prevPageContentFrame->GetFirstPrincipalChild(); michael@0: if (!canvasFrame || !prevCanvasFrame) { michael@0: // document's root element frame missing michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsFrameItems fixedPlaceholders; michael@0: nsIFrame* firstFixed = prevPageContentFrame->GetFirstChild(nsIFrame::kFixedList); michael@0: if (!firstFixed) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Don't allow abs-pos descendants of the fixed content to escape the content. michael@0: // This should not normally be possible (because fixed-pos elements should michael@0: // be absolute containers) but fixed-pos tables currently aren't abs-pos michael@0: // containers. michael@0: nsFrameConstructorState state(mPresShell, aParentFrame, michael@0: nullptr, michael@0: mRootElementFrame); michael@0: state.mCreatingExtraFrames = true; michael@0: michael@0: // We can't use an ancestor filter here, because we're not going to michael@0: // be usefully recurring down the tree. This means that other michael@0: // places in frame construction can't assume a filter is michael@0: // initialized! michael@0: michael@0: // Iterate across fixed frames and replicate each whose placeholder is a michael@0: // descendant of aFrame. (We don't want to explicitly copy placeholders that michael@0: // are within fixed frames, because that would cause duplicates on the new michael@0: // page - bug 389619) michael@0: for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) { michael@0: nsIFrame* prevPlaceholder = GetPlaceholderFrameFor(fixed); michael@0: if (prevPlaceholder && michael@0: nsLayoutUtils::IsProperAncestorFrame(prevCanvasFrame, prevPlaceholder)) { michael@0: // We want to use the same style as the primary style frame for michael@0: // our content michael@0: nsIContent* content = fixed->GetContent(); michael@0: nsStyleContext* styleContext = michael@0: nsLayoutUtils::GetStyleFrame(content)->StyleContext(); michael@0: FrameConstructionItemList items; michael@0: AddFrameConstructionItemsInternal(state, content, canvasFrame, michael@0: content->Tag(), michael@0: content->GetNameSpaceID(), michael@0: true, michael@0: styleContext, michael@0: ITEM_ALLOW_XBL_BASE | michael@0: ITEM_ALLOW_PAGE_BREAK, michael@0: nullptr, items); michael@0: ConstructFramesFromItemList(state, items, canvasFrame, fixedPlaceholders); michael@0: } michael@0: } michael@0: michael@0: // Add the placeholders to our primary child list. michael@0: // XXXbz this is a little screwed up, since the fixed frames will have michael@0: // broken auto-positioning. Oh, well. michael@0: NS_ASSERTION(!canvasFrame->GetFirstPrincipalChild(), michael@0: "leaking frames; doc root continuation must be empty"); michael@0: canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer, michael@0: nsIContent* aChildContent, michael@0: bool* aMultiple) michael@0: { michael@0: nsBindingManager *bindingManager = mDocument->BindingManager(); michael@0: michael@0: nsIContent* insertionElement; michael@0: if (aChildContent) { michael@0: // We've got an explicit insertion child. Check to see if it's michael@0: // anonymous. michael@0: if (aChildContent->GetBindingParent() == aContainer) { michael@0: // This child content is anonymous. Don't use the insertion michael@0: // point, since that's only for the explicit kids. michael@0: return GetFrameFor(aContainer); michael@0: } michael@0: michael@0: insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChildContent); michael@0: } michael@0: else { michael@0: bool multiple; michael@0: insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple); michael@0: michael@0: if (multiple) { michael@0: if (aMultiple) { michael@0: *aMultiple = true; michael@0: } michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: if (!insertionElement) { michael@0: insertionElement = aContainer; michael@0: } michael@0: michael@0: nsIFrame* insertionPoint = GetFrameFor(insertionElement); michael@0: michael@0: // fieldsets have multiple insertion points. Note that we might michael@0: // have to look at insertionElement here... michael@0: if (aMultiple && insertionElement->IsHTML(nsGkAtoms::fieldset)) { michael@0: *aMultiple = true; michael@0: } michael@0: michael@0: return insertionPoint; michael@0: } michael@0: michael@0: // Capture state for the frame tree rooted at the frame associated with the michael@0: // content object, aContent michael@0: void michael@0: nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent, michael@0: nsILayoutHistoryState* aHistoryState) michael@0: { michael@0: if (!aHistoryState) { michael@0: return; michael@0: } michael@0: nsIFrame* frame = aContent->GetPrimaryFrame(); michael@0: if (frame == mRootElementFrame) { michael@0: frame = mFixedContainingBlock; michael@0: } michael@0: for ( ; frame; michael@0: frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) { michael@0: CaptureFrameState(frame, aHistoryState); michael@0: } michael@0: } michael@0: michael@0: static bool EqualURIs(mozilla::css::URLValue *aURI1, michael@0: mozilla::css::URLValue *aURI2) michael@0: { michael@0: return aURI1 == aURI2 || // handle null==null, and optimize michael@0: (aURI1 && aURI2 && aURI1->URIEquals(*aURI2)); michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::MaybeRecreateFramesForElement(Element* aElement) michael@0: { michael@0: nsRefPtr oldContext = GetUndisplayedContent(aElement); michael@0: if (!oldContext) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // The parent has a frame, so try resolving a new context. michael@0: nsRefPtr newContext = mPresShell->StyleSet()-> michael@0: ResolveStyleFor(aElement, oldContext->GetParent()); michael@0: michael@0: ChangeUndisplayedContent(aElement, newContext); michael@0: const nsStyleDisplay* disp = newContext->StyleDisplay(); michael@0: if (disp->mDisplay == NS_STYLE_DISPLAY_NONE) { michael@0: // We can skip trying to recreate frames here, but only if our style michael@0: // context does not have a binding URI that differs from our old one. michael@0: // Otherwise, we should try to recreate, because we may want to apply the michael@0: // new binding michael@0: if (!disp->mBinding) { michael@0: return NS_OK; michael@0: } michael@0: const nsStyleDisplay* oldDisp = oldContext->PeekStyleDisplay(); michael@0: if (oldDisp && EqualURIs(disp->mBinding, oldDisp->mBinding)) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return RecreateFramesForContent(aElement, false); michael@0: } michael@0: michael@0: static nsIFrame* michael@0: FindFirstNonWhitespaceChild(nsIFrame* aParentFrame) michael@0: { michael@0: nsIFrame* f = aParentFrame->GetFirstPrincipalChild(); michael@0: while (f && f->GetType() == nsGkAtoms::textFrame && michael@0: f->GetContent()->TextIsOnlyWhitespace()) { michael@0: f = f->GetNextSibling(); michael@0: } michael@0: return f; michael@0: } michael@0: michael@0: static nsIFrame* michael@0: FindNextNonWhitespaceSibling(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* f = aFrame; michael@0: do { michael@0: f = f->GetNextSibling(); michael@0: } while (f && f->GetType() == nsGkAtoms::textFrame && michael@0: f->GetContent()->TextIsOnlyWhitespace()); michael@0: return f; michael@0: } michael@0: michael@0: static nsIFrame* michael@0: FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* f = aFrame; michael@0: do { michael@0: f = f->GetPrevSibling(); michael@0: } while (f && f->GetType() == nsGkAtoms::textFrame && michael@0: f->GetContent()->TextIsOnlyWhitespace()); michael@0: return f; michael@0: } michael@0: michael@0: bool michael@0: nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, michael@0: nsresult* aResult) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Must have a frame"); michael@0: NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root"); michael@0: NS_PRECONDITION(aResult, "Null out param?"); michael@0: NS_PRECONDITION(aFrame == aFrame->FirstContinuation(), michael@0: "aFrame not the result of GetPrimaryFrame()?"); michael@0: michael@0: if (IsFramePartOfIBSplit(aFrame)) { michael@0: // The removal functions can't handle removal of an {ib} split directly; we michael@0: // need to rebuild the containing block. michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: " michael@0: "frame="); michael@0: nsFrame::ListTag(stdout, aFrame); michael@0: printf(" is ib-split\n"); michael@0: } michael@0: #endif michael@0: michael@0: *aResult = ReframeContainingBlock(aFrame); michael@0: return true; michael@0: } michael@0: michael@0: if (aFrame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame && michael@0: aFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame) { michael@0: // When we remove the legend for a fieldset, we should reframe michael@0: // the fieldset to ensure another legend is used, if there is one michael@0: *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), false); michael@0: return true; michael@0: } michael@0: michael@0: // Now check for possibly needing to reconstruct due to a pseudo parent michael@0: nsIFrame* inFlowFrame = michael@0: (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ? michael@0: GetPlaceholderFrameFor(aFrame) : aFrame; michael@0: MOZ_ASSERT(inFlowFrame, "How did that happen?"); michael@0: MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(), michael@0: "placeholder for primary frame has previous continuations?"); michael@0: nsIFrame* parent = inFlowFrame->GetParent(); michael@0: if (IsTablePseudo(parent)) { michael@0: if (FindFirstNonWhitespaceChild(parent) == inFlowFrame || michael@0: !FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()) || michael@0: // If we're a table-column-group, then the GetFirstChild check above is michael@0: // not going to catch cases when we're the first child. michael@0: (inFlowFrame->GetType() == nsGkAtoms::tableColGroupFrame && michael@0: parent->GetFirstChild(nsIFrame::kColGroupList) == inFlowFrame) || michael@0: // Similar if we're a table-caption. michael@0: (inFlowFrame->GetType() == nsGkAtoms::tableCaptionFrame && michael@0: parent->GetFirstChild(nsIFrame::kCaptionList) == inFlowFrame)) { michael@0: // We're the first or last frame in the pseudo. Need to reframe. michael@0: // Good enough to recreate frames for |parent|'s content michael@0: *aResult = RecreateFramesForContent(parent->GetContent(), true); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // Might need to reconstruct things if this frame's nextSibling is a table michael@0: // pseudo, since removal of this frame might mean that this pseudo needs to michael@0: // get merged with the frame's prevSibling if that's also a table pseudo. michael@0: nsIFrame* nextSibling = michael@0: FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()); michael@0: NS_ASSERTION(!IsTablePseudo(inFlowFrame), "Shouldn't happen here"); michael@0: if (nextSibling && IsTablePseudo(nextSibling)) { michael@0: nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame); michael@0: if (prevSibling && IsTablePseudo(prevSibling)) { michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: " michael@0: "frame="); michael@0: nsFrame::ListTag(stdout, aFrame); michael@0: printf(" has a table pseudo next sibling of different type and a " michael@0: "table pseudo prevsibling\n"); michael@0: } michael@0: #endif michael@0: // Good enough to recreate frames for aFrame's parent's content; even if michael@0: // aFrame's parent is a table pseudo, that'll be the right content node. michael@0: *aResult = RecreateFramesForContent(parent->GetContent(), true); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // Might need to reconstruct things if the removed frame's nextSibling is an michael@0: // anonymous flex item. The removed frame might've been what divided two michael@0: // runs of inline content into two anonymous flex items, which would now michael@0: // need to be merged. michael@0: // NOTE: It's fine that we've advanced nextSibling past whitespace (up above); michael@0: // we're only interested in anonymous flex items here, and those can never michael@0: // be adjacent to whitespace, since they absorb contiguous runs of inline michael@0: // non-replaced content (including whitespace). michael@0: if (nextSibling && IsAnonymousFlexItem(nextSibling)) { michael@0: NS_ABORT_IF_FALSE(parent->GetType() == nsGkAtoms::flexContainerFrame, michael@0: "anonymous flex items should only exist as children " michael@0: "of flex container frames"); michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: " michael@0: "frame="); michael@0: nsFrame::ListTag(stdout, aFrame); michael@0: printf(" has an anonymous flex item as its next sibling\n"); michael@0: } michael@0: #endif // DEBUG michael@0: // Recreate frames for the flex container (the removed frame's parent) michael@0: *aResult = RecreateFramesForContent(parent->GetContent(), true); michael@0: return true; michael@0: } michael@0: michael@0: // Might need to reconstruct things if the removed frame's nextSibling is michael@0: // null and its parent is an anonymous flex item. (This might be the last michael@0: // remaining child of that anonymous flex item, which can then go away.) michael@0: if (!nextSibling && IsAnonymousFlexItem(parent)) { michael@0: NS_ABORT_IF_FALSE(parent->GetParent() && michael@0: parent->GetParent()->GetType() == nsGkAtoms::flexContainerFrame, michael@0: "anonymous flex items should only exist as children " michael@0: "of flex container frames"); michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: " michael@0: "frame="); michael@0: nsFrame::ListTag(stdout, aFrame); michael@0: printf(" has an anonymous flex item as its parent\n"); michael@0: } michael@0: #endif // DEBUG michael@0: // Recreate frames for the flex container (the removed frame's grandparent) michael@0: *aResult = RecreateFramesForContent(parent->GetParent()->GetContent(), michael@0: true); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: if (aFrame->GetType() == nsGkAtoms::popupSetFrame) { michael@0: nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell); michael@0: if (rootBox && rootBox->GetPopupSetFrame() == aFrame) { michael@0: *aResult = ReconstructDocElementHierarchy(); michael@0: return true; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // Reconstruct if inflowFrame is parent's only child, and parent is, or has, michael@0: // a non-fluid continuation, i.e. it was split by bidi resolution michael@0: if (!inFlowFrame->GetPrevSibling() && michael@0: !inFlowFrame->GetNextSibling() && michael@0: ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) || michael@0: (parent->GetNextContinuation() && !parent->GetNextInFlow()))) { michael@0: *aResult = RecreateFramesForContent(parent->GetContent(), true); michael@0: return true; michael@0: } michael@0: michael@0: // We might still need to reconstruct things if the parent of inFlowFrame is michael@0: // ib-split, since in that case the removal of aFrame might affect the michael@0: // splitting of its parent. michael@0: if (!IsFramePartOfIBSplit(parent)) { michael@0: return false; michael@0: } michael@0: michael@0: // If inFlowFrame is not the only in-flow child of |parent|, then removing michael@0: // it will change nothing about the {ib} split. michael@0: if (inFlowFrame != parent->GetFirstPrincipalChild() || michael@0: inFlowFrame->LastContinuation()->GetNextSibling()) { michael@0: return false; michael@0: } michael@0: michael@0: // If the parent is the first or last part of the {ib} split, then michael@0: // removing one of its kids will have no effect on the splitting. michael@0: // Get the first continuation up front so we don't have to do it twice. michael@0: nsIFrame* parentFirstContinuation = parent->FirstContinuation(); michael@0: if (!GetIBSplitSibling(parentFirstContinuation) || michael@0: !GetIBSplitPrevSibling(parentFirstContinuation)) { michael@0: return false; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: " michael@0: "frame="); michael@0: nsFrame::ListTag(stdout, parent); michael@0: printf(" is ib-split\n"); michael@0: } michael@0: #endif michael@0: michael@0: *aResult = ReframeContainingBlock(parent); michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, michael@0: bool aAsyncInsert) michael@0: { michael@0: NS_PRECONDITION(!aAsyncInsert || aContent->IsElement(), michael@0: "Can only insert elements async"); michael@0: // If there is no document, we don't want to recreate frames for it. (You michael@0: // shouldn't generally be giving this method content without a document michael@0: // anyway). michael@0: // Rebuilding the frame tree can have bad effects, especially if it's the michael@0: // frame tree for chrome (see bug 157322). michael@0: NS_ENSURE_TRUE(aContent->GetDocument(), NS_ERROR_FAILURE); michael@0: michael@0: // Is the frame ib-split? If so, we need to reframe the containing michael@0: // block *here*, rather than trying to remove and re-insert the michael@0: // content (which would otherwise result in *two* nested reframe michael@0: // containing block from ContentRemoved() and ContentInserted(), michael@0: // below!). We'd really like to optimize away one of those michael@0: // containing block reframes, hence the code here. michael@0: michael@0: nsIFrame* frame = aContent->GetPrimaryFrame(); michael@0: if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) { michael@0: // Reframe the topmost MathML element to prevent exponential blowup michael@0: // (see bug 397518) michael@0: while (true) { michael@0: nsIContent* parentContent = aContent->GetParent(); michael@0: nsIFrame* parentContentFrame = parentContent->GetPrimaryFrame(); michael@0: if (!parentContentFrame || !parentContentFrame->IsFrameOfType(nsIFrame::eMathML)) michael@0: break; michael@0: aContent = parentContent; michael@0: frame = parentContentFrame; michael@0: } michael@0: } michael@0: michael@0: if (frame) { michael@0: nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(frame); michael@0: if (nonGeneratedAncestor->GetContent() != aContent) { michael@0: return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), aAsyncInsert); michael@0: } michael@0: michael@0: if (frame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) { michael@0: // Recreate the frames for the entire nsIAnonymousContentCreator tree michael@0: // since |frame| or one of its descendants may need an nsStyleContext michael@0: // that associates it to a CSS pseudo-element, and only the michael@0: // nsIAnonymousContentCreator that created this content knows how to make michael@0: // that happen. michael@0: nsIAnonymousContentCreator* acc = nullptr; michael@0: nsIFrame* ancestor = frame->GetParent(); michael@0: while (!(acc = do_QueryFrame(ancestor))) { michael@0: ancestor = ancestor->GetParent(); michael@0: } michael@0: NS_ASSERTION(acc, "Where is the nsIAnonymousContentCreator? We may fail " michael@0: "to recreate its content correctly"); michael@0: // nsSVGUseFrame is special, and we know this is unnecessary for it. michael@0: if (ancestor->GetType() != nsGkAtoms::svgUseFrame) { michael@0: NS_ASSERTION(aContent->IsInNativeAnonymousSubtree(), michael@0: "Why is NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT set?"); michael@0: return RecreateFramesForContent(ancestor->GetContent(), aAsyncInsert); michael@0: } michael@0: } michael@0: michael@0: nsIFrame* parent = frame->GetParent(); michael@0: nsIContent* parentContent = parent ? parent->GetContent() : nullptr; michael@0: // If the parent frame is a leaf then the subsequent insert will fail to michael@0: // create a frame, so we need to recreate the parent content. This happens michael@0: // with native anonymous content from the editor. michael@0: if (parent && parent->IsLeaf() && parentContent && michael@0: parentContent != aContent) { michael@0: return RecreateFramesForContent(parentContent, aAsyncInsert); michael@0: } michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (frame && MaybeRecreateContainerForFrameRemoval(frame, &rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsINode* containerNode = aContent->GetParentNode(); michael@0: // XXXbz how can containerNode be null here? michael@0: if (containerNode) { michael@0: // Before removing the frames associated with the content object, michael@0: // ask them to save their state onto a temporary state object. michael@0: CaptureStateForFramesOf(aContent, mTempFrameTreeState); michael@0: michael@0: // Need the nsIContent parent, which might be null here, since we need to michael@0: // pass it to ContentInserted and ContentRemoved. michael@0: nsCOMPtr container = aContent->GetParent(); michael@0: michael@0: // Remove the frames associated with the content object. michael@0: bool didReconstruct; michael@0: rv = ContentRemoved(container, aContent, michael@0: aContent->IsRootOfAnonymousSubtree() ? michael@0: nullptr : michael@0: aContent->GetNextSibling(), michael@0: REMOVE_FOR_RECONSTRUCTION, &didReconstruct); michael@0: michael@0: if (NS_SUCCEEDED(rv) && !didReconstruct) { michael@0: // Now, recreate the frames associated with this content object. If michael@0: // ContentRemoved triggered reconstruction, then we don't need to do this michael@0: // because the frames will already have been built. michael@0: if (aAsyncInsert) { michael@0: RestyleManager()->PostRestyleEvent( michael@0: aContent->AsElement(), nsRestyleHint(0), nsChangeHint_ReconstructFrame); michael@0: } else { michael@0: rv = ContentInserted(container, aContent, mTempFrameTreeState, false); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////// michael@0: michael@0: // Block frame construction code michael@0: michael@0: already_AddRefed michael@0: nsCSSFrameConstructor::GetFirstLetterStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (aContent) { michael@0: return mPresShell->StyleSet()-> michael@0: ResolvePseudoElementStyle(aContent->AsElement(), michael@0: nsCSSPseudoElements::ePseudo_firstLetter, michael@0: aStyleContext, michael@0: nullptr); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsCSSFrameConstructor::GetFirstLineStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (aContent) { michael@0: return mPresShell->StyleSet()-> michael@0: ResolvePseudoElementStyle(aContent->AsElement(), michael@0: nsCSSPseudoElements::ePseudo_firstLine, michael@0: aStyleContext, michael@0: nullptr); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: // Predicate to see if a given content (block element) has michael@0: // first-letter style applied to it. michael@0: bool michael@0: nsCSSFrameConstructor::ShouldHaveFirstLetterStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext, michael@0: nsCSSPseudoElements::ePseudo_firstLetter, michael@0: mPresShell->GetPresContext()); michael@0: } michael@0: michael@0: bool michael@0: nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame) michael@0: { michael@0: NS_PRECONDITION(aBlockFrame, "Need a frame"); michael@0: NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame), michael@0: "Not a block frame?"); michael@0: michael@0: return (aBlockFrame->GetStateBits() & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0; michael@0: } michael@0: michael@0: bool michael@0: nsCSSFrameConstructor::ShouldHaveFirstLineStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: bool hasFirstLine = michael@0: nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext, michael@0: nsCSSPseudoElements::ePseudo_firstLine, michael@0: mPresShell->GetPresContext()); michael@0: if (hasFirstLine) { michael@0: // But disable for fieldsets michael@0: int32_t namespaceID; michael@0: nsIAtom* tag = mDocument->BindingManager()->ResolveTag(aContent, michael@0: &namespaceID); michael@0: // This check must match the one in FindHTMLData. michael@0: hasFirstLine = tag != nsGkAtoms::fieldset || michael@0: namespaceID != kNameSpaceID_XHTML; michael@0: } michael@0: michael@0: return hasFirstLine; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle(nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: bool* aHaveFirstLetterStyle, michael@0: bool* aHaveFirstLineStyle) michael@0: { michael@0: *aHaveFirstLetterStyle = michael@0: ShouldHaveFirstLetterStyle(aContent, aStyleContext); michael@0: *aHaveFirstLineStyle = michael@0: ShouldHaveFirstLineStyle(aContent, aStyleContext); michael@0: } michael@0: michael@0: /* static */ michael@0: const nsCSSFrameConstructor::PseudoParentData michael@0: nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = { michael@0: { // Cell michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | michael@0: FCDATA_USE_CHILD_ITEMS | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow), michael@0: &nsCSSFrameConstructor::ConstructTableCell), michael@0: &nsCSSAnonBoxes::tableCell michael@0: }, michael@0: { // Row michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | michael@0: FCDATA_USE_CHILD_ITEMS | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup), michael@0: &nsCSSFrameConstructor::ConstructTableRowOrRowGroup), michael@0: &nsCSSAnonBoxes::tableRow michael@0: }, michael@0: { // Row group michael@0: FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | michael@0: FCDATA_USE_CHILD_ITEMS | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), michael@0: &nsCSSFrameConstructor::ConstructTableRowOrRowGroup), michael@0: &nsCSSAnonBoxes::tableRowGroup michael@0: }, michael@0: { // Column group michael@0: FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | michael@0: FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS | michael@0: FCDATA_SKIP_ABSPOS_PUSH | michael@0: FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable), michael@0: NS_NewTableColGroupFrame), michael@0: &nsCSSAnonBoxes::tableColGroup michael@0: }, michael@0: { // Table michael@0: FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS, michael@0: &nsCSSFrameConstructor::ConstructTable), michael@0: &nsCSSAnonBoxes::table michael@0: } michael@0: }; michael@0: michael@0: void michael@0: nsCSSFrameConstructor::CreateNeededAnonFlexItems( michael@0: nsFrameConstructorState& aState, michael@0: FrameConstructionItemList& aItems, michael@0: nsIFrame* aParentFrame) michael@0: { michael@0: if (aItems.IsEmpty() || michael@0: aParentFrame->GetType() != nsGkAtoms::flexContainerFrame) { michael@0: return; michael@0: } michael@0: michael@0: FCItemIterator iter(aItems); michael@0: do { michael@0: // Advance iter past children that don't want to be wrapped michael@0: if (iter.SkipItemsThatDontNeedAnonFlexItem(aState)) { michael@0: // Hit the end of the items without finding any remaining children that michael@0: // need to be wrapped. We're finished! michael@0: return; michael@0: } michael@0: michael@0: // If our next potentially-wrappable child is whitespace, then see if michael@0: // there's anything wrappable immediately after it. If not, we just drop michael@0: // the whitespace and move on. (We're not supposed to create any anonymous michael@0: // flex items that _only_ contain whitespace). michael@0: // (BUT if this is generated content, then we don't give whitespace nodes michael@0: // any special treatment, because they're probably not really whitespace -- michael@0: // they're just temporarily empty, waiting for their generated text.) michael@0: // XXXdholbert If this node's generated text will *actually end up being michael@0: // entirely whitespace*, then we technically should still skip over it, per michael@0: // the flexbox spec. I'm not bothering with that at this point, since it's michael@0: // a pretty extreme edge case. michael@0: if (!aParentFrame->IsGeneratedContentFrame() && michael@0: iter.item().IsWhitespace(aState)) { michael@0: FCItemIterator afterWhitespaceIter(iter); michael@0: bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState); michael@0: bool nextChildNeedsAnonFlexItem = michael@0: !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexItem(aState); michael@0: michael@0: if (!nextChildNeedsAnonFlexItem) { michael@0: // There's nothing after the whitespace that we need to wrap, so we michael@0: // just drop this run of whitespace. michael@0: iter.DeleteItemsTo(afterWhitespaceIter); michael@0: if (hitEnd) { michael@0: // Nothing left to do -- we're finished! michael@0: return; michael@0: } michael@0: // else, we have a next child and it does not want to be wrapped. So, michael@0: // we jump back to the beginning of the loop to skip over that child michael@0: // (and anything else non-wrappable after it) michael@0: NS_ABORT_IF_FALSE(!iter.IsDone() && michael@0: !iter.item().NeedsAnonFlexItem(aState), michael@0: "hitEnd and/or nextChildNeedsAnonFlexItem lied"); michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: // Now |iter| points to the first child that needs to be wrapped in an michael@0: // anonymous flex item. Now we see how many children after it also want michael@0: // to be wrapped in an anonymous flex item. michael@0: FCItemIterator endIter(iter); // iterator to find the end of the group michael@0: endIter.SkipItemsThatNeedAnonFlexItem(aState); michael@0: michael@0: NS_ASSERTION(iter != endIter, michael@0: "Should've had at least one wrappable child to seek past"); michael@0: michael@0: // Now, we create the anonymous flex item to contain the children michael@0: // between |iter| and |endIter|. michael@0: nsIAtom* pseudoType = nsCSSAnonBoxes::anonymousFlexItem; michael@0: nsStyleContext* parentStyle = aParentFrame->StyleContext(); michael@0: nsIContent* parentContent = aParentFrame->GetContent(); michael@0: already_AddRefed wrapperStyle = michael@0: mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle); michael@0: michael@0: static const FrameConstructionData sBlockFormattingContextFCData = michael@0: FCDATA_DECL(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS, michael@0: NS_NewBlockFormattingContext); michael@0: michael@0: FrameConstructionItem* newItem = michael@0: new FrameConstructionItem(&sBlockFormattingContextFCData, michael@0: // Use the content of our parent frame michael@0: parentContent, michael@0: // Lie about the tag; it doesn't matter anyway michael@0: pseudoType, michael@0: iter.item().mNameSpaceID, michael@0: // no pending binding michael@0: nullptr, michael@0: wrapperStyle, michael@0: true, nullptr); michael@0: michael@0: newItem->mIsAllInline = newItem->mHasInlineEnds = michael@0: newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle(); michael@0: newItem->mIsBlock = !newItem->mIsAllInline; michael@0: michael@0: NS_ABORT_IF_FALSE(!newItem->mIsAllInline && newItem->mIsBlock, michael@0: "expecting anonymous flex items to be block-level " michael@0: "(this will make a difference when we encounter " michael@0: "'flex-align: baseline')"); michael@0: michael@0: // Anonymous flex items induce line boundaries around their michael@0: // contents. michael@0: newItem->mChildItems.SetLineBoundaryAtStart(true); michael@0: newItem->mChildItems.SetLineBoundaryAtEnd(true); michael@0: // The parent of the items in aItems is also the parent of the items michael@0: // in mChildItems michael@0: newItem->mChildItems.SetParentHasNoXBLChildren( michael@0: aItems.ParentHasNoXBLChildren()); michael@0: michael@0: // Eat up all items between |iter| and |endIter| and put them in our michael@0: // wrapper. This advances |iter| to point to |endIter|. michael@0: iter.AppendItemsToList(endIter, newItem->mChildItems); michael@0: michael@0: iter.InsertItem(newItem); michael@0: } while (!iter.IsDone()); michael@0: } michael@0: michael@0: /* michael@0: * This function works as follows: we walk through the child list (aItems) and michael@0: * find items that cannot have aParentFrame as their parent. We wrap michael@0: * continuous runs of such items into a FrameConstructionItem for a frame that michael@0: * gets them closer to their desired parents. For example, a run of non-row michael@0: * children of a row-group will get wrapped in a row. When we later construct michael@0: * the frame for this wrapper (in this case for the row), it'll be the correct michael@0: * parent for the cells in the set of items we wrapped or we'll wrap cells michael@0: * around everything else. At the end of this method, aItems is guaranteed to michael@0: * contain only items for frames that can be direct kids of aParentFrame. michael@0: */ michael@0: void michael@0: nsCSSFrameConstructor::CreateNeededTablePseudos(nsFrameConstructorState& aState, michael@0: FrameConstructionItemList& aItems, michael@0: nsIFrame* aParentFrame) michael@0: { michael@0: ParentType ourParentType = GetParentType(aParentFrame); michael@0: if (aItems.AllWantParentType(ourParentType)) { michael@0: // Nothing to do here michael@0: return; michael@0: } michael@0: michael@0: FCItemIterator iter(aItems); michael@0: do { michael@0: if (iter.SkipItemsWantingParentType(ourParentType)) { michael@0: // Nothing else to do here; we're finished michael@0: return; michael@0: } michael@0: michael@0: // Now we're pointing to the first child that wants a different parent michael@0: // type. michael@0: michael@0: // Now try to figure out what kids we can group together. We can generally michael@0: // group everything that has a different desired parent type from us. Two michael@0: // exceptions to this: michael@0: // 1) If our parent type is table, we can't group columns with anything michael@0: // else other than whitespace. michael@0: // 2) Whitespace that lies between two things we can group which both want michael@0: // a non-block parent should be dropped, even if we can't group them michael@0: // with each other and even if the whitespace wants a parent of michael@0: // ourParentType. Ends of the list count as things that don't want a michael@0: // block parent (so that for example we'll drop a whitespace-only list). michael@0: michael@0: FCItemIterator endIter(iter); /* iterator to find the end of the group */ michael@0: ParentType groupingParentType = endIter.item().DesiredParentType(); michael@0: if (aItems.AllWantParentType(groupingParentType) && michael@0: groupingParentType != eTypeBlock) { michael@0: // Just group them all and be done with it. We need the check for michael@0: // eTypeBlock here to catch the "all the items are whitespace" case michael@0: // described above. michael@0: endIter.SetToEnd(); michael@0: } else { michael@0: // Locate the end of the group. michael@0: michael@0: // Keep track of the type the previous item wanted, in case we have to michael@0: // deal with whitespace. Start it off with ourParentType, since that's michael@0: // the last thing |iter| would have skipped over. michael@0: ParentType prevParentType = ourParentType; michael@0: do { michael@0: /* Walk an iterator past any whitespace that we might be able to drop from the list */ michael@0: FCItemIterator spaceEndIter(endIter); michael@0: if (prevParentType != eTypeBlock && michael@0: !aParentFrame->IsGeneratedContentFrame() && michael@0: spaceEndIter.item().IsWhitespace(aState)) { michael@0: bool trailingSpaces = spaceEndIter.SkipWhitespace(aState); michael@0: michael@0: // We drop the whitespace if these are not trailing spaces and the next item michael@0: // does not want a block parent (see case 2 above) michael@0: // if these are trailing spaces and aParentFrame is a tabular container michael@0: // according to rule 1.3 of CSS 2.1 Sec 17.2.1. (Being a tabular container michael@0: // pretty much means ourParentType != eTypeBlock besides the eTypeColGroup case, michael@0: // which won't reach here.) michael@0: if ((trailingSpaces && ourParentType != eTypeBlock) || michael@0: (!trailingSpaces && spaceEndIter.item().DesiredParentType() != michael@0: eTypeBlock)) { michael@0: bool updateStart = (iter == endIter); michael@0: endIter.DeleteItemsTo(spaceEndIter); michael@0: NS_ASSERTION(trailingSpaces == endIter.IsDone(), "These should match"); michael@0: michael@0: if (updateStart) { michael@0: iter = endIter; michael@0: } michael@0: michael@0: if (trailingSpaces) { michael@0: break; /* Found group end */ michael@0: } michael@0: michael@0: if (updateStart) { michael@0: // Update groupingParentType, since it might have been eTypeBlock michael@0: // just because of the whitespace. michael@0: groupingParentType = iter.item().DesiredParentType(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Now endIter points to a non-whitespace item or a non-droppable michael@0: // whitespace item. In the latter case, if this is the end of the group michael@0: // we'll traverse this whitespace again. But it'll all just be quick michael@0: // DesiredParentType() checks which will match ourParentType (that's michael@0: // what it means that this is the group end), so it's OK. michael@0: prevParentType = endIter.item().DesiredParentType(); michael@0: if (prevParentType == ourParentType) { michael@0: // End the group at endIter. michael@0: break; michael@0: } michael@0: michael@0: if (ourParentType == eTypeTable && michael@0: (prevParentType == eTypeColGroup) != michael@0: (groupingParentType == eTypeColGroup)) { michael@0: // Either we started with columns and now found something else, or vice michael@0: // versa. In any case, end the grouping. michael@0: break; michael@0: } michael@0: michael@0: // Include the whitespace we didn't drop (if any) in the group, since michael@0: // this is not the end of the group. Note that this doesn't change michael@0: // prevParentType, since if we didn't drop the whitespace then we ended michael@0: // at something that wants a block parent. michael@0: endIter = spaceEndIter; michael@0: michael@0: endIter.Next(); michael@0: } while (!endIter.IsDone()); michael@0: } michael@0: michael@0: if (iter == endIter) { michael@0: // Nothing to wrap here; just skipped some whitespace michael@0: continue; michael@0: } michael@0: michael@0: // Now group together all the items between iter and endIter. The right michael@0: // parent type to use depends on ourParentType. michael@0: ParentType wrapperType; michael@0: switch (ourParentType) { michael@0: case eTypeBlock: michael@0: wrapperType = eTypeTable; michael@0: break; michael@0: case eTypeRow: michael@0: // The parent type for a cell is eTypeBlock, since that's what a cell michael@0: // looks like to its kids. michael@0: wrapperType = eTypeBlock; michael@0: break; michael@0: case eTypeRowGroup: michael@0: wrapperType = eTypeRow; michael@0: break; michael@0: case eTypeTable: michael@0: // Either colgroup or rowgroup, depending on what we're grouping. michael@0: wrapperType = groupingParentType == eTypeColGroup ? michael@0: eTypeColGroup : eTypeRowGroup; michael@0: break; michael@0: default: michael@0: MOZ_CRASH("Colgroups should be suppresing non-col child items"); michael@0: } michael@0: michael@0: const PseudoParentData& pseudoData = sPseudoParentData[wrapperType]; michael@0: nsIAtom* pseudoType = *pseudoData.mPseudoType; michael@0: nsStyleContext* parentStyle = aParentFrame->StyleContext(); michael@0: nsIContent* parentContent = aParentFrame->GetContent(); michael@0: michael@0: if (pseudoType == nsCSSAnonBoxes::table && michael@0: parentStyle->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE) { michael@0: pseudoType = nsCSSAnonBoxes::inlineTable; michael@0: } michael@0: michael@0: already_AddRefed wrapperStyle = michael@0: mPresShell->StyleSet()->ResolveAnonymousBoxStyle(pseudoType, parentStyle); michael@0: FrameConstructionItem* newItem = michael@0: new FrameConstructionItem(&pseudoData.mFCData, michael@0: // Use the content of our parent frame michael@0: parentContent, michael@0: // Lie about the tag; it doesn't matter anyway michael@0: pseudoType, michael@0: // The namespace does matter, however; it needs michael@0: // to match that of our first child item to michael@0: // match the old behavior michael@0: iter.item().mNameSpaceID, michael@0: // no pending binding michael@0: nullptr, michael@0: wrapperStyle, michael@0: true, nullptr); michael@0: michael@0: // Here we're cheating a tad... technically, table-internal items should be michael@0: // inline if aParentFrame is inline, but they'll get wrapped in an michael@0: // inline-table in the end, so it'll all work out. In any case, arguably michael@0: // we don't need to maintain this state at this point... but it's better michael@0: // to, I guess. michael@0: newItem->mIsAllInline = newItem->mHasInlineEnds = michael@0: newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle(); michael@0: michael@0: // Table pseudo frames always induce line boundaries around their michael@0: // contents. michael@0: newItem->mChildItems.SetLineBoundaryAtStart(true); michael@0: newItem->mChildItems.SetLineBoundaryAtEnd(true); michael@0: // The parent of the items in aItems is also the parent of the items michael@0: // in mChildItems michael@0: newItem->mChildItems.SetParentHasNoXBLChildren( michael@0: aItems.ParentHasNoXBLChildren()); michael@0: michael@0: // Eat up all items between |iter| and |endIter| and put them in our wrapper michael@0: // Advances |iter| to point to |endIter|. michael@0: iter.AppendItemsToList(endIter, newItem->mChildItems); michael@0: michael@0: iter.InsertItem(newItem); michael@0: michael@0: // Now |iter| points to the item that was the first one we didn't wrap; michael@0: // loop and see whether we need to skip it or wrap it in something michael@0: // different. michael@0: } while (!iter.IsDone()); michael@0: } michael@0: michael@0: inline void michael@0: nsCSSFrameConstructor::ConstructFramesFromItemList(nsFrameConstructorState& aState, michael@0: FrameConstructionItemList& aItems, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: CreateNeededTablePseudos(aState, aItems, aParentFrame); michael@0: CreateNeededAnonFlexItems(aState, aItems, aParentFrame); michael@0: michael@0: aItems.SetTriedConstructingFrames(); michael@0: for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) { michael@0: NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame), michael@0: "Needed pseudos didn't get created; expect bad things"); michael@0: ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems); michael@0: } michael@0: michael@0: NS_ASSERTION(!aState.mHavePendingPopupgroup, michael@0: "Should have proccessed it by now"); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::AddFCItemsForAnonymousContent( michael@0: nsFrameConstructorState& aState, michael@0: nsIFrame* aFrame, michael@0: nsTArray& aAnonymousItems, michael@0: FrameConstructionItemList& aItemsToConstruct, michael@0: uint32_t aExtraFlags) michael@0: { michael@0: for (uint32_t i = 0; i < aAnonymousItems.Length(); ++i) { michael@0: nsIContent* content = aAnonymousItems[i].mContent; michael@0: #ifdef DEBUG michael@0: nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame); michael@0: NS_ASSERTION(!creator || !creator->CreateFrameFor(content), michael@0: "If you need to use CreateFrameFor, you need to call " michael@0: "CreateAnonymousFrames manually and not follow the standard " michael@0: "ProcessChildren() codepath for this frame"); michael@0: #endif michael@0: // Assert some things about this content michael@0: NS_ABORT_IF_FALSE(!(content->GetFlags() & michael@0: (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)), michael@0: "Should not be marked as needing frames"); michael@0: NS_ABORT_IF_FALSE(!content->IsElement() || michael@0: !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS), michael@0: "Should have no pending restyle flags"); michael@0: NS_ABORT_IF_FALSE(!content->GetPrimaryFrame(), michael@0: "Should have no existing frame"); michael@0: NS_ABORT_IF_FALSE(!content->IsNodeOfType(nsINode::eCOMMENT) && michael@0: !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION), michael@0: "Why is someone creating garbage anonymous content"); michael@0: michael@0: nsRefPtr styleContext; michael@0: TreeMatchContext::AutoFlexItemStyleFixupSkipper michael@0: flexItemStyleFixupSkipper(aState.mTreeMatchContext); michael@0: if (aAnonymousItems[i].mStyleContext) { michael@0: styleContext = aAnonymousItems[i].mStyleContext.forget(); michael@0: } else { michael@0: styleContext = ResolveStyleContext(aFrame, content, &aState); michael@0: } michael@0: michael@0: nsTArray* anonChildren = nullptr; michael@0: if (!aAnonymousItems[i].mChildren.IsEmpty()) { michael@0: anonChildren = &aAnonymousItems[i].mChildren; michael@0: } michael@0: michael@0: uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK | michael@0: ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT | aExtraFlags; michael@0: michael@0: AddFrameConstructionItemsInternal(aState, content, aFrame, michael@0: content->Tag(), content->GetNameSpaceID(), michael@0: true, styleContext, flags, michael@0: anonChildren, aItemsToConstruct); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame* aFrame, michael@0: const bool aCanHaveGeneratedContent, michael@0: nsFrameItems& aFrameItems, michael@0: const bool aAllowBlockStyles, michael@0: PendingBinding* aPendingBinding, michael@0: nsIFrame* aPossiblyLeafFrame) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Must have parent frame here"); michael@0: NS_PRECONDITION(aFrame->GetContentInsertionFrame() == aFrame, michael@0: "Parent frame in ProcessChildren should be its own " michael@0: "content insertion frame"); michael@0: const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH; michael@0: static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow"); michael@0: AutoRestore savedDepth(mCurrentDepth); michael@0: if (mCurrentDepth != UINT16_MAX) { michael@0: ++mCurrentDepth; michael@0: } michael@0: michael@0: if (!aPossiblyLeafFrame) { michael@0: aPossiblyLeafFrame = aFrame; michael@0: } michael@0: michael@0: // XXXbz ideally, this would do all the pushing of various michael@0: // containing blocks as needed, so callers don't have to do it... michael@0: michael@0: bool haveFirstLetterStyle = false, haveFirstLineStyle = false; michael@0: if (aAllowBlockStyles) { michael@0: ShouldHaveSpecialBlockStyle(aContent, aStyleContext, &haveFirstLetterStyle, michael@0: &haveFirstLineStyle); michael@0: } michael@0: michael@0: // The logic here needs to match the logic in GetFloatContainingBlock() michael@0: nsFrameConstructorSaveState floatSaveState; michael@0: if (ShouldSuppressFloatingOfDescendants(aFrame)) { michael@0: aState.PushFloatContainingBlock(nullptr, floatSaveState); michael@0: } else if (aFrame->IsFloatContainingBlock()) { michael@0: aState.PushFloatContainingBlock(aFrame, floatSaveState); michael@0: } michael@0: michael@0: nsFrameConstructorState::PendingBindingAutoPusher pusher(aState, michael@0: aPendingBinding); michael@0: michael@0: FrameConstructionItemList itemsToConstruct; michael@0: michael@0: // If we have first-letter or first-line style then frames can get michael@0: // moved around so don't set these flags. michael@0: if (aAllowBlockStyles && !haveFirstLetterStyle && !haveFirstLineStyle) { michael@0: itemsToConstruct.SetLineBoundaryAtStart(true); michael@0: itemsToConstruct.SetLineBoundaryAtEnd(true); michael@0: } michael@0: michael@0: // Create any anonymous frames we need here. This must happen before the michael@0: // non-anonymous children are processed to ensure that popups are never michael@0: // constructed before the popupset. michael@0: nsAutoTArray anonymousItems; michael@0: GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems); michael@0: #ifdef DEBUG michael@0: for (uint32_t i = 0; i < anonymousItems.Length(); ++i) { michael@0: NS_ABORT_IF_FALSE(anonymousItems[i].mContent->IsRootOfAnonymousSubtree(), michael@0: "Content should know it's an anonymous subtree"); michael@0: } michael@0: #endif michael@0: AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems, michael@0: itemsToConstruct); michael@0: michael@0: if (!aPossiblyLeafFrame->IsLeaf()) { michael@0: // :before/:after content should have the same style context parent michael@0: // as normal kids. michael@0: // Note that we don't use this style context for looking up things like michael@0: // special block styles because in some cases involving table pseudo-frames michael@0: // it has nothing to do with the parent frame's desired behavior. michael@0: nsStyleContext* styleContext; michael@0: michael@0: if (aCanHaveGeneratedContent) { michael@0: aFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT); michael@0: styleContext = michael@0: nsFrame::CorrectStyleParentFrame(aFrame, nullptr)->StyleContext(); michael@0: // Probe for generated content before michael@0: CreateGeneratedContentItem(aState, aFrame, aContent, styleContext, michael@0: nsCSSPseudoElements::ePseudo_before, michael@0: itemsToConstruct); michael@0: } michael@0: michael@0: const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth); michael@0: if (!addChildItems) { michael@0: NS_WARNING("ProcessChildren max depth exceeded"); michael@0: } michael@0: michael@0: FlattenedChildIterator iter(aContent); michael@0: for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { michael@0: // Get the parent of the content and check if it is a XBL children element michael@0: // (if the content is a children element then parent != aContent because the michael@0: // FlattenedChildIterator will transitively iterate through michael@0: // for default content). Push the children element as an ancestor here because michael@0: // it does not have a frame and would not otherwise be pushed as an ancestor. michael@0: nsIContent* parent = child->GetParent(); michael@0: MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children."); michael@0: TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext); michael@0: if (parent != aContent && parent->IsElement()) { michael@0: if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { michael@0: ancestorPusher.PushAncestorAndStyleScope(parent->AsElement()); michael@0: } else { michael@0: ancestorPusher.PushStyleScope(parent->AsElement()); michael@0: } michael@0: } michael@0: michael@0: // Frame construction item construction should not post michael@0: // restyles, so removing restyle flags here is safe. michael@0: if (child->IsElement()) { michael@0: child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS); michael@0: } michael@0: if (addChildItems) { michael@0: AddFrameConstructionItems(aState, child, iter.XBLInvolved(), aFrame, michael@0: itemsToConstruct); michael@0: } else { michael@0: ClearLazyBits(child, child->GetNextSibling()); michael@0: } michael@0: } michael@0: itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved()); michael@0: michael@0: if (aCanHaveGeneratedContent) { michael@0: // Probe for generated content after michael@0: CreateGeneratedContentItem(aState, aFrame, aContent, styleContext, michael@0: nsCSSPseudoElements::ePseudo_after, michael@0: itemsToConstruct); michael@0: } michael@0: } else { michael@0: ClearLazyBits(aContent->GetFirstChild(), nullptr); michael@0: } michael@0: michael@0: ConstructFramesFromItemList(aState, itemsToConstruct, aFrame, aFrameItems); michael@0: michael@0: NS_ASSERTION(!aAllowBlockStyles || !aFrame->IsBoxFrame(), michael@0: "can't be both block and box"); michael@0: michael@0: if (haveFirstLetterStyle) { michael@0: WrapFramesInFirstLetterFrame(aContent, aFrame, aFrameItems); michael@0: } michael@0: if (haveFirstLineStyle) { michael@0: WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr, michael@0: aFrameItems); michael@0: } michael@0: michael@0: // We might end up with first-line frames that change michael@0: // AnyKidsNeedBlockParent() without changing itemsToConstruct, but that michael@0: // should never happen for cases whan aFrame->IsBoxFrame(). michael@0: NS_ASSERTION(!haveFirstLineStyle || !aFrame->IsBoxFrame(), michael@0: "Shouldn't have first-line style if we're a box"); michael@0: NS_ASSERTION(!aFrame->IsBoxFrame() || michael@0: itemsToConstruct.AnyItemsNeedBlockParent() == michael@0: (AnyKidsNeedBlockParent(aFrameItems.FirstChild()) != nullptr), michael@0: "Something went awry in our block parent calculations"); michael@0: michael@0: if (aFrame->IsBoxFrame() && itemsToConstruct.AnyItemsNeedBlockParent()) { michael@0: // XXXbz we could do this on the FrameConstructionItemList level, michael@0: // no? And if we cared we could look through the item list michael@0: // instead of groveling through the framelist here.. michael@0: nsStyleContext *frameStyleContext = aFrame->StyleContext(); michael@0: // Report a warning for non-GC frames, for chrome: michael@0: if (!aFrame->IsGeneratedContentFrame() && michael@0: mPresShell->GetPresContext()->IsChrome()) { michael@0: nsIContent *badKid = AnyKidsNeedBlockParent(aFrameItems.FirstChild()); michael@0: nsDependentAtomString parentTag(aContent->Tag()), kidTag(badKid->Tag()); michael@0: const char16_t* params[] = { parentTag.get(), kidTag.get() }; michael@0: const nsStyleDisplay *display = frameStyleContext->StyleDisplay(); michael@0: const char *message = michael@0: (display->mDisplay == NS_STYLE_DISPLAY_INLINE_BOX) michael@0: ? "NeededToWrapXULInlineBox" : "NeededToWrapXUL"; michael@0: nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("FrameConstructor"), michael@0: mDocument, michael@0: nsContentUtils::eXUL_PROPERTIES, michael@0: message, michael@0: params, ArrayLength(params)); michael@0: } michael@0: michael@0: nsRefPtr blockSC = mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozXULAnonymousBlock, michael@0: frameStyleContext); michael@0: nsIFrame *blockFrame = NS_NewBlockFrame(mPresShell, blockSC); michael@0: // We might, in theory, want to set NS_BLOCK_FLOAT_MGR and michael@0: // NS_BLOCK_MARGIN_ROOT, but I think it's a bad idea given that michael@0: // a real block placed here wouldn't get those set on it. michael@0: michael@0: InitAndRestoreFrame(aState, aContent, aFrame, blockFrame, false); michael@0: michael@0: NS_ASSERTION(!blockFrame->HasView(), "need to do view reparenting"); michael@0: ReparentFrames(this, blockFrame, aFrameItems); michael@0: michael@0: blockFrame->SetInitialChildList(kPrincipalList, aFrameItems); michael@0: NS_ASSERTION(aFrameItems.IsEmpty(), "How did that happen?"); michael@0: aFrameItems.Clear(); michael@0: aFrameItems.AddChild(blockFrame); michael@0: michael@0: aFrame->AddStateBits(NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK); michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: // Support for :first-line style michael@0: michael@0: // Special routine to handle placing a list of frames into a block michael@0: // frame that has first-line style. The routine ensures that the first michael@0: // collection of inline frames end up in a first-line frame. michael@0: // NOTE: aState may have containing block information related to a michael@0: // different part of the frame tree than where the first line occurs. michael@0: // In particular aState may be set up for where ContentInserted or michael@0: // ContentAppended is inserting content, which may be some michael@0: // non-first-in-flow continuation of the block to which the first-line michael@0: // belongs. So this function needs to be careful about how it uses michael@0: // aState. michael@0: void michael@0: nsCSSFrameConstructor::WrapFramesInFirstLineFrame( michael@0: nsFrameConstructorState& aState, michael@0: nsIContent* aBlockContent, michael@0: nsIFrame* aBlockFrame, michael@0: nsIFrame* aLineFrame, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: // Find the part of aFrameItems that we want to put in the first-line michael@0: nsFrameList::FrameLinkEnumerator link(aFrameItems); michael@0: while (!link.AtEnd() && link.NextFrame()->IsInlineOutside()) { michael@0: link.Next(); michael@0: } michael@0: michael@0: nsFrameList firstLineChildren = aFrameItems.ExtractHead(link); michael@0: michael@0: if (firstLineChildren.IsEmpty()) { michael@0: // Nothing is supposed to go into the first-line; nothing to do michael@0: return; michael@0: } michael@0: michael@0: if (!aLineFrame) { michael@0: // Create line frame michael@0: nsStyleContext* parentStyle = michael@0: nsFrame::CorrectStyleParentFrame(aBlockFrame, michael@0: nsCSSPseudoElements::firstLine)-> michael@0: StyleContext(); michael@0: nsRefPtr firstLineStyle = GetFirstLineStyle(aBlockContent, michael@0: parentStyle); michael@0: michael@0: aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle); michael@0: michael@0: // Initialize the line frame michael@0: InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame); michael@0: michael@0: // The lineFrame will be the block's first child; the rest of the michael@0: // frame list (after lastInlineFrame) will be the second and michael@0: // subsequent children; insert lineFrame into aFrameItems. michael@0: aFrameItems.InsertFrame(nullptr, nullptr, aLineFrame); michael@0: michael@0: NS_ASSERTION(aLineFrame->StyleContext() == firstLineStyle, michael@0: "Bogus style context on line frame"); michael@0: } michael@0: michael@0: // Give the inline frames to the lineFrame after reparenting them michael@0: ReparentFrames(this, aLineFrame, firstLineChildren); michael@0: if (aLineFrame->PrincipalChildList().IsEmpty() && michael@0: (aLineFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: aLineFrame->SetInitialChildList(kPrincipalList, firstLineChildren); michael@0: } else { michael@0: AppendFrames(aLineFrame, kPrincipalList, firstLineChildren); michael@0: } michael@0: } michael@0: michael@0: // Special routine to handle appending a new frame to a block frame's michael@0: // child list. Takes care of placing the new frame into the right michael@0: // place when first-line style is present. michael@0: void michael@0: nsCSSFrameConstructor::AppendFirstLineFrames( michael@0: nsFrameConstructorState& aState, michael@0: nsIContent* aBlockContent, michael@0: nsIFrame* aBlockFrame, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: // It's possible that aBlockFrame needs to have a first-line frame michael@0: // created because it doesn't currently have any children. michael@0: const nsFrameList& blockKids = aBlockFrame->PrincipalChildList(); michael@0: if (blockKids.IsEmpty()) { michael@0: WrapFramesInFirstLineFrame(aState, aBlockContent, michael@0: aBlockFrame, nullptr, aFrameItems); michael@0: return; michael@0: } michael@0: michael@0: // Examine the last block child - if it's a first-line frame then michael@0: // appended frames need special treatment. michael@0: nsIFrame* lastBlockKid = blockKids.LastChild(); michael@0: if (lastBlockKid->GetType() != nsGkAtoms::lineFrame) { michael@0: // No first-line frame at the end of the list, therefore there is michael@0: // an intervening block between any first-line frame the frames michael@0: // we are appending. Therefore, we don't need any special michael@0: // treatment of the appended frames. michael@0: return; michael@0: } michael@0: michael@0: WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, michael@0: lastBlockKid, aFrameItems); michael@0: } michael@0: michael@0: // Special routine to handle inserting a new frame into a block michael@0: // frame's child list. Takes care of placing the new frame into the michael@0: // right place when first-line style is present. michael@0: nsresult michael@0: nsCSSFrameConstructor::InsertFirstLineFrames( michael@0: nsFrameConstructorState& aState, michael@0: nsIContent* aContent, michael@0: nsIFrame* aBlockFrame, michael@0: nsIFrame** aParentFrame, michael@0: nsIFrame* aPrevSibling, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: // XXXbz If you make this method actually do something, check to michael@0: // make sure that the caller is passing what you expect. In michael@0: // particular, which content is aContent? And audit the rest of michael@0: // this code too; it makes bogus assumptions and may not build. michael@0: #if 0 michael@0: nsIFrame* parentFrame = *aParentFrame; michael@0: nsIFrame* newFrame = aFrameItems.childList; michael@0: bool isInline = IsInlineOutside(newFrame); michael@0: michael@0: if (!aPrevSibling) { michael@0: // Insertion will become the first frame. Two cases: we either michael@0: // already have a first-line frame or we don't. michael@0: nsIFrame* firstBlockKid = aBlockFrame->GetFirstPrincipalChild(); michael@0: if (firstBlockKid->GetType() == nsGkAtoms::lineFrame) { michael@0: // We already have a first-line frame michael@0: nsIFrame* lineFrame = firstBlockKid; michael@0: michael@0: if (isInline) { michael@0: // Easy case: the new inline frame will go into the lineFrame. michael@0: ReparentFrame(this, lineFrame, newFrame); michael@0: InsertFrames(lineFrame, kPrincipalList, nullptr, newFrame); michael@0: michael@0: // Since the frame is going into the lineFrame, don't let it michael@0: // go into the block too. michael@0: aFrameItems.childList = nullptr; michael@0: aFrameItems.lastChild = nullptr; michael@0: } michael@0: else { michael@0: // Harder case: We are about to insert a block level element michael@0: // before the first-line frame. michael@0: // XXX need a method to steal away frames from the line-frame michael@0: } michael@0: } michael@0: else { michael@0: // We do not have a first-line frame michael@0: if (isInline) { michael@0: // We now need a first-line frame to contain the inline frame. michael@0: nsIFrame* lineFrame = NS_NewFirstLineFrame(firstLineStyle); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // Lookup first-line style context michael@0: nsStyleContext* parentStyle = michael@0: nsFrame::CorrectStyleParentFrame(aBlockFrame, michael@0: nsCSSPseudoElements::firstLine)-> michael@0: StyleContext(); michael@0: nsRefPtr firstLineStyle = michael@0: GetFirstLineStyle(aContent, parentStyle); michael@0: michael@0: // Initialize the line frame michael@0: InitAndRestoreFrame(aState, aContent, aBlockFrame, lineFrame); michael@0: michael@0: // Make sure the caller inserts the lineFrame into the michael@0: // blocks list of children. michael@0: aFrameItems.childList = lineFrame; michael@0: aFrameItems.lastChild = lineFrame; michael@0: michael@0: // Give the inline frames to the lineFrame after michael@0: // reparenting them michael@0: NS_ASSERTION(lineFrame->StyleContext() == firstLineStyle, michael@0: "Bogus style context on line frame"); michael@0: ReparentFrame(aPresContext, lineFrame, newFrame); michael@0: lineFrame->SetInitialChildList(kPrincipalList, newFrame); michael@0: } michael@0: } michael@0: else { michael@0: // Easy case: the regular insertion logic can insert the new michael@0: // frame because it's a block frame. michael@0: } michael@0: } michael@0: } michael@0: else { michael@0: // Insertion will not be the first frame. michael@0: nsIFrame* prevSiblingParent = aPrevSibling->GetParent(); michael@0: if (prevSiblingParent == aBlockFrame) { michael@0: // Easy case: The prev-siblings parent is the block michael@0: // frame. Therefore the prev-sibling is not currently in a michael@0: // line-frame. Therefore the new frame which is going after it, michael@0: // regardless of type, is not going into a line-frame. michael@0: } michael@0: else { michael@0: // If the prevSiblingParent is not the block-frame then it must michael@0: // be a line-frame (if it were a letter-frame, that logic would michael@0: // already have adjusted the prev-sibling to be the michael@0: // letter-frame). michael@0: if (isInline) { michael@0: // Easy case: the insertion can go where the caller thinks it michael@0: // should go (which is into prevSiblingParent). michael@0: } michael@0: else { michael@0: // Block elements don't end up in line-frames, therefore michael@0: // change the insertion point to aBlockFrame. However, there michael@0: // might be more inline elements following aPrevSibling that michael@0: // need to be pulled out of the line-frame and become children michael@0: // of the block. michael@0: nsIFrame* nextSibling = aPrevSibling->GetNextSibling(); michael@0: nsIFrame* nextLineFrame = prevSiblingParent->GetNextInFlow(); michael@0: if (nextSibling || nextLineFrame) { michael@0: // Oy. We have work to do. Create a list of the new frames michael@0: // that are going into the block by stripping them away from michael@0: // the line-frame(s). michael@0: if (nextSibling) { michael@0: nsLineFrame* lineFrame = (nsLineFrame*) prevSiblingParent; michael@0: nsFrameList tail = lineFrame->StealFramesAfter(aPrevSibling); michael@0: // XXX do something with 'tail' michael@0: } michael@0: michael@0: nsLineFrame* nextLineFrame = (nsLineFrame*) lineFrame; michael@0: for (;;) { michael@0: nextLineFrame = nextLineFrame->GetNextInFlow(); michael@0: if (!nextLineFrame) { michael@0: break; michael@0: } michael@0: nsIFrame* kids = nextLineFrame->GetFirstPrincipalChild(); michael@0: } michael@0: } michael@0: else { michael@0: // We got lucky: aPrevSibling was the last inline frame in michael@0: // the line-frame. michael@0: ReparentFrame(this, aBlockFrame, newFrame); michael@0: InsertFrames(aBlockFrame, kPrincipalList, michael@0: prevSiblingParent, newFrame); michael@0: aFrameItems.childList = nullptr; michael@0: aFrameItems.lastChild = nullptr; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: #endif michael@0: return rv; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: // First-letter support michael@0: michael@0: // Determine how many characters in the text fragment apply to the michael@0: // first letter michael@0: static int32_t michael@0: FirstLetterCount(const nsTextFragment* aFragment) michael@0: { michael@0: int32_t count = 0; michael@0: int32_t firstLetterLength = 0; michael@0: michael@0: int32_t i, n = aFragment->GetLength(); michael@0: for (i = 0; i < n; i++) { michael@0: char16_t ch = aFragment->CharAt(i); michael@0: // FIXME: take content language into account when deciding whitespace. michael@0: if (dom::IsSpaceCharacter(ch)) { michael@0: if (firstLetterLength) { michael@0: break; michael@0: } michael@0: count++; michael@0: continue; michael@0: } michael@0: // XXX I18n michael@0: if ((ch == '\'') || (ch == '\"')) { michael@0: if (firstLetterLength) { michael@0: break; michael@0: } michael@0: // keep looping michael@0: firstLetterLength = 1; michael@0: } michael@0: else { michael@0: count++; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return count; michael@0: } michael@0: michael@0: static bool michael@0: NeedFirstLetterContinuation(nsIContent* aContent) michael@0: { michael@0: NS_PRECONDITION(aContent, "null ptr"); michael@0: michael@0: bool result = false; michael@0: if (aContent) { michael@0: const nsTextFragment* frag = aContent->GetText(); michael@0: if (frag) { michael@0: int32_t flc = FirstLetterCount(frag); michael@0: int32_t tl = frag->GetLength(); michael@0: if (flc < tl) { michael@0: result = true; michael@0: } michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static bool IsFirstLetterContent(nsIContent* aContent) michael@0: { michael@0: return aContent->TextLength() && michael@0: !aContent->TextIsOnlyWhitespace(); michael@0: } michael@0: michael@0: /** michael@0: * Create a letter frame, only make it a floating frame. michael@0: */ michael@0: void michael@0: nsCSSFrameConstructor::CreateFloatingLetterFrame( michael@0: nsFrameConstructorState& aState, michael@0: nsIFrame* aBlockFrame, michael@0: nsIContent* aTextContent, michael@0: nsIFrame* aTextFrame, michael@0: nsIContent* aBlockContent, michael@0: nsIFrame* aParentFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsFrameItems& aResult) michael@0: { michael@0: // Create the first-letter-frame michael@0: nsIFrame* letterFrame; michael@0: nsStyleSet *styleSet = mPresShell->StyleSet(); michael@0: michael@0: letterFrame = NS_NewFirstLetterFrame(mPresShell, aStyleContext); michael@0: // We don't want to use a text content for a non-text frame (because we want michael@0: // its primary frame to be a text frame). So use its parent for the michael@0: // first-letter. michael@0: nsIContent* letterContent = aTextContent->GetParent(); michael@0: nsIFrame* containingBlock = aState.GetGeometricParent( michael@0: aStyleContext->StyleDisplay(), aParentFrame); michael@0: InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame); michael@0: michael@0: // Init the text frame to refer to the letter frame. Make sure we michael@0: // get a proper style context for it (the one passed in is for the michael@0: // letter frame and will have the float property set on it; the text michael@0: // frame shouldn't have that set). michael@0: nsRefPtr textSC; michael@0: textSC = styleSet->ResolveStyleForNonElement(aStyleContext); michael@0: aTextFrame->SetStyleContextWithoutNotification(textSC); michael@0: InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame); michael@0: michael@0: // And then give the text frame to the letter frame michael@0: SetInitialSingleChild(letterFrame, aTextFrame); michael@0: michael@0: // See if we will need to continue the text frame (does it contain michael@0: // more than just the first-letter text or not?) If it does, then we michael@0: // create (in advance) a continuation frame for it. michael@0: nsIFrame* nextTextFrame = nullptr; michael@0: if (NeedFirstLetterContinuation(aTextContent)) { michael@0: // Create continuation michael@0: nextTextFrame = michael@0: CreateContinuingFrame(aState.mPresContext, aTextFrame, aParentFrame); michael@0: // Repair the continuations style context michael@0: nsStyleContext* parentStyleContext = aStyleContext->GetParent(); michael@0: if (parentStyleContext) { michael@0: nsRefPtr newSC; michael@0: newSC = styleSet->ResolveStyleForNonElement(parentStyleContext); michael@0: nextTextFrame->SetStyleContext(newSC); michael@0: } michael@0: } michael@0: michael@0: NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameItems!"); michael@0: // Put the new float before any of the floats in the block we're doing michael@0: // first-letter for, that is, before any floats whose parent is michael@0: // containingBlock. michael@0: nsFrameList::FrameLinkEnumerator link(aState.mFloatedItems); michael@0: while (!link.AtEnd() && link.NextFrame()->GetParent() != containingBlock) { michael@0: link.Next(); michael@0: } michael@0: michael@0: aState.AddChild(letterFrame, aResult, letterContent, aStyleContext, michael@0: aParentFrame, false, true, false, true, michael@0: link.PrevFrame()); michael@0: michael@0: if (nextTextFrame) { michael@0: aResult.AddChild(nextTextFrame); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Create a new letter frame for aTextFrame. The letter frame will be michael@0: * a child of aParentFrame. michael@0: */ michael@0: void michael@0: nsCSSFrameConstructor::CreateLetterFrame(nsIFrame* aBlockFrame, michael@0: nsIFrame* aBlockContinuation, michael@0: nsIContent* aTextContent, michael@0: nsIFrame* aParentFrame, michael@0: nsFrameItems& aResult) michael@0: { michael@0: NS_PRECONDITION(aTextContent->IsNodeOfType(nsINode::eTEXT), michael@0: "aTextContent isn't text"); michael@0: NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlockFrame), michael@0: "Not a block frame?"); michael@0: michael@0: // Get style context for the first-letter-frame michael@0: nsStyleContext* parentStyleContext = michael@0: nsFrame::CorrectStyleParentFrame(aParentFrame, michael@0: nsCSSPseudoElements::firstLetter)-> michael@0: StyleContext(); michael@0: michael@0: // Use content from containing block so that we can actually michael@0: // find a matching style rule. michael@0: nsIContent* blockContent = aBlockFrame->GetContent(); michael@0: michael@0: // Create first-letter style rule michael@0: nsRefPtr sc = GetFirstLetterStyle(blockContent, michael@0: parentStyleContext); michael@0: if (sc) { michael@0: nsRefPtr textSC; michael@0: textSC = mPresShell->StyleSet()->ResolveStyleForNonElement(sc); michael@0: michael@0: // Create a new text frame (the original one will be discarded) michael@0: // pass a temporary stylecontext, the correct one will be set michael@0: // later. Start off by unsetting the primary frame for michael@0: // aTextContent, so it's no longer pointing to the to-be-destroyed michael@0: // frame. michael@0: // XXXbz it would be really nice to destroy the old frame _first_, michael@0: // then create the new one, so we could avoid this hack. michael@0: aTextContent->SetPrimaryFrame(nullptr); michael@0: nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC); michael@0: michael@0: NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame), michael@0: "Containing block is confused"); michael@0: nsFrameConstructorState state(mPresShell, michael@0: GetAbsoluteContainingBlock(aParentFrame, FIXED_POS), michael@0: GetAbsoluteContainingBlock(aParentFrame, ABS_POS), michael@0: aBlockContinuation); michael@0: michael@0: // Create the right type of first-letter frame michael@0: const nsStyleDisplay* display = sc->StyleDisplay(); michael@0: if (display->IsFloatingStyle() && !aParentFrame->IsSVGText()) { michael@0: // Make a floating first-letter frame michael@0: CreateFloatingLetterFrame(state, aBlockFrame, aTextContent, textFrame, michael@0: blockContent, aParentFrame, sc, aResult); michael@0: } michael@0: else { michael@0: // Make an inflow first-letter frame michael@0: nsIFrame* letterFrame = NS_NewFirstLetterFrame(mPresShell, sc); michael@0: michael@0: // Initialize the first-letter-frame. We don't want to use a text michael@0: // content for a non-text frame (because we want its primary frame to michael@0: // be a text frame). So use its parent for the first-letter. michael@0: nsIContent* letterContent = aTextContent->GetParent(); michael@0: letterFrame->Init(letterContent, aParentFrame, nullptr); michael@0: michael@0: InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame); michael@0: michael@0: SetInitialSingleChild(letterFrame, textFrame); michael@0: aResult.Clear(); michael@0: aResult.AddChild(letterFrame); michael@0: NS_ASSERTION(!aBlockFrame->GetPrevContinuation(), michael@0: "should have the first continuation here"); michael@0: aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD); michael@0: } michael@0: aTextContent->SetPrimaryFrame(textFrame); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::WrapFramesInFirstLetterFrame( michael@0: nsIContent* aBlockContent, michael@0: nsIFrame* aBlockFrame, michael@0: nsFrameItems& aBlockFrames) michael@0: { michael@0: aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE); michael@0: michael@0: nsIFrame* parentFrame = nullptr; michael@0: nsIFrame* textFrame = nullptr; michael@0: nsIFrame* prevFrame = nullptr; michael@0: nsFrameItems letterFrames; michael@0: bool stopLooking = false; michael@0: WrapFramesInFirstLetterFrame(aBlockFrame, aBlockFrame, aBlockFrame, michael@0: aBlockFrames.FirstChild(), michael@0: &parentFrame, &textFrame, &prevFrame, michael@0: letterFrames, &stopLooking); michael@0: if (parentFrame) { michael@0: if (parentFrame == aBlockFrame) { michael@0: // Take textFrame out of the block's frame list and substitute the michael@0: // letter frame(s) instead. michael@0: aBlockFrames.DestroyFrame(textFrame); michael@0: aBlockFrames.InsertFrames(nullptr, prevFrame, letterFrames); michael@0: } michael@0: else { michael@0: // Take the old textFrame out of the inline parent's child list michael@0: RemoveFrame(kPrincipalList, textFrame); michael@0: michael@0: // Insert in the letter frame(s) michael@0: parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::WrapFramesInFirstLetterFrame( michael@0: nsIFrame* aBlockFrame, michael@0: nsIFrame* aBlockContinuation, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aParentFrameList, michael@0: nsIFrame** aModifiedParent, michael@0: nsIFrame** aTextFrame, michael@0: nsIFrame** aPrevFrame, michael@0: nsFrameItems& aLetterFrames, michael@0: bool* aStopLooking) michael@0: { michael@0: nsIFrame* prevFrame = nullptr; michael@0: nsIFrame* frame = aParentFrameList; michael@0: michael@0: while (frame) { michael@0: nsIFrame* nextFrame = frame->GetNextSibling(); michael@0: michael@0: nsIAtom* frameType = frame->GetType(); michael@0: if (nsGkAtoms::textFrame == frameType) { michael@0: // Wrap up first-letter content in a letter frame michael@0: nsIContent* textContent = frame->GetContent(); michael@0: if (IsFirstLetterContent(textContent)) { michael@0: // Create letter frame to wrap up the text michael@0: CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent, michael@0: aParentFrame, aLetterFrames); michael@0: michael@0: // Provide adjustment information for parent michael@0: *aModifiedParent = aParentFrame; michael@0: *aTextFrame = frame; michael@0: *aPrevFrame = prevFrame; michael@0: *aStopLooking = true; michael@0: return; michael@0: } michael@0: } michael@0: else if (IsInlineFrame(frame) && frameType != nsGkAtoms::brFrame) { michael@0: nsIFrame* kids = frame->GetFirstPrincipalChild(); michael@0: WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation, frame, michael@0: kids, aModifiedParent, aTextFrame, michael@0: aPrevFrame, aLetterFrames, aStopLooking); michael@0: if (*aStopLooking) { michael@0: return; michael@0: } michael@0: } michael@0: else { michael@0: // This will stop us looking to create more letter frames. For michael@0: // example, maybe the frame-type is "letterFrame" or michael@0: // "placeholderFrame". This keeps us from creating extra letter michael@0: // frames, and also prevents us from creating letter frames when michael@0: // the first real content child of a block is not text (e.g. an michael@0: // image, hr, etc.) michael@0: *aStopLooking = true; michael@0: break; michael@0: } michael@0: michael@0: prevFrame = frame; michael@0: frame = nextFrame; michael@0: } michael@0: } michael@0: michael@0: static nsIFrame* michael@0: FindFirstLetterFrame(nsIFrame* aFrame, nsIFrame::ChildListID aListID) michael@0: { michael@0: nsFrameList list = aFrame->GetChildList(aListID); michael@0: for (nsFrameList::Enumerator e(list); !e.AtEnd(); e.Next()) { michael@0: if (nsGkAtoms::letterFrame == e.get()->GetType()) { michael@0: return e.get(); michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( michael@0: nsPresContext* aPresContext, michael@0: nsIPresShell* aPresShell, michael@0: nsIFrame* aBlockFrame, michael@0: bool* aStopLooking) michael@0: { michael@0: // Look for the first letter frame on the kFloatList, then kPushedFloatsList. michael@0: nsIFrame* floatFrame = michael@0: ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kFloatList); michael@0: if (!floatFrame) { michael@0: floatFrame = michael@0: ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList); michael@0: if (!floatFrame) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // Take the text frame away from the letter frame (so it isn't michael@0: // destroyed when we destroy the letter frame). michael@0: nsIFrame* textFrame = floatFrame->GetFirstPrincipalChild(); michael@0: if (!textFrame) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Discover the placeholder frame for the letter frame michael@0: nsIFrame* parentFrame; michael@0: nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrameFor(floatFrame); michael@0: michael@0: if (!placeholderFrame) { michael@0: // Somethings really wrong michael@0: return NS_OK; michael@0: } michael@0: parentFrame = placeholderFrame->GetParent(); michael@0: if (!parentFrame) { michael@0: // Somethings really wrong michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Create a new text frame with the right style context that maps michael@0: // all of the content that was previously part of the letter frame michael@0: // (and probably continued elsewhere). michael@0: nsStyleContext* parentSC = parentFrame->StyleContext(); michael@0: nsIContent* textContent = textFrame->GetContent(); michael@0: if (!textContent) { michael@0: return NS_OK; michael@0: } michael@0: nsRefPtr newSC; michael@0: newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC); michael@0: nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC); michael@0: newTextFrame->Init(textContent, parentFrame, nullptr); michael@0: michael@0: // Destroy the old text frame's continuations (the old text frame michael@0: // will be destroyed when its letter frame is destroyed). michael@0: nsIFrame* frameToDelete = textFrame->LastContinuation(); michael@0: while (frameToDelete != textFrame) { michael@0: nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation(); michael@0: RemoveFrame(kPrincipalList, frameToDelete); michael@0: frameToDelete = nextFrameToDelete; michael@0: } michael@0: michael@0: nsIFrame* prevSibling = placeholderFrame->GetPrevSibling(); michael@0: michael@0: // Now that everything is set... michael@0: #ifdef NOISY_FIRST_LETTER michael@0: printf("RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p newTextFrame=%p\n", michael@0: textContent.get(), textFrame, newTextFrame); michael@0: #endif michael@0: michael@0: // Remove placeholder frame and the float michael@0: RemoveFrame(kPrincipalList, placeholderFrame); michael@0: michael@0: // Now that the old frames are gone, we can start pointing to our michael@0: // new primary frame. michael@0: textContent->SetPrimaryFrame(newTextFrame); michael@0: michael@0: // Wallpaper bug 822910. michael@0: bool offsetsNeedFixing = michael@0: prevSibling && prevSibling->GetType() == nsGkAtoms::textFrame; michael@0: if (offsetsNeedFixing) { michael@0: prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING); michael@0: } michael@0: michael@0: // Insert text frame in its place michael@0: nsFrameList textList(newTextFrame, newTextFrame); michael@0: InsertFrames(parentFrame, kPrincipalList, prevSibling, textList); michael@0: michael@0: if (offsetsNeedFixing) { michael@0: prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::RemoveFirstLetterFrames(nsPresContext* aPresContext, michael@0: nsIPresShell* aPresShell, michael@0: nsIFrame* aFrame, michael@0: nsIFrame* aBlockFrame, michael@0: bool* aStopLooking) michael@0: { michael@0: nsIFrame* prevSibling = nullptr; michael@0: nsIFrame* kid = aFrame->GetFirstPrincipalChild(); michael@0: michael@0: while (kid) { michael@0: if (nsGkAtoms::letterFrame == kid->GetType()) { michael@0: // Bingo. Found it. First steal away the text frame. michael@0: nsIFrame* textFrame = kid->GetFirstPrincipalChild(); michael@0: if (!textFrame) { michael@0: break; michael@0: } michael@0: michael@0: // Create a new textframe michael@0: nsStyleContext* parentSC = aFrame->StyleContext(); michael@0: if (!parentSC) { michael@0: break; michael@0: } michael@0: nsIContent* textContent = textFrame->GetContent(); michael@0: if (!textContent) { michael@0: break; michael@0: } michael@0: nsRefPtr newSC; michael@0: newSC = aPresShell->StyleSet()->ResolveStyleForNonElement(parentSC); michael@0: textFrame = NS_NewTextFrame(aPresShell, newSC); michael@0: textFrame->Init(textContent, aFrame, nullptr); michael@0: michael@0: // Next rip out the kid and replace it with the text frame michael@0: RemoveFrame(kPrincipalList, kid); michael@0: michael@0: // Now that the old frames are gone, we can start pointing to our michael@0: // new primary frame. michael@0: textContent->SetPrimaryFrame(textFrame); michael@0: michael@0: // Wallpaper bug 822910. michael@0: bool offsetsNeedFixing = michael@0: prevSibling && prevSibling->GetType() == nsGkAtoms::textFrame; michael@0: if (offsetsNeedFixing) { michael@0: prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING); michael@0: } michael@0: michael@0: // Insert text frame in its place michael@0: nsFrameList textList(textFrame, textFrame); michael@0: InsertFrames(aFrame, kPrincipalList, prevSibling, textList); michael@0: michael@0: if (offsetsNeedFixing) { michael@0: prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING); michael@0: } michael@0: michael@0: *aStopLooking = true; michael@0: NS_ASSERTION(!aBlockFrame->GetPrevContinuation(), michael@0: "should have the first continuation here"); michael@0: aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD); michael@0: break; michael@0: } michael@0: else if (IsInlineFrame(kid)) { michael@0: // Look inside child inline frame for the letter frame michael@0: RemoveFirstLetterFrames(aPresContext, aPresShell, michael@0: kid, aBlockFrame, aStopLooking); michael@0: if (*aStopLooking) { michael@0: break; michael@0: } michael@0: } michael@0: prevSibling = kid; michael@0: kid = kid->GetNextSibling(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::RemoveLetterFrames(nsPresContext* aPresContext, michael@0: nsIPresShell* aPresShell, michael@0: nsIFrame* aBlockFrame) michael@0: { michael@0: aBlockFrame = aBlockFrame->FirstContinuation(); michael@0: nsIFrame* continuation = aBlockFrame; michael@0: michael@0: bool stopLooking = false; michael@0: nsresult rv; michael@0: do { michael@0: rv = RemoveFloatingFirstLetterFrames(aPresContext, aPresShell, michael@0: continuation, &stopLooking); michael@0: if (NS_SUCCEEDED(rv) && !stopLooking) { michael@0: rv = RemoveFirstLetterFrames(aPresContext, aPresShell, michael@0: continuation, aBlockFrame, &stopLooking); michael@0: } michael@0: if (stopLooking) { michael@0: break; michael@0: } michael@0: continuation = continuation->GetNextContinuation(); michael@0: } while (continuation); michael@0: return rv; michael@0: } michael@0: michael@0: // Fixup the letter frame situation for the given block michael@0: void michael@0: nsCSSFrameConstructor::RecoverLetterFrames(nsIFrame* aBlockFrame) michael@0: { michael@0: aBlockFrame = aBlockFrame->FirstContinuation(); michael@0: nsIFrame* continuation = aBlockFrame; michael@0: michael@0: nsIFrame* parentFrame = nullptr; michael@0: nsIFrame* textFrame = nullptr; michael@0: nsIFrame* prevFrame = nullptr; michael@0: nsFrameItems letterFrames; michael@0: bool stopLooking = false; michael@0: do { michael@0: // XXX shouldn't this bit be set already (bug 408493), assert instead? michael@0: continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE); michael@0: WrapFramesInFirstLetterFrame(aBlockFrame, continuation, continuation, michael@0: continuation->GetFirstPrincipalChild(), michael@0: &parentFrame, &textFrame, &prevFrame, michael@0: letterFrames, &stopLooking); michael@0: if (stopLooking) { michael@0: break; michael@0: } michael@0: continuation = continuation->GetNextContinuation(); michael@0: } while (continuation); michael@0: michael@0: if (parentFrame) { michael@0: // Take the old textFrame out of the parents child list michael@0: RemoveFrame(kPrincipalList, textFrame); michael@0: michael@0: // Insert in the letter frame(s) michael@0: parentFrame->InsertFrames(kPrincipalList, prevFrame, letterFrames); michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: // listbox Widget Routines michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::CreateListBoxContent(nsPresContext* aPresContext, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aPrevFrame, michael@0: nsIContent* aChild, michael@0: nsIFrame** aNewFrame, michael@0: bool aIsAppend, michael@0: bool aIsScrollbar, michael@0: nsILayoutHistoryState* aFrameState) michael@0: { michael@0: #ifdef MOZ_XUL michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Construct a new frame michael@0: if (nullptr != aParentFrame) { michael@0: nsFrameItems frameItems; michael@0: nsFrameConstructorState state(mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS), michael@0: GetAbsoluteContainingBlock(aParentFrame, ABS_POS), michael@0: GetFloatContainingBlock(aParentFrame), michael@0: mTempFrameTreeState); michael@0: michael@0: // If we ever initialize the ancestor filter on |state|, make sure michael@0: // to push the right parent! michael@0: michael@0: nsRefPtr styleContext; michael@0: styleContext = ResolveStyleContext(aParentFrame, aChild, &state); michael@0: michael@0: // Pre-check for display "none" - only if we find that, do we create michael@0: // any frame at all michael@0: const nsStyleDisplay* display = styleContext->StyleDisplay(); michael@0: michael@0: if (NS_STYLE_DISPLAY_NONE == display->mDisplay) { michael@0: *aNewFrame = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: BeginUpdate(); michael@0: michael@0: FrameConstructionItemList items; michael@0: AddFrameConstructionItemsInternal(state, aChild, aParentFrame, michael@0: aChild->Tag(), aChild->GetNameSpaceID(), michael@0: true, styleContext, michael@0: ITEM_ALLOW_XBL_BASE, nullptr, items); michael@0: ConstructFramesFromItemList(state, items, aParentFrame, frameItems); michael@0: michael@0: nsIFrame* newFrame = frameItems.FirstChild(); michael@0: *aNewFrame = newFrame; michael@0: michael@0: if (newFrame) { michael@0: // Notify the parent frame michael@0: if (aIsAppend) michael@0: rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems); michael@0: else michael@0: rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems); michael@0: } michael@0: michael@0: EndUpdate(); michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: if (newFrame) { michael@0: nsAccessibilityService* accService = nsIPresShell::AccService(); michael@0: if (accService) { michael@0: accService->ContentRangeInserted(mPresShell, aChild->GetParent(), michael@0: aChild, aChild->GetNextSibling()); michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: return rv; michael@0: #else michael@0: return NS_ERROR_FAILURE; michael@0: #endif michael@0: } michael@0: michael@0: //---------------------------------------- michael@0: michael@0: void michael@0: nsCSSFrameConstructor::ConstructBlock(nsFrameConstructorState& aState, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsIContent* aContent, michael@0: nsIFrame* aParentFrame, michael@0: nsIFrame* aContentParentFrame, michael@0: nsStyleContext* aStyleContext, michael@0: nsIFrame** aNewFrame, michael@0: nsFrameItems& aFrameItems, michael@0: nsIFrame* aPositionedFrameForAbsPosContainer, michael@0: PendingBinding* aPendingBinding) michael@0: { michael@0: // Create column wrapper if necessary michael@0: nsIFrame* blockFrame = *aNewFrame; michael@0: NS_ASSERTION(blockFrame->GetType() == nsGkAtoms::blockFrame, "not a block frame?"); michael@0: nsIFrame* parent = aParentFrame; michael@0: nsRefPtr blockStyle = aStyleContext; michael@0: const nsStyleColumn* columns = aStyleContext->StyleColumn(); michael@0: michael@0: if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO michael@0: || columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) { michael@0: nsIFrame* columnSetFrame = nullptr; michael@0: columnSetFrame = michael@0: NS_NewColumnSetFrame(mPresShell, aStyleContext, nsFrameState(0)); michael@0: michael@0: InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetFrame); michael@0: blockStyle = mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(nsCSSAnonBoxes::columnContent, aStyleContext); michael@0: parent = columnSetFrame; michael@0: *aNewFrame = columnSetFrame; michael@0: michael@0: SetInitialSingleChild(columnSetFrame, blockFrame); michael@0: } michael@0: michael@0: blockFrame->SetStyleContextWithoutNotification(blockStyle); michael@0: InitAndRestoreFrame(aState, aContent, parent, blockFrame); michael@0: michael@0: aState.AddChild(*aNewFrame, aFrameItems, aContent, aStyleContext, michael@0: aContentParentFrame ? aContentParentFrame : michael@0: aParentFrame); michael@0: if (!mRootElementFrame) { michael@0: // The frame we're constructing will be the root element frame. michael@0: // Set mRootElementFrame before processing children. michael@0: mRootElementFrame = *aNewFrame; michael@0: } michael@0: michael@0: // We should make the outer frame be the absolute containing block, michael@0: // if one is required. We have to do this because absolute michael@0: // positioning must be computed with respect to the CSS dimensions michael@0: // of the element, which are the dimensions of the outer block. But michael@0: // we can't really do that because only blocks can have absolute michael@0: // children. So use the block and try to compensate with hacks michael@0: // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes. michael@0: nsFrameConstructorSaveState absoluteSaveState; michael@0: (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: if (aPositionedFrameForAbsPosContainer) { michael@0: // NS_ASSERTION(aRelPos, "should have made area frame for this"); michael@0: aState.PushAbsoluteContainingBlock(*aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState); michael@0: } michael@0: michael@0: // Process the child content michael@0: nsFrameItems childItems; michael@0: ProcessChildren(aState, aContent, aStyleContext, blockFrame, true, michael@0: childItems, true, aPendingBinding); michael@0: michael@0: // Set the frame's initial child list michael@0: blockFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aItem, michael@0: nsIFrame* aParentFrame, michael@0: const nsStyleDisplay* aDisplay, michael@0: nsFrameItems& aFrameItems) michael@0: { michael@0: // If an inline frame has non-inline kids, then we chop up the child list michael@0: // into runs of blocks and runs of inlines, create anonymous block frames to michael@0: // contain the runs of blocks, inline frames with our style context for the michael@0: // runs of inlines, and put all these frames, in order, into aFrameItems. We michael@0: // return the the first one. The whole setup is called an {ib} michael@0: // split; in what follows "frames in the split" refers to the anonymous blocks michael@0: // and inlines that contain our children. michael@0: // michael@0: // {ib} splits maintain the following invariants: michael@0: // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit michael@0: // set. michael@0: // 2) Each frame in the split has the nsIFrame::IBSplitSibling michael@0: // property pointing to the next frame in the split, except for the last michael@0: // one, which does not have it set. michael@0: // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling michael@0: // property pointing to the previous frame in the split, except for the michael@0: // first one, which does not have it set. michael@0: // 4) The first and last frame in the split are always inlines. michael@0: // michael@0: // An invariant that is NOT maintained is that the wrappers are actually michael@0: // linked via GetNextSibling linkage. A simple example is an inline michael@0: // containing an inline that contains a block. The three parts of the inner michael@0: // inline end up with three different parents. michael@0: // michael@0: // For example, this HTML: michael@0: // michael@0: //
a
michael@0: // michael@0: // b michael@0: //
c
michael@0: //
michael@0: // d michael@0: //
e
michael@0: // f michael@0: //
michael@0: // Gives the following frame tree: michael@0: // michael@0: // Inline (outer span) michael@0: // Block (anonymous, outer span) michael@0: // Block (div) michael@0: // Text("a") michael@0: // Inline (outer span) michael@0: // Inline (inner span) michael@0: // Text("b") michael@0: // Block (anonymous, outer span) michael@0: // Block (anonymous, inner span) michael@0: // Block (div) michael@0: // Text("c") michael@0: // Inline (outer span) michael@0: // Inline (inner span) michael@0: // Text("d") michael@0: // Block (anonymous, outer span) michael@0: // Block (div) michael@0: // Text("e") michael@0: // Inline (outer span) michael@0: // Text("f") michael@0: michael@0: nsIContent* const content = aItem.mContent; michael@0: nsStyleContext* const styleContext = aItem.mStyleContext; michael@0: michael@0: bool positioned = michael@0: NS_STYLE_DISPLAY_INLINE == aDisplay->mDisplay && michael@0: aDisplay->IsRelativelyPositionedStyle() && michael@0: !aParentFrame->IsSVGText(); michael@0: michael@0: nsIFrame* newFrame = NS_NewInlineFrame(mPresShell, styleContext); michael@0: michael@0: // Initialize the frame michael@0: InitAndRestoreFrame(aState, content, aParentFrame, newFrame); michael@0: michael@0: // Inline frames can always have generated content michael@0: newFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT); michael@0: michael@0: nsFrameConstructorSaveState absoluteSaveState; // definition cannot be inside next block michael@0: // because the object's destructor is significant michael@0: // this is part of the fix for bug 42372 michael@0: michael@0: newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: if (positioned) { michael@0: // Relatively positioned frames becomes a container for child michael@0: // frames that are positioned michael@0: aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState); michael@0: } michael@0: michael@0: // Process the child content michael@0: nsFrameItems childItems; michael@0: ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame, childItems); michael@0: michael@0: nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childItems); michael@0: if (!aItem.mIsAllInline) { michael@0: FindFirstBlock(firstBlockEnumerator); michael@0: } michael@0: michael@0: if (aItem.mIsAllInline || firstBlockEnumerator.AtEnd()) { michael@0: // This part is easy. We either already know we have no non-inline kids, michael@0: // or haven't found any when constructing actual frames (the latter can michael@0: // happen only if out-of-flows that we thought had no containing block michael@0: // acquired one when ancestor inline frames and {ib} splits got michael@0: // constructed). Just put all the kids into the single inline frame and michael@0: // bail. michael@0: newFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame); michael@0: return newFrame; michael@0: } michael@0: michael@0: // This inline frame contains several types of children. Therefore this frame michael@0: // has to be chopped into several pieces, as described above. michael@0: michael@0: // Grab the first inline's kids michael@0: nsFrameList firstInlineKids = childItems.ExtractHead(firstBlockEnumerator); michael@0: newFrame->SetInitialChildList(kPrincipalList, firstInlineKids); michael@0: michael@0: aFrameItems.AddChild(newFrame); michael@0: michael@0: CreateIBSiblings(aState, newFrame, positioned, childItems, aFrameItems); michael@0: michael@0: return newFrame; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState, michael@0: nsIFrame* aInitialInline, michael@0: bool aIsPositioned, michael@0: nsFrameItems& aChildItems, michael@0: nsFrameItems& aSiblings) michael@0: { michael@0: nsIContent* content = aInitialInline->GetContent(); michael@0: nsStyleContext* styleContext = aInitialInline->StyleContext(); michael@0: nsIFrame* parentFrame = aInitialInline->GetParent(); michael@0: michael@0: // Resolve the right style context for our anonymous blocks. michael@0: // The distinction in styles is needed because of CSS 2.1, section michael@0: // 9.2.1.1, which says: michael@0: // When such an inline box is affected by relative positioning, any michael@0: // resulting translation also affects the block-level box contained michael@0: // in the inline box. michael@0: nsRefPtr blockSC = michael@0: mPresShell->StyleSet()-> michael@0: ResolveAnonymousBoxStyle(aIsPositioned ? michael@0: nsCSSAnonBoxes::mozAnonymousPositionedBlock : michael@0: nsCSSAnonBoxes::mozAnonymousBlock, michael@0: styleContext); michael@0: michael@0: nsIFrame* lastNewInline = aInitialInline->FirstContinuation(); michael@0: do { michael@0: // On entry to this loop aChildItems is not empty and the first frame in it michael@0: // is block-level. michael@0: NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items"); michael@0: NS_PRECONDITION(!aChildItems.FirstChild()->IsInlineOutside(), michael@0: "Must have list starting with block"); michael@0: michael@0: // The initial run of blocks belongs to an anonymous block that we create michael@0: // right now. The anonymous block will be the parent of these block michael@0: // children of the inline. michael@0: nsIFrame* blockFrame; michael@0: blockFrame = NS_NewBlockFrame(mPresShell, blockSC); michael@0: michael@0: InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false); michael@0: michael@0: // Find the first non-block child which defines the end of our block kids michael@0: // and the start of our next inline's kids michael@0: nsFrameList::FrameLinkEnumerator firstNonBlock = michael@0: FindFirstNonBlock(aChildItems); michael@0: nsFrameList blockKids = aChildItems.ExtractHead(firstNonBlock); michael@0: michael@0: MoveChildrenTo(aState.mPresContext, aInitialInline, blockFrame, blockKids); michael@0: michael@0: SetFrameIsIBSplit(lastNewInline, blockFrame); michael@0: aSiblings.AddChild(blockFrame); michael@0: michael@0: // Now grab the initial inlines in aChildItems and put them into an inline michael@0: // frame michael@0: nsIFrame* inlineFrame = NS_NewInlineFrame(mPresShell, styleContext); michael@0: michael@0: InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false); michael@0: michael@0: inlineFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT | michael@0: NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); michael@0: if (aIsPositioned) { michael@0: inlineFrame->MarkAsAbsoluteContainingBlock(); michael@0: } michael@0: michael@0: if (aChildItems.NotEmpty()) { michael@0: nsFrameList::FrameLinkEnumerator firstBlock(aChildItems); michael@0: FindFirstBlock(firstBlock); michael@0: nsFrameList inlineKids = aChildItems.ExtractHead(firstBlock); michael@0: michael@0: MoveChildrenTo(aState.mPresContext, aInitialInline, inlineFrame, michael@0: inlineKids); michael@0: } michael@0: michael@0: SetFrameIsIBSplit(blockFrame, inlineFrame); michael@0: aSiblings.AddChild(inlineFrame); michael@0: lastNewInline = inlineFrame; michael@0: } while (aChildItems.NotEmpty()); michael@0: michael@0: SetFrameIsIBSplit(lastNewInline, nullptr); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::BuildInlineChildItems(nsFrameConstructorState& aState, michael@0: FrameConstructionItem& aParentItem, michael@0: bool aItemIsWithinSVGText, michael@0: bool aItemAllowsTextPathChild) michael@0: { michael@0: // XXXbz should we preallocate aParentItem.mChildItems to some sane michael@0: // length? Maybe even to parentContent->GetChildCount()? michael@0: nsFrameConstructorState::PendingBindingAutoPusher michael@0: pusher(aState, aParentItem.mPendingBinding); michael@0: michael@0: nsStyleContext* const parentStyleContext = aParentItem.mStyleContext; michael@0: nsIContent* const parentContent = aParentItem.mContent; michael@0: michael@0: TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext); michael@0: if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { michael@0: ancestorPusher.PushAncestorAndStyleScope(parentContent->AsElement()); michael@0: } else { michael@0: ancestorPusher.PushStyleScope(parentContent->AsElement()); michael@0: } michael@0: michael@0: if (!aItemIsWithinSVGText) { michael@0: // Probe for generated content before michael@0: CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext, michael@0: nsCSSPseudoElements::ePseudo_before, michael@0: aParentItem.mChildItems); michael@0: } michael@0: michael@0: uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK; michael@0: if (aItemIsWithinSVGText) { michael@0: flags |= ITEM_IS_WITHIN_SVG_TEXT; michael@0: } michael@0: if (aItemAllowsTextPathChild && aParentItem.mIsForSVGAElement) { michael@0: flags |= ITEM_ALLOWS_TEXT_PATH_CHILD; michael@0: } michael@0: michael@0: if (!aParentItem.mAnonChildren.IsEmpty()) { michael@0: // Use the anon-children list instead of the content tree child list so michael@0: // that we use any special style context that should be associated with michael@0: // the children, and so that we won't try to construct grandchildren frame michael@0: // constructor items before the frame is available for their parent. michael@0: AddFCItemsForAnonymousContent(aState, nullptr, aParentItem.mAnonChildren, michael@0: aParentItem.mChildItems, flags); michael@0: } else { michael@0: // Use the content tree child list: michael@0: FlattenedChildIterator iter(parentContent); michael@0: for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) { michael@0: // Get the parent of the content and check if it is a XBL children element michael@0: // (if the content is a children element then contentParent != parentContent because the michael@0: // FlattenedChildIterator will transitively iterate through michael@0: // for default content). Push the children element as an ancestor here because michael@0: // it does not have a frame and would not otherwise be pushed as an ancestor. michael@0: nsIContent* contentParent = content->GetParent(); michael@0: MOZ_ASSERT(contentParent, "Parent must be non-null because we are iterating children."); michael@0: TreeMatchContext::AutoAncestorPusher insertionPointPusher(aState.mTreeMatchContext); michael@0: if (contentParent != parentContent && contentParent->IsElement()) { michael@0: if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { michael@0: insertionPointPusher.PushAncestorAndStyleScope(contentParent->AsElement()); michael@0: } else { michael@0: insertionPointPusher.PushStyleScope(contentParent->AsElement()); michael@0: } michael@0: } michael@0: michael@0: // Manually check for comments/PIs, since we don't have a frame to pass to michael@0: // AddFrameConstructionItems. We know our parent is a non-replaced inline, michael@0: // so there is no need to do the NeedFrameFor check. michael@0: content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); michael@0: if (content->IsNodeOfType(nsINode::eCOMMENT) || michael@0: content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) { michael@0: continue; michael@0: } michael@0: if (content->IsElement()) { michael@0: // See comment explaining why we need to remove the "is possible michael@0: // restyle root" flags in AddFrameConstructionItems. But note michael@0: // that we can remove all restyle flags, just like in michael@0: // ProcessChildren and for the same reason. michael@0: content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS); michael@0: } michael@0: michael@0: nsRefPtr childContext = michael@0: ResolveStyleContext(parentStyleContext, content, &aState); michael@0: michael@0: AddFrameConstructionItemsInternal(aState, content, nullptr, content->Tag(), michael@0: content->GetNameSpaceID(), michael@0: iter.XBLInvolved(), childContext, michael@0: flags, nullptr, michael@0: aParentItem.mChildItems); michael@0: } michael@0: } michael@0: michael@0: if (!aItemIsWithinSVGText) { michael@0: // Probe for generated content after michael@0: CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext, michael@0: nsCSSPseudoElements::ePseudo_after, michael@0: aParentItem.mChildItems); michael@0: } michael@0: michael@0: aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline(); michael@0: } michael@0: michael@0: // return whether it's ok to append (in the AppendFrames sense) to michael@0: // aParentFrame if our nextSibling is aNextSibling. aParentFrame must michael@0: // be an ib-split inline. michael@0: static bool michael@0: IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame, nsIFrame* aNextSibling) michael@0: { michael@0: NS_PRECONDITION(IsInlineFrame(aParentFrame), michael@0: "Must have an inline parent here"); michael@0: do { michael@0: NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame), michael@0: "How is this not part of an ib-split?"); michael@0: if (aNextSibling || aParentFrame->GetNextContinuation() || michael@0: GetIBSplitSibling(aParentFrame)) { michael@0: return false; michael@0: } michael@0: michael@0: aNextSibling = aParentFrame->GetNextSibling(); michael@0: aParentFrame = aParentFrame->GetParent(); michael@0: } while (IsInlineFrame(aParentFrame)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, michael@0: nsIFrame* aContainingBlock, michael@0: nsIFrame* aFrame, michael@0: FrameConstructionItemList& aItems, michael@0: bool aIsAppend, michael@0: nsIFrame* aPrevSibling) michael@0: { michael@0: if (aItems.IsEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: // Before we go and append the frames, we must check for several michael@0: // special situations. michael@0: michael@0: // Situation #1 is a XUL frame that contains frames that are required michael@0: // to be wrapped in blocks. michael@0: if (aFrame->IsBoxFrame() && michael@0: !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) && michael@0: aItems.AnyItemsNeedBlockParent()) { michael@0: RecreateFramesForContent(aFrame->GetContent(), true); michael@0: return true; michael@0: } michael@0: michael@0: nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling); michael@0: michael@0: // Situation #2 is a flex container frame into which we're inserting new michael@0: // inline non-replaced children, adjacent to an existing anonymous flex item. michael@0: if (aFrame->GetType() == nsGkAtoms::flexContainerFrame) { michael@0: FCItemIterator iter(aItems); michael@0: michael@0: // Check if we're adding to-be-wrapped content right *after* an existing michael@0: // anonymous flex item (which would need to absorb this content). michael@0: if (aPrevSibling && IsAnonymousFlexItem(aPrevSibling) && michael@0: iter.item().NeedsAnonFlexItem(aState)) { michael@0: RecreateFramesForContent(aFrame->GetContent(), true); michael@0: return true; michael@0: } michael@0: michael@0: // Check if we're adding to-be-wrapped content right *before* an existing michael@0: // anonymous flex item (which would need to absorb this content). michael@0: if (nextSibling && IsAnonymousFlexItem(nextSibling)) { michael@0: // Jump to the last entry in the list michael@0: iter.SetToEnd(); michael@0: iter.Prev(); michael@0: if (iter.item().NeedsAnonFlexItem(aState)) { michael@0: RecreateFramesForContent(aFrame->GetContent(), true); michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Situation #3 is an anonymous flex item that's getting new children who michael@0: // don't want to be wrapped. michael@0: if (IsAnonymousFlexItem(aFrame)) { michael@0: nsIFrame* flexContainerFrame = aFrame->GetParent(); michael@0: NS_ABORT_IF_FALSE(flexContainerFrame && michael@0: flexContainerFrame->GetType() == nsGkAtoms::flexContainerFrame, michael@0: "anonymous flex items should only exist as children " michael@0: "of flex container frames"); michael@0: michael@0: // We need to push a null float containing block to be sure that michael@0: // "NeedsAnonFlexItem" will know we're not honoring floats for this michael@0: // inserted content. (In particular, this is necessary in order for michael@0: // NeedsAnonFlexItem's "GetGeometricParent" call to return the correct michael@0: // result.) We're not honoring floats on this content because it has the michael@0: // _flex container_ as its parent in the content tree. michael@0: nsFrameConstructorSaveState floatSaveState; michael@0: aState.PushFloatContainingBlock(nullptr, floatSaveState); michael@0: michael@0: FCItemIterator iter(aItems); michael@0: // Skip over things that _do_ need an anonymous flex item, because michael@0: // they're perfectly happy to go here -- they won't cause a reframe. michael@0: if (!iter.SkipItemsThatNeedAnonFlexItem(aState)) { michael@0: // We hit something that _doesn't_ need an anonymous flex item! michael@0: // Rebuild the flex container to bust it out. michael@0: RecreateFramesForContent(flexContainerFrame->GetContent(), true); michael@0: return true; michael@0: } michael@0: michael@0: // If we get here, then everything in |aItems| needs to be wrapped in michael@0: // an anonymous flex item. That's where it's already going - good! michael@0: } michael@0: michael@0: // Situation #4 is a case when table pseudo-frames don't work out right michael@0: ParentType parentType = GetParentType(aFrame); michael@0: // If all the kids want a parent of the type that aFrame is, then we're all michael@0: // set to go. Indeed, there won't be any table pseudo-frames created between michael@0: // aFrame and the kids, so those won't need to be merged with any table michael@0: // pseudo-frames that might already be kids of aFrame. If aFrame itself is a michael@0: // table pseudo-frame, then all the kids in this list would have wanted a michael@0: // frame of that type wrapping them anyway, so putting them inside it is ok. michael@0: if (!aItems.AllWantParentType(parentType)) { michael@0: // Don't give up yet. If parentType is not eTypeBlock and the parent is michael@0: // not a generated content frame, then try filtering whitespace out of the michael@0: // list. michael@0: if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) { michael@0: // For leading whitespace followed by a kid that wants our parent type, michael@0: // there are four cases: michael@0: // 1) We have a previous sibling which is not a table pseudo. That means michael@0: // that previous sibling wanted a (non-block) parent of the type we're michael@0: // looking at. Then the whitespace comes between two table-internal michael@0: // elements, so should be collapsed out. michael@0: // 2) We have a previous sibling which is a table pseudo. It might have michael@0: // kids who want this whitespace, so we need to reframe. michael@0: // 3) We have no previous sibling and our parent frame is not a table michael@0: // pseudo. That means that we'll be at the beginning of our actual michael@0: // non-block-type parent, and the whitespace is OK to collapse out. michael@0: // If something is ever inserted before us, it'll find our own parent michael@0: // as its parent and if it's something that would care about the michael@0: // whitespace it'll want a block parent, so it'll trigger a reframe at michael@0: // that point. michael@0: // 4) We have no previous sibling and our parent frame is a table pseudo. michael@0: // Need to reframe. michael@0: // All that is predicated on finding the correct previous sibling. We michael@0: // might have to walk backwards along continuations from aFrame to do so. michael@0: // michael@0: // It's always OK to drop whitespace between any two items that want a michael@0: // parent of type parentType. michael@0: // michael@0: // For trailing whitespace preceded by a kid that wants our parent type, michael@0: // there are four cases: michael@0: // 1) We have a next sibling which is not a table pseudo. That means michael@0: // that next sibling wanted a (non-block) parent of the type we're michael@0: // looking at. Then the whitespace comes between two table-internal michael@0: // elements, so should be collapsed out. michael@0: // 2) We have a next sibling which is a table pseudo. It might have michael@0: // kids who want this whitespace, so we need to reframe. michael@0: // 3) We have no next sibling and our parent frame is not a table michael@0: // pseudo. That means that we'll be at the end of our actual michael@0: // non-block-type parent, and the whitespace is OK to collapse out. michael@0: // If something is ever inserted after us, it'll find our own parent michael@0: // as its parent and if it's something that would care about the michael@0: // whitespace it'll want a block parent, so it'll trigger a reframe at michael@0: // that point. michael@0: // 4) We have no next sibling and our parent frame is a table pseudo. michael@0: // Need to reframe. michael@0: // All that is predicated on finding the correct next sibling. We might michael@0: // have to walk forward along continuations from aFrame to do so. That michael@0: // said, in the case when nextSibling is null at this point and aIsAppend michael@0: // is true, we know we're in case 3. Furthermore, in that case we don't michael@0: // even have to worry about the table pseudo situation; we know our michael@0: // parent is not a table pseudo there. michael@0: FCItemIterator iter(aItems); michael@0: FCItemIterator start(iter); michael@0: do { michael@0: if (iter.SkipItemsWantingParentType(parentType)) { michael@0: break; michael@0: } michael@0: michael@0: // iter points to an item that wants a different parent. If it's not michael@0: // whitespace, we're done; no more point scanning the list. michael@0: if (!iter.item().IsWhitespace(aState)) { michael@0: break; michael@0: } michael@0: michael@0: if (iter == start) { michael@0: // Leading whitespace. How to handle this depends on our michael@0: // previous sibling and aFrame. See the long comment above. michael@0: nsIFrame* prevSibling = aPrevSibling; michael@0: if (!prevSibling) { michael@0: // Try to find one after all michael@0: nsIFrame* parentPrevCont = aFrame->GetPrevContinuation(); michael@0: while (parentPrevCont) { michael@0: prevSibling = parentPrevCont->GetLastChild(kPrincipalList); michael@0: if (prevSibling) { michael@0: break; michael@0: } michael@0: parentPrevCont = parentPrevCont->GetPrevContinuation(); michael@0: } michael@0: }; michael@0: if (prevSibling) { michael@0: if (IsTablePseudo(prevSibling)) { michael@0: // need to reframe michael@0: break; michael@0: } michael@0: } else if (IsTablePseudo(aFrame)) { michael@0: // need to reframe michael@0: break; michael@0: } michael@0: } michael@0: michael@0: FCItemIterator spaceEndIter(iter); michael@0: // Advance spaceEndIter past any whitespace michael@0: bool trailingSpaces = spaceEndIter.SkipWhitespace(aState); michael@0: michael@0: bool okToDrop; michael@0: if (trailingSpaces) { michael@0: // Trailing whitespace. How to handle this depeds on aIsAppend, our michael@0: // next sibling and aFrame. See the long comment above. michael@0: okToDrop = aIsAppend && !nextSibling; michael@0: if (!okToDrop) { michael@0: if (!nextSibling) { michael@0: // Try to find one after all michael@0: nsIFrame* parentNextCont = aFrame->GetNextContinuation(); michael@0: while (parentNextCont) { michael@0: nextSibling = parentNextCont->GetFirstPrincipalChild(); michael@0: if (nextSibling) { michael@0: break; michael@0: } michael@0: parentNextCont = parentNextCont->GetNextContinuation(); michael@0: } michael@0: } michael@0: michael@0: okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) || michael@0: (!nextSibling && !IsTablePseudo(aFrame)); michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?"); michael@0: } michael@0: #endif michael@0: } else { michael@0: okToDrop = (spaceEndIter.item().DesiredParentType() == parentType); michael@0: } michael@0: michael@0: if (okToDrop) { michael@0: iter.DeleteItemsTo(spaceEndIter); michael@0: } else { michael@0: // We're done: we don't want to drop the whitespace, and it has the michael@0: // wrong parent type. michael@0: break; michael@0: } michael@0: michael@0: // Now loop, since |iter| points to item right after the whitespace we michael@0: // removed. michael@0: } while (!iter.IsDone()); michael@0: } michael@0: michael@0: // We might be able to figure out some sort of optimizations here, but they michael@0: // would have to depend on having a correct aPrevSibling and a correct next michael@0: // sibling. For example, we can probably avoid reframing if none of michael@0: // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it michael@0: // doesn't seem worth it to worry about that for now, especially since we michael@0: // in fact do not have a reliable aPrevSibling, nor any next sibling, in michael@0: // this method. michael@0: michael@0: // aItems might have changed, so recheck the parent type thing. In fact, michael@0: // it might be empty, so recheck that too. michael@0: if (aItems.IsEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: if (!aItems.AllWantParentType(parentType)) { michael@0: // Reframing aFrame->GetContent() is good enough, since the content of michael@0: // table pseudo-frames is the ancestor content. michael@0: RecreateFramesForContent(aFrame->GetContent(), true); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // Now we have several cases involving {ib} splits. Put them all in a michael@0: // do/while with breaks to take us to the "go and reconstruct" code. michael@0: do { michael@0: if (IsInlineFrame(aFrame)) { michael@0: if (aItems.AreAllItemsInline()) { michael@0: // We can just put the kids in. michael@0: return false; michael@0: } michael@0: michael@0: if (!IsFramePartOfIBSplit(aFrame)) { michael@0: // Need to go ahead and reconstruct. michael@0: break; michael@0: } michael@0: michael@0: // Now we're adding kids including some blocks to an inline part of an michael@0: // {ib} split. If we plan to call AppendFrames, and don't have a next michael@0: // sibling for the new frames, and our parent is the last continuation of michael@0: // the last part of the {ib} split, and the same is true of all our michael@0: // ancestor inlines (they have no following continuations and they're the michael@0: // last part of their {ib} splits and we'd be adding to the end for all michael@0: // of them), then AppendFrames will handle things for us. Bail out in michael@0: // that case. michael@0: if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) { michael@0: return false; michael@0: } michael@0: michael@0: // Need to reconstruct. michael@0: break; michael@0: } michael@0: michael@0: // Now we know we have a block parent. If it's not part of an michael@0: // ib-split, we're all set. michael@0: if (!IsFramePartOfIBSplit(aFrame)) { michael@0: return false; michael@0: } michael@0: michael@0: // We're adding some kids to a block part of an {ib} split. If all the michael@0: // kids are blocks, we don't need to reconstruct. michael@0: if (aItems.AreAllItemsBlock()) { michael@0: return false; michael@0: } michael@0: michael@0: // We might have some inline kids for this block. Just reconstruct. michael@0: break; michael@0: } while (0); michael@0: michael@0: // If we don't have a containing block, start with aFrame and look for one. michael@0: if (!aContainingBlock) { michael@0: aContainingBlock = aFrame; michael@0: } michael@0: michael@0: // To find the right block to reframe, just walk up the tree until we find a michael@0: // frame that is: michael@0: // 1) Not part of an IB split michael@0: // 2) Not a pseudo-frame michael@0: // 3) Not an inline frame michael@0: // We're guaranteed to find one, since nsStyleContext::ApplyStyleFixups michael@0: // enforces that the root is display:none, display:table, or display:block. michael@0: // Note that walking up "too far" is OK in terms of correctness, even if it michael@0: // might be a little inefficient. This is why we walk out of all michael@0: // pseudo-frames -- telling which ones are or are not OK to walk out of is michael@0: // too hard (and I suspect that we do in fact need to walk out of all of michael@0: // them). michael@0: while (IsFramePartOfIBSplit(aContainingBlock) || michael@0: aContainingBlock->IsInlineOutside() || michael@0: aContainingBlock->StyleContext()->GetPseudo()) { michael@0: aContainingBlock = aContainingBlock->GetParent(); michael@0: NS_ASSERTION(aContainingBlock, michael@0: "Must have non-inline, non-ib-split, non-pseudo frame as " michael@0: "root (or child of root, for a table root)!"); michael@0: } michael@0: michael@0: // Tell parent of the containing block to reformulate the michael@0: // entire block. This is painful and definitely not optimal michael@0: // but it will *always* get the right answer. michael@0: michael@0: nsIContent *blockContent = aContainingBlock->GetContent(); michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::WipeContainingBlock: blockContent=%p\n", michael@0: static_cast(blockContent)); michael@0: } michael@0: #endif michael@0: RecreateFramesForContent(blockContent, true); michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame) michael@0: { michael@0: michael@0: #ifdef DEBUG michael@0: // ReframeContainingBlock is a NASTY routine, it causes terrible performance problems michael@0: // so I want to see when it is happening! Unfortunately, it is happening way to often because michael@0: // so much content on the web causes block-in-inline frame situations and we handle them michael@0: // very poorly michael@0: if (gNoisyContentUpdates) { michael@0: printf("nsCSSFrameConstructor::ReframeContainingBlock frame=%p\n", michael@0: static_cast(aFrame)); michael@0: } michael@0: #endif michael@0: michael@0: // XXXbz how exactly would we get here while isReflowing anyway? Should this michael@0: // whole test be ifdef DEBUG? michael@0: if (mPresShell->IsReflowLocked()) { michael@0: // don't ReframeContainingBlock, this will result in a crash michael@0: // if we remove a tree that's in reflow - see bug 121368 for testcase michael@0: NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Get the first "normal" ancestor of the target frame. michael@0: nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame); michael@0: if (containingBlock) { michael@0: // From here we look for the containing block in case the target michael@0: // frame is already a block (which can happen when an inline frame michael@0: // wraps some of its content in an anonymous block; see michael@0: // ConstructInline) michael@0: michael@0: // NOTE: We used to get the FloatContainingBlock here, but it was often wrong. michael@0: // GetIBContainingBlock works much better and provides the correct container in all cases michael@0: // so GetFloatContainingBlock(aFrame) has been removed michael@0: michael@0: // And get the containingBlock's content michael@0: nsCOMPtr blockContent = containingBlock->GetContent(); michael@0: if (blockContent) { michael@0: #ifdef DEBUG michael@0: if (gNoisyContentUpdates) { michael@0: printf(" ==> blockContent=%p\n", static_cast(blockContent)); michael@0: } michael@0: #endif michael@0: return RecreateFramesForContent(blockContent, true); michael@0: } michael@0: } michael@0: michael@0: // If we get here, we're screwed! michael@0: return RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(), michael@0: true); michael@0: } michael@0: michael@0: nsresult michael@0: nsCSSFrameConstructor::GenerateChildFrames(nsIFrame* aFrame) michael@0: { michael@0: { michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: BeginUpdate(); michael@0: michael@0: nsFrameItems childItems; michael@0: nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr); michael@0: // We don't have a parent frame with a pending binding constructor here, michael@0: // so no need to worry about ordering of the kids' constructors with it. michael@0: // Pass null for the PendingBinding. michael@0: ProcessChildren(state, aFrame->GetContent(), aFrame->StyleContext(), michael@0: aFrame, false, childItems, false, michael@0: nullptr); michael@0: michael@0: aFrame->SetInitialChildList(kPrincipalList, childItems); michael@0: michael@0: EndUpdate(); michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: nsAccessibilityService* accService = nsIPresShell::AccService(); michael@0: if (accService) { michael@0: nsIContent* container = aFrame->GetContent(); michael@0: nsIContent* child = container->GetFirstChild(); michael@0: if (child) { michael@0: accService->ContentRangeInserted(mPresShell, container, child, nullptr); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // call XBL constructors after the frames are created michael@0: mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////// michael@0: // nsCSSFrameConstructor::FrameConstructionItem methods // michael@0: ////////////////////////////////////////////////////////// michael@0: bool michael@0: nsCSSFrameConstructor:: michael@0: FrameConstructionItem::IsWhitespace(nsFrameConstructorState& aState) const michael@0: { michael@0: NS_PRECONDITION(aState.mCreatingExtraFrames || michael@0: !mContent->GetPrimaryFrame(), "How did that happen?"); michael@0: if (!mIsText) { michael@0: return false; michael@0: } michael@0: mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | michael@0: NS_REFRAME_IF_WHITESPACE); michael@0: return mContent->TextIsOnlyWhitespace(); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////// michael@0: // nsCSSFrameConstructor::FrameConstructionItemList methods // michael@0: ////////////////////////////////////////////////////////////// michael@0: void michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: AdjustCountsForItem(FrameConstructionItem* aItem, int32_t aDelta) michael@0: { michael@0: NS_PRECONDITION(aDelta == 1 || aDelta == -1, "Unexpected delta"); michael@0: mItemCount += aDelta; michael@0: if (aItem->mIsAllInline) { michael@0: mInlineCount += aDelta; michael@0: } michael@0: if (aItem->mIsBlock) { michael@0: mBlockCount += aDelta; michael@0: } michael@0: if (aItem->mIsLineParticipant) { michael@0: mLineParticipantCount += aDelta; michael@0: } michael@0: mDesiredParentCounts[aItem->DesiredParentType()] += aDelta; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods // michael@0: //////////////////////////////////////////////////////////////////////// michael@0: inline bool michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: Iterator::SkipItemsWantingParentType(ParentType aParentType) michael@0: { michael@0: NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); michael@0: while (item().DesiredParentType() == aParentType) { michael@0: Next(); michael@0: if (IsDone()) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsCSSFrameConstructor::FrameConstructionItem:: michael@0: NeedsAnonFlexItem(const nsFrameConstructorState& aState) michael@0: { michael@0: if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) { michael@0: // This will be an inline non-replaced box. michael@0: return true; michael@0: } michael@0: michael@0: if (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) && michael@0: aState.GetGeometricParent(mStyleContext->StyleDisplay(), nullptr)) { michael@0: // We're abspos or fixedpos, which means we'll spawn a placeholder which michael@0: // we'll need to wrap in an anonymous flex item. So, we just treat michael@0: // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item, michael@0: // and then when we spawn the placeholder, it'll end up in the right spot. michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: inline bool michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: Iterator::SkipItemsThatNeedAnonFlexItem( michael@0: const nsFrameConstructorState& aState) michael@0: { michael@0: NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); michael@0: while (item().NeedsAnonFlexItem(aState)) { michael@0: Next(); michael@0: if (IsDone()) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: inline bool michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: Iterator::SkipItemsThatDontNeedAnonFlexItem( michael@0: const nsFrameConstructorState& aState) michael@0: { michael@0: NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); michael@0: while (!(item().NeedsAnonFlexItem(aState))) { michael@0: Next(); michael@0: if (IsDone()) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: inline bool michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: Iterator::SkipWhitespace(nsFrameConstructorState& aState) michael@0: { michael@0: NS_PRECONDITION(!IsDone(), "Shouldn't be done yet"); michael@0: NS_PRECONDITION(item().IsWhitespace(aState), "Not pointing to whitespace?"); michael@0: do { michael@0: Next(); michael@0: if (IsDone()) { michael@0: return true; michael@0: } michael@0: } while (item().IsWhitespace(aState)); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: Iterator::AppendItemToList(FrameConstructionItemList& aTargetList) michael@0: { michael@0: NS_ASSERTION(&aTargetList != &mList, "Unexpected call"); michael@0: NS_PRECONDITION(!IsDone(), "should not be done"); michael@0: michael@0: FrameConstructionItem* item = ToItem(mCurrent); michael@0: Next(); michael@0: PR_REMOVE_LINK(item); michael@0: PR_APPEND_LINK(item, &aTargetList.mItems); michael@0: michael@0: mList.AdjustCountsForItem(item, -1); michael@0: aTargetList.AdjustCountsForItem(item, 1); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: Iterator::AppendItemsToList(const Iterator& aEnd, michael@0: FrameConstructionItemList& aTargetList) michael@0: { michael@0: NS_ASSERTION(&aTargetList != &mList, "Unexpected call"); michael@0: NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?"); michael@0: michael@0: // We can't just move our guts to the other list if it already has michael@0: // some information or if we're not moving our entire list. michael@0: if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty() || michael@0: !aTargetList.mUndisplayedItems.IsEmpty()) { michael@0: do { michael@0: AppendItemToList(aTargetList); michael@0: } while (*this != aEnd); michael@0: return; michael@0: } michael@0: michael@0: // move over the list of items michael@0: PR_INSERT_AFTER(&aTargetList.mItems, &mList.mItems); michael@0: // Need to init when we remove to makd ~FrameConstructionItemList work right. michael@0: PR_REMOVE_AND_INIT_LINK(&mList.mItems); michael@0: michael@0: // Copy over the various counters michael@0: aTargetList.mInlineCount = mList.mInlineCount; michael@0: aTargetList.mBlockCount = mList.mBlockCount; michael@0: aTargetList.mLineParticipantCount = mList.mLineParticipantCount; michael@0: aTargetList.mItemCount = mList.mItemCount; michael@0: memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts, michael@0: sizeof(aTargetList.mDesiredParentCounts)); michael@0: michael@0: // Swap out undisplayed item arrays, before we nuke the array on our end michael@0: aTargetList.mUndisplayedItems.SwapElements(mList.mUndisplayedItems); michael@0: michael@0: // reset mList michael@0: mList.~FrameConstructionItemList(); michael@0: new (&mList) FrameConstructionItemList(); michael@0: michael@0: // Point ourselves to aEnd, as advertised michael@0: mCurrent = mEnd = &mList.mItems; michael@0: NS_POSTCONDITION(*this == aEnd, "How did that happen?"); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: Iterator::InsertItem(FrameConstructionItem* aItem) michael@0: { michael@0: // Just insert the item before us. There's no magic here. michael@0: PR_INSERT_BEFORE(aItem, mCurrent); michael@0: mList.AdjustCountsForItem(aItem, 1); michael@0: michael@0: NS_POSTCONDITION(PR_NEXT_LINK(aItem) == mCurrent, "How did that happen?"); michael@0: } michael@0: michael@0: void michael@0: nsCSSFrameConstructor::FrameConstructionItemList:: michael@0: Iterator::DeleteItemsTo(const Iterator& aEnd) michael@0: { michael@0: NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?"); michael@0: NS_PRECONDITION(*this != aEnd, "Shouldn't be at aEnd yet"); michael@0: michael@0: do { michael@0: NS_ASSERTION(!IsDone(), "Ran off end of list?"); michael@0: FrameConstructionItem* item = ToItem(mCurrent); michael@0: Next(); michael@0: PR_REMOVE_LINK(item); michael@0: mList.AdjustCountsForItem(item, -1); michael@0: delete item; michael@0: } while (*this != aEnd); michael@0: }