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