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/. */
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
17 const char_type
* pos
= aMimeType
.BeginReading();
18 const char_type
* end
= aMimeType
.EndReading();
19 while (pos
< end
&& NS_IsHTTPWhitespace(*pos
)) {
25 while (end
> pos
&& NS_IsHTTPWhitespace(*(end
- 1))) {
30 const char_type
* typeStart
= pos
;
31 while (pos
< end
&& *pos
!= '/') {
32 if (!NS_IsHTTPTokenPoint(*pos
)) {
37 const char_type
* typeEnd
= pos
;
38 if (typeStart
== typeEnd
) {
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
)) {
60 while (pos
< end
&& *pos
!= ';') {
61 if (!NS_IsHTTPWhitespace(*pos
)) {
72 if (subtypeEnd
== nullptr) {
75 if (subtypeStart
== subtypeEnd
) {
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
);
97 while (pos
< end
&& NS_IsHTTPWhitespace(*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
));
114 // Might as well check for base64 now
116 // trim leading and trailing spaces
117 while (namePos
< pos
&& NS_IsHTTPWhitespace(*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
;
146 ParameterValue paramValue
;
147 bool paramValueHadInvalidChars
= false;
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
);
169 if (pos
< end
&& *pos
== '\\') {
175 if (!NS_IsHTTPQuotedStringTokenPoint(*pos
)) {
176 paramValueHadInvalidChars
= true;
178 if (!NS_IsHTTPTokenPoint(*pos
)) {
179 paramValue
.mRequiresQuoting
= true;
181 paramValue
.Append(*pos
);
187 paramValue
.Append('\\');
188 paramValue
.mRequiresQuoting
= true;
196 while (pos
< end
&& *pos
!= ';') {
203 const char_type
* paramValueStart
= pos
;
204 while (pos
< end
&& *pos
!= ';') {
209 const char_type
* paramValueLastChar
= pos
- 1;
210 while (paramValueLastChar
>= paramValueStart
&&
211 NS_IsHTTPWhitespace(*paramValueLastChar
)) {
212 --paramValueLastChar
;
216 if (paramValueStart
> paramValueLastChar
) {
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
);
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
);
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;
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
));
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")) {
292 parsed
= Parse(mimeTypeString
);
295 aOutEssence
.Truncate();
296 aOutCharset
.Truncate();
300 parsed
->GetEssence(aOutEssence
);
302 if (aOutEssence
.EqualsLiteral("*/*")) {
303 aOutEssence
.Assign(prevContentType
);
307 bool eq
= !prevContentType
.IsEmpty() && aOutEssence
.Equals(prevContentType
);
310 prevContentType
.Assign(aOutEssence
);
313 bool typeHasCharset
= false;
314 if (parsed
->GetParameterValue(kCharset
, aOutCharset
, false, false)) {
315 typeHasCharset
= true;
317 aOutCharset
.Assign(prevCharset
);
320 if ((!eq
&& !prevCharset
.IsEmpty()) || typeHasCharset
) {
321 prevCharset
.Assign(aOutCharset
);
328 template <typename char_type
>
329 void TMimeType
<char_type
>::Serialize(nsTSubstring
<char_type
>& aOutput
) const {
330 aOutput
.Assign(mType
);
332 aOutput
.Append(mSubtype
);
333 for (uint32_t i
= 0; i
< mParameterNames
.Length(); i
++) {
334 auto name
= mParameterNames
[i
];
336 aOutput
.Append(name
);
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
);
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 {
363 ParameterValue value
;
364 if (!mParameters
.Get(aName
, &value
)) {
368 if (aWithQuotes
&& (value
.mRequiresQuoting
|| value
.IsEmpty())) {
370 const char_type
* vcur
= value
.BeginReading();
371 const char_type
* vend
= value
.EndReading();
372 while (vcur
< vend
) {
373 if (*vcur
== '"' || *vcur
== '\\') {
374 aOutput
.Append('\\');
376 aOutput
.Append(*vcur
);
381 aOutput
.Append(value
);
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
) {
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
);