Serialize.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/StringBuilder.h>
  7. #include <AK/Utf8View.h>
  8. #include <LibWeb/CSS/Serialize.h>
  9. namespace Web::CSS {
  10. // https://www.w3.org/TR/cssom-1/#escape-a-character
  11. ErrorOr<void> escape_a_character(StringBuilder& builder, u32 character)
  12. {
  13. TRY(builder.try_append('\\'));
  14. TRY(builder.try_append_code_point(character));
  15. return {};
  16. }
  17. // https://www.w3.org/TR/cssom-1/#escape-a-character-as-code-point
  18. ErrorOr<void> escape_a_character_as_code_point(StringBuilder& builder, u32 character)
  19. {
  20. TRY(builder.try_appendff("\\{:x} ", character));
  21. return {};
  22. }
  23. // https://www.w3.org/TR/cssom-1/#serialize-an-identifier
  24. ErrorOr<void> serialize_an_identifier(StringBuilder& builder, StringView ident)
  25. {
  26. Utf8View characters { ident };
  27. auto first_character = characters.is_empty() ? 0 : *characters.begin();
  28. // To serialize an identifier means to create a string represented by the concatenation of,
  29. // for each character of the identifier:
  30. for (auto character : characters) {
  31. // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER (U+FFFD).
  32. if (character == 0) {
  33. TRY(builder.try_append_code_point(0xFFFD));
  34. continue;
  35. }
  36. // If the character is in the range [\1-\1f] (U+0001 to U+001F) or is U+007F,
  37. // then the character escaped as code point.
  38. if ((character >= 0x0001 && character <= 0x001F) || (character == 0x007F)) {
  39. TRY(escape_a_character_as_code_point(builder, character));
  40. continue;
  41. }
  42. // If the character is the first character and is in the range [0-9] (U+0030 to U+0039),
  43. // then the character escaped as code point.
  44. if (builder.is_empty() && character >= '0' && character <= '9') {
  45. TRY(escape_a_character_as_code_point(builder, character));
  46. continue;
  47. }
  48. // If the character is the second character and is in the range [0-9] (U+0030 to U+0039)
  49. // and the first character is a "-" (U+002D), then the character escaped as code point.
  50. if (builder.length() == 1 && first_character == '-' && character >= '0' && character <= '9') {
  51. TRY(escape_a_character_as_code_point(builder, character));
  52. continue;
  53. }
  54. // If the character is the first character and is a "-" (U+002D), and there is no second
  55. // character, then the escaped character.
  56. if (builder.is_empty() && character == '-' && characters.length() == 1) {
  57. TRY(escape_a_character(builder, character));
  58. continue;
  59. }
  60. // If the character is not handled by one of the above rules and is greater than or equal to U+0080, is "-" (U+002D) or "_" (U+005F), or is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to U+005A), or \[a-z] (U+0061 to U+007A), then the character itself.
  61. if ((character >= 0x0080)
  62. || (character == '-') || (character == '_')
  63. || (character >= '0' && character <= '9')
  64. || (character >= 'A' && character <= 'Z')
  65. || (character >= 'a' && character <= 'z')) {
  66. TRY(builder.try_append_code_point(character));
  67. continue;
  68. }
  69. // Otherwise, the escaped character.
  70. TRY(escape_a_character(builder, character));
  71. }
  72. return {};
  73. }
  74. // https://www.w3.org/TR/cssom-1/#serialize-a-string
  75. ErrorOr<void> serialize_a_string(StringBuilder& builder, StringView string)
  76. {
  77. Utf8View characters { string };
  78. // To serialize a string means to create a string represented by '"' (U+0022), followed by the result
  79. // of applying the rules below to each character of the given string, followed by '"' (U+0022):
  80. TRY(builder.try_append('"'));
  81. for (auto character : characters) {
  82. // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER (U+FFFD).
  83. if (character == 0) {
  84. TRY(builder.try_append_code_point(0xFFFD));
  85. continue;
  86. }
  87. // If the character is in the range [\1-\1f] (U+0001 to U+001F) or is U+007F, the character escaped as code point.
  88. if ((character >= 0x0001 && character <= 0x001F) || (character == 0x007F)) {
  89. TRY(escape_a_character_as_code_point(builder, character));
  90. continue;
  91. }
  92. // If the character is '"' (U+0022) or "\" (U+005C), the escaped character.
  93. if (character == 0x0022 || character == 0x005C) {
  94. TRY(escape_a_character(builder, character));
  95. continue;
  96. }
  97. // Otherwise, the character itself.
  98. TRY(builder.try_append_code_point(character));
  99. }
  100. TRY(builder.try_append('"'));
  101. return {};
  102. }
  103. // https://www.w3.org/TR/cssom-1/#serialize-a-url
  104. ErrorOr<void> serialize_a_url(StringBuilder& builder, StringView url)
  105. {
  106. // To serialize a URL means to create a string represented by "url(",
  107. // followed by the serialization of the URL as a string, followed by ")".
  108. TRY(builder.try_append("url("sv));
  109. TRY(serialize_a_string(builder, url.to_deprecated_string()));
  110. TRY(builder.try_append(')'));
  111. return {};
  112. }
  113. // https://www.w3.org/TR/cssom-1/#serialize-a-local
  114. ErrorOr<void> serialize_a_local(StringBuilder& builder, StringView path)
  115. {
  116. // To serialize a LOCAL means to create a string represented by "local(",
  117. // followed by the serialization of the LOCAL as a string, followed by ")".
  118. TRY(builder.try_append("local("sv));
  119. TRY(serialize_a_string(builder, path.to_deprecated_string()));
  120. TRY(builder.try_append(')'));
  121. return {};
  122. }
  123. // NOTE: No spec currently exists for serializing a <'unicode-range'>.
  124. ErrorOr<void> serialize_unicode_ranges(StringBuilder& builder, Vector<UnicodeRange> const& unicode_ranges)
  125. {
  126. TRY(serialize_a_comma_separated_list(builder, unicode_ranges, [](auto& builder, UnicodeRange unicode_range) -> ErrorOr<void> {
  127. return serialize_a_string(builder, TRY(unicode_range.to_string()));
  128. }));
  129. return {};
  130. }
  131. // https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
  132. ErrorOr<void> serialize_a_srgb_value(StringBuilder& builder, Color color)
  133. {
  134. // The serialized form is derived from the computed value and thus, uses either the rgb() or rgba() form
  135. // (depending on whether the alpha is exactly 1, or not), with lowercase letters for the function name.
  136. // NOTE: Since we use Gfx::Color, having an "alpha of 1" means its value is 255.
  137. if (color.alpha() == 255)
  138. TRY(builder.try_appendff("rgb({}, {}, {})"sv, color.red(), color.green(), color.blue()));
  139. else
  140. TRY(builder.try_appendff("rgba({}, {}, {}, {})"sv, color.red(), color.green(), color.blue(), (float)(color.alpha()) / 255.0f));
  141. return {};
  142. }
  143. ErrorOr<String> escape_a_character(u32 character)
  144. {
  145. StringBuilder builder;
  146. TRY(escape_a_character(builder, character));
  147. return builder.to_string();
  148. }
  149. ErrorOr<String> escape_a_character_as_code_point(u32 character)
  150. {
  151. StringBuilder builder;
  152. TRY(escape_a_character_as_code_point(builder, character));
  153. return builder.to_string();
  154. }
  155. ErrorOr<String> serialize_an_identifier(StringView ident)
  156. {
  157. StringBuilder builder;
  158. TRY(serialize_an_identifier(builder, ident));
  159. return builder.to_string();
  160. }
  161. ErrorOr<String> serialize_a_string(StringView string)
  162. {
  163. StringBuilder builder;
  164. TRY(serialize_a_string(builder, string));
  165. return builder.to_string();
  166. }
  167. ErrorOr<String> serialize_a_url(StringView url)
  168. {
  169. StringBuilder builder;
  170. TRY(serialize_a_url(builder, url));
  171. return builder.to_string();
  172. }
  173. ErrorOr<String> serialize_a_srgb_value(Color color)
  174. {
  175. StringBuilder builder;
  176. TRY(serialize_a_srgb_value(builder, color));
  177. return builder.to_string();
  178. }
  179. }