1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsHTMLTags.h"
7 #include "nsCRT.h"
8 #include "nsDataHashtable.h"
9 #include "nsReadableUtils.h"
10 #include "nsString.h"
11 #include "nsStaticAtom.h"
12 #include "nsUnicharUtils.h"
13 #include "mozilla/HashFunctions.h"
14 #include <algorithm>
15
16 using namespace mozilla;
17
18 // static array of unicode tag names
19 #define HTML_TAG(_tag, _classname, _interfacename) (u"" #_tag),
20 #define HTML_OTHER(_tag)
21 const char16_t* const nsHTMLTags::sTagUnicodeTable[] = {
22 #include "nsHTMLTagList.h"
23 };
24 #undef HTML_TAG
25 #undef HTML_OTHER
26
27 int32_t nsHTMLTags::gTableRefCount;
28 nsHTMLTags::TagStringHash* nsHTMLTags::gTagTable;
29 nsHTMLTags::TagAtomHash* nsHTMLTags::gTagAtomTable;
30
31 #define NS_HTMLTAG_NAME_MAX_LENGTH 10
32
33 // This would use NS_STATIC_ATOM_DEFN if it wasn't an array.
34 nsStaticAtom* nsHTMLTags::sTagAtomTable[eHTMLTag_userdefined - 1];
35
36 #define HTML_TAG(_tag, _classname, _interfacename) \
37 NS_STATIC_ATOM_BUFFER(_tag, #_tag)
38 #define HTML_OTHER(_tag)
39 #include "nsHTMLTagList.h"
40 #undef HTML_TAG
41 #undef HTML_OTHER
42
RegisterAtoms(void)43 /* static */ void nsHTMLTags::RegisterAtoms(void) {
44 // This would use NS_STATIC_ATOM_SETUP if it wasn't an array.
45 static const nsStaticAtomSetup sTagAtomSetup[] = {
46 #define HTML_TAG(_tag, _classname, _interfacename) \
47 {_tag##_buffer, &nsHTMLTags::sTagAtomTable[eHTMLTag_##_tag - 1]},
48 #define HTML_OTHER(_tag)
49 #include "nsHTMLTagList.h"
50 #undef HTML_TAG
51 #undef HTML_OTHER
52 };
53
54 NS_RegisterStaticAtoms(sTagAtomSetup);
55
56 #if defined(DEBUG)
57 {
58 // let's verify that all names in the the table are lowercase...
59 for (int32_t i = 0; i < NS_HTML_TAG_MAX; ++i) {
60 nsAutoString temp1((char16_t*)sTagAtomSetup[i].mString);
61 nsAutoString temp2((char16_t*)sTagAtomSetup[i].mString);
62 ToLowerCase(temp1);
63 NS_ASSERTION(temp1.Equals(temp2), "upper case char in table");
64 }
65
66 // let's verify that all names in the unicode strings above are
67 // correct.
68 for (int32_t i = 0; i < NS_HTML_TAG_MAX; ++i) {
69 nsAutoString temp1(sTagUnicodeTable[i]);
70 nsAutoString temp2((char16_t*)sTagAtomSetup[i].mString);
71 NS_ASSERTION(temp1.Equals(temp2), "Bad unicode tag name!");
72 }
73
74 // let's verify that NS_HTMLTAG_NAME_MAX_LENGTH is correct
75 uint32_t maxTagNameLength = 0;
76 for (int32_t i = 0; i < NS_HTML_TAG_MAX; ++i) {
77 uint32_t len = NS_strlen(sTagUnicodeTable[i]);
78 maxTagNameLength = std::max(len, maxTagNameLength);
79 }
80 NS_ASSERTION(maxTagNameLength == NS_HTMLTAG_NAME_MAX_LENGTH,
81 "NS_HTMLTAG_NAME_MAX_LENGTH not set correctly!");
82 }
83 #endif
84 }
85
86 // static
AddRefTable(void)87 nsresult nsHTMLTags::AddRefTable(void) {
88 if (gTableRefCount++ == 0) {
89 NS_ASSERTION(!gTagTable && !gTagAtomTable, "pre existing hash!");
90
91 gTagTable = new TagStringHash(64);
92 gTagAtomTable = new TagAtomHash(64);
93
94 // Fill in gTagTable with the above static char16_t strings as
95 // keys and the value of the corresponding enum as the value in
96 // the table.
97
98 int32_t i;
99 for (i = 0; i < NS_HTML_TAG_MAX; ++i) {
100 const char16_t* tagName = sTagUnicodeTable[i];
101 const nsHTMLTag tagValue = static_cast<nsHTMLTag>(i + 1);
102 // We use AssignLiteral here to avoid a string copy. This is okay
103 // because this is truly static data.
104 nsString tmp;
105 tmp.AssignLiteral(tagName, nsString::char_traits::length(tagName));
106 gTagTable->Put(tmp, tagValue);
107 gTagAtomTable->Put(sTagAtomTable[i], tagValue);
108 }
109 }
110
111 return NS_OK;
112 }
113
114 // static
ReleaseTable(void)115 void nsHTMLTags::ReleaseTable(void) {
116 if (0 == --gTableRefCount) {
117 delete gTagTable;
118 delete gTagAtomTable;
119 gTagTable = nullptr;
120 gTagAtomTable = nullptr;
121 }
122 }
123
124 // static
StringTagToId(const nsAString & aTagName)125 nsHTMLTag nsHTMLTags::StringTagToId(const nsAString& aTagName) {
126 uint32_t length = aTagName.Length();
127
128 if (length > NS_HTMLTAG_NAME_MAX_LENGTH) {
129 return eHTMLTag_userdefined;
130 }
131
132 // Setup a stack allocated string buffer with the appropriate length.
133 nsAutoString lowerCase;
134 lowerCase.SetLength(length);
135
136 // Operate on the raw buffers to avoid bounds checks.
137 auto src = aTagName.BeginReading();
138 auto dst = lowerCase.BeginWriting();
139
140 // Fast lowercasing-while-copying of ASCII characters into a
141 // nsString buffer.
142
143 for (uint32_t i = 0; i < length; i++) {
144 char16_t c = src[i];
145
146 if (c <= 'Z' && c >= 'A') {
147 c |= 0x20; // Lowercase the ASCII character.
148 }
149
150 dst[i] = c; // Copy ASCII character.
151 }
152
153 return CaseSensitiveStringTagToId(lowerCase);
154 }
155
156 #ifdef DEBUG
TestTagTable()157 void nsHTMLTags::TestTagTable() {
158 const char16_t* tag;
159 nsHTMLTag id;
160 RefPtr<nsAtom> atom;
161
162 nsHTMLTags::AddRefTable();
163 // Make sure we can find everything we are supposed to
164 for (int i = 0; i < NS_HTML_TAG_MAX; ++i) {
165 tag = sTagUnicodeTable[i];
166 const nsAString& tagString = nsDependentString(tag);
167 id = StringTagToId(tagString);
168 NS_ASSERTION(id != eHTMLTag_userdefined, "can't find tag id");
169
170 nsAutoString uname(tagString);
171 ToUpperCase(uname);
172 NS_ASSERTION(id == StringTagToId(uname), "wrong id");
173
174 NS_ASSERTION(id == CaseSensitiveStringTagToId(tagString), "wrong id");
175
176 atom = NS_Atomize(tag);
177 NS_ASSERTION(id == CaseSensitiveAtomTagToId(atom), "wrong id");
178 }
179
180 // Make sure we don't find things that aren't there
181 id = StringTagToId(NS_LITERAL_STRING("@"));
182 NS_ASSERTION(id == eHTMLTag_userdefined, "found @");
183 id = StringTagToId(NS_LITERAL_STRING("zzzzz"));
184 NS_ASSERTION(id == eHTMLTag_userdefined, "found zzzzz");
185
186 atom = NS_Atomize("@");
187 id = CaseSensitiveAtomTagToId(atom);
188 NS_ASSERTION(id == eHTMLTag_userdefined, "found @");
189 atom = NS_Atomize("zzzzz");
190 id = CaseSensitiveAtomTagToId(atom);
191 NS_ASSERTION(id == eHTMLTag_userdefined, "found zzzzz");
192
193 ReleaseTable();
194 }
195
196 #endif // DEBUG
197