forked from mirrors/gecko-dev
		
	Bug 1542807 part 1 - Create generated content and use normal box construction for list-style-type/list-style-image ::markers. r=emilio
The change from 0x25FE to 0x25AA for list-style-type:square was approved here: https://github.com/w3c/csswg-drafts/issues/6200#issuecomment-828616747 Differential Revision: https://phabricator.services.mozilla.com/D111691
This commit is contained in:
		
							parent
							
								
									f62193272a
								
							
						
					
					
						commit
						a901b05850
					
				
					 21 changed files with 555 additions and 122 deletions
				
			
		|  | @ -1129,11 +1129,10 @@ LocalAccessible* nsAccessibilityService::CreateAccessible( | ||||||
|       } |       } | ||||||
|     } else if (content->IsGeneratedContentContainerForMarker()) { |     } else if (content->IsGeneratedContentContainerForMarker()) { | ||||||
|       if (aContext->IsHTMLListItem()) { |       if (aContext->IsHTMLListItem()) { | ||||||
|         const nsStyleList* styleList = frame->StyleList(); |  | ||||||
|         if (!styleList->mListStyleImage.IsNone() || |  | ||||||
|             !styleList->mCounterStyle.IsNone()) { |  | ||||||
|         newAcc = new HTMLListBulletAccessible(content, document); |         newAcc = new HTMLListBulletAccessible(content, document); | ||||||
|       } |       } | ||||||
|  |       if (aIsSubtreeHidden) { | ||||||
|  |         *aIsSubtreeHidden = true; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -422,8 +422,8 @@ uint32_t HyperTextAccessible::TransformOffset(LocalAccessible* aDescendant, | ||||||
|       // We manually set the offset so the error doesn't propagate up.
 |       // We manually set the offset so the error doesn't propagate up.
 | ||||||
|       if (offset == 0 && parent && parent->IsHTMLListItem() && |       if (offset == 0 && parent && parent->IsHTMLListItem() && | ||||||
|           descendant->LocalPrevSibling() && |           descendant->LocalPrevSibling() && | ||||||
|           descendant->LocalPrevSibling()->GetFrame() && |           descendant->LocalPrevSibling() == | ||||||
|           descendant->LocalPrevSibling()->GetFrame()->IsBulletFrame()) { |               parent->AsHTMLListItem()->Bullet()) { | ||||||
|         offset = 0; |         offset = 0; | ||||||
|       } else { |       } else { | ||||||
|         offset = (offset > 0 || descendant->IndexInParent() > 0) ? 1 : 0; |         offset = (offset > 0 || descendant->IndexInParent() > 0) ? 1 : 0; | ||||||
|  |  | ||||||
|  | @ -10,11 +10,10 @@ | ||||||
| #include "DocAccessible.h" | #include "DocAccessible.h" | ||||||
| #include "EventTree.h" | #include "EventTree.h" | ||||||
| #include "nsAccUtils.h" | #include "nsAccUtils.h" | ||||||
| #include "nsTextEquivUtils.h" | #include "nsPersistentProperties.h" | ||||||
| #include "Role.h" | #include "Role.h" | ||||||
| #include "States.h" | #include "States.h" | ||||||
| 
 | 
 | ||||||
| #include "nsBulletFrame.h" |  | ||||||
| #include "nsLayoutUtils.h" | #include "nsLayoutUtils.h" | ||||||
| 
 | 
 | ||||||
| using namespace mozilla; | using namespace mozilla; | ||||||
|  | @ -90,24 +89,7 @@ HTMLListBulletAccessible::HTMLListBulletAccessible(nsIContent* aContent, | ||||||
| // HTMLListBulletAccessible: LocalAccessible
 | // HTMLListBulletAccessible: LocalAccessible
 | ||||||
| 
 | 
 | ||||||
| ENameValueFlag HTMLListBulletAccessible::Name(nsString& aName) const { | ENameValueFlag HTMLListBulletAccessible::Name(nsString& aName) const { | ||||||
|   aName.Truncate(); |   nsLayoutUtils::GetMarkerSpokenText(mContent, aName); | ||||||
| 
 |  | ||||||
|   // Native anonymous content, ARIA can't be used. Get list bullet text.
 |  | ||||||
|   if (nsBulletFrame* frame = do_QueryFrame(GetFrame())) { |  | ||||||
|     if (!frame->StyleList()->mListStyleImage.IsNone()) { |  | ||||||
|       // Bullet is an image, so use default bullet character.
 |  | ||||||
|       const char16_t kDiscCharacter = 0x2022; |  | ||||||
|       aName.Assign(kDiscCharacter); |  | ||||||
|       aName.Append(' '); |  | ||||||
|       return eNameOK; |  | ||||||
|     } |  | ||||||
|     frame->GetSpokenText(aName); |  | ||||||
|   } else { |  | ||||||
|     // If marker is not a bullet frame but instead has content
 |  | ||||||
|     nsTextEquivUtils::AppendFromDOMChildren(mContent, &aName); |  | ||||||
|     aName.CompressWhitespace(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return eNameOK; |   return eNameOK; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -30,6 +30,11 @@ already_AddRefed<GeneratedImageContent> GeneratedImageContent::Create( | ||||||
|   return image.forget(); |   return image.forget(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | already_AddRefed<GeneratedImageContent> | ||||||
|  | GeneratedImageContent::CreateForListStyleImage(Document& aDocument) { | ||||||
|  |   return Create(aDocument, uint32_t(-1)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| JSObject* GeneratedImageContent::WrapNode(JSContext* aCx, | JSObject* GeneratedImageContent::WrapNode(JSContext* aCx, | ||||||
|                                           JS::Handle<JSObject*> aGivenProto) { |                                           JS::Handle<JSObject*> aGivenProto) { | ||||||
|   return dom::HTMLElement_Binding::Wrap(aCx, this, aGivenProto); |   return dom::HTMLElement_Binding::Wrap(aCx, this, aGivenProto); | ||||||
|  |  | ||||||
|  | @ -23,6 +23,9 @@ class GeneratedImageContent final : public nsGenericHTMLElement { | ||||||
|  public: |  public: | ||||||
|   static already_AddRefed<GeneratedImageContent> Create(Document&, |   static already_AddRefed<GeneratedImageContent> Create(Document&, | ||||||
|                                                         uint32_t aContentIndex); |                                                         uint32_t aContentIndex); | ||||||
|  |   // An image created from 'list-style-image' for a ::marker pseudo.
 | ||||||
|  |   static already_AddRefed<GeneratedImageContent> CreateForListStyleImage( | ||||||
|  |       Document&); | ||||||
| 
 | 
 | ||||||
|   explicit GeneratedImageContent(already_AddRefed<dom::NodeInfo>&& aNodeInfo) |   explicit GeneratedImageContent(already_AddRefed<dom::NodeInfo>&& aNodeInfo) | ||||||
|       : nsGenericHTMLElement(std::move(aNodeInfo)) { |       : nsGenericHTMLElement(std::move(aNodeInfo)) { | ||||||
|  | @ -30,6 +33,13 @@ class GeneratedImageContent final : public nsGenericHTMLElement { | ||||||
|                "Someone messed up our nodeinfo"); |                "Someone messed up our nodeinfo"); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   EventStates IntrinsicState() const override { | ||||||
|  |     EventStates state = nsGenericHTMLElement::IntrinsicState(); | ||||||
|  |     if (mBroken) { | ||||||
|  |       state |= NS_EVENT_STATE_BROKEN; | ||||||
|  |     } | ||||||
|  |     return state; | ||||||
|  |   } | ||||||
|   nsresult Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const final; |   nsresult Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const final; | ||||||
| 
 | 
 | ||||||
|   nsresult CopyInnerTo(GeneratedImageContent* aDest) { |   nsresult CopyInnerTo(GeneratedImageContent* aDest) { | ||||||
|  | @ -39,14 +49,25 @@ class GeneratedImageContent final : public nsGenericHTMLElement { | ||||||
|     return NS_OK; |     return NS_OK; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // Is this an image created from 'list-style-image'?
 | ||||||
|  |   bool IsForListStyleImageMarker() const { return Index() == uint32_t(-1); } | ||||||
|  | 
 | ||||||
|  |   // @note we use -1 for images created from 'list-style-image'
 | ||||||
|   uint32_t Index() const { return mIndex; } |   uint32_t Index() const { return mIndex; } | ||||||
| 
 | 
 | ||||||
|  |   // Notify this image failed to load.
 | ||||||
|  |   void NotifyLoadFailed() { | ||||||
|  |     mBroken = true; | ||||||
|  |     UpdateState(true); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  protected: |  protected: | ||||||
|   JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final; |   JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final; | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   virtual ~GeneratedImageContent() = default; |   virtual ~GeneratedImageContent() = default; | ||||||
|   uint32_t mIndex = 0; |   uint32_t mIndex = 0; | ||||||
|  |   bool mBroken = false; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| }  // namespace dom
 | }  // namespace dom
 | ||||||
|  |  | ||||||
|  | @ -37,7 +37,6 @@ | ||||||
| #include "Layers.h" | #include "Layers.h" | ||||||
| #include "nsAnimationManager.h" | #include "nsAnimationManager.h" | ||||||
| #include "nsBlockFrame.h" | #include "nsBlockFrame.h" | ||||||
| #include "nsBulletFrame.h" |  | ||||||
| #include "nsContentUtils.h" | #include "nsContentUtils.h" | ||||||
| #include "nsCSSFrameConstructor.h" | #include "nsCSSFrameConstructor.h" | ||||||
| #include "nsCSSRendering.h" | #include "nsCSSRendering.h" | ||||||
|  | @ -465,7 +464,11 @@ static bool StateChangeMayAffectFrame(const Element& aElement, | ||||||
|                                       const nsIFrame& aFrame, |                                       const nsIFrame& aFrame, | ||||||
|                                       EventStates aStates) { |                                       EventStates aStates) { | ||||||
|   if (aFrame.IsGeneratedContentFrame()) { |   if (aFrame.IsGeneratedContentFrame()) { | ||||||
|     // If it's generated content, ignore LOADING/etc state changes on it.
 |     if (aElement.IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage)) { | ||||||
|  |       return aStates.HasState(NS_EVENT_STATE_BROKEN); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // If it's other generated content, ignore LOADING/etc state changes on it.
 | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -1961,7 +1964,7 @@ static const nsIFrame* ExpectedOwnerForChild(const nsIFrame* aFrame) { | ||||||
| 
 | 
 | ||||||
|   parent = FirstContinuationOrPartOfIBSplit(parent); |   parent = FirstContinuationOrPartOfIBSplit(parent); | ||||||
| 
 | 
 | ||||||
|   // We've handled already anon boxes and bullet frames, so now we're looking at
 |   // We've handled already anon boxes, so now we're looking at
 | ||||||
|   // a frame of a DOM element or pseudo. Hop through anon and line-boxes
 |   // a frame of a DOM element or pseudo. Hop through anon and line-boxes
 | ||||||
|   // generated by our DOM parent, and go find the owner frame for it.
 |   // generated by our DOM parent, and go find the owner frame for it.
 | ||||||
|   while (parent && (IsAnonBox(parent) || parent->IsLineFrame())) { |   while (parent && (IsAnonBox(parent) || parent->IsLineFrame())) { | ||||||
|  |  | ||||||
|  | @ -268,8 +268,8 @@ nsIFrame* NS_NewScrollbarButtonFrame(PresShell* aPresShell, | ||||||
|                                      ComputedStyle* aStyle); |                                      ComputedStyle* aStyle); | ||||||
| 
 | 
 | ||||||
| nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*); | nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*); | ||||||
| 
 |  | ||||||
| nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*); | nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*); | ||||||
|  | nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*); | ||||||
| 
 | 
 | ||||||
| // Returns true if aFrame is an anonymous flex/grid item.
 | // Returns true if aFrame is an anonymous flex/grid item.
 | ||||||
| static inline bool IsAnonymousFlexOrGridItem(const nsIFrame* aFrame) { | static inline bool IsAnonymousFlexOrGridItem(const nsIFrame* aFrame) { | ||||||
|  | @ -1600,6 +1600,66 @@ already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGeneratedContent( | ||||||
|   return nullptr; |   return nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void nsCSSFrameConstructor::CreateGeneratedContentFromListStyle( | ||||||
|  |     nsFrameConstructorState& aState, const ComputedStyle& aPseudoStyle, | ||||||
|  |     const FunctionRef<void(nsIContent*)> aAddChild) { | ||||||
|  |   const nsStyleList* styleList = aPseudoStyle.StyleList(); | ||||||
|  |   if (!styleList->mListStyleImage.IsNone()) { | ||||||
|  |     RefPtr<nsIContent> child = | ||||||
|  |         GeneratedImageContent::CreateForListStyleImage(*mDocument); | ||||||
|  |     aAddChild(child); | ||||||
|  |     child = CreateGenConTextNode(aState, u" "_ns, nullptr); | ||||||
|  |     aAddChild(child); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   CreateGeneratedContentFromListStyleType(aState, aPseudoStyle, aAddChild); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nsCSSFrameConstructor::CreateGeneratedContentFromListStyleType( | ||||||
|  |     nsFrameConstructorState& aState, const ComputedStyle& aPseudoStyle, | ||||||
|  |     const FunctionRef<void(nsIContent*)> aAddChild) { | ||||||
|  |   const nsStyleList* styleList = aPseudoStyle.StyleList(); | ||||||
|  |   CounterStyle* counterStyle = | ||||||
|  |       mPresShell->GetPresContext()->CounterStyleManager()->ResolveCounterStyle( | ||||||
|  |           styleList->mCounterStyle); | ||||||
|  |   bool needUseNode = false; | ||||||
|  |   switch (counterStyle->GetStyle()) { | ||||||
|  |     case NS_STYLE_LIST_STYLE_NONE: | ||||||
|  |       return; | ||||||
|  |     case NS_STYLE_LIST_STYLE_DISC: | ||||||
|  |     case NS_STYLE_LIST_STYLE_CIRCLE: | ||||||
|  |     case NS_STYLE_LIST_STYLE_SQUARE: | ||||||
|  |     case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: | ||||||
|  |     case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       const auto* anonStyle = counterStyle->AsAnonymous(); | ||||||
|  |       if (!anonStyle || !anonStyle->IsSingleString()) { | ||||||
|  |         needUseNode = true; | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   auto node = MakeUnique<nsCounterUseNode>(nsCounterUseNode::ForLegacyBullet, | ||||||
|  |                                            styleList->mCounterStyle); | ||||||
|  |   if (!needUseNode) { | ||||||
|  |     nsAutoString text; | ||||||
|  |     node->GetText(WritingMode(&aPseudoStyle), counterStyle, text); | ||||||
|  |     // Note that we're done with 'node' in this case.  It's not inserted into
 | ||||||
|  |     // any list so it's deleted when we return.
 | ||||||
|  |     RefPtr<nsIContent> child = CreateGenConTextNode(aState, text, nullptr); | ||||||
|  |     aAddChild(child); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   nsCounterList* counterList = | ||||||
|  |       mCounterManager.CounterListFor(nsGkAtoms::list_item); | ||||||
|  |   auto initializer = MakeUnique<nsGenConInitializer>( | ||||||
|  |       std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty); | ||||||
|  |   RefPtr<nsIContent> child = | ||||||
|  |       CreateGenConTextNode(aState, EmptyString(), std::move(initializer)); | ||||||
|  |   aAddChild(child); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * aParentFrame - the frame that should be the parent of the generated |  * aParentFrame - the frame that should be the parent of the generated | ||||||
|  *   content.  This is the frame for the corresponding content node, |  *   content.  This is the frame for the corresponding content node, | ||||||
|  | @ -1660,8 +1720,10 @@ void nsCSSFrameConstructor::CreateGeneratedContentItem( | ||||||
|       MOZ_ASSERT_UNREACHABLE("unexpected aPseudoElement"); |       MOZ_ASSERT_UNREACHABLE("unexpected aPseudoElement"); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // |ProbePseudoStyleFor| checked the 'display' property and the
 |   // |ProbePseudoElementStyle| checked the 'display' property and the
 | ||||||
|   // |ContentCount()| of the 'content' property for us.
 |   // |ContentCount()| of the 'content' property for us, and for ::marker
 | ||||||
|  |   // also that we have either a 'list-style-type' or 'list-style-image'
 | ||||||
|  |   // non-initial value in case we have no 'content'.
 | ||||||
|   RefPtr<NodeInfo> nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo( |   RefPtr<NodeInfo> nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo( | ||||||
|       elemName, nullptr, kNameSpaceID_None, nsINode::ELEMENT_NODE); |       elemName, nullptr, kNameSpaceID_None, nsINode::ELEMENT_NODE); | ||||||
|   RefPtr<Element> container; |   RefPtr<Element> container; | ||||||
|  | @ -1704,26 +1766,31 @@ void nsCSSFrameConstructor::CreateGeneratedContentItem( | ||||||
|     pseudoStyle = ServoStyleSet::ResolveServoStyle(*container); |     pseudoStyle = ServoStyleSet::ResolveServoStyle(*container); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   uint32_t contentCount = pseudoStyle->StyleContent()->ContentCount(); |   auto AppendChild = [&container, this](nsIContent* aChild) { | ||||||
|   for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) { |  | ||||||
|     nsCOMPtr<nsIContent> content = CreateGeneratedContent( |  | ||||||
|         aState, aOriginatingElement, *pseudoStyle, contentIndex); |  | ||||||
|     if (!content) { |  | ||||||
|       continue; |  | ||||||
|     } |  | ||||||
|     // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
 |     // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
 | ||||||
|     // here; it would get set under AppendChildTo.  But AppendChildTo might
 |     // here; it would get set under AppendChildTo.  But AppendChildTo might
 | ||||||
|     // think that we're going from not being anonymous to being anonymous and
 |     // think that we're going from not being anonymous to being anonymous and
 | ||||||
|     // do some extra work; setting the flag here avoids that.
 |     // do some extra work; setting the flag here avoids that.
 | ||||||
|     content->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); |     aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); | ||||||
|     container->AppendChildTo(content, false, IgnoreErrors()); |     container->AppendChildTo(aChild, false, IgnoreErrors()); | ||||||
|     if (auto* element = Element::FromNode(content)) { |     if (auto* childElement = Element::FromNode(aChild)) { | ||||||
|       // If we created any children elements, Servo needs to traverse them, but
 |       // If we created any children elements, Servo needs to traverse them, but
 | ||||||
|       // the root is already set up.
 |       // the root is already set up.
 | ||||||
|       mPresShell->StyleSet()->StyleNewSubtree(element); |       mPresShell->StyleSet()->StyleNewSubtree(childElement); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   const uint32_t contentCount = pseudoStyle->StyleContent()->ContentCount(); | ||||||
|  |   for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) { | ||||||
|  |     if (RefPtr<nsIContent> content = CreateGeneratedContent( | ||||||
|  |             aState, aOriginatingElement, *pseudoStyle, contentIndex)) { | ||||||
|  |       AppendChild(content); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // If a ::marker has no 'content' then generate it from its 'list-style-*'.
 | ||||||
|  |   if (contentCount == 0 && aPseudoElement == PseudoStyleType::marker) { | ||||||
|  |     CreateGeneratedContentFromListStyle(aState, *pseudoStyle, AppendChild); | ||||||
|  |   } | ||||||
|   AddFrameConstructionItemsInternal(aState, container, aParentFrame, true, |   AddFrameConstructionItemsInternal(aState, container, aParentFrame, true, | ||||||
|                                     pseudoStyle, {ItemFlag::IsGeneratedContent}, |                                     pseudoStyle, {ItemFlag::IsGeneratedContent}, | ||||||
|                                     aItems); |                                     aItems); | ||||||
|  | @ -3354,6 +3421,13 @@ nsCSSFrameConstructor::FindGeneratedImageData(const Element& aElement, | ||||||
|     return nullptr; |     return nullptr; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   auto& generatedContent = static_cast<const GeneratedImageContent&>(aElement); | ||||||
|  |   if (generatedContent.IsForListStyleImageMarker()) { | ||||||
|  |     static const FrameConstructionData sImgData = | ||||||
|  |         SIMPLE_FCDATA(NS_NewImageFrameForListStyleImage); | ||||||
|  |     return &sImgData; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   static const FrameConstructionData sImgData = |   static const FrameConstructionData sImgData = | ||||||
|       SIMPLE_FCDATA(NS_NewImageFrameForGeneratedContentIndex); |       SIMPLE_FCDATA(NS_NewImageFrameForGeneratedContentIndex); | ||||||
|   return &sImgData; |   return &sImgData; | ||||||
|  | @ -3807,16 +3881,6 @@ void nsCSSFrameConstructor::ConstructFrameFromItemInternal( | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (computedStyle->GetPseudoType() == PseudoStyleType::marker && |  | ||||||
|       newFrame->IsBulletFrame()) { |  | ||||||
|     MOZ_ASSERT(!computedStyle->StyleContent()->ContentCount()); |  | ||||||
|     auto* node = new nsCounterUseNode(nsCounterUseNode::ForLegacyBullet); |  | ||||||
|     auto* list = mCounterManager.CounterListFor(nsGkAtoms::list_item); |  | ||||||
|     if (node->InitBullet(list, newFrame)) { |  | ||||||
|       CountersDirty(); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) == |   NS_ASSERTION(newFrame->IsFrameOfType(nsIFrame::eLineParticipant) == | ||||||
|                    ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0), |                    ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0), | ||||||
|                "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits"); |                "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits"); | ||||||
|  | @ -5231,16 +5295,6 @@ nsCSSFrameConstructor::FindElementTagData(const Element& aElement, | ||||||
|                                           ComputedStyle& aStyle, |                                           ComputedStyle& aStyle, | ||||||
|                                           nsIFrame* aParentFrame, |                                           nsIFrame* aParentFrame, | ||||||
|                                           ItemFlags aFlags) { |                                           ItemFlags aFlags) { | ||||||
|   // A ::marker pseudo creates a nsBulletFrame, unless 'content' was set.
 |  | ||||||
|   if (aStyle.GetPseudoType() == PseudoStyleType::marker && |  | ||||||
|       aStyle.StyleContent()->ContentCount() == 0) { |  | ||||||
|     static const FrameConstructionData data = FCDATA_DECL( |  | ||||||
|         FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | |  | ||||||
|             FCDATA_DISALLOW_GENERATED_CONTENT | FCDATA_IS_LINE_PARTICIPANT | |  | ||||||
|             FCDATA_IS_INLINE | FCDATA_USE_CHILD_ITEMS, |  | ||||||
|         NS_NewBulletFrame); |  | ||||||
|     return &data; |  | ||||||
|   } |  | ||||||
|   switch (aElement.GetNameSpaceID()) { |   switch (aElement.GetNameSpaceID()) { | ||||||
|     case kNameSpaceID_XHTML: |     case kNameSpaceID_XHTML: | ||||||
|       return FindHTMLData(aElement, aParentFrame, aStyle); |       return FindHTMLData(aElement, aParentFrame, aStyle); | ||||||
|  | @ -7063,6 +7117,48 @@ void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aStartChild, | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // This handles fallback to 'list-style-type' when a 'list-style-image' fails
 | ||||||
|  |   // to load.
 | ||||||
|  |   if (aStartChild->IsInNativeAnonymousSubtree() && | ||||||
|  |       aStartChild->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage)) { | ||||||
|  |     MOZ_ASSERT(isSingleInsert); | ||||||
|  |     MOZ_ASSERT(insertion.mParentFrame->Style()->GetPseudoType() == | ||||||
|  |                    PseudoStyleType::marker, | ||||||
|  |                "we can only handle ::marker fallback for now"); | ||||||
|  |     nsIContent* const nextSibling = aStartChild->GetNextSibling(); | ||||||
|  |     MOZ_ASSERT(nextSibling && nextSibling->IsText(), | ||||||
|  |                "expected a text node after the list-style-image image"); | ||||||
|  |     RemoveFrame(kPrincipalList, nextSibling->GetPrimaryFrame()); | ||||||
|  |     auto* const container = aStartChild->GetParent()->AsElement(); | ||||||
|  |     nsIContent* firstNewChild = nullptr; | ||||||
|  |     auto InsertChild = [this, container, nextSibling, | ||||||
|  |                         &firstNewChild](RefPtr<nsIContent>&& aChild) { | ||||||
|  |       // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
 | ||||||
|  |       // here; it would get set under AppendChildTo.  But AppendChildTo might
 | ||||||
|  |       // think that we're going from not being anonymous to being anonymous and
 | ||||||
|  |       // do some extra work; setting the flag here avoids that.
 | ||||||
|  |       aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); | ||||||
|  |       container->InsertChildBefore(aChild, nextSibling, false, IgnoreErrors()); | ||||||
|  |       if (auto* childElement = Element::FromNode(aChild)) { | ||||||
|  |         // If we created any children elements, Servo needs to traverse them,
 | ||||||
|  |         // but the root is already set up.
 | ||||||
|  |         mPresShell->StyleSet()->StyleNewSubtree(childElement); | ||||||
|  |       } | ||||||
|  |       if (!firstNewChild) { | ||||||
|  |         firstNewChild = aChild; | ||||||
|  |       } | ||||||
|  |     }; | ||||||
|  |     CreateGeneratedContentFromListStyleType( | ||||||
|  |         state, *insertion.mParentFrame->Style(), InsertChild); | ||||||
|  |     if (!firstNewChild) { | ||||||
|  |       // No fallback content - we're done.
 | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     aStartChild = firstNewChild; | ||||||
|  |     MOZ_ASSERT(firstNewChild->GetNextSibling() == nextSibling, | ||||||
|  |                "list-style-type should only create one child"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   AutoFrameConstructionItemList items(this); |   AutoFrameConstructionItemList items(this); | ||||||
|   ParentType parentType = GetParentType(frameType); |   ParentType parentType = GetParentType(frameType); | ||||||
|   FlattenedChildIterator iter(insertion.mContainer); |   FlattenedChildIterator iter(insertion.mContainer); | ||||||
|  | @ -9575,6 +9671,13 @@ void nsCSSFrameConstructor::ProcessChildren( | ||||||
|           listItem->SetMarkerFrameForListItem(childFrame); |           listItem->SetMarkerFrameForListItem(childFrame); | ||||||
|           MOZ_ASSERT(listItem->HasAnyStateBits( |           MOZ_ASSERT(listItem->HasAnyStateBits( | ||||||
|                          NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER) == isOutsideMarker); |                          NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER) == isOutsideMarker); | ||||||
|  | #ifdef ACCESSIBILITY | ||||||
|  |           if (nsAccessibilityService* accService = | ||||||
|  |                   PresShell::GetAccessibilityService()) { | ||||||
|  |             auto* marker = markerFrame->GetContent(); | ||||||
|  |             accService->ContentRangeInserted(mPresShell, marker, nullptr); | ||||||
|  |           } | ||||||
|  | #endif | ||||||
|           break; |           break; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "mozilla/ArenaAllocator.h" | #include "mozilla/ArenaAllocator.h" | ||||||
| #include "mozilla/Attributes.h" | #include "mozilla/Attributes.h" | ||||||
|  | #include "mozilla/FunctionRef.h" | ||||||
| #include "mozilla/LinkedList.h" | #include "mozilla/LinkedList.h" | ||||||
| #include "mozilla/Maybe.h" | #include "mozilla/Maybe.h" | ||||||
| #include "mozilla/RestyleManager.h" | #include "mozilla/RestyleManager.h" | ||||||
|  | @ -333,8 +334,10 @@ class nsCSSFrameConstructor final : public nsFrameManager { | ||||||
| 
 | 
 | ||||||
|   void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const; |   void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const; | ||||||
| 
 | 
 | ||||||
|   // temporary - please don't add external uses outside of nsBulletFrame
 | #ifdef ACCESSIBILITY | ||||||
|   nsCounterManager* CounterManager() { return &mCounterManager; } |   // Exposed only for nsLayoutUtils::GetMarkerSpokenText to use.
 | ||||||
|  |   const nsCounterManager* CounterManager() const { return &mCounterManager; } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|  private: |  private: | ||||||
|   struct FrameConstructionItem; |   struct FrameConstructionItem; | ||||||
|  | @ -460,6 +463,19 @@ class nsCSSFrameConstructor final : public nsFrameManager { | ||||||
|       nsFrameConstructorState& aState, const Element& aOriginatingElement, |       nsFrameConstructorState& aState, const Element& aOriginatingElement, | ||||||
|       ComputedStyle& aComputedStyle, uint32_t aContentIndex); |       ComputedStyle& aComputedStyle, uint32_t aContentIndex); | ||||||
| 
 | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Create child content nodes for a ::marker from its 'list-style-*' values. | ||||||
|  |    */ | ||||||
|  |   void CreateGeneratedContentFromListStyle( | ||||||
|  |       nsFrameConstructorState& aState, const ComputedStyle& aPseudoStyle, | ||||||
|  |       const mozilla::FunctionRef<void(nsIContent*)> aAddChild); | ||||||
|  |   /**
 | ||||||
|  |    * Create child content nodes for a ::marker from its 'list-style-type'. | ||||||
|  |    */ | ||||||
|  |   void CreateGeneratedContentFromListStyleType( | ||||||
|  |       nsFrameConstructorState& aState, const ComputedStyle& aPseudoStyle, | ||||||
|  |       const mozilla::FunctionRef<void(nsIContent*)> aAddChild); | ||||||
|  | 
 | ||||||
|   // aParentFrame may be null; this method doesn't use it directly in any case.
 |   // aParentFrame may be null; this method doesn't use it directly in any case.
 | ||||||
|   void CreateGeneratedContentItem(nsFrameConstructorState& aState, |   void CreateGeneratedContentItem(nsFrameConstructorState& aState, | ||||||
|                                   nsContainerFrame* aParentFrame, |                                   nsContainerFrame* aParentFrame, | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ | ||||||
| #include "mozilla/PresShell.h" | #include "mozilla/PresShell.h" | ||||||
| #include "mozilla/StaticPrefs_layout.h" | #include "mozilla/StaticPrefs_layout.h" | ||||||
| #include "mozilla/WritingModes.h" | #include "mozilla/WritingModes.h" | ||||||
| #include "nsBulletFrame.h"  // legacy location for list style type to text code
 |  | ||||||
| #include "nsContentUtils.h" | #include "nsContentUtils.h" | ||||||
| #include "nsIContent.h" | #include "nsIContent.h" | ||||||
| #include "nsTArray.h" | #include "nsTArray.h" | ||||||
|  | @ -43,13 +42,6 @@ bool nsCounterUseNode::InitTextFrame(nsGenConList* aList, | ||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool nsCounterUseNode::InitBullet(nsGenConList* aList, nsIFrame* aBullet) { |  | ||||||
|   MOZ_ASSERT(aBullet->IsBulletFrame()); |  | ||||||
|   MOZ_ASSERT(aBullet->Style()->GetPseudoType() == PseudoStyleType::marker); |  | ||||||
|   MOZ_ASSERT(mForLegacyBullet); |  | ||||||
|   return InitTextFrame(aList, aBullet, nullptr); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // assign the correct |mValueAfter| value to a node that has been inserted
 | // assign the correct |mValueAfter| value to a node that has been inserted
 | ||||||
| // Should be called immediately after calling |Insert|.
 | // Should be called immediately after calling |Insert|.
 | ||||||
| void nsCounterUseNode::Calc(nsCounterList* aList, bool aNotify) { | void nsCounterUseNode::Calc(nsCounterList* aList, bool aNotify) { | ||||||
|  | @ -59,11 +51,6 @@ void nsCounterUseNode::Calc(nsCounterList* aList, bool aNotify) { | ||||||
|     nsAutoString contentString; |     nsAutoString contentString; | ||||||
|     GetText(contentString); |     GetText(contentString); | ||||||
|     mText->SetText(contentString, aNotify); |     mText->SetText(contentString, aNotify); | ||||||
|   } else if (mForLegacyBullet) { |  | ||||||
|     MOZ_ASSERT_IF(mPseudoFrame, mPseudoFrame->IsBulletFrame()); |  | ||||||
|     if (nsBulletFrame* f = do_QueryFrame(mPseudoFrame)) { |  | ||||||
|       f->SetOrdinal(mValueAfter, aNotify); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -82,9 +69,34 @@ void nsCounterChangeNode::Calc(nsCounterList* aList) { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // The text that should be displayed for this counter.
 |  | ||||||
| void nsCounterUseNode::GetText(nsString& aResult) { | void nsCounterUseNode::GetText(nsString& aResult) { | ||||||
|   aResult.Truncate(); |   CounterStyle* style = | ||||||
|  |       mPseudoFrame->PresContext()->CounterStyleManager()->ResolveCounterStyle( | ||||||
|  |           mCounterStyle); | ||||||
|  |   GetText(mPseudoFrame->GetWritingMode(), style, aResult); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void nsCounterUseNode::GetText(WritingMode aWM, CounterStyle* aStyle, | ||||||
|  |                                nsString& aResult) { | ||||||
|  |   const bool isBidiRTL = aWM.IsBidiRTL(); | ||||||
|  |   auto AppendCounterText = [&aResult, isBidiRTL](const nsAutoString& aText, | ||||||
|  |                                                  bool aIsRTL) { | ||||||
|  |     if (MOZ_LIKELY(isBidiRTL == aIsRTL)) { | ||||||
|  |       aResult.Append(aText); | ||||||
|  |     } else { | ||||||
|  |       // RLM = 0x200f, LRM = 0x200e
 | ||||||
|  |       const char16_t mark = aIsRTL ? 0x200f : 0x200e; | ||||||
|  |       aResult.Append(mark); | ||||||
|  |       aResult.Append(aText); | ||||||
|  |       aResult.Append(mark); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   if (mForLegacyBullet) { | ||||||
|  |     nsAutoString prefix; | ||||||
|  |     aStyle->GetPrefix(prefix); | ||||||
|  |     aResult.Assign(prefix); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   AutoTArray<nsCounterNode*, 8> stack; |   AutoTArray<nsCounterNode*, 8> stack; | ||||||
|   stack.AppendElement(static_cast<nsCounterNode*>(this)); |   stack.AppendElement(static_cast<nsCounterNode*>(this)); | ||||||
|  | @ -95,21 +107,26 @@ void nsCounterUseNode::GetText(nsString& aResult) { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   WritingMode wm = mPseudoFrame->GetWritingMode(); |   for (nsCounterNode* n : Reversed(stack)) { | ||||||
|   CounterStyle* style = |  | ||||||
|       mPseudoFrame->PresContext()->CounterStyleManager()->ResolveCounterStyle( |  | ||||||
|           mCounterStyle); |  | ||||||
|   for (uint32_t i = stack.Length() - 1;; --i) { |  | ||||||
|     nsCounterNode* n = stack[i]; |  | ||||||
|     nsAutoString text; |     nsAutoString text; | ||||||
|     bool isTextRTL; |     bool isTextRTL; | ||||||
|     style->GetCounterText(n->mValueAfter, wm, text, isTextRTL); |     aStyle->GetCounterText(n->mValueAfter, aWM, text, isTextRTL); | ||||||
|  |     if (!mForLegacyBullet || aStyle->IsBullet()) { | ||||||
|       aResult.Append(text); |       aResult.Append(text); | ||||||
|     if (i == 0) { |     } else { | ||||||
|  |       AppendCounterText(text, isTextRTL); | ||||||
|  |     } | ||||||
|  |     if (n == this) { | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     aResult.Append(mSeparator); |     aResult.Append(mSeparator); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   if (mForLegacyBullet) { | ||||||
|  |     nsAutoString suffix; | ||||||
|  |     aStyle->GetSuffix(suffix); | ||||||
|  |     aResult.Append(suffix); | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void nsCounterList::SetScope(nsCounterNode* aNode) { | void nsCounterList::SetScope(nsCounterNode* aNode) { | ||||||
|  | @ -340,6 +357,41 @@ bool nsCounterManager::DestroyNodesFor(nsIFrame* aFrame) { | ||||||
|   return destroyedAny; |   return destroyedAny; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef ACCESSIBILITY | ||||||
|  | void nsCounterManager::GetSpokenCounterText(nsIFrame* aFrame, | ||||||
|  |                                             nsAString& aText) const { | ||||||
|  |   CounterValue ordinal = 1; | ||||||
|  |   if (const auto* list = mNames.Get(nsGkAtoms::list_item)) { | ||||||
|  |     for (nsCounterNode* n = list->GetFirstNodeFor(aFrame); | ||||||
|  |          n && n->mPseudoFrame == aFrame; n = list->Next(n)) { | ||||||
|  |       if (n->mType == nsCounterNode::USE) { | ||||||
|  |         ordinal = n->mValueAfter; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   CounterStyle* counterStyle = | ||||||
|  |       aFrame->PresContext()->CounterStyleManager()->ResolveCounterStyle( | ||||||
|  |           aFrame->StyleList()->mCounterStyle); | ||||||
|  |   nsAutoString text; | ||||||
|  |   bool isBullet; | ||||||
|  |   counterStyle->GetSpokenCounterText(ordinal, aFrame->GetWritingMode(), text, | ||||||
|  |                                      isBullet); | ||||||
|  |   if (isBullet) { | ||||||
|  |     aText = text; | ||||||
|  |     if (!counterStyle->IsNone()) { | ||||||
|  |       aText.Append(' '); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     counterStyle->GetPrefix(aText); | ||||||
|  |     aText += text; | ||||||
|  |     nsAutoString suffix; | ||||||
|  |     counterStyle->GetSuffix(suffix); | ||||||
|  |     aText += suffix; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||
| void nsCounterManager::Dump() { | void nsCounterManager::Dump() { | ||||||
|   printf("\n\nCounter Manager Lists:\n"); |   printf("\n\nCounter Manager Lists:\n"); | ||||||
|  |  | ||||||
|  | @ -62,6 +62,7 @@ struct nsCounterNode : public nsGenConNode { | ||||||
|   // 'counter-reset', 'counter-increment' or 'counter-set'  property
 |   // 'counter-reset', 'counter-increment' or 'counter-set'  property
 | ||||||
|   // instead of within the 'content' property but offset to ensure
 |   // instead of within the 'content' property but offset to ensure
 | ||||||
|   // that (reset, increment, set, use) sort in that order.
 |   // that (reset, increment, set, use) sort in that order.
 | ||||||
|  |   // It is zero for legacy bullet USE counter nodes.
 | ||||||
|   // (This slight weirdness allows sharing a lot of code with 'quotes'.)
 |   // (This slight weirdness allows sharing a lot of code with 'quotes'.)
 | ||||||
|   nsCounterNode(int32_t aContentIndex, Type aType) |   nsCounterNode(int32_t aContentIndex, Type aType) | ||||||
|       : nsGenConNode(aContentIndex), mType(aType) {} |       : nsGenConNode(aContentIndex), mType(aType) {} | ||||||
|  | @ -83,10 +84,10 @@ struct nsCounterUseNode : public nsCounterNode { | ||||||
|   bool mForLegacyBullet = false; |   bool mForLegacyBullet = false; | ||||||
| 
 | 
 | ||||||
|   enum ForLegacyBullet { ForLegacyBullet }; |   enum ForLegacyBullet { ForLegacyBullet }; | ||||||
|   explicit nsCounterUseNode(enum ForLegacyBullet) |   nsCounterUseNode(enum ForLegacyBullet, mozilla::CounterStylePtr aCounterStyle) | ||||||
|       : nsCounterNode(0, USE), mForLegacyBullet(true) { |       : nsCounterNode(0, USE), | ||||||
|     mCounterStyle = nsGkAtoms::list_item; |         mCounterStyle(std::move(aCounterStyle)), | ||||||
|   } |         mForLegacyBullet(true) {} | ||||||
| 
 | 
 | ||||||
|   // args go directly to member variables here and of nsGenConNode
 |   // args go directly to member variables here and of nsGenConNode
 | ||||||
|   nsCounterUseNode(mozilla::CounterStylePtr aCounterStyle, nsString aSeparator, |   nsCounterUseNode(mozilla::CounterStylePtr aCounterStyle, nsString aSeparator, | ||||||
|  | @ -98,11 +99,9 @@ struct nsCounterUseNode : public nsCounterNode { | ||||||
|     NS_ASSERTION(aContentIndex <= INT32_MAX, "out of range"); |     NS_ASSERTION(aContentIndex <= INT32_MAX, "out of range"); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   virtual bool InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame, |   bool InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame, | ||||||
|                      nsIFrame* aTextFrame) override; |                      nsIFrame* aTextFrame) override; | ||||||
| 
 | 
 | ||||||
|   bool InitBullet(nsGenConList* aList, nsIFrame* aBulletFrame); |  | ||||||
| 
 |  | ||||||
|   // assign the correct |mValueAfter| value to a node that has been inserted,
 |   // assign the correct |mValueAfter| value to a node that has been inserted,
 | ||||||
|   // and update the value of the text node, notifying if `aNotify` is true.
 |   // and update the value of the text node, notifying if `aNotify` is true.
 | ||||||
|   // Should be called immediately after calling |Insert|.
 |   // Should be called immediately after calling |Insert|.
 | ||||||
|  | @ -110,6 +109,8 @@ struct nsCounterUseNode : public nsCounterNode { | ||||||
| 
 | 
 | ||||||
|   // The text that should be displayed for this counter.
 |   // The text that should be displayed for this counter.
 | ||||||
|   void GetText(nsString& aResult); |   void GetText(nsString& aResult); | ||||||
|  |   void GetText(mozilla::WritingMode aWM, mozilla::CounterStyle* aStyle, | ||||||
|  |                nsString& aResult); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct nsCounterChangeNode : public nsCounterNode { | struct nsCounterChangeNode : public nsCounterNode { | ||||||
|  | @ -171,6 +172,11 @@ class nsCounterList : public nsGenConList { | ||||||
|  public: |  public: | ||||||
|   nsCounterList() : nsGenConList(), mDirty(false) {} |   nsCounterList() : nsGenConList(), mDirty(false) {} | ||||||
| 
 | 
 | ||||||
|  |   // Return the first node for aFrame on this list, or nullptr.
 | ||||||
|  |   nsCounterNode* GetFirstNodeFor(nsIFrame* aFrame) const { | ||||||
|  |     return static_cast<nsCounterNode*>(nsGenConList::GetFirstNodeFor(aFrame)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   void Insert(nsCounterNode* aNode) { |   void Insert(nsCounterNode* aNode) { | ||||||
|     nsGenConList::Insert(aNode); |     nsGenConList::Insert(aNode); | ||||||
|     // Don't SetScope if we're dirty -- we'll reset all the scopes anyway,
 |     // Don't SetScope if we're dirty -- we'll reset all the scopes anyway,
 | ||||||
|  | @ -235,6 +241,11 @@ class nsCounterManager { | ||||||
|   // Clear all data.
 |   // Clear all data.
 | ||||||
|   void Clear() { mNames.Clear(); } |   void Clear() { mNames.Clear(); } | ||||||
| 
 | 
 | ||||||
|  | #ifdef ACCESSIBILITY | ||||||
|  |   // Returns the spoken text for the 'list-item' counter for aFrame in aText.
 | ||||||
|  |   void GetSpokenCounterText(nsIFrame* aFrame, nsAString& aText) const; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||
|   void Dump(); |   void Dump(); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -34,7 +34,6 @@ struct nsGenConNode : public mozilla::LinkedListElement<nsGenConNode> { | ||||||
|   // null for:
 |   // null for:
 | ||||||
|   //  * content: no-open-quote / content: no-close-quote
 |   //  * content: no-open-quote / content: no-close-quote
 | ||||||
|   //  * counter nodes for increments and resets
 |   //  * counter nodes for increments and resets
 | ||||||
|   //  * counter nodes for bullets (mPseudoFrame->IsBulletFrame()).
 |  | ||||||
|   RefPtr<nsTextNode> mText; |   RefPtr<nsTextNode> mText; | ||||||
| 
 | 
 | ||||||
|   explicit nsGenConNode(int32_t aContentIndex) |   explicit nsGenConNode(int32_t aContentIndex) | ||||||
|  | @ -65,12 +64,9 @@ struct nsGenConNode : public mozilla::LinkedListElement<nsGenConNode> { | ||||||
|   void CheckFrameAssertions() { |   void CheckFrameAssertions() { | ||||||
|     NS_ASSERTION( |     NS_ASSERTION( | ||||||
|         mContentIndex < int32_t(mPseudoFrame->StyleContent()->ContentCount()) || |         mContentIndex < int32_t(mPseudoFrame->StyleContent()->ContentCount()) || | ||||||
|             // Special-case for the use node created for the legacy markers,
 |             // Special-case for the USE node created for the legacy markers,
 | ||||||
|             // which don't use the content property.
 |             // which don't use the content property.
 | ||||||
|             (mPseudoFrame->IsBulletFrame() && mContentIndex == 0 && |             mContentIndex == 0, | ||||||
|              mPseudoFrame->Style()->GetPseudoType() == |  | ||||||
|                  mozilla::PseudoStyleType::marker && |  | ||||||
|              !mPseudoFrame->StyleContent()->ContentCount()), |  | ||||||
|         "index out of range"); |         "index out of range"); | ||||||
|     // We allow negative values of mContentIndex for 'counter-reset' and
 |     // We allow negative values of mContentIndex for 'counter-reset' and
 | ||||||
|     // 'counter-increment'.
 |     // 'counter-increment'.
 | ||||||
|  | @ -112,6 +108,11 @@ class nsGenConList { | ||||||
|   // have been destroyed; otherwise false.
 |   // have been destroyed; otherwise false.
 | ||||||
|   bool DestroyNodesFor(nsIFrame* aFrame); |   bool DestroyNodesFor(nsIFrame* aFrame); | ||||||
| 
 | 
 | ||||||
|  |   // Return the first node for aFrame on this list, or nullptr.
 | ||||||
|  |   nsGenConNode* GetFirstNodeFor(nsIFrame* aFrame) const { | ||||||
|  |     return mNodes.Get(aFrame); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   // Return true if |aNode1| is after |aNode2|.
 |   // Return true if |aNode1| is after |aNode2|.
 | ||||||
|   static bool NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2); |   static bool NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -893,6 +893,40 @@ nsIFrame* nsLayoutUtils::GetMarkerFrame(const nsIContent* aContent) { | ||||||
|   return pseudo ? pseudo->GetPrimaryFrame() : nullptr; |   return pseudo ? pseudo->GetPrimaryFrame() : nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef ACCESSIBILITY | ||||||
|  | void nsLayoutUtils::GetMarkerSpokenText(const nsIContent* aContent, | ||||||
|  |                                         nsAString& aText) { | ||||||
|  |   MOZ_ASSERT(aContent && aContent->IsGeneratedContentContainerForMarker()); | ||||||
|  | 
 | ||||||
|  |   aText.Truncate(); | ||||||
|  | 
 | ||||||
|  |   nsIFrame* frame = aContent->GetPrimaryFrame(); | ||||||
|  |   if (!frame) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (frame->StyleContent()->ContentCount() > 0) { | ||||||
|  |     for (nsIFrame* child : frame->PrincipalChildList()) { | ||||||
|  |       nsIFrame::RenderedText text = child->GetRenderedText(); | ||||||
|  |       aText += text.mString; | ||||||
|  |     } | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!frame->StyleList()->mListStyleImage.IsNone()) { | ||||||
|  |     // ::marker is an image, so use default bullet character.
 | ||||||
|  |     static const char16_t kDiscMarkerString[] = {0x2022, ' ', 0}; | ||||||
|  |     aText.AssignLiteral(kDiscMarkerString); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   frame->PresContext() | ||||||
|  |       ->FrameConstructor() | ||||||
|  |       ->CounterManager() | ||||||
|  |       ->GetSpokenCounterText(frame, aText); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| // static
 | // static
 | ||||||
| nsIFrame* nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame, | nsIFrame* nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame, | ||||||
|                                                LayoutFrameType aFrameType, |                                                LayoutFrameType aFrameType, | ||||||
|  |  | ||||||
|  | @ -273,6 +273,14 @@ class nsLayoutUtils { | ||||||
|    */ |    */ | ||||||
|   static nsIFrame* GetMarkerFrame(const nsIContent* aContent); |   static nsIFrame* GetMarkerFrame(const nsIContent* aContent); | ||||||
| 
 | 
 | ||||||
|  | #ifdef ACCESSIBILITY | ||||||
|  |   /**
 | ||||||
|  |    * Set aText to the spoken text for the given ::marker content (aContent) | ||||||
|  |    * if it has a frame, or the empty string otherwise. | ||||||
|  |    */ | ||||||
|  |   static void GetMarkerSpokenText(const nsIContent* aContent, nsAString& aText); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|   /**
 |   /**
 | ||||||
|    * Given a frame, search up the frame tree until we find an |    * Given a frame, search up the frame tree until we find an | ||||||
|    * ancestor that (or the frame itself) is of type aFrameType, if any. |    * ancestor that (or the frame itself) is of type aFrameType, if any. | ||||||
|  |  | ||||||
|  | @ -7395,6 +7395,11 @@ void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame* aMarkerFrame) { | ||||||
|     SetProperty(InsideMarkerProperty(), aMarkerFrame); |     SetProperty(InsideMarkerProperty(), aMarkerFrame); | ||||||
|     AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER); |     AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER); | ||||||
|   } else { |   } else { | ||||||
|  |     if (nsBlockFrame* marker = do_QueryFrame(aMarkerFrame)) { | ||||||
|  |       // An outside ::marker needs to be an independent formatting context
 | ||||||
|  |       // to avoid being influenced by the float manager etc.
 | ||||||
|  |       marker->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS); | ||||||
|  |     } | ||||||
|     SetProperty(OutsideMarkerProperty(), |     SetProperty(OutsideMarkerProperty(), | ||||||
|                 new (PresShell()) nsFrameList(aMarkerFrame, aMarkerFrame)); |                 new (PresShell()) nsFrameList(aMarkerFrame, aMarkerFrame)); | ||||||
|     AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER); |     AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER); | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ | ||||||
| #include "nsFontMetrics.h" | #include "nsFontMetrics.h" | ||||||
| #include "nsIImageLoadingContent.h" | #include "nsIImageLoadingContent.h" | ||||||
| #include "nsImageLoadingContent.h" | #include "nsImageLoadingContent.h" | ||||||
|  | #include "nsImageRenderer.h" | ||||||
| #include "nsString.h" | #include "nsString.h" | ||||||
| #include "nsPrintfCString.h" | #include "nsPrintfCString.h" | ||||||
| #include "nsPresContext.h" | #include "nsPresContext.h" | ||||||
|  | @ -98,6 +99,89 @@ using namespace mozilla::layers; | ||||||
| 
 | 
 | ||||||
| using mozilla::layout::TextDrawTarget; | using mozilla::layout::TextDrawTarget; | ||||||
| 
 | 
 | ||||||
|  | class nsDisplayGradient final : public nsPaintedDisplayItem { | ||||||
|  |  public: | ||||||
|  |   nsDisplayGradient(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame) | ||||||
|  |       : nsPaintedDisplayItem(aBuilder, aFrame) { | ||||||
|  |     MOZ_COUNT_CTOR(nsDisplayGradient); | ||||||
|  |   } | ||||||
|  |   ~nsDisplayGradient() final { MOZ_COUNT_DTOR(nsDisplayGradient); } | ||||||
|  | 
 | ||||||
|  |   nsDisplayItemGeometry* AllocateGeometry( | ||||||
|  |       nsDisplayListBuilder* aBuilder) final { | ||||||
|  |     return new nsDisplayItemGenericImageGeometry(this, aBuilder); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   nsRect GetBounds(bool* aSnap) const { | ||||||
|  |     *aSnap = true; | ||||||
|  | 
 | ||||||
|  |     auto* imageFrame = static_cast<nsImageFrame*>(mFrame); | ||||||
|  |     return imageFrame->GetInnerArea() + ToReferenceFrame(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   nsRect GetBounds(nsDisplayListBuilder*, bool* aSnap) const final { | ||||||
|  |     return GetBounds(aSnap); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void Paint(nsDisplayListBuilder*, gfxContext* aCtx) final; | ||||||
|  | 
 | ||||||
|  |   bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder&, | ||||||
|  |                                mozilla::wr::IpcResourceUpdateQueue&, | ||||||
|  |                                const StackingContextHelper&, | ||||||
|  |                                mozilla::layers::RenderRootStateManager*, | ||||||
|  |                                nsDisplayListBuilder*) final; | ||||||
|  | 
 | ||||||
|  |   NS_DISPLAY_DECL_NAME("Gradient", TYPE_GRADIENT) | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void nsDisplayGradient::Paint(nsDisplayListBuilder* aBuilder, | ||||||
|  |                               gfxContext* aCtx) { | ||||||
|  |   auto* frame = static_cast<nsImageFrame*>(Frame()); | ||||||
|  |   nsImageRenderer imageRenderer(frame, frame->GetImageFromStyle(), | ||||||
|  |                                 aBuilder->GetImageRendererFlags()); | ||||||
|  |   nsSize size = frame->GetSize(); | ||||||
|  |   imageRenderer.SetPreferredSize({}, size); | ||||||
|  | 
 | ||||||
|  |   ImgDrawResult result; | ||||||
|  |   if (!imageRenderer.PrepareImage()) { | ||||||
|  |     result = imageRenderer.PrepareResult(); | ||||||
|  |   } else { | ||||||
|  |     nsRect dest(ToReferenceFrame(), size); | ||||||
|  |     result = imageRenderer.DrawLayer(frame->PresContext(), *aCtx, dest, dest, | ||||||
|  |                                      dest.TopLeft(), GetPaintRect(), | ||||||
|  |                                      dest.Size(), /* aOpacity = */ 1.0f); | ||||||
|  |   } | ||||||
|  |   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool nsDisplayGradient::CreateWebRenderCommands( | ||||||
|  |     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, | ||||||
|  |     const StackingContextHelper& aSc, | ||||||
|  |     mozilla::layers::RenderRootStateManager* aManager, | ||||||
|  |     nsDisplayListBuilder* aDisplayListBuilder) { | ||||||
|  |   auto* frame = static_cast<nsImageFrame*>(Frame()); | ||||||
|  |   nsImageRenderer imageRenderer(frame, frame->GetImageFromStyle(), | ||||||
|  |                                 aDisplayListBuilder->GetImageRendererFlags()); | ||||||
|  |   nsSize size = frame->GetSize(); | ||||||
|  |   imageRenderer.SetPreferredSize({}, size); | ||||||
|  | 
 | ||||||
|  |   ImgDrawResult result; | ||||||
|  |   if (!imageRenderer.PrepareImage()) { | ||||||
|  |     result = imageRenderer.PrepareResult(); | ||||||
|  |   } else { | ||||||
|  |     nsRect dest(ToReferenceFrame(), size); | ||||||
|  |     result = imageRenderer.BuildWebRenderDisplayItemsForLayer( | ||||||
|  |         frame->PresContext(), aBuilder, aResources, aSc, aManager, this, dest, | ||||||
|  |         dest, dest.TopLeft(), dest, dest.Size(), | ||||||
|  |         /* aOpacity = */ 1.0f); | ||||||
|  |     if (result == ImgDrawResult::NOT_SUPPORTED) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // sizes (pixels) for image icon, padding and border frame
 | // sizes (pixels) for image icon, padding and border frame
 | ||||||
| #define ICON_SIZE (16) | #define ICON_SIZE (16) | ||||||
| #define ICON_PADDING (3) | #define ICON_PADDING (3) | ||||||
|  | @ -178,6 +262,12 @@ nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell* aPresShell, | ||||||
|                    nsImageFrame::Kind::ContentPropertyAtIndex); |                    nsImageFrame::Kind::ContentPropertyAtIndex); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | nsIFrame* NS_NewImageFrameForListStyleImage(PresShell* aPresShell, | ||||||
|  |                                             ComputedStyle* aStyle) { | ||||||
|  |   return new (aPresShell) nsImageFrame(aStyle, aPresShell->GetPresContext(), | ||||||
|  |                                        nsImageFrame::Kind::ListStyleImage); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool nsImageFrame::ShouldShowBrokenImageIcon() const { | bool nsImageFrame::ShouldShowBrokenImageIcon() const { | ||||||
|   // NOTE(emilio, https://github.com/w3c/csswg-drafts/issues/2832): WebKit and
 |   // NOTE(emilio, https://github.com/w3c/csswg-drafts/issues/2832): WebKit and
 | ||||||
|   // Blink behave differently here for content: url(..), for now adapt to
 |   // Blink behave differently here for content: url(..), for now adapt to
 | ||||||
|  | @ -241,6 +331,11 @@ NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame) | ||||||
| 
 | 
 | ||||||
| #ifdef ACCESSIBILITY | #ifdef ACCESSIBILITY | ||||||
| a11y::AccType nsImageFrame::AccessibleType() { | a11y::AccType nsImageFrame::AccessibleType() { | ||||||
|  |   if (mKind == Kind::ListStyleImage) { | ||||||
|  |     // This is an HTMLListBulletAccessible.
 | ||||||
|  |     return a11y::eNoType; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   // Don't use GetImageMap() to avoid reentrancy into accessibility.
 |   // Don't use GetImageMap() to avoid reentrancy into accessibility.
 | ||||||
|   if (HasImageMap()) { |   if (HasImageMap()) { | ||||||
|     return a11y::eHTMLImageMapType; |     return a11y::eHTMLImageMapType; | ||||||
|  | @ -327,6 +422,12 @@ void nsImageFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) { | ||||||
| 
 | 
 | ||||||
|   MaybeRecordContentUrlOnImageTelemetry(); |   MaybeRecordContentUrlOnImageTelemetry(); | ||||||
| 
 | 
 | ||||||
|  |   // A ::marker's default size is calculated from the font's em-size.
 | ||||||
|  |   if (IsForMarkerPseudo()) { | ||||||
|  |     mIntrinsicSize = IntrinsicSize(0, 0); | ||||||
|  |     UpdateIntrinsicSize(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   auto newOrientation = StyleVisibility()->mImageOrientation; |   auto newOrientation = StyleVisibility()->mImageOrientation; | ||||||
| 
 | 
 | ||||||
|   // We need to update our orientation either if we had no ComputedStyle before
 |   // We need to update our orientation either if we had no ComputedStyle before
 | ||||||
|  | @ -361,8 +462,15 @@ static bool SizeIsAvailable(imgIRequest* aRequest) { | ||||||
| 
 | 
 | ||||||
| const StyleImage* nsImageFrame::GetImageFromStyle() const { | const StyleImage* nsImageFrame::GetImageFromStyle() const { | ||||||
|   if (mKind == Kind::ImageElement) { |   if (mKind == Kind::ImageElement) { | ||||||
|  |     MOZ_ASSERT_UNREACHABLE("Don't call me"); | ||||||
|     return nullptr; |     return nullptr; | ||||||
|   } |   } | ||||||
|  |   if (mKind == Kind::ListStyleImage) { | ||||||
|  |     MOZ_ASSERT( | ||||||
|  |         GetParent()->GetContent()->IsGeneratedContentContainerForMarker()); | ||||||
|  |     MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage)); | ||||||
|  |     return &StyleList()->mListStyleImage; | ||||||
|  |   } | ||||||
|   uint32_t contentIndex = 0; |   uint32_t contentIndex = 0; | ||||||
|   const nsStyleContent* styleContent = StyleContent(); |   const nsStyleContent* styleContent = StyleContent(); | ||||||
|   if (mKind == Kind::ContentPropertyAtIndex) { |   if (mKind == Kind::ContentPropertyAtIndex) { | ||||||
|  | @ -414,14 +522,16 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, | ||||||
|     imageLoader->FrameCreated(this); |     imageLoader->FrameCreated(this); | ||||||
|   } else { |   } else { | ||||||
|     const StyleImage* image = GetImageFromStyle(); |     const StyleImage* image = GetImageFromStyle(); | ||||||
|     MOZ_ASSERT(image->IsImageRequestType(), |     MOZ_ASSERT(mKind == Kind::ListStyleImage || image->IsImageRequestType(), | ||||||
|                "Content image should only parse url() type"); |                "Content image should only parse url() type"); | ||||||
|     Document* doc = PresContext()->Document(); |     if (image->IsImageRequestType()) { | ||||||
|       if (imgRequestProxy* proxy = image->GetImageRequest()) { |       if (imgRequestProxy* proxy = image->GetImageRequest()) { | ||||||
|       proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest)); |         proxy->Clone(mListener, PresContext()->Document(), | ||||||
|  |                      getter_AddRefs(mContentURLRequest)); | ||||||
|         SetupForContentURLRequest(); |         SetupForContentURLRequest(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   // Give image loads associated with an image frame a small priority boost.
 |   // Give image loads associated with an image frame a small priority boost.
 | ||||||
|   if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) { |   if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) { | ||||||
|  | @ -491,6 +601,22 @@ static void ScaleIntrinsicSizeForDensity(imgIContainer* aImage, | ||||||
|   ScaleIntrinsicSizeForDensity(aSize, resolution); |   ScaleIntrinsicSizeForDensity(aSize, resolution); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static nscoord ListImageDefaultLength(const nsImageFrame& aFrame) { | ||||||
|  |   // https://drafts.csswg.org/css-lists-3/#image-markers
 | ||||||
|  |   // The spec says we should use 1em x 1em, but that seems too large.
 | ||||||
|  |   // See disussion in https://github.com/w3c/csswg-drafts/issues/4207
 | ||||||
|  |   auto* pc = aFrame.PresContext(); | ||||||
|  |   RefPtr<nsFontMetrics> fm = | ||||||
|  |       nsLayoutUtils::GetFontMetricsForComputedStyle(aFrame.Style(), pc); | ||||||
|  |   auto emAU = fm->GetThebesFontGroup() | ||||||
|  |                   ->GetFirstValidFont() | ||||||
|  |                   ->GetMetrics(fm->Orientation()) | ||||||
|  |                   .emHeight * | ||||||
|  |               pc->AppUnitsPerDevPixel(); | ||||||
|  |   return std::max(NSToCoordRound(0.4f * emAU), | ||||||
|  |                   nsPresContext::CSSPixelsToAppUnits(1)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage, | static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage, | ||||||
|                                           bool aUseMappedRatio, |                                           bool aUseMappedRatio, | ||||||
|                                           nsImageFrame::Kind aKind, |                                           nsImageFrame::Kind aKind, | ||||||
|  | @ -505,6 +631,17 @@ static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage, | ||||||
|     IntrinsicSize intrinsicSize; |     IntrinsicSize intrinsicSize; | ||||||
|     intrinsicSize.width = size.width == -1 ? Nothing() : Some(size.width); |     intrinsicSize.width = size.width == -1 ? Nothing() : Some(size.width); | ||||||
|     intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height); |     intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height); | ||||||
|  |     if (aKind == nsImageFrame::Kind::ListStyleImage) { | ||||||
|  |       if (intrinsicSize.width.isNothing() || intrinsicSize.height.isNothing()) { | ||||||
|  |         nscoord defaultLength = ListImageDefaultLength(aFrame); | ||||||
|  |         if (intrinsicSize.width.isNothing()) { | ||||||
|  |           intrinsicSize.width = Some(defaultLength); | ||||||
|  |         } | ||||||
|  |         if (intrinsicSize.height.isNothing()) { | ||||||
|  |           intrinsicSize.height = Some(defaultLength); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|     if (aKind == nsImageFrame::Kind::ImageElement) { |     if (aKind == nsImageFrame::Kind::ImageElement) { | ||||||
|       ScaleIntrinsicSizeForDensity(aImage, *aFrame.GetContent(), intrinsicSize); |       ScaleIntrinsicSizeForDensity(aImage, *aFrame.GetContent(), intrinsicSize); | ||||||
|     } else { |     } else { | ||||||
|  | @ -514,6 +651,12 @@ static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage, | ||||||
|     return intrinsicSize; |     return intrinsicSize; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   if (aKind == nsImageFrame::Kind::ListStyleImage) { | ||||||
|  |     // Note: images are handled above, this handles gradients etc.
 | ||||||
|  |     nscoord defaultLength = ListImageDefaultLength(aFrame); | ||||||
|  |     return IntrinsicSize(defaultLength, defaultLength); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   if (aFrame.ShouldShowBrokenImageIcon()) { |   if (aFrame.ShouldShowBrokenImageIcon()) { | ||||||
|     nscoord edgeLengthToUse = nsPresContext::CSSPixelsToAppUnits( |     nscoord edgeLengthToUse = nsPresContext::CSSPixelsToAppUnits( | ||||||
|         ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH))); |         ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH))); | ||||||
|  | @ -780,6 +923,13 @@ void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) { | ||||||
|   } else { |   } else { | ||||||
|     // We no longer have a valid image, so release our stored image container.
 |     // We no longer have a valid image, so release our stored image container.
 | ||||||
|     mImage = mPrevImage = nullptr; |     mImage = mPrevImage = nullptr; | ||||||
|  |     if (mKind == Kind::ListStyleImage) { | ||||||
|  |       auto* genContent = static_cast<GeneratedImageContent*>(GetContent()); | ||||||
|  |       genContent->NotifyLoadFailed(); | ||||||
|  |       // No need to continue below since the above state change will destroy
 | ||||||
|  |       // this frame.
 | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|   // NOTE(emilio): Intentionally using `|` instead of `||` to avoid
 |   // NOTE(emilio): Intentionally using `|` instead of `||` to avoid
 | ||||||
|   // short-circuiting.
 |   // short-circuiting.
 | ||||||
|  | @ -796,9 +946,11 @@ void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) { | ||||||
|     // already gotten the initial reflow.
 |     // already gotten the initial reflow.
 | ||||||
|     if (!(mState & IMAGE_SIZECONSTRAINED)) { |     if (!(mState & IMAGE_SIZECONSTRAINED)) { | ||||||
| #ifdef ACCESSIBILITY | #ifdef ACCESSIBILITY | ||||||
|  |       if (mKind != Kind::ListStyleImage) { | ||||||
|         if (nsAccessibilityService* accService = GetAccService()) { |         if (nsAccessibilityService* accService = GetAccService()) { | ||||||
|           accService->NotifyOfImageSizeAvailable(PresShell(), mContent); |           accService->NotifyOfImageSizeAvailable(PresShell(), mContent); | ||||||
|         } |         } | ||||||
|  |       } | ||||||
| #endif | #endif | ||||||
|       PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange, |       PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange, | ||||||
|                                     NS_FRAME_IS_DIRTY); |                                     NS_FRAME_IS_DIRTY); | ||||||
|  | @ -955,6 +1107,14 @@ nsRect nsImageFrame::PredictedDestRect(const nsRect& aFrameContentBox) { | ||||||
|                                               mIntrinsicRatio, StylePosition()); |                                               mIntrinsicRatio, StylePosition()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool nsImageFrame::IsForMarkerPseudo() const { | ||||||
|  |   if (mKind == Kind::ImageElement) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   auto* subtreeRoot = GetContent()->GetClosestNativeAnonymousSubtreeRoot(); | ||||||
|  |   return subtreeRoot && subtreeRoot->IsGeneratedContentContainerForMarker(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void nsImageFrame::EnsureIntrinsicSizeAndRatio() { | void nsImageFrame::EnsureIntrinsicSizeAndRatio() { | ||||||
|   if (StyleDisplay()->IsContainSize()) { |   if (StyleDisplay()->IsContainSize()) { | ||||||
|     // If we have 'contain:size', then our intrinsic size and ratio are 0,0
 |     // If we have 'contain:size', then our intrinsic size and ratio are 0,0
 | ||||||
|  | @ -965,8 +1125,9 @@ void nsImageFrame::EnsureIntrinsicSizeAndRatio() { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // If mIntrinsicSize.width and height are 0, then we need to update from the
 |   // If mIntrinsicSize.width and height are 0, then we need to update from the
 | ||||||
|   // image container.
 |   // image container.  Note that we handle ::marker intrinsic size/ratio in
 | ||||||
|   if (mIntrinsicSize != IntrinsicSize(0, 0)) { |   // DidSetComputedStyle.
 | ||||||
|  |   if (mIntrinsicSize != IntrinsicSize(0, 0) && !IsForMarkerPseudo()) { | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -2188,7 +2349,9 @@ void nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, | ||||||
|     // XXX(seth): The SizeIsAvailable check here should not be necessary - the
 |     // XXX(seth): The SizeIsAvailable check here should not be necessary - the
 | ||||||
|     // intention is that a non-null mImage means we have a size, but there is
 |     // intention is that a non-null mImage means we have a size, but there is
 | ||||||
|     // currently some code that violates this invariant.
 |     // currently some code that violates this invariant.
 | ||||||
|     if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) { |     if ((mKind == Kind::ImageElement || | ||||||
|  |          GetImageFromStyle()->IsImageRequestType()) && | ||||||
|  |         (!imageOK || !mImage || !SizeIsAvailable(currentRequest))) { | ||||||
|       // No image yet, or image load failed. Draw the alt-text and an icon
 |       // No image yet, or image load failed. Draw the alt-text and an icon
 | ||||||
|       // indicating the status
 |       // indicating the status
 | ||||||
|       aLists.Content()->AppendNewToTop<nsDisplayAltFeedback>(aBuilder, this); |       aLists.Content()->AppendNewToTop<nsDisplayAltFeedback>(aBuilder, this); | ||||||
|  | @ -2208,8 +2371,12 @@ void nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|  |       if (mImage) { | ||||||
|         aLists.Content()->AppendNewToTop<nsDisplayImage>(aBuilder, this, mImage, |         aLists.Content()->AppendNewToTop<nsDisplayImage>(aBuilder, this, mImage, | ||||||
|                                                          mPrevImage); |                                                          mPrevImage); | ||||||
|  |       } else if (mKind != Kind::ImageElement) { | ||||||
|  |         aLists.Content()->AppendNewToTop<nsDisplayGradient>(aBuilder, this); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       // If we were previously displaying an icon, we're not anymore
 |       // If we were previously displaying an icon, we're not anymore
 | ||||||
|       if (mDisplayingIcon) { |       if (mDisplayingIcon) { | ||||||
|  |  | ||||||
|  | @ -187,6 +187,8 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { | ||||||
|     // For a child of a ::before / ::after pseudo-element that had an url() item
 |     // For a child of a ::before / ::after pseudo-element that had an url() item
 | ||||||
|     // for the content property.
 |     // for the content property.
 | ||||||
|     ContentPropertyAtIndex, |     ContentPropertyAtIndex, | ||||||
|  |     // For a list-style-image ::marker.
 | ||||||
|  |     ListStyleImage, | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   // Creates a suitable continuing frame for this frame.
 |   // Creates a suitable continuing frame for this frame.
 | ||||||
|  | @ -199,6 +201,8 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { | ||||||
|                                                       ComputedStyle*); |                                                       ComputedStyle*); | ||||||
|   friend nsIFrame* NS_NewImageFrameForGeneratedContentIndex(mozilla::PresShell*, |   friend nsIFrame* NS_NewImageFrameForGeneratedContentIndex(mozilla::PresShell*, | ||||||
|                                                             ComputedStyle*); |                                                             ComputedStyle*); | ||||||
|  |   friend nsIFrame* NS_NewImageFrameForListStyleImage(mozilla::PresShell*, | ||||||
|  |                                                      ComputedStyle*); | ||||||
| 
 | 
 | ||||||
|   nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, Kind aKind) |   nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, Kind aKind) | ||||||
|       : nsImageFrame(aStyle, aPresContext, kClassID, aKind) {} |       : nsImageFrame(aStyle, aPresContext, kClassID, aKind) {} | ||||||
|  | @ -260,6 +264,11 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { | ||||||
|    */ |    */ | ||||||
|   void MaybeDecodeForPredictedSize(); |   void MaybeDecodeForPredictedSize(); | ||||||
| 
 | 
 | ||||||
|  |   /**
 | ||||||
|  |    * Is this frame part of a ::marker pseudo? | ||||||
|  |    */ | ||||||
|  |   bool IsForMarkerPseudo() const; | ||||||
|  | 
 | ||||||
|  protected: |  protected: | ||||||
|   friend class nsImageListener; |   friend class nsImageListener; | ||||||
|   friend class nsImageLoadingContent; |   friend class nsImageLoadingContent; | ||||||
|  | @ -353,7 +362,7 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { | ||||||
| 
 | 
 | ||||||
|   RefPtr<nsImageListener> mListener; |   RefPtr<nsImageListener> mListener; | ||||||
| 
 | 
 | ||||||
|   // An image request created for content: url(..).
 |   // An image request created for content: url(..) or list-style-image.
 | ||||||
|   RefPtr<imgRequestProxy> mContentURLRequest; |   RefPtr<imgRequestProxy> mContentURLRequest; | ||||||
| 
 | 
 | ||||||
|   nsCOMPtr<imgIContainer> mImage; |   nsCOMPtr<imgIContainer> mImage; | ||||||
|  | @ -426,6 +435,7 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { | ||||||
|   static mozilla::StaticRefPtr<IconLoad> gIconLoad; |   static mozilla::StaticRefPtr<IconLoad> gIconLoad; | ||||||
| 
 | 
 | ||||||
|   friend class nsDisplayImage; |   friend class nsDisplayImage; | ||||||
|  |   friend class nsDisplayGradient; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -5027,6 +5027,15 @@ void nsTextFrame::GetTextDecorations( | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (context->GetPseudoType() == PseudoStyleType::marker && | ||||||
|  |         (context->StyleList()->mListStylePosition == | ||||||
|  |              NS_STYLE_LIST_STYLE_POSITION_OUTSIDE || | ||||||
|  |          !context->StyleDisplay()->IsInlineOutsideStyle())) { | ||||||
|  |       // Outside ::marker pseudos, and inside markers that aren't inlines, don't
 | ||||||
|  |       // have text decorations.
 | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const nsStyleTextReset* const styleTextReset = context->StyleTextReset(); |     const nsStyleTextReset* const styleTextReset = context->StyleTextReset(); | ||||||
|     const StyleTextDecorationLine textDecorations = |     const StyleTextDecorationLine textDecorations = | ||||||
|         styleTextReset->mTextDecorationLine; |         styleTextReset->mTextDecorationLine; | ||||||
|  |  | ||||||
|  | @ -48,6 +48,7 @@ DECLARE_DISPLAY_ITEM_TYPE(FOREIGN_OBJECT, | ||||||
| DECLARE_DISPLAY_ITEM_TYPE(FRAMESET_BLANK, TYPE_RENDERS_NO_IMAGES) | DECLARE_DISPLAY_ITEM_TYPE(FRAMESET_BLANK, TYPE_RENDERS_NO_IMAGES) | ||||||
| DECLARE_DISPLAY_ITEM_TYPE(FRAMESET_BORDER, TYPE_RENDERS_NO_IMAGES) | DECLARE_DISPLAY_ITEM_TYPE(FRAMESET_BORDER, TYPE_RENDERS_NO_IMAGES) | ||||||
| DECLARE_DISPLAY_ITEM_TYPE(GENERIC, TYPE_RENDERS_NO_IMAGES) | DECLARE_DISPLAY_ITEM_TYPE(GENERIC, TYPE_RENDERS_NO_IMAGES) | ||||||
|  | DECLARE_DISPLAY_ITEM_TYPE(GRADIENT, TYPE_IS_CONTENTFUL) | ||||||
| DECLARE_DISPLAY_ITEM_TYPE(HEADER_FOOTER, TYPE_RENDERS_NO_IMAGES) | DECLARE_DISPLAY_ITEM_TYPE(HEADER_FOOTER, TYPE_RENDERS_NO_IMAGES) | ||||||
| DECLARE_DISPLAY_ITEM_TYPE(IMAGE, TYPE_IS_CONTENTFUL) | DECLARE_DISPLAY_ITEM_TYPE(IMAGE, TYPE_IS_CONTENTFUL) | ||||||
| DECLARE_DISPLAY_ITEM_TYPE(LINK, TYPE_RENDERS_NO_IMAGES) | DECLARE_DISPLAY_ITEM_TYPE(LINK, TYPE_RENDERS_NO_IMAGES) | ||||||
|  |  | ||||||
|  | @ -585,7 +585,7 @@ void BuiltinCounterStyle::GetSuffix(nsAString& aResult) { | ||||||
| 
 | 
 | ||||||
| static const char16_t kDiscCharacter = 0x2022; | static const char16_t kDiscCharacter = 0x2022; | ||||||
| static const char16_t kCircleCharacter = 0x25e6; | static const char16_t kCircleCharacter = 0x25e6; | ||||||
| static const char16_t kSquareCharacter = 0x25fe; | static const char16_t kSquareCharacter = 0x25aa; | ||||||
| static const char16_t kRightPointingCharacter = 0x25b8; | static const char16_t kRightPointingCharacter = 0x25b8; | ||||||
| static const char16_t kLeftPointingCharacter = 0x25c2; | static const char16_t kLeftPointingCharacter = 0x25c2; | ||||||
| static const char16_t kDownPointingCharacter = 0x25be; | static const char16_t kDownPointingCharacter = 0x25be; | ||||||
|  |  | ||||||
|  | @ -677,6 +677,13 @@ bool ServoStyleSet::GeneratedContentPseudoExists( | ||||||
|     if (!aParentStyle.StyleDisplay()->IsListItem()) { |     if (!aParentStyle.StyleDisplay()->IsListItem()) { | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|  |     // ::marker only exist if we have 'content' or at least one of
 | ||||||
|  |     // 'list-style-type' or 'list-style-image'.
 | ||||||
|  |     if (aPseudoStyle.StyleList()->mCounterStyle.IsNone() && | ||||||
|  |         aPseudoStyle.StyleList()->mListStyleImage.IsNone() && | ||||||
|  |         aPseudoStyle.StyleContent()->ContentCount() == 0) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|     // display:none is equivalent to not having the pseudo-element at all.
 |     // display:none is equivalent to not having the pseudo-element at all.
 | ||||||
|     if (aPseudoStyle.StyleDisplay()->mDisplay == StyleDisplay::None) { |     if (aPseudoStyle.StyleDisplay()->mDisplay == StyleDisplay::None) { | ||||||
|       return false; |       return false; | ||||||
|  |  | ||||||
|  | @ -631,12 +631,11 @@ nsChangeHint nsStyleList::CalcDifference( | ||||||
|   // relies on that when the display value changes from something else
 |   // relies on that when the display value changes from something else
 | ||||||
|   // to list-item, that change itself would cause ReconstructFrame.
 |   // to list-item, that change itself would cause ReconstructFrame.
 | ||||||
|   if (aOldDisplay.IsListItem()) { |   if (aOldDisplay.IsListItem()) { | ||||||
|     if (mListStylePosition != aNewData.mListStylePosition) { |     if (mListStylePosition != aNewData.mListStylePosition || | ||||||
|  |         mCounterStyle != aNewData.mCounterStyle || | ||||||
|  |         mListStyleImage != aNewData.mListStyleImage) { | ||||||
|       return nsChangeHint_ReconstructFrame; |       return nsChangeHint_ReconstructFrame; | ||||||
|     } |     } | ||||||
|     if (mCounterStyle != aNewData.mCounterStyle) { |  | ||||||
|       return NS_STYLE_HINT_REFLOW; |  | ||||||
|     } |  | ||||||
|   } else if (mListStylePosition != aNewData.mListStylePosition || |   } else if (mListStylePosition != aNewData.mListStylePosition || | ||||||
|              mCounterStyle != aNewData.mCounterStyle) { |              mCounterStyle != aNewData.mCounterStyle) { | ||||||
|     hint = nsChangeHint_NeutralChange; |     hint = nsChangeHint_NeutralChange; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Mats Palmgren
						Mats Palmgren