Browse Source

LibWeb: Re-implement HTML::Navigator using IDL

Get rid of the bespoke NavigatorObject class and use the modern IDL
strategies for creating platform objects to re-implement Navigator and
its associcated mixin interfaces. While we're here, implement it in a
way that brings WorkerNavigator up to spec :^)
Andrew Kaster 2 years ago
parent
commit
2d5bee256e
27 changed files with 343 additions and 208 deletions
  1. 2 5
      Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp
  2. 0 39
      Userland/Libraries/LibWeb/Bindings/NavigatorConstructor.cpp
  3. 0 28
      Userland/Libraries/LibWeb/Bindings/NavigatorConstructor.h
  4. 0 66
      Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp
  5. 0 30
      Userland/Libraries/LibWeb/Bindings/NavigatorObject.h
  6. 0 25
      Userland/Libraries/LibWeb/Bindings/NavigatorPrototype.h
  7. 2 2
      Userland/Libraries/LibWeb/CMakeLists.txt
  8. 1 0
      Userland/Libraries/LibWeb/Forward.h
  9. 27 0
      Userland/Libraries/LibWeb/HTML/Navigator.cpp
  10. 48 0
      Userland/Libraries/LibWeb/HTML/Navigator.h
  11. 41 0
      Userland/Libraries/LibWeb/HTML/Navigator.idl
  12. 17 0
      Userland/Libraries/LibWeb/HTML/NavigatorConcurrentHardware.h
  13. 4 0
      Userland/Libraries/LibWeb/HTML/NavigatorConcurrentHardware.idl
  14. 53 0
      Userland/Libraries/LibWeb/HTML/NavigatorID.cpp
  15. 51 0
      Userland/Libraries/LibWeb/HTML/NavigatorID.h
  16. 12 0
      Userland/Libraries/LibWeb/HTML/NavigatorID.idl
  17. 28 0
      Userland/Libraries/LibWeb/HTML/NavigatorLanguage.h
  18. 7 0
      Userland/Libraries/LibWeb/HTML/NavigatorLanguage.idl
  19. 18 0
      Userland/Libraries/LibWeb/HTML/NavigatorOnLine.h
  20. 4 0
      Userland/Libraries/LibWeb/HTML/NavigatorOnLine.idl
  21. 5 4
      Userland/Libraries/LibWeb/HTML/Window.cpp
  22. 1 0
      Userland/Libraries/LibWeb/HTML/Window.h
  23. 0 1
      Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.h
  24. 2 1
      Userland/Libraries/LibWeb/HTML/WorkerNavigator.cpp
  25. 9 2
      Userland/Libraries/LibWeb/HTML/WorkerNavigator.h
  26. 10 5
      Userland/Libraries/LibWeb/HTML/WorkerNavigator.idl
  27. 1 0
      Userland/Libraries/LibWeb/idl_files.cmake

+ 2 - 5
Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp

@@ -69,14 +69,12 @@ static ErrorOr<void> generate_exposed_interface_implementation(StringView class_
 )~~~");
     }
 
-    // FIXME: Special case window. We should convert Window, Location, and Navigator to use IDL
+    // FIXME: Special case window. We should convert Window and Location to use IDL
     if (class_name == "Window"sv) {
         generator.append(R"~~~(#include <LibWeb/Bindings/WindowConstructor.h>
 #include <LibWeb/Bindings/WindowPrototype.h>
 #include <LibWeb/Bindings/LocationConstructor.h>
 #include <LibWeb/Bindings/LocationPrototype.h>
-#include <LibWeb/Bindings/NavigatorConstructor.h>
-#include <LibWeb/Bindings/NavigatorPrototype.h>
 )~~~");
     }
 
@@ -108,12 +106,11 @@ void add_@global_object_snake_name@_exposed_interfaces(JS::Object& global, JS::R
         add_interface(gen, interface.name, interface.prototype_class, interface.constructor_class);
     }
 
-    // FIXME: Special case window. We should convert Window, Location, and Navigator to use IDL
+    // FIXME: Special case window. We should convert Window and Location to use IDL
     if (class_name == "Window"sv) {
         auto gen = generator.fork();
         add_interface(gen, "Window"sv, "WindowPrototype"sv, "WindowConstructor"sv);
         add_interface(gen, "Location"sv, "LocationPrototype"sv, "LocationConstructor"sv);
-        add_interface(gen, "Navigator"sv, "NavigatorPrototype"sv, "NavigatorConstructor"sv);
     }
 
     generator.append(R"~~~(

+ 0 - 39
Userland/Libraries/LibWeb/Bindings/NavigatorConstructor.cpp

@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2022, the SerenityOS developers.
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <LibWeb/Bindings/Intrinsics.h>
-#include <LibWeb/Bindings/NavigatorConstructor.h>
-#include <LibWeb/Bindings/NavigatorPrototype.h>
-
-namespace Web::Bindings {
-
-NavigatorConstructor::NavigatorConstructor(JS::Realm& realm)
-    : NativeFunction(*realm.intrinsics().function_prototype())
-{
-}
-
-NavigatorConstructor::~NavigatorConstructor() = default;
-
-JS::ThrowCompletionOr<JS::Value> NavigatorConstructor::call()
-{
-    return vm().throw_completion<JS::TypeError>(JS::ErrorType::ConstructorWithoutNew, "Navigator");
-}
-
-JS::ThrowCompletionOr<JS::Object*> NavigatorConstructor::construct(FunctionObject&)
-{
-    return vm().throw_completion<JS::TypeError>(JS::ErrorType::NotAConstructor, "Navigator");
-}
-
-void NavigatorConstructor::initialize(JS::Realm& realm)
-{
-    auto& vm = this->vm();
-
-    NativeFunction::initialize(realm);
-    define_direct_property(vm.names.prototype, &cached_web_prototype(realm, "Navigator"), 0);
-    define_direct_property(vm.names.length, JS::Value(0), JS::Attribute::Configurable);
-}
-
-}

+ 0 - 28
Userland/Libraries/LibWeb/Bindings/NavigatorConstructor.h

@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2022, the SerenityOS developers.
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#pragma once
-
-#include <LibJS/Runtime/NativeFunction.h>
-
-namespace Web::Bindings {
-
-class NavigatorConstructor : public JS::NativeFunction {
-    JS_OBJECT(NavigatorConstructor, JS::NativeFunction);
-
-public:
-    explicit NavigatorConstructor(JS::Realm&);
-    virtual void initialize(JS::Realm&) override;
-    virtual ~NavigatorConstructor() override;
-
-    virtual JS::ThrowCompletionOr<JS::Value> call() override;
-    virtual JS::ThrowCompletionOr<JS::Object*> construct(JS::FunctionObject& new_target) override;
-
-private:
-    virtual bool has_constructor() const override { return true; }
-};
-
-}

+ 0 - 66
Userland/Libraries/LibWeb/Bindings/NavigatorObject.cpp

@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <LibJS/Runtime/Array.h>
-#include <LibWeb/Bindings/Intrinsics.h>
-#include <LibWeb/Bindings/NavigatorObject.h>
-#include <LibWeb/Bindings/NavigatorPrototype.h>
-#include <LibWeb/Loader/ResourceLoader.h>
-
-namespace Web {
-namespace Bindings {
-
-NavigatorObject::NavigatorObject(JS::Realm& realm)
-    : Object(cached_web_prototype(realm, "Navigator"))
-{
-}
-
-void NavigatorObject::initialize(JS::Realm& realm)
-{
-    auto& heap = this->heap();
-    auto* languages = MUST(JS::Array::create(realm, 0));
-    languages->indexed_properties().append(js_string(heap, "en-US"));
-
-    // FIXME: All of these should be in Navigator's prototype and be native accessors
-    u8 attr = JS::Attribute::Configurable | JS::Attribute::Writable | JS::Attribute::Enumerable;
-    define_direct_property("appCodeName", js_string(heap, "Mozilla"), attr);
-    define_direct_property("appName", js_string(heap, "Netscape"), attr);
-    define_direct_property("appVersion", js_string(heap, "4.0"), attr);
-    define_direct_property("language", languages->get_without_side_effects(0), attr);
-    define_direct_property("languages", languages, attr);
-    define_direct_property("platform", js_string(heap, "SerenityOS"), attr);
-    define_direct_property("product", js_string(heap, "Gecko"), attr);
-
-    define_native_accessor(realm, "userAgent", user_agent_getter, {}, JS::Attribute::Configurable | JS::Attribute::Enumerable);
-    define_native_accessor(realm, "cookieEnabled", cookie_enabled_getter, {}, JS::Attribute::Configurable | JS::Attribute::Enumerable);
-
-    define_native_function(realm, "javaEnabled", java_enabled, 0, JS::Attribute::Configurable | JS::Attribute::Enumerable);
-
-    // FIXME: Reflect actual connectivity status.
-    define_direct_property("onLine", JS::Value(true), attr);
-}
-
-JS_DEFINE_NATIVE_FUNCTION(NavigatorObject::user_agent_getter)
-{
-    return JS::js_string(vm, ResourceLoader::the().user_agent());
-}
-
-JS_DEFINE_NATIVE_FUNCTION(NavigatorObject::cookie_enabled_getter)
-{
-    // No way of disabling cookies right now :^)
-    return JS::Value(true);
-}
-
-// https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-javaenabled
-JS_DEFINE_NATIVE_FUNCTION(NavigatorObject::java_enabled)
-{
-    // The NavigatorPlugins mixin's javaEnabled() method steps are to return false.
-    return JS::Value(false);
-}
-
-}
-
-}

+ 0 - 30
Userland/Libraries/LibWeb/Bindings/NavigatorObject.h

@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#pragma once
-
-#include <LibJS/Runtime/Object.h>
-#include <LibWeb/Forward.h>
-
-namespace Web {
-namespace Bindings {
-
-class NavigatorObject final : public JS::Object {
-    JS_OBJECT(NavigatorObject, JS::Object);
-
-public:
-    NavigatorObject(JS::Realm&);
-    virtual void initialize(JS::Realm&) override;
-    virtual ~NavigatorObject() override = default;
-
-private:
-    JS_DECLARE_NATIVE_FUNCTION(user_agent_getter);
-    JS_DECLARE_NATIVE_FUNCTION(cookie_enabled_getter);
-    JS_DECLARE_NATIVE_FUNCTION(java_enabled);
-};
-
-}
-}

+ 0 - 25
Userland/Libraries/LibWeb/Bindings/NavigatorPrototype.h

@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2022, the SerenityOS developers.
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#pragma once
-
-#include <LibJS/Runtime/Object.h>
-#include <LibJS/Runtime/VM.h>
-#include <LibWeb/Forward.h>
-
-namespace Web::Bindings {
-
-class NavigatorPrototype final : public JS::Object {
-    JS_OBJECT(NavigatorPrototype, JS::Object);
-
-public:
-    explicit NavigatorPrototype(JS::Realm& realm)
-        : JS::Object(*realm.intrinsics().object_prototype())
-    {
-    }
-};
-
-}

+ 2 - 2
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -10,8 +10,6 @@ set(SOURCES
     Bindings/LocationConstructor.cpp
     Bindings/LocationObject.cpp
     Bindings/MainThreadVM.cpp
-    Bindings/NavigatorConstructor.cpp
-    Bindings/NavigatorObject.cpp
     Bindings/OptionConstructor.cpp
     Bindings/PlatformObject.cpp
     Bindings/WindowConstructor.cpp
@@ -245,6 +243,8 @@ set(SOURCES
     HTML/MessageChannel.cpp
     HTML/MessageEvent.cpp
     HTML/MessagePort.cpp
+    HTML/Navigator.cpp
+    HTML/NavigatorID.cpp
     HTML/PageTransitionEvent.cpp
     HTML/Parser/Entities.cpp
     HTML/Parser/HTMLEncodingDetection.cpp

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

@@ -304,6 +304,7 @@ class MessageChannel;
 class MessageEvent;
 class MessagePort;
 struct NavigationParams;
+class Navigator;
 class Origin;
 class PageTransitionEvent;
 class Path2D;

+ 27 - 0
Userland/Libraries/LibWeb/HTML/Navigator.cpp

@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Realm.h>
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/HTML/Navigator.h>
+
+namespace Web::HTML {
+
+JS::NonnullGCPtr<Navigator> Navigator::create(JS::Realm& realm)
+{
+    return *realm.heap().allocate<Navigator>(realm, realm);
+}
+
+Navigator::Navigator(JS::Realm& realm)
+    : PlatformObject(realm)
+{
+    set_prototype(&Bindings::cached_web_prototype(realm, "Navigator"));
+}
+
+Navigator::~Navigator() = default;
+
+}

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

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/HTML/NavigatorConcurrentHardware.h>
+#include <LibWeb/HTML/NavigatorID.h>
+#include <LibWeb/HTML/NavigatorLanguage.h>
+#include <LibWeb/HTML/NavigatorOnLine.h>
+
+namespace Web::HTML {
+
+class Navigator : public Bindings::PlatformObject
+    , public NavigatorConcurrentHardwareMixin
+    , public NavigatorIDMixin
+    , public NavigatorLanguageMixin
+    , public NavigatorOnLineMixin {
+    WEB_PLATFORM_OBJECT(Navigator, Bindings::PlatformObject);
+
+public:
+    static JS::NonnullGCPtr<Navigator> create(JS::Realm&);
+
+    // FIXME: Implement NavigatorContentUtilsMixin
+
+    // NavigatorCookies
+    // FIXME: Hook up to Agent level state
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-cookieenabled
+    bool cookie_enabled() const { return true; }
+
+    // NavigatorPlugins
+    // FIXME: Actually support pdf viewing
+    // 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 { return false; }
+
+    virtual ~Navigator() override;
+
+private:
+    explicit Navigator(JS::Realm&);
+};
+
+}

+ 41 - 0
Userland/Libraries/LibWeb/HTML/Navigator.idl

@@ -0,0 +1,41 @@
+#import <HTML/NavigatorID.idl>
+#import <HTML/NavigatorLanguage.idl>
+#import <HTML/NavigatorOnLine.idl>
+#import <HTML/NavigatorConcurrentHardware.idl>
+
+// https://html.spec.whatwg.org/multipage/system-state.html#navigator
+[Exposed=Window]
+interface Navigator {
+  // objects implementing this interface also implement the interfaces given below
+};
+
+// NOTE: As NavigatorContentUtils, NavigatorCookies, and NavigatorPlugins are not used in WorkerNavigator,
+//       we define them here.
+
+// https://html.spec.whatwg.org/multipage/system-state.html#navigatorcontentutils
+interface mixin NavigatorContentUtils {
+  // FIXME: [SecureContext] undefined registerProtocolHandler(DOMString scheme, USVString url);
+  // FIXME: [SecureContext] undefined unregisterProtocolHandler(DOMString scheme, USVString url);
+};
+
+// https://html.spec.whatwg.org/multipage/system-state.html#navigatorcookies
+interface mixin NavigatorCookies {
+  readonly attribute boolean cookieEnabled;
+};
+
+// 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;
+  boolean javaEnabled();
+  readonly attribute boolean pdfViewerEnabled;
+};
+
+Navigator includes NavigatorID;
+Navigator includes NavigatorLanguage;
+Navigator includes NavigatorOnLine;
+Navigator includes NavigatorContentUtils;
+Navigator includes NavigatorCookies;
+Navigator includes NavigatorPlugins;
+Navigator includes NavigatorConcurrentHardware;
+

+ 17 - 0
Userland/Libraries/LibWeb/HTML/NavigatorConcurrentHardware.h

@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+namespace Web::HTML {
+
+class NavigatorConcurrentHardwareMixin {
+public:
+    // https://html.spec.whatwg.org/multipage/workers.html#dom-navigator-hardwareconcurrency
+    unsigned long long hardware_concurrency() { return 1; }
+};
+
+}

+ 4 - 0
Userland/Libraries/LibWeb/HTML/NavigatorConcurrentHardware.idl

@@ -0,0 +1,4 @@
+// https://html.spec.whatwg.org/multipage/workers.html#navigatorconcurrenthardware
+interface mixin NavigatorConcurrentHardware {
+  readonly attribute unsigned long long hardwareConcurrency;
+};

+ 53 - 0
Userland/Libraries/LibWeb/HTML/NavigatorID.cpp

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/String.h>
+#include <LibWeb/HTML/NavigatorID.h>
+#include <LibWeb/Loader/ResourceLoader.h>
+
+namespace Web::HTML {
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-appversion
+String NavigatorIDMixin::app_version() const
+{
+    // Must return the appropriate string that starts with "5.0 (", as follows:
+
+    // Let trail be the substring of default `User-Agent` value that follows the "Mozilla/" prefix.
+    auto user_agent_string = ResourceLoader::the().user_agent();
+
+    auto trail = user_agent_string.substring_view(strlen("Mozilla/"), user_agent_string.length() - strlen("Mozilla/"));
+
+    // If the navigator compatibility mode is Chrome or WebKit
+    // NOTE: We are using Chrome for now. Make sure to update all APIs if you add a toggle for this.
+
+    // Return trail.
+    return trail;
+
+    // If the navigator compatibility mode is Gecko
+    //    If trail starts with "5.0 (Windows", then return "5.0 (Windows)".
+    //    Otherwise, return the prefix of trail up to but not including the first U+003B (;), concatenated with the
+    //        character U+0029 RIGHT PARENTHESIS. For example, "5.0 (Macintosh)", "5.0 (Android 10)", or "5.0 (X11)".
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-platform
+String NavigatorIDMixin::platform() const
+{
+    // Must return a string representing the platform on which the browser is executing (e.g. "MacIntel", "Win32",
+    // "Linux x86_64", "Linux armv81") or, for privacy and compatibility, a string that is commonly returned on another
+    // platform.
+
+    // FIXME: Use some portion of the user agent string to make spoofing work 100%
+    return "SerenityOS";
+}
+
+// https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-useragent
+String NavigatorIDMixin::user_agent() const
+{
+    // Must return the default `User-Agent` value.
+    return ResourceLoader::the().user_agent();
+}
+
+}

+ 51 - 0
Userland/Libraries/LibWeb/HTML/NavigatorID.h

@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/String.h>
+
+namespace Web::HTML {
+
+class NavigatorIDMixin {
+public:
+    // WARNING: Any information in this API that varies from user to user can be used to profile the user. In fact, if
+    // enough such information is available, a user can actually be uniquely identified. For this reason, user agent
+    // implementers are strongly urged to include as little information in this API as possible.
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-appcodename
+    String app_code_name() const { return "Mozilla"sv; }
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-appcodename
+    String app_name() const { return "Netscape"sv; }
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-appversion
+    String app_version() const;
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-platform
+    String platform() const;
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-product
+    String product() const { return "Gecko"sv; }
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-productsub
+    String product_sub() const { return "20030107"sv; } // Compatability mode "Chrome"
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-useragent
+    String user_agent() const;
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-vendor
+    String vendor() const { return "Google Inc."sv; } // Compatability mode "Chrome"
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-vendorsub
+    String vendor_sub() const { return ""sv; }
+
+    // NOTE: If the navigator compatibility mode is Gecko, then the user agent must also support the following partial interface:
+    //       bool taint_enabled()
+    //       String oscpu()
+};
+
+}

+ 12 - 0
Userland/Libraries/LibWeb/HTML/NavigatorID.idl

@@ -0,0 +1,12 @@
+// https://html.spec.whatwg.org/multipage/system-state.html#navigatorid
+interface mixin NavigatorID {
+  readonly attribute DOMString appCodeName; // constant "Mozilla"
+  readonly attribute DOMString appName; // constant "Netscape"
+  readonly attribute DOMString appVersion;
+  readonly attribute DOMString platform;
+  readonly attribute DOMString product; // constant "Gecko"
+  [Exposed=Window] readonly attribute DOMString productSub;
+  readonly attribute DOMString userAgent;
+  [Exposed=Window] readonly attribute DOMString vendor;
+  [Exposed=Window] readonly attribute DOMString vendorSub; // constant ""
+};

+ 28 - 0
Userland/Libraries/LibWeb/HTML/NavigatorLanguage.h

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/String.h>
+#include <AK/Vector.h>
+
+namespace Web::HTML {
+
+class NavigatorLanguageMixin {
+public:
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-language
+    String language() const { return m_current_preferred_languages[0]; }
+
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-languages
+    // FIXME: The same object must be returned until the user agent needs to return different values, or values in a
+    //        different order.
+    Vector<String> languages() const { return m_current_preferred_languages; }
+
+protected:
+    Vector<String> m_current_preferred_languages = { "en-US" };
+};
+
+}

+ 7 - 0
Userland/Libraries/LibWeb/HTML/NavigatorLanguage.idl

@@ -0,0 +1,7 @@
+// https://html.spec.whatwg.org/multipage/system-state.html#navigatorlanguage
+interface mixin NavigatorLanguage {
+  readonly attribute DOMString language;
+  // FIXME: readonly attribute FrozenArray<DOMString> languages;
+  //        This is supposed to be a FrozenArray that always returns the same object
+  sequence<DOMString> languages();
+};

+ 18 - 0
Userland/Libraries/LibWeb/HTML/NavigatorOnLine.h

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+namespace Web::HTML {
+
+class NavigatorOnLineMixin {
+public:
+    // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-online
+    // FIXME: Reflect actual connectivity status.
+    bool on_line() const { return true; }
+};
+
+}

+ 4 - 0
Userland/Libraries/LibWeb/HTML/NavigatorOnLine.idl

@@ -0,0 +1,4 @@
+// https://html.spec.whatwg.org/multipage/system-state.html#navigatoronline
+interface mixin NavigatorOnLine {
+  readonly attribute boolean onLine;
+};

+ 5 - 4
Userland/Libraries/LibWeb/HTML/Window.cpp

@@ -17,7 +17,6 @@
 #include <LibWeb/Bindings/CSSNamespace.h>
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
 #include <LibWeb/Bindings/LocationObject.h>
-#include <LibWeb/Bindings/NavigatorObject.h>
 #include <LibWeb/Bindings/Replaceable.h>
 #include <LibWeb/Bindings/WindowExposedInterfaces.h>
 #include <LibWeb/Bindings/WindowPrototype.h>
@@ -33,6 +32,7 @@
 #include <LibWeb/HTML/EventHandler.h>
 #include <LibWeb/HTML/EventLoop/EventLoop.h>
 #include <LibWeb/HTML/MessageEvent.h>
+#include <LibWeb/HTML/Navigator.h>
 #include <LibWeb/HTML/Origin.h>
 #include <LibWeb/HTML/PageTransitionEvent.h>
 #include <LibWeb/HTML/Scripting/ClassicScript.h>
@@ -95,6 +95,7 @@ void Window::visit_edges(JS::Cell::Visitor& visitor)
     visitor.visit(m_screen.ptr());
     visitor.visit(m_location_object);
     visitor.visit(m_crypto);
+    visitor.visit(m_navigator);
     for (auto& it : m_timers)
         visitor.visit(it.value.ptr());
 }
@@ -813,9 +814,9 @@ void Window::initialize_web_interfaces(Badge<WindowEnvironmentSettingsObject>)
 
     m_location_object = heap().allocate<Bindings::LocationObject>(realm, realm);
 
-    auto* m_navigator_object = heap().allocate<Bindings::NavigatorObject>(realm, realm);
-    define_direct_property("navigator", m_navigator_object, JS::Attribute::Enumerable | JS::Attribute::Configurable);
-    define_direct_property("clientInformation", m_navigator_object, JS::Attribute::Enumerable | JS::Attribute::Configurable);
+    m_navigator = heap().allocate<HTML::Navigator>(realm, realm);
+    define_direct_property("navigator", m_navigator, JS::Attribute::Enumerable | JS::Attribute::Configurable);
+    define_direct_property("clientInformation", m_navigator, JS::Attribute::Enumerable | JS::Attribute::Configurable);
 
     // NOTE: location is marked as [LegacyUnforgeable], meaning it isn't configurable.
     define_native_accessor(realm, "location", location_getter, location_setter, JS::Attribute::Enumerable);

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

@@ -157,6 +157,7 @@ private:
     JS::GCPtr<HighResolutionTime::Performance> m_performance;
     JS::GCPtr<Crypto::Crypto> m_crypto;
     JS::GCPtr<CSS::Screen> m_screen;
+    JS::GCPtr<HTML::Navigator> m_navigator;
 
     AnimationFrameCallbackDriver m_animation_frame_callback_driver;
 

+ 0 - 1
Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.h

@@ -79,7 +79,6 @@ private:
 
     JS::GCPtr<WorkerLocation> m_location;
 
-    // FIXME: Implement WorkerNavigator according to the spec
     JS::GCPtr<WorkerNavigator> m_navigator;
 
     // FIXME: Add all these internal slots

+ 2 - 1
Userland/Libraries/LibWeb/HTML/WorkerNavigator.cpp

@@ -5,6 +5,7 @@
  */
 
 #include <LibJS/Heap/Heap.h>
+#include <LibWeb/Bindings/Intrinsics.h>
 #include <LibWeb/HTML/WorkerGlobalScope.h>
 #include <LibWeb/HTML/WorkerNavigator.h>
 
@@ -18,7 +19,7 @@ JS::NonnullGCPtr<WorkerNavigator> WorkerNavigator::create(WorkerGlobalScope& glo
 WorkerNavigator::WorkerNavigator(WorkerGlobalScope& global_scope)
     : PlatformObject(global_scope.realm())
 {
-    // FIXME: Set prototype once we can get to worker scope prototypes.
+    set_prototype(&Bindings::cached_web_prototype(global_scope.realm(), "WorkerNavigator"));
 }
 
 WorkerNavigator::~WorkerNavigator() = default;

+ 9 - 2
Userland/Libraries/LibWeb/HTML/WorkerNavigator.h

@@ -7,11 +7,18 @@
 #pragma once
 
 #include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/HTML/NavigatorConcurrentHardware.h>
+#include <LibWeb/HTML/NavigatorID.h>
+#include <LibWeb/HTML/NavigatorLanguage.h>
+#include <LibWeb/HTML/NavigatorOnLine.h>
 
 namespace Web::HTML {
 
-// FIXME: Add Mixin APIs from https://html.spec.whatwg.org/multipage/workers.html#the-workernavigator-object
-class WorkerNavigator : public Bindings::PlatformObject {
+class WorkerNavigator : public Bindings::PlatformObject
+    , public NavigatorConcurrentHardwareMixin
+    , public NavigatorIDMixin
+    , public NavigatorLanguageMixin
+    , public NavigatorOnLineMixin {
     WEB_PLATFORM_OBJECT(WorkerNavigator, Bindings::PlatformObject);
 
 public:

+ 10 - 5
Userland/Libraries/LibWeb/HTML/WorkerNavigator.idl

@@ -1,8 +1,13 @@
+#import <HTML/NavigatorID.idl>
+#import <HTML/NavigatorLanguage.idl>
+#import <HTML/NavigatorOnLine.idl>
+#import <HTML/NavigatorConcurrentHardware.idl>
+
+// https://html.spec.whatwg.org/multipage/workers.html#workernavigator
 [Exposed=Worker]
 interface WorkerNavigator {};
 
-// FIXME: Add these mixins that are used to prevent duplication b/w WorkerNavigator and Navigator
-// WorkerNavigator includes NavigatorID;
-// WorkerNavigator includes NavigatorLanguage;
-// WorkerNavigator includes NavigatorOnLine;
-// WorkerNavigator includes NavigatorConcurrentHardware;
+WorkerNavigator includes NavigatorID;
+WorkerNavigator includes NavigatorLanguage;
+WorkerNavigator includes NavigatorOnLine;
+WorkerNavigator includes NavigatorConcurrentHardware;

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

@@ -146,6 +146,7 @@ libweb_js_bindings(HTML/ImageData)
 libweb_js_bindings(HTML/MessageChannel)
 libweb_js_bindings(HTML/MessageEvent)
 libweb_js_bindings(HTML/MessagePort)
+libweb_js_bindings(HTML/Navigator)
 libweb_js_bindings(HTML/PageTransitionEvent)
 libweb_js_bindings(HTML/Path2D)
 libweb_js_bindings(HTML/PromiseRejectionEvent)