diff --git a/components/config/prefs.rs b/components/config/prefs.rs index 3bc0b30a3b7..a6dffb72b43 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -89,6 +89,7 @@ pub struct Preferences { pub dom_microdata_testing_enabled: bool, pub dom_mouse_event_which_enabled: bool, pub dom_mutation_observer_enabled: bool, + pub dom_notification_enabled: bool, pub dom_offscreen_canvas_enabled: bool, pub dom_permissions_enabled: bool, pub dom_permissions_testing_allowed_in_nonsecure_contexts: bool, @@ -252,6 +253,7 @@ impl Preferences { dom_microdata_testing_enabled: false, dom_mouse_event_which_enabled: false, dom_mutation_observer_enabled: true, + dom_notification_enabled: false, dom_offscreen_canvas_enabled: false, dom_permissions_enabled: false, dom_permissions_testing_allowed_in_nonsecure_contexts: false, diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 11d38cf29c7..4ba5bc63b6d 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -80,6 +80,7 @@ use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{ ImageBitmapOptions, ImageBitmapSource, }; use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods; +use crate::dom::bindings::codegen::Bindings::NotificationBinding::NotificationPermissionCallback; use crate::dom::bindings::codegen::Bindings::PerformanceBinding::Performance_Binding::PerformanceMethods; use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{ PermissionName, PermissionState, @@ -370,6 +371,10 @@ pub(crate) struct GlobalScope { /// #[ignore_malloc_size_of = "Rc is hard"] count_queuing_strategy_size_function: OnceCell>, + + #[ignore_malloc_size_of = "Rc is hard"] + notification_permission_request_callback_map: + DomRefCell>>, } /// A wrapper for glue-code between the ipc router and the event-loop. @@ -763,6 +768,7 @@ impl GlobalScope { unminified_js_dir: unminify_js.then(|| unminified_path("unminified-js")), byte_length_queuing_strategy_size_function: OnceCell::new(), count_queuing_strategy_size_function: OnceCell::new(), + notification_permission_request_callback_map: Default::default(), } } @@ -3268,6 +3274,25 @@ impl GlobalScope { pub(crate) fn get_count_queuing_strategy_size(&self) -> Option> { self.count_queuing_strategy_size_function.get().cloned() } + + pub(crate) fn add_notification_permission_request_callback( + &self, + callback_id: String, + callback: Rc, + ) { + self.notification_permission_request_callback_map + .borrow_mut() + .insert(callback_id, callback.clone()); + } + + pub(crate) fn remove_notification_permission_request_callback( + &self, + callback_id: String, + ) -> Option> { + self.notification_permission_request_callback_map + .borrow_mut() + .remove(&callback_id) + } } /// Returns the Rust global scope from a JS global object. diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index c2049aab55c..b0ea5d3aeb6 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -454,6 +454,7 @@ pub(crate) mod node; pub(crate) mod nodeiterator; #[allow(dead_code)] pub(crate) mod nodelist; +pub(crate) mod notification; pub(crate) mod offlineaudiocompletionevent; pub(crate) mod offlineaudiocontext; pub(crate) mod offscreencanvas; diff --git a/components/script/dom/notification.rs b/components/script/dom/notification.rs new file mode 100644 index 00000000000..49bbd4b74d6 --- /dev/null +++ b/components/script/dom/notification.rs @@ -0,0 +1,562 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::time::{SystemTime, UNIX_EPOCH}; + +use dom_struct::dom_struct; +use js::jsapi::Heap; +use js::jsval::JSVal; +use js::rust::{HandleObject, MutableHandleValue}; +use servo_url::{ImmutableOrigin, ServoUrl}; +use uuid::Uuid; + +use super::bindings::refcounted::TrustedPromise; +use super::bindings::reflector::DomGlobal; +use super::permissionstatus::PermissionStatus; +use crate::dom::bindings::callback::ExceptionHandling; +use crate::dom::bindings::codegen::Bindings::NotificationBinding::{ + NotificationAction, NotificationDirection, NotificationMethods, NotificationOptions, + NotificationPermission, NotificationPermissionCallback, +}; +use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionStatus_Binding::PermissionStatusMethods; +use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{ + PermissionDescriptor, PermissionName, PermissionState, +}; +use crate::dom::bindings::codegen::UnionTypes::UnsignedLongOrUnsignedLongSequence; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::import::module::{Fallible, Rc}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::reflect_dom_object_with_proto; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::{DOMString, USVString}; +use crate::dom::bindings::trace::RootedTraceableBox; +use crate::dom::bindings::utils::to_frozen_array; +use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; +use crate::dom::permissions::{descriptor_permission_state, PermissionAlgorithm, Permissions}; +use crate::dom::promise::Promise; +use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; +use crate::dom::serviceworkerregistration::ServiceWorkerRegistration; +use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; + +// TODO: Service Worker API (persistent notification) +// https://notifications.spec.whatwg.org/#service-worker-api + +/// +#[dom_struct] +pub(crate) struct Notification { + eventtarget: EventTarget, + /// + serviceworker_registration: Option>, + /// + title: DOMString, + /// + body: DOMString, + /// + #[ignore_malloc_size_of = "mozjs"] + data: Heap, + /// + dir: NotificationDirection, + /// + image: Option, + /// + icon: Option, + /// + badge: Option, + /// + lang: DOMString, + /// + silent: Option, + /// + tag: DOMString, + /// + #[no_trace] // ImmutableOrigin is not traceable + origin: ImmutableOrigin, + /// + vibration_pattern: Vec, + /// + timestamp: u64, + /// + renotify: bool, + /// + require_interaction: bool, + /// + actions: Vec, + // TODO: image resource, icon resource, and badge resource +} + +impl Notification { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new( + global: &GlobalScope, + title: DOMString, + options: RootedTraceableBox, + origin: ImmutableOrigin, + base_url: ServoUrl, + fallback_timestamp: u64, + proto: Option, + can_gc: CanGc, + ) -> DomRoot { + let notification = reflect_dom_object_with_proto( + Box::new(Notification::new_inherited( + global, + title, + &options, + origin, + base_url, + fallback_timestamp, + )), + global, + proto, + can_gc, + ); + + notification.data.set(options.data.get()); + + notification + } + + /// partial implementation of + fn new_inherited( + global: &GlobalScope, + title: DOMString, + options: &RootedTraceableBox, + origin: ImmutableOrigin, + base_url: ServoUrl, + fallback_timestamp: u64, + ) -> Self { + // TODO: missing call to https://html.spec.whatwg.org/multipage/#structuredserializeforstorage + // may be find in `dom/bindings/structuredclone.rs` + let data = Heap::default(); + + let title = title.clone(); + let dir = options.dir; + let lang = options.lang.clone(); + let body = options.body.clone(); + let tag = options.tag.clone(); + + // If options["image"] exists, then parse it using baseURL, and if that does not return failure, + // set notification’s image URL to the return value. (Otherwise notification’s image URL is not set.) + let image = options.image.as_ref().and_then(|image_url| { + ServoUrl::parse_with_base(Some(&base_url), image_url.as_ref()) + .map(|url| USVString::from(url.to_string())) + .ok() + }); + // If options["icon"] exists, then parse it using baseURL, and if that does not return failure, + // set notification’s icon URL to the return value. (Otherwise notification’s icon URL is not set.) + let icon = options.icon.as_ref().and_then(|icon_url| { + ServoUrl::parse_with_base(Some(&base_url), icon_url.as_ref()) + .map(|url| USVString::from(url.to_string())) + .ok() + }); + // If options["badge"] exists, then parse it using baseURL, and if that does not return failure, + // set notification’s badge URL to the return value. (Otherwise notification’s badge URL is not set.) + let badge = options.badge.as_ref().and_then(|badge_url| { + ServoUrl::parse_with_base(Some(&base_url), badge_url.as_ref()) + .map(|url| USVString::from(url.to_string())) + .ok() + }); + // If options["vibrate"] exists, then validate and normalize it and + // set notification’s vibration pattern to the return value. + let vibration_pattern = match &options.vibrate { + Some(pattern) => validate_and_normalize_vibration_pattern(pattern), + None => Vec::new(), + }; + // If options["timestamp"] exists, then set notification’s timestamp to the value. + // Otherwise, set notification’s timestamp to fallbackTimestamp. + let timestamp = options.timestamp.unwrap_or(fallback_timestamp); + let renotify = options.renotify; + let silent = options.silent; + let require_interaction = options.requireInteraction; + + // For each entry in options["actions"] + // up to the maximum number of actions supported (skip any excess entries): + let mut actions: Vec = Vec::new(); + let max_actions = Notification::MaxActions(global); + for action in options.actions.iter().take(max_actions as usize) { + actions.push(Action { + name: action.action.clone(), + title: action.title.clone(), + // If entry["icon"] exists, then parse it using baseURL, and if that does not return failure + // set action’s icon URL to the return value. (Otherwise action’s icon URL remains null.) + icon_url: action.icon.as_ref().and_then(|icon_url| { + ServoUrl::parse_with_base(Some(&base_url), icon_url.as_ref()) + .map(|url| USVString::from(url.to_string())) + .ok() + }), + }); + } + + Self { + eventtarget: EventTarget::new_inherited(), + // A non-persistent notification is a notification whose service worker registration is null. + serviceworker_registration: None, + title, + body, + data, + dir, + image, + icon, + badge, + lang, + silent, + origin, + vibration_pattern, + timestamp, + renotify, + tag, + require_interaction, + actions, + } + } +} + +impl NotificationMethods for Notification { + /// + fn Constructor( + global: &GlobalScope, + proto: Option, + can_gc: CanGc, + title: DOMString, + options: RootedTraceableBox, + ) -> Fallible> { + // step 1: Check global is a ServiceWorkerGlobalScope + if global.is::() { + return Err(Error::Type( + "Notification constructor cannot be used in service worker.".to_string(), + )); + } + + // step 2: Check options.actions must be empty + if !options.actions.is_empty() { + return Err(Error::Type( + "Actions are only supported for persistent notifications.".to_string(), + )); + } + + // step 3: Create a notification with a settings object + let notification = + create_notification_with_settings_object(global, title, options, proto, can_gc)?; + + // TODO: Run step 5.1, 5.2 in parallel + // step 5.1: If the result of getting the notifications permission state is not "granted", + // then queue a task to fire an event named error on this, and abort these steps. + let permission_state = get_notifications_permission_state(global); + if permission_state != NotificationPermission::Granted { + global + .task_manager() + .dom_manipulation_task_source() + .queue_simple_event(notification.upcast(), atom!("error")); + // TODO: abort steps + } + // TODO: step 5.2: Run the notification show steps for notification + // https://notifications.spec.whatwg.org/#notification-show-steps + + Ok(notification) + } + + /// + fn GetPermission(global: &GlobalScope) -> Fallible { + Ok(get_notifications_permission_state(global)) + } + + /// + fn RequestPermission( + global: &GlobalScope, + permission_callback: Option>, + can_gc: CanGc, + ) -> Rc { + // Step 2: Let promise be a new promise in this’s relevant Realm. + let promise = Promise::new(global, can_gc); + + // TODO: Step 3: Run these steps in parallel: + // Step 3.1: Let permissionState be the result of requesting permission to use "notifications". + let notification_permission = request_notification_permission(global); + + // Step 3.2: Queue a global task on the DOM manipulation task source given global to run these steps: + let trusted_promise = TrustedPromise::new(promise.clone()); + let uuid = Uuid::new_v4().simple().to_string(); + let uuid_ = uuid.clone(); + + if let Some(callback) = permission_callback { + global.add_notification_permission_request_callback(uuid.clone(), callback.clone()); + } + + global.task_manager().dom_manipulation_task_source().queue( + task!(request_permission: move || { + let promise = trusted_promise.root(); + let global = promise.global(); + + // Step 3.2.1: If deprecatedCallback is given, + // then invoke deprecatedCallback with « permissionState » and "report". + if let Some(callback) = global.remove_notification_permission_request_callback(uuid_) { + let _ = callback.Call__(notification_permission, ExceptionHandling::Report); + } + + // Step 3.2.2: Resolve promise with permissionState. + promise.resolve_native(¬ification_permission); + }), + ); + + promise + } + + // + event_handler!(click, GetOnclick, SetOnclick); + // + event_handler!(show, GetOnshow, SetOnshow); + // + event_handler!(error, GetOnerror, SetOnerror); + // + event_handler!(close, GetOnclose, SetOnclose); + + /// + fn MaxActions(_global: &GlobalScope) -> u32 { + // TODO: determine the maximum number of actions + 2 + } + /// + fn Title(&self) -> DOMString { + self.title.clone() + } + /// + fn Dir(&self) -> NotificationDirection { + self.dir + } + /// + fn Lang(&self) -> DOMString { + self.lang.clone() + } + /// + fn Body(&self) -> DOMString { + self.body.clone() + } + /// + fn Tag(&self) -> DOMString { + self.tag.clone() + } + /// + fn Image(&self) -> USVString { + // step 1: If there is no this’s notification’s image URL, then return the empty string. + // step 2: Return this’s notification’s image URL, serialized. + self.image.clone().unwrap_or_default() + } + /// + fn Icon(&self) -> USVString { + // step 1: If there is no this’s notification’s icon URL, then return the empty string. + // step 2: Return this’s notification’s icon URL, serialized. + self.icon.clone().unwrap_or_default() + } + /// + fn Badge(&self) -> USVString { + // step 1: If there is no this’s notification’s badge URL, then return the empty string. + // step 2: Return this’s notification’s badge URL, serialized. + self.badge.clone().unwrap_or_default() + } + /// + fn Renotify(&self) -> bool { + self.renotify + } + /// + fn GetSilent(&self) -> Option { + self.silent + } + /// + fn RequireInteraction(&self) -> bool { + self.require_interaction + } + /// + fn Data(&self, _cx: SafeJSContext, mut retval: MutableHandleValue) { + retval.set(self.data.get()); + } + /// + fn Actions(&self, cx: SafeJSContext, retval: MutableHandleValue) { + // step 1: Let frozenActions be an empty list of type NotificationAction. + let mut frozen_actions: Vec = Vec::new(); + + // step 2: For each entry of this’s notification’s actions + for action in self.actions.iter() { + let action = NotificationAction { + action: action.name.clone(), + title: action.title.clone(), + // If entry’s icon URL is non-null, + // then set action["icon"] to entry’s icon URL, icon_url, serialized. + icon: action.icon_url.clone(), + }; + + // TODO: step 2.5: Call Object.freeze on action, to prevent accidental mutation by scripts. + // step 2.6: Append action to frozenActions. + frozen_actions.push(action); + } + + // step 3: Return the result of create a frozen array from frozenActions. + to_frozen_array(frozen_actions.as_slice(), cx, retval); + } + /// + fn Vibrate(&self, cx: SafeJSContext, retval: MutableHandleValue) { + to_frozen_array(self.vibration_pattern.as_slice(), cx, retval); + } + /// + fn Timestamp(&self) -> u64 { + self.timestamp + } + /// + fn Close(&self) { + // TODO: If notification is a persistent notification and notification was closed by the end user + // then fire a service worker notification event named "notificationclose" given notification. + + // If notification is a non-persistent notification + // then queue a task to fire an event named close on the Notification object representing notification. + self.global() + .task_manager() + .dom_manipulation_task_source() + .queue_simple_event(self.upcast(), atom!("close")); + } +} + +/// +#[derive(JSTraceable, MallocSizeOf)] +struct Action { + /// + name: DOMString, + /// + title: DOMString, + /// + icon_url: Option, + // TODO: icon_resource +} + +/// +fn create_notification_with_settings_object( + global: &GlobalScope, + title: DOMString, + options: RootedTraceableBox, + proto: Option, + can_gc: CanGc, +) -> Fallible> { + // step 1: Let origin be settings’s origin. + let origin = global.origin().immutable().clone(); + // step 2: Let baseURL be settings’s API base URL. + let base_url = global.api_base_url(); + // step 3: Let fallbackTimestamp be the number of milliseconds from + // the Unix epoch to settings’s current wall time, rounded to the nearest integer. + let fallback_timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64; + // step 4: Return the result of creating a notification given title, options, origin, + // baseURL, and fallbackTimestamp. + create_notification( + global, + title, + options, + origin, + base_url, + fallback_timestamp, + proto, + can_gc, + ) +} + +/// , + origin: ImmutableOrigin, + base_url: ServoUrl, + fallback_timestamp: u64, + proto: Option, + can_gc: CanGc, +) -> Fallible> { + // If options["silent"] is true and options["vibrate"] exists, then throw a TypeError. + if options.silent.is_some() && options.vibrate.is_some() { + return Err(Error::Type( + "Can't specify vibration patterns when setting notification to silent.".to_string(), + )); + } + // If options["renotify"] is true and options["tag"] is the empty string, then throw a TypeError. + if options.renotify && options.tag.is_empty() { + return Err(Error::Type( + "tag must be set to renotify as an existing notification.".to_string(), + )); + } + + Ok(Notification::new( + global, + title, + options, + origin, + base_url, + fallback_timestamp, + proto, + can_gc, + )) +} + +/// +fn validate_and_normalize_vibration_pattern( + pattern: &UnsignedLongOrUnsignedLongSequence, +) -> Vec { + // Step 1: If pattern is a list, proceed to the next step. Otherwise run the following substeps: + let mut pattern: Vec = match pattern { + UnsignedLongOrUnsignedLongSequence::UnsignedLong(value) => { + // Step 1.1: Let list be an initially empty list, and add pattern to list. + // Step 1.2: Set pattern to list. + vec![*value] + }, + UnsignedLongOrUnsignedLongSequence::UnsignedLongSequence(values) => values.clone(), + }; + + // Step 2: Let max length have the value 10. + // Step 3: If the length of pattern is greater than max length, truncate pattern, + // leaving only the first max length entries. + pattern.truncate(10); + + // If the length of the pattern is even and not zero then the last entry in the pattern will + // have no effect so an implementation can remove it from the pattern at this point. + if pattern.len() % 2 == 0 && !pattern.is_empty() { + pattern.pop(); + } + + // Step 4: Let max duration have the value 10000. + // Step 5: For each entry in pattern whose value is greater than max duration, + // set the entry's value to max duration. + pattern.iter_mut().for_each(|entry| { + *entry = 10000.min(*entry); + }); + + // Step 6: Return pattern. + pattern +} + +/// +fn get_notifications_permission_state(global: &GlobalScope) -> NotificationPermission { + let permission_state = descriptor_permission_state(PermissionName::Notifications, Some(global)); + match permission_state { + PermissionState::Granted => NotificationPermission::Granted, + PermissionState::Denied => NotificationPermission::Denied, + PermissionState::Prompt => NotificationPermission::Default, + } +} + +fn request_notification_permission(global: &GlobalScope) -> NotificationPermission { + let cx = GlobalScope::get_cx(); + let promise = &Promise::new(global, CanGc::note()); + let descriptor = PermissionDescriptor { + name: PermissionName::Notifications, + }; + let status = PermissionStatus::new(global, &descriptor); + + // The implementation of `request_notification_permission` seemed to be synchronous + Permissions::permission_request(cx, promise, &descriptor, &status); + + match status.State() { + PermissionState::Granted => NotificationPermission::Granted, + PermissionState::Denied => NotificationPermission::Denied, + // Should only receive "Granted" or "Denied" from the permission request + PermissionState::Prompt => NotificationPermission::Default, + } +} diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index d8c7f94917a..bdc57b39746 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -383,6 +383,10 @@ DOMInterfaces = { 'canGc': ['CloneNode', 'SetTextContent'], }, +'Notification': { + 'canGc': ['RequestPermission'], +}, + 'OfflineAudioContext': { 'inRealms': ['StartRendering'], 'canGc': ['StartRendering'], diff --git a/components/script_bindings/webidls/Notification.webidl b/components/script_bindings/webidls/Notification.webidl new file mode 100644 index 00000000000..fefd41cc234 --- /dev/null +++ b/components/script_bindings/webidls/Notification.webidl @@ -0,0 +1,113 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://notifications.spec.whatwg.org/#api +[Exposed=(Window,Worker), Pref="dom_notification_enabled"] +interface Notification : EventTarget { + [Throws] + constructor(DOMString title, optional NotificationOptions options = {}); + + [GetterThrows] + static readonly attribute NotificationPermission permission; + + [Exposed=Window] + static Promise requestPermission(optional NotificationPermissionCallback deprecatedCallback); + + static readonly attribute unsigned long maxActions; + + attribute EventHandler onclick; + attribute EventHandler onshow; + attribute EventHandler onerror; + attribute EventHandler onclose; + + [Pure] + readonly attribute DOMString title; + + [Pure] + readonly attribute NotificationDirection dir; + + [Pure] + readonly attribute DOMString lang; + + [Pure] + readonly attribute DOMString body; + + [Constant] + readonly attribute DOMString tag; + + [Pure] + readonly attribute USVString image; + + [Pure] + readonly attribute USVString icon; + + [Pure] + readonly attribute USVString badge; + + readonly attribute boolean renotify; + + [Constant] + readonly attribute boolean requireInteraction; + + [Constant] + readonly attribute boolean? silent; + + // [Cached, Frozen, Pure] + readonly attribute /*FrozenArray<*/any vibrate; + + readonly attribute EpochTimeStamp timestamp; + + [Constant] + readonly attribute any data; + + // [Cached, Frozen, Pure] + readonly attribute /*FrozenArray*/any actions; + + undefined close(); +}; + +// +typedef unsigned long long EpochTimeStamp; +typedef (unsigned long or sequence) VibratePattern; + +dictionary NotificationOptions { + NotificationDirection dir = "auto"; + DOMString lang = ""; + DOMString body = ""; + DOMString tag = ""; + USVString image; + USVString icon; + USVString badge; + VibratePattern vibrate; + EpochTimeStamp timestamp; + boolean renotify = false; + boolean? silent = null; + boolean requireInteraction = false; + any data = null; + sequence actions = []; +}; + +dictionary GetNotificationOptions { + DOMString tag = ""; +}; + +enum NotificationPermission { + "default", + "denied", + "granted" +}; + +callback NotificationPermissionCallback = undefined (NotificationPermission permission); + +enum NotificationDirection { + "auto", + "ltr", + "rtl" +}; + +dictionary NotificationAction { + required DOMString action; + required DOMString title; + USVString icon; +}; diff --git a/python/tidy/tidy.py b/python/tidy/tidy.py index 03a937be9ec..4109e95786c 100644 --- a/python/tidy/tidy.py +++ b/python/tidy/tidy.py @@ -101,6 +101,7 @@ WEBIDL_STANDARDS = [ b"//github.com/immersive-web/webxr-test-api/", b"//github.com/immersive-web/webxr-hands-input/", b"//gpuweb.github.io", + b"//notifications.spec.whatwg.org", # Not a URL b"// This interface is entirely internal to Servo, and should not be" + b" accessible to\n// web pages." diff --git a/tests/wpt/meta/__dir__.ini b/tests/wpt/meta/__dir__.ini index 0dbe435bfb9..b5b2db98287 100644 --- a/tests/wpt/meta/__dir__.ini +++ b/tests/wpt/meta/__dir__.ini @@ -1 +1 @@ -prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_shadowdom_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true"] +prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_shadowdom_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true", "dom_notification_enabled:true"] diff --git a/tests/wpt/meta/notifications/constructor-basic.https.html.ini b/tests/wpt/meta/notifications/constructor-basic.https.html.ini deleted file mode 100644 index e9cc5687908..00000000000 --- a/tests/wpt/meta/notifications/constructor-basic.https.html.ini +++ /dev/null @@ -1,9 +0,0 @@ -[constructor-basic.https.html] - [Called the notification constructor with one argument.] - expected: FAIL - - [Constructing a notification without a NotificationOptions defaults to null.] - expected: FAIL - - [constructing a notification with a NotificationOptions dictionary correctly sets and reflects the silent attribute.] - expected: FAIL diff --git a/tests/wpt/meta/notifications/constructor-invalid.https.html.ini b/tests/wpt/meta/notifications/constructor-invalid.https.html.ini deleted file mode 100644 index c9e61fa72d4..00000000000 --- a/tests/wpt/meta/notifications/constructor-invalid.https.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[constructor-invalid.https.html] - [Called the notification constructor with no arguments.] - expected: FAIL diff --git a/tests/wpt/meta/notifications/constructor-non-secure.html.ini b/tests/wpt/meta/notifications/constructor-non-secure.html.ini deleted file mode 100644 index 398ddff620d..00000000000 --- a/tests/wpt/meta/notifications/constructor-non-secure.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[constructor-non-secure.html] - [new Notification calls onerror in non-secure contexts] - expected: FAIL diff --git a/tests/wpt/meta/notifications/historical.any.js.ini b/tests/wpt/meta/notifications/historical.any.js.ini deleted file mode 100644 index 7c869f4ee55..00000000000 --- a/tests/wpt/meta/notifications/historical.any.js.ini +++ /dev/null @@ -1,8 +0,0 @@ -[historical.any.worker.html] - [Notification.get is obsolete] - expected: FAIL - - -[historical.any.html] - [Notification.get is obsolete] - expected: FAIL diff --git a/tests/wpt/meta/notifications/idlharness.https.any.js.ini b/tests/wpt/meta/notifications/idlharness.https.any.js.ini index 449bce2fd42..56add32751d 100644 --- a/tests/wpt/meta/notifications/idlharness.https.any.js.ini +++ b/tests/wpt/meta/notifications/idlharness.https.any.js.ini @@ -2,171 +2,6 @@ expected: ERROR [idlharness.https.any.worker.html] - [idl_test setup] - expected: FAIL - - [Notification interface: existence and properties of interface object] - expected: FAIL - - [Notification interface object length] - expected: FAIL - - [Notification interface object name] - expected: FAIL - - [Notification interface: existence and properties of interface prototype object] - expected: FAIL - - [Notification interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Notification interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Notification interface: attribute permission] - expected: FAIL - - [Notification interface: member requestPermission] - expected: FAIL - - [Notification interface: attribute maxActions] - expected: FAIL - - [Notification interface: attribute onclick] - expected: FAIL - - [Notification interface: attribute onshow] - expected: FAIL - - [Notification interface: attribute onerror] - expected: FAIL - - [Notification interface: attribute onclose] - expected: FAIL - - [Notification interface: attribute title] - expected: FAIL - - [Notification interface: attribute dir] - expected: FAIL - - [Notification interface: attribute lang] - expected: FAIL - - [Notification interface: attribute body] - expected: FAIL - - [Notification interface: attribute tag] - expected: FAIL - - [Notification interface: attribute image] - expected: FAIL - - [Notification interface: attribute icon] - expected: FAIL - - [Notification interface: attribute badge] - expected: FAIL - - [Notification interface: attribute vibrate] - expected: FAIL - - [Notification interface: attribute timestamp] - expected: FAIL - - [Notification interface: attribute renotify] - expected: FAIL - - [Notification interface: attribute silent] - expected: FAIL - - [Notification interface: attribute requireInteraction] - expected: FAIL - - [Notification interface: attribute data] - expected: FAIL - - [Notification interface: attribute actions] - expected: FAIL - - [Notification interface: operation close()] - expected: FAIL - - [Notification must be primary interface of notification] - expected: FAIL - - [Stringification of notification] - expected: FAIL - - [Notification interface: notification must inherit property "permission" with the proper type] - expected: FAIL - - [Notification interface: notification must not have property "requestPermission"] - expected: FAIL - - [Notification interface: notification must inherit property "maxActions" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "onclick" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "onshow" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "onerror" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "onclose" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "title" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "dir" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "lang" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "body" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "tag" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "image" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "icon" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "badge" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "vibrate" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "timestamp" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "renotify" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "silent" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "requireInteraction" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "data" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "actions" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "close()" with the proper type] - expected: FAIL - [ServiceWorkerRegistration interface: operation showNotification(DOMString, optional NotificationOptions)] expected: FAIL @@ -175,174 +10,6 @@ [idlharness.https.any.html] - [idl_test setup] - expected: FAIL - - [Notification interface: existence and properties of interface object] - expected: FAIL - - [Notification interface object length] - expected: FAIL - - [Notification interface object name] - expected: FAIL - - [Notification interface: existence and properties of interface prototype object] - expected: FAIL - - [Notification interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Notification interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Notification interface: attribute permission] - expected: FAIL - - [Notification interface: operation requestPermission(optional NotificationPermissionCallback)] - expected: FAIL - - [Notification interface: attribute maxActions] - expected: FAIL - - [Notification interface: attribute onclick] - expected: FAIL - - [Notification interface: attribute onshow] - expected: FAIL - - [Notification interface: attribute onerror] - expected: FAIL - - [Notification interface: attribute onclose] - expected: FAIL - - [Notification interface: attribute title] - expected: FAIL - - [Notification interface: attribute dir] - expected: FAIL - - [Notification interface: attribute lang] - expected: FAIL - - [Notification interface: attribute body] - expected: FAIL - - [Notification interface: attribute tag] - expected: FAIL - - [Notification interface: attribute image] - expected: FAIL - - [Notification interface: attribute icon] - expected: FAIL - - [Notification interface: attribute badge] - expected: FAIL - - [Notification interface: attribute vibrate] - expected: FAIL - - [Notification interface: attribute timestamp] - expected: FAIL - - [Notification interface: attribute renotify] - expected: FAIL - - [Notification interface: attribute silent] - expected: FAIL - - [Notification interface: attribute requireInteraction] - expected: FAIL - - [Notification interface: attribute data] - expected: FAIL - - [Notification interface: attribute actions] - expected: FAIL - - [Notification interface: operation close()] - expected: FAIL - - [Notification must be primary interface of notification] - expected: FAIL - - [Stringification of notification] - expected: FAIL - - [Notification interface: notification must inherit property "permission" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "requestPermission(optional NotificationPermissionCallback)" with the proper type] - expected: FAIL - - [Notification interface: calling requestPermission(optional NotificationPermissionCallback) on notification with too few arguments must throw TypeError] - expected: FAIL - - [Notification interface: notification must inherit property "maxActions" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "onclick" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "onshow" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "onerror" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "onclose" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "title" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "dir" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "lang" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "body" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "tag" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "image" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "icon" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "badge" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "vibrate" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "timestamp" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "renotify" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "silent" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "requireInteraction" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "data" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "actions" with the proper type] - expected: FAIL - - [Notification interface: notification must inherit property "close()" with the proper type] - expected: FAIL - [ServiceWorkerRegistration interface: operation showNotification(DOMString, optional NotificationOptions)] expected: FAIL diff --git a/tests/wpt/meta/notifications/permission.html.ini b/tests/wpt/meta/notifications/permission.html.ini deleted file mode 100644 index b9b5d759f7c..00000000000 --- a/tests/wpt/meta/notifications/permission.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[permission.html] - [Checked the Notification.permission property.] - expected: FAIL diff --git a/tests/wpt/meta/notifications/permissions-non-secure.html.ini b/tests/wpt/meta/notifications/permissions-non-secure.html.ini index adceb650578..45a5c16ff7c 100644 --- a/tests/wpt/meta/notifications/permissions-non-secure.html.ini +++ b/tests/wpt/meta/notifications/permissions-non-secure.html.ini @@ -1,10 +1,4 @@ [permissions-non-secure.html] expected: ERROR - [Notification.requestPermission must be called from a secure context] - expected: FAIL - - [Notification.permission must be called from a secure context] - expected: FAIL - [Notification.permission must be called from a secure worker] expected: TIMEOUT diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 66ea0dc9151..e1671aa476a 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -10518,7 +10518,7 @@ [] ], "interfaces.js": [ - "e2c2de8556c7fa88f54a76a3c18e06be14722de9", + "fbfc396b62c55415c0493b7528bd5e2b959452e6", [] ], "max-session-history-frame.html": [ @@ -13506,14 +13506,14 @@ ] ], "interfaces.https.html": [ - "501caf7410bba6533e11c0d83acb2729da48a289", + "4e9a583f82479e20dfa932459d3eff607e530d01", [ null, {} ] ], "interfaces.worker.js": [ - "5288c40b0fb6e67f2b903f82f074580faa4dca33", + "fc621bbafeec167942f802caae43b9f2ef23b29b", [ "mozilla/interfaces.worker.html", {} diff --git a/tests/wpt/mozilla/meta/mozilla/interfaces.https.html.ini b/tests/wpt/mozilla/meta/mozilla/interfaces.https.html.ini index 605489ac6bc..e5e4eea915d 100644 --- a/tests/wpt/mozilla/meta/mozilla/interfaces.https.html.ini +++ b/tests/wpt/mozilla/meta/mozilla/interfaces.https.html.ini @@ -1 +1 @@ -prefs: [dom_webxr_test: false] \ No newline at end of file +prefs: [dom_webxr_test: false, dom_notification_enabled: true] diff --git a/tests/wpt/mozilla/meta/mozilla/interfaces.worker.js.ini b/tests/wpt/mozilla/meta/mozilla/interfaces.worker.js.ini new file mode 100644 index 00000000000..cfff165a9fd --- /dev/null +++ b/tests/wpt/mozilla/meta/mozilla/interfaces.worker.js.ini @@ -0,0 +1 @@ +prefs: [dom_notification_enabled: true] diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index 501caf7410b..4e9a583f824 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -210,6 +210,7 @@ test_interfaces([ "NodeFilter", "NodeIterator", "NodeList", + "Notification", "OfflineAudioCompletionEvent", "OfflineAudioContext", "Option", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.js b/tests/wpt/mozilla/tests/mozilla/interfaces.js index e2c2de8556c..fbfc396b62c 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.js @@ -34,6 +34,7 @@ function test_interfaces(interfaceNamesInGlobalScope) { "MessageChannel", "MessagePort", "NaN", + "Notification", "Number", "Object", "Promise", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index 5288c40b0fb..fc621bbafee 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -42,6 +42,7 @@ test_interfaces([ "MessageChannel", "MessageEvent", "MessagePort", + "Notification", "Performance", "PerformanceEntry", "PerformanceMark",