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 java.io.Serializable;
18  import java.util.function.Predicate;
19  
20  import org.htmlunit.WebClient;
21  import org.htmlunit.corejs.javascript.Scriptable;
22  import org.htmlunit.html.DomNode;
23  import org.htmlunit.html.HtmlAttributeChangeEvent;
24  import org.htmlunit.html.HtmlElement;
25  import org.htmlunit.html.HtmlLink;
26  import org.htmlunit.html.HtmlStyle;
27  import org.htmlunit.javascript.HtmlUnitScriptable;
28  import org.htmlunit.javascript.JavaScriptEngine;
29  import org.htmlunit.javascript.configuration.JsxClass;
30  import org.htmlunit.javascript.configuration.JsxConstructor;
31  import org.htmlunit.javascript.configuration.JsxFunction;
32  import org.htmlunit.javascript.configuration.JsxGetter;
33  import org.htmlunit.javascript.host.dom.AbstractList.EffectOnCache;
34  import org.htmlunit.javascript.host.dom.Document;
35  import org.htmlunit.javascript.host.html.HTMLCollection;
36  import org.htmlunit.javascript.host.html.HTMLElement;
37  import org.htmlunit.javascript.host.html.HTMLLinkElement;
38  import org.htmlunit.javascript.host.html.HTMLStyleElement;
39  
40  /**
41   * <p>An ordered list of stylesheets, accessible via <code>document.styleSheets</code>, as specified by the
42   * <a href="http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList">DOM
43   * Level 2 Style spec</a> and the <a href="https://developer.mozilla.org/en-US/docs/DOM/document.styleSheets">Gecko
44   * DOM Guide</a>.</p>
45   *
46   * <p>If CSS is disabled via {@link org.htmlunit.WebClientOptions#setCssEnabled(boolean)}, instances
47   * of this class will always be empty. This allows us to check for CSS enablement/disablement in a single
48   * location, without having to sprinkle checks throughout the code.</p>
49   *
50   * @author Daniel Gredler
51   * @author Ahmed Ashour
52   * @author Ronald Brill
53   * @author Frank Danek
54   * @author Carsten Steul
55   */
56  @JsxClass
57  public class StyleSheetList extends HtmlUnitScriptable {
58  
59      /**
60       * We back the stylesheet list with an {@link HTMLCollection} of styles/links because this list must be "live".
61       */
62      private HTMLCollection nodes_;
63  
64      /**
65       * Creates an instance.
66       */
67      public StyleSheetList() {
68          super();
69      }
70  
71      /**
72       * JavaScript constructor.
73       */
74      @JsxConstructor
75      public void jsConstructor() {
76          // nothing to do
77      }
78  
79      /**
80       * Creates a new style sheet list owned by the specified document.
81       *
82       * @param document the owning document
83       */
84      public StyleSheetList(final Document document) {
85          super();
86          setParentScope(document);
87          setPrototype(getPrototype(getClass()));
88  
89          final WebClient webClient = getWindow().getWebWindow().getWebClient();
90  
91          if (webClient.getOptions().isCssEnabled()) {
92              nodes_ = new HTMLCollection(document.getDomNodeOrDie(), true);
93  
94              nodes_.setEffectOnCacheFunction(
95                      (java.util.function.Function<HtmlAttributeChangeEvent, EffectOnCache> & Serializable)
96                      event -> {
97                          final HtmlElement node = event.getHtmlElement();
98                          if (node instanceof HtmlLink && "rel".equalsIgnoreCase(event.getName())) {
99                              return EffectOnCache.RESET;
100                         }
101                         return EffectOnCache.NONE;
102                     });
103 
104             nodes_.setIsMatchingPredicate(
105                     (Predicate<DomNode> & Serializable)
106                     node -> {
107                         if (node instanceof HtmlStyle) {
108                             return true;
109                         }
110                         if (node instanceof HtmlLink) {
111                             return ((HtmlLink) node).isActiveStyleSheetLink();
112                         }
113                         return false;
114                     });
115         }
116         else {
117             nodes_ = HTMLCollection.emptyCollection(getWindow().getDomNodeOrDie());
118         }
119     }
120 
121     /**
122      * Returns the list's length.
123      *
124      * @return the list's length
125      */
126     @JsxGetter
127     public int getLength() {
128         return nodes_.getLength();
129     }
130 
131     /**
132      * Returns the style sheet at the specified index.
133      *
134      * @param index the index of the style sheet to return
135      * @return the style sheet at the specified index
136      */
137     @JsxFunction
138     public Object item(final int index) {
139         final Object item = get(index, this);
140         if (JavaScriptEngine.UNDEFINED == item) {
141             return null;
142         }
143         return item;
144     }
145 
146     /**
147      * {@inheritDoc}
148      */
149     @Override
150     public Object get(final int index, final Scriptable start) {
151         if (this == start) {
152             if (nodes_ == null || index < 0 || index >= nodes_.getLength()) {
153                 return JavaScriptEngine.UNDEFINED;
154             }
155 
156             final HTMLElement element = (HTMLElement) nodes_.item(Integer.valueOf(index));
157 
158             // <style type="text/css"> ... </style>
159             if (element instanceof HTMLStyleElement) {
160                 return ((HTMLStyleElement) element).getSheet();
161             }
162             // <link rel="stylesheet" type="text/css" href="..." />
163             return ((HTMLLinkElement) element).getSheet();
164         }
165         return super.get(index, start);
166     }
167 
168     /**
169      * {@inheritDoc}
170      */
171     @Override
172     protected Object equivalentValues(final Object value) {
173         return value != null
174                 && getClass() == value.getClass()
175                 && getDomNodeOrNull() == ((StyleSheetList) value).getDomNodeOrNull();
176     }
177 }