View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.css;
16  
17  import org.htmlunit.corejs.javascript.Scriptable;
18  import org.htmlunit.javascript.HtmlUnitScriptable;
19  import org.htmlunit.javascript.configuration.JsxClass;
20  import org.htmlunit.javascript.configuration.JsxStaticFunction;
21  import org.htmlunit.util.StringUtils;
22  
23  /**
24   * A JavaScript object for {@code CSS}.
25   *
26   * @author Ahmed Ashour
27   * @author Ronald Brill
28   */
29  @JsxClass
30  public class CSS extends HtmlUnitScriptable {
31  
32      /**
33       * {@inheritDoc}
34       */
35      @Override
36      public Object get(final String name, final Scriptable start) {
37          if ("prototype".equals(name)) {
38              return NOT_FOUND;
39          }
40          return super.get(name, start);
41      }
42  
43      /**
44       * @return a Boolean value indicating if the browser supports a given CSS feature, or not
45       */
46      @JsxStaticFunction
47      public static boolean supports() {
48          // for the moment we support everything :-)
49          return true;
50      }
51  
52      /**
53       * @param ident the string to be escaped
54       * @return a string containing the escaped string passed as parameter,
55       *     mostly for use as part of a CSS selector
56       */
57      @JsxStaticFunction
58      public static String escape(final String ident) {
59          if (StringUtils.isEmptyOrNull(ident)) {
60              return ident;
61          }
62  
63          final int length = ident.length();
64          final StringBuilder escaped = new StringBuilder();
65          for (int i = 0; i < length; i++) {
66              final char c = ident.charAt(i);
67  
68              // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER (U+FFFD).
69              if (c == '\u0000') {
70                  escaped.append('\ufffd');
71              }
72  
73              // If the character is in the range [\1-\1f] (U+0001 to U+001F) or is U+007F,
74              // then the character escaped as code point.
75              else if (('\u0001' <= c && c <= '\u001f') || c == '\u007f') {
76                  escaped.append('\\').append(Integer.toHexString(c)).append(' ');
77              }
78  
79              // If the character is the first character and is in the range [0-9] (U+0030 to U+0039),
80              // then the character escaped as code point.
81              else if (i == 0 && ('\u0030' <= c && c <= '\u0039')) {
82                  escaped.append('\\').append(Integer.toHexString(c)).append(' ');
83              }
84  
85              // If the character is the second character and is in the range [0-9] (U+0030 to U+0039)
86              // and the first character is a "-" (U+002D), then the character escaped as code point.
87              else if (i == 1 && ('\u0030' <= c && c <= '\u0039') && ident.charAt(0) == '-') {
88                  escaped.append('\\').append(Integer.toHexString(c)).append(' ');
89              }
90  
91              // If the character is the first character and is a "-" (U+002D),
92              // and there is no second character, then the escaped character.
93              else if (i == 0 && c == '-' && length == 1) {
94                  escaped.append('\\').append(c);
95              }
96  
97              // If the character is not handled by one of the above rules
98              // and is greater than or equal to U+0080, is "-" (U+002D) or "_" (U+005F),
99              // or is in one of the ranges [0-9] (U+0030 to U+0039),
100             // [A-Z] (U+0041 to U+005A), or [a-z] (U+0061 to U+007A),
101             // then the character itself.
102             else if (c >= '\u0080'
103                         || c == '\u002d'
104                         || c == '\u005f'
105                         || ('\u0030' <= c && c <= '\u0039')
106                         || ('\u0041' <= c && c <= '\u005A')
107                         || ('\u0061' <= c && c <= '\u007A')) {
108                 escaped.append(c);
109             }
110 
111             // Otherwise, the escaped character
112             else {
113                 escaped.append('\\').append(c);
114             }
115         }
116 
117         return escaped.toString();
118     }
119 }