瀏覽代碼

LibWeb: Implement navigator.{plugins,mimeTypes}

Luke Wilde 2 年之前
父節點
當前提交
4d0277cd9a

+ 4 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -270,6 +270,8 @@ set(SOURCES
     HTML/MessageChannel.cpp
     HTML/MessageEvent.cpp
     HTML/MessagePort.cpp
+    HTML/MimeType.cpp
+    HTML/MimeTypeArray.cpp
     HTML/Navigator.cpp
     HTML/NavigatorID.cpp
     HTML/PageTransitionEvent.cpp
@@ -281,6 +283,8 @@ set(SOURCES
     HTML/Parser/ListOfActiveFormattingElements.cpp
     HTML/Parser/StackOfOpenElements.cpp
     HTML/Path2D.cpp
+    HTML/Plugin.cpp
+    HTML/PluginArray.cpp
     HTML/PromiseRejectionEvent.cpp
     HTML/Scripting/ClassicScript.cpp
     HTML/Scripting/Environments.cpp

+ 4 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -328,11 +328,15 @@ class Location;
 class MessageChannel;
 class MessageEvent;
 class MessagePort;
+class MimeType;
+class MimeTypeArray;
 struct NavigationParams;
 class Navigator;
 class Origin;
 class PageTransitionEvent;
 class Path2D;
+class Plugin;
+class PluginArray;
 struct PolicyContainer;
 class PromiseRejectionEvent;
 class WorkerDebugConsoleClient;

+ 65 - 0
Userland/Libraries/LibWeb/HTML/MimeType.cpp

@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/HTML/MimeType.h>
+#include <LibWeb/HTML/Scripting/Environments.h>
+#include <LibWeb/HTML/Window.h>
+
+namespace Web::HTML {
+
+MimeType::MimeType(JS::Realm& realm, String const& type)
+    : Bindings::PlatformObject(realm)
+    , m_type(type)
+{
+}
+
+MimeType::~MimeType() = default;
+
+JS::ThrowCompletionOr<void> MimeType::initialize(JS::Realm& realm)
+{
+    MUST_OR_THROW_OOM(Base::initialize(realm));
+    set_prototype(&Bindings::ensure_web_prototype<Bindings::MimeTypePrototype>(realm, "MimeType"));
+
+    return {};
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#concept-mimetype-type
+String const& MimeType::type() const
+{
+    // The MimeType interface's type getter steps are to return this's type.
+    return m_type;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-mimetype-description
+JS::ThrowCompletionOr<String> MimeType::description() const
+{
+    // The MimeType interface's description getter steps are to return "Portable Document Format".
+    static String description_string = TRY_OR_THROW_OOM(vm(), String::from_utf8("Portable Document Format"sv));
+    return description_string;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-mimetype-suffixes
+String const& MimeType::suffixes() const
+{
+    // The MimeType interface's suffixes getter steps are to return "pdf".
+    static String suffixes_string = String::from_utf8_short_string("pdf"sv);
+    return suffixes_string;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-mimetype-enabledplugin
+JS::NonnullGCPtr<Plugin> MimeType::enabled_plugin() const
+{
+    // The MimeType interface's enabledPlugin getter steps are to return this's relevant global object's PDF viewer plugin objects[0] (i.e., the generic "PDF Viewer" one).
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    auto plugin_objects = window.pdf_viewer_plugin_objects();
+
+    // NOTE: If a MimeType object was created, that means PDF viewer support is enabled, meaning there will be Plugin objects.
+    VERIFY(!plugin_objects.is_empty());
+    return plugin_objects.first();
+}
+
+}

+ 34 - 0
Userland/Libraries/LibWeb/HTML/MimeType.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/LegacyPlatformObject.h>
+
+namespace Web::HTML {
+
+// https://html.spec.whatwg.org/multipage/system-state.html#mimetype
+class MimeType : public Bindings::PlatformObject {
+    WEB_PLATFORM_OBJECT(MimeType, Bindings::PlatformObject);
+
+public:
+    virtual ~MimeType() override;
+
+    String const& type() const;
+    JS::ThrowCompletionOr<String> description() const;
+    String const& suffixes() const;
+    JS::NonnullGCPtr<Plugin> enabled_plugin() const;
+
+private:
+    MimeType(JS::Realm&, String const& type);
+
+    virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#concept-mimetype-type
+    String m_type;
+};
+
+}

+ 9 - 0
Userland/Libraries/LibWeb/HTML/MimeType.idl

@@ -0,0 +1,9 @@
+#import <HTML/Plugin.idl>
+
+[Exposed=Window, UseNewAKString]
+interface MimeType {
+    readonly attribute DOMString type;
+    readonly attribute DOMString description;
+    readonly attribute DOMString suffixes;
+    readonly attribute Plugin enabledPlugin;
+};

+ 112 - 0
Userland/Libraries/LibWeb/HTML/MimeTypeArray.cpp

@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/HTML/MimeTypeArray.h>
+#include <LibWeb/HTML/Scripting/Environments.h>
+#include <LibWeb/HTML/Window.h>
+#include <LibWeb/Page/Page.h>
+
+namespace Web::HTML {
+
+MimeTypeArray::MimeTypeArray(JS::Realm& realm)
+    : Bindings::LegacyPlatformObject(realm)
+{
+}
+
+MimeTypeArray::~MimeTypeArray() = default;
+
+JS::ThrowCompletionOr<void> MimeTypeArray::initialize(JS::Realm& realm)
+{
+    MUST_OR_THROW_OOM(Base::initialize(realm));
+    set_prototype(&Bindings::ensure_web_prototype<Bindings::MimeTypeArrayPrototype>(realm, "MimeTypeArray"));
+
+    return {};
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewing-support:support-named-properties-2
+Vector<DeprecatedString> MimeTypeArray::supported_property_names() const
+{
+    // The MimeTypeArray interface supports named properties. If the user agent's PDF viewer supported is true, then they are the PDF viewer mime types. Otherwise, they are the empty list.
+    auto const& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    VERIFY(window.page());
+    if (!window.page()->pdf_viewer_supported())
+        return {};
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-mime-types
+    static Vector<DeprecatedString> mime_types = {
+        "application/pdf"sv,
+        "text/pdf"sv,
+    };
+
+    return mime_types;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewing-support:supports-indexed-properties-2
+bool MimeTypeArray::is_supported_property_index(u32 index) const
+{
+    // The MimeTypeArray interface supports indexed properties. The supported property indices are the indices of this's relevant global object's PDF viewer mime type objects.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    return index < window.pdf_viewer_mime_type_objects().size();
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-mimetypearray-length
+size_t MimeTypeArray::length() const
+{
+    // The MimeTypeArray interface's length getter steps are to return this's relevant global object's PDF viewer mime type objects's size.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    return window.pdf_viewer_mime_type_objects().size();
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-mimetypearray-item
+JS::GCPtr<MimeType> MimeTypeArray::item(u32 index) const
+{
+    // 1. Let mimeTypes be this's relevant global object's PDF viewer mime type objects.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    auto mime_types = window.pdf_viewer_mime_type_objects();
+
+    // 2. If index < mimeType's size, then return mimeTypes[index].
+    if (index < mime_types.size())
+        return mime_types[index];
+
+    // 3. Return null.
+    return nullptr;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-mimetypearray-nameditem
+JS::GCPtr<MimeType> MimeTypeArray::named_item(String const& name) const
+{
+    // 1. For each MimeType mimeType of this's relevant global object's PDF viewer mime type objects: if mimeType's type is name, then return mimeType.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    auto mime_types = window.pdf_viewer_mime_type_objects();
+
+    for (auto& mime_type : mime_types) {
+        if (mime_type->type() == name)
+            return mime_type;
+    }
+
+    // 2. Return null.
+    return nullptr;
+}
+
+WebIDL::ExceptionOr<JS::Value> MimeTypeArray::item_value(size_t index) const
+{
+    auto return_value = item(index);
+    if (!return_value)
+        return JS::js_null();
+    return return_value.ptr();
+}
+
+WebIDL::ExceptionOr<JS::Value> MimeTypeArray::named_item_value(DeprecatedFlyString const& name) const
+{
+    auto converted_name = TRY_OR_THROW_OOM(vm(), String::from_deprecated_string(name));
+    auto return_value = named_item(converted_name);
+    if (!return_value)
+        return JS::js_null();
+    return return_value.ptr();
+}
+
+}

+ 48 - 0
Userland/Libraries/LibWeb/HTML/MimeTypeArray.h

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/LegacyPlatformObject.h>
+
+namespace Web::HTML {
+
+// https://html.spec.whatwg.org/multipage/system-state.html#mimetypearray
+class MimeTypeArray : public Bindings::LegacyPlatformObject {
+    WEB_PLATFORM_OBJECT(MimeTypeArray, Bindings::LegacyPlatformObject);
+
+public:
+    virtual ~MimeTypeArray() override;
+
+    size_t length() const;
+    JS::GCPtr<MimeType> item(u32 index) const;
+    JS::GCPtr<MimeType> named_item(String const& name) const;
+
+private:
+    MimeTypeArray(JS::Realm&);
+
+    virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
+
+    // ^Bindings::LegacyPlatformObject
+    virtual Vector<DeprecatedString> supported_property_names() const override;
+    virtual WebIDL::ExceptionOr<JS::Value> item_value(size_t index) const override;
+    virtual WebIDL::ExceptionOr<JS::Value> named_item_value(DeprecatedFlyString const& name) const override;
+    virtual bool is_supported_property_index(u32) const override;
+
+    virtual bool supports_indexed_properties() const override { return true; }
+    virtual bool supports_named_properties() const override { return true; }
+    virtual bool has_indexed_property_setter() const override { return false; }
+    virtual bool has_named_property_setter() const override { return false; }
+    virtual bool has_named_property_deleter() const override { return false; }
+    virtual bool has_legacy_override_built_ins_interface_extended_attribute() const override { return false; }
+    virtual bool has_legacy_unenumerable_named_properties_interface_extended_attribute() const override { return true; }
+    virtual bool has_global_interface_extended_attribute() const override { return false; }
+    virtual bool indexed_property_setter_has_identifier() const override { return false; }
+    virtual bool named_property_setter_has_identifier() const override { return false; }
+    virtual bool named_property_deleter_has_identifier() const override { return false; }
+};
+
+}

+ 8 - 0
Userland/Libraries/LibWeb/HTML/MimeTypeArray.idl

@@ -0,0 +1,8 @@
+#import <HTML/MimeType.idl>
+
+[Exposed=Window, LegacyUnenumerableNamedProperties, UseNewAKString]
+interface MimeTypeArray {
+    readonly attribute unsigned long length;
+    getter MimeType? item(unsigned long index);
+    getter MimeType? namedItem(DOMString name);
+};

+ 22 - 1
Userland/Libraries/LibWeb/HTML/Navigator.cpp

@@ -39,7 +39,7 @@ JS::ThrowCompletionOr<void> Navigator::initialize(JS::Realm& realm)
 bool Navigator::pdf_viewer_enabled() const
 {
     // The NavigatorPlugins mixin's pdfViewerEnabled getter steps are to return the user agent's PDF viewer supported.
-    // NOTE: THe NavigatorPlugins mixin should only be exposed on the Window object.
+    // NOTE: The NavigatorPlugins mixin should only be exposed on the Window object.
     auto const& window = verify_cast<HTML::Window>(HTML::current_global_object());
     return window.page()->pdf_viewer_supported();
 }
@@ -54,4 +54,25 @@ bool Navigator::webdriver() const
     return window.page()->is_webdriver_active();
 }
 
+void Navigator::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_mime_type_array);
+    visitor.visit(m_plugin_array);
+}
+
+JS::ThrowCompletionOr<JS::NonnullGCPtr<MimeTypeArray>> Navigator::mime_types()
+{
+    if (!m_mime_type_array)
+        m_mime_type_array = TRY(heap().allocate<MimeTypeArray>(realm(), realm()));
+    return *m_mime_type_array;
+}
+
+JS::ThrowCompletionOr<JS::NonnullGCPtr<PluginArray>> Navigator::plugins()
+{
+    if (!m_plugin_array)
+        m_plugin_array = TRY(heap().allocate<PluginArray>(realm(), realm()));
+    return *m_plugin_array;
+}
+
 }

+ 11 - 1
Userland/Libraries/LibWeb/HTML/Navigator.h

@@ -7,10 +7,12 @@
 #pragma once
 
 #include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/HTML/MimeTypeArray.h>
 #include <LibWeb/HTML/NavigatorConcurrentHardware.h>
 #include <LibWeb/HTML/NavigatorID.h>
 #include <LibWeb/HTML/NavigatorLanguage.h>
 #include <LibWeb/HTML/NavigatorOnLine.h>
+#include <LibWeb/HTML/PluginArray.h>
 
 namespace Web::HTML {
 
@@ -35,17 +37,25 @@ public:
     // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-javaenabled
     bool java_enabled() const { return false; }
 
-    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-pdfviewerenabled
     bool pdf_viewer_enabled() const;
 
     bool webdriver() const;
 
+    JS::ThrowCompletionOr<JS::NonnullGCPtr<MimeTypeArray>> mime_types();
+    JS::ThrowCompletionOr<JS::NonnullGCPtr<PluginArray>> plugins();
+
     virtual ~Navigator() override;
 
+protected:
+    virtual void visit_edges(Cell::Visitor&) override;
+
 private:
     explicit Navigator(JS::Realm&);
 
     virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
+
+    JS::GCPtr<PluginArray> m_plugin_array;
+    JS::GCPtr<MimeTypeArray> m_mime_type_array;
 };
 
 }

+ 4 - 2
Userland/Libraries/LibWeb/HTML/Navigator.idl

@@ -1,7 +1,9 @@
+#import <HTML/MimeTypeArray.idl>
 #import <HTML/NavigatorID.idl>
 #import <HTML/NavigatorLanguage.idl>
 #import <HTML/NavigatorOnLine.idl>
 #import <HTML/NavigatorConcurrentHardware.idl>
+#import <HTML/PluginArray.idl>
 
 // https://html.spec.whatwg.org/multipage/system-state.html#navigator
 [Exposed=Window]
@@ -25,8 +27,8 @@ interface mixin NavigatorCookies {
 
 // https://html.spec.whatwg.org/multipage/system-state.html#navigatorplugins
 interface mixin NavigatorPlugins {
-    // FIXME: [SameObject] readonly attribute PluginArray plugins;
-    // FIXME: [SameObject] readonly attribute MimeTypeArray mimeTypes;
+    [SameObject] readonly attribute PluginArray plugins;
+    [SameObject] readonly attribute MimeTypeArray mimeTypes;
     boolean javaEnabled();
     readonly attribute boolean pdfViewerEnabled;
 };

+ 135 - 0
Userland/Libraries/LibWeb/HTML/Plugin.cpp

@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/HTML/Plugin.h>
+#include <LibWeb/HTML/Scripting/Environments.h>
+#include <LibWeb/HTML/Window.h>
+#include <LibWeb/Page/Page.h>
+
+namespace Web::HTML {
+
+Plugin::Plugin(JS::Realm& realm, String const& name)
+    : Bindings::LegacyPlatformObject(realm)
+    , m_name(name)
+{
+}
+
+Plugin::~Plugin() = default;
+
+JS::ThrowCompletionOr<void> Plugin::initialize(JS::Realm& realm)
+{
+    MUST_OR_THROW_OOM(Base::initialize(realm));
+    set_prototype(&Bindings::ensure_web_prototype<Bindings::PluginPrototype>(realm, "Plugin"));
+
+    return {};
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-plugin-name
+String const& Plugin::name() const
+{
+    // The Plugin interface's name getter steps are to return this's name.
+    return m_name;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-plugin-description
+JS::ThrowCompletionOr<String> Plugin::description() const
+{
+    // The Plugin interface's description getter steps are to return "Portable Document Format".
+    static String description_string = TRY_OR_THROW_OOM(vm(), String::from_utf8("Portable Document Format"sv));
+    return description_string;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-plugin-filename
+JS::ThrowCompletionOr<String> Plugin::filename() const
+{
+    // The Plugin interface's filename getter steps are to return "internal-pdf-viewer".
+    static String filename_string = TRY_OR_THROW_OOM(vm(), String::from_utf8("internal-pdf-viewer"sv));
+    return filename_string;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewing-support:support-named-properties-3
+Vector<DeprecatedString> Plugin::supported_property_names() const
+{
+    // The Plugin interface supports named properties. If the user agent's PDF viewer supported is true, then they are the PDF viewer mime types. Otherwise, they are the empty list.
+    auto const& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    VERIFY(window.page());
+    if (!window.page()->pdf_viewer_supported())
+        return {};
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-mime-types
+    static Vector<DeprecatedString> mime_types = {
+        "application/pdf"sv,
+        "text/pdf"sv,
+    };
+
+    return mime_types;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewing-support:supports-indexed-properties-3
+bool Plugin::is_supported_property_index(u32 index) const
+{
+    // The Plugin interface supports indexed properties. The supported property indices are the indices of this's relevant global object's PDF viewer mime type objects.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    return index < window.pdf_viewer_mime_type_objects().size();
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-plugin-length
+size_t Plugin::length() const
+{
+    // The Plugin interface's length getter steps are to return this's relevant global object's PDF viewer mime type objects's size.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    return window.pdf_viewer_mime_type_objects().size();
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-plugin-item
+JS::GCPtr<MimeType> Plugin::item(u32 index) const
+{
+    // 1. Let mimeTypes be this's relevant global object's PDF viewer mime type objects.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    auto mime_types = window.pdf_viewer_mime_type_objects();
+
+    // 2. If index < mimeType's size, then return mimeTypes[index].
+    if (index < mime_types.size())
+        return mime_types[index];
+
+    // 3. Return null.
+    return nullptr;
+}
+
+JS::GCPtr<MimeType> Plugin::named_item(String const& name) const
+{
+    // 1. For each MimeType mimeType of this's relevant global object's PDF viewer mime type objects: if mimeType's type is name, then return mimeType.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    auto mime_types = window.pdf_viewer_mime_type_objects();
+
+    for (auto& mime_type : mime_types) {
+        if (mime_type->type() == name)
+            return mime_type;
+    }
+
+    // 2. Return null.
+    return nullptr;
+}
+
+WebIDL::ExceptionOr<JS::Value> Plugin::item_value(size_t index) const
+{
+    auto return_value = item(index);
+    if (!return_value)
+        return JS::js_null();
+    return return_value.ptr();
+}
+
+WebIDL::ExceptionOr<JS::Value> Plugin::named_item_value(DeprecatedFlyString const& name) const
+{
+    auto converted_name = TRY_OR_THROW_OOM(vm(), String::from_deprecated_string(name));
+    auto return_value = named_item(converted_name);
+    if (!return_value)
+        return JS::js_null();
+    return return_value.ptr();
+}
+
+}

+ 54 - 0
Userland/Libraries/LibWeb/HTML/Plugin.h

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/LegacyPlatformObject.h>
+
+namespace Web::HTML {
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-plugin
+class Plugin : public Bindings::LegacyPlatformObject {
+    WEB_PLATFORM_OBJECT(Plugin, Bindings::LegacyPlatformObject);
+
+public:
+    virtual ~Plugin() override;
+
+    String const& name() const;
+    JS::ThrowCompletionOr<String> description() const;
+    JS::ThrowCompletionOr<String> filename() const;
+    size_t length() const;
+    JS::GCPtr<MimeType> item(u32 index) const;
+    JS::GCPtr<MimeType> named_item(String const& name) const;
+
+private:
+    Plugin(JS::Realm&, String const& name);
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#concept-plugin-name
+    String m_name;
+
+    virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
+
+    // ^Bindings::LegacyPlatformObject
+    virtual Vector<DeprecatedString> supported_property_names() const override;
+    virtual WebIDL::ExceptionOr<JS::Value> item_value(size_t index) const override;
+    virtual WebIDL::ExceptionOr<JS::Value> named_item_value(DeprecatedFlyString const& name) const override;
+    virtual bool is_supported_property_index(u32) const override;
+
+    virtual bool supports_indexed_properties() const override { return true; }
+    virtual bool supports_named_properties() const override { return true; }
+    virtual bool has_indexed_property_setter() const override { return false; }
+    virtual bool has_named_property_setter() const override { return false; }
+    virtual bool has_named_property_deleter() const override { return false; }
+    virtual bool has_legacy_override_built_ins_interface_extended_attribute() const override { return false; }
+    virtual bool has_legacy_unenumerable_named_properties_interface_extended_attribute() const override { return true; }
+    virtual bool has_global_interface_extended_attribute() const override { return false; }
+    virtual bool indexed_property_setter_has_identifier() const override { return false; }
+    virtual bool named_property_setter_has_identifier() const override { return false; }
+    virtual bool named_property_deleter_has_identifier() const override { return false; }
+};
+
+}

+ 11 - 0
Userland/Libraries/LibWeb/HTML/Plugin.idl

@@ -0,0 +1,11 @@
+#import <HTML/MimeType.idl>
+
+[Exposed=Window, LegacyUnenumerableNamedProperties, UseNewAKString]
+interface Plugin {
+    readonly attribute DOMString name;
+    readonly attribute DOMString description;
+    readonly attribute DOMString filename;
+    readonly attribute unsigned long length;
+    getter MimeType? item(unsigned long index);
+    getter MimeType? namedItem(DOMString name);
+};

+ 121 - 0
Userland/Libraries/LibWeb/HTML/PluginArray.cpp

@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/HTML/PluginArray.h>
+#include <LibWeb/HTML/Scripting/Environments.h>
+#include <LibWeb/HTML/Window.h>
+#include <LibWeb/Page/Page.h>
+
+namespace Web::HTML {
+
+PluginArray::PluginArray(JS::Realm& realm)
+    : Bindings::LegacyPlatformObject(realm)
+{
+}
+
+PluginArray::~PluginArray() = default;
+
+JS::ThrowCompletionOr<void> PluginArray::initialize(JS::Realm& realm)
+{
+    MUST_OR_THROW_OOM(Base::initialize(realm));
+    set_prototype(&Bindings::ensure_web_prototype<Bindings::PluginArrayPrototype>(realm, "PluginArray"));
+
+    return {};
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-pluginarray-refresh
+void PluginArray::refresh() const
+{
+    // The PluginArray interface's refresh() method steps are to do nothing.
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewing-support:support-named-properties
+Vector<DeprecatedString> PluginArray::supported_property_names() const
+{
+    // The PluginArray interface supports named properties. If the user agent's PDF viewer supported is true, then they are the PDF viewer plugin names. Otherwise, they are the empty list.
+    auto const& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    VERIFY(window.page());
+    if (!window.page()->pdf_viewer_supported())
+        return {};
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-plugin-names
+    static Vector<DeprecatedString> plugin_names = {
+        "PDF Viewer"sv,
+        "Chrome PDF Viewer"sv,
+        "Chromium PDF Viewer"sv,
+        "Microsoft Edge PDF Viewer"sv,
+        "WebKit built-in PDF"sv,
+    };
+
+    return plugin_names;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewing-support:supports-indexed-properties
+bool PluginArray::is_supported_property_index(u32 index) const
+{
+    // The PluginArray interface supports indexed properties. The supported property indices are the indices of this's relevant global object's PDF viewer plugin objects.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    return index < window.pdf_viewer_plugin_objects().size();
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-pluginarray-length
+size_t PluginArray::length() const
+{
+    // The PluginArray interface's length getter steps are to return this's relevant global object's PDF viewer plugin objects's size.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    return window.pdf_viewer_plugin_objects().size();
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-pluginarray-item
+JS::GCPtr<Plugin> PluginArray::item(u32 index) const
+{
+    // 1. Let plugins be this's relevant global object's PDF viewer plugin objects.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    auto plugins = window.pdf_viewer_plugin_objects();
+
+    // 2. If index < plugins's size, then return plugins[index].
+    if (index < plugins.size())
+        return plugins[index];
+
+    // 3. Return null.
+    return nullptr;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-pluginarray-nameditem
+JS::GCPtr<Plugin> PluginArray::named_item(String const& name) const
+{
+    // 1. For each Plugin plugin of this's relevant global object's PDF viewer plugin objects: if plugin's name is name, then return plugin.
+    auto& window = verify_cast<HTML::Window>(HTML::relevant_global_object(*this));
+    auto plugins = window.pdf_viewer_plugin_objects();
+
+    for (auto& plugin : plugins) {
+        if (plugin->name() == name)
+            return plugin;
+    }
+
+    // 2. Return null.
+    return nullptr;
+}
+
+WebIDL::ExceptionOr<JS::Value> PluginArray::item_value(size_t index) const
+{
+    auto return_value = item(index);
+    if (!return_value)
+        return JS::js_null();
+    return return_value.ptr();
+}
+
+WebIDL::ExceptionOr<JS::Value> PluginArray::named_item_value(DeprecatedFlyString const& name) const
+{
+    auto converted_name = TRY_OR_THROW_OOM(vm(), String::from_deprecated_string(name));
+    auto return_value = named_item(converted_name);
+    if (!return_value)
+        return JS::js_null();
+    return return_value.ptr();
+}
+
+}

+ 49 - 0
Userland/Libraries/LibWeb/HTML/PluginArray.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/LegacyPlatformObject.h>
+
+namespace Web::HTML {
+
+// https://html.spec.whatwg.org/multipage/system-state.html#pluginarray
+class PluginArray : public Bindings::LegacyPlatformObject {
+    WEB_PLATFORM_OBJECT(PluginArray, Bindings::LegacyPlatformObject);
+
+public:
+    virtual ~PluginArray() override;
+
+    void refresh() const;
+    size_t length() const;
+    JS::GCPtr<Plugin> item(u32 index) const;
+    JS::GCPtr<Plugin> named_item(String const& name) const;
+
+private:
+    PluginArray(JS::Realm&);
+
+    virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
+
+    // ^Bindings::LegacyPlatformObject
+    virtual Vector<DeprecatedString> supported_property_names() const override;
+    virtual WebIDL::ExceptionOr<JS::Value> item_value(size_t index) const override;
+    virtual WebIDL::ExceptionOr<JS::Value> named_item_value(DeprecatedFlyString const& name) const override;
+    virtual bool is_supported_property_index(u32) const override;
+
+    virtual bool supports_indexed_properties() const override { return true; }
+    virtual bool supports_named_properties() const override { return true; }
+    virtual bool has_indexed_property_setter() const override { return false; }
+    virtual bool has_named_property_setter() const override { return false; }
+    virtual bool has_named_property_deleter() const override { return false; }
+    virtual bool has_legacy_override_built_ins_interface_extended_attribute() const override { return false; }
+    virtual bool has_legacy_unenumerable_named_properties_interface_extended_attribute() const override { return true; }
+    virtual bool has_global_interface_extended_attribute() const override { return false; }
+    virtual bool indexed_property_setter_has_identifier() const override { return false; }
+    virtual bool named_property_setter_has_identifier() const override { return false; }
+    virtual bool named_property_deleter_has_identifier() const override { return false; }
+};
+
+}

+ 9 - 0
Userland/Libraries/LibWeb/HTML/PluginArray.idl

@@ -0,0 +1,9 @@
+#import <HTML/Plugin.idl>
+
+[Exposed=Window, LegacyUnenumerableNamedProperties, UseNewAKString]
+interface PluginArray {
+    undefined refresh();
+    readonly attribute unsigned long length;
+    getter Plugin? item(unsigned long index);
+    getter Plugin? namedItem(DOMString name);
+};

+ 52 - 0
Userland/Libraries/LibWeb/HTML/Window.cpp

@@ -108,6 +108,10 @@ void Window::visit_edges(JS::Cell::Visitor& visitor)
     visitor.visit(m_navigator);
     for (auto& it : m_timers)
         visitor.visit(it.value.ptr());
+    for (auto& plugin_object : m_pdf_viewer_plugin_objects)
+        visitor.visit(plugin_object);
+    for (auto& mime_type_object : m_pdf_viewer_mime_type_objects)
+        visitor.visit(mime_type_object);
 }
 
 Window::~Window() = default;
@@ -1060,6 +1064,54 @@ HTML::BrowsingContext* Window::browsing_context()
     return m_associated_document->browsing_context();
 }
 
+// https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-plugin-objects
+Vector<JS::NonnullGCPtr<Plugin>> Window::pdf_viewer_plugin_objects()
+{
+    // Each Window object has a PDF viewer plugin objects list. If the user agent's PDF viewer supported is false, then it is the empty list.
+    // Otherwise, it is a list containing five Plugin objects, whose names are, respectively:
+    // 0.   "PDF Viewer"
+    // 1.   "Chrome PDF Viewer"
+    // 2.   "Chromium PDF Viewer"
+    // 3.   "Microsoft Edge PDF Viewer"
+    // 4.   "WebKit built-in PDF"
+    // The values of the above list form the PDF viewer plugin names list. https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-plugin-names
+    VERIFY(page());
+    if (!page()->pdf_viewer_supported())
+        return {};
+
+    if (m_pdf_viewer_plugin_objects.is_empty()) {
+        // FIXME: Remove the MUSTs and propagate the errors instead.
+        m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), MUST(String::from_utf8("PDF Viewer"sv))).release_allocated_value_but_fixme_should_propagate_errors());
+        m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), MUST(String::from_utf8("Chrome PDF Viewer"sv))).release_allocated_value_but_fixme_should_propagate_errors());
+        m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), MUST(String::from_utf8("Chromium PDF Viewer"sv))).release_allocated_value_but_fixme_should_propagate_errors());
+        m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), MUST(String::from_utf8("Microsoft Edge PDF Viewer"sv))).release_allocated_value_but_fixme_should_propagate_errors());
+        m_pdf_viewer_plugin_objects.append(realm().heap().allocate<Plugin>(realm(), realm(), MUST(String::from_utf8("WebKit built-in PDF"sv))).release_allocated_value_but_fixme_should_propagate_errors());
+    }
+
+    return m_pdf_viewer_plugin_objects;
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-mime-type-objects
+Vector<JS::NonnullGCPtr<MimeType>> Window::pdf_viewer_mime_type_objects()
+{
+    // Each Window object has a PDF viewer mime type objects list. If the user agent's PDF viewer supported is false, then it is the empty list.
+    // Otherwise, it is a list containing two MimeType objects, whose types are, respectively:
+    // 0.   "application/pdf"
+    // 1.   "text/pdf"
+    // The values of the above list form the PDF viewer mime types list. https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-mime-types
+    VERIFY(page());
+    if (!page()->pdf_viewer_supported())
+        return {};
+
+    if (m_pdf_viewer_mime_type_objects.is_empty()) {
+        // FIXME: Remove the MUSTs and propagate the errors instead.
+        m_pdf_viewer_mime_type_objects.append(realm().heap().allocate<MimeType>(realm(), realm(), MUST(String::from_utf8("application/pdf"sv))).release_allocated_value_but_fixme_should_propagate_errors());
+        m_pdf_viewer_mime_type_objects.append(realm().heap().allocate<MimeType>(realm(), realm(), MUST(String::from_utf8("text/pdf"sv))).release_allocated_value_but_fixme_should_propagate_errors());
+    }
+
+    return m_pdf_viewer_mime_type_objects;
+}
+
 void Window::initialize_web_interfaces(Badge<WindowEnvironmentSettingsObject>)
 {
     auto& realm = this->realm();

+ 11 - 0
Userland/Libraries/LibWeb/HTML/Window.h

@@ -20,6 +20,8 @@
 #include <LibWeb/HTML/AnimationFrameCallbackDriver.h>
 #include <LibWeb/HTML/CrossOrigin/CrossOriginPropertyDescriptorMap.h>
 #include <LibWeb/HTML/GlobalEventHandlers.h>
+#include <LibWeb/HTML/MimeType.h>
+#include <LibWeb/HTML/Plugin.h>
 #include <LibWeb/HTML/Scripting/ImportMap.h>
 #include <LibWeb/HTML/WindowEventHandlers.h>
 
@@ -137,6 +139,9 @@ public:
 
     void initialize_web_interfaces(Badge<WindowEnvironmentSettingsObject>);
 
+    Vector<JS::NonnullGCPtr<Plugin>> pdf_viewer_plugin_objects();
+    Vector<JS::NonnullGCPtr<MimeType>> pdf_viewer_mime_type_objects();
+
 private:
     explicit Window(JS::Realm&);
 
@@ -184,6 +189,12 @@ private:
     // https://w3c.github.io/requestidlecallback/#dfn-idle-callback-identifier
     u32 m_idle_callback_identifier = 0;
 
+    // https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-plugin-objects
+    Vector<JS::NonnullGCPtr<Plugin>> m_pdf_viewer_plugin_objects;
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#pdf-viewer-mime-type-objects
+    Vector<JS::NonnullGCPtr<MimeType>> m_pdf_viewer_mime_type_objects;
+
 public:
     HTML::Origin origin() const;
 

+ 4 - 0
Userland/Libraries/LibWeb/idl_files.cmake

@@ -152,9 +152,13 @@ libweb_js_bindings(HTML/Location)
 libweb_js_bindings(HTML/MessageChannel)
 libweb_js_bindings(HTML/MessageEvent)
 libweb_js_bindings(HTML/MessagePort)
+libweb_js_bindings(HTML/MimeType)
+libweb_js_bindings(HTML/MimeTypeArray)
 libweb_js_bindings(HTML/Navigator)
 libweb_js_bindings(HTML/PageTransitionEvent)
 libweb_js_bindings(HTML/Path2D)
+libweb_js_bindings(HTML/Plugin)
+libweb_js_bindings(HTML/PluginArray)
 libweb_js_bindings(HTML/PromiseRejectionEvent)
 libweb_js_bindings(HTML/Storage)
 libweb_js_bindings(HTML/SubmitEvent)