nsTextNode.cpp (8285B)
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 Text node. 9 */ 10 11 #include "nsTextNode.h" 12 13 #include "mozilla/IntegerPrintfMacros.h" 14 #include "mozilla/dom/Document.h" 15 #include "mozilla/dom/TextBinding.h" 16 #include "nsContentUtils.h" 17 #include "nsStubMutationObserver.h" 18 #include "nsThreadUtils.h" 19 #ifdef MOZ_DOM_LIST 20 # include "nsRange.h" 21 #endif 22 23 using namespace mozilla; 24 using namespace mozilla::dom; 25 26 /** 27 * class used to implement attr() generated content 28 */ 29 class nsAttributeTextNode final : public nsTextNode, 30 public nsStubMutationObserver { 31 public: 32 NS_DECL_ISUPPORTS_INHERITED 33 34 nsAttributeTextNode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, 35 int32_t aNameSpaceID, nsAtom* aAttrName, 36 nsAtom* aFallback) 37 : nsTextNode(std::move(aNodeInfo)), 38 mGrandparent(nullptr), 39 mNameSpaceID(aNameSpaceID), 40 mAttrName(aAttrName), 41 mFallback(aFallback) { 42 NS_ASSERTION(mNameSpaceID != kNameSpaceID_Unknown, "Must know namespace"); 43 NS_ASSERTION(mAttrName, "Must have attr name"); 44 } 45 46 nsresult BindToTree(BindContext&, nsINode& aParent) override; 47 void UnbindFromTree(UnbindContext&) override; 48 49 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED 50 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED 51 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); 57 if (aCloneText) { 58 it->mBuffer = mBuffer; 59 } 60 61 return it.forget(); 62 } 63 64 // Public method for the event to run 65 void UpdateText() { UpdateText(true); } 66 67 private: 68 virtual ~nsAttributeTextNode() { 69 NS_ASSERTION(!mGrandparent, "We were not unbound!"); 70 } 71 72 // Update our text to our parent's current attr value 73 void UpdateText(bool aNotify); 74 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 81 int32_t mNameSpaceID; 82 RefPtr<nsAtom> mAttrName; 83 RefPtr<nsAtom> mFallback; 84 }; 85 86 nsTextNode::~nsTextNode() = default; 87 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) 91 92 JSObject* nsTextNode::WrapNode(JSContext* aCx, 93 JS::Handle<JSObject*> aGivenProto) { 94 return Text_Binding::Wrap(aCx, this, aGivenProto); 95 } 96 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)); 101 if (aCloneText) { 102 it->mBuffer = mBuffer; 103 } 104 105 return it.forget(); 106 } 107 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(mBuffer.GetLength(), 0, aBuffer, aLength, aNotify, 114 MutationEffectOnScript::KeepTrustWorthiness, &details); 115 } 116 117 #ifdef MOZ_DOM_LIST 118 void nsTextNode::List(FILE* out, int32_t aIndent) const { 119 int32_t index; 120 for (index = aIndent; --index >= 0;) fputs(" ", out); 121 122 fprintf(out, "Text@%p", static_cast<const void*>(this)); 123 fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); 124 if (IsClosestCommonInclusiveAncestorForRangeInSelection()) { 125 const LinkedList<AbstractRange>* ranges = 126 GetExistingClosestCommonInclusiveAncestorRanges(); 127 uint32_t count = ranges ? ranges->length() : 0; 128 fprintf(out, " ranges:%d", count); 129 } 130 fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame())); 131 fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get()); 132 133 nsAutoString tmp; 134 ToCString(tmp, 0, mBuffer.GetLength()); 135 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out); 136 137 fputs(">\n", out); 138 } 139 140 void nsTextNode::DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const { 141 if (aDumpAll) { 142 int32_t index; 143 for (index = aIndent; --index >= 0;) fputs(" ", out); 144 145 nsAutoString tmp; 146 ToCString(tmp, 0, mBuffer.GetLength()); 147 148 if (!tmp.EqualsLiteral("\\n")) { 149 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out); 150 if (aIndent) fputs("\n", out); 151 } 152 } 153 } 154 #endif 155 156 nsresult NS_NewAttributeContent(nsNodeInfoManager* aNodeInfoManager, 157 int32_t aNameSpaceID, nsAtom* aAttrName, 158 nsAtom* aFallback, nsIContent** aResult) { 159 MOZ_ASSERT(aNodeInfoManager, "Missing nodeInfoManager"); 160 MOZ_ASSERT(aAttrName, "Must have an attr name"); 161 MOZ_ASSERT(aNameSpaceID != kNameSpaceID_Unknown, "Must know namespace"); 162 163 *aResult = nullptr; 164 165 RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfoManager->GetTextNodeInfo(); 166 167 RefPtr<nsAttributeTextNode> textNode = new (aNodeInfoManager) 168 nsAttributeTextNode(ni.forget(), aNameSpaceID, aAttrName, aFallback); 169 textNode.forget(aResult); 170 171 return NS_OK; 172 } 173 174 NS_IMPL_ISUPPORTS_INHERITED(nsAttributeTextNode, nsTextNode, 175 nsIMutationObserver) 176 177 nsresult nsAttributeTextNode::BindToTree(BindContext& aContext, 178 nsINode& aParent) { 179 MOZ_ASSERT(aParent.IsContent() && aParent.GetParent(), 180 "This node can't be a child of the document or of " 181 "the document root"); 182 183 nsresult rv = nsTextNode::BindToTree(aContext, aParent); 184 NS_ENSURE_SUCCESS(rv, rv); 185 186 NS_ASSERTION(!mGrandparent, "We were already bound!"); 187 mGrandparent = aParent.GetParent()->AsElement(); 188 mGrandparent->AddMutationObserver(this); 189 190 // Note that there is no need to notify here, since we have no 191 // frame yet at this point. 192 UpdateText(false); 193 194 return NS_OK; 195 } 196 197 void nsAttributeTextNode::UnbindFromTree(UnbindContext& aContext) { 198 // UnbindFromTree can be called anytime so we have to be safe. 199 if (mGrandparent) { 200 // aContext might not be true here, but we want to remove the 201 // mutation observer anyway since we only need it while we're 202 // in the document. 203 mGrandparent->RemoveMutationObserver(this); 204 mGrandparent = nullptr; 205 } 206 nsTextNode::UnbindFromTree(aContext); 207 } 208 209 void nsAttributeTextNode::AttributeChanged(Element* aElement, 210 int32_t aNameSpaceID, 211 nsAtom* aAttribute, AttrModType, 212 const nsAttrValue* aOldValue) { 213 if (aNameSpaceID == mNameSpaceID && aAttribute == mAttrName && 214 aElement == mGrandparent) { 215 // Since UpdateText notifies, do it when it's safe to run script. Note 216 // that if we get unbound while the event is up that's ok -- we'll just 217 // have no grandparent when it fires, and will do nothing. 218 void (nsAttributeTextNode::*update)() = &nsAttributeTextNode::UpdateText; 219 nsContentUtils::AddScriptRunner(NewRunnableMethod( 220 "nsAttributeTextNode::AttributeChanged", this, update)); 221 } 222 } 223 224 void nsAttributeTextNode::NodeWillBeDestroyed(nsINode* aNode) { 225 NS_ASSERTION(aNode == static_cast<nsINode*>(mGrandparent), "Wrong node!"); 226 mGrandparent = nullptr; 227 } 228 229 void nsAttributeTextNode::UpdateText(bool aNotify) { 230 if (mGrandparent) { 231 nsAutoString attrValue; 232 233 if (!mGrandparent->GetAttr(mNameSpaceID, mAttrName, attrValue)) { 234 // Attr value does not exist, use fallback instead 235 mFallback->ToString(attrValue); 236 } 237 238 SetText(attrValue, aNotify); 239 } 240 }