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/. */
8 * Implementation of DOM Core's Text node.
11 #include "nsTextNode.h"
12 #include "mozilla/dom/TextBinding.h"
13 #include "nsContentUtils.h"
14 #include "mozilla/dom/DirectionalityUtils.h"
15 #include "mozilla/dom/Document.h"
16 #include "nsThreadUtils.h"
17 #include "nsStubMutationObserver.h"
18 #include "mozilla/IntegerPrintfMacros.h"
23 using namespace mozilla
;
24 using namespace mozilla::dom
;
27 * class used to implement attr() generated content
29 class nsAttributeTextNode final
: public nsTextNode
,
30 public nsStubMutationObserver
{
32 NS_DECL_ISUPPORTS_INHERITED
34 nsAttributeTextNode(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
,
35 int32_t aNameSpaceID
, nsAtom
* aAttrName
,
37 : nsTextNode(std::move(aNodeInfo
)),
38 mGrandparent(nullptr),
39 mNameSpaceID(aNameSpaceID
),
41 mFallback(aFallback
) {
42 NS_ASSERTION(mNameSpaceID
!= kNameSpaceID_Unknown
, "Must know namespace");
43 NS_ASSERTION(mAttrName
, "Must have attr name");
46 nsresult
BindToTree(BindContext
&, nsINode
& aParent
) override
;
47 void UnbindFromTree(UnbindContext
&) override
;
49 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
50 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
52 already_AddRefed
<CharacterData
> CloneDataNode(
53 mozilla::dom::NodeInfo
* aNodeInfo
, bool aCloneText
) const override
{
54 RefPtr
<nsAttributeTextNode
> it
=
55 new (aNodeInfo
->NodeInfoManager()) nsAttributeTextNode(
56 do_AddRef(aNodeInfo
), mNameSpaceID
, mAttrName
, mFallback
);
64 // Public method for the event to run
65 void UpdateText() { UpdateText(true); }
68 virtual ~nsAttributeTextNode() {
69 NS_ASSERTION(!mGrandparent
, "We were not unbound!");
72 // Update our text to our parent's current attr value
73 void UpdateText(bool aNotify
);
75 // This doesn't need to be a strong pointer because it's only non-null
76 // while we're bound to the document tree, and it points to an ancestor
77 // so the ancestor must be bound to the document tree the whole time
78 // and can't be deleted.
79 Element
* mGrandparent
;
80 // What attribute we're showing
82 RefPtr
<nsAtom
> mAttrName
;
83 RefPtr
<nsAtom
> mFallback
;
86 nsTextNode::~nsTextNode() = default;
88 // Use the CC variant of this, even though this class does not define
89 // a new CC participant, to make QIing to the CC interfaces faster.
90 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(nsTextNode
, CharacterData
)
92 JSObject
* nsTextNode::WrapNode(JSContext
* aCx
,
93 JS::Handle
<JSObject
*> aGivenProto
) {
94 return Text_Binding::Wrap(aCx
, this, aGivenProto
);
97 already_AddRefed
<CharacterData
> nsTextNode::CloneDataNode(
98 mozilla::dom::NodeInfo
* aNodeInfo
, bool aCloneText
) const {
99 RefPtr
<nsTextNode
> it
=
100 new (aNodeInfo
->NodeInfoManager()) nsTextNode(do_AddRef(aNodeInfo
));
108 nsresult
nsTextNode::AppendTextForNormalize(const char16_t
* aBuffer
,
109 uint32_t aLength
, bool aNotify
,
110 nsIContent
* aNextSibling
) {
111 CharacterDataChangeInfo::Details details
= {
112 CharacterDataChangeInfo::Details::eMerge
, aNextSibling
};
113 return SetTextInternal(mText
.GetLength(), 0, aBuffer
, aLength
, aNotify
,
117 nsresult
nsTextNode::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
118 nsresult rv
= CharacterData::BindToTree(aContext
, aParent
);
119 NS_ENSURE_SUCCESS(rv
, rv
);
121 SetDirectionFromNewTextNode(this);
126 void nsTextNode::UnbindFromTree(UnbindContext
& aContext
) {
127 CharacterData::UnbindFromTree(aContext
);
128 ResetDirectionSetByTextNode(this, aContext
);
132 void nsTextNode::List(FILE* out
, int32_t aIndent
) const {
134 for (index
= aIndent
; --index
>= 0;) fputs(" ", out
);
136 fprintf(out
, "Text@%p", static_cast<const void*>(this));
137 fprintf(out
, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
138 if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
139 const LinkedList
<AbstractRange
>* ranges
=
140 GetExistingClosestCommonInclusiveAncestorRanges();
141 uint32_t count
= ranges
? ranges
->length() : 0;
142 fprintf(out
, " ranges:%d", count
);
144 fprintf(out
, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
145 fprintf(out
, " refcount=%" PRIuPTR
"<", mRefCnt
.get());
148 ToCString(tmp
, 0, mText
.GetLength());
149 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
154 void nsTextNode::DumpContent(FILE* out
, int32_t aIndent
, bool aDumpAll
) const {
157 for (index
= aIndent
; --index
>= 0;) fputs(" ", out
);
160 ToCString(tmp
, 0, mText
.GetLength());
162 if (!tmp
.EqualsLiteral("\\n")) {
163 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
164 if (aIndent
) fputs("\n", out
);
170 nsresult
NS_NewAttributeContent(nsNodeInfoManager
* aNodeInfoManager
,
171 int32_t aNameSpaceID
, nsAtom
* aAttrName
,
172 nsAtom
* aFallback
, nsIContent
** aResult
) {
173 MOZ_ASSERT(aNodeInfoManager
, "Missing nodeInfoManager");
174 MOZ_ASSERT(aAttrName
, "Must have an attr name");
175 MOZ_ASSERT(aNameSpaceID
!= kNameSpaceID_Unknown
, "Must know namespace");
179 RefPtr
<mozilla::dom::NodeInfo
> ni
= aNodeInfoManager
->GetTextNodeInfo();
181 RefPtr
<nsAttributeTextNode
> textNode
= new (aNodeInfoManager
)
182 nsAttributeTextNode(ni
.forget(), aNameSpaceID
, aAttrName
, aFallback
);
183 textNode
.forget(aResult
);
188 NS_IMPL_ISUPPORTS_INHERITED(nsAttributeTextNode
, nsTextNode
,
191 nsresult
nsAttributeTextNode::BindToTree(BindContext
& aContext
,
193 MOZ_ASSERT(aParent
.IsContent() && aParent
.GetParent(),
194 "This node can't be a child of the document or of "
195 "the document root");
197 nsresult rv
= nsTextNode::BindToTree(aContext
, aParent
);
198 NS_ENSURE_SUCCESS(rv
, rv
);
200 NS_ASSERTION(!mGrandparent
, "We were already bound!");
201 mGrandparent
= aParent
.GetParent()->AsElement();
202 mGrandparent
->AddMutationObserver(this);
204 // Note that there is no need to notify here, since we have no
205 // frame yet at this point.
211 void nsAttributeTextNode::UnbindFromTree(UnbindContext
& aContext
) {
212 // UnbindFromTree can be called anytime so we have to be safe.
214 // aContext might not be true here, but we want to remove the
215 // mutation observer anyway since we only need it while we're
217 mGrandparent
->RemoveMutationObserver(this);
218 mGrandparent
= nullptr;
220 nsTextNode::UnbindFromTree(aContext
);
223 void nsAttributeTextNode::AttributeChanged(Element
* aElement
,
224 int32_t aNameSpaceID
,
225 nsAtom
* aAttribute
, int32_t aModType
,
226 const nsAttrValue
* aOldValue
) {
227 if (aNameSpaceID
== mNameSpaceID
&& aAttribute
== mAttrName
&&
228 aElement
== mGrandparent
) {
229 // Since UpdateText notifies, do it when it's safe to run script. Note
230 // that if we get unbound while the event is up that's ok -- we'll just
231 // have no grandparent when it fires, and will do nothing.
232 void (nsAttributeTextNode::*update
)() = &nsAttributeTextNode::UpdateText
;
233 nsContentUtils::AddScriptRunner(NewRunnableMethod(
234 "nsAttributeTextNode::AttributeChanged", this, update
));
238 void nsAttributeTextNode::NodeWillBeDestroyed(nsINode
* aNode
) {
239 NS_ASSERTION(aNode
== static_cast<nsINode
*>(mGrandparent
), "Wrong node!");
240 mGrandparent
= nullptr;
243 void nsAttributeTextNode::UpdateText(bool aNotify
) {
245 nsAutoString attrValue
;
247 if (!mGrandparent
->GetAttr(mNameSpaceID
, mAttrName
, attrValue
)) {
248 // Attr value does not exist, use fallback instead
249 mFallback
->ToString(attrValue
);
252 SetText(attrValue
, aNotify
);