URL.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /*
  2. * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
  3. * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
  4. * Copyright (c) 2023, Karol Kosek <krkk@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <LibURL/URL.h>
  9. #include <LibWeb/Fetch/Infrastructure/URL.h>
  10. #include <Userland/Libraries/LibWeb/Infra/Base64.h>
  11. namespace Web::Fetch::Infrastructure {
  12. // https://fetch.spec.whatwg.org/#is-local
  13. bool is_local_url(URL::URL const& url)
  14. {
  15. // A URL is local if its scheme is a local scheme.
  16. return any_of(LOCAL_SCHEMES, [&](auto scheme) { return url.scheme() == scheme; });
  17. }
  18. // https://fetch.spec.whatwg.org/#fetch-scheme
  19. bool is_fetch_scheme(StringView scheme)
  20. {
  21. // A fetch scheme is "about", "blob", "data", "file", or an HTTP(S) scheme.
  22. return any_of(FETCH_SCHEMES, [&](auto fetch_scheme) { return scheme == fetch_scheme; });
  23. }
  24. // https://fetch.spec.whatwg.org/#http-scheme
  25. bool is_http_or_https_scheme(StringView scheme)
  26. {
  27. // An HTTP(S) scheme is "http" or "https".
  28. return any_of(HTTP_SCHEMES, [&](auto http_scheme) { return scheme == http_scheme; });
  29. }
  30. // https://fetch.spec.whatwg.org/#data-url-processor
  31. ErrorOr<DataURL> process_data_url(URL::URL const& data_url)
  32. {
  33. // 1. Assert: dataURL’s scheme is "data".
  34. VERIFY(data_url.scheme() == "data");
  35. // 2. Let input be the result of running the URL serializer on dataURL with exclude fragment set to true.
  36. auto input_serialized = data_url.serialize(URL::ExcludeFragment::Yes);
  37. StringView input = input_serialized;
  38. // 3. Remove the leading "data:" from input.
  39. input = input.substring_view("data:"sv.length());
  40. // 4. Let position point at the start of input.
  41. // 5. Let mimeType be the result of collecting a sequence of code points that are not equal to U+002C (,), given position.
  42. auto position = input.find(',');
  43. auto mime_type = input.substring_view(0, position.value_or(input.length()));
  44. // 6. Strip leading and trailing ASCII whitespace from mimeType.
  45. mime_type = mime_type.trim_whitespace(TrimMode::Both);
  46. // 7. If position is past the end of input, then return failure.
  47. if (!position.has_value())
  48. return Error::from_string_literal("Missing a comma character");
  49. // 8. Advance position by 1.
  50. position = position.value() + 1;
  51. // 9. Let encodedBody be the remainder of input.
  52. auto encoded_body = input.substring_view(position.value());
  53. // 10. Let body be the percent-decoding of encodedBody.
  54. auto body = URL::percent_decode(encoded_body).to_byte_buffer();
  55. // 11. If mimeType ends with U+003B (;), followed by zero or more U+0020 SPACE, followed by an ASCII case-insensitive match for "base64", then:
  56. if (mime_type.ends_with("base64"sv, CaseSensitivity::CaseInsensitive)) {
  57. auto trimmed_substring_view = mime_type.substring_view(0, mime_type.length() - 6);
  58. trimmed_substring_view = trimmed_substring_view.trim(" "sv, TrimMode::Right);
  59. if (trimmed_substring_view.ends_with(';')) {
  60. // 1. Let stringBody be the isomorphic decode of body.
  61. auto string_body = StringView(body);
  62. // 2. Set body to the forgiving-base64 decode of stringBody.
  63. // 3. If body is failure, then return failure.
  64. body = TRY(Infra::decode_forgiving_base64(string_body));
  65. // 4. Remove the last 6 code points from mimeType.
  66. // 5. Remove trailing U+0020 SPACE code points from mimeType, if any.
  67. // 6. Remove the last U+003B (;) from mimeType.
  68. mime_type = trimmed_substring_view.substring_view(0, trimmed_substring_view.length() - 1);
  69. }
  70. }
  71. // 12. If mimeType starts with ";", then prepend "text/plain" to mimeType.
  72. StringBuilder builder;
  73. if (mime_type.starts_with(';')) {
  74. builder.append("text/plain"sv);
  75. builder.append(mime_type);
  76. mime_type = builder.string_view();
  77. }
  78. // FIXME: Parse the MIME type's components according to https://mimesniff.spec.whatwg.org/#parse-a-mime-type
  79. // FIXME: 13. Let mimeTypeRecord be the result of parsing mimeType.
  80. auto mime_type_record = mime_type.trim("\n\r\t "sv, TrimMode::Both);
  81. // 14. If mimeTypeRecord is failure, then set mimeTypeRecord to text/plain;charset=US-ASCII.
  82. if (mime_type_record.is_empty())
  83. mime_type_record = "text/plain;charset=US-ASCII"sv;
  84. // 15. Return a new data: URL struct whose MIME type is mimeTypeRecord and body is body.
  85. return DataURL { TRY(String::from_utf8(mime_type_record)), body };
  86. }
  87. }