mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
feat(notification): add EmbedderMsg::ShowNotification
(#36055)
Signed-off-by: Jason Tsai <git@pews.dev>
This commit is contained in:
parent
c09eed759b
commit
e9ed5dd023
6 changed files with 151 additions and 20 deletions
|
@ -235,6 +235,7 @@ mod from_script {
|
||||||
Self::PlayGamepadHapticEffect(..) => target_variant!("PlayGamepadHapticEffect"),
|
Self::PlayGamepadHapticEffect(..) => target_variant!("PlayGamepadHapticEffect"),
|
||||||
Self::StopGamepadHapticEffect(..) => target_variant!("StopGamepadHapticEffect"),
|
Self::StopGamepadHapticEffect(..) => target_variant!("StopGamepadHapticEffect"),
|
||||||
Self::ShutdownComplete => target_variant!("ShutdownComplete"),
|
Self::ShutdownComplete => target_variant!("ShutdownComplete"),
|
||||||
|
Self::ShowNotification(..) => target_variant!("ShowNotification"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,10 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use content_security_policy::Destination;
|
use content_security_policy::Destination;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
use embedder_traits::{
|
||||||
|
EmbedderMsg, Notification as EmbedderNotification,
|
||||||
|
NotificationAction as EmbedderNotificationAction,
|
||||||
|
};
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use js::jsapi::Heap;
|
use js::jsapi::Heap;
|
||||||
|
@ -253,23 +257,39 @@ impl Notification {
|
||||||
|
|
||||||
/// <https://notifications.spec.whatwg.org/#notification-show-steps>
|
/// <https://notifications.spec.whatwg.org/#notification-show-steps>
|
||||||
fn show(&self) {
|
fn show(&self) {
|
||||||
// TODO: step 3: set shown to false
|
// step 3: set shown to false
|
||||||
|
let shown = false;
|
||||||
|
|
||||||
// TODO: step 4: Let oldNotification be the notification in the list of notifications
|
// TODO: step 4: Let oldNotification be the notification in the list of notifications
|
||||||
// whose tag is not the empty string and is notification’s tag,
|
// whose tag is not the empty string and is notification’s tag,
|
||||||
// and whose origin is same origin with notification’s origin,
|
// and whose origin is same origin with notification’s origin,
|
||||||
// if any, and null otherwise.
|
// if any, and null otherwise.
|
||||||
|
|
||||||
// TODO: step 5: If oldNotification is non-null, then:
|
// TODO: step 5: If oldNotification is non-null, then:
|
||||||
// TODO: step 6: If shown is false, then:
|
// TODO: step 5.1: Handle close events with oldNotification.
|
||||||
// TODO: step 6.1: Append notification to the list of notifications.
|
// TODO: step 5.2: If the notification platform supports replacement, then:
|
||||||
// TODO: step 6.2: Display notification on the device
|
// TODO: step 5.2.1: Replace oldNotification with notification, in the list of notifications.
|
||||||
// TODO: Add EmbedderMsg::ShowNotification(...) event
|
// TODO: step 5.2.2: Set shown to true.
|
||||||
|
// TODO: step 5.3: Otherwise, remove oldNotification from the list of notifications.
|
||||||
|
|
||||||
|
// step 6: If shown is false, then:
|
||||||
|
if !shown {
|
||||||
|
// TODO: step 6.1: Append notification to the list of notifications.
|
||||||
|
// step 6.2: Display notification on the device
|
||||||
|
self.global()
|
||||||
|
.send_to_embedder(EmbedderMsg::ShowNotification(
|
||||||
|
self.global().webview_id(),
|
||||||
|
self.to_embedder_notification(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: step 7: If shown is false or oldNotification is non-null,
|
// TODO: step 7: If shown is false or oldNotification is non-null,
|
||||||
// and notification’s renotify preference is true,
|
// and notification’s renotify preference is true,
|
||||||
// then run the alert steps for notification.
|
// then run the alert steps for notification.
|
||||||
|
|
||||||
// step 8: If notification is a non-persistent notification,
|
// step 8: If notification is a non-persistent notification,
|
||||||
// then queue a task to fire an event named show on
|
// then queue a task to fire an event named show on
|
||||||
// the Notification object representing notification.
|
// the Notification object representing notification.
|
||||||
if self.serviceworker_registration.is_none() {
|
if self.serviceworker_registration.is_none() {
|
||||||
self.global()
|
self.global()
|
||||||
.task_manager()
|
.task_manager()
|
||||||
|
@ -277,6 +297,46 @@ impl Notification {
|
||||||
.queue_simple_event(self.upcast(), atom!("show"));
|
.queue_simple_event(self.upcast(), atom!("show"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an [`embedder_traits::Notification`].
|
||||||
|
fn to_embedder_notification(&self) -> EmbedderNotification {
|
||||||
|
EmbedderNotification {
|
||||||
|
title: self.title.to_string(),
|
||||||
|
body: self.body.to_string(),
|
||||||
|
tag: self.tag.to_string(),
|
||||||
|
language: self.lang.to_string(),
|
||||||
|
require_interaction: self.require_interaction,
|
||||||
|
silent: self.silent,
|
||||||
|
icon_url: self
|
||||||
|
.icon
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|icon| ServoUrl::parse(icon).ok()),
|
||||||
|
badge_url: self
|
||||||
|
.badge
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|badge| ServoUrl::parse(badge).ok()),
|
||||||
|
image_url: self
|
||||||
|
.image
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|image| ServoUrl::parse(image).ok()),
|
||||||
|
actions: self
|
||||||
|
.actions
|
||||||
|
.iter()
|
||||||
|
.map(|action| EmbedderNotificationAction {
|
||||||
|
name: action.name.to_string(),
|
||||||
|
title: action.title.to_string(),
|
||||||
|
icon_url: action
|
||||||
|
.icon_url
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|icon| ServoUrl::parse(icon).ok()),
|
||||||
|
icon_resource: action.icon_resource.borrow().clone(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
icon_resource: self.icon_resource.borrow().clone(),
|
||||||
|
badge_resource: self.badge_resource.borrow().clone(),
|
||||||
|
image_resource: self.image_resource.borrow().clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NotificationMethods<crate::DomTypeHolder> for Notification {
|
impl NotificationMethods<crate::DomTypeHolder> for Notification {
|
||||||
|
@ -316,12 +376,12 @@ impl NotificationMethods<crate::DomTypeHolder> for Notification {
|
||||||
.dom_manipulation_task_source()
|
.dom_manipulation_task_source()
|
||||||
.queue_simple_event(notification.upcast(), atom!("error"));
|
.queue_simple_event(notification.upcast(), atom!("error"));
|
||||||
// TODO: abort steps
|
// TODO: abort steps
|
||||||
|
} else {
|
||||||
|
// step 5.2: Run the notification show steps for notification
|
||||||
|
// <https://notifications.spec.whatwg.org/#notification-show-steps>
|
||||||
|
// step 1: Run the fetch steps for notification.
|
||||||
|
notification.fetch_resources_and_show_when_ready();
|
||||||
}
|
}
|
||||||
// TODO: step 5.2: Run the notification show steps for notification
|
|
||||||
// <https://notifications.spec.whatwg.org/#notification-show-steps>
|
|
||||||
// step 1: Run the fetch steps for notification.
|
|
||||||
// following steps are processed in show_steps after all resources are fetched
|
|
||||||
notification.fetch_resources_and_show_when_ready();
|
|
||||||
|
|
||||||
Ok(notification)
|
Ok(notification)
|
||||||
}
|
}
|
||||||
|
@ -478,10 +538,12 @@ impl NotificationMethods<crate::DomTypeHolder> for Notification {
|
||||||
|
|
||||||
// If notification is a non-persistent notification
|
// If notification is a non-persistent notification
|
||||||
// then queue a task to fire an event named close on the Notification object representing notification.
|
// then queue a task to fire an event named close on the Notification object representing notification.
|
||||||
self.global()
|
if self.serviceworker_registration.is_none() {
|
||||||
.task_manager()
|
self.global()
|
||||||
.dom_manipulation_task_source()
|
.task_manager()
|
||||||
.queue_simple_event(self.upcast(), atom!("close"));
|
.dom_manipulation_task_source()
|
||||||
|
.queue_simple_event(self.upcast(), atom!("close"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,7 +816,7 @@ impl Notification {
|
||||||
None,
|
None,
|
||||||
url.clone(),
|
url.clone(),
|
||||||
Destination::Image,
|
Destination::Image,
|
||||||
None, // TODO: check CORS
|
None, // TODO: check which CORS should be used
|
||||||
None,
|
None,
|
||||||
global.get_referrer(),
|
global.get_referrer(),
|
||||||
global.insecure_requests_policy(),
|
global.insecure_requests_policy(),
|
||||||
|
@ -809,7 +871,7 @@ impl Notification {
|
||||||
let cache_result = global.image_cache().get_cached_image_status(
|
let cache_result = global.image_cache().get_cached_image_status(
|
||||||
request.url.clone(),
|
request.url.clone(),
|
||||||
global.origin().immutable().clone(),
|
global.origin().immutable().clone(),
|
||||||
None, // TODO: check CORS
|
None, // TODO: check which CORS should be used
|
||||||
UsePlaceholder::No,
|
UsePlaceholder::No,
|
||||||
);
|
);
|
||||||
match cache_result {
|
match cache_result {
|
||||||
|
|
|
@ -985,6 +985,12 @@ impl Servo {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
EmbedderMsg::ShowNotification(webview_id, notification) => {
|
||||||
|
match webview_id.and_then(|webview_id| self.get_webview_handle(webview_id)) {
|
||||||
|
Some(webview) => webview.delegate().show_notification(webview, notification),
|
||||||
|
None => self.delegate().show_notification(notification),
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use embedder_traits::Notification;
|
||||||
|
|
||||||
use crate::Servo;
|
use crate::Servo;
|
||||||
use crate::webview_delegate::{AllowOrDenyRequest, WebResourceLoad};
|
use crate::webview_delegate::{AllowOrDenyRequest, WebResourceLoad};
|
||||||
|
|
||||||
|
@ -34,6 +36,9 @@ pub trait ServoDelegate {
|
||||||
/// [`WebView`]. For loads associated with a [`WebView`], Servo will call
|
/// [`WebView`]. For loads associated with a [`WebView`], Servo will call
|
||||||
/// [`crate::WebViewDelegate::load_web_resource`].
|
/// [`crate::WebViewDelegate::load_web_resource`].
|
||||||
fn load_web_resource(&self, _load: WebResourceLoad) {}
|
fn load_web_resource(&self, _load: WebResourceLoad) {}
|
||||||
|
|
||||||
|
/// Request to display a notification.
|
||||||
|
fn show_notification(&self, _notification: Notification) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct DefaultServoDelegate;
|
pub(crate) struct DefaultServoDelegate;
|
||||||
|
|
|
@ -8,8 +8,9 @@ use base::id::PipelineId;
|
||||||
use constellation_traits::ConstellationMsg;
|
use constellation_traits::ConstellationMsg;
|
||||||
use embedder_traits::{
|
use embedder_traits::{
|
||||||
AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern,
|
AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern,
|
||||||
GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent, PermissionFeature,
|
GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent, Notification,
|
||||||
SimpleDialog, WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
|
PermissionFeature, SimpleDialog, WebResourceRequest, WebResourceResponse,
|
||||||
|
WebResourceResponseMsg,
|
||||||
};
|
};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use keyboard_types::KeyboardEvent;
|
use keyboard_types::KeyboardEvent;
|
||||||
|
@ -462,6 +463,9 @@ pub trait WebViewDelegate {
|
||||||
/// For loads not associated with a [`WebView`], such as those for service workers, Servo
|
/// For loads not associated with a [`WebView`], such as those for service workers, Servo
|
||||||
/// will call [`crate::ServoDelegate::load_web_resource`].
|
/// will call [`crate::ServoDelegate::load_web_resource`].
|
||||||
fn load_web_resource(&self, _webview: WebView, _load: WebResourceLoad) {}
|
fn load_web_resource(&self, _webview: WebView, _load: WebResourceLoad) {}
|
||||||
|
|
||||||
|
/// Request to display a notification.
|
||||||
|
fn show_notification(&self, _webview: WebView, _notification: Notification) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct DefaultWebViewDelegate;
|
pub(crate) struct DefaultWebViewDelegate;
|
||||||
|
|
|
@ -14,6 +14,7 @@ mod webdriver;
|
||||||
|
|
||||||
use std::fmt::{Debug, Error, Formatter};
|
use std::fmt::{Debug, Error, Formatter};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base::id::{PipelineId, WebViewId};
|
use base::id::{PipelineId, WebViewId};
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
|
@ -23,6 +24,7 @@ pub use keyboard_types::{KeyboardEvent, Modifiers};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
|
use pixels::Image;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use strum_macros::IntoStaticStr;
|
use strum_macros::IntoStaticStr;
|
||||||
|
@ -326,6 +328,8 @@ pub enum EmbedderMsg {
|
||||||
/// Required because the constellation can have pending calls to make
|
/// Required because the constellation can have pending calls to make
|
||||||
/// (e.g. SetFrameTree) at the time that we send it an ExitMsg.
|
/// (e.g. SetFrameTree) at the time that we send it an ExitMsg.
|
||||||
ShutdownComplete,
|
ShutdownComplete,
|
||||||
|
/// Request to display a notification.
|
||||||
|
ShowNotification(Option<WebViewId>, Notification),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for EmbedderMsg {
|
impl Debug for EmbedderMsg {
|
||||||
|
@ -582,3 +586,52 @@ pub enum LoadStatus {
|
||||||
/// See <https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState>
|
/// See <https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState>
|
||||||
Complete,
|
Complete,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data that could be used to display a desktop notification to the end user
|
||||||
|
/// when the [Notification API](<https://notifications.spec.whatwg.org/#notifications>) is called.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct Notification {
|
||||||
|
/// Title of the notification.
|
||||||
|
pub title: String,
|
||||||
|
/// Body string of the notification.
|
||||||
|
pub body: String,
|
||||||
|
/// An identifier tag for the notification. Notification with the same tag
|
||||||
|
/// can be replaced by another to avoid users' screen being filled up with similar notifications.
|
||||||
|
pub tag: String,
|
||||||
|
/// The tag for the language used in the notification's title, body, and the title of each its actions. [RFC 5646](https://datatracker.ietf.org/doc/html/rfc5646)
|
||||||
|
pub language: String,
|
||||||
|
/// A boolean value indicates the notification should remain readily available
|
||||||
|
/// until the end user activates or dismisses the notification.
|
||||||
|
pub require_interaction: bool,
|
||||||
|
/// When `true`, indicates no sounds or vibrations should be made. When `None`,
|
||||||
|
/// the device's default settings should be respected.
|
||||||
|
pub silent: Option<bool>,
|
||||||
|
/// The URL of an icon. The icon will be displayed as part of the notification.
|
||||||
|
pub icon_url: Option<ServoUrl>,
|
||||||
|
/// Icon's raw image data and metadata.
|
||||||
|
pub icon_resource: Option<Arc<Image>>,
|
||||||
|
/// The URL of a badge. The badge is used when there is no enough space to display the notification,
|
||||||
|
/// such as on a mobile device's notification bar.
|
||||||
|
pub badge_url: Option<ServoUrl>,
|
||||||
|
/// Badge's raw image data and metadata.
|
||||||
|
pub badge_resource: Option<Arc<Image>>,
|
||||||
|
/// The URL of an image. The image will be displayed as part of the notification.
|
||||||
|
pub image_url: Option<ServoUrl>,
|
||||||
|
/// Image's raw image data and metadata.
|
||||||
|
pub image_resource: Option<Arc<Image>>,
|
||||||
|
/// Actions available for users to choose from for interacting with the notification.
|
||||||
|
pub actions: Vec<NotificationAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Actions available for users to choose from for interacting with the notification.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct NotificationAction {
|
||||||
|
/// A string that identifies the action.
|
||||||
|
pub name: String,
|
||||||
|
/// The title string of the action to be shown to the user.
|
||||||
|
pub title: String,
|
||||||
|
/// The URL of an icon. The icon will be displayed with the action.
|
||||||
|
pub icon_url: Option<ServoUrl>,
|
||||||
|
/// Icon's raw image data and metadata.
|
||||||
|
pub icon_resource: Option<Arc<Image>>,
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue