diff options
Diffstat (limited to 'layout/base')
23 files changed, 1143 insertions, 1654 deletions
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 124b5535e..5c599e1ef 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -146,8 +146,10 @@ RestyleManager::RestyleElement(Element* aElement, } if (aMinHint & nsChangeHint_ReconstructFrame) { - FrameConstructor()->RecreateFramesForContent(aElement, false, - nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr); + FrameConstructor()->RecreateFramesForContent( + aElement, + nsCSSFrameConstructor::InsertionKind::Sync, + nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION); } else if (aPrimaryFrame) { ComputeAndProcessStyleChange(aPrimaryFrame, aMinHint, aRestyleTracker, aRestyleHint, aRestyleHintData); @@ -246,14 +248,14 @@ ElementForStyleContext(nsIContent* aParentContent, // Forwarded nsIDocumentObserver method, to handle restyling (and // passing the notification to the frame). -nsresult +void RestyleManager::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) { // XXXbz it would be good if this function only took Elements, but // we'd have to make ESM guarantee that usefully. if (!aContent->IsElement()) { - return NS_OK; + return; } Element* aElement = aContent->AsElement(); @@ -263,7 +265,6 @@ RestyleManager::ContentStateChanged(nsIContent* aContent, ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint); PostRestyleEvent(aElement, restyleHint, changeHint); - return NS_OK; } // Forwarded nsIMutationObserver method, to handle restyling. @@ -1062,6 +1063,25 @@ ElementForStyleContext(nsIContent* aParentContent, return f->GetContent()->AsElement(); } + Element* frameElement = aFrame->GetContent()->AsElement(); + if (frameElement->IsNativeAnonymous() && + nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(aPseudoType)) { + // NAC-implemented pseudos use the closest non-NAC element as their + // element to inherit from. + // + // FIXME(heycam): In theory we shouldn't need to limit this only to + // JS-created pseudo-implementing NAC, as all pseudo-implementing + // should use the closest non-native anonymous ancestor element as + // its originating element. But removing that part of the condition + // reveals some bugs in style resultion with display:contents and + // XBL. See bug 1345809. + Element* originatingElement = + nsContentUtils::GetClosestNonNativeAnonymousAncestor(frameElement); + if (originatingElement) { + return originatingElement; + } + } + if (aParentContent) { return aParentContent->AsElement(); } @@ -1811,7 +1831,7 @@ ElementRestyler::ConditionallyRestyleUndisplayedNodes( } for (UndisplayedNode* undisplayed = aUndisplayed; undisplayed; - undisplayed = undisplayed->mNext) { + undisplayed = undisplayed->getNext()) { if (!undisplayed->mContent->IsElement()) { continue; @@ -2341,6 +2361,16 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf, return; } + // Each NAC element inherits from the first non-NAC ancestor, so child + // NAC may inherit from our parent instead of us. That means we can't + // cull traversal if our style context didn't change. + if (aSelf->GetContent() && aSelf->GetContent()->IsNativeAnonymous()) { + LOG_RESTYLE_CONTINUE("native anonymous content"); + aRestyleResult = RestyleResult::eContinue; + aCanStopWithStyleChange = false; + return; + } + // Style changes might have moved children between the two nsLetterFrames // (the one matching ::first-letter and the one containing the rest of the // content). Continue restyling to the children of the nsLetterFrame so @@ -3442,7 +3472,7 @@ ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, if (undisplayed) { pusher.PushAncestorAndStyleScope(undisplayedParent); } - for (; undisplayed; undisplayed = undisplayed->mNext) { + for (; undisplayed; undisplayed = undisplayed->getNext()) { NS_ASSERTION(undisplayedParent || undisplayed->mContent == mPresContext->Document()->GetRootElement(), @@ -3459,7 +3489,7 @@ ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, // not have a frame and would not otherwise be pushed as an ancestor. nsIContent* parent = undisplayed->mContent->GetParent(); TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext); - if (parent && nsContentUtils::IsContentInsertionPoint(parent)) { + if (parent && parent->IsActiveChildrenElement()) { insertionPointPusher.PushAncestorAndStyleScope(parent); } @@ -3598,14 +3628,14 @@ ElementRestyler::MustReframeForPseudo(CSSPseudoElementType aPseudoType, // Check for a ::before pseudo style and the absence of a ::before content, // but only if aFrame is null or is the first continuation/ib-split. if ((aFrame && !nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame)) || - nsLayoutUtils::GetBeforeFrameForContent(aGenConParentFrame, aContent)) { + nsLayoutUtils::GetBeforeFrame(aContent)) { return false; } } else { // Similarly for ::after, but check for being the last continuation/ // ib-split. if ((aFrame && nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) || - nsLayoutUtils::GetAfterFrameForContent(aGenConParentFrame, aContent)) { + nsLayoutUtils::GetAfterFrame(aContent)) { return false; } } @@ -3685,7 +3715,7 @@ ElementRestyler::RestyleContentChildren(nsIFrame* aParent, // nsPageFrame that does not have a content. nsIContent* parent = child->GetContent() ? child->GetContent()->GetParent() : nullptr; TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext); - if (parent && nsContentUtils::IsContentInsertionPoint(parent)) { + if (parent && parent->IsActiveChildrenElement()) { insertionPointPusher.PushAncestorAndStyleScope(parent); } diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h index e22fe9058..3b60b331a 100644 --- a/layout/base/RestyleManager.h +++ b/layout/base/RestyleManager.h @@ -59,8 +59,8 @@ public: // Forwarded nsIDocumentObserver method, to handle restyling (and // passing the notification to the frame). - nsresult ContentStateChanged(nsIContent* aContent, - EventStates aStateMask); + void ContentStateChanged(nsIContent* aContent, + EventStates aStateMask); // Forwarded nsIMutationObserver method, to handle restyling. void AttributeWillChange(Element* aElement, diff --git a/layout/base/RestyleManagerBase.cpp b/layout/base/RestyleManagerBase.cpp index 6ef048a19..1a3cae4bf 100644 --- a/layout/base/RestyleManagerBase.cpp +++ b/layout/base/RestyleManagerBase.cpp @@ -1249,8 +1249,10 @@ if (!mDestroyedFrames) { // We could also have problems with triggering of CSS transitions // on elements whose frames are reconstructed, since we depend on // the reconstruction happening synchronously. - frameConstructor->RecreateFramesForContent(content, false, - nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION, nullptr); + frameConstructor->RecreateFramesForContent( + content, + nsCSSFrameConstructor::InsertionKind::Sync, + nsCSSFrameConstructor::REMOVE_FOR_RECONSTRUCTION); } else { NS_ASSERTION(frame, "This shouldn't happen"); diff --git a/layout/base/RestyleManagerHandle.h b/layout/base/RestyleManagerHandle.h index 8be04d12c..bee1fc6d9 100644 --- a/layout/base/RestyleManagerHandle.h +++ b/layout/base/RestyleManagerHandle.h @@ -125,8 +125,8 @@ public: nsIContent* aChild); inline void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent); - inline nsresult ContentStateChanged(nsIContent* aContent, - EventStates aStateMask); + inline void ContentStateChanged(nsIContent* aContent, + EventStates aStateMask); inline void AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/base/RestyleManagerHandleInlines.h b/layout/base/RestyleManagerHandleInlines.h index cc374edd5..8d6ca9142 100644 --- a/layout/base/RestyleManagerHandleInlines.h +++ b/layout/base/RestyleManagerHandleInlines.h @@ -122,7 +122,7 @@ RestyleManagerHandle::Ptr::RestyleForAppend(nsIContent* aContainer, FORWARD(RestyleForAppend, (aContainer, aFirstNewContent)); } -nsresult +void RestyleManagerHandle::Ptr::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) { diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index 42ca23bb1..0659ab857 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -277,24 +277,17 @@ ServoRestyleManager::FrameForPseudoElement(const nsIContent* aContent, nsIAtom* aPseudoTagOrNull) { MOZ_ASSERT_IF(aPseudoTagOrNull, aContent->IsElement()); - nsIFrame* primaryFrame = aContent->GetPrimaryFrame(); if (!aPseudoTagOrNull) { - return primaryFrame; - } - - if (!primaryFrame) { - return nullptr; + return aContent->GetPrimaryFrame(); } - // NOTE: we probably need to special-case display: contents here. Gecko's - // RestyleManager passes the primary frame of the parent instead. if (aPseudoTagOrNull == nsCSSPseudoElements::before) { - return nsLayoutUtils::GetBeforeFrameForContent(primaryFrame, aContent); + return nsLayoutUtils::GetBeforeFrame(aContent); } if (aPseudoTagOrNull == nsCSSPseudoElements::after) { - return nsLayoutUtils::GetAfterFrameForContent(primaryFrame, aContent); + return nsLayoutUtils::GetAfterFrame(aContent); } MOZ_CRASH("Unkown pseudo-element given to " @@ -513,12 +506,12 @@ ServoRestyleManager::ContentRemoved(nsINode* aContainer, NS_WARNING("stylo: ServoRestyleManager::ContentRemoved not implemented"); } -nsresult +void ServoRestyleManager::ContentStateChanged(nsIContent* aContent, EventStates aChangedBits) { if (!aContent->IsElement()) { - return NS_OK; + return; } Element* aElement = aContent->AsElement(); @@ -552,7 +545,6 @@ ServoRestyleManager::ContentStateChanged(nsIContent* aContent, snapshot->AddState(previousState); PostRestyleEvent(aElement, restyleHint, changeHint); - return NS_OK; } void diff --git a/layout/base/ServoRestyleManager.h b/layout/base/ServoRestyleManager.h index 6856171c1..b6bb63d08 100644 --- a/layout/base/ServoRestyleManager.h +++ b/layout/base/ServoRestyleManager.h @@ -63,8 +63,7 @@ public: nsIContent* aChild); void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent); - nsresult ContentStateChanged(nsIContent* aContent, - EventStates aStateMask); + void ContentStateChanged(nsIContent* aContent, EventStates aStateMask); void AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, diff --git a/layout/base/crashtests/1261351-iframe.html b/layout/base/crashtests/1261351-iframe.html index 82c1e25fa..a0484f332 100644 --- a/layout/base/crashtests/1261351-iframe.html +++ b/layout/base/crashtests/1261351-iframe.html @@ -3,23 +3,26 @@ 'use strict'; // -sp-context: content (function () { - let proto = Object.create(HTMLDivElement.prototype); - proto.template = `<style></style>`; - proto.createdCallback = function() { - let shadow = this.createShadowRoot(); - if (this.template) { - let te = document.createElement('template'); - te.innerHTML = this.template; - shadow.appendChild(document.importNode(te.content, true)); - } - }; + class UiComponentTest extends HTMLDivElement { + constructor() { + super(); + this.template = `<style></style>`; + } - let UiComponentTest = document.registerElement('ui-component-test', { - prototype: proto, - }); + connectedCallback() { + let shadow = this.createShadowRoot(); + if (this.template) { + let te = document.createElement('template'); + te.innerHTML = this.template; + shadow.appendChild(document.importNode(te.content, true)); + } + } + }; - let uic = new UiComponentTest(); - document.body.appendChild(uic); + customElements.define('ui-component-test', UiComponentTest, { extend: 'div'} ); + + let uic = new UiComponentTest(); + document.body.appendChild(uic); })(); </script> diff --git a/layout/base/crashtests/1404789-1.html b/layout/base/crashtests/1404789-1.html new file mode 100644 index 000000000..bd577ae24 --- /dev/null +++ b/layout/base/crashtests/1404789-1.html @@ -0,0 +1,16 @@ +<!doctype html> +<script> + // Test for content redistribution outside of the document. + // Passes if it doesn't assert. + let host = document.createElement('div'); + host.innerHTML = "<div id='foo'></div>"; + + let shadowRoot = host.createShadowRoot(); + shadowRoot.innerHTML = "<content select='#foo'></content>"; + + host.firstElementChild.removeAttribute('id'); + + // Move to the document, do the same. + document.documentElement.appendChild(host); + host.firstElementChild.setAttribute('id', 'foo'); +</script> diff --git a/layout/base/crashtests/1404789-2.html b/layout/base/crashtests/1404789-2.html new file mode 100644 index 000000000..667618141 --- /dev/null +++ b/layout/base/crashtests/1404789-2.html @@ -0,0 +1,2 @@ +<!doctype html> +<iframe style="display: none" src="1404789-1.html"></iframe> diff --git a/layout/base/crashtests/1419762.html b/layout/base/crashtests/1419762.html new file mode 100644 index 000000000..08a56106d --- /dev/null +++ b/layout/base/crashtests/1419762.html @@ -0,0 +1,15 @@ +<style id='style_1'> + :first-child { display: table-column-group; } +</style> +<script> + try { o1 = document.createElement('isindex') } catch(e) { } + try { o2 = document.createElement('input') } catch(e) { } + try { o3 = document.createElement('optgroup') } catch(e) { } + try { o4 = document.createElement('col') } catch(e) { } + try { document.documentElement.appendChild(o1) } catch(e) { } + try { o1.appendChild(o2) } catch(e) { } + try { o1.appendChild(o3) } catch(e) { } + try { document.documentElement.offsetTop; } catch (e) { } + try { document.documentElement.appendChild(o4) } catch(e) { } + try { document.styleSheets[0].insertRule('optgroup::first-line { list-style-type: japanese-formal; }', 0); } catch(e) { } +</script> diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 6ded4ff3f..9b09d1c84 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -483,3 +483,6 @@ load 1308793.svg load 1308848-1.html load 1308848-2.html asserts(0-1) load 1343606.html # bug 1343948 +load 1404789-1.html +load 1404789-2.html +load 1419762.html diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 37cd3e45e..fc458b5eb 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -43,6 +43,7 @@ #include "nsContainerFrame.h" #include "nsNameSpaceManager.h" #include "nsIComboboxControlFrame.h" +#include "nsComboboxControlFrame.h" #include "nsIListControlFrame.h" #include "nsIDOMCharacterData.h" #include "nsPlaceholderFrame.h" @@ -691,10 +692,9 @@ nsAbsoluteItems::nsAbsoluteItems(nsContainerFrame* aContainingBlock) void nsAbsoluteItems::AddChild(nsIFrame* aChild) { - NS_ASSERTION(aChild->PresContext()->FrameManager()-> - GetPlaceholderFrameFor(aChild), - "Child without placeholder being added to nsAbsoluteItems?"); aChild->AddStateBits(NS_FRAME_OUT_OF_FLOW); + NS_ASSERTION(aChild->GetPlaceholderFrame(), + "Child without placeholder being added to nsAbsoluteItems?"); nsFrameItems::AddChild(aChild); } @@ -1831,11 +1831,7 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat aPseudoElement == CSSPseudoElementType::after, "unexpected aPseudoElement"); - // XXXbz is this ever true? - if (!aParentContent->IsElement()) { - NS_ERROR("Bogus generated content parent"); - return; - } + MOZ_ASSERT(aParentContent->IsElement()); StyleSetHandle styleSet = mPresShell->StyleSet(); @@ -1863,7 +1859,15 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget()); if (NS_FAILED(rv)) return; + + // Cleared when the pseudo is unbound from the tree, so no need to store a + // strong reference, nor a destructor. + nsIAtom* property = isBefore + ? nsGkAtoms::beforePseudoProperty : nsGkAtoms::afterPseudoProperty; + aParentContent->SetProperty(property, container.get()); + container->SetIsNativeAnonymousRoot(); + container->SetPseudoElementType(aPseudoElement); // If the parent is in a shadow tree, make sure we don't // bind with a document because shadow roots and its descendants @@ -3027,14 +3031,12 @@ nsCSSFrameConstructor::CreatePlaceholderFrameFor(nsIPresShell* aPresShell, placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow); - // The placeholder frame has a pointer back to the out-of-flow frame + // Associate the placeholder/out-of-flow with each other. placeholderFrame->SetOutOfFlowFrame(aFrame); + aFrame->SetProperty(nsIFrame::PlaceholderFrameProperty(), placeholderFrame); aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW); - // Add mapping from absolutely positioned frame to its placeholder frame - aPresShell->FrameManager()->RegisterPlaceholderFrame(placeholderFrame); - return placeholderFrame; } @@ -3075,7 +3077,7 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, // The drop-down list's frame is created explicitly. The combobox frame shares its content // with the drop-down list. nsFrameState flags = NS_BLOCK_FLOAT_MGR; - nsContainerFrame* comboboxFrame = + nsComboboxControlFrame* comboboxFrame = NS_NewComboboxControlFrame(mPresShell, styleContext, flags); // Save the history state so we don't restore during construction @@ -3090,10 +3092,6 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, aState.AddChild(comboboxFrame, aFrameItems, content, styleContext, aParentFrame); - nsIComboboxControlFrame* comboBox = do_QueryFrame(comboboxFrame); - NS_ASSERTION(comboBox, "NS_NewComboboxControlFrame returned frame that " - "doesn't implement nsIComboboxControlFrame"); - // Resolve pseudo element style for the dropdown list RefPtr<nsStyleContext> listStyle; listStyle = mPresShell->StyleSet()-> @@ -3108,7 +3106,7 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, listControlFrame->SetComboboxFrame(comboboxFrame); } // Notify combobox that it should use the listbox as it's popup - comboBox->SetDropDown(listFrame); + comboboxFrame->SetDropDown(listFrame); NS_ASSERTION(!listFrame->IsAbsPosContainingBlock(), "Ended up with positioned dropdown list somehow."); @@ -3129,10 +3127,29 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, // Create display and button frames from the combobox's anonymous content. // The anonymous content is appended to existing anonymous content for this // element (the scrollbars). - nsFrameItems childItems; - CreateAnonymousFrames(aState, content, comboboxFrame, - aItem.mPendingBinding, childItems); + + // nsComboboxControlFrame needs special frame creation behavior for its first + // piece of anonymous content, which means that we can't take the normal + // ProcessChildren path. + AutoTArray<nsIAnonymousContentCreator::ContentInfo, 2> newAnonymousItems; + DebugOnly<nsresult> rv = GetAnonymousContent(content, comboboxFrame, newAnonymousItems); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + MOZ_ASSERT(newAnonymousItems.Length() == 2); + + // Manually create a frame for the special NAC. + MOZ_ASSERT(newAnonymousItems[0].mContent == comboboxFrame->GetDisplayNode()); + newAnonymousItems.RemoveElementAt(0); + nsIFrame* customFrame = comboboxFrame->CreateFrameForDisplayNode(); + MOZ_ASSERT(customFrame); + customFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT); + childItems.AddChild(customFrame); + + // The other piece of NAC can take the normal path. + FrameConstructionItemList fcItems; + AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems, + fcItems); + ConstructFramesFromItemList(aState, fcItems, comboboxFrame, childItems); comboboxFrame->SetInitialChildList(kPrincipalList, childItems); @@ -3172,7 +3189,7 @@ nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState, * But the select tag should really be fixed to use GFX scrollbars that can * be create with BuildScrollFrame. */ -nsresult +void nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState, nsContainerFrame* scrollFrame, nsContainerFrame* scrolledFrame, @@ -3218,7 +3235,6 @@ nsCSSFrameConstructor::InitializeSelectFrame(nsFrameConstructorState& aState, // Set the scrolled frame's initial child lists scrolledFrame->SetInitialChildList(kPrincipalList, childItems); - return NS_OK; } nsIFrame* @@ -3805,7 +3821,7 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt // AutoDisplayContentsAncestorPusher above.) TreeMatchContext::AutoAncestorPusher insertionPointPusher(aState.mTreeMatchContext); - if (adcp.IsEmpty() && parent && nsContentUtils::IsContentInsertionPoint(parent)) { + if (adcp.IsEmpty() && parent && parent->IsActiveChildrenElement()) { if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { insertionPointPusher.PushAncestorAndStyleScope(parent); } else { @@ -4094,70 +4110,6 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt } } -// after the node has been constructed and initialized create any -// anonymous content a node needs. -nsresult -nsCSSFrameConstructor::CreateAnonymousFrames(nsFrameConstructorState& aState, - nsIContent* aParent, - nsContainerFrame* aParentFrame, - PendingBinding* aPendingBinding, - nsFrameItems& aChildItems) -{ - AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> newAnonymousItems; - nsresult rv = GetAnonymousContent(aParent, aParentFrame, newAnonymousItems); - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t count = newAnonymousItems.Length(); - if (count == 0) { - return NS_OK; - } - - nsFrameConstructorState::PendingBindingAutoPusher pusher(aState, - aPendingBinding); - TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext); - if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { - ancestorPusher.PushAncestorAndStyleScope(aParent->AsElement()); - } else { - ancestorPusher.PushStyleScope(aParent->AsElement()); - } - - nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame); - NS_ASSERTION(creator, - "How can that happen if we have nodes to construct frames for?"); - - InsertionPoint insertion(aParentFrame, aParent); - for (uint32_t i=0; i < count; i++) { - nsIContent* content = newAnonymousItems[i].mContent; - NS_ASSERTION(content, "null anonymous content?"); - NS_ASSERTION(!newAnonymousItems[i].mStyleContext, "Unexpected style context"); - NS_ASSERTION(newAnonymousItems[i].mChildren.IsEmpty(), - "This method is not currently used with frames that implement " - "nsIAnonymousContentCreator::CreateAnonymousContent to " - "output a list where the items have their own children"); - - nsIFrame* newFrame = creator->CreateFrameFor(content); - if (newFrame) { - NS_ASSERTION(content->GetPrimaryFrame(), - "Content must have a primary frame now"); - newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT); - aChildItems.AddChild(newFrame); - } else { - FrameConstructionItemList items; - { - // Skip parent display based style-fixup during our - // AddFrameConstructionItems() call: - TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper - parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext); - - AddFrameConstructionItems(aState, content, true, insertion, items); - } - ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems); - } - } - - return NS_OK; -} - static void SetFlagsOnSubtree(nsIContent *aNode, uintptr_t aFlagsToSet) { @@ -4209,6 +4161,13 @@ ConnectAnonymousTreeDescendants(nsIContent* aParent, } } +void SetNativeAnonymousBitOnDescendants(nsIContent *aRoot) +{ + for (nsIContent* curr = aRoot; curr; curr = curr->GetNextNode(aRoot)) { + curr->SetFlags(NODE_IS_NATIVE_ANONYMOUS); + } +} + nsresult nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, nsIFrame* aParentFrame, @@ -4230,16 +4189,32 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, nsIContent* content = aContent[i].mContent; NS_ASSERTION(content, "null anonymous content?"); - // least-surprise CSS binding until we do the SVG specified - // cascading rules for <svg:use> - bug 265894 - if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) { + ConnectAnonymousTreeDescendants(content, aContent[i].mChildren); + + nsIAtom* parentFrameType = aParentFrame->GetType(); + if (parentFrameType == nsGkAtoms::svgUseFrame) { + // least-surprise CSS binding until we do the SVG specified + // cascading rules for <svg:use> - bug 265894 content->SetFlags(NODE_IS_ANONYMOUS_ROOT); } else { content->SetIsNativeAnonymousRoot(); + // Don't mark descendants of the custom content container + // as native anonymous. When canvas custom content is initially + // created and appended to the custom content container, in + // nsIDocument::InsertAnonymousContent, it is not considered native + // anonymous content. But if we end up reframing the root element, + // we will re-create the nsCanvasFrame, and we would end up in here, + // marking it as NAC. Existing uses of canvas custom content would + // break if it becomes NAC (since each element starts inheriting + // styles from its closest non-NAC ancestor, rather than from its + // parent). + if (!(parentFrameType == nsGkAtoms::canvasFrame && + content == static_cast<nsCanvasFrame*>(aParentFrame) + ->GetCustomContentContainer())) { + SetNativeAnonymousBitOnDescendants(content); + } } - ConnectAnonymousTreeDescendants(content, aContent[i].mChildren); - bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE); // If the parent is in a shadow tree, make sure we don't @@ -4264,11 +4239,9 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, } if (ServoStyleSet* styleSet = mPresShell->StyleSet()->GetAsServo()) { - // Eagerly compute styles for the anonymous content tree, but only do so - // if the content doesn't have an explicit style context (if it does, we - // don't need the normal computed values). + // Eagerly compute styles for the anonymous content tree. for (auto& info : aContent) { - if (!info.mStyleContext) { + if (info.mContent->IsElement()) { styleSet->StyleNewSubtree(info.mContent); } } @@ -4574,11 +4547,25 @@ nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState, // if there are any anonymous children for the scroll frame, create // frames for them. - // Pass a null pending binding: we don't care how constructors for any of - // this anonymous content order with anything else. It's never been - // consistent anyway. - CreateAnonymousFrames(aState, aContent, gfxScrollFrame, nullptr, - anonymousItems); + // + // We can't take the normal ProcessChildren path, because the NAC needs to + // be parented to the scrollframe, and everything else needs to be parented + // to the scrolledframe. + AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> scrollNAC; + DebugOnly<nsresult> rv = GetAnonymousContent(aContent, gfxScrollFrame, scrollNAC); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + if (scrollNAC.Length() > 0) { + TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext); + if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) { + ancestorPusher.PushAncestorAndStyleScope(aContent->AsElement()); + } else { + ancestorPusher.PushStyleScope(aContent->AsElement()); + } + + FrameConstructionItemList items; + AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items); + ConstructFramesFromItemList(aState, items, gfxScrollFrame, anonymousItems); + } aNewFrame = gfxScrollFrame; @@ -5033,22 +5020,46 @@ nsCSSFrameConstructor::ResolveStyleContext(const InsertionPoint& aInsertion, already_AddRefed<nsStyleContext> nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext, nsIContent* aContent, - nsFrameConstructorState* aState) + nsFrameConstructorState* aState, + Element* aOriginatingElementOrNull) { StyleSetHandle styleSet = mPresShell->StyleSet(); aContent->OwnerDoc()->FlushPendingLinkUpdates(); RefPtr<nsStyleContext> result; if (aContent->IsElement()) { - if (aState) { - result = styleSet->ResolveStyleFor(aContent->AsElement(), - aParentStyleContext, - aState->mTreeMatchContext); + auto pseudoType = aContent->AsElement()->GetPseudoElementType(); + if (pseudoType == CSSPseudoElementType::NotPseudo) { + MOZ_ASSERT(!aOriginatingElementOrNull); + if (aState) { + result = styleSet->ResolveStyleFor(aContent->AsElement(), + aParentStyleContext, + aState->mTreeMatchContext); + } else { + result = styleSet->ResolveStyleFor(aContent->AsElement(), + aParentStyleContext); + } } else { - result = styleSet->ResolveStyleFor(aContent->AsElement(), - aParentStyleContext); + MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree()); + if (!aOriginatingElementOrNull) { + // For pseudo-implementing NAC created by JS using the ChromeOnly + // document.createElement(..., { pseudo: ... }) API, we find the + // originating element by lookup the tree until we find a non-NAC + // ancestor. (These are the correct semantics for C++-generated pseudo- + // implementing NAC as well, but for those cases we already have a + // correct originating element passed in.) + MOZ_ASSERT(nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)); + aOriginatingElementOrNull = + nsContentUtils::GetClosestNonNativeAnonymousAncestor(aContent->AsElement()); + } + MOZ_ASSERT(aOriginatingElementOrNull); + result = styleSet->ResolvePseudoElementStyle(aOriginatingElementOrNull, + pseudoType, + aParentStyleContext, + aContent->AsElement()); } } else { + MOZ_ASSERT(!aOriginatingElementOrNull); NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), "shouldn't waste time creating style contexts for " "comments and processing instructions"); @@ -6065,9 +6076,10 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState static void AddGenConPseudoToFrame(nsIFrame* aOwnerFrame, nsIContent* aContent) { - NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aOwnerFrame), - "property should only be set on first continuation/ib-sibling"); + // FIXME(emilio): Remove this property, and use the frame of the generated + // content itself to tear the content down? It should be quite simpler. + aOwnerFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(aOwnerFrame); nsIFrame::ContentArray* value = aOwnerFrame->GetProperty(nsIFrame::GenConProperty()); if (!value) { @@ -6198,16 +6210,16 @@ IsRootBoxFrame(nsIFrame *aFrame) return (aFrame->GetType() == nsGkAtoms::rootFrame); } -nsresult +void nsCSSFrameConstructor::ReconstructDocElementHierarchy() { Element* rootElement = mDocument->GetRootElement(); if (!rootElement) { /* nothing to do */ - return NS_OK; + return; } - return RecreateFramesForContent(rootElement, false, REMOVE_FOR_RECONSTRUCTION, - nullptr); + RecreateFramesForContent(rootElement, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); } nsContainerFrame* @@ -6312,129 +6324,24 @@ nsCSSFrameConstructor::GetFloatContainingBlock(nsIFrame* aFrame) } /** - * This function will check whether aContainer has :after generated content. - * If so, appending to it should actually insert. The return value is the - * parent to use for newly-appended content. *aAfterFrame points to the :after - * frame before which appended content should go, if there is one. - */ -static nsContainerFrame* -AdjustAppendParentForAfterContent(nsFrameManager* aFrameManager, - nsIContent* aContainer, - nsContainerFrame* aParentFrame, - nsIContent* aChild, - nsIFrame** aAfterFrame) -{ - // If the parent frame has any pseudo-elements or aContainer is a - // display:contents node then we need to walk through the child - // frames to find the first one that is either a ::after frame for an - // ancestor of aChild or a frame that is for a node later in the - // document than aChild and return that in aAfterFrame. - if (aParentFrame->GetGenConPseudos() || - nsLayoutUtils::HasPseudoStyle(aContainer, aParentFrame->StyleContext(), - CSSPseudoElementType::after, - aParentFrame->PresContext()) || - aFrameManager->GetDisplayContentsStyleFor(aContainer)) { - nsIFrame* afterFrame = nullptr; - nsContainerFrame* parent = - static_cast<nsContainerFrame*>(aParentFrame->LastContinuation()); - bool done = false; - while (!done && parent) { - // Ensure that all normal flow children are on the principal child list. - parent->DrainSelfOverflowList(); - - nsIFrame* child = parent->GetChildList(nsIFrame::kPrincipalList).LastChild(); - if (child && child->IsPseudoFrame(aContainer) && - !child->IsGeneratedContentFrame()) { - // Drill down into non-generated pseudo frames of aContainer. - nsContainerFrame* childAsContainer = do_QueryFrame(child); - if (childAsContainer) { - parent = nsLayoutUtils::LastContinuationWithChild(childAsContainer); - continue; - } - } - - for (; child; child = child->GetPrevSibling()) { - nsIContent* c = child->GetContent(); - if (child->IsGeneratedContentFrame()) { - nsIContent* p = c->GetParent(); - if (c->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentafter) { - if (!nsContentUtils::ContentIsDescendantOf(aChild, p) && - p != aContainer && - nsContentUtils::PositionIsBefore(p, aChild)) { - // ::after generated content for content earlier in the doc and not - // for an ancestor. "p != aContainer" may seem redundant but it - // checks if the ::after belongs to the XBL insertion point we're - // inserting aChild into (in which case ContentIsDescendantOf is - // false even though p == aContainer). - // See layout/reftests/bugs/482592-1a.xhtml for an example of that. - done = true; - break; - } - } else if (nsContentUtils::PositionIsBefore(p, aChild)) { - // Non-::after generated content for content earlier in the doc. - done = true; - break; - } - } else if (nsContentUtils::PositionIsBefore(c, aChild)) { - // Content is before aChild. - done = true; - break; - } - afterFrame = child; - } - - parent = static_cast<nsContainerFrame*>(parent->GetPrevContinuation()); - } - if (afterFrame) { - *aAfterFrame = afterFrame; - return afterFrame->GetParent(); - } - } - - *aAfterFrame = nullptr; - - if (IsFramePartOfIBSplit(aParentFrame)) { - // We might be in a situation where the last part of the {ib} split was - // empty. Since we have no ::after pseudo-element, we do in fact want to be - // appending to that last part, so advance to it if needed. Note that here - // aParentFrame is the result of a GetLastIBSplitSibling call, so must be - // either the last or next to last ib-split sibling. - nsContainerFrame* trailingInline = GetIBSplitSibling(aParentFrame); - if (trailingInline) { - aParentFrame = trailingInline; - } - - // Always make sure to look at the last continuation of the frame - // for the {ib} case, even if that continuation is empty. We - // don't do this for the non-ib-split-frame case, since in the - // other cases appending to the last nonempty continuation is fine - // and in fact not doing that can confuse code that doesn't know - // to pull kids from continuations other than its next one. - aParentFrame = - static_cast<nsContainerFrame*>(aParentFrame->LastContinuation()); - } - - return aParentFrame; -} - -/** * This function will get the previous sibling to use for an append operation. - * it takes a parent frame (must not be null) and its :after frame (may be - * null). + * + * It takes a parent frame (must not be null) and the next insertion sibling, if + * the parent content is display: contents or has ::after content (may be null). */ static nsIFrame* -FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame) +FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aNextSibling) { - if (aAfterFrame) { - NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent"); - NS_ASSERTION(aAfterFrame->GetPrevSibling() || - aParentFrame->PrincipalChildList().FirstChild() == aAfterFrame, - ":after frame must be on the principal child list here"); - return aAfterFrame->GetPrevSibling(); - } - aParentFrame->DrainSelfOverflowList(); + if (aNextSibling) { + MOZ_ASSERT(aNextSibling->GetParent() == aParentFrame, "Wrong parent"); + MOZ_ASSERT(aNextSibling->GetPrevSibling() || + aParentFrame->PrincipalChildList().FirstChild() == aNextSibling, + "next sibling must be on the principal child list here"); + return aNextSibling->GetPrevSibling(); + } + return aParentFrame->GetChildList(kPrincipalList).LastChild(); } @@ -6457,7 +6364,7 @@ GetInsertNextSibling(nsIFrame* aParentFrame, nsIFrame* aPrevSibling) * appending flowed frames to a parent's principal child list. It handles the * case where the parent is the trailing inline of an {ib} split. */ -nsresult +void nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aState, nsContainerFrame* aParentFrame, nsFrameItems& aFrameList, @@ -6542,13 +6449,11 @@ nsCSSFrameConstructor::AppendFramesToParent(nsFrameConstructorState& aStat return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings, aParentFrame, true); } - - return NS_OK; + return; } // Insert the frames after our aPrevSibling InsertFrames(aParentFrame, kPrincipalList, aPrevSibling, aFrameList); - return NS_OK; } #define UNSET_DISPLAY static_cast<StyleDisplay>(255) @@ -6650,111 +6555,147 @@ nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling, return true; } +// FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a +// bit (no need to pass aTargetContent or aTargetContentDisplay, and the +// adjust() calls can be responsibility of the caller). +template<nsCSSFrameConstructor::SiblingDirection aDirection> nsIFrame* -nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent, - nsIContent* aTargetContent, - StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame, - bool aPrevSibling) -{ - nsIFrame* sibling = aContent->GetPrimaryFrame(); - if (!sibling && GetDisplayContentsStyleFor(aContent)) { - // A display:contents node - check if it has a ::before / ::after frame... - sibling = aPrevSibling ? - nsLayoutUtils::GetAfterFrameForContent(aParentFrame, aContent) : - nsLayoutUtils::GetBeforeFrameForContent(aParentFrame, aContent); - if (!sibling) { - // ... then recurse into children ... - const bool forward = !aPrevSibling; - FlattenedChildIterator iter(aContent, forward); - sibling = aPrevSibling ? - FindPreviousSibling(iter, aTargetContent, aTargetContentDisplay, aParentFrame) : - FindNextSibling(iter, aTargetContent, aTargetContentDisplay, aParentFrame); - } - if (!sibling) { - // ... then ::after / ::before on the opposite end. - sibling = aPrevSibling ? - nsLayoutUtils::GetBeforeFrameForContent(aParentFrame, aContent) : - nsLayoutUtils::GetAfterFrameForContent(aParentFrame, aContent); - } - if (!sibling) { - return nullptr; +nsCSSFrameConstructor::FindSiblingInternal( + FlattenedChildIterator aIter, + nsIContent* aTargetContent, + StyleDisplay& aTargetContentDisplay) +{ + auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* { + return AdjustSiblingFrame( + aPotentialSiblingFrame, aTargetContent, aTargetContentDisplay, + aDirection); + }; + + auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* { + return aDirection == SiblingDirection::Forward + ? aIter.GetNextChild() : aIter.GetPreviousChild(); + }; + + auto getNearPseudo = [](const nsIContent* aContent) -> nsIFrame* { + return aDirection == SiblingDirection::Forward + ? nsLayoutUtils::GetBeforeFrame(aContent) + : nsLayoutUtils::GetAfterFrame(aContent); + }; + + auto getFarPseudo = [](const nsIContent* aContent) -> nsIFrame* { + return aDirection == SiblingDirection::Forward + ? nsLayoutUtils::GetAfterFrame(aContent) + : nsLayoutUtils::GetBeforeFrame(aContent); + }; + + while (nsIContent* sibling = nextDomSibling(aIter)) { + if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) { + // XXX the GetContent() == sibling check is needed due to bug 135040. + // Remove it once that's fixed. + if (primaryFrame->GetContent() == sibling) { + if (nsIFrame* frame = adjust(primaryFrame)) { + return frame; + } + } + } + + if (GetDisplayContentsStyleFor(sibling)) { + if (nsIFrame* frame = adjust(getNearPseudo(sibling))) { + return frame; + } + + const bool startFromBeginning = aDirection == SiblingDirection::Forward; + FlattenedChildIterator iter(sibling, startFromBeginning); + nsIFrame* sibling = FindSiblingInternal<aDirection>( + iter, aTargetContent, aTargetContentDisplay); + if (sibling) { + return sibling; + } } - } else if (!sibling || sibling->GetContent() != aContent) { - // XXX the GetContent() != aContent check is needed due to bug 135040. - // Remove it once that's fixed. - return nullptr; } - // If the frame is out-of-flow, GetPrimaryFrame() will have returned the - // out-of-flow frame; we want the placeholder. - if (sibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { - nsIFrame* placeholderFrame = GetPlaceholderFrameFor(sibling); - NS_ASSERTION(placeholderFrame, "no placeholder for out-of-flow frame"); - sibling = placeholderFrame; + return adjust(getFarPseudo(aIter.Parent())); +} + +nsIFrame* +nsCSSFrameConstructor::AdjustSiblingFrame( + nsIFrame* aSibling, + nsIContent* aTargetContent, + mozilla::StyleDisplay& aTargetContentDisplay, + SiblingDirection aDirection) +{ + if (!aSibling) { + return nullptr; } - // The frame we have now should never be a continuation - NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?"); + if (aSibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { + aSibling = aSibling->GetPlaceholderFrame(); + MOZ_ASSERT(aSibling); + } - if (aPrevSibling) { - // The frame may be a ib-split frame (a split inline frame that - // contains a block). Get the last part of that split. - if (IsFramePartOfIBSplit(sibling)) { - sibling = GetLastIBSplitSibling(sibling, true); + MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?"); + if (aDirection == SiblingDirection::Backward) { + // The frame may be a ib-split frame (a split inline frame that contains a + // block). Get the last part of that split. + if (IsFramePartOfIBSplit(aSibling)) { + aSibling = GetLastIBSplitSibling(aSibling, true); } // The frame may have a continuation. If so, we want the last // non-overflow-container continuation as our previous sibling. - sibling = sibling->GetTailContinuation(); + aSibling = aSibling->GetTailContinuation(); } - if (aTargetContent && - !IsValidSibling(sibling, aTargetContent, aTargetContentDisplay)) { - sibling = nullptr; + if (!IsValidSibling(aSibling, aTargetContent, aTargetContentDisplay)) { + return nullptr; } - return sibling; + return aSibling; } nsIFrame* -nsCSSFrameConstructor::FindPreviousSibling(FlattenedChildIterator aIter, - nsIContent* aTargetContent, - StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame) +nsCSSFrameConstructor::FindPreviousSibling(const FlattenedChildIterator& aIter, + StyleDisplay& aTargetContentDisplay) { - // Note: not all content objects are associated with a frame (e.g., if it's - // `display: none') so keep looking until we find a previous frame. - while (nsIContent* sibling = aIter.GetPreviousChild()) { - MOZ_ASSERT(sibling != aTargetContent); - nsIFrame* prevSibling = - FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay, - aParentFrame, true); - if (prevSibling) { - // Found a previous sibling, we're done! - return prevSibling; - } - } + return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay); +} - return nullptr; +nsIFrame* +nsCSSFrameConstructor::FindNextSibling(const FlattenedChildIterator& aIter, + StyleDisplay& aTargetContentDisplay) +{ + return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay); } +template<nsCSSFrameConstructor::SiblingDirection aDirection> nsIFrame* -nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter, - nsIContent* aTargetContent, - StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame) +nsCSSFrameConstructor::FindSibling(const FlattenedChildIterator& aIter, + StyleDisplay& aTargetContentDisplay) { - while (nsIContent* sibling = aIter.GetNextChild()) { - MOZ_ASSERT(sibling != aTargetContent); - nsIFrame* nextSibling = - FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay, - aParentFrame, false); + nsIContent* targetContent = aIter.Get(); + nsIFrame* sibling = + FindSiblingInternal<aDirection>(aIter, targetContent, aTargetContentDisplay); + if (sibling) { + return sibling; + } + + // Our siblings (if any) do not have a frame to guide us. The frame for the + // target content should be inserted whereever a frame for the container would + // be inserted. This is needed when inserting into display: contents nodes. + const nsIContent* current = aIter.Parent(); + while (GetDisplayContentsStyleFor(current)) { + const nsIContent* parent = current->GetFlattenedTreeParent(); + MOZ_ASSERT(parent, "No display: contents on the root"); - if (nextSibling) { - // We found a next sibling, we're done! - return nextSibling; + FlattenedChildIterator iter(parent); + iter.Seek(current); + sibling = FindSiblingInternal<aDirection>( + iter, targetContent, aTargetContentDisplay); + if (sibling) { + return sibling; } + + current = parent; } return nullptr; @@ -6820,8 +6761,7 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion, // Note that FindPreviousSibling is passed the iterator by value, so that // the later usage of the iterator starts from the same place. StyleDisplay childDisplay = UNSET_DISPLAY; - nsIFrame* prevSibling = - FindPreviousSibling(iter, iter.Get(), childDisplay, aInsertion->mParentFrame); + nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay); // Now, find the geometric parent so that we can handle // continuations properly. Use the prev sibling if we have it; @@ -6834,48 +6774,20 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion, iter.Seek(aEndSkipChild); iter.GetPreviousChild(); } - nsIFrame* nextSibling = - FindNextSibling(iter, iter.Get(), childDisplay, aInsertion->mParentFrame); - if (GetDisplayContentsStyleFor(aInsertion->mContainer)) { - if (!nextSibling) { - // Our siblings (if any) does not have a frame to guide us. - // The frame for aChild should be inserted whereever a frame for - // the container would be inserted. This is needed when inserting - // into nested display:contents nodes. - nsIContent* child = aInsertion->mContainer; - nsIContent* parent = child->GetParent(); - aInsertion->mParentFrame = - ::GetAdjustedParentFrame(aInsertion->mParentFrame, - aInsertion->mParentFrame->GetType(), - parent); - InsertionPoint fakeInsertion(aInsertion->mParentFrame, parent); - nsIFrame* result = GetInsertionPrevSibling(&fakeInsertion, child, aIsAppend, - aIsRangeInsertSafe, nullptr, nullptr); - MOZ_ASSERT(aInsertion->mParentFrame->GetContent() == - fakeInsertion.mParentFrame->GetContent()); - // fakeInsertion.mParentFrame may now be a continuation of the frame - // we started with in the ctor above. - aInsertion->mParentFrame = fakeInsertion.mParentFrame; - return result; - } - - prevSibling = nextSibling->GetPrevSibling(); - } - - if (nextSibling) { + if (nsIFrame* nextSibling = FindNextSibling(iter, childDisplay)) { aInsertion->mParentFrame = nextSibling->GetParent()->GetContentInsertionFrame(); } else { // No previous or next sibling, so treat this like an appended frame. *aIsAppend = true; if (IsFramePartOfIBSplit(aInsertion->mParentFrame)) { // Since we're appending, we'll walk to the last anonymous frame - // that was created for the broken inline frame. But don't walk - // to the trailing inline if it's empty; stop at the block. + // that was created for the broken inline frame. We can walk to the + // trailing inline, since we know this is a real append, and not an + // insert (that would've been handled by `FindNextSibling`). aInsertion->mParentFrame = - GetLastIBSplitSibling(aInsertion->mParentFrame, false); + GetLastIBSplitSibling(aInsertion->mParentFrame, true); } - // Get continuation that parents the last child. This MUST be done - // before the AdjustAppendParentForAfterContent call. + // Get continuation that parents the last child. aInsertion->mParentFrame = nsLayoutUtils::LastContinuationWithChild(aInsertion->mParentFrame); // Deal with fieldsets @@ -6883,12 +6795,7 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion, ::GetAdjustedParentFrame(aInsertion->mParentFrame, aInsertion->mParentFrame->GetType(), aChild); - nsIFrame* appendAfterFrame; - aInsertion->mParentFrame = - ::AdjustAppendParentForAfterContent(this, aInsertion->mContainer, - aInsertion->mParentFrame, - aChild, &appendAfterFrame); - prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, appendAfterFrame); + prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, nullptr); } } @@ -7004,7 +6911,8 @@ nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aParentContent, } NS_ASSERTION(!aContent->GetPrimaryFrame(), "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE"); - ContentInserted(aParentContent, aContent, nullptr, false); + const bool allowLazyConstruction = true; + ContentInserted(aParentContent, aContent, nullptr, allowLazyConstruction); } // For inserts aChild should be valid, for appends it should be null. @@ -7014,11 +6922,8 @@ nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation, nsIContent* aContainer, nsIContent* aChild) { - // XXXmats no lazy frames for display:contents direct descendants yet - // (Mozilla bug 979782). if (mPresShell->GetPresContext()->IsChrome() || !aContainer || - aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXULElement() || - GetDisplayContentsStyleFor(aContainer)) { + aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXULElement()) { return false; } @@ -7054,8 +6959,7 @@ nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation, // hit a node with a leaf frame. // // Also, it's fine if one of the nodes without primary frame is a display: - // contents node except if it's the direct ancestor of the children we're - // recreating frames for. + // contents node. bool noPrimaryFrame = false; bool needsFrameBitSet = false; #endif @@ -7287,8 +7191,9 @@ nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame, cur = cur->GetNextSibling()) { if (IsSpecialFramesetChild(cur)) { // Just reframe the parent, since framesets are weird like that. - RecreateFramesForContent(aParentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aParentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); return true; } } @@ -7296,7 +7201,7 @@ nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame, return false; } -nsresult +void nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent, bool aAllowLazyConstruction) @@ -7339,8 +7244,8 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, if (tag == nsGkAtoms::treechildren || tag == nsGkAtoms::treeitem || tag == nsGkAtoms::treerow) - return NS_OK; + return; } #endif // MOZ_XUL @@ -7353,21 +7258,21 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, //XXXsmaug This is super unefficient! nsIContent* bindingParent = aContainer->GetBindingParent(); LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(bindingParent, false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(bindingParent, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // See comment in ContentRangeInserted for why this is necessary. if (!GetContentInsertionFrameFor(aContainer) && !aContainer->IsActiveChildrenElement()) { - return NS_OK; + return; } if (aAllowLazyConstruction && MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) { - return NS_OK; + return; } LAYOUT_PHASE_TEMP_EXIT(); @@ -7377,13 +7282,13 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, nsContainerFrame*& parentFrame = insertion.mParentFrame; LAYOUT_PHASE_TEMP_REENTER(); if (!parentFrame) { - return NS_OK; + return; } LAYOUT_PHASE_TEMP_EXIT(); if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) { LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -7391,21 +7296,22 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, // Nothing to do here; we shouldn't be constructing kids of leaves // Clear lazy bits so we don't try to construct again. ClearLazyBits(aFirstNewContent, nullptr); - return NS_OK; + return; } if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) { LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(parentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // If the frame we are manipulating is a ib-split frame (that is, one // that's been created as a result of a block-in-inline situation) then we // need to append to the last ib-split sibling, not to the frame itself. - bool parentIBSplit = IsFramePartOfIBSplit(parentFrame); + const bool parentIBSplit = IsFramePartOfIBSplit(parentFrame); if (parentIBSplit) { #ifdef DEBUG if (gNoisyContentUpdates) { @@ -7431,38 +7337,79 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, parentFrame->GetType() != nsGkAtoms::detailsFrame, "Parent frame should not be fieldset or details!"); - // Deal with possible :after generated content on the parent - nsIFrame* parentAfterFrame; - parentFrame = - ::AdjustAppendParentForAfterContent(this, insertion.mContainer, parentFrame, - aFirstNewContent, &parentAfterFrame); + // Deal with possible :after generated content on the parent, or display: + // contents. + nsIFrame* nextSibling = nullptr; + if (GetDisplayContentsStyleFor(insertion.mContainer) || + nsLayoutUtils::GetAfterFrame(insertion.mContainer)) { + FlattenedChildIterator iter(insertion.mContainer); + iter.Seek(insertion.mContainer->GetLastChild()); + StyleDisplay unused = UNSET_DISPLAY; + nextSibling = FindNextSibling(iter, unused); + } - // Create some new frames - nsFrameConstructorState state(mPresShell, - GetAbsoluteContainingBlock(parentFrame, FIXED_POS), - GetAbsoluteContainingBlock(parentFrame, ABS_POS), - GetFloatContainingBlock(parentFrame)); - state.mTreeMatchContext.InitAncestors(aContainer->AsElement()); + if (nextSibling) { + parentFrame = nextSibling->GetParent()->GetContentInsertionFrame(); + } else if (parentIBSplit) { + // We might be in a situation where the last part of the {ib} split was + // empty. Since we have no ::after pseudo-element, we do in fact want to be + // appending to that last part, so advance to it if needed. Note that here + // aParentFrame is the result of a GetLastIBSplitSibling call, so must be + // either the last or next to last ib-split sibling. + if (nsContainerFrame* trailingInline = GetIBSplitSibling(parentFrame)) { + parentFrame = trailingInline; + } - // See if the containing block has :first-letter style applied. - bool haveFirstLetterStyle = false, haveFirstLineStyle = false; - nsContainerFrame* containingBlock = state.mFloatedItems.containingBlock; - if (containingBlock) { - haveFirstLetterStyle = HasFirstLetterStyle(containingBlock); - haveFirstLineStyle = - ShouldHaveFirstLineStyle(containingBlock->GetContent(), - containingBlock->StyleContext()); + // Always make sure to look at the last continuation of the frame + // for the {ib} case, even if that continuation is empty. We + // don't do this for the non-ib-split-frame case, since in the + // other cases appending to the last nonempty continuation is fine + // and in fact not doing that can confuse code that doesn't know + // to pull kids from continuations other than its next one. + parentFrame = + static_cast<nsContainerFrame*>(parentFrame->LastContinuation()); } + nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame); + + // See if the containing block has :first-letter style applied. + const bool haveFirstLetterStyle = + containingBlock && HasFirstLetterStyle(containingBlock); + + const bool haveFirstLineStyle = + containingBlock && + ShouldHaveFirstLineStyle(containingBlock->GetContent(), + containingBlock->StyleContext()); + if (haveFirstLetterStyle) { + nsWeakFrame wf(nextSibling); // Before we get going, remove the current letter frames - RemoveLetterFrames(state.mPresShell, containingBlock); + RemoveLetterFrames(mPresShell, containingBlock); + + // Reget nextSibling, since we may have killed it. + if (nextSibling && !wf) { + FlattenedChildIterator iter(insertion.mContainer); + iter.Seek(insertion.mContainer->GetLastChild()); + StyleDisplay unused = UNSET_DISPLAY; + nextSibling = FindNextSibling(iter, unused); + if (nextSibling) { + parentFrame = nextSibling->GetParent()->GetContentInsertionFrame(); + containingBlock = GetFloatContainingBlock(parentFrame); + } + } } + // Create some new frames + nsFrameConstructorState state(mPresShell, + GetAbsoluteContainingBlock(parentFrame, FIXED_POS), + GetAbsoluteContainingBlock(parentFrame, ABS_POS), + containingBlock); + state.mTreeMatchContext.InitAncestors(aContainer->AsElement()); + nsIAtom* frameType = parentFrame->GetType(); FlattenedChildIterator iter(aContainer); - bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild()); + const bool haveNoXBLChildren = !iter.XBLInvolved() || !iter.GetNextChild(); FrameConstructionItemList items; if (aFirstNewContent->GetPreviousSibling() && GetParentType(frameType) == eTypeBlock && @@ -7487,7 +7434,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, AddFrameConstructionItems(state, child, false, insertion, items); } - nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame); + nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, nextSibling); // Perform special check for diddling around with the frames in // a ib-split inline frame. @@ -7497,7 +7444,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, if (WipeContainingBlock(state, containingBlock, parentFrame, items, true, prevSibling)) { LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -7510,8 +7457,11 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, !prevSibling->IsInlineOutside() || prevSibling->GetType() == nsGkAtoms::brFrame); // :after content can't be <br> so no need to check it - items.SetLineBoundaryAtEnd(!parentAfterFrame || - !parentAfterFrame->IsInlineOutside()); + // + // FIXME(emilio): A display: contents sibling could! Write a test-case and + // fix. + items.SetLineBoundaryAtEnd( + !nextSibling || !nextSibling->IsInlineOutside()); } // To suppress whitespace-only text frames, we have to verify that // our container's DOM child list matches its flattened tree child list. @@ -7584,7 +7534,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer, } #endif - return NS_OK; + return; } #ifdef MOZ_XUL @@ -7627,17 +7577,17 @@ bool NotifyListBoxBody(nsPresContext* aPresContext, } #endif // MOZ_XUL -nsresult +void nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer, nsIContent* aChild, nsILayoutHistoryState* aFrameState, bool aAllowLazyConstruction) { - return ContentRangeInserted(aContainer, - aChild, - aChild->GetNextSibling(), - aFrameState, - aAllowLazyConstruction); + ContentRangeInserted(aContainer, + aChild, + aChild->GetNextSibling(), + aFrameState, + aAllowLazyConstruction); } // ContentRangeInserted handles creating frames for a range of nodes that @@ -7658,7 +7608,7 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer, // in the caption list, while skipping any nodes in the range being inserted // (because when we treat the caption frames the other nodes have had their // frames constructed but not yet inserted into the frame tree). -nsresult +void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, nsIContent* aStartChild, nsIContent* aEndChild, @@ -7715,7 +7665,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // The insert case in NotifyListBoxBody // doesn't use "old next sibling". aStartChild, nullptr, nullptr, CONTENT_INSERTED)) { - return NS_OK; + return; } } else { // We don't handle a range insert to a listbox parent, issue single @@ -7724,7 +7674,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, aAllowLazyConstruction); LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } } #endif // MOZ_XUL @@ -7739,7 +7689,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, if (aStartChild != docElement) { // Not the root element; just bail out - return NS_OK; + return; } NS_PRECONDITION(nullptr == mRootElementFrame, @@ -7776,7 +7726,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, } #endif - return NS_OK; + return; } if (aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) && @@ -7789,10 +7739,10 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, //XXXsmaug This is super unefficient! nsIContent* bindingParent = aContainer->GetBindingParent(); LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(bindingParent, false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(bindingParent, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // Put 'parentFrame' inside a scope so we don't confuse it with @@ -7803,7 +7753,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // a parent. While its uncommon to change the structure of the default content itself, a label, // for example, can be reframed by having its value attribute set or removed. if (!parentFrame && !aContainer->IsActiveChildrenElement()) { - return NS_OK; + return; } // Otherwise, we've got parent content. Find its frame. @@ -7812,7 +7762,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, if (aAllowLazyConstruction && MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) { - return NS_OK; + return; } } @@ -7832,7 +7782,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, } if (!insertion.mParentFrame) { - return NS_OK; + return; } bool isAppend, isRangeInsertSafe; @@ -7846,16 +7796,14 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, aAllowLazyConstruction); LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } - nsIContent* container = insertion.mParentFrame->GetContent(); - nsIAtom* frameType = insertion.mParentFrame->GetType(); LAYOUT_PHASE_TEMP_EXIT(); if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild, aEndChild)) { LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -7872,10 +7820,11 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // and if so, proceed. But we'd have to extend nsFieldSetFrame // to locate this legend in the inserted frames and extract it. LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(insertion.mParentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(insertion.mParentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // We should only get here with details when doing a single insertion because @@ -7887,26 +7836,27 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // expensive to recreate the entire details frame, but it's the simplest way // to handle the insertion. LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = - RecreateFramesForContent(insertion.mParentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(insertion.mParentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // Don't construct kids of leaves if (insertion.mParentFrame->IsLeaf()) { // Clear lazy bits so we don't try to construct again. ClearLazyBits(aStartChild, aEndChild); - return NS_OK; + return; } if (insertion.mParentFrame->IsFrameOfType(nsIFrame::eMathML)) { LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(insertion.mParentFrame->GetContent(), false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(insertion.mParentFrame->GetContent(), + InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } nsFrameConstructorState state(mPresShell, @@ -7956,7 +7906,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, // the placeholder frame. if (insertion.mParentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { nsPlaceholderFrame* placeholderFrame = - GetPlaceholderFrameFor(insertion.mParentFrame); + insertion.mParentFrame->GetPlaceholderFrame(); NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?"); insertion.mParentFrame = placeholderFrame->GetParent(); } else { @@ -7983,30 +7933,13 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, IssueSingleInsertNofications(aContainer, aStartChild, aEndChild, aAllowLazyConstruction); LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } - container = insertion.mParentFrame->GetContent(); frameType = insertion.mParentFrame->GetType(); } } - if (!prevSibling) { - // We're inserting the new frames as the first child. See if the - // parent has a :before pseudo-element - nsIFrame* firstChild = insertion.mParentFrame->PrincipalChildList().FirstChild(); - - if (firstChild && - nsLayoutUtils::IsGeneratedContentFor(container, firstChild, - nsCSSPseudoElements::before)) { - // Insert the new frames after the last continuation of the :before - prevSibling = firstChild->GetTailContinuation(); - insertion.mParentFrame = prevSibling->GetParent()->GetContentInsertionFrame(); - // Don't change isAppend here; we'll can call AppendFrames as needed, and - // the change to our prevSibling doesn't affect that. - } - } - FrameConstructionItemList items; ParentType parentType = GetParentType(frameType); FlattenedChildIterator iter(aContainer); @@ -8051,7 +7984,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items, isAppend, prevSibling)) { LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -8075,65 +8008,11 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, } } - // If the parent of our current prevSibling is different from the frame we'll - // actually use as the parent, then the calculated insertion point is now - // invalid and as it is unknown where to insert correctly we append instead - // (bug 341858). - // This can affect our prevSibling and isAppend, but should not have any - // effect on the WipeContainingBlock above, since this should only happen - // when neither parent is a ib-split frame and should not affect whitespace - // handling inside table-related frames (and in fact, can only happen when - // one of the parents is a table wrapper and one is an inner table or when the - // parent is a fieldset or fieldset content frame). So it won't affect the - // {ib} or XUL box cases in WipeContainingBlock(), and the table pseudo - // handling will only be affected by us maybe thinking we're not inserting - // at the beginning, whereas we really are. That would have made us reframe - // unnecessarily, but that's ok. - // XXXbz we should push our frame construction item code up higher, so we - // know what our items are by the time we start figuring out previous - // siblings - if (prevSibling && frameItems.NotEmpty() && - frameItems.FirstChild()->GetParent() != prevSibling->GetParent()) { -#ifdef DEBUG - nsIFrame* frame1 = frameItems.FirstChild()->GetParent(); - nsIFrame* frame2 = prevSibling->GetParent(); - NS_ASSERTION(!IsFramePartOfIBSplit(frame1) && - !IsFramePartOfIBSplit(frame2), - "Neither should be ib-split"); - NS_ASSERTION((frame1->GetType() == nsGkAtoms::tableFrame && - frame2->GetType() == nsGkAtoms::tableWrapperFrame) || - (frame1->GetType() == nsGkAtoms::tableWrapperFrame && - frame2->GetType() == nsGkAtoms::tableFrame) || - frame1->GetType() == nsGkAtoms::fieldSetFrame || - (frame1->GetParent() && - frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame), - "Unexpected frame types"); -#endif - isAppend = true; - nsIFrame* appendAfterFrame; - insertion.mParentFrame = - ::AdjustAppendParentForAfterContent(this, container, - frameItems.FirstChild()->GetParent(), - aStartChild, &appendAfterFrame); - prevSibling = ::FindAppendPrevSibling(insertion.mParentFrame, appendAfterFrame); - } - - if (haveFirstLineStyle && insertion.mParentFrame == containingBlock) { + if (haveFirstLineStyle && insertion.mParentFrame == containingBlock && isAppend) { // It's possible that the new frame goes into a first-line // frame. Look at it and see... - if (isAppend) { - // Use append logic when appending - AppendFirstLineFrames(state, containingBlock->GetContent(), - containingBlock, frameItems); - } - else { - // Use more complicated insert logic when inserting - // XXXbz this method is a no-op, so it's easy for the args being passed - // here to make no sense without anyone noticing... If it ever stops - // being a no-op, vet them carefully! - InsertFirstLineFrames(state, container, containingBlock, &insertion.mParentFrame, - prevSibling, frameItems); - } + AppendFirstLineFrames(state, containingBlock->GetContent(), + containingBlock, frameItems); } // We might have captions; put them into the caption list of the @@ -8216,32 +8095,35 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, #endif #ifdef ACCESSIBILITY - nsAccessibilityService* accService = nsIPresShell::AccService(); - if (accService) { + if (nsAccessibilityService* accService = nsIPresShell::AccService()) { accService->ContentRangeInserted(mPresShell, aContainer, aStartChild, aEndChild); } #endif - return NS_OK; + return; } -nsresult -nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, - nsIContent* aChild, - nsIContent* aOldNextSibling, - RemoveFlags aFlags, - bool* aDidReconstruct, - nsIContent** aDestroyedFramesFor) -{ +void +nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, + nsIContent* aChild, + nsIContent* aOldNextSibling, + RemoveFlags aFlags, + bool* aDidReconstruct) +{ + MOZ_ASSERT(aChild); + MOZ_ASSERT(aDidReconstruct); AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); NS_PRECONDITION(mUpdateCount != 0, "Should be in an update while destroying frames"); *aDidReconstruct = false; - if (aDestroyedFramesFor) { - *aDestroyedFramesFor = aChild; - } + + // Only recreate sync if we're already in frame construction, that is, + // recreate async for XBL DOM changes, and normal content removals. + const InsertionKind insertionKind = (aFlags == REMOVE_FOR_RECONSTRUCTION) + ? InsertionKind::Sync + : InsertionKind::Async; nsPresContext* presContext = mPresShell->GetPresContext(); MOZ_ASSERT(presContext, "Our presShell should have a valid presContext"); @@ -8274,7 +8156,6 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, } #endif - nsresult rv = NS_OK; nsIFrame* childFrame = aChild->GetPrimaryFrame(); if (!childFrame || childFrame->GetContent() != aChild) { // XXXbz the GetContent() != aChild check is needed due to bug 135040. @@ -8284,37 +8165,33 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, MOZ_ASSERT(!childFrame || !GetDisplayContentsStyleFor(aChild), "display:contents nodes shouldn't have a frame"); if (!childFrame && GetDisplayContentsStyleFor(aChild)) { - nsIFrame* ancestorFrame = nullptr; - nsIContent* ancestor = aContainer; - for (; ancestor; ancestor = ancestor->GetParent()) { - ancestorFrame = ancestor->GetPrimaryFrame(); - if (ancestorFrame) { - break; - } + nsIContent* ancestor = aChild->GetFlattenedTreeParent(); + MOZ_ASSERT(ancestor, "display: contents on the root?"); + while (!ancestor->GetPrimaryFrame()) { + ancestor = ancestor->GetFlattenedTreeParent(); + MOZ_ASSERT(ancestor, "we can't have a display: contents subtree root!"); } - if (ancestorFrame) { - nsIFrame* contentInsertion = ancestorFrame->GetContentInsertionFrame(); - if (ancestorFrame->GetGenConPseudos() || - (contentInsertion && contentInsertion->GetGenConPseudos())) { - *aDidReconstruct = true; - LAYOUT_PHASE_TEMP_EXIT(); - // XXXmats Can we recreate frames only for the ::after/::before content? - // XXX Perhaps even only those that belong to the aChild sub-tree? - RecreateFramesForContent(ancestor, false, aFlags, aDestroyedFramesFor); - LAYOUT_PHASE_TEMP_REENTER(); - return NS_OK; - } + + nsIFrame* ancestorFrame = ancestor->GetPrimaryFrame(); + if (ancestorFrame->GetProperty(nsIFrame::GenConProperty())) { + *aDidReconstruct = true; + + // XXXmats Can we recreate frames only for the ::after/::before content? + // XXX Perhaps even only those that belong to the aChild sub-tree? + LAYOUT_PHASE_TEMP_EXIT(); + RecreateFramesForContent(ancestor, insertionKind, aFlags); + LAYOUT_PHASE_TEMP_REENTER(); + return; } FlattenedChildIterator iter(aChild); for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) { if (c->GetPrimaryFrame() || GetDisplayContentsStyleFor(c)) { LAYOUT_PHASE_TEMP_EXIT(); - rv = ContentRemoved(aChild, c, nullptr, aFlags, aDidReconstruct, aDestroyedFramesFor); + ContentRemoved(aChild, c, nullptr, aFlags, aDidReconstruct); LAYOUT_PHASE_TEMP_REENTER(); - NS_ENSURE_SUCCESS(rv, rv); if (aFlags != REMOVE_DESTROY_FRAMES && *aDidReconstruct) { - return rv; + return; } } } @@ -8327,7 +8204,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, if (aFlags == REMOVE_DESTROY_FRAMES) { CaptureStateForFramesOf(aChild, mTempFrameTreeState); } - return NS_OK; + return; } #endif // MOZ_XUL @@ -8365,10 +8242,9 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, nsIContent* bindingParent = aContainer->GetBindingParent(); *aDidReconstruct = true; LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(bindingParent, false, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(bindingParent, insertionKind, aFlags); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } if (aFlags == REMOVE_DESTROY_FRAMES) { @@ -8380,15 +8256,11 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // See whether we need to remove more than just childFrame LAYOUT_PHASE_TEMP_EXIT(); - nsIContent* container; - if (MaybeRecreateContainerForFrameRemoval(childFrame, aFlags, &rv, &container)) { - LAYOUT_PHASE_TEMP_REENTER(); - MOZ_ASSERT(container); + if (MaybeRecreateContainerForFrameRemoval( + childFrame, insertionKind, aFlags)) { *aDidReconstruct = true; - if (aDestroyedFramesFor) { - *aDestroyedFramesFor = container; - } - return rv; + LAYOUT_PHASE_TEMP_REENTER(); + return; } LAYOUT_PHASE_TEMP_REENTER(); @@ -8401,10 +8273,10 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // Just reframe the parent, since framesets are weird like that. *aDidReconstruct = true; LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(parentFrame->GetContent(), insertionKind, + aFlags); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // If we're a child of MathML, then we should reframe the MathML content. @@ -8415,10 +8287,9 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML)) { *aDidReconstruct = true; LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(possibleMathMLAncestor->GetContent(), - false, aFlags, aDestroyedFramesFor); + RecreateFramesForContent(parentFrame->GetContent(), insertionKind, aFlags); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // Undo XUL wrapping if it's no longer needed. @@ -8432,15 +8303,14 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, !AnyKidsNeedBlockParent(childFrame->GetNextSibling())) { *aDidReconstruct = true; LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(grandparentFrame->GetContent(), true, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(grandparentFrame->GetContent(), insertionKind, + aFlags); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } #ifdef ACCESSIBILITY - nsAccessibilityService* accService = nsIPresShell::AccService(); - if (accService) { + if (nsAccessibilityService* accService = nsIPresShell::AccService()) { accService->ContentRemoved(mPresShell, aChild); } #endif @@ -8449,7 +8319,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // :first-letter style applies. nsIFrame* inflowChild = childFrame; if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { - inflowChild = GetPlaceholderFrameFor(childFrame); + inflowChild = childFrame->GetPlaceholderFrame(); NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?"); } nsContainerFrame* containingBlock = @@ -8479,7 +8349,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // XXXbz the GetContent() != aChild check is needed due to bug 135040. // Remove it once that's fixed. ClearUndisplayedContentIn(aChild, aContainer); - return NS_OK; + return; } parentFrame = childFrame->GetParent(); parentType = parentFrame->GetType(); @@ -8505,7 +8375,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // Notify the parent frame that it should delete the frame if (childFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { - childFrame = GetPlaceholderFrameFor(childFrame); + childFrame = childFrame->GetPlaceholderFrame(); NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow."); parentFrame = childFrame->GetParent(); } @@ -8543,6 +8413,9 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, // and hence it's adjacent to a block end. // If aOldNextSibling is null, then the text node before the node being // removed is the last node, and we don't need to worry about it. + // + // FIXME(emilio): This should probably use the lazy frame construction + // bits if possible instead of reframing it in place. if (aOldNextSibling) { nsIContent* prevSibling = aOldNextSibling->GetPreviousSibling(); if (prevSibling && prevSibling->GetPreviousSibling()) { @@ -8569,7 +8442,7 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, #endif } - return rv; + return; } /** @@ -8631,12 +8504,11 @@ nsCSSFrameConstructor::EnsureFrameForTextNode(nsGenericDOMDataNode* aContent) return aContent->GetPrimaryFrame(); } -nsresult +void nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent, CharacterDataChangeInfo* aInfo) { AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); - nsresult rv = NS_OK; if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) && !aContent->TextIsOnlyWhitespace()) || @@ -8648,10 +8520,10 @@ nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent, "Bit should never be set on generated content"); #endif LAYOUT_PHASE_TEMP_EXIT(); - nsresult rv = RecreateFramesForContent(aContent, false, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aContent, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); LAYOUT_PHASE_TEMP_REENTER(); - return rv; + return; } // Find the child frame @@ -8699,8 +8571,6 @@ nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent, RecoverLetterFrames(block); } } - - return rv; } void @@ -9149,7 +9019,7 @@ nsCSSFrameConstructor::ReplicateFixedFrames(nsPageContentFrame* aParentFrame) // are within fixed frames, because that would cause duplicates on the new // page - bug 389619) for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) { - nsIFrame* prevPlaceholder = GetPlaceholderFrameFor(fixed); + nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame(); if (prevPlaceholder && nsLayoutUtils::IsProperAncestorFrame(prevCanvasFrame, prevPlaceholder)) { // We want to use the same style as the primary style frame for @@ -9314,7 +9184,8 @@ nsCSSFrameConstructor::MaybeRecreateFramesForElement(Element* aElement) } } - RecreateFramesForContent(aElement, false, REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aElement, InsertionKind::Sync, + REMOVE_FOR_RECONSTRUCTION); return nullptr; } @@ -9357,19 +9228,16 @@ FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) } bool -nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, - RemoveFlags aFlags, - nsresult* aResult, - nsIContent** aDestroyedFramesFor) +nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval( + nsIFrame* aFrame, + InsertionKind aInsertionKind, + RemoveFlags aFlags) { NS_PRECONDITION(aFrame, "Must have a frame"); NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root"); - NS_PRECONDITION(aResult, "Null out param?"); NS_PRECONDITION(aFrame == aFrame->FirstContinuation(), "aFrame not the result of GetPrimaryFrame()?"); - *aDestroyedFramesFor = nullptr; - if (IsFramePartOfIBSplit(aFrame)) { // The removal functions can't handle removal of an {ib} split directly; we // need to rebuild the containing block. @@ -9382,44 +9250,44 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, } #endif - *aResult = ReframeContainingBlock(aFrame, aFlags, aDestroyedFramesFor); + ReframeContainingBlock(aFrame, aInsertionKind, aFlags); return true; } nsContainerFrame* insertionFrame = aFrame->GetContentInsertionFrame(); if (insertionFrame && insertionFrame->GetType() == nsGkAtoms::legendFrame && aFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame) { - // When we remove the legend for a fieldset, we should reframe - // the fieldset to ensure another legend is used, if there is one - *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), false, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(aFrame->GetParent()->GetContent(), aInsertionKind, + aFlags); return true; } - if (insertionFrame && - aFrame->GetParent()->GetType() == nsGkAtoms::detailsFrame) { + nsIFrame* inFlowFrame = + (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ? + aFrame->GetPlaceholderFrame() : aFrame; + MOZ_ASSERT(inFlowFrame, "How did that happen?"); + MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(), + "placeholder for primary frame has previous continuations?"); + nsIFrame* parent = inFlowFrame->GetParent(); + + if (parent && parent->GetType() == nsGkAtoms::detailsFrame) { HTMLSummaryElement* summary = - HTMLSummaryElement::FromContent(insertionFrame->GetContent()); + HTMLSummaryElement::FromContent(aFrame->GetContent()); + DetailsFrame* detailsFrame = static_cast<DetailsFrame*>(parent); - if (summary && summary->IsMainSummary()) { + // Unlike adding summary element cases, we need to check children of the + // parent details frame since at this moment the summary element has been + // already removed from the parent details element's child list. + if (summary && detailsFrame->HasMainSummaryFrame(aFrame)) { // When removing a summary, we should reframe the parent details frame to // ensure that another summary is used or the default summary is // generated. - *aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(), - false, REMOVE_FOR_RECONSTRUCTION, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } } // Now check for possibly needing to reconstruct due to a pseudo parent - nsIFrame* inFlowFrame = - (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ? - GetPlaceholderFrameFor(aFrame) : aFrame; - MOZ_ASSERT(inFlowFrame, "How did that happen?"); - MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(), - "placeholder for primary frame has previous continuations?"); - nsIFrame* parent = inFlowFrame->GetParent(); // For the case of ruby pseudo parent, effectively, only pseudo rb/rt frame // need to be checked here, since all other types of parent will be catched // by "Check ruby containers" section below. @@ -9437,10 +9305,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, // Similar if we're a table-caption. (inFlowFrame->IsTableCaption() && parent->GetChildList(nsIFrame::kCaptionList).FirstChild() == inFlowFrame)) { - // We're the first or last frame in the pseudo. Need to reframe. - // Good enough to recreate frames for |parent|'s content - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } } @@ -9469,8 +9334,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, #endif // Good enough to recreate frames for aFrame's parent's content; even if // aFrame's parent is a pseudo, that'll be the right content node. - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } } @@ -9486,8 +9350,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, // frames may be constructed or destroyed accordingly. // 2. The type of the first child of a ruby frame determines // whether a pseudo ruby base container should exist. - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } @@ -9510,8 +9373,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, } #endif // DEBUG // Recreate frames for the flex container (the removed frame's parent) - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } @@ -9529,8 +9391,8 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, } #endif // DEBUG // Recreate frames for the flex container (the removed frame's grandparent) - *aResult = RecreateFramesForContent(parent->GetParent()->GetContent(), true, - aFlags, aDestroyedFramesFor); + RecreateFramesForContent(parent->GetParent()->GetContent(), aInsertionKind, + aFlags); return true; } @@ -9538,7 +9400,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, if (aFrame->GetType() == nsGkAtoms::popupSetFrame) { nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell); if (rootBox && rootBox->GetPopupSetFrame() == aFrame) { - *aResult = ReconstructDocElementHierarchy(); + ReconstructDocElementHierarchy(); return true; } } @@ -9550,8 +9412,7 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, !inFlowFrame->GetNextSibling() && ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) || (parent->GetNextContinuation() && !parent->GetNextInFlow()))) { - *aResult = RecreateFramesForContent(parent->GetContent(), true, aFlags, - aDestroyedFramesFor); + RecreateFramesForContent(parent->GetContent(), aInsertionKind, aFlags); return true; } @@ -9587,24 +9448,26 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, } #endif - *aResult = ReframeContainingBlock(parent, aFlags, aDestroyedFramesFor); + ReframeContainingBlock(parent, aInsertionKind, aFlags); return true; } -nsresult -nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, - bool aAsyncInsert, - RemoveFlags aFlags, - nsIContent** aDestroyedFramesFor) +void +nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, + InsertionKind aInsertionKind, + RemoveFlags aFlags) { - NS_PRECONDITION(!aAsyncInsert || aContent->IsElement(), - "Can only insert elements async"); + MOZ_ASSERT(aInsertionKind != InsertionKind::Async || aContent->IsElement()); + MOZ_ASSERT(aContent); + // If there is no document, we don't want to recreate frames for it. (You // shouldn't generally be giving this method content without a document // anyway). // Rebuilding the frame tree can have bad effects, especially if it's the // frame tree for chrome (see bug 157322). - NS_ENSURE_TRUE(aContent->GetComposedDoc(), NS_ERROR_FAILURE); + if (NS_WARN_IF(!aContent->GetComposedDoc())) { + return; + } // Is the frame ib-split? If so, we need to reframe the containing // block *here*, rather than trying to remove and re-insert the @@ -9630,8 +9493,8 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, if (frame) { nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(frame); if (nonGeneratedAncestor->GetContent() != aContent) { - return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), aAsyncInsert, - aFlags, aDestroyedFramesFor); + return RecreateFramesForContent(nonGeneratedAncestor->GetContent(), + aInsertionKind, aFlags); } if (frame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) { @@ -9651,8 +9514,8 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, if (ancestor->GetType() != nsGkAtoms::svgUseFrame) { NS_ASSERTION(aContent->IsInNativeAnonymousSubtree(), "Why is NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT set?"); - return RecreateFramesForContent(ancestor->GetContent(), aAsyncInsert, - aFlags, aDestroyedFramesFor); + return RecreateFramesForContent(ancestor->GetContent(), aInsertionKind, + aFlags); } } @@ -9663,20 +9526,13 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, // with native anonymous content from the editor. if (parent && parent->IsLeaf() && parentContent && parentContent != aContent) { - return RecreateFramesForContent(parentContent, aAsyncInsert, aFlags, - aDestroyedFramesFor); + return RecreateFramesForContent(parentContent, aInsertionKind, aFlags); } } - nsresult rv = NS_OK; - nsIContent* container; - if (frame && MaybeRecreateContainerForFrameRemoval(frame, aFlags, &rv, - &container)) { - MOZ_ASSERT(container); - if (aDestroyedFramesFor) { - *aDestroyedFramesFor = container; - } - return rv; + if (frame && + MaybeRecreateContainerForFrameRemoval(frame, aInsertionKind, aFlags)) { + return; } nsINode* containerNode = aContent->GetParentNode(); @@ -9688,47 +9544,44 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent, // Need the nsIContent parent, which might be null here, since we need to // pass it to ContentInserted and ContentRemoved. + // + // FIXME(emilio): Do we need a strong ref here? nsCOMPtr<nsIContent> container = aContent->GetParent(); // Remove the frames associated with the content object. bool didReconstruct; nsIContent* nextSibling = aContent->IsRootOfAnonymousSubtree() ? nullptr : aContent->GetNextSibling(); - const bool reconstruct = aFlags != REMOVE_DESTROY_FRAMES; - RemoveFlags flags = reconstruct ? REMOVE_FOR_RECONSTRUCTION : aFlags; - rv = ContentRemoved(container, aContent, nextSibling, flags, - &didReconstruct, aDestroyedFramesFor); - if (NS_FAILED(rv)) { - return rv; - } - if (reconstruct && !didReconstruct) { - // Now, recreate the frames associated with this content object. If - // ContentRemoved triggered reconstruction, then we don't need to do this - // because the frames will already have been built. - if (aAsyncInsert) { + ContentRemoved(container, aContent, nextSibling, aFlags, &didReconstruct); + if (!didReconstruct) { + if (aInsertionKind == InsertionKind::Async) { // XXXmats doesn't frame state need to be restored in this case too? - RestyleManager()->PostRestyleEvent( - aContent->AsElement(), nsRestyleHint(0), nsChangeHint_ReconstructFrame); + RestyleManager()->PostRestyleEvent(aContent->AsElement(), + nsRestyleHint(0), + nsChangeHint_ReconstructFrame); } else { - rv = ContentInserted(container, aContent, mTempFrameTreeState, false); + // Now, recreate the frames associated with this content object. If + // ContentRemoved triggered reconstruction, then we don't need to do this + // because the frames will already have been built. + ContentInserted(container, aContent, mTempFrameTreeState, false); } } } - - return rv; } void -nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor) +nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent, + bool* aDidReconstruct) { MOZ_ASSERT(aContent && aContent->GetParentNode()); - bool didReconstruct; nsIContent* nextSibling = aContent->IsRootOfAnonymousSubtree() ? nullptr : aContent->GetNextSibling(); - ContentRemoved(aContent->GetParent(), aContent, nextSibling, - REMOVE_DESTROY_FRAMES, &didReconstruct, aDestroyedFramesFor); + ContentRemoved(aContent->GetParent(), + aContent, + nextSibling, + REMOVE_DESTROY_FRAMES, + aDidReconstruct); } ////////////////////////////////////////////////////////////////////// @@ -10660,13 +10513,6 @@ nsCSSFrameConstructor::AddFCItemsForAnonymousContent( { for (uint32_t i = 0; i < aAnonymousItems.Length(); ++i) { nsIContent* content = aAnonymousItems[i].mContent; -#ifdef DEBUG - nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame); - NS_ASSERTION(!creator || !creator->CreateFrameFor(content), - "If you need to use CreateFrameFor, you need to call " - "CreateAnonymousFrames manually and not follow the standard " - "ProcessChildren() codepath for this frame"); -#endif // Gecko-styled nodes should have no pending restyle flags. MOZ_ASSERT_IF(!content->IsStyledByServo(), !content->IsElement() || @@ -10684,24 +10530,111 @@ nsCSSFrameConstructor::AddFCItemsForAnonymousContent( RefPtr<nsStyleContext> styleContext; TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext); - if (aAnonymousItems[i].mStyleContext) { - // If we have an explicit style context, that means that the anonymous - // content creator had its own plan for the style, and doesn't need the - // computed style obtained by cascading this content as a normal node. - // This happens when a native anonymous node is used to implement a - // pseudo-element. Allowing Servo to traverse these nodes would be wasted - // work, so assert that we didn't do that. - MOZ_ASSERT_IF(content->IsStyledByServo(), !content->HasServoData()); - styleContext = aAnonymousItems[i].mStyleContext.forget(); - } else { - // If we don't have an explicit style context, that means we need the - // ordinary computed values. Make sure we eagerly cascaded them when the - // anonymous nodes were created. - MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(), - content->HasServoData()); - styleContext = ResolveStyleContext(aFrame, content, &aState); + + // Make sure we eagerly performed the servo cascade when the anonymous + // nodes were created. + MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(), + content->AsElement()->HasServoData()); + + // Determine whether this NAC is pseudo-implementing. + nsIAtom* pseudo = nullptr; + if (content->IsElement()) { + auto pseudoType = content->AsElement()->GetPseudoElementType(); + if (pseudoType != CSSPseudoElementType::NotPseudo) { + pseudo = nsCSSPseudoElements::GetPseudoAtom(pseudoType); + } + } + + // Determine the appropriate parent style for this NAC, and if the NAC + // implements a pseudo-element, the appropriate originating element + // (that is to say, the element to the left of the ::pseudo-element in + // the selector). This is all rather tricky, and merits some discussion. + // + // First, it's important to note that author stylesheets generally do not + // apply to elements in native-anonymous subtrees. The exceptions to + // this are web-exposed pseudo-elements, where authors can style the + // pseudo-implementing NAC if the originating element is not itself in a NAC + // subtree. + // + // For this reason, it's very important that we avoid using a style parent + // that is inside a NAC subtree together with an originating element that + // is not inside a NAC subtree, since that would allow authors to + // explicitly inherit styles from internal elements, potentially making + // the NAC hierarchy observable. To ensure this, and generally simplify + // things, we always set the originating element to the style parent. + // + // As a consequence of the above, all web-exposed pseudo-elements (which, + // by definition, must have a content-accessible originating element) must + // also inherit style from that same content-accessible element. To avoid + // unintuitive behavior differences between NAC elements that do and don't + // correspond to web-exposed pseudo-elements, we follow this protocol for + // all NAC, pseudo-implementing or not. + // + // However, things get tricky with the <video> element, where we have a + // bunch of XBL-generated anonymous content descending from a native- + // anonymous XULElement. The XBL elements inherit style from their + // flattened tree parent, because that's how XBL works. But then we need + // to figure out what to do when one of those anonymous XBL elements + // (like an <input> element) generates its own (possibly pseudo-element- + // implementing) NAC. + // + // In this case, we inherit style from the XBL-generated NAC-creating + // element, rather than the <video> element. There are a number of good + // reasons for this. First, inheriting from the great-grandparent while + // the parent inherits from the grandparent would be bizarre at best. + // Second, exposing pseudo-elements from elements within our particular + // XBL implementation would allow content styles to (un)intentionally + // alter the video controls, which would be very bad. Third, our UA + // stylesheets have selectors like: + // + // input[type=range][orient=horizontal]::-moz-range-track + // + // and we need to make sure that the originating element is the <input>, + // not the <video>, because that's where the |orient| attribute lives. + // + // The upshot of all of this is that, to find the style parent (and + // originating element, if applicable), we walk up our parent chain to the + // first element that is not itself NAC (distinct from whether it happens + // to be in a NAC subtree). + // + // The one exception to all of this is scrollbar content, which we parent + // directly to the scrollframe. This is because the special-snowflake + // construction of scroll frames doesn't result in the placeholder frame + // being constructed until later, which means that GetInFlowParent() doesn't + // work right in the case of out-of-flow scrollframes. + // + // To implement all this, we need to pass the correct parent style context + // here because SetPrimaryFrame() may not have been called on the content + // yet and thus ResolveStyleContext can't find it otherwise. + // + // We don't need to worry about display:contents here, because such + // elements don't get a frame and thus can't generate NAC. But we do need + // to worry about anonymous boxes, which CorrectStyleParentFrame handles + // for us. + nsIFrame* inheritFrame = aFrame; + if (!content->IsNativeScrollbarContent()) { + while (inheritFrame->GetContent()->IsNativeAnonymous()) { + inheritFrame = inheritFrame->GetInFlowParent(); + } } + nsIFrame* styleParentFrame = + nsFrame::CorrectStyleParentFrame(inheritFrame, pseudo); + // The only way we can not have a style parent now is if inheritFrame is the + // canvas frame and we're the NAC parent for all the things added via + // nsIDocument::InsertAnonymousContent. + MOZ_ASSERT_IF(!styleParentFrame, + inheritFrame->GetType() == nsGkAtoms::canvasFrame); + // And that anonymous div has no pseudo. + MOZ_ASSERT_IF(!styleParentFrame, !pseudo); + + Element* originating = + pseudo ? styleParentFrame->GetContent()->AsElement() : nullptr; + nsStyleContext* parentStyle = + styleParentFrame ? styleParentFrame->StyleContext() : nullptr; + styleContext = + ResolveStyleContext(parentStyle, content, &aState, originating); + nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr; if (!aAnonymousItems[i].mChildren.IsEmpty()) { anonChildren = &aAnonymousItems[i].mChildren; @@ -11028,151 +10961,6 @@ nsCSSFrameConstructor::AppendFirstLineFrames( lineFrame, aFrameItems); } -// Special routine to handle inserting a new frame into a block -// frame's child list. Takes care of placing the new frame into the -// right place when first-line style is present. -nsresult -nsCSSFrameConstructor::InsertFirstLineFrames( - nsFrameConstructorState& aState, - nsIContent* aContent, - nsIFrame* aBlockFrame, - nsContainerFrame** aParentFrame, - nsIFrame* aPrevSibling, - nsFrameItems& aFrameItems) -{ - nsresult rv = NS_OK; - // XXXbz If you make this method actually do something, check to - // make sure that the caller is passing what you expect. In - // particular, which content is aContent? And audit the rest of - // this code too; it makes bogus assumptions and may not build. -#if 0 - nsIFrame* parentFrame = *aParentFrame; - nsIFrame* newFrame = aFrameItems.childList; - bool isInline = IsInlineOutside(newFrame); - - if (!aPrevSibling) { - // Insertion will become the first frame. Two cases: we either - // already have a first-line frame or we don't. - nsIFrame* firstBlockKid = aBlockFrame->PrincipalChildList().FirstChild(); - if (firstBlockKid->GetType() == nsGkAtoms::lineFrame) { - // We already have a first-line frame - nsIFrame* lineFrame = firstBlockKid; - - if (isInline) { - // Easy case: the new inline frame will go into the lineFrame. - ReparentFrame(this, lineFrame, newFrame); - InsertFrames(lineFrame, kPrincipalList, nullptr, newFrame); - - // Since the frame is going into the lineFrame, don't let it - // go into the block too. - aFrameItems.childList = nullptr; - aFrameItems.lastChild = nullptr; - } - else { - // Harder case: We are about to insert a block level element - // before the first-line frame. - // XXX need a method to steal away frames from the line-frame - } - } - else { - // We do not have a first-line frame - if (isInline) { - // We now need a first-line frame to contain the inline frame. - nsIFrame* lineFrame = NS_NewFirstLineFrame(firstLineStyle); - - if (NS_SUCCEEDED(rv)) { - // Lookup first-line style context - nsStyleContext* parentStyle = - nsFrame::CorrectStyleParentFrame(aBlockFrame, - nsCSSPseudoElements::firstLine)-> - StyleContext(); - RefPtr<nsStyleContext> firstLineStyle = - GetFirstLineStyle(aContent, parentStyle); - - // Initialize the line frame - InitAndRestoreFrame(aState, aContent, aBlockFrame, lineFrame); - - // Make sure the caller inserts the lineFrame into the - // blocks list of children. - aFrameItems.childList = lineFrame; - aFrameItems.lastChild = lineFrame; - - // Give the inline frames to the lineFrame <b>after</b> - // reparenting them - NS_ASSERTION(lineFrame->StyleContext() == firstLineStyle, - "Bogus style context on line frame"); - ReparentFrame(aPresContext, lineFrame, newFrame); - lineFrame->SetInitialChildList(kPrincipalList, newFrame); - } - } - else { - // Easy case: the regular insertion logic can insert the new - // frame because it's a block frame. - } - } - } - else { - // Insertion will not be the first frame. - nsIFrame* prevSiblingParent = aPrevSibling->GetParent(); - if (prevSiblingParent == aBlockFrame) { - // Easy case: The prev-siblings parent is the block - // frame. Therefore the prev-sibling is not currently in a - // line-frame. Therefore the new frame which is going after it, - // regardless of type, is not going into a line-frame. - } - else { - // If the prevSiblingParent is not the block-frame then it must - // be a line-frame (if it were a letter-frame, that logic would - // already have adjusted the prev-sibling to be the - // letter-frame). - if (isInline) { - // Easy case: the insertion can go where the caller thinks it - // should go (which is into prevSiblingParent). - } - else { - // Block elements don't end up in line-frames, therefore - // change the insertion point to aBlockFrame. However, there - // might be more inline elements following aPrevSibling that - // need to be pulled out of the line-frame and become children - // of the block. - nsIFrame* nextSibling = aPrevSibling->GetNextSibling(); - nsIFrame* nextLineFrame = prevSiblingParent->GetNextInFlow(); - if (nextSibling || nextLineFrame) { - // Oy. We have work to do. Create a list of the new frames - // that are going into the block by stripping them away from - // the line-frame(s). - if (nextSibling) { - nsLineFrame* lineFrame = (nsLineFrame*) prevSiblingParent; - nsFrameList tail = lineFrame->StealFramesAfter(aPrevSibling); - // XXX do something with 'tail' - } - - nsLineFrame* nextLineFrame = (nsLineFrame*) lineFrame; - for (;;) { - nextLineFrame = nextLineFrame->GetNextInFlow(); - if (!nextLineFrame) { - break; - } - nsIFrame* kids = nextLineFrame->PrincipalChildList().FirstChild(); - } - } - else { - // We got lucky: aPrevSibling was the last inline frame in - // the line-frame. - ReparentFrame(this, aBlockFrame, newFrame); - InsertFrames(aBlockFrame, kPrincipalList, - prevSiblingParent, newFrame); - aFrameItems.childList = nullptr; - aFrameItems.lastChild = nullptr; - } - } - } - } - -#endif - return rv; -} - //---------------------------------------------------------------------- // First-letter support @@ -11494,7 +11282,7 @@ FindFirstLetterFrame(nsIFrame* aFrame, nsIFrame::ChildListID aListID) return nullptr; } -nsresult +void nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( nsIPresShell* aPresShell, nsIFrame* aBlockFrame) @@ -11506,7 +11294,7 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( floatFrame = ::FindFirstLetterFrame(aBlockFrame, nsIFrame::kPushedFloatsList); if (!floatFrame) { - return NS_OK; + return; } } @@ -11514,19 +11302,19 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( // destroyed when we destroy the letter frame). nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild(); if (!textFrame) { - return NS_OK; + return; } // Discover the placeholder frame for the letter frame - nsPlaceholderFrame* placeholderFrame = GetPlaceholderFrameFor(floatFrame); + nsPlaceholderFrame* placeholderFrame = floatFrame->GetPlaceholderFrame(); if (!placeholderFrame) { // Somethings really wrong - return NS_OK; + return; } nsContainerFrame* parentFrame = placeholderFrame->GetParent(); if (!parentFrame) { // Somethings really wrong - return NS_OK; + return; } // Create a new text frame with the right style context that maps @@ -11535,7 +11323,7 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( nsStyleContext* parentSC = parentFrame->StyleContext(); nsIContent* textContent = textFrame->GetContent(); if (!textContent) { - return NS_OK; + return; } RefPtr<nsStyleContext> newSC = aPresShell->StyleSet()-> ResolveStyleForText(textContent, parentSC); @@ -11580,11 +11368,9 @@ nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( if (offsetsNeedFixing) { prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING); } - - return NS_OK; } -nsresult +void nsCSSFrameConstructor::RemoveFirstLetterFrames(nsIPresShell* aPresShell, nsContainerFrame* aFrame, nsContainerFrame* aBlockFrame, @@ -11657,11 +11443,9 @@ nsCSSFrameConstructor::RemoveFirstLetterFrames(nsIPresShell* aPresShell, prevSibling = kid; kid = kid->GetNextSibling(); } - - return NS_OK; } -nsresult +void nsCSSFrameConstructor::RemoveLetterFrames(nsIPresShell* aPresShell, nsContainerFrame* aBlockFrame) { @@ -11670,20 +11454,16 @@ nsCSSFrameConstructor::RemoveLetterFrames(nsIPresShell* aPresShell, nsContainerFrame* continuation = aBlockFrame; bool stopLooking = false; - nsresult rv; do { - rv = RemoveFloatingFirstLetterFrames(aPresShell, continuation); - if (NS_SUCCEEDED(rv)) { - rv = RemoveFirstLetterFrames(aPresShell, - continuation, aBlockFrame, &stopLooking); - } + RemoveFloatingFirstLetterFrames(aPresShell, continuation); + RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame, + &stopLooking); if (stopLooking) { break; } continuation = static_cast<nsContainerFrame*>(continuation->GetNextContinuation()); } while (continuation); - return rv; } // Fixup the letter frame situation for the given block @@ -11726,7 +11506,7 @@ nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame) // listbox Widget Routines -nsresult +void nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, nsIFrame* aPrevFrame, nsIContent* aChild, @@ -11734,8 +11514,6 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, bool aIsAppend) { #ifdef MOZ_XUL - nsresult rv = NS_OK; - // Construct a new frame if (nullptr != aParentFrame) { nsFrameItems frameItems; @@ -11756,7 +11534,7 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, if (StyleDisplay::None == display->mDisplay) { *aNewFrame = nullptr; - return NS_OK; + return; } BeginUpdate(); @@ -11775,9 +11553,9 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, if (newFrame) { // Notify the parent frame if (aIsAppend) - rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems); + ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems); else - rv = ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems); + ((nsListBoxBodyFrame*)aParentFrame)->ListBoxInsertFrames(aPrevFrame, frameItems); } EndUpdate(); @@ -11792,10 +11570,6 @@ nsCSSFrameConstructor::CreateListBoxContent(nsContainerFrame* aParentFrame, } #endif } - - return rv; -#else - return NS_ERROR_FAILURE; #endif } @@ -12218,8 +11992,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, if (aFrame->IsXULBoxFrame() && !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) && aItems.AnyItemsNeedBlockParent()) { - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } @@ -12238,8 +12012,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, const bool isWebkitBox = IsFlexContainerForLegacyBox(aFrame, frameType); if (aPrevSibling && IsAnonymousFlexOrGridItem(aPrevSibling) && iter.item().NeedsAnonFlexOrGridItem(aState, isWebkitBox)) { - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } @@ -12250,8 +12024,9 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, iter.SetToEnd(); iter.Prev(); if (iter.item().NeedsAnonFlexOrGridItem(aState, isWebkitBox)) { - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), + InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } } @@ -12281,8 +12056,9 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, isWebkitBox)) { // We hit something that _doesn't_ need an anonymous flex item! // Rebuild the flex container to bust it out. - RecreateFramesForContent(containerFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(containerFrame->GetContent(), + InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } @@ -12306,8 +12082,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // We want to optimize it better, and avoid reframing as much as // possible. But given the cases above, and the fact that a ruby // usually won't be very large, it should be fine to reframe it. - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } @@ -12473,8 +12249,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, if (!aItems.AllWantParentType(parentType)) { // Reframing aFrame->GetContent() is good enough, since the content of // table pseudo-frames is the ancestor content. - RecreateFramesForContent(aFrame->GetContent(), true, - REMOVE_FOR_RECONSTRUCTION, nullptr); + RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } } @@ -12562,15 +12338,15 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, static_cast<void*>(blockContent)); } #endif - RecreateFramesForContent(blockContent, true, REMOVE_FOR_RECONSTRUCTION, - nullptr); + RecreateFramesForContent(blockContent, InsertionKind::Async, + REMOVE_FOR_RECONSTRUCTION); return true; } -nsresult -nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame, - RemoveFlags aFlags, - nsIContent** aDestroyedFramesFor) +void +nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame, + InsertionKind aInsertionKind, + RemoveFlags aFlags) { #ifdef DEBUG @@ -12590,7 +12366,7 @@ nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame, // don't ReframeContainingBlock, this will result in a crash // if we remove a tree that's in reflow - see bug 121368 for testcase NS_ERROR("Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a Reflow!!!"); - return NS_OK; + return; } // Get the first "normal" ancestor of the target frame. @@ -12606,23 +12382,25 @@ nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame, // so GetFloatContainingBlock(aFrame) has been removed // And get the containingBlock's content - nsCOMPtr<nsIContent> blockContent = containingBlock->GetContent(); - if (blockContent) { + if (nsIContent* blockContent = containingBlock->GetContent()) { #ifdef DEBUG if (gNoisyContentUpdates) { printf(" ==> blockContent=%p\n", static_cast<void*>(blockContent)); } #endif - return RecreateFramesForContent(blockContent, true, aFlags, aDestroyedFramesFor); + RecreateFramesForContent(blockContent->AsElement(), aInsertionKind, + REMOVE_FOR_RECONSTRUCTION); + return; } } // If we get here, we're screwed! - return RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(), - true, aFlags, nullptr); + RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(), + aInsertionKind, + REMOVE_FOR_RECONSTRUCTION); } -nsresult +void nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame) { { @@ -12656,8 +12434,6 @@ nsCSSFrameConstructor::GenerateChildFrames(nsContainerFrame* aFrame) // call XBL constructors after the frames are created mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue(); - - return NS_OK; } ////////////////////////////////////////////////////////// diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 7d1b8d42f..0de00a90c 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -48,7 +48,7 @@ class FlattenedChildIterator; } // namespace dom } // namespace mozilla -class nsCSSFrameConstructor : public nsFrameManager +class nsCSSFrameConstructor final : public nsFrameManager { public: typedef mozilla::CSSPseudoElementType CSSPseudoElementType; @@ -60,7 +60,7 @@ public: nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell); ~nsCSSFrameConstructor(void) { - NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?"); + MOZ_ASSERT(mUpdateCount == 0, "Dying in the middle of our own update?"); } // get the alternate text for a content node @@ -78,7 +78,7 @@ public: nsIFrame* ConstructRootFrame(); - nsresult ReconstructDocElementHierarchy(); + void ReconstructDocElementHierarchy(); // Create frames for content nodes that are marked as needing frames. This // should be called before ProcessPendingRestyles. @@ -97,8 +97,8 @@ private: // aChild is the child being inserted for inserts, and the first // child being appended for appends. bool MaybeConstructLazily(Operation aOperation, - nsIContent* aContainer, - nsIContent* aChild); + nsIContent* aContainer, + nsIContent* aChild); // Issues a single ContentInserted for each child of aContainer in the range // [aStartChild, aEndChild). @@ -152,8 +152,8 @@ private: // Returns true if parent was recreated due to frameset child, false otherwise. bool MaybeRecreateForFrameset(nsIFrame* aParentFrame, - nsIContent* aStartChild, - nsIContent* aEndChild); + nsIContent* aStartChild, + nsIContent* aEndChild); public: /** @@ -202,16 +202,16 @@ public: // If aAllowLazyConstruction is true then frame construction of the new // children can be done lazily. - nsresult ContentAppended(nsIContent* aContainer, - nsIContent* aFirstNewContent, - bool aAllowLazyConstruction); + void ContentAppended(nsIContent* aContainer, + nsIContent* aFirstNewContent, + bool aAllowLazyConstruction); // If aAllowLazyConstruction is true then frame construction of the new child // can be done lazily. - nsresult ContentInserted(nsIContent* aContainer, - nsIContent* aChild, - nsILayoutHistoryState* aFrameState, - bool aAllowLazyConstruction); + void ContentInserted(nsIContent* aContainer, + nsIContent* aChild, + nsILayoutHistoryState* aFrameState, + bool aAllowLazyConstruction); // Like ContentInserted but handles inserting the children of aContainer in // the range [aStartChild, aEndChild). aStartChild must be non-null. @@ -219,12 +219,17 @@ public: // aStartChild. If aAllowLazyConstruction is true then frame construction of // the new children can be done lazily. It is only allowed to be true when // inserting a single node. - nsresult ContentRangeInserted(nsIContent* aContainer, - nsIContent* aStartChild, - nsIContent* aEndChild, - nsILayoutHistoryState* aFrameState, - bool aAllowLazyConstruction); + void ContentRangeInserted(nsIContent* aContainer, + nsIContent* aStartChild, + nsIContent* aEndChild, + nsILayoutHistoryState* aFrameState, + bool aAllowLazyConstruction); +public: + // FIXME(emilio): How important is it to keep the frame tree state around for + // REMOVE_DESTROY_FRAMES? + // + // Seems like otherwise we could just remove it. enum RemoveFlags { REMOVE_CONTENT, REMOVE_FOR_RECONSTRUCTION, REMOVE_DESTROY_FRAMES }; /** @@ -243,15 +248,14 @@ public: * only when aFlags == REMOVE_DESTROY_FRAMES, otherwise it will only be * captured if we reconstructed frames for an ancestor. */ - nsresult ContentRemoved(nsIContent* aContainer, - nsIContent* aChild, - nsIContent* aOldNextSibling, - RemoveFlags aFlags, - bool* aDidReconstruct, - nsIContent** aDestroyedFramesFor = nullptr); + void ContentRemoved(nsIContent* aContainer, + nsIContent* aChild, + nsIContent* aOldNextSibling, + RemoveFlags aFlags, + bool* aDidReconstruct); - nsresult CharacterDataChanged(nsIContent* aContent, - CharacterDataChangeInfo* aInfo); + void CharacterDataChanged(nsIContent* aContent, + CharacterDataChangeInfo* aInfo); // If aContent is a text node that has been optimized away due to being // whitespace next to a block boundary (or for some other reason), stop @@ -260,8 +264,8 @@ public: // Returns the frame for aContent if there is one. nsIFrame* EnsureFrameForTextNode(nsGenericDOMDataNode* aContent); - // generate the child frames and process bindings - nsresult GenerateChildFrames(nsContainerFrame* aFrame); + // Generate the child frames and process bindings + void GenerateChildFrames(nsContainerFrame* aFrame); // Should be called when a frame is going to be destroyed and // WillDestroyFrameTree hasn't been called yet. @@ -280,14 +284,10 @@ public: /** * Destroy the frames for aContent. Note that this may destroy frames - * for an ancestor instead - aDestroyedFramesFor contains the content node - * where frames were actually destroyed (which should be used in the - * ContentInserted call to recreate frames). The frame tree state - * is captured before the frames are destroyed and can be retrieved using - * GetLastCapturedLayoutHistoryState(). + * for an ancestor instead - aDidReconstruct contains whether a reconstruct + * was posted for any ancestor. */ - void DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor); + void DestroyFramesFor(nsIContent* aContent, bool* aDidReconstruct); // Request to create a continuing frame. This method never returns null. nsIFrame* CreateContinuingFrame(nsPresContext* aPresContext, @@ -303,11 +303,11 @@ public: */ InsertionPoint GetInsertionPoint(nsIContent* aContainer, nsIContent* aChild); - nsresult CreateListBoxContent(nsContainerFrame* aParentFrame, - nsIFrame* aPrevFrame, - nsIContent* aChild, - nsIFrame** aResult, - bool aIsAppend); + void CreateListBoxContent(nsContainerFrame* aParentFrame, + nsIFrame* aPrevFrame, + nsIContent* aChild, + nsIFrame** aResult, + bool aIsAppend); // GetInitialContainingBlock() is deprecated in favor of GetRootElementFrame(); // nsIFrame* GetInitialContainingBlock() { return mRootElementFrame; } @@ -322,15 +322,6 @@ public: nsContainerFrame* GetDocElementContainingBlock() { return mDocElementContainingBlock; } - /** - * Return the layout history state that was captured in the last - * ContentRemoved / RecreateFramesForContent call. - */ - nsILayoutHistoryState* GetLastCapturedLayoutHistoryState() - { - return mTempFrameTreeState; - } - private: struct FrameConstructionItem; class FrameConstructionItemList; @@ -364,7 +355,8 @@ private: already_AddRefed<nsStyleContext> ResolveStyleContext(nsStyleContext* aParentStyleContext, nsIContent* aContent, - nsFrameConstructorState* aState); + nsFrameConstructorState* aState, + Element* aOriginatingElementOrNull = nullptr); // Add the frame construction items for the given aContent and aParentFrame // to the list. This might add more than one item in some rare cases. @@ -418,14 +410,14 @@ private: * @param [out] aNewContent the content node we create * @param [out] aNewFrame the new frame we create */ - nsresult CreateAttributeContent(nsIContent* aParentContent, - nsIFrame* aParentFrame, - int32_t aAttrNamespace, - nsIAtom* aAttrName, - nsStyleContext* aStyleContext, - nsCOMArray<nsIContent>& aGeneratedContent, - nsIContent** aNewContent, - nsIFrame** aNewFrame); + void CreateAttributeContent(nsIContent* aParentContent, + nsIFrame* aParentFrame, + int32_t aAttrNamespace, + nsIAtom* aAttrName, + nsStyleContext* aStyleContext, + nsCOMArray<nsIContent>& aGeneratedContent, + nsIContent** aNewContent, + nsIFrame** aNewFrame); /** * Create a text node containing the given string. If aText is non-null @@ -463,11 +455,11 @@ private: // aParentFrame. aPrevSibling must be the frame after which aFrameList is to // be placed on aParentFrame's principal child list. It may be null if // aFrameList is being added at the beginning of the child list. - nsresult AppendFramesToParent(nsFrameConstructorState& aState, - nsContainerFrame* aParentFrame, - nsFrameItems& aFrameList, - nsIFrame* aPrevSibling, - bool aIsRecursiveCall = false); + void AppendFramesToParent(nsFrameConstructorState& aState, + nsContainerFrame* aParentFrame, + nsFrameItems& aFrameList, + nsIFrame* aPrevSibling, + bool aIsRecursiveCall = false); // BEGIN TABLE SECTION /** @@ -1440,12 +1432,6 @@ private: nsFrameItems& aFrameItems); static bool AtLineBoundary(FCItemIterator& aIter); - nsresult CreateAnonymousFrames(nsFrameConstructorState& aState, - nsIContent* aParent, - nsContainerFrame* aParentFrame, - PendingBinding* aPendingBinding, - nsFrameItems& aChildItems); - nsresult GetAnonymousContent(nsIContent* aParent, nsIFrame* aParentFrame, nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonContent); @@ -1697,7 +1683,7 @@ private: // InitializeSelectFrame puts scrollFrame in aFrameItems if aBuildCombobox is false // aBuildCombobox indicates if we are building a combobox that has a dropdown // popup widget or not. - nsresult + void InitializeSelectFrame(nsFrameConstructorState& aState, nsContainerFrame* aScrollFrame, nsContainerFrame* aScrolledFrame, @@ -1718,34 +1704,36 @@ private: nsStyleContext* MaybeRecreateFramesForElement(Element* aElement); /** + * Whether insertion should be done synchronously or asynchronously. + * + * Generally, insertion is synchronous if we're reconstructing something from + * frame construction/reconstruction, and async if we're removing stuff, like + * from ContentRemoved. + */ + enum class InsertionKind + { + Sync, + Async, + }; + + /** * Recreate frames for aContent. * @param aContent the content to recreate frames for - * @param aAsyncInsert if true then a restyle event will be posted to handle - * the required ContentInserted call instead of doing it immediately. * @param aFlags normally you want to pass REMOVE_FOR_RECONSTRUCTION here - * @param aDestroyedFramesFor if non-null, it will contain the content that - * was actually reframed - it may be different than aContent. */ - nsresult - RecreateFramesForContent(nsIContent* aContent, - bool aAsyncInsert, - RemoveFlags aFlags, - nsIContent** aDestroyedFramesFor); + void RecreateFramesForContent(nsIContent* aContent, + InsertionKind aInsertionKind, + RemoveFlags aFlags); // If removal of aFrame from the frame tree requires reconstruction of some // containing block (either of aFrame or of its parent) due to {ib} splits or // table pseudo-frames, recreate the relevant frame subtree. The return value - // indicates whether this happened. If this method returns true, *aResult is - // the return value of ReframeContainingBlock or RecreateFramesForContent. If - // this method returns false, the value of *aResult is not affected. aFrame - // and aResult must not be null. aFrame must be the result of a + // indicates whether this happened. aFrame must be the result of a // GetPrimaryFrame() call on a content node (which means its parent is also - // not null). If this method returns true, aDestroyedFramesFor contains the - // content that was reframed. - bool MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, - RemoveFlags aFlags, - nsresult* aResult, - nsIContent** aDestroyedFramesFor); + // not null). + bool MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, + InsertionKind aInsertionKind, + RemoveFlags aFlags); nsIFrame* CreateContinuingOuterTableFrame(nsIPresShell* aPresShell, nsPresContext* aPresContext, @@ -1869,9 +1857,9 @@ private: bool aIsAppend, nsIFrame* aPrevSibling); - nsresult ReframeContainingBlock(nsIFrame* aFrame, - RemoveFlags aFlags, - nsIContent** aReframeContent); + void ReframeContainingBlock(nsIFrame* aFrame, + InsertionKind aInsertionKind, + RemoveFlags aFlags); //---------------------------------------- @@ -1925,18 +1913,18 @@ private: void RecoverLetterFrames(nsContainerFrame* aBlockFrame); // - nsresult RemoveLetterFrames(nsIPresShell* aPresShell, - nsContainerFrame* aBlockFrame); + void RemoveLetterFrames(nsIPresShell* aPresShell, + nsContainerFrame* aBlockFrame); // Recursive helper for RemoveLetterFrames - nsresult RemoveFirstLetterFrames(nsIPresShell* aPresShell, - nsContainerFrame* aFrame, - nsContainerFrame* aBlockFrame, - bool* aStopLooking); + void RemoveFirstLetterFrames(nsIPresShell* aPresShell, + nsContainerFrame* aFrame, + nsContainerFrame* aBlockFrame, + bool* aStopLooking); // Special remove method for those pesky floating first-letter frames - nsresult RemoveFloatingFirstLetterFrames(nsIPresShell* aPresShell, - nsIFrame* aBlockFrame); + void RemoveFloatingFirstLetterFrames(nsIPresShell* aPresShell, + nsIFrame* aBlockFrame); // Capture state for the frame tree rooted at the frame associated with the // content object, aContent @@ -1967,71 +1955,53 @@ private: nsContainerFrame* aBlockFrame, nsFrameItems& aFrameItems); - nsresult InsertFirstLineFrames(nsFrameConstructorState& aState, - nsIContent* aContent, - nsIFrame* aBlockFrame, - nsContainerFrame** aParentFrame, - nsIFrame* aPrevSibling, - nsFrameItems& aFrameItems); + // The direction in which we should look for siblings. + enum class SiblingDirection + { + Forward, + Backward, + }; /** - * Find the right frame to use for aContent when looking for sibling - * frames for aTargetContent. If aPrevSibling is true, this - * will look for last continuations, etc, as necessary. This calls - * IsValidSibling as needed; if that returns false it returns null. + * Find the frame for the content immediately next to the one aIter points to, + * in the direction SiblingDirection indicates, following continuations if + * necessary. * - * @param aContent the content to search for frames - * @param aTargetContent the content we're finding a sibling frame for - * @param aTargetContentDisplay the CSS display enum for aTargetContent if - * already known, UNSET_DISPLAY otherwise. It will be filled in - * if needed. - * @param aParentFrame the nearest ancestor frame, used internally for - * finding ::after / ::before frames - * @param aPrevSibling true if we're searching in reverse DOM order - */ - nsIFrame* FindFrameForContentSibling(nsIContent* aContent, - nsIContent* aTargetContent, - mozilla::StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame, - bool aPrevSibling); - - /** - * Find the frame for the content immediately preceding the one aIter - * points to, following continuations if necessary. aIter is passed by - * value on purpose, so as not to modify the caller's iterator. + * aIter is passed by const reference on purpose, so as not to modify the + * caller's iterator. * * @param aIter should be positioned such that aIter.GetPreviousChild() * is the first content to search for frames - * @param aTargetContent the content we're finding a sibling frame for - * @param aTargetContentDisplay the CSS display enum for aTargetContent if - * already known, UNSET_DISPLAY otherwise. It will be filled in - * if needed. - * @param aParentFrame the nearest ancestor frame, used inernally for - * finding ::after / ::before frames - */ - nsIFrame* FindPreviousSibling(mozilla::dom::FlattenedChildIterator aIter, - nsIContent* aTargetContent, - mozilla::StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame); - - /** - * Find the frame for the content node immediately following the one aIter - * points to, following continuations if necessary. aIter is passed by value - * on purpose, so as not to modify the caller's iterator. - * - * @param aIter should be positioned such that aIter.GetNextChild() - * is the first content to search for frames - * @param aTargetContent the content we're finding a sibling frame for - * @param aTargetContentDisplay the CSS display enum for aTargetContent if - * already known, UNSET_DISPLAY otherwise. It will be filled in - * if needed. - * @param aParentFrame the nearest ancestor frame, used inernally for - * finding ::after / ::before frames + * @param aTargetContentDisplay the CSS display enum for the content aIter + * points to if already known, UNSET_DISPLAY otherwise. It will be + * filled in if needed. */ - nsIFrame* FindNextSibling(mozilla::dom::FlattenedChildIterator aIter, - nsIContent* aTargetContent, - mozilla::StyleDisplay& aTargetContentDisplay, - nsContainerFrame* aParentFrame); + template<SiblingDirection> + nsIFrame* FindSibling(const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::StyleDisplay& aTargetContentDisplay); + + // Helper for the implementation of FindSibling. + template<SiblingDirection> + nsIFrame* FindSiblingInternal( + mozilla::dom::FlattenedChildIterator, + nsIContent* aTargetContent, + mozilla::StyleDisplay& aTargetContentDisplay); + + // An alias of FindSibling<SiblingDirection::Forward>. + nsIFrame* FindNextSibling(const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::StyleDisplay& aTargetContentDisplay); + // An alias of FindSibling<SiblingDirection::Backwards>. + nsIFrame* FindPreviousSibling(const mozilla::dom::FlattenedChildIterator& aIter, + mozilla::StyleDisplay& aTargetContentDisplay); + + // Given a potential first-continuation sibling frame for aTargetContent, + // verify that it is an actual valid sibling for it, and return the + // appropriate continuation the new frame for aTargetContent should be + // inserted next to. + nsIFrame* AdjustSiblingFrame(nsIFrame* aSibling, + nsIContent* aTargetContent, + mozilla::StyleDisplay& aTargetContentDisplay, + SiblingDirection aDirection); // Find the right previous sibling for an insertion. This also updates the // parent frame to point to the correct continuation of the parent frame to diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 97b022da8..be8cd7147 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -38,47 +38,17 @@ #include "nsIStatefulFrame.h" #include "nsContainerFrame.h" - #ifdef DEBUG - //#define DEBUG_UNDISPLAYED_MAP - //#define DEBUG_DISPLAY_CONTENTS_MAP - #else - #undef DEBUG_UNDISPLAYED_MAP - #undef DEBUG_DISPLAY_CONTENTS_MAP - #endif +// #define DEBUG_UNDISPLAYED_MAP +// #define DEBUG_DISPLAY_CONTENTS_MAP using namespace mozilla; using namespace mozilla::dom; //---------------------------------------------------------------------- -struct PlaceholderMapEntry : public PLDHashEntryHdr { - // key (the out of flow frame) can be obtained through placeholder frame - nsPlaceholderFrame *placeholderFrame; -}; - -static bool -PlaceholderMapMatchEntry(const PLDHashEntryHdr *hdr, const void *key) -{ - const PlaceholderMapEntry *entry = - static_cast<const PlaceholderMapEntry*>(hdr); - NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() != - (void*)0xdddddddd, - "Dead placeholder in placeholder map"); - return entry->placeholderFrame->GetOutOfFlowFrame() == key; -} - -static const PLDHashTableOps PlaceholderMapOps = { - PLDHashTable::HashVoidPtrKeyStub, - PlaceholderMapMatchEntry, - PLDHashTable::MoveEntryStub, - PLDHashTable::ClearEntryStub, - nullptr -}; - nsFrameManagerBase::nsFrameManagerBase() : mPresShell(nullptr) , mRootFrame(nullptr) - , mPlaceholderMap(&PlaceholderMapOps, sizeof(PlaceholderMapEntry)) , mUndisplayedMap(nullptr) , mDisplayContentsMap(nullptr) , mIsDestroyingFrames(false) @@ -87,40 +57,49 @@ nsFrameManagerBase::nsFrameManagerBase() //---------------------------------------------------------------------- -// XXXldb This seems too complicated for what I think it's doing, and it -// should also be using PLDHashTable rather than plhash to use less memory. +/** + * The undisplayed map is a class that maps a parent content node to the + * undisplayed content children, and their style contexts. + * + * The linked list of nodes holds strong references to the style contexts and + * the content. + */ +class nsFrameManagerBase::UndisplayedMap : + private nsClassHashtable<nsPtrHashKey<nsIContent>, + LinkedList<UndisplayedNode>> +{ + typedef nsClassHashtable<nsPtrHashKey<nsIContent>, LinkedList<UndisplayedNode>> base_type; -class nsFrameManagerBase::UndisplayedMap { public: - explicit UndisplayedMap(uint32_t aNumBuckets = 16); - ~UndisplayedMap(void); + UndisplayedMap(); + ~UndisplayedMap(); UndisplayedNode* GetFirstNode(nsIContent* aParentContent); - nsresult AddNodeFor(nsIContent* aParentContent, - nsIContent* aChild, nsStyleContext* aStyle); + void AddNodeFor(nsIContent* aParentContent, + nsIContent* aChild, + nsStyleContext* aStyle); - void RemoveNodeFor(nsIContent* aParentContent, - UndisplayedNode* aNode); + void RemoveNodeFor(nsIContent* aParentContent, UndisplayedNode* aNode); void RemoveNodesFor(nsIContent* aParentContent); - UndisplayedNode* UnlinkNodesFor(nsIContent* aParentContent); + + nsAutoPtr<LinkedList<UndisplayedNode>> + UnlinkNodesFor(nsIContent* aParentContent); // Removes all entries from the hash table - void Clear(void); + void Clear(); protected: + LinkedList<UndisplayedNode>* GetListFor(nsIContent* aParentContent); + LinkedList<UndisplayedNode>* GetOrCreateListFor(nsIContent* aParentContent); + void AppendNodeFor(UndisplayedNode* aNode, nsIContent* aParentContent); /** - * Gets the entry for the provided parent content. If the content - * is a <xbl:children> element, |**aParentContent| is set to - * the parent of the children element. + * Get the applicable parent for the map lookup. This is almost always the + * provided argument, except if it's a <xbl:children> element, in which case + * it's the parent of the children element. */ - PLHashEntry** GetEntryFor(nsIContent** aParentContent); - void AppendNodeFor(UndisplayedNode* aNode, - nsIContent* aParentContent); - - PLHashTable* mTable; - PLHashEntry** mLastLookup; + nsIContent* GetApplicableParent(nsIContent* aParent); }; //---------------------------------------------------------------------- @@ -138,14 +117,10 @@ nsFrameManager::Destroy() // Destroy the frame hierarchy. mPresShell->SetIgnoreFrameDestruction(true); - // Unregister all placeholders before tearing down the frame tree - nsFrameManager::ClearPlaceholderFrameMap(); - if (mRootFrame) { mRootFrame->Destroy(); mRootFrame = nullptr; } - delete mUndisplayedMap; mUndisplayedMap = nullptr; delete mDisplayContentsMap; @@ -156,58 +131,9 @@ nsFrameManager::Destroy() //---------------------------------------------------------------------- -// Placeholder frame functions -nsPlaceholderFrame* -nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame) -{ - NS_PRECONDITION(aFrame, "null param unexpected"); - - auto entry = static_cast<PlaceholderMapEntry*> - (const_cast<PLDHashTable*>(&mPlaceholderMap)->Search(aFrame)); - if (entry) { - return entry->placeholderFrame; - } - - return nullptr; -} - -void -nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) -{ - MOZ_ASSERT(aPlaceholderFrame, "null param unexpected"); - MOZ_ASSERT(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), - "unexpected frame type"); - auto entry = static_cast<PlaceholderMapEntry*> - (mPlaceholderMap.Add(aPlaceholderFrame->GetOutOfFlowFrame())); - MOZ_ASSERT(!entry->placeholderFrame, - "Registering a placeholder for a frame that already has a placeholder!"); - entry->placeholderFrame = aPlaceholderFrame; -} - -void -nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) -{ - NS_PRECONDITION(aPlaceholderFrame, "null param unexpected"); - NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(), - "unexpected frame type"); - - mPlaceholderMap.Remove(aPlaceholderFrame->GetOutOfFlowFrame()); -} - -void -nsFrameManager::ClearPlaceholderFrameMap() -{ - for (auto iter = mPlaceholderMap.Iter(); !iter.Done(); iter.Next()) { - auto entry = static_cast<PlaceholderMapEntry*>(iter.Get()); - entry->placeholderFrame->SetOutOfFlowFrame(nullptr); - } - mPlaceholderMap.Clear(); -} - -//---------------------------------------------------------------------- - /* static */ nsStyleContext* -nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent) +nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, + const nsIContent* aContent) { if (!aContent) { return nullptr; @@ -215,7 +141,7 @@ nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent) nsIContent* parent = aContent->GetParentElementCrossingShadowRoot(); MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements"); for (UndisplayedNode* node = aMap->GetFirstNode(parent); - node; node = node->mNext) { + node; node = node->getNext()) { if (node->mContent == aContent) return node->mStyle; } @@ -241,16 +167,16 @@ nsFrameManager::SetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent, nsStyleContext* aStyleContext) { - NS_PRECONDITION(!aStyleContext->GetPseudo(), - "Should only have actual elements here"); + MOZ_ASSERT(!aStyleContext->GetPseudo(), + "Should only have actual elements here"); #if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP) static int i = 0; printf("SetStyleContextInMap(%d): p=%p \n", i++, (void *)aContent); #endif - NS_ASSERTION(!GetStyleContextInMap(aMap, aContent), - "Already have an entry for aContent"); + MOZ_ASSERT(!GetStyleContextInMap(aMap, aContent), + "Already have an entry for aContent"); nsIContent* parent = aContent->GetParentElementCrossingShadowRoot(); MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements"); @@ -287,7 +213,7 @@ nsFrameManager::ChangeStyleContextInMap(UndisplayedMap* aMap, #endif for (UndisplayedNode* node = aMap->GetFirstNode(aContent->GetParent()); - node; node = node->mNext) { + node; node = node->getNext()) { if (node->mContent == aContent) { node->mStyle = aStyleContext; return; @@ -306,25 +232,25 @@ nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent, printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent); #endif - if (mUndisplayedMap) { - UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent); - while (node) { - if (node->mContent == aContent) { - mUndisplayedMap->RemoveNodeFor(aParentContent, node); + if (!mUndisplayedMap) { + return; + } + + for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent); + node; node = node->getNext()) { + if (node->mContent == aContent) { + mUndisplayedMap->RemoveNodeFor(aParentContent, node); #ifdef DEBUG_UNDISPLAYED_MAP - printf( "REMOVED!\n"); + printf( "REMOVED!\n"); #endif -#ifdef DEBUG - // make sure that there are no more entries for the same content - nsStyleContext *context = GetUndisplayedContent(aContent); - NS_ASSERTION(context == nullptr, "Found more undisplayed content data after removal"); -#endif - return; - } - node = node->mNext; + // make sure that there are no more entries for the same content + MOZ_ASSERT(!GetUndisplayedContent(aContent), + "Found more undisplayed content data after removal"); + return; } } + #ifdef DEBUG_UNDISPLAYED_MAP printf( "not found.\n"); #endif @@ -380,26 +306,25 @@ nsFrameManager::ClearDisplayContentsIn(nsIContent* aContent, static int i = 0; printf("ClearDisplayContents(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent); #endif - - if (mDisplayContentsMap) { - UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent); - while (node) { - if (node->mContent == aContent) { - mDisplayContentsMap->RemoveNodeFor(aParentContent, node); + + if (!mDisplayContentsMap) { + return; + } + + for (UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent); + node; node = node->getNext()) { + if (node->mContent == aContent) { + mDisplayContentsMap->RemoveNodeFor(aParentContent, node); #ifdef DEBUG_DISPLAY_CONTENTS_MAP - printf( "REMOVED!\n"); -#endif -#ifdef DEBUG - // make sure that there are no more entries for the same content - nsStyleContext* context = GetDisplayContentsStyleFor(aContent); - NS_ASSERTION(context == nullptr, "Found more entries for aContent after removal"); + printf( "REMOVED!\n"); #endif - ClearAllDisplayContentsIn(aContent); - ClearAllUndisplayedContentIn(aContent); - return; - } - node = node->mNext; + // make sure that there are no more entries for the same content + MOZ_ASSERT(!GetDisplayContentsStyleFor(aContent), + "Found more entries for aContent after removal"); + ClearAllDisplayContentsIn(aContent); + ClearAllUndisplayedContentIn(aContent); + return; } } #ifdef DEBUG_DISPLAY_CONTENTS_MAP @@ -416,14 +341,14 @@ nsFrameManager::ClearAllDisplayContentsIn(nsIContent* aParentContent) #endif if (mDisplayContentsMap) { - UndisplayedNode* cur = mDisplayContentsMap->UnlinkNodesFor(aParentContent); - while (cur) { - UndisplayedNode* next = cur->mNext; - cur->mNext = nullptr; - ClearAllDisplayContentsIn(cur->mContent); - ClearAllUndisplayedContentIn(cur->mContent); - delete cur; - cur = next; + nsAutoPtr<LinkedList<UndisplayedNode>> list = + mDisplayContentsMap->UnlinkNodesFor(aParentContent); + if (list) { + while (UndisplayedNode* node = list->popFirst()) { + ClearAllDisplayContentsIn(node->mContent); + ClearAllUndisplayedContentIn(node->mContent); + delete node; + } } } @@ -495,7 +420,7 @@ nsFrameManager::RemoveFrame(ChildListID aListID, aOldFrame->GetType() == nsGkAtoms::textFrame, "Must remove first continuation."); NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW && - GetPlaceholderFrameFor(aOldFrame)), + aOldFrame->GetPlaceholderFrame()), "Must call RemoveFrame on placeholder for out-of-flows."); nsContainerFrame* parentFrame = aOldFrame->GetParent(); if (parentFrame->IsAbsoluteContainer() && @@ -654,182 +579,132 @@ nsFrameManager::RestoreFrameState(nsIFrame* aFrame, //---------------------------------------------------------------------- -static PLHashNumber -HashKey(void* key) -{ - return NS_PTR_TO_INT32(key); -} - -static int -CompareKeys(void* key1, void* key2) -{ - return key1 == key2; -} - -//---------------------------------------------------------------------- - -nsFrameManagerBase::UndisplayedMap::UndisplayedMap(uint32_t aNumBuckets) +nsFrameManagerBase::UndisplayedMap::UndisplayedMap() { MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap); - mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey, - (PLHashComparator)CompareKeys, - (PLHashComparator)nullptr, - nullptr, nullptr); - mLastLookup = nullptr; } nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void) { MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap); Clear(); - PL_HashTableDestroy(mTable); } -PLHashEntry** -nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent) +void +nsFrameManagerBase::UndisplayedMap::Clear() { - nsIContent* parentContent = *aParentContent; - - if (mLastLookup && (parentContent == (*mLastLookup)->key)) { - return mLastLookup; + for (auto iter = Iter(); !iter.Done(); iter.Next()) { + auto* list = iter.UserData(); + while (auto* node = list->popFirst()) { + delete node; + } + iter.Remove(); } +} +nsIContent* +nsFrameManagerBase::UndisplayedMap::GetApplicableParent(nsIContent* aParent) +{ // In the case of XBL default content, <xbl:children> elements do not get a // frame causing a mismatch between the content tree and the frame tree. // |GetEntryFor| is sometimes called with the content tree parent (which may // be a <xbl:children> element) but the parent in the frame tree would be the // insertion parent (parent of the <xbl:children> element). Here the children // elements are normalized to the insertion parent to correct for the mismatch. - if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) { - parentContent = parentContent->GetParent(); - // Change the caller's pointer for the parent content to be the insertion parent. - *aParentContent = parentContent; + if (aParent && aParent->IsActiveChildrenElement()) { + return aParent->GetParent(); } - PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent); - PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent); - if (*entry) { - mLastLookup = entry; - } - return entry; + return aParent; } -UndisplayedNode* -nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent) +LinkedList<UndisplayedNode>* +nsFrameManagerBase::UndisplayedMap::GetListFor(nsIContent* aParent) { - PLHashEntry** entry = GetEntryFor(&aParentContent); - if (*entry) { - return (UndisplayedNode*)((*entry)->value); + aParent = GetApplicableParent(aParent); + + LinkedList<UndisplayedNode>* list; + if (Get(aParent, &list)) { + return list; } + return nullptr; } +LinkedList<UndisplayedNode>* +nsFrameManagerBase::UndisplayedMap::GetOrCreateListFor(nsIContent* aParent) +{ + aParent = GetApplicableParent(aParent); + return LookupOrAdd(aParent); +} + + +UndisplayedNode* +nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent) +{ + auto* list = GetListFor(aParentContent); + return list ? list->getFirst() : nullptr; +} + void nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode, nsIContent* aParentContent) { - PLHashEntry** entry = GetEntryFor(&aParentContent); - if (*entry) { - UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); - while (node->mNext) { - if (node->mContent == aNode->mContent) { - // We actually need to check this in optimized builds because - // there are some callers that do this. See bug 118014, bug - // 136704, etc. - NS_NOTREACHED("node in map twice"); - delete aNode; - return; - } - node = node->mNext; - } - node->mNext = aNode; - } - else { - PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent); - PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode); - mLastLookup = nullptr; // hashtable may have shifted bucket out from under us + LinkedList<UndisplayedNode>* list = GetOrCreateListFor(aParentContent); + +#ifdef DEBUG + for (UndisplayedNode* node = list->getFirst(); node; node = node->getNext()) { + // NOTE: In the original code there was a work around for this case, I want + // to check it still happens before hacking around it the same way. + MOZ_ASSERT(node->mContent != aNode->mContent, + "Duplicated content in undisplayed list!"); } +#endif + + list->insertBack(aNode); } -nsresult +void nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent, - nsIContent* aChild, + nsIContent* aChild, nsStyleContext* aStyle) { UndisplayedNode* node = new UndisplayedNode(aChild, aStyle); - AppendNodeFor(node, aParentContent); - return NS_OK; } void nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent, UndisplayedNode* aNode) { - PLHashEntry** entry = GetEntryFor(&aParentContent); - NS_ASSERTION(*entry, "content not in map"); - if (*entry) { - if ((UndisplayedNode*)((*entry)->value) == aNode) { // first node - if (aNode->mNext) { - (*entry)->value = aNode->mNext; - aNode->mNext = nullptr; - } - else { - PL_HashTableRawRemove(mTable, entry, *entry); - mLastLookup = nullptr; // hashtable may have shifted bucket out from under us - } - } - else { - UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); - while (node->mNext) { - if (node->mNext == aNode) { - node->mNext = aNode->mNext; - aNode->mNext = nullptr; - break; - } - node = node->mNext; - } - } - } +#ifdef DEBUG + auto list = GetListFor(aParentContent); + MOZ_ASSERT(list, "content not in map"); + aNode->removeFrom(*list); +#else + aNode->remove(); +#endif delete aNode; } -UndisplayedNode* +nsAutoPtr<LinkedList<UndisplayedNode>> nsFrameManagerBase::UndisplayedMap::UnlinkNodesFor(nsIContent* aParentContent) { - PLHashEntry** entry = GetEntryFor(&aParentContent); - NS_ASSERTION(entry, "content not in map"); - if (*entry) { - UndisplayedNode* node = (UndisplayedNode*)((*entry)->value); - NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap"); - PL_HashTableRawRemove(mTable, entry, *entry); - mLastLookup = nullptr; // hashtable may have shifted bucket out from under us - return node; - } - return nullptr; + nsAutoPtr<LinkedList<UndisplayedNode>> list; + RemoveAndForget(GetApplicableParent(aParentContent), list); + return list; } void nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent) { - delete UnlinkNodesFor(aParentContent); -} - -static int -RemoveUndisplayedEntry(PLHashEntry* he, int i, void* arg) -{ - UndisplayedNode* node = (UndisplayedNode*)(he->value); - delete node; - // Remove and free this entry and continue enumerating - return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT; -} - -void -nsFrameManagerBase::UndisplayedMap::Clear(void) -{ - mLastLookup = nullptr; - PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0); + nsAutoPtr<LinkedList<UndisplayedNode>> list = UnlinkNodesFor(aParentContent); + if (list) { + while (auto* node = list->popFirst()) { + delete node; + } + } } uint32_t nsFrameManagerBase::sGlobalGenerationNumber; diff --git a/layout/base/nsFrameManager.h b/layout/base/nsFrameManager.h index 1b9148314..ae7477d3d 100644 --- a/layout/base/nsFrameManager.h +++ b/layout/base/nsFrameManager.h @@ -33,40 +33,25 @@ namespace mozilla { * Node in a linked list, containing the style for an element that * does not have a frame but whose parent does have a frame. */ -struct UndisplayedNode { +struct UndisplayedNode : public LinkedListElement<UndisplayedNode> +{ UndisplayedNode(nsIContent* aContent, nsStyleContext* aStyle) - : mContent(aContent), - mStyle(aStyle), - mNext(nullptr) + : mContent(aContent) + , mStyle(aStyle) { MOZ_COUNT_CTOR(mozilla::UndisplayedNode); } - ~UndisplayedNode() - { - MOZ_COUNT_DTOR(mozilla::UndisplayedNode); - - // Delete mNext iteratively to avoid blowing up the stack (bug 460461). - UndisplayedNode* cur = mNext; - while (cur) { - UndisplayedNode* next = cur->mNext; - cur->mNext = nullptr; - delete cur; - cur = next; - } - } + ~UndisplayedNode() { MOZ_COUNT_DTOR(mozilla::UndisplayedNode); } - nsCOMPtr<nsIContent> mContent; - RefPtr<nsStyleContext> mStyle; - UndisplayedNode* mNext; + nsCOMPtr<nsIContent> mContent; + RefPtr<nsStyleContext> mStyle; }; } // namespace mozilla /** - * Frame manager interface. The frame manager serves two purposes: - * <li>provides a service for mapping from content to frame and from - * out-of-flow frame to placeholder frame. + * Frame manager interface. The frame manager serves one purpose: * <li>handles structural modifications to the frame model. If the frame model * lock can be acquired, then the changes are processed immediately; otherwise, * they're queued and processed later. @@ -94,15 +79,8 @@ public: */ void Destroy(); - // Placeholder frame functions - nsPlaceholderFrame* GetPlaceholderFrameFor(const nsIFrame* aFrame); - void RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame); - void UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame); - - void ClearPlaceholderFrameMap(); - // Mapping undisplayed content - nsStyleContext* GetUndisplayedContent(nsIContent* aContent) + nsStyleContext* GetUndisplayedContent(const nsIContent* aContent) { if (!mUndisplayedMap) { return nullptr; @@ -127,7 +105,7 @@ public: /** * Return the registered display:contents style context for aContent, if any. */ - nsStyleContext* GetDisplayContentsStyleFor(nsIContent* aContent) + nsStyleContext* GetDisplayContentsStyleFor(const nsIContent* aContent) { if (!mDisplayContentsMap) { return nullptr; @@ -207,7 +185,7 @@ public: nsILayoutHistoryState* aState); protected: static nsStyleContext* GetStyleContextInMap(UndisplayedMap* aMap, - nsIContent* aContent); + const nsIContent* aContent); static mozilla::UndisplayedNode* GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap, nsIContent* aParentContent); diff --git a/layout/base/nsFrameManagerBase.h b/layout/base/nsFrameManagerBase.h index 757dce7e5..bb192b64f 100644 --- a/layout/base/nsFrameManagerBase.h +++ b/layout/base/nsFrameManagerBase.h @@ -53,7 +53,6 @@ protected: // weak link, because the pres shell owns us nsIPresShell* MOZ_NON_OWNING_REF mPresShell; nsIFrame* mRootFrame; - PLDHashTable mPlaceholderMap; UndisplayedMap* mUndisplayedMap; UndisplayedMap* mDisplayContentsMap; bool mIsDestroyingFrames; // The frame manager is destroying some frame(s). diff --git a/layout/base/nsFrameTraversal.cpp b/layout/base/nsFrameTraversal.cpp index 40fb5ff30..76dd40af1 100644 --- a/layout/base/nsFrameTraversal.cpp +++ b/layout/base/nsFrameTraversal.cpp @@ -82,6 +82,10 @@ protected: virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame); virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame); + /** + * Return the placeholder frame for aFrame if it has one, otherwise return + * aFrame itself. + */ nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame); bool IsPopupFrame(nsIFrame* aFrame); @@ -486,18 +490,11 @@ nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) { nsIFrame* nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame) { - nsIFrame* result = aFrame; - nsIPresShell *presShell = mPresContext->GetPresShell(); - if (presShell) { - nsIFrame* placeholder = presShell->GetPlaceholderFrameFor(aFrame); - if (placeholder) - result = placeholder; + if (MOZ_LIKELY(!aFrame || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) { + return aFrame; } - - if (result != aFrame) - result = GetPlaceholderFrame(result); - - return result; + nsIFrame* placeholder = aFrame->GetPlaceholderFrame(); + return placeholder ? placeholder : aFrame; } bool diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 865f5534c..5f83b9c15 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -486,12 +486,6 @@ public: virtual nsCanvasFrame* GetCanvasFrame() const = 0; /** - * Gets the placeholder frame associated with the specified frame. This is - * a helper frame that forwards the request to the frame manager. - */ - virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const = 0; - - /** * Tell the pres shell that a frame needs to be marked dirty and needs * Reflow. It's OK if this is an ancestor of the frame needing reflow as * long as the ancestor chain between them doesn't cross a reflow root. @@ -545,20 +539,12 @@ public: virtual void NotifyCounterStylesAreDirty() = 0; /** - * Destroy the frames for aContent. Note that this may destroy frames - * for an ancestor instead - aDestroyedFramesFor contains the content node - * where frames were actually destroyed (which should be used in the - * CreateFramesFor call). The frame tree state will be captured before - * the frames are destroyed in the frame constructor. - */ - virtual void DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor) = 0; - /** - * Create new frames for aContent. It will use the last captured layout - * history state captured in the frame constructor to restore the state - * in the new frame tree. + * Destroy the frames for aElement, and reconstruct them asynchronously if + * needed. + * + * Note that this may destroy frames for an ancestor instead. */ - virtual void CreateFramesFor(nsIContent* aContent) = 0; + virtual void DestroyFramesForAndRestyle(mozilla::dom::Element* aElement) = 0; /** * Recreates the frames for a node @@ -977,7 +963,7 @@ public: /** * Reconstruct frames for all elements in the document */ - virtual nsresult ReconstructFrames() = 0; + virtual void ReconstructFrames() = 0; /** * Notify that a content node's state has changed diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 21d20c69f..7496a4946 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1486,90 +1486,38 @@ nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) return id; } -/*static*/ nsIFrame* -nsLayoutUtils::GetBeforeFrameForContent(nsIFrame* aFrame, - const nsIContent* aContent) -{ - // We need to call GetGenConPseudos() on the first continuation/ib-split. - // Find it, for symmetry with GetAfterFrameForContent. - nsContainerFrame* genConParentFrame = - FirstContinuationOrIBSplitSibling(aFrame)->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsTArray<nsIContent*>* prop = genConParentFrame->GetGenConPseudos(); - if (prop) { - const nsTArray<nsIContent*>& pseudos(*prop); - for (uint32_t i = 0; i < pseudos.Length(); ++i) { - if (pseudos[i]->GetParent() == aContent && - pseudos[i]->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentbefore) { - return pseudos[i]->GetPrimaryFrame(); - } - } - } - // If the first child frame is a pseudo-frame, then try that. - // Note that the frame we create for the generated content is also a - // pseudo-frame and so don't drill down in that case. - nsIFrame* childFrame = genConParentFrame->PrincipalChildList().FirstChild(); - if (childFrame && - childFrame->IsPseudoFrame(aContent) && - !childFrame->IsGeneratedContentFrame()) { - return GetBeforeFrameForContent(childFrame, aContent); - } - return nullptr; +static Element* +GetPseudo(const nsIContent* aContent, nsIAtom* aPseudoProperty) +{ + MOZ_ASSERT(aPseudoProperty == nsGkAtoms::beforePseudoProperty || + aPseudoProperty == nsGkAtoms::afterPseudoProperty); + return static_cast<Element*>(aContent->GetProperty(aPseudoProperty)); } -/*static*/ nsIFrame* -nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame) +/*static*/ Element* +nsLayoutUtils::GetBeforePseudo(const nsIContent* aContent) { - return GetBeforeFrameForContent(aFrame, aFrame->GetContent()); + return GetPseudo(aContent, nsGkAtoms::beforePseudoProperty); } /*static*/ nsIFrame* -nsLayoutUtils::GetAfterFrameForContent(nsIFrame* aFrame, - const nsIContent* aContent) -{ - // We need to call GetGenConPseudos() on the first continuation, - // but callers are likely to pass the last. - nsContainerFrame* genConParentFrame = - FirstContinuationOrIBSplitSibling(aFrame)->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsTArray<nsIContent*>* prop = genConParentFrame->GetGenConPseudos(); - if (prop) { - const nsTArray<nsIContent*>& pseudos(*prop); - for (uint32_t i = 0; i < pseudos.Length(); ++i) { - if (pseudos[i]->GetParent() == aContent && - pseudos[i]->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentafter) { - return pseudos[i]->GetPrimaryFrame(); - } - } - } - // If the last child frame is a pseudo-frame, then try that. - // Note that the frame we create for the generated content is also a - // pseudo-frame and so don't drill down in that case. - genConParentFrame = aFrame->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsIFrame* lastParentContinuation = - LastContinuationWithChild(static_cast<nsContainerFrame*>( - LastContinuationOrIBSplitSibling(genConParentFrame))); - nsIFrame* childFrame = - lastParentContinuation->GetChildList(nsIFrame::kPrincipalList).LastChild(); - if (childFrame && - childFrame->IsPseudoFrame(aContent) && - !childFrame->IsGeneratedContentFrame()) { - return GetAfterFrameForContent(childFrame->FirstContinuation(), aContent); - } - return nullptr; +nsLayoutUtils::GetBeforeFrame(const nsIContent* aContent) +{ + Element* pseudo = GetBeforePseudo(aContent); + return pseudo ? pseudo->GetPrimaryFrame() : nullptr; +} + +/*static*/ Element* +nsLayoutUtils::GetAfterPseudo(const nsIContent* aContent) +{ + return GetPseudo(aContent, nsGkAtoms::afterPseudoProperty); } /*static*/ nsIFrame* -nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame) +nsLayoutUtils::GetAfterFrame(const nsIContent* aContent) { - return GetAfterFrameForContent(aFrame, aFrame->GetContent()); + Element* pseudo = GetAfterPseudo(aContent); + return pseudo ? pseudo->GetPrimaryFrame() : nullptr; } // static @@ -1640,33 +1588,6 @@ nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) { } // static -bool -nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent, - nsIFrame* aFrame, - nsIAtom* aPseudoElement) -{ - NS_PRECONDITION(aFrame, "Must have a frame"); - NS_PRECONDITION(aPseudoElement, "Must have a pseudo name"); - - if (!aFrame->IsGeneratedContentFrame()) { - return false; - } - nsIFrame* parent = aFrame->GetParent(); - NS_ASSERTION(parent, "Generated content can't be root frame"); - if (parent->IsGeneratedContentFrame()) { - // Not the root of the generated content - return false; - } - - if (aContent && parent->GetContent() != aContent) { - return false; - } - - return (aFrame->GetContent()->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentbefore) == - (aPseudoElement == nsCSSPseudoElements::before); -} - -// static nsIFrame* nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame, nsPoint* aExtraOffset) @@ -4400,8 +4321,7 @@ nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame* aFrame) { if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && !aFrame->GetPrevInFlow()) { - return aFrame->PresContext()->PresShell()->FrameManager()-> - GetPlaceholderFrameFor(aFrame); + return aFrame->GetProperty(nsIFrame::PlaceholderFrameProperty()); } return aFrame->GetParent(); } diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index bba1f3265..36a43e46b 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -276,50 +276,26 @@ public: static mozilla::layout::FrameChildListID GetChildListNameFor(nsIFrame* aChildFrame); /** - * GetBeforeFrameForContent returns the ::before frame for aContent, if - * one exists. This is typically O(1). The frame passed in must be - * the first-in-flow. - * - * @param aGenConParentFrame an ancestor of the ::before frame - * @param aContent the content whose ::before is wanted - * @return the ::before frame or nullptr if there isn't one + * Returns the ::before pseudo-element for aContent, if any. */ - static nsIFrame* GetBeforeFrameForContent(nsIFrame* aGenConParentFrame, - const nsIContent* aContent); + static mozilla::dom::Element* GetBeforePseudo(const nsIContent* aContent); /** - * GetBeforeFrame returns the outermost ::before frame of the given frame, if - * one exists. This is typically O(1). The frame passed in must be - * the first-in-flow. - * - * @param aFrame the frame whose ::before is wanted - * @return the :before frame or nullptr if there isn't one + * Returns the frame corresponding to the ::before pseudo-element for + * aContent, if any. */ - static nsIFrame* GetBeforeFrame(nsIFrame* aFrame); + static nsIFrame* GetBeforeFrame(const nsIContent* aContent); /** - * GetAfterFrameForContent returns the ::after frame for aContent, if one - * exists. This will walk the in-flow chain of aGenConParentFrame to the - * last-in-flow if needed. This function is typically O(N) in the number - * of child frames, following in-flows, etc. - * - * @param aGenConParentFrame an ancestor of the ::after frame - * @param aContent the content whose ::after is wanted - * @return the ::after frame or nullptr if there isn't one + * Returns the ::after pseudo-element for aContent, if any. */ - static nsIFrame* GetAfterFrameForContent(nsIFrame* aGenConParentFrame, - const nsIContent* aContent); + static mozilla::dom::Element* GetAfterPseudo(const nsIContent* aContent); /** - * GetAfterFrame returns the outermost ::after frame of the given frame, if one - * exists. This will walk the in-flow chain to the last-in-flow if - * needed. This function is typically O(N) in the number of child - * frames, following in-flows, etc. - * - * @param aFrame the frame whose ::after is wanted - * @return the :after frame or nullptr if there isn't one + * Returns the frame corresponding to the ::after pseudo-element for aContent, + * if any. */ - static nsIFrame* GetAfterFrame(nsIFrame* aFrame); + static nsIFrame* GetAfterFrame(const nsIContent* aContent); /** * Given a frame, search up the frame tree until we find an @@ -377,23 +353,6 @@ public: */ static nsIFrame* GetRealPrimaryFrameFor(const nsIContent* aContent); - /** - * IsGeneratedContentFor returns true if aFrame is the outermost - * frame for generated content of type aPseudoElement for aContent. - * aFrame *might not* have the aPseudoElement pseudo-style! For example - * it might be a table wrapper frame and the inner table frame might - * have the pseudo-style. - * - * @param aContent the content node we're looking at. If this is - * null, then we just assume that aFrame has the right content - * pointer. - * @param aFrame the frame we're looking at - * @param aPseudoElement the pseudo type we're interested in - * @return whether aFrame is the generated aPseudoElement frame for aContent - */ - static bool IsGeneratedContentFor(nsIContent* aContent, nsIFrame* aFrame, - nsIAtom* aPseudoElement); - #ifdef DEBUG // TODO: remove, see bug 598468. static bool gPreventAssertInCompareTreePosition; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 264b52b18..bd5125637 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -2828,10 +2828,9 @@ PresShell::CancelAllPendingReflows() } void -PresShell::DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor) +PresShell::DestroyFramesForAndRestyle(Element* aElement) { - MOZ_ASSERT(aContent); + MOZ_ASSERT(aElement); NS_ENSURE_TRUE_VOID(mPresContext); if (!mDidInitialize) { return; @@ -2843,43 +2842,20 @@ PresShell::DestroyFramesFor(nsIContent* aContent, ++mChangeNestCount; nsCSSFrameConstructor* fc = FrameConstructor(); + bool didReconstruct; fc->BeginUpdate(); - fc->DestroyFramesFor(aContent, aDestroyedFramesFor); + fc->DestroyFramesFor(aElement, &didReconstruct); fc->EndUpdate(); - --mChangeNestCount; -} + auto changeHint = didReconstruct + ? nsChangeHint(0) + : nsChangeHint_ReconstructFrame; -void -PresShell::CreateFramesFor(nsIContent* aContent) -{ - NS_ENSURE_TRUE_VOID(mPresContext); - if (!mDidInitialize) { - // Nothing to do here. In fact, if we proceed and aContent is the - // root we will crash. - return; - } - - // Don't call RecreateFramesForContent since that is not exported and we want - // to keep the number of entrypoints down. - - NS_ASSERTION(mViewManager, "Should have view manager"); - MOZ_ASSERT(aContent); - - // Have to make sure that the content notifications are flushed before we - // start messing with the frame model; otherwise we can get content doubling. - mDocument->FlushPendingNotifications(Flush_ContentAndNotify); - - nsAutoScriptBlocker scriptBlocker; - - // Mark ourselves as not safe to flush while we're doing frame construction. - ++mChangeNestCount; - - nsCSSFrameConstructor* fc = FrameConstructor(); - nsILayoutHistoryState* layoutState = fc->GetLastCapturedLayoutHistoryState(); - fc->BeginUpdate(); - fc->ContentInserted(aContent->GetParent(), aContent, layoutState, false); - fc->EndUpdate(); + // NOTE(emilio): eRestyle_Subtree is needed to force also a full subtree + // restyle for the content (in Stylo, where the existence of frames != the + // existence of styles). + mPresContext->RestyleManager()->PostRestyleEvent( + aElement, eRestyle_Subtree, changeHint); --mChangeNestCount; } @@ -4444,14 +4420,14 @@ PresShell::NotifyCounterStylesAreDirty() mFrameConstructor->EndUpdate(); } -nsresult -PresShell::ReconstructFrames(void) +void +PresShell::ReconstructFrames() { NS_PRECONDITION(!mFrameConstructor->GetRootFrame() || mDidInitialize, "Must not have root frame before initial reflow"); if (!mDidInitialize || mIsDestroying) { // Nothing to do here - return NS_OK; + return; } nsCOMPtr<nsIPresShell> kungFuDeathGrip(this); @@ -4461,16 +4437,14 @@ PresShell::ReconstructFrames(void) mDocument->FlushPendingNotifications(Flush_ContentAndNotify); if (mIsDestroying) { - return NS_OK; + return; } nsAutoCauseReflowNotifier crNotifier(this); mFrameConstructor->BeginUpdate(); - nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy(); + mFrameConstructor->ReconstructDocElementHierarchy(); VERIFY_STYLE_TREE; mFrameConstructor->EndUpdate(); - - return rv; } void @@ -4598,12 +4572,6 @@ PresShell::StyleRuleRemoved(StyleSheet* aStyleSheet) RecordStyleSheetChange(aStyleSheet); } -nsIFrame* -PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const -{ - return mFrameConstructor->GetPlaceholderFrameFor(aFrame); -} - nsresult PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags, nscolor aBackgroundColor, @@ -4755,9 +4723,11 @@ PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder, frame->GetOffsets(frameStartOffset, frameEndOffset); int32_t hilightStart = - atStart ? std::max(aRange->StartOffset(), frameStartOffset) : frameStartOffset; + atStart ? std::max(static_cast<int32_t>(aRange->StartOffset()), + frameStartOffset) : frameStartOffset; int32_t hilightEnd = - atEnd ? std::min(aRange->EndOffset(), frameEndOffset) : frameEndOffset; + atEnd ? std::min(static_cast<int32_t>(aRange->EndOffset()), + frameEndOffset) : frameEndOffset; if (hilightStart < hilightEnd) { // determine the location of the start and end edges of the range. nsPoint startPoint, endPoint; diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index f20370d73..628e613c8 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -130,7 +130,6 @@ public: virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override; virtual nsCanvasFrame* GetCanvasFrame() const override; - virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override; virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, nsFrameState aBitToAdd, ReflowRootHandling aRootHandling = @@ -140,9 +139,7 @@ public: virtual bool IsSafeToFlush() const override; virtual void FlushPendingNotifications(mozFlushType aType) override; virtual void FlushPendingNotifications(mozilla::ChangesToFlush aType) override; - virtual void DestroyFramesFor(nsIContent* aContent, - nsIContent** aDestroyedFramesFor) override; - virtual void CreateFramesFor(nsIContent* aContent) override; + virtual void DestroyFramesForAndRestyle(mozilla::dom::Element* aElement) override; /** * Recreates the frames for a node @@ -200,7 +197,7 @@ public: virtual void NotifyCounterStylesAreDirty() override; - virtual nsresult ReconstructFrames(void) override; + virtual void ReconstructFrames(void) override; virtual void Freeze() override; virtual void Thaw() override; virtual void FireOrClearDelayedEvents(bool aFireEvents) override; |