View Javadoc
1   /*
2    * Copyright (c) 2002-2017 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    * http://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 com.gargoylesoftware.htmlunit.javascript.host.dom;
16  
17  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_CREATE_HTMLDOCOMENT_REQUIRES_TITLE;
18  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CORE_3;
19  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CSS2_1;
20  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CSS2_3;
21  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CSS3_1;
22  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CSS3_2;
23  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CSS3_3;
24  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CSS_1;
25  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CSS_2;
26  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_CSS_3;
27  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_EVENTS_1;
28  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_KEYBOARDEVENTS;
29  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_LS;
30  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_MUTATIONNAMEEVENTS;
31  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_RANGE_1;
32  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_RANGE_3;
33  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_STYLESHEETS;
34  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_SVG_BASICSTRUCTURE_1_2;
35  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_TEXTEVENTS;
36  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_UIEVENTS_2;
37  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_VALIDATION;
38  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_VIEWS_1;
39  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_VIEWS_3;
40  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_DOMIMPLEMENTATION_FEATURE_XPATH;
41  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
42  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
43  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF;
44  
45  import com.gargoylesoftware.htmlunit.StringWebResponse;
46  import com.gargoylesoftware.htmlunit.WebClient;
47  import com.gargoylesoftware.htmlunit.WebResponse;
48  import com.gargoylesoftware.htmlunit.html.HtmlPage;
49  import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
50  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
51  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
52  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
53  import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLBodyElement;
54  import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLDocument;
55  import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLHeadElement;
56  import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLHtmlElement;
57  import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLTitleElement;
58  import com.gargoylesoftware.htmlunit.javascript.host.xml.XMLDocument;
59  import com.gargoylesoftware.htmlunit.xml.XmlPage;
60  
61  import net.sourceforge.htmlunit.corejs.javascript.Context;
62  import net.sourceforge.htmlunit.corejs.javascript.Undefined;
63  
64  /**
65   * A JavaScript object for {@code DOMImplementation}.
66   *
67   * @author Ahmed Ashour
68   * @author Frank Danek
69   * @author Ronald Brill
70   * @author Adam Afeltowicz
71   *
72   * @see <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-core.html#ID-102161490">
73   * W3C Dom Level 1</a>
74   */
75  @JsxClass
76  public class DOMImplementation extends SimpleScriptable {
77  
78      /**
79       * Creates an instance.
80       */
81      @JsxConstructor({CHROME, FF, EDGE})
82      public DOMImplementation() {
83      }
84  
85      /**
86       * Test if the DOM implementation implements a specific feature.
87       * @param feature the name of the feature to test (case-insensitive)
88       * @param version the version number of the feature to test
89       * @return true if the feature is implemented in the specified version, false otherwise
90       */
91      @JsxFunction
92      public boolean hasFeature(final String feature, final String version) {
93          switch (feature) {
94              case "Core":
95              case "HTML":
96              case "XHTML":
97              case "XML":
98                  switch (version) {
99                      case "1.0":
100                     case "2.0":
101                         return true;
102                     case "3.0":
103                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CORE_3);
104                     default:
105                 }
106                 break;
107 
108             case "Views":
109                 switch (version) {
110                     case "1.0":
111                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_VIEWS_1);
112                     case "2.0":
113                         return true;
114                     case "3.0":
115                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_VIEWS_3);
116                     default:
117                 }
118                 break;
119 
120             case "StyleSheets":
121                 return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_STYLESHEETS);
122 
123             case "CSS":
124                 switch (version) {
125                     case "1.0":
126                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CSS_1);
127                     case "2.0":
128                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CSS_2);
129                     case "3.0":
130                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CSS_3);
131                     default:
132                 }
133                 break;
134 
135             case "CSS2":
136                 switch (version) {
137                     case "1.0":
138                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CSS2_1);
139                     case "2.0":
140                         return true;
141                     case "3.0":
142                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CSS2_3);
143                     default:
144                 }
145                 break;
146 
147             case "CSS3":
148                 switch (version) {
149                     case "1.0":
150                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CSS3_1);
151                     case "2.0":
152                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CSS3_2);
153                     case "3.0":
154                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_CSS3_3);
155                     default:
156                 }
157                 break;
158 
159             case "Events":
160             case "HTMLEvents":
161             case "MouseEvents":
162             case "MutationEvents":
163                 switch (version) {
164                     case "1.0":
165                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_EVENTS_1);
166                     case "2.0":
167                     case "3.0":
168                         return true;
169                     default:
170                 }
171                 break;
172 
173             case "UIEvents":
174                 switch (version) {
175                     case "1.0":
176                     case "2.0":
177                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_UIEVENTS_2);
178                     case "3.0":
179                         return true;
180                     default:
181                 }
182                 break;
183 
184             case "KeyboardEvents":
185                 return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_KEYBOARDEVENTS);
186 
187             case "MutationNameEvents":
188                 return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_MUTATIONNAMEEVENTS);
189 
190             case "TextEvents":
191                 return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_TEXTEVENTS);
192 
193             case "LS":
194             case "LS-Async":
195                 return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_LS);
196 
197             case "Range":
198             case "Traversal":
199                 switch (version) {
200                     case "1.0":
201                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_RANGE_1);
202                     case "2.0":
203                         return true;
204                     case "3.0":
205                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_RANGE_3);
206                     default:
207                 }
208                 break;
209 
210             case "Validation":
211                 return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_VALIDATION);
212 
213             case "XPath":
214                 return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_XPATH);
215 
216             case "http://www.w3.org/TR/SVG11/feature#BasicStructure":
217             case "http://www.w3.org/TR/SVG11/feature#Shape":
218                 switch (version) {
219                     case "1.0":
220                     case "1.1":
221                         return true;
222                     case "1.2":
223                         return getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_FEATURE_SVG_BASICSTRUCTURE_1_2);
224                     default:
225                 }
226                 break;
227 
228             default:
229         }
230         //TODO: other features.
231         return false;
232     }
233 
234     /**
235      * Creates an {@link XMLDocument}.
236      *
237      * @param namespaceURI the URI that identifies an XML namespace
238      * @param qualifiedName the qualified name of the document to instantiate
239      * @param doctype the document types of the document
240      * @return the newly created {@link XMLDocument}
241      */
242     @JsxFunction
243     public XMLDocument createDocument(final String namespaceURI, final String qualifiedName,
244             final DocumentType doctype) {
245         final XMLDocument document = new XMLDocument(getWindow().getWebWindow());
246         document.setParentScope(getParentScope());
247         document.setPrototype(getPrototype(document.getClass()));
248         if (qualifiedName != null && !qualifiedName.isEmpty()) {
249             final XmlPage page = (XmlPage) document.getDomNodeOrDie();
250             page.appendChild(page.createElementNS("".equals(namespaceURI) ? null : namespaceURI, qualifiedName));
251         }
252         return document;
253     }
254 
255     /**
256      * Creates an {@link HTMLDocument}.
257      * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createHTMLDocument">
258      *   createHTMLDocument (MDN)</a>
259      *
260      * @param titleObj the document title
261      * @return the newly created {@link HTMLDocument}
262      */
263     @JsxFunction
264     public HTMLDocument createHTMLDocument(final Object titleObj) {
265         if (titleObj == Undefined.instance
266                 && getBrowserVersion().hasFeature(JS_DOMIMPLEMENTATION_CREATE_HTMLDOCOMENT_REQUIRES_TITLE)) {
267             throw Context.reportRuntimeError("Title is required");
268         }
269 
270         final HTMLDocument document = new HTMLDocument();
271         document.setParentScope(getParentScope());
272         document.setPrototype(getPrototype(document.getClass()));
273 
274         // According to spec and behavior of function in browsers new document
275         // has no location object and is not connected with any window
276         final WebResponse resp = new StringWebResponse("", WebClient.URL_ABOUT_BLANK);
277         final HtmlPage page = new HtmlPage(resp, getWindow().getWebWindow());
278         page.setEnclosingWindow(null);
279         document.setDomNode(page);
280 
281         final HTMLHtmlElement html = (HTMLHtmlElement) document.createElement("html");
282         page.appendChild(html.getDomNodeOrDie());
283 
284         final HTMLHeadElement head = (HTMLHeadElement) document.createElement("head");
285         html.appendChild(head);
286 
287         if (titleObj != Undefined.instance) {
288             final HTMLTitleElement title = (HTMLTitleElement) document.createElement("title");
289             head.appendChild(title);
290             title.setTextContent(Context.toString(titleObj));
291         }
292 
293         final HTMLBodyElement body = (HTMLBodyElement) document.createElement("body");
294         html.appendChild(body);
295 
296         return document;
297     }
298 }