1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "DetailsFrame.h"
8
9 #include "mozilla/Attributes.h"
10 #include "mozilla/PresShell.h"
11 #include "mozilla/dom/HTMLDetailsElement.h"
12 #include "mozilla/dom/HTMLSummaryElement.h"
13 #include "nsContentUtils.h"
14 #include "nsPlaceholderFrame.h"
15 #include "nsTextNode.h"
16
17 using namespace mozilla;
18 using namespace mozilla::dom;
19
20 NS_IMPL_FRAMEARENA_HELPERS(DetailsFrame)
21
NS_QUERYFRAME_HEAD(DetailsFrame)22 NS_QUERYFRAME_HEAD(DetailsFrame)
23 NS_QUERYFRAME_ENTRY(DetailsFrame)
24 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
25 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
26
27 nsBlockFrame* NS_NewDetailsFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
28 return new (aPresShell) DetailsFrame(aStyle, aPresShell->GetPresContext());
29 }
30
31 namespace mozilla {
32
DetailsFrame(ComputedStyle * aStyle,nsPresContext * aPresContext)33 DetailsFrame::DetailsFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
34 : nsBlockFrame(aStyle, aPresContext, kClassID) {}
35
36 DetailsFrame::~DetailsFrame() = default;
37
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)38 void DetailsFrame::SetInitialChildList(ChildListID aListID,
39 nsFrameList& aChildList) {
40 #ifdef DEBUG
41 if (aListID == kPrincipalList) {
42 CheckValidMainSummary(aChildList);
43 }
44 #endif
45
46 nsBlockFrame::SetInitialChildList(aListID, aChildList);
47 }
48
49 #ifdef DEBUG
CheckValidMainSummary(const nsFrameList & aFrameList) const50 bool DetailsFrame::CheckValidMainSummary(const nsFrameList& aFrameList) const {
51 for (nsIFrame* child : aFrameList) {
52 if (child->IsGeneratedContentFrame()) {
53 continue;
54 }
55 HTMLSummaryElement* summary =
56 HTMLSummaryElement::FromNode(child->GetContent());
57 if (child == aFrameList.FirstChild()) {
58 if (summary && summary->IsMainSummary()) {
59 return true;
60 } else if (child->GetContent() == GetContent()) {
61 // The child frame's content is the same as our content, which means
62 // it's a kind of wrapper frame. Descend into its child list to find
63 // main summary.
64 if (CheckValidMainSummary(child->PrincipalChildList())) {
65 return true;
66 }
67 }
68 } else {
69 NS_ASSERTION(!summary || !summary->IsMainSummary(),
70 "Rest of the children are either not summary element "
71 "or are not the main summary!");
72 }
73 }
74 return false;
75 }
76 #endif
77
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)78 void DetailsFrame::DestroyFrom(nsIFrame* aDestructRoot,
79 PostDestroyData& aPostDestroyData) {
80 aPostDestroyData.AddAnonymousContent(mDefaultSummary.forget());
81 nsBlockFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
82 }
83
CreateAnonymousContent(nsTArray<ContentInfo> & aElements)84 nsresult DetailsFrame::CreateAnonymousContent(
85 nsTArray<ContentInfo>& aElements) {
86 auto* details = HTMLDetailsElement::FromNode(GetContent());
87 if (details->GetFirstSummary()) {
88 return NS_OK;
89 }
90
91 // The <details> element lacks any direct <summary> child. Create a default
92 // <summary> element as an anonymous content.
93 nsNodeInfoManager* nodeInfoManager =
94 GetContent()->NodeInfo()->NodeInfoManager();
95
96 RefPtr<NodeInfo> nodeInfo = nodeInfoManager->GetNodeInfo(
97 nsGkAtoms::summary, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
98 mDefaultSummary = new (nodeInfoManager) HTMLSummaryElement(nodeInfo.forget());
99
100 nsAutoString defaultSummaryText;
101 nsContentUtils::GetMaybeLocalizedString(
102 nsContentUtils::eFORMS_PROPERTIES, "DefaultSummary",
103 GetContent()->OwnerDoc(), defaultSummaryText);
104 RefPtr<nsTextNode> description =
105 new (nodeInfoManager) nsTextNode(nodeInfoManager);
106 description->SetText(defaultSummaryText, false);
107 mDefaultSummary->AppendChildTo(description, false);
108
109 aElements.AppendElement(mDefaultSummary);
110
111 return NS_OK;
112 }
113
AppendAnonymousContentTo(nsTArray<nsIContent * > & aElements,uint32_t aFilter)114 void DetailsFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
115 uint32_t aFilter) {
116 if (mDefaultSummary) {
117 aElements.AppendElement(mDefaultSummary);
118 }
119 }
120
HasMainSummaryFrame(nsIFrame * aSummaryFrame)121 bool DetailsFrame::HasMainSummaryFrame(nsIFrame* aSummaryFrame) {
122 const ChildListIDs flowLists = {kPrincipalList, kOverflowList};
123 for (nsIFrame* frag = this; frag; frag = frag->GetNextInFlow()) {
124 for (const auto& [list, listID] : frag->ChildLists()) {
125 if (!flowLists.contains(listID)) {
126 continue;
127 }
128 for (nsIFrame* child : list) {
129 child = nsPlaceholderFrame::GetRealFrameFor(child);
130 // We skip any non-primary frames such as a list-style-position:inside
131 // bullet frame for the <details> itself.
132 if (!child->IsGeneratedContentFrame()) {
133 return aSummaryFrame == child;
134 }
135 }
136 }
137 }
138 return false;
139 }
140
141 } // namespace mozilla
142