Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Libraries/LibWeb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ set(SOURCES
HTML/NavigationParams.cpp
HTML/NavigationTransition.cpp
HTML/Navigator.cpp
HTML/NavigatorBadge.cpp
HTML/NavigatorBeacon.cpp
HTML/NavigatorConcurrentHardware.cpp
HTML/NavigatorDeviceMemory.cpp
Expand Down
6 changes: 6 additions & 0 deletions Libraries/LibWeb/DOM/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3282,6 +3282,12 @@ bool Document::is_fully_active() const
if (!navigable)
return false;

// When a navigable is being destroyed (e.g., iframe.remove()), the has_been_destroyed flag is set immediately,
// but the navigable pointer is not nulled until later in a queued task. We must check the flag to ensure we
// correctly detect that the document is no longer fully active during this transitional period.
if (navigable->has_been_destroyed())
return false;

auto traversable = navigable->traversable_navigable();
if (navigable == traversable && traversable->is_top_level_traversable())
return true;
Expand Down
5 changes: 5 additions & 0 deletions Libraries/LibWeb/HTML/Navigator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ Navigator::Navigator(JS::Realm& realm)

Navigator::~Navigator() = default;

HTML::Window& Navigator::window()
{
return as<HTML::Window>(HTML::current_principal_global_object());
}

void Navigator::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(Navigator);
Expand Down
5 changes: 5 additions & 0 deletions Libraries/LibWeb/HTML/Navigator.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <LibWeb/GPC/GlobalPrivacyControl.h>
#include <LibWeb/Gamepad/NavigatorGamepad.h>
#include <LibWeb/HTML/MimeTypeArray.h>
#include <LibWeb/HTML/NavigatorBadge.h>
#include <LibWeb/HTML/NavigatorBeacon.h>
#include <LibWeb/HTML/NavigatorConcurrentHardware.h>
#include <LibWeb/HTML/NavigatorDeviceMemory.h>
Expand All @@ -27,6 +28,7 @@ namespace Web::HTML {

class Navigator
: public Bindings::PlatformObject
, public NavigatorBadgeMixin
, public NavigatorBeaconPartial
, public NavigatorConcurrentHardwareMixin
, public NavigatorDeviceMemoryMixin
Expand Down Expand Up @@ -78,6 +80,9 @@ class Navigator
protected:
virtual void visit_edges(Cell::Visitor&) override;

// ^NavigatorBadgeMixin
virtual HTML::Window& window() override;

private:
explicit Navigator(JS::Realm&);

Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/HTML/Navigator.idl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#import <Geolocation/Geolocation.idl>
#import <GPC/GlobalPrivacyControl.idl>
#import <HTML/MimeTypeArray.idl>
#import <HTML/NavigatorBadge.idl>
#import <HTML/NavigatorBeacon.idl>
#import <HTML/NavigatorConcurrentHardware.idl>
#import <HTML/NavigatorDeviceMemory.idl>
Expand Down Expand Up @@ -89,3 +90,4 @@ Navigator includes NavigatorConcurrentHardware;
Navigator includes NavigatorAutomationInformation;
Navigator includes NavigatorStorage;
Navigator includes NavigatorDeviceMemory;
Navigator includes NavigatorBadge;
74 changes: 74 additions & 0 deletions Libraries/LibWeb/HTML/NavigatorBadge.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2025, Estefania Sanchez <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/Navigator.h>
#include <LibWeb/HTML/NavigatorBadge.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/Promise.h>

namespace Web::HTML {

// https://w3c.github.io/badging/#setting-the-application-badge
GC::Ref<WebIDL::Promise> NavigatorBadgeMixin::set_app_badge(Optional<u64> contents)
{
// 1. Let global be context's relevant global object.
auto& window_object = window();
auto& realm = window_object.realm();

// 2. If global is a Window object, then:
// 2-1. Let document be global's associated Document.
auto& document = window_object.associated_document();

// 2-2. If document is not fully active, return a promise rejected with a "InvalidStateError" DOMException.
if (!document.is_fully_active()) {
auto exception = WebIDL::InvalidStateError::create(realm, "Document is not fully active"_utf16);
return WebIDL::create_rejected_promise(realm, exception);
}

// 2-3. If document's relevant settings object's origin is not same origin-domain with this's relevant settings
// object's top-level origin, return a promise rejected with a "SecurityError" DOMException.
auto const document_origin = document.relevant_settings_object().origin();
auto navigator = window_object.navigator();
auto& this_settings = HTML::relevant_settings_object(*navigator);
if (this_settings.top_level_origin.has_value() && !document_origin.is_same_origin_domain(this_settings.top_level_origin.value())) {
auto exception = WebIDL::SecurityError::create(realm, "Document's origin is not same origin-domain with top-level origin"_utf16);
return WebIDL::create_rejected_promise(realm, exception);
}

// 3. Let promise be a new promise.
auto promise = WebIDL::create_promise(realm);

// FIXME: 4. In parallel:
// FIXME: 4-1. If the user agent requires express permission to set the application badge, then:
// FIXME: 4-1-1. Let permissionState be the result of getting the current permission state with "notifications".
// FIXME: 4-1-2. If permissionState is not "granted", queue a global task on the user interaction task source given
// global to reject promise with a NotAllowedError and terminate this algorithm.

// FIXME: 4-2. Switching on contents, if it happens to be the case that:
// contents was not passed: Set badge to "flag".
// contents is 0: Set badge to "nothing".
// contents: Set badge to contents.
(void)contents;

// FIXME: 4-3. Queue a global task on the DOM manipulation task source given global to resolve promise with undefined.
WebIDL::resolve_promise(realm, promise, JS::js_undefined());

// 5. Return promise.
return promise;
}

// https://w3c.github.io/badging/#clearappbadge-method
GC::Ref<WebIDL::Promise> NavigatorBadgeMixin::clear_app_badge()
{
// When the clearAppBadge() method is called, the user agent MUST set the application badge of this to 0.
return set_app_badge(0);
}

}
26 changes: 26 additions & 0 deletions Libraries/LibWeb/HTML/NavigatorBadge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2025, Estefania Sanchez <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <AK/Optional.h>
#include <LibGC/Ptr.h>
#include <LibWeb/Forward.h>

namespace Web::HTML {

class NavigatorBadgeMixin {
public:
virtual ~NavigatorBadgeMixin() = default;

GC::Ref<WebIDL::Promise> set_app_badge(Optional<u64> contents);
GC::Ref<WebIDL::Promise> clear_app_badge();

protected:
virtual HTML::Window& window() = 0;
};

}
6 changes: 6 additions & 0 deletions Libraries/LibWeb/HTML/NavigatorBadge.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// https://w3c.github.io/badging
[SecureContext]
interface mixin NavigatorBadge {
Promise<undefined> setAppBadge(optional [EnforceRange] unsigned long long contents);
Promise<undefined> clearAppBadge();
};
1 change: 1 addition & 0 deletions Libraries/LibWeb/HTML/WorkerNavigator.idl
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ WorkerNavigator includes NavigatorOnLine;
WorkerNavigator includes NavigatorConcurrentHardware;
WorkerNavigator includes NavigatorStorage;
WorkerNavigator includes NavigatorDeviceMemory;
// FIXME: WorkerNavigator includes NavigatorBadge; https://w3c.github.io/badging/
Loading