Line data Source code
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 : /*
8 : * Implementation of DOM Core's nsIDOMText node.
9 : */
10 :
11 : #include "nsTextNode.h"
12 : #include "mozilla/dom/TextBinding.h"
13 : #include "nsContentUtils.h"
14 : #include "mozilla/dom/DirectionalityUtils.h"
15 : #include "nsIDOMEventListener.h"
16 : #include "nsIDOMMutationEvent.h"
17 : #include "nsIDocument.h"
18 : #include "nsThreadUtils.h"
19 : #include "nsStubMutationObserver.h"
20 : #include "mozilla/IntegerPrintfMacros.h"
21 : #ifdef DEBUG
22 : #include "nsRange.h"
23 : #endif
24 :
25 : using namespace mozilla;
26 : using namespace mozilla::dom;
27 :
28 : /**
29 : * class used to implement attr() generated content
30 : */
31 : class nsAttributeTextNode final : public nsTextNode,
32 : public nsStubMutationObserver
33 : {
34 : public:
35 : NS_DECL_ISUPPORTS_INHERITED
36 :
37 0 : nsAttributeTextNode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
38 : int32_t aNameSpaceID,
39 0 : nsIAtom* aAttrName) :
40 : nsTextNode(aNodeInfo),
41 : mGrandparent(nullptr),
42 : mNameSpaceID(aNameSpaceID),
43 0 : mAttrName(aAttrName)
44 : {
45 0 : NS_ASSERTION(mNameSpaceID != kNameSpaceID_Unknown, "Must know namespace");
46 0 : NS_ASSERTION(mAttrName, "Must have attr name");
47 0 : }
48 :
49 : virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
50 : nsIContent* aBindingParent,
51 : bool aCompileEventHandlers) override;
52 : virtual void UnbindFromTree(bool aDeep = true,
53 : bool aNullParent = true) override;
54 :
55 : NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
56 : NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
57 :
58 0 : virtual nsGenericDOMDataNode *CloneDataNode(mozilla::dom::NodeInfo *aNodeInfo,
59 : bool aCloneText) const override
60 : {
61 : already_AddRefed<mozilla::dom::NodeInfo> ni =
62 0 : RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
63 : nsAttributeTextNode *it = new nsAttributeTextNode(ni,
64 0 : mNameSpaceID,
65 0 : mAttrName);
66 0 : if (it && aCloneText) {
67 0 : it->mText = mText;
68 : }
69 :
70 0 : return it;
71 : }
72 :
73 : // Public method for the event to run
74 0 : void UpdateText() {
75 0 : UpdateText(true);
76 0 : }
77 :
78 : private:
79 0 : virtual ~nsAttributeTextNode() {
80 0 : NS_ASSERTION(!mGrandparent, "We were not unbound!");
81 0 : }
82 :
83 : // Update our text to our parent's current attr value
84 : void UpdateText(bool aNotify);
85 :
86 : // This doesn't need to be a strong pointer because it's only non-null
87 : // while we're bound to the document tree, and it points to an ancestor
88 : // so the ancestor must be bound to the document tree the whole time
89 : // and can't be deleted.
90 : nsIContent* mGrandparent;
91 : // What attribute we're showing
92 : int32_t mNameSpaceID;
93 : nsCOMPtr<nsIAtom> mAttrName;
94 : };
95 :
96 6 : nsTextNode::~nsTextNode()
97 : {
98 9 : }
99 :
100 3050 : NS_IMPL_ISUPPORTS_INHERITED(nsTextNode, nsGenericDOMDataNode, nsIDOMNode,
101 : nsIDOMText, nsIDOMCharacterData)
102 :
103 : JSObject*
104 1 : nsTextNode::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
105 : {
106 1 : return TextBinding::Wrap(aCx, this, aGivenProto);
107 : }
108 :
109 : bool
110 1834 : nsTextNode::IsNodeOfType(uint32_t aFlags) const
111 : {
112 1834 : return !(aFlags & ~(eCONTENT | eTEXT | eDATA_NODE));
113 : }
114 :
115 : nsGenericDOMDataNode*
116 16 : nsTextNode::CloneDataNode(mozilla::dom::NodeInfo *aNodeInfo, bool aCloneText) const
117 : {
118 32 : already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
119 16 : nsTextNode *it = new nsTextNode(ni);
120 16 : if (aCloneText) {
121 16 : it->mText = mText;
122 : }
123 :
124 32 : return it;
125 : }
126 :
127 : nsresult
128 0 : nsTextNode::AppendTextForNormalize(const char16_t* aBuffer, uint32_t aLength,
129 : bool aNotify, nsIContent* aNextSibling)
130 : {
131 : CharacterDataChangeInfo::Details details = {
132 : CharacterDataChangeInfo::Details::eMerge, aNextSibling
133 0 : };
134 0 : return SetTextInternal(mText.GetLength(), 0, aBuffer, aLength, aNotify, &details);
135 : }
136 :
137 : nsresult
138 489 : nsTextNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
139 : nsIContent* aBindingParent, bool aCompileEventHandlers)
140 : {
141 489 : nsresult rv = nsGenericDOMDataNode::BindToTree(aDocument, aParent,
142 : aBindingParent,
143 489 : aCompileEventHandlers);
144 489 : NS_ENSURE_SUCCESS(rv, rv);
145 :
146 489 : SetDirectionFromNewTextNode(this);
147 :
148 489 : return NS_OK;
149 : }
150 :
151 30 : void nsTextNode::UnbindFromTree(bool aDeep, bool aNullParent)
152 : {
153 30 : ResetDirectionSetByTextNode(this);
154 :
155 30 : nsGenericDOMDataNode::UnbindFromTree(aDeep, aNullParent);
156 30 : }
157 :
158 : #ifdef DEBUG
159 : void
160 0 : nsTextNode::List(FILE* out, int32_t aIndent) const
161 : {
162 : int32_t index;
163 0 : for (index = aIndent; --index >= 0; ) fputs(" ", out);
164 :
165 0 : fprintf(out, "Text@%p", static_cast<const void*>(this));
166 0 : fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
167 0 : if (IsCommonAncestorForRangeInSelection()) {
168 : typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
169 : RangeHashTable* ranges =
170 0 : static_cast<RangeHashTable*>(GetProperty(nsGkAtoms::range));
171 0 : fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0);
172 : }
173 0 : fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
174 0 : fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
175 :
176 0 : nsAutoString tmp;
177 0 : ToCString(tmp, 0, mText.GetLength());
178 0 : fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
179 :
180 0 : fputs(">\n", out);
181 0 : }
182 :
183 : void
184 0 : nsTextNode::DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const
185 : {
186 0 : if(aDumpAll) {
187 : int32_t index;
188 0 : for (index = aIndent; --index >= 0; ) fputs(" ", out);
189 :
190 0 : nsAutoString tmp;
191 0 : ToCString(tmp, 0, mText.GetLength());
192 :
193 0 : if(!tmp.EqualsLiteral("\\n")) {
194 0 : fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
195 0 : if(aIndent) fputs("\n", out);
196 : }
197 : }
198 0 : }
199 : #endif
200 :
201 : nsresult
202 0 : NS_NewAttributeContent(nsNodeInfoManager *aNodeInfoManager,
203 : int32_t aNameSpaceID, nsIAtom* aAttrName,
204 : nsIContent** aResult)
205 : {
206 0 : NS_PRECONDITION(aNodeInfoManager, "Missing nodeInfoManager");
207 0 : NS_PRECONDITION(aAttrName, "Must have an attr name");
208 0 : NS_PRECONDITION(aNameSpaceID != kNameSpaceID_Unknown, "Must know namespace");
209 :
210 0 : *aResult = nullptr;
211 :
212 0 : already_AddRefed<mozilla::dom::NodeInfo> ni = aNodeInfoManager->GetTextNodeInfo();
213 :
214 : nsAttributeTextNode* textNode = new nsAttributeTextNode(ni,
215 : aNameSpaceID,
216 0 : aAttrName);
217 0 : NS_ADDREF(*aResult = textNode);
218 :
219 0 : return NS_OK;
220 : }
221 :
222 0 : NS_IMPL_ISUPPORTS_INHERITED(nsAttributeTextNode, nsTextNode,
223 : nsIMutationObserver)
224 :
225 : nsresult
226 0 : nsAttributeTextNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
227 : nsIContent* aBindingParent,
228 : bool aCompileEventHandlers)
229 : {
230 0 : NS_PRECONDITION(aParent && aParent->GetParent(),
231 : "This node can't be a child of the document or of the document root");
232 :
233 0 : nsresult rv = nsTextNode::BindToTree(aDocument, aParent,
234 0 : aBindingParent, aCompileEventHandlers);
235 0 : NS_ENSURE_SUCCESS(rv, rv);
236 :
237 0 : NS_ASSERTION(!mGrandparent, "We were already bound!");
238 0 : mGrandparent = aParent->GetParent();
239 0 : mGrandparent->AddMutationObserver(this);
240 :
241 : // Note that there is no need to notify here, since we have no
242 : // frame yet at this point.
243 0 : UpdateText(false);
244 :
245 0 : return NS_OK;
246 : }
247 :
248 : void
249 0 : nsAttributeTextNode::UnbindFromTree(bool aDeep, bool aNullParent)
250 : {
251 : // UnbindFromTree can be called anytime so we have to be safe.
252 0 : if (mGrandparent) {
253 : // aNullParent might not be true here, but we want to remove the
254 : // mutation observer anyway since we only need it while we're
255 : // in the document.
256 0 : mGrandparent->RemoveMutationObserver(this);
257 0 : mGrandparent = nullptr;
258 : }
259 0 : nsTextNode::UnbindFromTree(aDeep, aNullParent);
260 0 : }
261 :
262 : void
263 0 : nsAttributeTextNode::AttributeChanged(nsIDocument* aDocument,
264 : Element* aElement,
265 : int32_t aNameSpaceID,
266 : nsIAtom* aAttribute,
267 : int32_t aModType,
268 : const nsAttrValue* aOldValue)
269 : {
270 0 : if (aNameSpaceID == mNameSpaceID && aAttribute == mAttrName &&
271 0 : aElement == mGrandparent) {
272 : // Since UpdateText notifies, do it when it's safe to run script. Note
273 : // that if we get unbound while the event is up that's ok -- we'll just
274 : // have no grandparent when it fires, and will do nothing.
275 0 : void (nsAttributeTextNode::*update)() = &nsAttributeTextNode::UpdateText;
276 0 : nsContentUtils::AddScriptRunner(
277 0 : NewRunnableMethod("nsAttributeTextNode::AttributeChanged", this, update));
278 : }
279 0 : }
280 :
281 : void
282 0 : nsAttributeTextNode::NodeWillBeDestroyed(const nsINode* aNode)
283 : {
284 0 : NS_ASSERTION(aNode == static_cast<nsINode*>(mGrandparent), "Wrong node!");
285 0 : mGrandparent = nullptr;
286 0 : }
287 :
288 : void
289 0 : nsAttributeTextNode::UpdateText(bool aNotify)
290 : {
291 0 : if (mGrandparent) {
292 0 : nsAutoString attrValue;
293 0 : mGrandparent->GetAttr(mNameSpaceID, mAttrName, attrValue);
294 0 : SetText(attrValue, aNotify);
295 : }
296 0 : }
297 :
|