feat: custom toast xml and failure reporting for notifications (#25401)
* allow custom toast xml and report failures * docs * tests * don't use namespaces * lint doesn't like trailing commas * addressing feedback
This commit is contained in:
parent
d2282ac51a
commit
b43859f098
9 changed files with 282 additions and 219 deletions
|
@ -29,9 +29,9 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu
|
||||||
### `new Notification([options])`
|
### `new Notification([options])`
|
||||||
|
|
||||||
* `options` Object (optional)
|
* `options` Object (optional)
|
||||||
* `title` String - A title for the notification, which will be shown at the top of the notification window when it is shown.
|
* `title` String (optional) - A title for the notification, which will be shown at the top of the notification window when it is shown.
|
||||||
* `subtitle` String (optional) _macOS_ - A subtitle for the notification, which will be displayed below the title.
|
* `subtitle` String (optional) _macOS_ - A subtitle for the notification, which will be displayed below the title.
|
||||||
* `body` String - The body text of the notification, which will be displayed below the title or subtitle.
|
* `body` String (optional) - The body text of the notification, which will be displayed below the title or subtitle.
|
||||||
* `silent` Boolean (optional) - Whether or not to emit an OS notification noise when showing the notification.
|
* `silent` Boolean (optional) - Whether or not to emit an OS notification noise when showing the notification.
|
||||||
* `icon` (String | [NativeImage](native-image.md)) (optional) - An icon to use in the notification.
|
* `icon` (String | [NativeImage](native-image.md)) (optional) - An icon to use in the notification.
|
||||||
* `hasReply` Boolean (optional) _macOS_ - Whether or not to add an inline reply option to the notification.
|
* `hasReply` Boolean (optional) _macOS_ - Whether or not to add an inline reply option to the notification.
|
||||||
|
@ -41,6 +41,7 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu
|
||||||
* `urgency` String (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
|
* `urgency` String (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
|
||||||
* `actions` [NotificationAction[]](structures/notification-action.md) (optional) _macOS_ - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation.
|
* `actions` [NotificationAction[]](structures/notification-action.md) (optional) _macOS_ - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation.
|
||||||
* `closeButtonText` String (optional) _macOS_ - A custom title for the close button of an alert. An empty string will cause the default localized text to be used.
|
* `closeButtonText` String (optional) _macOS_ - A custom title for the close button of an alert. An empty string will cause the default localized text to be used.
|
||||||
|
* `toastXml` String (optional) _Windows_ - A custom description of the Notification on Windows superseding all properties above. Provides full customization of design and behavior of the notification.
|
||||||
|
|
||||||
### Instance Events
|
### Instance Events
|
||||||
|
|
||||||
|
@ -94,6 +95,15 @@ Returns:
|
||||||
* `event` Event
|
* `event` Event
|
||||||
* `index` Number - The index of the action that was activated.
|
* `index` Number - The index of the action that was activated.
|
||||||
|
|
||||||
|
#### Event: 'failed' _Windows_
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
* `event` Event
|
||||||
|
* `error` String - The error encountered during execution of the `show()` method.
|
||||||
|
|
||||||
|
Emitted when an error is encountered while creating and showing the native notification.
|
||||||
|
|
||||||
### Instance Methods
|
### Instance Methods
|
||||||
|
|
||||||
Objects created with `new Notification` have the following instance methods:
|
Objects created with `new Notification` have the following instance methods:
|
||||||
|
@ -162,6 +172,10 @@ If `timeoutType` is set to 'never', the notification never expires. It stays ope
|
||||||
|
|
||||||
A [`NotificationAction[]`](structures/notification-action.md) property representing the actions of the notification.
|
A [`NotificationAction[]`](structures/notification-action.md) property representing the actions of the notification.
|
||||||
|
|
||||||
|
#### `notification.toastXml` _Windows_
|
||||||
|
|
||||||
|
A `String` property representing the custom Toast XML of the notification.
|
||||||
|
|
||||||
### Playing Sounds
|
### Playing Sounds
|
||||||
|
|
||||||
On macOS, you can specify the name of the sound you'd like to play when the
|
On macOS, you can specify the name of the sound you'd like to play when the
|
||||||
|
|
|
@ -72,6 +72,7 @@ Notification::Notification(gin::Arguments* args) {
|
||||||
opts.Get("actions", &actions_);
|
opts.Get("actions", &actions_);
|
||||||
opts.Get("sound", &sound_);
|
opts.Get("sound", &sound_);
|
||||||
opts.Get("closeButtonText", &close_button_text_);
|
opts.Get("closeButtonText", &close_button_text_);
|
||||||
|
opts.Get("toastXml", &toast_xml_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +136,10 @@ base::string16 Notification::GetCloseButtonText() const {
|
||||||
return close_button_text_;
|
return close_button_text_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base::string16 Notification::GetToastXml() const {
|
||||||
|
return toast_xml_;
|
||||||
|
}
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
void Notification::SetTitle(const base::string16& new_title) {
|
void Notification::SetTitle(const base::string16& new_title) {
|
||||||
title_ = new_title;
|
title_ = new_title;
|
||||||
|
@ -181,6 +186,10 @@ void Notification::SetCloseButtonText(const base::string16& text) {
|
||||||
close_button_text_ = text;
|
close_button_text_ = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Notification::SetToastXml(const base::string16& new_toast_xml) {
|
||||||
|
toast_xml_ = new_toast_xml;
|
||||||
|
}
|
||||||
|
|
||||||
void Notification::NotificationAction(int index) {
|
void Notification::NotificationAction(int index) {
|
||||||
Emit("action", index);
|
Emit("action", index);
|
||||||
}
|
}
|
||||||
|
@ -197,6 +206,10 @@ void Notification::NotificationDisplayed() {
|
||||||
Emit("show");
|
Emit("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Notification::NotificationFailed(const std::string& error) {
|
||||||
|
Emit("failed", error);
|
||||||
|
}
|
||||||
|
|
||||||
void Notification::NotificationDestroyed() {}
|
void Notification::NotificationDestroyed() {}
|
||||||
|
|
||||||
void Notification::NotificationClosed() {
|
void Notification::NotificationClosed() {
|
||||||
|
@ -231,6 +244,7 @@ void Notification::Show() {
|
||||||
options.sound = sound_;
|
options.sound = sound_;
|
||||||
options.close_button_text = close_button_text_;
|
options.close_button_text = close_button_text_;
|
||||||
options.urgency = urgency_;
|
options.urgency = urgency_;
|
||||||
|
options.toast_xml = toast_xml_;
|
||||||
notification_->Show(options);
|
notification_->Show(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,6 +279,8 @@ v8::Local<v8::ObjectTemplate> Notification::FillObjectTemplate(
|
||||||
&Notification::SetActions)
|
&Notification::SetActions)
|
||||||
.SetProperty("closeButtonText", &Notification::GetCloseButtonText,
|
.SetProperty("closeButtonText", &Notification::GetCloseButtonText,
|
||||||
&Notification::SetCloseButtonText)
|
&Notification::SetCloseButtonText)
|
||||||
|
.SetProperty("toastXml", &Notification::GetToastXml,
|
||||||
|
&Notification::SetToastXml)
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ class Notification : public gin::Wrappable<Notification>,
|
||||||
void NotificationDisplayed() override;
|
void NotificationDisplayed() override;
|
||||||
void NotificationDestroyed() override;
|
void NotificationDestroyed() override;
|
||||||
void NotificationClosed() override;
|
void NotificationClosed() override;
|
||||||
|
void NotificationFailed(const std::string& error) override;
|
||||||
|
|
||||||
// gin::Wrappable
|
// gin::Wrappable
|
||||||
static gin::WrapperInfo kWrapperInfo;
|
static gin::WrapperInfo kWrapperInfo;
|
||||||
|
@ -75,6 +76,7 @@ class Notification : public gin::Wrappable<Notification>,
|
||||||
base::string16 GetSound() const;
|
base::string16 GetSound() const;
|
||||||
std::vector<electron::NotificationAction> GetActions() const;
|
std::vector<electron::NotificationAction> GetActions() const;
|
||||||
base::string16 GetCloseButtonText() const;
|
base::string16 GetCloseButtonText() const;
|
||||||
|
base::string16 GetToastXml() const;
|
||||||
|
|
||||||
// Prop Setters
|
// Prop Setters
|
||||||
void SetTitle(const base::string16& new_title);
|
void SetTitle(const base::string16& new_title);
|
||||||
|
@ -88,6 +90,7 @@ class Notification : public gin::Wrappable<Notification>,
|
||||||
void SetSound(const base::string16& sound);
|
void SetSound(const base::string16& sound);
|
||||||
void SetActions(const std::vector<electron::NotificationAction>& actions);
|
void SetActions(const std::vector<electron::NotificationAction>& actions);
|
||||||
void SetCloseButtonText(const base::string16& text);
|
void SetCloseButtonText(const base::string16& text);
|
||||||
|
void SetToastXml(const base::string16& text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::string16 title_;
|
base::string16 title_;
|
||||||
|
@ -104,6 +107,7 @@ class Notification : public gin::Wrappable<Notification>,
|
||||||
base::string16 urgency_;
|
base::string16 urgency_;
|
||||||
std::vector<electron::NotificationAction> actions_;
|
std::vector<electron::NotificationAction> actions_;
|
||||||
base::string16 close_button_text_;
|
base::string16 close_button_text_;
|
||||||
|
base::string16 toast_xml_;
|
||||||
|
|
||||||
electron::NotificationPresenter* presenter_;
|
electron::NotificationPresenter* presenter_;
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,9 @@ void Notification::NotificationDismissed() {
|
||||||
Destroy();
|
Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Notification::NotificationFailed() {
|
void Notification::NotificationFailed(const std::string& error) {
|
||||||
if (delegate())
|
if (delegate())
|
||||||
delegate()->NotificationFailed();
|
delegate()->NotificationFailed(error);
|
||||||
Destroy();
|
Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct NotificationOptions {
|
||||||
base::string16 urgency; // Linux
|
base::string16 urgency; // Linux
|
||||||
std::vector<NotificationAction> actions;
|
std::vector<NotificationAction> actions;
|
||||||
base::string16 close_button_text;
|
base::string16 close_button_text;
|
||||||
|
base::string16 toast_xml;
|
||||||
|
|
||||||
NotificationOptions();
|
NotificationOptions();
|
||||||
~NotificationOptions();
|
~NotificationOptions();
|
||||||
|
@ -56,7 +57,7 @@ class Notification {
|
||||||
// Should be called by derived classes.
|
// Should be called by derived classes.
|
||||||
void NotificationClicked();
|
void NotificationClicked();
|
||||||
void NotificationDismissed();
|
void NotificationDismissed();
|
||||||
void NotificationFailed();
|
void NotificationFailed(const std::string& error = "");
|
||||||
|
|
||||||
// delete this.
|
// delete this.
|
||||||
void Destroy();
|
void Destroy();
|
||||||
|
|
|
@ -15,7 +15,7 @@ class NotificationDelegate {
|
||||||
virtual void NotificationDestroyed() {}
|
virtual void NotificationDestroyed() {}
|
||||||
|
|
||||||
// Failed to send the notification.
|
// Failed to send the notification.
|
||||||
virtual void NotificationFailed() {}
|
virtual void NotificationFailed(const std::string& error) {}
|
||||||
|
|
||||||
// Notification was replied to
|
// Notification was replied to
|
||||||
virtual void NotificationReplied(const std::string& reply) {}
|
virtual void NotificationReplied(const std::string& reply) {}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "shell/browser/notifications/win/windows_toast_notification.h"
|
#include "shell/browser/notifications/win/windows_toast_notification.h"
|
||||||
|
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
|
#include <wrl\wrappers\corewrappers.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/environment.h"
|
#include "base/environment.h"
|
||||||
|
@ -23,11 +24,33 @@
|
||||||
|
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlAttribute;
|
using ABI::Windows::Data::Xml::Dom::IXmlAttribute;
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlDocument;
|
using ABI::Windows::Data::Xml::Dom::IXmlDocument;
|
||||||
|
using ABI::Windows::Data::Xml::Dom::IXmlDocumentIO;
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlElement;
|
using ABI::Windows::Data::Xml::Dom::IXmlElement;
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlNamedNodeMap;
|
using ABI::Windows::Data::Xml::Dom::IXmlNamedNodeMap;
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlNode;
|
using ABI::Windows::Data::Xml::Dom::IXmlNode;
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlNodeList;
|
using ABI::Windows::Data::Xml::Dom::IXmlNodeList;
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlText;
|
using ABI::Windows::Data::Xml::Dom::IXmlText;
|
||||||
|
using Microsoft::WRL::Wrappers::HStringReference;
|
||||||
|
|
||||||
|
#define RETURN_IF_FAILED(hr) \
|
||||||
|
do { \
|
||||||
|
HRESULT _hrTemp = hr; \
|
||||||
|
if (FAILED(_hrTemp)) { \
|
||||||
|
return _hrTemp; \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
#define REPORT_AND_RETURN_IF_FAILED(hr, msg) \
|
||||||
|
do { \
|
||||||
|
HRESULT _hrTemp = hr; \
|
||||||
|
std::string _msgTemp = msg; \
|
||||||
|
if (FAILED(_hrTemp)) { \
|
||||||
|
std::string _err = _msgTemp + ",ERROR " + std::to_string(_hrTemp); \
|
||||||
|
if (IsDebuggingNotifications()) \
|
||||||
|
LOG(INFO) << _err; \
|
||||||
|
Notification::NotificationFailed(_err); \
|
||||||
|
return _hrTemp; \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
|
@ -36,7 +59,6 @@ namespace {
|
||||||
bool IsDebuggingNotifications() {
|
bool IsDebuggingNotifications() {
|
||||||
return base::Environment::Create()->HasVar("ELECTRON_DEBUG_NOTIFICATIONS");
|
return base::Environment::Create()->HasVar("ELECTRON_DEBUG_NOTIFICATIONS");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -83,59 +105,17 @@ WindowsToastNotification::~WindowsToastNotification() {
|
||||||
// Remove the notification on exit.
|
// Remove the notification on exit.
|
||||||
if (toast_notification_) {
|
if (toast_notification_) {
|
||||||
RemoveCallbacks(toast_notification_.Get());
|
RemoveCallbacks(toast_notification_.Get());
|
||||||
Dismiss();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsToastNotification::Show(const NotificationOptions& options) {
|
void WindowsToastNotification::Show(const NotificationOptions& options) {
|
||||||
auto* presenter_win = static_cast<NotificationPresenterWin*>(presenter());
|
if (SUCCEEDED(ShowInternal(options))) {
|
||||||
std::wstring icon_path =
|
if (IsDebuggingNotifications())
|
||||||
presenter_win->SaveIconToFilesystem(options.icon, options.icon_url);
|
LOG(INFO) << "Notification created";
|
||||||
|
|
||||||
ComPtr<IXmlDocument> toast_xml;
|
if (delegate())
|
||||||
if (FAILED(GetToastXml(toast_manager_.Get(), options.title, options.msg,
|
delegate()->NotificationDisplayed();
|
||||||
icon_path, options.timeout_type, options.silent,
|
|
||||||
&toast_xml))) {
|
|
||||||
NotificationFailed();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedHString toast_str(
|
|
||||||
RuntimeClass_Windows_UI_Notifications_ToastNotification);
|
|
||||||
if (!toast_str.success()) {
|
|
||||||
NotificationFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComPtr<ABI::Windows::UI::Notifications::IToastNotificationFactory>
|
|
||||||
toast_factory;
|
|
||||||
if (FAILED(Windows::Foundation::GetActivationFactory(toast_str,
|
|
||||||
&toast_factory))) {
|
|
||||||
NotificationFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(toast_factory->CreateToastNotification(toast_xml.Get(),
|
|
||||||
&toast_notification_))) {
|
|
||||||
NotificationFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SetupCallbacks(toast_notification_.Get())) {
|
|
||||||
NotificationFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(toast_notifier_->Show(toast_notification_.Get()))) {
|
|
||||||
NotificationFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsDebuggingNotifications())
|
|
||||||
LOG(INFO) << "Notification created";
|
|
||||||
|
|
||||||
if (delegate())
|
|
||||||
delegate()->NotificationDisplayed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsToastNotification::Dismiss() {
|
void WindowsToastNotification::Dismiss() {
|
||||||
|
@ -144,7 +124,50 @@ void WindowsToastNotification::Dismiss() {
|
||||||
toast_notifier_->Hide(toast_notification_.Get());
|
toast_notifier_->Hide(toast_notification_.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::GetToastXml(
|
HRESULT WindowsToastNotification::ShowInternal(
|
||||||
|
const NotificationOptions& options) {
|
||||||
|
ComPtr<IXmlDocument> toast_xml;
|
||||||
|
// The custom xml takes priority over the preset template.
|
||||||
|
if (!options.toast_xml.empty()) {
|
||||||
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
|
XmlDocumentFromString(options.toast_xml.c_str(), &toast_xml),
|
||||||
|
"XML: Invalid XML");
|
||||||
|
} else {
|
||||||
|
auto* presenter_win = static_cast<NotificationPresenterWin*>(presenter());
|
||||||
|
std::wstring icon_path =
|
||||||
|
presenter_win->SaveIconToFilesystem(options.icon, options.icon_url);
|
||||||
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
|
GetToastXml(toast_manager_.Get(), options.title, options.msg, icon_path,
|
||||||
|
options.timeout_type, options.silent, &toast_xml),
|
||||||
|
"XML: Failed to create XML document");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedHString toast_str(
|
||||||
|
RuntimeClass_Windows_UI_Notifications_ToastNotification);
|
||||||
|
if (!toast_str.success()) {
|
||||||
|
NotificationFailed("Creating ScopedHString failed");
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr<ABI::Windows::UI::Notifications::IToastNotificationFactory>
|
||||||
|
toast_factory;
|
||||||
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
|
Windows::Foundation::GetActivationFactory(toast_str, &toast_factory),
|
||||||
|
"WinAPI: GetActivationFactory failed");
|
||||||
|
|
||||||
|
REPORT_AND_RETURN_IF_FAILED(toast_factory->CreateToastNotification(
|
||||||
|
toast_xml.Get(), &toast_notification_),
|
||||||
|
"WinAPI: CreateToastNotification failed");
|
||||||
|
|
||||||
|
REPORT_AND_RETURN_IF_FAILED(SetupCallbacks(toast_notification_.Get()),
|
||||||
|
"WinAPI: SetupCallbacks failed");
|
||||||
|
|
||||||
|
REPORT_AND_RETURN_IF_FAILED(toast_notifier_->Show(toast_notification_.Get()),
|
||||||
|
"WinAPI: Show failed");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT WindowsToastNotification::GetToastXml(
|
||||||
ABI::Windows::UI::Notifications::IToastNotificationManagerStatics*
|
ABI::Windows::UI::Notifications::IToastNotificationManagerStatics*
|
||||||
toastManager,
|
toastManager,
|
||||||
const std::wstring& title,
|
const std::wstring& title,
|
||||||
|
@ -161,10 +184,15 @@ bool WindowsToastNotification::GetToastXml(
|
||||||
? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01
|
? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01
|
||||||
: ABI::Windows::UI::Notifications::
|
: ABI::Windows::UI::Notifications::
|
||||||
ToastTemplateType_ToastImageAndText01;
|
ToastTemplateType_ToastImageAndText01;
|
||||||
if (FAILED(toast_manager_->GetTemplateContent(template_type, toast_xml)))
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
return false;
|
toast_manager_->GetTemplateContent(template_type, toast_xml),
|
||||||
if (!SetXmlText(*toast_xml, title.empty() ? msg : title))
|
"XML: Fetching XML ToastImageAndText01 template failed");
|
||||||
return false;
|
std::wstring toastMsg = title.empty() ? msg : title;
|
||||||
|
// we can't create an empty notification
|
||||||
|
toastMsg = toastMsg.empty() ? L"[no message]" : toastMsg;
|
||||||
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
|
SetXmlText(*toast_xml, toastMsg),
|
||||||
|
"XML: Filling XML ToastImageAndText01 template failed");
|
||||||
} else {
|
} else {
|
||||||
// Title and body toast.
|
// Title and body toast.
|
||||||
template_type =
|
template_type =
|
||||||
|
@ -172,284 +200,256 @@ bool WindowsToastNotification::GetToastXml(
|
||||||
? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText02
|
? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText02
|
||||||
: ABI::Windows::UI::Notifications::
|
: ABI::Windows::UI::Notifications::
|
||||||
ToastTemplateType_ToastImageAndText02;
|
ToastTemplateType_ToastImageAndText02;
|
||||||
if (FAILED(toastManager->GetTemplateContent(template_type, toast_xml))) {
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
if (IsDebuggingNotifications())
|
toastManager->GetTemplateContent(template_type, toast_xml),
|
||||||
LOG(INFO) << "Fetching XML template failed";
|
"XML: Fetching XML ToastImageAndText02 template failed");
|
||||||
return false;
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
}
|
SetXmlText(*toast_xml, title, msg),
|
||||||
|
"XML: Filling XML ToastImageAndText02 template failed");
|
||||||
if (!SetXmlText(*toast_xml, title, msg)) {
|
|
||||||
if (IsDebuggingNotifications())
|
|
||||||
LOG(INFO) << "Setting text fields on template failed";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the toast's timeout settings
|
// Configure the toast's timeout settings
|
||||||
if (timeout_type == base::ASCIIToUTF16("never")) {
|
if (timeout_type == base::ASCIIToUTF16("never")) {
|
||||||
if (FAILED(SetXmlScenarioReminder(*toast_xml))) {
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
if (IsDebuggingNotifications())
|
(SetXmlScenarioReminder(*toast_xml)),
|
||||||
LOG(INFO) << "Setting \"scenario\" option on notification failed";
|
"XML: Setting \"scenario\" option on notification failed");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the toast's notification sound
|
// Configure the toast's notification sound
|
||||||
if (silent) {
|
if (silent) {
|
||||||
if (FAILED(SetXmlAudioSilent(*toast_xml))) {
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
if (IsDebuggingNotifications()) {
|
SetXmlAudioSilent(*toast_xml),
|
||||||
LOG(INFO) << "Setting \"silent\" option on notification failed";
|
"XML: Setting \"silent\" option on notification failed");
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the toast's image
|
// Configure the toast's image
|
||||||
if (!icon_path.empty())
|
if (!icon_path.empty()) {
|
||||||
return SetXmlImage(*toast_xml, icon_path);
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
|
SetXmlImage(*toast_xml, icon_path),
|
||||||
|
"XML: Setting \"icon\" option on notification failed");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::SetXmlScenarioReminder(IXmlDocument* doc) {
|
HRESULT WindowsToastNotification::SetXmlScenarioReminder(IXmlDocument* doc) {
|
||||||
ScopedHString tag(L"toast");
|
ScopedHString tag(L"toast");
|
||||||
if (!tag.success())
|
if (!tag.success())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ComPtr<IXmlNodeList> node_list;
|
ComPtr<IXmlNodeList> node_list;
|
||||||
if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
|
RETURN_IF_FAILED(doc->GetElementsByTagName(tag, &node_list));
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check that root "toast" node exists
|
// Check that root "toast" node exists
|
||||||
ComPtr<IXmlNode> root;
|
ComPtr<IXmlNode> root;
|
||||||
if (FAILED(node_list->Item(0, &root)))
|
RETURN_IF_FAILED(node_list->Item(0, &root));
|
||||||
return false;
|
|
||||||
|
|
||||||
// get attributes of root "toast" node
|
// get attributes of root "toast" node
|
||||||
ComPtr<IXmlNamedNodeMap> attributes;
|
ComPtr<IXmlNamedNodeMap> attributes;
|
||||||
if (FAILED(root->get_Attributes(&attributes)))
|
RETURN_IF_FAILED(root->get_Attributes(&attributes));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlAttribute> scenario_attribute;
|
ComPtr<IXmlAttribute> scenario_attribute;
|
||||||
ScopedHString scenario_str(L"scenario");
|
ScopedHString scenario_str(L"scenario");
|
||||||
if (FAILED(doc->CreateAttribute(scenario_str, &scenario_attribute)))
|
RETURN_IF_FAILED(doc->CreateAttribute(scenario_str, &scenario_attribute));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> scenario_attribute_node;
|
ComPtr<IXmlNode> scenario_attribute_node;
|
||||||
if (FAILED(scenario_attribute.As(&scenario_attribute_node)))
|
RETURN_IF_FAILED(scenario_attribute.As(&scenario_attribute_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
ScopedHString scenario_value(L"reminder");
|
ScopedHString scenario_value(L"reminder");
|
||||||
if (!scenario_value.success())
|
if (!scenario_value.success())
|
||||||
return false;
|
return E_FAIL;
|
||||||
|
|
||||||
ComPtr<IXmlText> scenario_text;
|
ComPtr<IXmlText> scenario_text;
|
||||||
if (FAILED(doc->CreateTextNode(scenario_value, &scenario_text)))
|
RETURN_IF_FAILED(doc->CreateTextNode(scenario_value, &scenario_text));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> scenario_node;
|
ComPtr<IXmlNode> scenario_node;
|
||||||
if (FAILED(scenario_text.As(&scenario_node)))
|
RETURN_IF_FAILED(scenario_text.As(&scenario_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> child_node;
|
ComPtr<IXmlNode> child_node;
|
||||||
if (FAILED(scenario_attribute_node->AppendChild(scenario_node.Get(),
|
RETURN_IF_FAILED(
|
||||||
&child_node)))
|
scenario_attribute_node->AppendChild(scenario_node.Get(), &child_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> scenario_attribute_pnode;
|
ComPtr<IXmlNode> scenario_attribute_pnode;
|
||||||
return SUCCEEDED(attributes.Get()->SetNamedItem(scenario_attribute_node.Get(),
|
return attributes.Get()->SetNamedItem(scenario_attribute_node.Get(),
|
||||||
&scenario_attribute_pnode));
|
&scenario_attribute_pnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::SetXmlAudioSilent(IXmlDocument* doc) {
|
HRESULT WindowsToastNotification::SetXmlAudioSilent(IXmlDocument* doc) {
|
||||||
ScopedHString tag(L"toast");
|
ScopedHString tag(L"toast");
|
||||||
if (!tag.success())
|
if (!tag.success())
|
||||||
return false;
|
return E_FAIL;
|
||||||
|
|
||||||
ComPtr<IXmlNodeList> node_list;
|
ComPtr<IXmlNodeList> node_list;
|
||||||
if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
|
RETURN_IF_FAILED(doc->GetElementsByTagName(tag, &node_list));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> root;
|
ComPtr<IXmlNode> root;
|
||||||
if (FAILED(node_list->Item(0, &root)))
|
RETURN_IF_FAILED(node_list->Item(0, &root));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlElement> audio_element;
|
ComPtr<IXmlElement> audio_element;
|
||||||
ScopedHString audio_str(L"audio");
|
ScopedHString audio_str(L"audio");
|
||||||
if (FAILED(doc->CreateElement(audio_str, &audio_element)))
|
RETURN_IF_FAILED(doc->CreateElement(audio_str, &audio_element));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> audio_node_tmp;
|
ComPtr<IXmlNode> audio_node_tmp;
|
||||||
if (FAILED(audio_element.As(&audio_node_tmp)))
|
RETURN_IF_FAILED(audio_element.As(&audio_node_tmp));
|
||||||
return false;
|
|
||||||
|
|
||||||
// Append audio node to toast xml
|
// Append audio node to toast xml
|
||||||
ComPtr<IXmlNode> audio_node;
|
ComPtr<IXmlNode> audio_node;
|
||||||
if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node)))
|
RETURN_IF_FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
// Create silent attribute
|
// Create silent attribute
|
||||||
ComPtr<IXmlNamedNodeMap> attributes;
|
ComPtr<IXmlNamedNodeMap> attributes;
|
||||||
if (FAILED(audio_node->get_Attributes(&attributes)))
|
RETURN_IF_FAILED(audio_node->get_Attributes(&attributes));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlAttribute> silent_attribute;
|
ComPtr<IXmlAttribute> silent_attribute;
|
||||||
ScopedHString silent_str(L"silent");
|
ScopedHString silent_str(L"silent");
|
||||||
if (FAILED(doc->CreateAttribute(silent_str, &silent_attribute)))
|
RETURN_IF_FAILED(doc->CreateAttribute(silent_str, &silent_attribute));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> silent_attribute_node;
|
ComPtr<IXmlNode> silent_attribute_node;
|
||||||
if (FAILED(silent_attribute.As(&silent_attribute_node)))
|
RETURN_IF_FAILED(silent_attribute.As(&silent_attribute_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
// Set silent attribute to true
|
// Set silent attribute to true
|
||||||
ScopedHString silent_value(L"true");
|
ScopedHString silent_value(L"true");
|
||||||
if (!silent_value.success())
|
if (!silent_value.success())
|
||||||
return false;
|
return E_FAIL;
|
||||||
|
|
||||||
ComPtr<IXmlText> silent_text;
|
ComPtr<IXmlText> silent_text;
|
||||||
if (FAILED(doc->CreateTextNode(silent_value, &silent_text)))
|
RETURN_IF_FAILED(doc->CreateTextNode(silent_value, &silent_text));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> silent_node;
|
ComPtr<IXmlNode> silent_node;
|
||||||
if (FAILED(silent_text.As(&silent_node)))
|
RETURN_IF_FAILED(silent_text.As(&silent_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> child_node;
|
ComPtr<IXmlNode> child_node;
|
||||||
if (FAILED(
|
RETURN_IF_FAILED(
|
||||||
silent_attribute_node->AppendChild(silent_node.Get(), &child_node)))
|
silent_attribute_node->AppendChild(silent_node.Get(), &child_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> silent_attribute_pnode;
|
ComPtr<IXmlNode> silent_attribute_pnode;
|
||||||
return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(),
|
return attributes.Get()->SetNamedItem(silent_attribute_node.Get(),
|
||||||
&silent_attribute_pnode));
|
&silent_attribute_pnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::SetXmlText(IXmlDocument* doc,
|
HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc,
|
||||||
const std::wstring& text) {
|
const std::wstring& text) {
|
||||||
ScopedHString tag;
|
ScopedHString tag;
|
||||||
ComPtr<IXmlNodeList> node_list;
|
ComPtr<IXmlNodeList> node_list;
|
||||||
if (!GetTextNodeList(&tag, doc, &node_list, 1))
|
RETURN_IF_FAILED(GetTextNodeList(&tag, doc, &node_list, 1));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> node;
|
ComPtr<IXmlNode> node;
|
||||||
if (FAILED(node_list->Item(0, &node)))
|
RETURN_IF_FAILED(node_list->Item(0, &node));
|
||||||
return false;
|
|
||||||
|
|
||||||
return AppendTextToXml(doc, node.Get(), text);
|
return AppendTextToXml(doc, node.Get(), text);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::SetXmlText(IXmlDocument* doc,
|
HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc,
|
||||||
const std::wstring& title,
|
const std::wstring& title,
|
||||||
const std::wstring& body) {
|
const std::wstring& body) {
|
||||||
ScopedHString tag;
|
ScopedHString tag;
|
||||||
ComPtr<IXmlNodeList> node_list;
|
ComPtr<IXmlNodeList> node_list;
|
||||||
if (!GetTextNodeList(&tag, doc, &node_list, 2))
|
RETURN_IF_FAILED(GetTextNodeList(&tag, doc, &node_list, 2));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> node;
|
ComPtr<IXmlNode> node;
|
||||||
if (FAILED(node_list->Item(0, &node)))
|
RETURN_IF_FAILED(node_list->Item(0, &node));
|
||||||
return false;
|
RETURN_IF_FAILED(AppendTextToXml(doc, node.Get(), title));
|
||||||
|
RETURN_IF_FAILED(node_list->Item(1, &node));
|
||||||
if (!AppendTextToXml(doc, node.Get(), title))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (FAILED(node_list->Item(1, &node)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return AppendTextToXml(doc, node.Get(), body);
|
return AppendTextToXml(doc, node.Get(), body);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::SetXmlImage(IXmlDocument* doc,
|
HRESULT WindowsToastNotification::SetXmlImage(IXmlDocument* doc,
|
||||||
const std::wstring& icon_path) {
|
const std::wstring& icon_path) {
|
||||||
ScopedHString tag(L"image");
|
ScopedHString tag(L"image");
|
||||||
if (!tag.success())
|
if (!tag.success())
|
||||||
return false;
|
return E_FAIL;
|
||||||
|
|
||||||
ComPtr<IXmlNodeList> node_list;
|
ComPtr<IXmlNodeList> node_list;
|
||||||
if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
|
RETURN_IF_FAILED(doc->GetElementsByTagName(tag, &node_list));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> image_node;
|
ComPtr<IXmlNode> image_node;
|
||||||
if (FAILED(node_list->Item(0, &image_node)))
|
RETURN_IF_FAILED(node_list->Item(0, &image_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNamedNodeMap> attrs;
|
ComPtr<IXmlNamedNodeMap> attrs;
|
||||||
if (FAILED(image_node->get_Attributes(&attrs)))
|
RETURN_IF_FAILED(image_node->get_Attributes(&attrs));
|
||||||
return false;
|
|
||||||
|
|
||||||
ScopedHString src(L"src");
|
ScopedHString src(L"src");
|
||||||
if (!src.success())
|
if (!src.success())
|
||||||
return false;
|
return E_FAIL;
|
||||||
|
|
||||||
ComPtr<IXmlNode> src_attr;
|
ComPtr<IXmlNode> src_attr;
|
||||||
if (FAILED(attrs->GetNamedItem(src, &src_attr)))
|
RETURN_IF_FAILED(attrs->GetNamedItem(src, &src_attr));
|
||||||
return false;
|
|
||||||
|
|
||||||
ScopedHString img_path(icon_path.c_str());
|
ScopedHString img_path(icon_path.c_str());
|
||||||
if (!img_path.success())
|
if (!img_path.success())
|
||||||
return false;
|
return E_FAIL;
|
||||||
|
|
||||||
ComPtr<IXmlText> src_text;
|
ComPtr<IXmlText> src_text;
|
||||||
if (FAILED(doc->CreateTextNode(img_path, &src_text)))
|
RETURN_IF_FAILED(doc->CreateTextNode(img_path, &src_text));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> src_node;
|
ComPtr<IXmlNode> src_node;
|
||||||
if (FAILED(src_text.As(&src_node)))
|
RETURN_IF_FAILED(src_text.As(&src_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> child_node;
|
ComPtr<IXmlNode> child_node;
|
||||||
return SUCCEEDED(src_attr->AppendChild(src_node.Get(), &child_node));
|
return src_attr->AppendChild(src_node.Get(), &child_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::GetTextNodeList(ScopedHString* tag,
|
HRESULT WindowsToastNotification::GetTextNodeList(ScopedHString* tag,
|
||||||
IXmlDocument* doc,
|
IXmlDocument* doc,
|
||||||
IXmlNodeList** node_list,
|
IXmlNodeList** node_list,
|
||||||
uint32_t req_length) {
|
uint32_t req_length) {
|
||||||
tag->Reset(L"text");
|
tag->Reset(L"text");
|
||||||
if (!tag->success())
|
if (!tag->success())
|
||||||
return false;
|
return E_FAIL;
|
||||||
|
|
||||||
if (FAILED(doc->GetElementsByTagName(*tag, node_list)))
|
RETURN_IF_FAILED(doc->GetElementsByTagName(*tag, node_list));
|
||||||
return false;
|
|
||||||
|
|
||||||
uint32_t node_length;
|
uint32_t node_length;
|
||||||
if (FAILED((*node_list)->get_Length(&node_length)))
|
RETURN_IF_FAILED((*node_list)->get_Length(&node_length));
|
||||||
return false;
|
|
||||||
|
|
||||||
return node_length >= req_length;
|
return node_length >= req_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::AppendTextToXml(IXmlDocument* doc,
|
HRESULT WindowsToastNotification::AppendTextToXml(IXmlDocument* doc,
|
||||||
IXmlNode* node,
|
IXmlNode* node,
|
||||||
const std::wstring& text) {
|
const std::wstring& text) {
|
||||||
ScopedHString str(text);
|
ScopedHString str(text);
|
||||||
if (!str.success())
|
if (!str.success())
|
||||||
return false;
|
return E_FAIL;
|
||||||
|
|
||||||
ComPtr<IXmlText> xml_text;
|
ComPtr<IXmlText> xml_text;
|
||||||
if (FAILED(doc->CreateTextNode(str, &xml_text)))
|
RETURN_IF_FAILED(doc->CreateTextNode(str, &xml_text));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> text_node;
|
ComPtr<IXmlNode> text_node;
|
||||||
if (FAILED(xml_text.As(&text_node)))
|
RETURN_IF_FAILED(xml_text.As(&text_node));
|
||||||
return false;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> append_node;
|
ComPtr<IXmlNode> append_node;
|
||||||
return SUCCEEDED(node->AppendChild(text_node.Get(), &append_node));
|
RETURN_IF_FAILED(node->AppendChild(text_node.Get(), &append_node));
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::SetupCallbacks(
|
HRESULT WindowsToastNotification::XmlDocumentFromString(
|
||||||
|
const wchar_t* xmlString,
|
||||||
|
IXmlDocument** doc) {
|
||||||
|
ComPtr<IXmlDocument> xmlDoc;
|
||||||
|
RETURN_IF_FAILED(Windows::Foundation::ActivateInstance(
|
||||||
|
HStringReference(RuntimeClass_Windows_Data_Xml_Dom_XmlDocument).Get(),
|
||||||
|
&xmlDoc));
|
||||||
|
|
||||||
|
ComPtr<IXmlDocumentIO> docIO;
|
||||||
|
RETURN_IF_FAILED(xmlDoc.As(&docIO));
|
||||||
|
|
||||||
|
RETURN_IF_FAILED(docIO->LoadXml(HStringReference(xmlString).Get()));
|
||||||
|
|
||||||
|
return xmlDoc.CopyTo(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT WindowsToastNotification::SetupCallbacks(
|
||||||
ABI::Windows::UI::Notifications::IToastNotification* toast) {
|
ABI::Windows::UI::Notifications::IToastNotification* toast) {
|
||||||
event_handler_ = Make<ToastEventHandler>(this);
|
event_handler_ = Make<ToastEventHandler>(this);
|
||||||
if (FAILED(toast->add_Activated(event_handler_.Get(), &activated_token_)))
|
RETURN_IF_FAILED(
|
||||||
return false;
|
toast->add_Activated(event_handler_.Get(), &activated_token_));
|
||||||
|
RETURN_IF_FAILED(
|
||||||
if (FAILED(toast->add_Dismissed(event_handler_.Get(), &dismissed_token_)))
|
toast->add_Dismissed(event_handler_.Get(), &dismissed_token_));
|
||||||
return false;
|
RETURN_IF_FAILED(toast->add_Failed(event_handler_.Get(), &failed_token_));
|
||||||
|
return S_OK;
|
||||||
return SUCCEEDED(toast->add_Failed(event_handler_.Get(), &failed_token_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::RemoveCallbacks(
|
bool WindowsToastNotification::RemoveCallbacks(
|
||||||
|
@ -498,11 +498,15 @@ IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||||
IFACEMETHODIMP ToastEventHandler::Invoke(
|
IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||||
ABI::Windows::UI::Notifications::IToastNotification* sender,
|
ABI::Windows::UI::Notifications::IToastNotification* sender,
|
||||||
ABI::Windows::UI::Notifications::IToastFailedEventArgs* e) {
|
ABI::Windows::UI::Notifications::IToastFailedEventArgs* e) {
|
||||||
base::PostTask(
|
HRESULT error;
|
||||||
FROM_HERE, {content::BrowserThread::UI},
|
e->get_ErrorCode(&error);
|
||||||
base::BindOnce(&Notification::NotificationFailed, notification_));
|
std::string errorMessage =
|
||||||
|
"Notification failed. HRESULT:" + std::to_string(error);
|
||||||
|
base::PostTask(FROM_HERE, {content::BrowserThread::UI},
|
||||||
|
base::BindOnce(&Notification::NotificationFailed,
|
||||||
|
notification_, errorMessage));
|
||||||
if (IsDebuggingNotifications())
|
if (IsDebuggingNotifications())
|
||||||
LOG(INFO) << "Notification failed";
|
LOG(INFO) << errorMessage;
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,8 @@ class WindowsToastNotification : public Notification {
|
||||||
private:
|
private:
|
||||||
friend class ToastEventHandler;
|
friend class ToastEventHandler;
|
||||||
|
|
||||||
bool GetToastXml(
|
HRESULT ShowInternal(const NotificationOptions& options);
|
||||||
|
HRESULT GetToastXml(
|
||||||
ABI::Windows::UI::Notifications::IToastNotificationManagerStatics*
|
ABI::Windows::UI::Notifications::IToastNotificationManagerStatics*
|
||||||
toastManager,
|
toastManager,
|
||||||
const std::wstring& title,
|
const std::wstring& title,
|
||||||
|
@ -66,23 +67,27 @@ class WindowsToastNotification : public Notification {
|
||||||
const std::wstring& timeout_type,
|
const std::wstring& timeout_type,
|
||||||
const bool silent,
|
const bool silent,
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlDocument** toastXml);
|
ABI::Windows::Data::Xml::Dom::IXmlDocument** toastXml);
|
||||||
bool SetXmlAudioSilent(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
HRESULT SetXmlAudioSilent(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
||||||
bool SetXmlScenarioReminder(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
HRESULT SetXmlScenarioReminder(
|
||||||
bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
||||||
const std::wstring& text);
|
HRESULT SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
||||||
bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
const std::wstring& text);
|
||||||
const std::wstring& title,
|
HRESULT SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
||||||
const std::wstring& body);
|
const std::wstring& title,
|
||||||
bool SetXmlImage(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
const std::wstring& body);
|
||||||
const std::wstring& icon_path);
|
HRESULT SetXmlImage(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
||||||
bool GetTextNodeList(ScopedHString* tag,
|
const std::wstring& icon_path);
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
HRESULT GetTextNodeList(ScopedHString* tag,
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlNodeList** nodeList,
|
ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
||||||
uint32_t reqLength);
|
ABI::Windows::Data::Xml::Dom::IXmlNodeList** nodeList,
|
||||||
bool AppendTextToXml(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
uint32_t reqLength);
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlNode* node,
|
HRESULT AppendTextToXml(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
||||||
const std::wstring& text);
|
ABI::Windows::Data::Xml::Dom::IXmlNode* node,
|
||||||
bool SetupCallbacks(
|
const std::wstring& text);
|
||||||
|
HRESULT XmlDocumentFromString(
|
||||||
|
const wchar_t* xmlString,
|
||||||
|
ABI::Windows::Data::Xml::Dom::IXmlDocument** doc);
|
||||||
|
HRESULT SetupCallbacks(
|
||||||
ABI::Windows::UI::Notifications::IToastNotification* toast);
|
ABI::Windows::UI::Notifications::IToastNotification* toast);
|
||||||
bool RemoveCallbacks(
|
bool RemoveCallbacks(
|
||||||
ABI::Windows::UI::Notifications::IToastNotification* toast);
|
ABI::Windows::UI::Notifications::IToastNotification* toast);
|
||||||
|
|
|
@ -108,6 +108,14 @@ describe('Notification module', () => {
|
||||||
n.close();
|
n.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ifit(process.platform === 'win32')('inits, gets and sets custom xml', () => {
|
||||||
|
const n = new Notification({
|
||||||
|
toastXml: '<xml/>'
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(n.toastXml).to.equal('<xml/>');
|
||||||
|
});
|
||||||
|
|
||||||
ifit(process.platform === 'darwin')('emits show and close events', async () => {
|
ifit(process.platform === 'darwin')('emits show and close events', async () => {
|
||||||
const n = new Notification({
|
const n = new Notification({
|
||||||
title: 'test notification',
|
title: 'test notification',
|
||||||
|
@ -126,5 +134,16 @@ describe('Notification module', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ifit(process.platform === 'win32')('emits failed event', async () => {
|
||||||
|
const n = new Notification({
|
||||||
|
toastXml: 'not xml'
|
||||||
|
});
|
||||||
|
{
|
||||||
|
const e = emittedOnce(n, 'failed');
|
||||||
|
n.show();
|
||||||
|
await e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// TODO(sethlu): Find way to test init with notification icon?
|
// TODO(sethlu): Find way to test init with notification icon?
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue