Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / base / MimeType.cpp
blob56c2fafe7d07effbb7c8c7018a5ab4a2f52c3fcd
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 */ mozilla::UniquePtr<TMimeType<char_type>>
13 TMimeType<char_type>::Parse(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 mozilla::UniquePtr<TMimeType<char_type>> mimeType(
89 mozilla::MakeUnique<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 void TMimeType<char_type>::Serialize(nsTSubstring<char_type>& aOutput) const {
248 aOutput.Assign(mType);
249 aOutput.AppendLiteral("/");
250 aOutput.Append(mSubtype);
251 for (uint32_t i = 0; i < mParameterNames.Length(); i++) {
252 auto name = mParameterNames[i];
253 aOutput.AppendLiteral(";");
254 aOutput.Append(name);
255 aOutput.AppendLiteral("=");
256 GetParameterValue(name, aOutput, true);
260 template <typename char_type>
261 void TMimeType<char_type>::GetFullType(nsTSubstring<char_type>& aOutput) const {
262 aOutput.Assign(mType);
263 aOutput.AppendLiteral("/");
264 aOutput.Append(mSubtype);
267 template <typename char_type>
268 bool TMimeType<char_type>::HasParameter(
269 const nsTSubstring<char_type>& aName) const {
270 return mParameters.Get(aName, nullptr);
273 template <typename char_type>
274 bool TMimeType<char_type>::GetParameterValue(
275 const nsTSubstring<char_type>& aName, nsTSubstring<char_type>& aOutput,
276 bool aAppend) const {
277 if (!aAppend) {
278 aOutput.Truncate();
281 ParameterValue value;
282 if (!mParameters.Get(aName, &value)) {
283 return false;
286 if (value.mRequiresQuoting || value.IsEmpty()) {
287 aOutput.AppendLiteral("\"");
288 const char_type* vcur = value.BeginReading();
289 const char_type* vend = value.EndReading();
290 while (vcur < vend) {
291 if (*vcur == '"' || *vcur == '\\') {
292 aOutput.AppendLiteral("\\");
294 aOutput.Append(*vcur);
295 vcur++;
297 aOutput.AppendLiteral("\"");
298 } else {
299 aOutput.Append(value);
302 return true;
305 template <typename char_type>
306 void TMimeType<char_type>::SetParameterValue(
307 const nsTSubstring<char_type>& aName,
308 const nsTSubstring<char_type>& aValue) {
309 mParameters.WithEntryHandle(aName, [&](auto&& entry) {
310 if (!entry) {
311 mParameterNames.AppendElement(aName);
313 ParameterValue value;
314 value.Append(aValue);
315 entry.InsertOrUpdate(std::move(value));
319 template mozilla::UniquePtr<TMimeType<char16_t>> TMimeType<char16_t>::Parse(
320 const nsTSubstring<char16_t>& aMimeType);
321 template mozilla::UniquePtr<TMimeType<char>> TMimeType<char>::Parse(
322 const nsTSubstring<char>& aMimeType);
323 template void TMimeType<char16_t>::Serialize(
324 nsTSubstring<char16_t>& aOutput) const;
325 template void TMimeType<char>::Serialize(nsTSubstring<char>& aOutput) const;
326 template void TMimeType<char16_t>::GetFullType(
327 nsTSubstring<char16_t>& aOutput) const;
328 template void TMimeType<char>::GetFullType(nsTSubstring<char>& aOutput) const;
329 template bool TMimeType<char16_t>::HasParameter(
330 const nsTSubstring<char16_t>& aName) const;
331 template bool TMimeType<char>::HasParameter(
332 const nsTSubstring<char>& aName) const;
333 template bool TMimeType<char16_t>::GetParameterValue(
334 const nsTSubstring<char16_t>& aName, nsTSubstring<char16_t>& aOutput,
335 bool aAppend) const;
336 template bool TMimeType<char>::GetParameterValue(
337 const nsTSubstring<char>& aName, nsTSubstring<char>& aOutput,
338 bool aAppend) const;
339 template void TMimeType<char16_t>::SetParameterValue(
340 const nsTSubstring<char16_t>& aName, const nsTSubstring<char16_t>& aValue);
341 template void TMimeType<char>::SetParameterValue(
342 const nsTSubstring<char>& aName, const nsTSubstring<char>& aValue);