From cff71d238a33c98c67cb9edf4c5a81b5e6077df8 Mon Sep 17 00:00:00 2001 From: Ronald Brill Date: Wed, 20 Nov 2024 16:06:50 +0100 Subject: [PATCH] Implementation of Plugin/PluginArray/MimeType/MimeTypeArray rewritten. The navigator properties plugins and mimeTypes returning static results in all browsers. Class PluginConfiguration removed, the config is static for all browsers. A bit more code cleanup - flash is finally gone. --- src/changes/changes.xml | 10 + .../java/org/htmlunit/BrowserVersion.java | 17 -- .../org/htmlunit/PluginConfiguration.java | 194 ------------- .../JavaScriptConfiguration.java | 3 +- .../htmlunit/javascript/host/MimeType.java | 4 +- .../javascript/host/MimeTypeArray.java | 90 ++++++- .../htmlunit/javascript/host/Navigator.java | 81 ++++-- .../org/htmlunit/javascript/host/Plugin.java | 100 +++++-- .../htmlunit/javascript/host/PluginArray.java | 92 ++++++- .../htmlunit/javascript/host/SimpleArray.java | 115 -------- .../java/org/htmlunit/BrowserVersionTest.java | 11 - .../htmlunit/archunit/ArchitectureTest.java | 3 - .../ElementOwnPropertySymbolsTest.java | 8 +- .../general/ElementPropertiesTest.java | 12 + .../javascript/host/MimeTypeTest.java | 128 ++++++--- .../javascript/host/NavigatorTest.java | 67 +++-- .../htmlunit/javascript/host/PluginTest.java | 254 ++++++++++++++++++ 17 files changed, 740 insertions(+), 449 deletions(-) delete mode 100644 src/main/java/org/htmlunit/PluginConfiguration.java delete mode 100644 src/main/java/org/htmlunit/javascript/host/SimpleArray.java create mode 100644 src/test/java/org/htmlunit/javascript/host/PluginTest.java diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 9cf935eed62..ceb859fb62e 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -8,6 +8,16 @@ + + Implementation of Plugin/PluginArray/MimeType/MimeTypeArray rewritten. The navigator properties + plugins and mimeTypes returning static results in all browsers. + + + Class PluginConfiguration removed, the config is static for all browsers. + + + A bit more code cleanup - flash is finally gone. + Upgrade Apache commons-io to 2.18.0. diff --git a/src/main/java/org/htmlunit/BrowserVersion.java b/src/main/java/org/htmlunit/BrowserVersion.java index 3fbff0759f6..410b11995d3 100644 --- a/src/main/java/org/htmlunit/BrowserVersion.java +++ b/src/main/java/org/htmlunit/BrowserVersion.java @@ -19,7 +19,6 @@ import java.lang.reflect.Field; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -112,7 +111,6 @@ public final class BrowserVersion implements Serializable { /** The default browser version. */ private static BrowserVersion DefaultBrowserVersion_ = BEST_SUPPORTED; - /* Register plugins for the browser versions. */ static { FIREFOX_ESR.applicationVersion_ = "5.0 (Windows)"; FIREFOX_ESR.userAgent_ = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:" @@ -401,7 +399,6 @@ public final class BrowserVersion implements Serializable { private String platform_ = PLATFORM_WIN32; private TimeZone systemTimezone_ = TimeZone.getTimeZone(TIMEZONE_NEW_YORK); private String userAgent_; - private final Set plugins_; private final Set features_; private String acceptEncodingHeader_; private String acceptLanguageHeader_; @@ -436,7 +433,6 @@ public final class BrowserVersion implements Serializable { secClientHintUserAgentHeader_ = ""; secClientHintUserAgentPlatformHeader_ = "\"Windows\""; - plugins_ = new HashSet<>(); features_ = EnumSet.noneOf(BrowserVersionFeatures.class); uploadMimeTypes_ = new HashMap<>(); @@ -732,15 +728,6 @@ public String getSecClientHintUserAgentPlatformHeader() { return secClientHintUserAgentPlatformHeader_; } - /** - * Returns the available plugins. This makes only sense for Firefox as only this - * browser makes this kind of information available via JavaScript. - * @return the available plugins - */ - public Set getPlugins() { - return plugins_; - } - /** * Indicates if this instance has the given feature. Used for HtmlUnit internal processing. * @param property the property name @@ -872,10 +859,6 @@ public BrowserVersionBuilder(final BrowserVersion version) { .setHeaderNamesOrdered(version.getHeaderNamesOrdered()) .setFontHeights(version.fontHeights_); - for (final PluginConfiguration pluginConf : version.getPlugins()) { - workPiece_.plugins_.add(pluginConf.clone()); - } - workPiece_.features_.addAll(version.features_); workPiece_.uploadMimeTypes_.putAll(version.uploadMimeTypes_); } diff --git a/src/main/java/org/htmlunit/PluginConfiguration.java b/src/main/java/org/htmlunit/PluginConfiguration.java deleted file mode 100644 index 5c78009472d..00000000000 --- a/src/main/java/org/htmlunit/PluginConfiguration.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2002-2024 Gargoyle Software Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.htmlunit; - -import java.io.Serializable; -import java.util.HashSet; -import java.util.Set; - -/** - * Contains information about a plugin as available in JavaScript via document.navigator.plugins, - * as well as the associated mime types. - * - * @author Marc Guillemot - * @author Ronald Brill - * @see XUL Planet Documentation - */ -public class PluginConfiguration implements Serializable, Cloneable { - - private final String description_; - private final String filename_; - private final String name_; - private final String version_; - private final Set mimeTypes_ = new HashSet<>(); - - /** - * Creates a new instance. - * @param name the plugin name - * @param description the plugin description - * @param version the version - * @param filename the plugin filename - */ - public PluginConfiguration(final String name, final String description, final String version, - final String filename) { - WebAssert.notNull("name", name); - name_ = name; - description_ = description; - version_ = version; - filename_ = filename; - } - - /** - * Gets the plugin's description. - * @return the description - */ - public String getDescription() { - return description_; - } - - /** - * Gets the plugin's file name. - * @return the file name - */ - public String getFilename() { - return filename_; - } - - /** - * Gets the plugin's name. - * @return the name - */ - public String getName() { - return name_; - } - - /** - * Gets the plugin's version. - * @return the version - */ - public String getVersion() { - return version_; - } - - /** - * Gets the associated mime types. - * @return a set of {@link MimeType} - */ - public Set getMimeTypes() { - return mimeTypes_; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return name_.hashCode(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(final Object o) { - if (!(o instanceof PluginConfiguration)) { - return false; - } - final PluginConfiguration other = (PluginConfiguration) o; - return name_.equals(other.name_); - } - - /** - * Creates and return a copy of this object. Current instance and cloned - * object can be modified independently. - * @return a clone of this instance. - */ - @Override - public PluginConfiguration clone() { - final PluginConfiguration clone = new PluginConfiguration(getName(), getDescription(), getVersion(), - getFilename()); - clone.getMimeTypes().addAll(getMimeTypes()); - - return clone; - } - - /** - * Holds information about a single mime type associated with a plugin. - */ - public static class MimeType implements Serializable { - - private final String description_; - private final String suffixes_; - private final String type_; - - /** - * Creates a new instance. - * @param type the mime type - * @param description the type description - * @param suffixes the file suffixes - */ - public MimeType(final String type, final String description, final String suffixes) { - WebAssert.notNull("type", type); - type_ = type; - description_ = description; - suffixes_ = suffixes; - } - - /** - * Returns the mime type's description. - * @return the description - */ - public String getDescription() { - return description_; - } - - /** - * Returns the mime type's suffixes. - * @return the suffixes - */ - public String getSuffixes() { - return suffixes_; - } - - /** - * Returns the mime type. - * @return the type - */ - public String getType() { - return type_; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return type_.hashCode(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(final Object o) { - if (!(o instanceof PluginConfiguration.MimeType)) { - return false; - } - final PluginConfiguration.MimeType other = (PluginConfiguration.MimeType) o; - return type_.equals(other.type_); - } - } - -} diff --git a/src/main/java/org/htmlunit/javascript/configuration/JavaScriptConfiguration.java b/src/main/java/org/htmlunit/javascript/configuration/JavaScriptConfiguration.java index 4bc44605c82..8dc95783f18 100644 --- a/src/main/java/org/htmlunit/javascript/configuration/JavaScriptConfiguration.java +++ b/src/main/java/org/htmlunit/javascript/configuration/JavaScriptConfiguration.java @@ -56,7 +56,6 @@ import org.htmlunit.javascript.host.Screen; import org.htmlunit.javascript.host.ScreenOrientation; import org.htmlunit.javascript.host.SharedWorker; -import org.htmlunit.javascript.host.SimpleArray; import org.htmlunit.javascript.host.Storage; import org.htmlunit.javascript.host.StorageManager; import org.htmlunit.javascript.host.TextDecoder; @@ -659,7 +658,7 @@ public final class JavaScriptConfiguration extends AbstractJavaScriptConfigurati Screen.class, ScreenOrientation.class, ScriptProcessorNode.class, SecurityPolicyViolationEvent.class, Selection.class, ServiceWorker.class, ServiceWorkerContainer.class, ServiceWorkerRegistration.class, - ShadowRoot.class, SharedWorker.class, SimpleArray.class, SourceBuffer.class, SourceBufferList.class, + ShadowRoot.class, SharedWorker.class, SourceBuffer.class, SourceBufferList.class, SpeechSynthesis.class, SpeechSynthesisErrorEvent.class, SpeechSynthesisEvent.class, SpeechSynthesisUtterance.class, SpeechSynthesisVoice.class, StereoPannerNode.class, Storage.class, StorageEvent.class, StorageManager.class, diff --git a/src/main/java/org/htmlunit/javascript/host/MimeType.java b/src/main/java/org/htmlunit/javascript/host/MimeType.java index 16ab50d5903..74c6961e147 100644 --- a/src/main/java/org/htmlunit/javascript/host/MimeType.java +++ b/src/main/java/org/htmlunit/javascript/host/MimeType.java @@ -26,7 +26,7 @@ * @author Ahmed Ashour * @author Ronald Brill * - * @see XUL Planet + * @see MimeType */ @JsxClass public class MimeType extends HtmlUnitScriptable { @@ -98,7 +98,7 @@ public String getType() { * @return the plugin */ @JsxGetter - public Object getEnabledPlugin() { + public Plugin getEnabledPlugin() { return enabledPlugin_; } } diff --git a/src/main/java/org/htmlunit/javascript/host/MimeTypeArray.java b/src/main/java/org/htmlunit/javascript/host/MimeTypeArray.java index a75da37aa09..82c68a29bd4 100644 --- a/src/main/java/org/htmlunit/javascript/host/MimeTypeArray.java +++ b/src/main/java/org/htmlunit/javascript/host/MimeTypeArray.java @@ -14,8 +14,17 @@ */ package org.htmlunit.javascript.host; +import java.util.ArrayList; +import java.util.List; + +import org.htmlunit.corejs.javascript.Scriptable; +import org.htmlunit.javascript.HtmlUnitScriptable; +import org.htmlunit.javascript.JavaScriptEngine; import org.htmlunit.javascript.configuration.JsxClass; import org.htmlunit.javascript.configuration.JsxConstructor; +import org.htmlunit.javascript.configuration.JsxFunction; +import org.htmlunit.javascript.configuration.JsxGetter; +import org.htmlunit.javascript.configuration.JsxSymbol; /** * A JavaScript object for {@code MimeTypeArray}. @@ -24,10 +33,12 @@ * @author Ahmed Ashour * @author Ronald Brill * - * @see XUL Planet + * @see MimeTypeArray */ @JsxClass -public class MimeTypeArray extends SimpleArray { +public class MimeTypeArray extends HtmlUnitScriptable { + + private final List elements_ = new ArrayList<>(); /** * JavaScript constructor. @@ -42,8 +53,81 @@ public void jsConstructor() { * @param element a {@link MimeType} * @return the name */ - @Override protected String getItemName(final Object element) { return ((MimeType) element).getType(); } + + /** + * Returns the item at the given index. + * @param index the index + * @return the item at the given position + */ + @JsxFunction + public MimeType item(final int index) { + return elements_.get(index); + } + + /** + * {@inheritDoc} + */ + @Override + protected Object getWithPreemption(final String name) { + final MimeType response = namedItem(name); + if (response != null) { + return response; + } + return NOT_FOUND; + } + + /** + * Returns the element at the specified index, or {@code null} if the index is invalid. + * {@inheritDoc} + */ + @Override + public final MimeType get(final int index, final Scriptable start) { + final MimeTypeArray array = (MimeTypeArray) start; + final List elements = array.elements_; + + if (index >= 0 && index < elements.size()) { + return elements.get(index); + } + return null; + } + + /** + * Returns the item at the given index. + * @param name the item name + * @return the item with the given name + */ + @JsxFunction + public MimeType namedItem(final String name) { + for (final MimeType element : elements_) { + if (name.equals(getItemName(element))) { + return element; + } + } + return null; + } + + /** + * Gets the array size. + * @return the number elements + */ + @JsxGetter + public int getLength() { + return elements_.size(); + } + + /** + * Adds an element. + * @param element the element to add + */ + void add(final MimeType element) { + elements_.add(element); + } + + @JsxSymbol + public Scriptable iterator() { + return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this); + } } diff --git a/src/main/java/org/htmlunit/javascript/host/Navigator.java b/src/main/java/org/htmlunit/javascript/host/Navigator.java index a3286c8c65e..b9e6c854de9 100644 --- a/src/main/java/org/htmlunit/javascript/host/Navigator.java +++ b/src/main/java/org/htmlunit/javascript/host/Navigator.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import org.apache.commons.lang3.StringUtils; -import org.htmlunit.PluginConfiguration; import org.htmlunit.WebClient; import org.htmlunit.corejs.javascript.Scriptable; import org.htmlunit.javascript.HtmlUnitScriptable; @@ -185,12 +184,16 @@ public String getUserAgent() { * @return an empty array */ @JsxGetter - public Object getPlugins() { - initPlugins(); + public PluginArray getPlugins() { + initPluginsAndMimeTypes(); return plugins_; } - private void initPlugins() { + private void initPluginsAndMimeTypes() { + // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/plugins + // Recent versions of the specification hard-code the returned list. + // If inline viewing of PDF files is supported the property lists five standard plugins. + // If inline PDF viewing is not supported then an empty list is returned. if (plugins_ != null) { return; } @@ -198,26 +201,60 @@ private void initPlugins() { plugins_.setParentScope(this); plugins_.setPrototype(getPrototype(PluginArray.class)); + Plugin plugin = new Plugin("PDF Viewer", "Portable Document Format", "internal-pdf-viewer"); + plugin.setParentScope(this); + plugin.setPrototype(getPrototype(Plugin.class)); + + // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/mimeTypes + // Recent versions of the specification hard-code the returned set of MIME types. + // If PDF files can be displayed inline then application/pdf and text/pdf are listed. + // Otherwise an empty list is returned. mimeTypes_ = new MimeTypeArray(); mimeTypes_.setParentScope(this); mimeTypes_.setPrototype(getPrototype(MimeTypeArray.class)); - for (final PluginConfiguration pluginConfig : getBrowserVersion().getPlugins()) { - final Plugin plugin = new Plugin(pluginConfig.getName(), pluginConfig.getDescription(), - pluginConfig.getVersion(), pluginConfig.getFilename()); - plugin.setParentScope(this); - plugin.setPrototype(getPrototype(Plugin.class)); - plugins_.add(plugin); - - for (final PluginConfiguration.MimeType mimeTypeConfig : pluginConfig.getMimeTypes()) { - final MimeType mimeType = new MimeType(mimeTypeConfig.getType(), mimeTypeConfig.getDescription(), - mimeTypeConfig.getSuffixes(), plugin); - mimeType.setParentScope(this); - mimeType.setPrototype(getPrototype(MimeType.class)); - mimeTypes_.add(mimeType); - plugin.add(mimeType); - } - } + final MimeType mimeTypeAppPdf = new MimeType("application/pdf", "Portable Document Format", "pdf", plugin); + mimeTypeAppPdf.setParentScope(this); + mimeTypeAppPdf.setPrototype(getPrototype(MimeType.class)); + mimeTypes_.add(mimeTypeAppPdf); + + final MimeType mimeTypeTxtPdf = new MimeType("text/pdf", "Portable Document Format", "pdf", plugin); + mimeTypeTxtPdf.setParentScope(this); + mimeTypeTxtPdf.setPrototype(getPrototype(MimeType.class)); + mimeTypes_.add(mimeTypeTxtPdf); + + plugin.add(mimeTypeAppPdf); + plugin.add(mimeTypeTxtPdf); + plugins_.add(plugin); + + // all the others + plugin = new Plugin("Chrome PDF Viewer", "Portable Document Format", "internal-pdf-viewer"); + plugin.setParentScope(this); + plugin.setPrototype(getPrototype(Plugin.class)); + plugin.add(mimeTypeAppPdf); + plugin.add(mimeTypeTxtPdf); + plugins_.add(plugin); + + plugin = new Plugin("Chromium PDF Viewer", "Portable Document Format", "internal-pdf-viewer"); + plugin.setParentScope(this); + plugin.setPrototype(getPrototype(Plugin.class)); + plugin.add(mimeTypeAppPdf); + plugin.add(mimeTypeTxtPdf); + plugins_.add(plugin); + + plugin = new Plugin("Microsoft Edge PDF Viewer", "Portable Document Format", "internal-pdf-viewer"); + plugin.setParentScope(this); + plugin.setPrototype(getPrototype(Plugin.class)); + plugin.add(mimeTypeAppPdf); + plugin.add(mimeTypeTxtPdf); + plugins_.add(plugin); + + plugin = new Plugin("WebKit built-in PDF", "Portable Document Format", "internal-pdf-viewer"); + plugin.setParentScope(this); + plugin.setPrototype(getPrototype(Plugin.class)); + plugin.add(mimeTypeAppPdf); + plugin.add(mimeTypeTxtPdf); + plugins_.add(plugin); } /** @@ -225,8 +262,8 @@ private void initPlugins() { * @return the {@code mimeTypes} property */ @JsxGetter - public Object getMimeTypes() { - initPlugins(); + public MimeTypeArray getMimeTypes() { + initPluginsAndMimeTypes(); return mimeTypes_; } diff --git a/src/main/java/org/htmlunit/javascript/host/Plugin.java b/src/main/java/org/htmlunit/javascript/host/Plugin.java index d4303a684c3..06bee96bee2 100644 --- a/src/main/java/org/htmlunit/javascript/host/Plugin.java +++ b/src/main/java/org/htmlunit/javascript/host/Plugin.java @@ -14,12 +14,17 @@ */ package org.htmlunit.javascript.host; -import static org.htmlunit.javascript.configuration.SupportedBrowser.FF; -import static org.htmlunit.javascript.configuration.SupportedBrowser.FF_ESR; +import java.util.ArrayList; +import java.util.List; +import org.htmlunit.corejs.javascript.Scriptable; +import org.htmlunit.javascript.HtmlUnitScriptable; +import org.htmlunit.javascript.JavaScriptEngine; import org.htmlunit.javascript.configuration.JsxClass; import org.htmlunit.javascript.configuration.JsxConstructor; +import org.htmlunit.javascript.configuration.JsxFunction; import org.htmlunit.javascript.configuration.JsxGetter; +import org.htmlunit.javascript.configuration.JsxSymbol; /** * A JavaScript object for {@code Plugin}. @@ -30,11 +35,12 @@ * @see XUL Planet */ @JsxClass -public class Plugin extends SimpleArray { +public class Plugin extends HtmlUnitScriptable { private String description_; private String filename_; private String name_; - private String version_; + + private final List elements_ = new ArrayList<>(); /** * Creates an instance. @@ -56,25 +62,85 @@ public void jsConstructor() { * * @param name the plugin name * @param description the plugin description - * @param version the version * @param filename the plugin filename */ - public Plugin(final String name, final String description, final String version, final String filename) { + public Plugin(final String name, final String description, final String filename) { super(); name_ = name; description_ = description; - version_ = version; filename_ = filename; } /** - * Gets the name of the mime type. - * @param element a {@link MimeType} - * @return the name + * Returns the item at the given index. + * @param index the index + * @return the item at the given position + */ + @JsxFunction + public MimeType item(final int index) { + if (index >= 0 && index < elements_.size()) { + return elements_.get(index); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + protected Object getWithPreemption(final String name) { + final MimeType response = namedItem(name); + if (response != null) { + return response; + } + return NOT_FOUND; + } + + /** + * Returns the element at the specified index, or {@code null} if the index is invalid. + * {@inheritDoc} */ @Override - protected String getItemName(final Object element) { - return ((MimeType) element).getType(); + public final MimeType get(final int index, final Scriptable start) { + final Plugin plugin = (Plugin) start; + final List elements = plugin.elements_; + + if (index >= 0 && index < elements.size()) { + return elements.get(index); + } + return null; + } + + /** + * Returns the item at the given index. + * @param name the item name + * @return the item with the given name + */ + @JsxFunction + public MimeType namedItem(final String name) { + for (final MimeType element : elements_) { + if (name.equals(element.getType())) { + return element; + } + } + return null; + } + + /** + * Gets the array size. + * @return the number elements + */ + @JsxGetter + public int getLength() { + return elements_.size(); + } + + /** + * Adds an element. + * @param element the element to add + */ + void add(final MimeType element) { + elements_.add(element); } /** @@ -104,12 +170,8 @@ public String getName() { return name_; } - /** - * Gets the plugin's version. - * @return the name - */ - @JsxGetter({FF, FF_ESR}) - public String getVersion() { - return version_; + @JsxSymbol + public Scriptable iterator() { + return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this); } } diff --git a/src/main/java/org/htmlunit/javascript/host/PluginArray.java b/src/main/java/org/htmlunit/javascript/host/PluginArray.java index 9d1a9387850..db9d985cb2c 100644 --- a/src/main/java/org/htmlunit/javascript/host/PluginArray.java +++ b/src/main/java/org/htmlunit/javascript/host/PluginArray.java @@ -14,9 +14,17 @@ */ package org.htmlunit.javascript.host; +import java.util.ArrayList; +import java.util.List; + +import org.htmlunit.corejs.javascript.Scriptable; +import org.htmlunit.javascript.HtmlUnitScriptable; +import org.htmlunit.javascript.JavaScriptEngine; import org.htmlunit.javascript.configuration.JsxClass; import org.htmlunit.javascript.configuration.JsxConstructor; import org.htmlunit.javascript.configuration.JsxFunction; +import org.htmlunit.javascript.configuration.JsxGetter; +import org.htmlunit.javascript.configuration.JsxSymbol; /** * A JavaScript object for {@code PluginArray}. @@ -25,10 +33,12 @@ * @author Ahmed Ashour * @author Ronald Brill * - * @see XUL Planet + * @see PluginArray */ @JsxClass -public class PluginArray extends SimpleArray { +public class PluginArray extends HtmlUnitScriptable { + + private final List elements_ = new ArrayList<>(); /** * JavaScript constructor. @@ -38,6 +48,70 @@ public void jsConstructor() { // nothing to do } + /** + * Returns the item at the given index. + * @param index the index + * @return the item at the given position + */ + @JsxFunction + public Plugin item(final int index) { + if (index >= 0 && index < elements_.size()) { + return elements_.get(index); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + protected Object getWithPreemption(final String name) { + final Plugin response = namedItem(name); + if (response != null) { + return response; + } + return NOT_FOUND; + } + + /** + * Returns the element at the specified index, or {@code null} if the index is invalid. + * {@inheritDoc} + */ + @Override + public final Plugin get(final int index, final Scriptable start) { + final PluginArray array = (PluginArray) start; + final List elements = array.elements_; + + if (index >= 0 && index < elements.size()) { + return elements.get(index); + } + return null; + } + + /** + * Returns the item at the given index. + * @param name the item name + * @return the item with the given name + */ + @JsxFunction + public Plugin namedItem(final String name) { + for (final Plugin element : elements_) { + if (name.equals(getItemName(element))) { + return element; + } + } + return null; + } + + /** + * Gets the array size. + * @return the number elements + */ + @JsxGetter + public int getLength() { + return elements_.size(); + } + /** * Current implementation does nothing. * @param reloadDocuments reload yes / no @@ -53,8 +127,20 @@ public void refresh(final boolean reloadDocuments) { * @param element a {@link Plugin} * @return the name */ - @Override protected String getItemName(final Object element) { return ((Plugin) element).getName(); } + + /** + * Adds an element. + * @param element the element to add + */ + void add(final Plugin element) { + elements_.add(element); + } + + @JsxSymbol + public Scriptable iterator() { + return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this); + } } diff --git a/src/main/java/org/htmlunit/javascript/host/SimpleArray.java b/src/main/java/org/htmlunit/javascript/host/SimpleArray.java deleted file mode 100644 index 1ae7ffd2e1d..00000000000 --- a/src/main/java/org/htmlunit/javascript/host/SimpleArray.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2002-2024 Gargoyle Software Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.htmlunit.javascript.host; - -import java.util.ArrayList; -import java.util.List; - -import org.htmlunit.corejs.javascript.Scriptable; -import org.htmlunit.javascript.HtmlUnitScriptable; -import org.htmlunit.javascript.configuration.JsxClass; -import org.htmlunit.javascript.configuration.JsxFunction; -import org.htmlunit.javascript.configuration.JsxGetter; - -/** - * A JavaScript object for {@code SimpleArray} allowing access per key and index (like {@link MimeTypeArray}). - * - * @author Marc Guillemot - * - * @see XUL Planet - */ -@JsxClass(isJSObject = false) -public class SimpleArray extends HtmlUnitScriptable { - private final List elements_ = new ArrayList<>(); - - /** - * Returns the item at the given index. - * @param index the index - * @return the item at the given position - */ - @JsxFunction - public Object item(final int index) { - return elements_.get(index); - } - - /** - * {@inheritDoc} - */ - @Override - protected Object getWithPreemption(final String name) { - final Object response = namedItem(name); - if (response != null) { - return response; - } - return NOT_FOUND; - } - - /** - * Returns the element at the specified index, or {@code null} if the index is invalid. - * {@inheritDoc} - */ - @Override - public final Object get(final int index, final Scriptable start) { - final SimpleArray array = (SimpleArray) start; - final List elements = array.elements_; - - if (index >= 0 && index < elements.size()) { - return elements.get(index); - } - return null; - } - - /** - * Returns the item at the given index. - * @param name the item name - * @return the item with the given name - */ - @JsxFunction - public Object namedItem(final String name) { - for (final Object element : elements_) { - if (name.equals(getItemName(element))) { - return element; - } - } - return null; - } - - /** - * Gets the name of the element. - * Should be abstract but current implementation of prototype configuration doesn't allow it. - * @param element the array's element - * @return the element's name - */ - protected String getItemName(final Object element) { - return null; - } - - /** - * Gets the array size. - * @return the number elements - */ - @JsxGetter - public int getLength() { - return elements_.size(); - } - - /** - * Adds an element. - * @param element the element to add - */ - void add(final Object element) { - elements_.add(element); - } -} diff --git a/src/test/java/org/htmlunit/BrowserVersionTest.java b/src/test/java/org/htmlunit/BrowserVersionTest.java index 05ea46d41ad..1858636c7b4 100644 --- a/src/test/java/org/htmlunit/BrowserVersionTest.java +++ b/src/test/java/org/htmlunit/BrowserVersionTest.java @@ -51,12 +51,6 @@ public void getBrowserVersionNumeric() { public void testClone() { final BrowserVersion ff = BrowserVersion.FIREFOX; - final PluginConfiguration flash = new PluginConfiguration("Shockwave Flash", - "Shockwave Flash 32.0 r0", "32.0.0.445", "Flash.ocx"); - flash.getMimeTypes().add(new PluginConfiguration.MimeType("application/x-shockwave-flash", - "Shockwave Flash", "swf")); - ff.getPlugins().add(flash); - final BrowserVersion clone = new BrowserVersion.BrowserVersionBuilder(ff).build(); // Nickname is used as key for dictionaries storing browser setups @@ -64,11 +58,6 @@ public void testClone() { assertFalse(ff == clone); assertFalse(ff.equals(clone)); - - assertFalse(clone.getPlugins().isEmpty()); - clone.getPlugins().clear(); - assertTrue(clone.getPlugins().isEmpty()); - assertFalse(ff.getPlugins().isEmpty()); } /** diff --git a/src/test/java/org/htmlunit/archunit/ArchitectureTest.java b/src/test/java/org/htmlunit/archunit/ArchitectureTest.java index 5535851fab5..97b4221dbbc 100644 --- a/src/test/java/org/htmlunit/archunit/ArchitectureTest.java +++ b/src/test/java/org/htmlunit/archunit/ArchitectureTest.java @@ -186,10 +186,7 @@ public boolean test(final JavaClass javaClass) { .that() .areAnnotatedWith(JsxGetter.class) .and().doNotHaveFullName("org.htmlunit.javascript.host.History.getState()") - .and().doNotHaveFullName("org.htmlunit.javascript.host.MimeType.getEnabledPlugin()") .and().doNotHaveFullName("org.htmlunit.javascript.host.Navigator.getDoNotTrack()") - .and().doNotHaveFullName("org.htmlunit.javascript.host.Navigator.getMimeTypes()") - .and().doNotHaveFullName("org.htmlunit.javascript.host.Navigator.getPlugins()") .and().doNotHaveFullName("org.htmlunit.javascript.host.URL.getOrigin()") .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getClientInformation()") .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getControllers()") diff --git a/src/test/java/org/htmlunit/general/ElementOwnPropertySymbolsTest.java b/src/test/java/org/htmlunit/general/ElementOwnPropertySymbolsTest.java index 4736d8a1bbe..f475d518b8a 100644 --- a/src/test/java/org/htmlunit/general/ElementOwnPropertySymbolsTest.java +++ b/src/test/java/org/htmlunit/general/ElementOwnPropertySymbolsTest.java @@ -3264,10 +3264,10 @@ public void mimeTypeArray() throws Exception { * @throws Exception if an error occurs */ @Test - @Alerts(CHROME = "Symbol(Symbol.iterator) [WC] [function],Symbol(Symbol.toStringTag) [C] [MimeType]", - EDGE = "Symbol(Symbol.iterator) [WC] [function],Symbol(Symbol.toStringTag) [C] [MimeType]", - FF = "Symbol(Symbol.iterator) [WC] [function],Symbol(Symbol.toStringTag) [C] [MimeType]", - FF_ESR = "Symbol(Symbol.iterator) [WC] [function],Symbol(Symbol.toStringTag) [C] [MimeType]") + @Alerts(CHROME = ",Symbol(Symbol.toStringTag) [C] [MimeType]", + EDGE = "Symbol(Symbol.toStringTag) [C] [MimeType]", + FF = "Symbol(Symbol.toStringTag) [C] [MimeType]", + FF_ESR = "Symbol(Symbol.toStringTag) [C] [MimeType]") public void mimeType() throws Exception { testString("", "navigator.mimeTypes[0]"); } diff --git a/src/test/java/org/htmlunit/general/ElementPropertiesTest.java b/src/test/java/org/htmlunit/general/ElementPropertiesTest.java index d91b864c0cc..913bcc6d5cf 100644 --- a/src/test/java/org/htmlunit/general/ElementPropertiesTest.java +++ b/src/test/java/org/htmlunit/general/ElementPropertiesTest.java @@ -9192,6 +9192,10 @@ public void fileList2() throws Exception { EDGE = "0,1,2,3,4,item(),length,namedItem(),refresh()", FF = "0,1,2,3,4,item(),length,namedItem(),refresh()", FF_ESR = "0,1,2,3,4,item(),length,namedItem(),refresh()") + @HtmlUnitNYI(CHROME = "item(),length,namedItem(),refresh()", + EDGE = "item(),length,namedItem(),refresh()", + FF = "item(),length,namedItem(),refresh()", + FF_ESR = "item(),length,namedItem(),refresh()") public void pluginArray() throws Exception { testString("", "navigator.plugins"); } @@ -9206,6 +9210,10 @@ public void pluginArray() throws Exception { EDGE = "0,1,description,filename,item(),length,name,namedItem()", FF = "0,1,description,filename,item(),length,name,namedItem()", FF_ESR = "0,1,description,filename,item(),length,name,namedItem()") + @HtmlUnitNYI(CHROME = "description,filename,item(),length,name,namedItem()", + EDGE = "description,filename,item(),length,name,namedItem()", + FF = "description,filename,item(),length,name,namedItem()", + FF_ESR = "description,filename,item(),length,name,namedItem()") public void plugin() throws Exception { testString("", "navigator.plugins[0]"); } @@ -9220,6 +9228,10 @@ public void plugin() throws Exception { EDGE = "0,1,item(),length,namedItem()", FF = "0,1,item(),length,namedItem()", FF_ESR = "0,1,item(),length,namedItem()") + @HtmlUnitNYI(CHROME = "item(),length,namedItem()", + EDGE = "item(),length,namedItem()", + FF = "item(),length,namedItem()", + FF_ESR = "item(),length,namedItem()") public void mimeTypeArray() throws Exception { testString("", "navigator.mimeTypes"); } diff --git a/src/test/java/org/htmlunit/javascript/host/MimeTypeTest.java b/src/test/java/org/htmlunit/javascript/host/MimeTypeTest.java index 94e1987ecf6..b7375b36786 100644 --- a/src/test/java/org/htmlunit/javascript/host/MimeTypeTest.java +++ b/src/test/java/org/htmlunit/javascript/host/MimeTypeTest.java @@ -14,11 +14,7 @@ */ package org.htmlunit.javascript.host; -import java.util.HashSet; -import java.util.Set; - -import org.htmlunit.PluginConfiguration; -import org.htmlunit.SimpleWebTestCase; +import org.htmlunit.WebDriverTestCase; import org.htmlunit.junit.BrowserRunner; import org.htmlunit.junit.BrowserRunner.Alerts; import org.junit.Test; @@ -31,32 +27,85 @@ * @author Ronald Brill */ @RunWith(BrowserRunner.class) -public class MimeTypeTest extends SimpleWebTestCase { +public class MimeTypeTest extends WebDriverTestCase { /** - * Tests default configuration of Flash plugin for Firefox. * @throws Exception if the test fails */ @Test - @Alerts(DEFAULT = {"[object MimeType]", "swf", "Shockwave Flash", "true", "true"}, - CHROME = "undefined") - public void flashMimeType() throws Exception { + @Alerts({"2", "pdf", "Portable Document Format", "application/pdf", + "pdf", "Portable Document Format", "text/pdf"}) + public void mimeType() throws Exception { final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * Tests default configuration of Pdf plugin. + * @throws Exception if the test fails + */ + @Test + @Alerts({"[object MimeType]", "pdf", "PDF Viewer", "true", "true"}) + public void pdfMimeType() throws Exception { + final String html = "\n" + ""; - loadPage(html); + loadPageVerifyTitle2(html); + } + + /** + * Tests default configuration of Pdf plugin. + * @throws Exception if the test fails + */ + @Test + @Alerts({"[object MimeType]", "pdf", "PDF Viewer", "true", "true"}) + public void textPdfMimeType() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); } /** @@ -64,25 +113,40 @@ public void flashMimeType() throws Exception { * @throws Exception if the test fails */ @Test - @Alerts({"undefined", "undefined", "null"}) - public void removeFlashMimeType() throws Exception { + @Alerts("undefined") + public void flashMimeType() throws Exception { final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"2", "application/pdf", "text/pdf"}) + public void iterator() throws Exception { + final String html = "\n" + ""; - final Set plugins = new HashSet<>(getBrowserVersion().getPlugins()); - getBrowserVersion().getPlugins().clear(); - try { - loadPage(html); - } - finally { - getBrowserVersion().getPlugins().addAll(plugins); - } + loadPageVerifyTitle2(html); } } diff --git a/src/test/java/org/htmlunit/javascript/host/NavigatorTest.java b/src/test/java/org/htmlunit/javascript/host/NavigatorTest.java index 05c8987e5ab..55ca9e837e1 100644 --- a/src/test/java/org/htmlunit/javascript/host/NavigatorTest.java +++ b/src/test/java/org/htmlunit/javascript/host/NavigatorTest.java @@ -14,15 +14,12 @@ */ package org.htmlunit.javascript.host; -import org.htmlunit.PluginConfiguration; import org.htmlunit.WebDriverTestCase; import org.htmlunit.html.HtmlPageTest; import org.htmlunit.junit.BrowserRunner; import org.htmlunit.junit.BrowserRunner.Alerts; import org.junit.Test; import org.junit.runner.RunWith; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; /** * Tests for {@link Navigator}. @@ -171,38 +168,34 @@ public void userLanguage() throws Exception { * @throws Exception on test failure */ @Test + @Alerts({"5", + "PDF Viewer", "Portable Document Format", "internal-pdf-viewer", "undefined", + "Chrome PDF Viewer", "Portable Document Format", "internal-pdf-viewer", "undefined", + "Chromium PDF Viewer", "Portable Document Format", "internal-pdf-viewer", "undefined", + "Microsoft Edge PDF Viewer", "Portable Document Format", "internal-pdf-viewer", "undefined", + "WebKit built-in PDF", "Portable Document Format", "internal-pdf-viewer", "undefined"}) public void plugins() throws Exception { final String html = "\n" + "\n" - + " test\n" + " \n" + "\n" + "\n" - + " \n" + "\n" + ""; - final WebDriver driver = loadPage2(html); - final String alerts = driver.findElement(By.id("myTextarea")).getAttribute("value"); - - for (final PluginConfiguration plugin : getBrowserVersion().getPlugins()) { - assertTrue(plugin.getName() + " not found", alerts.contains(plugin.getName())); - } + loadPageVerifyTitle2(html); } /** @@ -241,6 +234,36 @@ public void pluginsShockwaveFlash() throws Exception { loadPageVerifyTitle2(html); } + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"2", + "application/pdf", "Portable Document Format", "pdf", "[object Plugin]", "true", + "text/pdf", "Portable Document Format", "pdf", "[object Plugin]", "true"}) + public void mimeTypes() throws Exception { + final String html + = "\n" + + " \n" + + "\n" + + ""; + + loadPageVerifyTitle2(html); + } + /** * Tests the {@code taintEnabled} property. * @throws Exception on test failure diff --git a/src/test/java/org/htmlunit/javascript/host/PluginTest.java b/src/test/java/org/htmlunit/javascript/host/PluginTest.java new file mode 100644 index 00000000000..4e6d8e95ab9 --- /dev/null +++ b/src/test/java/org/htmlunit/javascript/host/PluginTest.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2002-2024 Gargoyle Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.htmlunit.javascript.host; + +import org.htmlunit.WebDriverTestCase; +import org.htmlunit.junit.BrowserRunner; +import org.htmlunit.junit.BrowserRunner.Alerts; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Unit tests for {@link Plugin}. + * + * @author Ronald Brill + */ +@RunWith(BrowserRunner.class) +public class PluginTest extends WebDriverTestCase { + + @Test + @Alerts({"5", + "PDF Viewer", "Portable Document Format", "internal-pdf-viewer", "undefined", + "Chrome PDF Viewer", "Portable Document Format", "internal-pdf-viewer", "undefined", + "Chromium PDF Viewer", "Portable Document Format", "internal-pdf-viewer", "undefined", + "Microsoft Edge PDF Viewer", "Portable Document Format", "internal-pdf-viewer", "undefined", + "WebKit built-in PDF", "Portable Document Format", "internal-pdf-viewer", "undefined"}) + public void plugins() throws Exception { + final String html = "\n" + + "\n" + + " \n" + + "\n" + + "\n" + + "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts("Chromium PDF Viewer") + public void index() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts("Microsoft Edge PDF Viewer") + public void item() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts("Chromium PDF Viewer") + public void namedItem() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"true", "false"}) + public void in() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"2", "[object MimeType]", "application/pdf", "[object MimeType]", "text/pdf", "undefined"}) + public void pluginIndex() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"2", "[object MimeType]", "application/pdf", "[object MimeType]", "text/pdf", "null"}) + public void pluginItem() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"[object MimeType]", "application/pdf", "[object MimeType]", "application/pdf", "null"}) + public void pluginNamedItem() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"5", "PDF Viewer", "Chrome PDF Viewer", "Chromium PDF Viewer", + "Microsoft Edge PDF Viewer", "WebKit built-in PDF"}) + public void iterator() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"2", "application/pdf", "text/pdf"}) + public void pluginIterator() throws Exception { + final String html = "\n" + + ""; + + loadPageVerifyTitle2(html); + } +}