View Javadoc
1   /*
2    * Copyright (c) 2002-2026 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.html;
16  
17  import org.w3c.dom.Element;
18  import org.w3c.dom.Node;
19  
20  /**
21   * A marker interface for those classes that can be disabled.
22   *
23   * @author David D. Kilzer
24   * @author Ronald Brill
25   */
26  public interface DisabledElement extends Element {
27  
28      /** The "disabled" attribute name. */
29      String ATTRIBUTE_DISABLED = "disabled";
30  
31      /**
32       * Returns {@code true} if the disabled attribute is set for this element.
33       * <p>
34       * Per spec, a disabled {@link HtmlFieldSet} ancestor disables its
35       * descendants EXCEPT for descendants of that fieldset's first
36       * {@code <legend>} child, if any. This is checked cheaply while walking
37       * up the ancestor chain: at each step, the node we just came from
38       * (tracked as {@code previous}) is by construction a direct child of the
39       * node we're currently examining, so when that node turns out to be a
40       * disabled fieldset we already know its relevant direct child without
41       * any second traversal -- see {@link #isFirstLegendChild(HtmlFieldSet, Node)}.
42       * </p>
43       *
44       * @return {@code true} if the disabled attribute is set for this element
45       */
46      default boolean isDisabled() {
47          if (hasAttribute(ATTRIBUTE_DISABLED)) {
48              return true;
49          }
50  
51          Node previous = this;
52          Node node = getParentNode();
53          while (node != null) {
54              if (node instanceof HtmlFieldSet fieldSet) {
55                  if (fieldSet.hasAttribute(ATTRIBUTE_DISABLED)
56                          && !isFirstLegendChild(fieldSet, previous)) {
57                      return true;
58                  }
59              }
60              else if (node instanceof DisabledElement element
61                      && element.hasAttribute(ATTRIBUTE_DISABLED)) {
62                  return true;
63              }
64              previous = node;
65              node = node.getParentNode();
66          }
67  
68          return false;
69      }
70  
71      /**
72       * Checks whether {@code candidate} -- the direct child of {@code fieldSet}
73       * that lies on the path up from this element -- is {@code fieldSet}'s
74       * FIRST {@code <legend>} child. The exemption from fieldset disabling
75       * applies only to the first legend -- a control inside a second,
76       * non-conforming {@code <legend>} is still disabled. Bounded by how many
77       * children precede the first legend (in practice O(1), since a legend is
78       * almost always the first child), not by how deep this element is nested
79       * inside it.
80       *
81       * @param fieldSet the disabled fieldset ancestor to check the exemption against
82       * @param candidate the direct child of {@code fieldSet} on the path up from this element
83       * @return {@code true} if {@code candidate} is {@code fieldSet}'s first {@code <legend>} child
84       */
85      private static boolean isFirstLegendChild(final HtmlFieldSet fieldSet, final Node candidate) {
86          if (!(candidate instanceof HtmlLegend)) {
87              return false;
88          }
89  
90          for (final DomNode child : fieldSet.getChildren()) {
91              if (child instanceof HtmlLegend) {
92                  return child == candidate;
93              }
94          }
95          return false;
96      }
97  
98      /**
99       * Returns the value of the attribute {@code disabled}. Refer to the
100      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
101      * documentation for details on the use of this attribute.
102      *
103      * @return the value of the attribute {@code disabled} or an empty string if that attribute isn't defined
104      */
105     String getDisabledAttribute();
106 }