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 "mozilla/dom/Text.h"
8 #include "nsTextNode.h"
9 #include "mozAutoDocUpdate.h"
10 
11 namespace mozilla::dom {
12 
SplitText(uint32_t aOffset,ErrorResult & aRv)13 already_AddRefed<Text> Text::SplitText(uint32_t aOffset, ErrorResult& aRv) {
14   nsAutoString cutText;
15   const uint32_t length = TextLength();
16 
17   if (aOffset > length) {
18     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
19     return nullptr;
20   }
21 
22   const uint32_t cutStartOffset = aOffset;
23   const uint32_t cutLength = length - aOffset;
24   SubstringData(cutStartOffset, cutLength, cutText, aRv);
25   if (aRv.Failed()) {
26     return nullptr;
27   }
28 
29   Document* document = GetComposedDoc();
30   mozAutoDocUpdate updateBatch(document, true);
31 
32   // Use Clone for creating the new node so that the new node is of same class
33   // as this node!
34   RefPtr<CharacterData> clone = CloneDataNode(mNodeInfo, false);
35   MOZ_ASSERT(clone && clone->IsText());
36   RefPtr<Text> newContent = static_cast<Text*>(clone.get());
37 
38   // nsRange expects the CharacterDataChanged notification is followed
39   // by an insertion of |newContent|. If you change this code,
40   // make sure you make the appropriate changes in nsRange.
41   newContent->SetText(cutText, true);  // XXX should be false?
42 
43   CharacterDataChangeInfo::Details details = {
44       CharacterDataChangeInfo::Details::eSplit, newContent};
45   nsresult rv =
46       SetTextInternal(cutStartOffset, cutLength, nullptr, 0, true, &details);
47   if (NS_FAILED(rv)) {
48     aRv.Throw(rv);
49     return nullptr;
50   }
51 
52   nsCOMPtr<nsINode> parent = GetParentNode();
53   if (parent) {
54     nsCOMPtr<nsIContent> beforeNode = GetNextSibling();
55     parent->InsertChildBefore(newContent, beforeNode, true, IgnoreErrors());
56   }
57 
58   return newContent.forget();
59 }
60 
FirstLogicallyAdjacentTextNode(Text * aNode)61 static Text* FirstLogicallyAdjacentTextNode(Text* aNode) {
62   do {
63     nsIContent* sibling = aNode->GetPreviousSibling();
64     if (!sibling || !sibling->IsText()) {
65       return aNode;
66     }
67     aNode = static_cast<Text*>(sibling);
68   } while (1);  // Must run out of previous siblings eventually!
69 }
70 
LastLogicallyAdjacentTextNode(Text * aNode)71 static Text* LastLogicallyAdjacentTextNode(Text* aNode) {
72   do {
73     nsIContent* sibling = aNode->GetNextSibling();
74     if (!sibling || !sibling->IsText()) {
75       return aNode;
76     }
77 
78     aNode = static_cast<Text*>(sibling);
79   } while (1);  // Must run out of next siblings eventually!
80 }
81 
GetWholeText(nsAString & aWholeText,ErrorResult & aRv)82 void Text::GetWholeText(nsAString& aWholeText, ErrorResult& aRv) {
83   nsIContent* parent = GetParent();
84 
85   // Handle parent-less nodes
86   if (!parent) {
87     GetData(aWholeText);
88     return;
89   }
90 
91   Text* first = FirstLogicallyAdjacentTextNode(this);
92   Text* last = LastLogicallyAdjacentTextNode(this);
93 
94   aWholeText.Truncate();
95 
96   nsAutoString tmp;
97 
98   while (true) {
99     first->GetData(tmp);
100     aWholeText.Append(tmp);
101 
102     if (first == last) {
103       break;
104     }
105 
106     nsIContent* next = first->GetNextSibling();
107     MOZ_ASSERT(next && next->IsText(),
108                "How did we run out of text before hitting `last`?");
109     first = static_cast<Text*>(next);
110   }
111 }
112 
113 /* static */
Constructor(const GlobalObject & aGlobal,const nsAString & aData,ErrorResult & aRv)114 already_AddRefed<Text> Text::Constructor(const GlobalObject& aGlobal,
115                                          const nsAString& aData,
116                                          ErrorResult& aRv) {
117   nsCOMPtr<nsPIDOMWindowInner> window =
118       do_QueryInterface(aGlobal.GetAsSupports());
119   if (!window || !window->GetDoc()) {
120     aRv.Throw(NS_ERROR_FAILURE);
121     return nullptr;
122   }
123 
124   return window->GetDoc()->CreateTextNode(aData);
125 }
126 
HasTextForTranslation()127 bool Text::HasTextForTranslation() {
128   if (mText.Is2b()) {
129     // The fragment contains non-8bit characters which means there
130     // was at least one "interesting" character to trigger non-8bit.
131     return true;
132   }
133 
134   if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE) &&
135       HasFlag(NS_TEXT_IS_ONLY_WHITESPACE)) {
136     return false;
137   }
138 
139   const char* cp = mText.Get1b();
140   const char* end = cp + mText.GetLength();
141 
142   unsigned char ch;
143   for (; cp < end; cp++) {
144     ch = *cp;
145 
146     // These are the characters that are letters
147     // in the first 256 UTF-8 codepoints.
148     if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
149         (ch >= 192 && ch <= 214) || (ch >= 216 && ch <= 246) || (ch >= 248)) {
150       return true;
151     }
152   }
153 
154   return false;
155 }
156 
157 }  // namespace mozilla::dom
158