Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / quota / OriginParser.cpp
blob96e2da56fce3ea6b4e67523527d8fb8132686fd8
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "OriginParser.h"
9 #include <regex>
11 #include "mozilla/OriginAttributes.h"
12 #include "mozilla/dom/quota/Constants.h"
13 #include "mozilla/dom/quota/QuotaCommon.h"
15 namespace mozilla::dom::quota {
17 // static
18 auto OriginParser::ParseOrigin(const nsACString& aOrigin, nsCString& aSpec,
19 OriginAttributes* aAttrs,
20 nsCString& aOriginalSuffix) -> ResultType {
21 MOZ_ASSERT(!aOrigin.IsEmpty());
22 MOZ_ASSERT(aAttrs);
24 nsCString origin(aOrigin);
25 int32_t pos = origin.RFindChar('^');
27 if (pos == kNotFound) {
28 aOriginalSuffix.Truncate();
29 } else {
30 aOriginalSuffix = Substring(origin, pos);
33 OriginAttributes originAttributes;
35 nsCString originNoSuffix;
36 bool ok = originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix);
37 if (!ok) {
38 return InvalidOrigin;
41 OriginParser parser(originNoSuffix);
43 *aAttrs = originAttributes;
44 return parser.Parse(aSpec);
47 auto OriginParser::Parse(nsACString& aSpec) -> ResultType {
48 while (mTokenizer.hasMoreTokens()) {
49 const nsDependentCSubstring& token = mTokenizer.nextToken();
51 HandleToken(token);
53 if (mError) {
54 break;
57 if (!mHandledTokens.IsEmpty()) {
58 mHandledTokens.AppendLiteral(", ");
60 mHandledTokens.Append('\'');
61 mHandledTokens.Append(token);
62 mHandledTokens.Append('\'');
65 if (!mError && mTokenizer.separatorAfterCurrentToken()) {
66 HandleTrailingSeparator();
69 if (mError) {
70 QM_WARNING("Origin '%s' failed to parse, handled tokens: %s", mOrigin.get(),
71 mHandledTokens.get());
73 return (mSchemeType == eChrome || mSchemeType == eAbout) ? ObsoleteOrigin
74 : InvalidOrigin;
77 MOZ_ASSERT(mState == eComplete || mState == eHandledTrailingSeparator);
79 // For IPv6 URL, it should at least have three groups.
80 MOZ_ASSERT_IF(mIPGroup > 0, mIPGroup >= 3);
82 nsAutoCString spec(mScheme);
84 if (mSchemeType == eFile) {
85 spec.AppendLiteral("://");
87 if (mUniversalFileOrigin) {
88 MOZ_ASSERT(mPathnameComponents.Length() == 1);
90 spec.Append(mPathnameComponents[0]);
91 } else {
92 for (uint32_t count = mPathnameComponents.Length(), index = 0;
93 index < count; index++) {
94 spec.Append('/');
95 spec.Append(mPathnameComponents[index]);
99 aSpec = spec;
101 return ValidOrigin;
104 if (mSchemeType == eAbout) {
105 if (mMaybeObsolete) {
106 // The "moz-safe-about+++home" was acciedntally created by a buggy nightly
107 // and can be safely removed.
108 return mHost.EqualsLiteral("home") ? ObsoleteOrigin : InvalidOrigin;
110 spec.Append(':');
111 } else if (mSchemeType != eChrome) {
112 spec.AppendLiteral("://");
115 spec.Append(mHost);
117 if (!mPort.IsNull()) {
118 spec.Append(':');
119 spec.AppendInt(mPort.Value());
122 aSpec = spec;
124 return mScheme.EqualsLiteral("app") ? ObsoleteOrigin : ValidOrigin;
127 void OriginParser::HandleScheme(const nsDependentCSubstring& aToken) {
128 MOZ_ASSERT(!aToken.IsEmpty());
129 MOZ_ASSERT(mState == eExpectingAppIdOrScheme || mState == eExpectingScheme);
131 bool isAbout = false;
132 bool isMozSafeAbout = false;
133 bool isFile = false;
134 bool isChrome = false;
135 if (aToken.EqualsLiteral("http") || aToken.EqualsLiteral("https") ||
136 (isAbout = aToken.EqualsLiteral("about") ||
137 (isMozSafeAbout = aToken.EqualsLiteral("moz-safe-about"))) ||
138 aToken.EqualsLiteral("indexeddb") ||
139 (isFile = aToken.EqualsLiteral("file")) || aToken.EqualsLiteral("app") ||
140 aToken.EqualsLiteral("resource") ||
141 aToken.EqualsLiteral("moz-extension") ||
142 (isChrome = aToken.EqualsLiteral(kChromeOrigin)) ||
143 aToken.EqualsLiteral("uuid")) {
144 mScheme = aToken;
146 if (isAbout) {
147 mSchemeType = eAbout;
148 mState = isMozSafeAbout ? eExpectingEmptyToken1OrHost : eExpectingHost;
149 } else if (isChrome) {
150 mSchemeType = eChrome;
151 if (mTokenizer.hasMoreTokens()) {
152 mError = true;
154 mState = eComplete;
155 } else {
156 if (isFile) {
157 mSchemeType = eFile;
159 mState = eExpectingEmptyToken1;
162 return;
165 QM_WARNING("'%s' is not a valid scheme!", nsCString(aToken).get());
167 mError = true;
170 void OriginParser::HandlePathnameComponent(
171 const nsDependentCSubstring& aToken) {
172 MOZ_ASSERT(!aToken.IsEmpty());
173 MOZ_ASSERT(mState == eExpectingEmptyTokenOrDriveLetterOrPathnameComponent ||
174 mState == eExpectingEmptyTokenOrPathnameComponent);
175 MOZ_ASSERT(mSchemeType == eFile);
177 mPathnameComponents.AppendElement(aToken);
179 mState = mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
180 : eComplete;
183 void OriginParser::HandleToken(const nsDependentCSubstring& aToken) {
184 switch (mState) {
185 case eExpectingAppIdOrScheme: {
186 if (aToken.IsEmpty()) {
187 QM_WARNING("Expected an app id or scheme (not an empty string)!");
189 mError = true;
190 return;
193 if (IsAsciiDigit(aToken.First())) {
194 // nsDependentCSubstring doesn't provice ToInteger()
195 nsCString token(aToken);
197 nsresult rv;
198 Unused << token.ToInteger(&rv);
199 if (NS_SUCCEEDED(rv)) {
200 mState = eExpectingInMozBrowser;
201 return;
205 HandleScheme(aToken);
207 return;
210 case eExpectingInMozBrowser: {
211 if (aToken.Length() != 1) {
212 QM_WARNING("'%zu' is not a valid length for the inMozBrowser flag!",
213 aToken.Length());
215 mError = true;
216 return;
219 if ((aToken.First() != 't') && (aToken.First() != 'f')) {
220 QM_WARNING("'%s' is not a valid value for the inMozBrowser flag!",
221 nsCString(aToken).get());
223 mError = true;
224 return;
227 mState = eExpectingScheme;
229 return;
232 case eExpectingScheme: {
233 if (aToken.IsEmpty()) {
234 QM_WARNING("Expected a scheme (not an empty string)!");
236 mError = true;
237 return;
240 HandleScheme(aToken);
242 return;
245 case eExpectingEmptyToken1: {
246 if (!aToken.IsEmpty()) {
247 QM_WARNING("Expected the first empty token!");
249 mError = true;
250 return;
253 mState = eExpectingEmptyToken2;
255 return;
258 case eExpectingEmptyToken2: {
259 if (!aToken.IsEmpty()) {
260 QM_WARNING("Expected the second empty token!");
262 mError = true;
263 return;
266 if (mSchemeType == eFile) {
267 mState = eExpectingEmptyTokenOrUniversalFileOrigin;
268 } else {
269 if (mSchemeType == eAbout) {
270 mMaybeObsolete = true;
272 mState = eExpectingHost;
275 return;
278 case eExpectingEmptyTokenOrUniversalFileOrigin: {
279 MOZ_ASSERT(mSchemeType == eFile);
281 if (aToken.IsEmpty()) {
282 mState = mTokenizer.hasMoreTokens()
283 ? eExpectingEmptyTokenOrDriveLetterOrPathnameComponent
284 : eComplete;
286 return;
289 if (aToken.EqualsLiteral("UNIVERSAL_FILE_URI_ORIGIN")) {
290 mUniversalFileOrigin = true;
292 mPathnameComponents.AppendElement(aToken);
294 mState = eComplete;
296 return;
299 QM_WARNING(
300 "Expected the third empty token or "
301 "UNIVERSAL_FILE_URI_ORIGIN!");
303 mError = true;
304 return;
307 case eExpectingHost: {
308 if (aToken.IsEmpty()) {
309 QM_WARNING("Expected a host (not an empty string)!");
311 mError = true;
312 return;
315 mHost = aToken;
317 if (aToken.First() == '[') {
318 MOZ_ASSERT(mIPGroup == 0);
320 ++mIPGroup;
321 mState = eExpectingIPV6Token;
323 MOZ_ASSERT(mTokenizer.hasMoreTokens());
324 return;
327 if (mTokenizer.hasMoreTokens()) {
328 if (mSchemeType == eAbout) {
329 QM_WARNING("Expected an empty string after host!");
331 mError = true;
332 return;
335 mState = eExpectingPort;
337 return;
340 mState = eComplete;
342 return;
345 case eExpectingPort: {
346 MOZ_ASSERT(mSchemeType == eNone);
348 if (aToken.IsEmpty()) {
349 QM_WARNING("Expected a port (not an empty string)!");
351 mError = true;
352 return;
355 // nsDependentCSubstring doesn't provice ToInteger()
356 nsCString token(aToken);
358 nsresult rv;
359 uint32_t port = token.ToInteger(&rv);
360 if (NS_SUCCEEDED(rv)) {
361 mPort.SetValue() = port;
362 } else {
363 QM_WARNING("'%s' is not a valid port number!", token.get());
365 mError = true;
366 return;
369 mState = eComplete;
371 return;
374 case eExpectingEmptyTokenOrDriveLetterOrPathnameComponent: {
375 MOZ_ASSERT(mSchemeType == eFile);
377 if (aToken.IsEmpty()) {
378 mPathnameComponents.AppendElement(""_ns);
380 mState = mTokenizer.hasMoreTokens()
381 ? eExpectingEmptyTokenOrPathnameComponent
382 : eComplete;
384 return;
387 if (aToken.Length() == 1 && IsAsciiAlpha(aToken.First())) {
388 mMaybeDriveLetter = true;
390 mPathnameComponents.AppendElement(aToken);
392 mState = mTokenizer.hasMoreTokens()
393 ? eExpectingEmptyTokenOrPathnameComponent
394 : eComplete;
396 return;
399 HandlePathnameComponent(aToken);
401 return;
404 case eExpectingEmptyTokenOrPathnameComponent: {
405 MOZ_ASSERT(mSchemeType == eFile);
407 if (aToken.IsEmpty()) {
408 if (mMaybeDriveLetter) {
409 MOZ_ASSERT(mPathnameComponents.Length() == 1);
411 nsCString& pathnameComponent = mPathnameComponents[0];
412 pathnameComponent.Append(':');
414 mMaybeDriveLetter = false;
415 } else {
416 mPathnameComponents.AppendElement(""_ns);
419 mState = mTokenizer.hasMoreTokens()
420 ? eExpectingEmptyTokenOrPathnameComponent
421 : eComplete;
423 return;
426 HandlePathnameComponent(aToken);
428 return;
431 case eExpectingEmptyToken1OrHost: {
432 MOZ_ASSERT(mSchemeType == eAbout &&
433 mScheme.EqualsLiteral("moz-safe-about"));
435 if (aToken.IsEmpty()) {
436 mState = eExpectingEmptyToken2;
437 } else {
438 mHost = aToken;
439 mState = mTokenizer.hasMoreTokens() ? eExpectingPort : eComplete;
442 return;
445 case eExpectingIPV6Token: {
446 // A safe check for preventing infinity recursion.
447 if (++mIPGroup > 8) {
448 mError = true;
449 return;
452 mHost.AppendLiteral(":");
453 mHost.Append(aToken);
454 if (!aToken.IsEmpty() && aToken.Last() == ']') {
455 mState = mTokenizer.hasMoreTokens() ? eExpectingPort : eComplete;
458 return;
461 default:
462 MOZ_CRASH("Should never get here!");
466 void OriginParser::HandleTrailingSeparator() {
467 MOZ_ASSERT(mState == eComplete);
468 MOZ_ASSERT(mSchemeType == eFile);
470 mPathnameComponents.AppendElement(""_ns);
472 mState = eHandledTrailingSeparator;
475 bool IsUUIDOrigin(const nsCString& aOrigin) {
476 if (!StringBeginsWith(aOrigin, kUUIDOriginScheme)) {
477 return false;
480 static const std::regex pattern(
481 "^uuid://[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab"
482 "][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$");
484 return regex_match(aOrigin.get(), pattern);
487 } // namespace mozilla::dom::quota