Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / MimeType.cpp
blobda61489c1fe697c6851369e3689333faec2fc45c
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/. */
7 #include "MimeType.h"
8 #include "nsNetUtil.h"
9 #include "nsUnicharUtils.h"
11 template <typename char_type>
12 /* static */ RefPtr<TMimeType<char_type>> TMimeType<char_type>::Parse(
13 const nsTSubstring<char_type>& aMimeType) {
14 // See https://mimesniff.spec.whatwg.org/#parsing-a-mime-type
16 // Steps 1-2
17 const char_type* pos = aMimeType.BeginReading();
18 const char_type* end = aMimeType.EndReading();
19 while (pos < end && NS_IsHTTPWhitespace(*pos)) {
20 ++pos;
22 if (pos == end) {
23 return nullptr;
25 while (end > pos && NS_IsHTTPWhitespace(*(end - 1))) {
26 --end;
29 // Steps 3-4
30 const char_type* typeStart = pos;
31 while (pos < end && *pos != '/') {
32 if (!NS_IsHTTPTokenPoint(*pos)) {
33 return nullptr;
35 ++pos;
37 const char_type* typeEnd = pos;
38 if (typeStart == typeEnd) {
39 return nullptr;
42 // Step 5
43 if (pos == end) {
44 return nullptr;
47 // Step 6
48 ++pos;
50 // Step 7-9
51 const char_type* subtypeStart = pos;
52 const char_type* subtypeEnd = nullptr;
53 while (pos < end && *pos != ';') {
54 if (!NS_IsHTTPTokenPoint(*pos)) {
55 // If we hit a whitespace, check that the rest of
56 // the subtype is whitespace, otherwise fail.
57 if (NS_IsHTTPWhitespace(*pos)) {
58 subtypeEnd = pos;
59 ++pos;
60 while (pos < end && *pos != ';') {
61 if (!NS_IsHTTPWhitespace(*pos)) {
62 return nullptr;
64 ++pos;
66 break;
68 return nullptr;
70 ++pos;
72 if (subtypeEnd == nullptr) {
73 subtypeEnd = pos;
75 if (subtypeStart == subtypeEnd) {
76 return nullptr;
79 // Step 10
80 nsTString<char_type> type;
81 nsTString<char_type> subtype;
82 for (const char_type* c = typeStart; c < typeEnd; ++c) {
83 type.Append(ToLowerCaseASCII(*c));
85 for (const char_type* c = subtypeStart; c < subtypeEnd; ++c) {
86 subtype.Append(ToLowerCaseASCII(*c));
88 RefPtr<TMimeType<char_type>> mimeType =
89 new TMimeType<char_type>(type, subtype);
91 // Step 11
92 while (pos < end) {
93 // Step 11.1
94 ++pos;
96 // Step 11.2
97 while (pos < end && NS_IsHTTPWhitespace(*pos)) {
98 ++pos;
101 const char_type* namePos = pos;
103 // Steps 11.3 and 11.4
104 nsTString<char_type> paramName;
105 bool paramNameHadInvalidChars = false;
106 while (pos < end && *pos != ';' && *pos != '=') {
107 if (!NS_IsHTTPTokenPoint(*pos)) {
108 paramNameHadInvalidChars = true;
110 paramName.Append(ToLowerCaseASCII(*pos));
111 ++pos;
114 // Might as well check for base64 now
115 if (*pos != '=') {
116 // trim leading and trailing spaces
117 while (namePos < pos && NS_IsHTTPWhitespace(*namePos)) {
118 ++namePos;
120 if (namePos < pos && ToLowerCaseASCII(*namePos) == 'b' &&
121 ++namePos < pos && ToLowerCaseASCII(*namePos) == 'a' &&
122 ++namePos < pos && ToLowerCaseASCII(*namePos) == 's' &&
123 ++namePos < pos && ToLowerCaseASCII(*namePos) == 'e' &&
124 ++namePos < pos && ToLowerCaseASCII(*namePos) == '6' &&
125 ++namePos < pos && ToLowerCaseASCII(*namePos) == '4') {
126 while (++namePos < pos && NS_IsHTTPWhitespace(*namePos)) {
128 mimeType->mIsBase64 = namePos == pos;
132 // Step 11.5
133 if (pos < end) {
134 if (*pos == ';') {
135 continue;
137 ++pos;
140 // Step 11.6
141 if (pos == end) {
142 break;
145 // Step 11.7
146 ParameterValue paramValue;
147 bool paramValueHadInvalidChars = false;
149 // Step 11.8
150 if (*pos == '"') {
151 // Step 11.8.1
152 ++pos;
154 // Step 11.8.2
155 while (true) {
156 // Step 11.8.2.1
157 while (pos < end && *pos != '"' && *pos != '\\') {
158 if (!NS_IsHTTPQuotedStringTokenPoint(*pos)) {
159 paramValueHadInvalidChars = true;
161 if (!NS_IsHTTPTokenPoint(*pos)) {
162 paramValue.mRequiresQuoting = true;
164 paramValue.Append(*pos);
165 ++pos;
168 // Step 11.8.2.2
169 if (pos < end && *pos == '\\') {
170 // Step 11.8.2.2.1
171 ++pos;
173 // Step 11.8.2.2.2
174 if (pos < end) {
175 if (!NS_IsHTTPQuotedStringTokenPoint(*pos)) {
176 paramValueHadInvalidChars = true;
178 if (!NS_IsHTTPTokenPoint(*pos)) {
179 paramValue.mRequiresQuoting = true;
181 paramValue.Append(*pos);
182 ++pos;
183 continue;
186 // Step 11.8.2.2.3
187 paramValue.Append('\\');
188 paramValue.mRequiresQuoting = true;
191 // Step 11.8.2.3
192 break;
195 // Step 11.8.3
196 while (pos < end && *pos != ';') {
197 ++pos;
200 // Step 11.9
201 } else {
202 // Step 11.9.1
203 const char_type* paramValueStart = pos;
204 while (pos < end && *pos != ';') {
205 ++pos;
208 // Step 11.9.2
209 const char_type* paramValueLastChar = pos - 1;
210 while (paramValueLastChar >= paramValueStart &&
211 NS_IsHTTPWhitespace(*paramValueLastChar)) {
212 --paramValueLastChar;
215 // Step 11.9.3
216 if (paramValueStart > paramValueLastChar) {
217 continue;
220 for (const char_type* c = paramValueStart; c <= paramValueLastChar; ++c) {
221 if (!NS_IsHTTPQuotedStringTokenPoint(*c)) {
222 paramValueHadInvalidChars = true;
224 if (!NS_IsHTTPTokenPoint(*c)) {
225 paramValue.mRequiresQuoting = true;
227 paramValue.Append(*c);
231 // Step 11.10
232 if (!paramName.IsEmpty() && !paramNameHadInvalidChars &&
233 !paramValueHadInvalidChars) {
234 // XXX Is the assigned value used anywhere?
235 paramValue = mimeType->mParameters.LookupOrInsertWith(paramName, [&] {
236 mimeType->mParameterNames.AppendElement(paramName);
237 return paramValue;
242 // Step 12
243 return mimeType;
246 template <typename char_type>
247 /* static */ nsTArray<nsTDependentSubstring<char_type>>
248 TMimeType<char_type>::SplitMimetype(const nsTSubstring<char_type>& aMimeType) {
249 nsTArray<nsTDependentSubstring<char_type>> mimeTypeParts;
250 bool inQuotes = false;
251 size_t start = 0;
253 for (size_t i = 0; i < aMimeType.Length(); i++) {
254 char_type c = aMimeType[i];
256 if (c == '\"' && (i == 0 || aMimeType[i - 1] != '\\')) {
257 inQuotes = !inQuotes;
258 } else if (c == ',' && !inQuotes) {
259 mimeTypeParts.AppendElement(Substring(aMimeType, start, i - start));
260 start = i + 1;
263 if (start < aMimeType.Length()) {
264 mimeTypeParts.AppendElement(Substring(aMimeType, start));
266 return mimeTypeParts;
269 template <typename char_type>
270 /* static */ bool TMimeType<char_type>::Parse(
271 const nsTSubstring<char_type>& aMimeType,
272 nsTSubstring<char_type>& aOutEssence,
273 nsTSubstring<char_type>& aOutCharset) {
274 static char_type kCHARSET[] = {'c', 'h', 'a', 'r', 's', 'e', 't'};
275 static nsTDependentSubstring<char_type> kCharset(kCHARSET, 7);
277 RefPtr<TMimeType<char_type>> parsed;
278 nsTAutoString<char_type> prevContentType;
279 nsTAutoString<char_type> prevCharset;
281 prevContentType.Assign(aOutEssence);
282 prevCharset.Assign(aOutCharset);
284 nsTArray<nsTDependentSubstring<char_type>> mimeTypeParts =
285 SplitMimetype(aMimeType);
287 for (auto& mimeTypeString : mimeTypeParts) {
288 if (mimeTypeString.EqualsLiteral("error")) {
289 continue;
292 parsed = Parse(mimeTypeString);
294 if (!parsed) {
295 aOutEssence.Truncate();
296 aOutCharset.Truncate();
297 return false;
300 parsed->GetEssence(aOutEssence);
302 if (aOutEssence.EqualsLiteral("*/*")) {
303 aOutEssence.Assign(prevContentType);
304 continue;
307 bool eq = !prevContentType.IsEmpty() && aOutEssence.Equals(prevContentType);
309 if (!eq) {
310 prevContentType.Assign(aOutEssence);
313 bool typeHasCharset = false;
314 if (parsed->GetParameterValue(kCharset, aOutCharset, false, false)) {
315 typeHasCharset = true;
316 } else if (eq) {
317 aOutCharset.Assign(prevCharset);
320 if ((!eq && !prevCharset.IsEmpty()) || typeHasCharset) {
321 prevCharset.Assign(aOutCharset);
325 return true;
328 template <typename char_type>
329 void TMimeType<char_type>::Serialize(nsTSubstring<char_type>& aOutput) const {
330 aOutput.Assign(mType);
331 aOutput.AppendLiteral("/");
332 aOutput.Append(mSubtype);
333 for (uint32_t i = 0; i < mParameterNames.Length(); i++) {
334 auto name = mParameterNames[i];
335 aOutput.AppendLiteral(";");
336 aOutput.Append(name);
337 aOutput.AppendLiteral("=");
338 GetParameterValue(name, aOutput, true);
342 template <typename char_type>
343 void TMimeType<char_type>::GetEssence(nsTSubstring<char_type>& aOutput) const {
344 aOutput.Assign(mType);
345 aOutput.AppendLiteral("/");
346 aOutput.Append(mSubtype);
349 template <typename char_type>
350 bool TMimeType<char_type>::HasParameter(
351 const nsTSubstring<char_type>& aName) const {
352 return mParameters.Get(aName, nullptr);
355 template <typename char_type>
356 bool TMimeType<char_type>::GetParameterValue(
357 const nsTSubstring<char_type>& aName, nsTSubstring<char_type>& aOutput,
358 bool aAppend, bool aWithQuotes) const {
359 if (!aAppend) {
360 aOutput.Truncate();
363 ParameterValue value;
364 if (!mParameters.Get(aName, &value)) {
365 return false;
368 if (aWithQuotes && (value.mRequiresQuoting || value.IsEmpty())) {
369 aOutput.AppendLiteral("\"");
370 const char_type* vcur = value.BeginReading();
371 const char_type* vend = value.EndReading();
372 while (vcur < vend) {
373 if (*vcur == '"' || *vcur == '\\') {
374 aOutput.AppendLiteral("\\");
376 aOutput.Append(*vcur);
377 vcur++;
379 aOutput.AppendLiteral("\"");
380 } else {
381 aOutput.Append(value);
384 return true;
387 template <typename char_type>
388 void TMimeType<char_type>::SetParameterValue(
389 const nsTSubstring<char_type>& aName,
390 const nsTSubstring<char_type>& aValue) {
391 mParameters.WithEntryHandle(aName, [&](auto&& entry) {
392 if (!entry) {
393 mParameterNames.AppendElement(aName);
395 ParameterValue value;
396 value.Append(aValue);
397 entry.InsertOrUpdate(std::move(value));
401 template RefPtr<TMimeType<char16_t>> TMimeType<char16_t>::Parse(
402 const nsTSubstring<char16_t>& aMimeType);
403 template RefPtr<TMimeType<char>> TMimeType<char>::Parse(
404 const nsTSubstring<char>& aMimeType);
405 template bool TMimeType<char16_t>::Parse(
406 const nsTSubstring<char16_t>& aMimeType,
407 nsTSubstring<char16_t>& aOutEssence, nsTSubstring<char16_t>& aOutCharset);
408 template bool TMimeType<char>::Parse(const nsTSubstring<char>& aMimeType,
409 nsTSubstring<char>& aOutEssence,
410 nsTSubstring<char>& aOutCharset);
411 template nsTArray<nsTDependentSubstring<char>> TMimeType<char>::SplitMimetype(
412 const nsTSubstring<char>& aMimeType);
413 template void TMimeType<char16_t>::Serialize(
414 nsTSubstring<char16_t>& aOutput) const;
415 template void TMimeType<char>::Serialize(nsTSubstring<char>& aOutput) const;
416 template void TMimeType<char16_t>::GetEssence(
417 nsTSubstring<char16_t>& aOutput) const;
418 template void TMimeType<char>::GetEssence(nsTSubstring<char>& aOutput) const;
419 template bool TMimeType<char16_t>::HasParameter(
420 const nsTSubstring<char16_t>& aName) const;
421 template bool TMimeType<char>::HasParameter(
422 const nsTSubstring<char>& aName) const;
423 template bool TMimeType<char16_t>::GetParameterValue(
424 const nsTSubstring<char16_t>& aName, nsTSubstring<char16_t>& aOutput,
425 bool aAppend, bool aWithQuotes) const;
426 template bool TMimeType<char>::GetParameterValue(
427 const nsTSubstring<char>& aName, nsTSubstring<char>& aOutput, bool aAppend,
428 bool aWithQuotes) const;
429 template void TMimeType<char16_t>::SetParameterValue(
430 const nsTSubstring<char16_t>& aName, const nsTSubstring<char16_t>& aValue);
431 template void TMimeType<char>::SetParameterValue(
432 const nsTSubstring<char>& aName, const nsTSubstring<char>& aValue);