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;
16  
17  import static org.htmlunit.BrowserVersionFeatures.JS_NAVIGATOR_DO_NOT_TRACK_UNSPECIFIED;
18  import static org.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
19  import static org.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
20  import static org.htmlunit.javascript.configuration.SupportedBrowser.FF;
21  import static org.htmlunit.javascript.configuration.SupportedBrowser.FF_ESR;
22  
23  import java.util.ArrayList;
24  
25  import org.apache.commons.lang3.StringUtils;
26  import org.htmlunit.WebClient;
27  import org.htmlunit.corejs.javascript.Scriptable;
28  import org.htmlunit.javascript.HtmlUnitScriptable;
29  import org.htmlunit.javascript.JavaScriptEngine;
30  import org.htmlunit.javascript.configuration.JsxClass;
31  import org.htmlunit.javascript.configuration.JsxConstructor;
32  import org.htmlunit.javascript.configuration.JsxFunction;
33  import org.htmlunit.javascript.configuration.JsxGetter;
34  import org.htmlunit.javascript.host.geo.Geolocation;
35  import org.htmlunit.javascript.host.media.MediaDevices;
36  import org.htmlunit.javascript.host.network.NetworkInformation;
37  
38  /**
39   * A JavaScript object for {@code Navigator}.
40   *
41   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
42   * @author Daniel Gredler
43   * @author Chris Erskine
44   * @author Ahmed Ashour
45   * @author Marc Guillemot
46   * @author Frank Danek
47   * @author Ronald Brill
48   *
49   * @see <a href="http://msdn.microsoft.com/en-us/library/ms535867.aspx">MSDN documentation</a>
50   */
51  @JsxClass
52  public class Navigator extends HtmlUnitScriptable {
53  
54      private PluginArray plugins_;
55      private MimeTypeArray mimeTypes_;
56      private MediaDevices mediaDevices_;
57  
58      /**
59       * JavaScript constructor.
60       */
61      @JsxConstructor
62      public void jsConstructor() {
63          // nothing to do
64      }
65  
66      /**
67       * Returns the {@code appCodeName} property.
68       * @return the {@code appCodeName} property
69       */
70      @JsxGetter
71      public String getAppCodeName() {
72          return getBrowserVersion().getApplicationCodeName();
73      }
74  
75      /**
76       * Returns the {@code appName} property.
77       * @return the {@code appName} property
78       */
79      @JsxGetter
80      public String getAppName() {
81          return getBrowserVersion().getApplicationName();
82      }
83  
84      /**
85       * Returns the {@code appVersion} property.
86       * @return the {@code appVersion} property
87       */
88      @JsxGetter
89      public String getAppVersion() {
90          return getBrowserVersion().getApplicationVersion();
91      }
92  
93      /**
94       * Returns the language of the browser.
95       * @return the language
96       */
97      @JsxGetter
98      public String getLanguage() {
99          return getBrowserVersion().getBrowserLanguage();
100     }
101 
102     /**
103      * Returns the language of the browser.
104      * @return the language
105      */
106     @JsxGetter
107     public Scriptable getLanguages() {
108         final String acceptLang = getBrowserVersion().getAcceptLanguageHeader();
109         if (StringUtils.isEmpty(acceptLang)) {
110             return JavaScriptEngine.newArray(this, 0);
111         }
112 
113         final ArrayList<String> res = new ArrayList<>();
114         final String[] parts = org.htmlunit.util.StringUtils.splitAtComma(acceptLang);
115         for (final String part : parts) {
116             if (!StringUtils.isEmpty(part)) {
117                 final String lang = StringUtils.substringBefore(part, ";").trim();
118                 if (!StringUtils.isEmpty(part)) {
119                     res.add(lang);
120                 }
121             }
122         }
123 
124         return JavaScriptEngine.newArray(this, res.toArray());
125     }
126 
127     /**
128      * Returns the {@code cookieEnabled} property.
129      * @return the {@code cookieEnabled} property
130      */
131     @JsxGetter
132     public boolean isCookieEnabled() {
133         return getWindow().getWebWindow().getWebClient().getCookieManager().isCookiesEnabled();
134     }
135 
136     /**
137      * Returns the {@code onLine} property.
138      * @return the {@code onLine} property
139      */
140     @JsxGetter
141     public boolean isOnLine() {
142         return getBrowserVersion().isOnLine();
143     }
144 
145     /**
146      * Returns the {@code platform} property.
147      * @return the {@code platform} property
148      */
149     @JsxGetter
150     public String getPlatform() {
151         return getBrowserVersion().getPlatform();
152     }
153 
154     /**
155      * Returns the {@code product} property.
156      * @return the {@code product} property
157      */
158     @JsxGetter
159     public String getProduct() {
160         return "Gecko";
161     }
162 
163     /**
164      * Returns the build number of the current browser.
165      * @see <a href="https://developer.mozilla.org/en/navigator.productSub">Mozilla Doc</a>
166      * @return false
167      */
168     @JsxGetter
169     public String getProductSub() {
170         return getBrowserVersion().getProductSub();
171     }
172 
173     /**
174      * Returns the property {@code userAgent}.
175      * @return the property {@code userAgent}
176      */
177     @JsxGetter
178     public String getUserAgent() {
179         return getBrowserVersion().getUserAgent();
180     }
181 
182     /**
183      * Returns an empty array because HtmlUnit does not support embedded objects.
184      * @return an empty array
185      */
186     @JsxGetter
187     public PluginArray getPlugins() {
188         initPluginsAndMimeTypes();
189         return plugins_;
190     }
191 
192     private void initPluginsAndMimeTypes() {
193         // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/plugins
194         // Recent versions of the specification hard-code the returned list.
195         // If inline viewing of PDF files is supported the property lists five standard plugins.
196         // If inline PDF viewing is not supported then an empty list is returned.
197         if (plugins_ != null) {
198             return;
199         }
200         plugins_ = new PluginArray();
201         plugins_.setParentScope(this);
202         plugins_.setPrototype(getPrototype(PluginArray.class));
203 
204         Plugin plugin = new Plugin("PDF Viewer", "Portable Document Format", "internal-pdf-viewer");
205         plugin.setParentScope(this);
206         plugin.setPrototype(getPrototype(Plugin.class));
207 
208         // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/mimeTypes
209         // Recent versions of the specification hard-code the returned set of MIME types.
210         // If PDF files can be displayed inline then application/pdf and text/pdf are listed.
211         // Otherwise an empty list is returned.
212         mimeTypes_ = new MimeTypeArray();
213         mimeTypes_.setParentScope(this);
214         mimeTypes_.setPrototype(getPrototype(MimeTypeArray.class));
215 
216         final MimeType mimeTypeAppPdf = new MimeType("application/pdf", "Portable Document Format", "pdf", plugin);
217         mimeTypeAppPdf.setParentScope(this);
218         mimeTypeAppPdf.setPrototype(getPrototype(MimeType.class));
219         mimeTypes_.add(mimeTypeAppPdf);
220 
221         final MimeType mimeTypeTxtPdf = new MimeType("text/pdf", "Portable Document Format", "pdf", plugin);
222         mimeTypeTxtPdf.setParentScope(this);
223         mimeTypeTxtPdf.setPrototype(getPrototype(MimeType.class));
224         mimeTypes_.add(mimeTypeTxtPdf);
225 
226         plugin.add(mimeTypeAppPdf);
227         plugin.add(mimeTypeTxtPdf);
228         plugins_.add(plugin);
229 
230         // all the others
231         plugin = new Plugin("Chrome PDF Viewer", "Portable Document Format", "internal-pdf-viewer");
232         plugin.setParentScope(this);
233         plugin.setPrototype(getPrototype(Plugin.class));
234         plugin.add(mimeTypeAppPdf);
235         plugin.add(mimeTypeTxtPdf);
236         plugins_.add(plugin);
237 
238         plugin = new Plugin("Chromium PDF Viewer", "Portable Document Format", "internal-pdf-viewer");
239         plugin.setParentScope(this);
240         plugin.setPrototype(getPrototype(Plugin.class));
241         plugin.add(mimeTypeAppPdf);
242         plugin.add(mimeTypeTxtPdf);
243         plugins_.add(plugin);
244 
245         plugin = new Plugin("Microsoft Edge PDF Viewer", "Portable Document Format", "internal-pdf-viewer");
246         plugin.setParentScope(this);
247         plugin.setPrototype(getPrototype(Plugin.class));
248         plugin.add(mimeTypeAppPdf);
249         plugin.add(mimeTypeTxtPdf);
250         plugins_.add(plugin);
251 
252         plugin = new Plugin("WebKit built-in PDF", "Portable Document Format", "internal-pdf-viewer");
253         plugin.setParentScope(this);
254         plugin.setPrototype(getPrototype(Plugin.class));
255         plugin.add(mimeTypeAppPdf);
256         plugin.add(mimeTypeTxtPdf);
257         plugins_.add(plugin);
258     }
259 
260     /**
261      * Returns the {@code mimeTypes} property.
262      * @return the {@code mimeTypes} property
263      */
264     @JsxGetter
265     public MimeTypeArray getMimeTypes() {
266         initPluginsAndMimeTypes();
267         return mimeTypes_;
268     }
269 
270     /**
271      * Indicates if Java is enabled.
272      * @return false
273      */
274     @JsxFunction
275     public boolean javaEnabled() {
276         return false;
277     }
278 
279     /**
280      * Returns {@code false} always as data tainting support is not enabled in HtmlUnit.
281      * @return false
282      */
283     @JsxFunction({FF, FF_ESR})
284     public boolean taintEnabled() {
285         return false;
286     }
287 
288     /**
289      * Returns the {@code geolocation} property.
290      * @return the {@code geolocation} property
291      */
292     @JsxGetter
293     public Geolocation getGeolocation() {
294         final Geolocation geolocation = new Geolocation();
295         geolocation.setPrototype(getPrototype(geolocation.getClass()));
296         geolocation.setParentScope(getParentScope());
297         return geolocation;
298     }
299 
300     /**
301      * @return true whether the browser supports inline display
302      *         of PDF files when navigating to them
303      */
304     @JsxGetter
305     public boolean getPdfViewerEnabled() {
306         return true;
307     }
308 
309     /**
310      * Returns the {@code buildID} property.
311      * @return the {@code buildID} property
312      */
313     @JsxGetter({FF, FF_ESR})
314     public String getBuildID() {
315         return getBrowserVersion().getBuildId();
316     }
317 
318     /**
319      * Returns the {@code vendor} property.
320      * @return the {@code vendor} property
321      */
322     @JsxGetter
323     public String getVendor() {
324         return getBrowserVersion().getVendor();
325     }
326 
327     /**
328      * Returns the {@code vendorSub} property.
329      * @return the {@code vendorSub} property
330      */
331     @JsxGetter
332     public String getVendorSub() {
333         return "";
334     }
335 
336     /**
337      * Returns the {@code doNotTrack} property.
338      * @return the {@code doNotTrack} property
339      */
340     @JsxGetter
341     public Object getDoNotTrack() {
342         final WebClient client = getWindow().getWebWindow().getWebClient();
343         if (client.getOptions().isDoNotTrackEnabled()) {
344             return 1;
345         }
346         if (client.getBrowserVersion().hasFeature(JS_NAVIGATOR_DO_NOT_TRACK_UNSPECIFIED)) {
347             return "unspecified";
348         }
349         return null;
350     }
351 
352     /**
353      * Returns the {@code oscpu} property.
354      * @return the {@code oscpu} property
355      */
356     @JsxGetter({FF, FF_ESR})
357     public String getOscpu() {
358         return "Windows NT 6.1";
359     }
360 
361     /**
362      * Returns the {@code connection} property.
363      * @return the {@code connection} property
364      */
365     @JsxGetter({CHROME, EDGE})
366     public NetworkInformation getConnection() {
367         final NetworkInformation networkInformation = new NetworkInformation();
368         networkInformation.setPrototype(getPrototype(networkInformation.getClass()));
369         networkInformation.setParentScope(getParentScope());
370         return networkInformation;
371     }
372 
373     /**
374      * Returns the {@code mimeTypes} property.
375      * @return the {@code mimeTypes} property
376      */
377     @JsxGetter
378     public MediaDevices getMediaDevices() {
379         if (mediaDevices_ == null) {
380             mediaDevices_ = new MediaDevices();
381             mediaDevices_.setPrototype(getPrototype(mediaDevices_.getClass()));
382             mediaDevices_.setParentScope(getParentScope());
383         }
384         return mediaDevices_;
385     }
386 }