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