refactor: use XmlWriter for Windows toasts (#48130)
refactor: use XmlWriter for Windows toasts Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
This commit is contained in:
parent
0c2271a515
commit
f28d08ad86
3 changed files with 129 additions and 401 deletions
1
BUILD.gn
1
BUILD.gn
|
|
@ -686,6 +686,7 @@ source_set("electron_lib") {
|
||||||
deps += [
|
deps += [
|
||||||
"//components/app_launch_prefetch",
|
"//components/app_launch_prefetch",
|
||||||
"//components/crash/core/app:crash_export_thunks",
|
"//components/crash/core/app:crash_export_thunks",
|
||||||
|
"//third_party/libxml:xml_writer",
|
||||||
"//ui/native_theme:native_theme_browser",
|
"//ui/native_theme:native_theme_browser",
|
||||||
"//ui/wm",
|
"//ui/wm",
|
||||||
"//ui/wm/public",
|
"//ui/wm/public",
|
||||||
|
|
|
||||||
|
|
@ -17,24 +17,21 @@
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/strcat.h"
|
#include "base/strings/strcat.h"
|
||||||
#include "base/strings/string_number_conversions.h"
|
#include "base/strings/string_number_conversions.h"
|
||||||
|
#include "base/strings/string_util.h"
|
||||||
#include "base/strings/string_util_win.h"
|
#include "base/strings/string_util_win.h"
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "content/public/browser/browser_task_traits.h"
|
#include "content/public/browser/browser_task_traits.h"
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "shell/browser/notifications/notification_delegate.h"
|
#include "shell/browser/notifications/notification_delegate.h"
|
||||||
#include "shell/browser/notifications/win/notification_presenter_win.h"
|
#include "shell/browser/notifications/win/notification_presenter_win.h"
|
||||||
#include "shell/browser/win/scoped_hstring.h"
|
#include "shell/browser/win/scoped_hstring.h"
|
||||||
#include "shell/common/application_info.h"
|
#include "shell/common/application_info.h"
|
||||||
|
#include "third_party/libxml/chromium/xml_writer.h"
|
||||||
#include "ui/base/l10n/l10n_util_win.h"
|
#include "ui/base/l10n/l10n_util_win.h"
|
||||||
#include "ui/strings/grit/ui_strings.h"
|
#include "ui/strings/grit/ui_strings.h"
|
||||||
|
|
||||||
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::IXmlDocumentIO;
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlElement;
|
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlNamedNodeMap;
|
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlNode;
|
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlNodeList;
|
|
||||||
using ABI::Windows::Data::Xml::Dom::IXmlText;
|
|
||||||
using Microsoft::WRL::Wrappers::HStringReference;
|
using Microsoft::WRL::Wrappers::HStringReference;
|
||||||
|
|
||||||
namespace winui = ABI::Windows::UI;
|
namespace winui = ABI::Windows::UI;
|
||||||
|
|
@ -74,6 +71,43 @@ std::wstring GetTag(const std::string_view notification_id) {
|
||||||
return base::NumberToWString(base::FastHash(notification_id));
|
return base::NumberToWString(base::FastHash(notification_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr char kToast[] = "toast";
|
||||||
|
constexpr char kVisual[] = "visual";
|
||||||
|
constexpr char kBinding[] = "binding";
|
||||||
|
constexpr char kTemplate[] = "template";
|
||||||
|
constexpr char kToastText01[] = "ToastText01";
|
||||||
|
constexpr char kToastText02[] = "ToastText02";
|
||||||
|
constexpr char kToastImageAndText01[] = "ToastImageAndText01";
|
||||||
|
constexpr char kToastImageAndText02[] = "ToastImageAndText02";
|
||||||
|
constexpr char kText[] = "text";
|
||||||
|
constexpr char kImage[] = "image";
|
||||||
|
constexpr char kPlacement[] = "placement";
|
||||||
|
constexpr char kAppLogoOverride[] = "appLogoOverride";
|
||||||
|
constexpr char kHintCrop[] = "hint-crop";
|
||||||
|
constexpr char kHintCropNone[] = "none";
|
||||||
|
constexpr char kSrc[] = "src";
|
||||||
|
constexpr char kAudio[] = "audio";
|
||||||
|
constexpr char kSilent[] = "silent";
|
||||||
|
constexpr char kTrue[] = "true";
|
||||||
|
constexpr char kID[] = "id";
|
||||||
|
constexpr char kScenario[] = "scenario";
|
||||||
|
constexpr char kReminder[] = "reminder";
|
||||||
|
constexpr char kActions[] = "actions";
|
||||||
|
constexpr char kAction[] = "action";
|
||||||
|
constexpr char kActivationType[] = "activationType";
|
||||||
|
constexpr char kSystem[] = "system";
|
||||||
|
constexpr char kArguments[] = "arguments";
|
||||||
|
constexpr char kDismiss[] = "dismiss";
|
||||||
|
// The XML version header that has to be stripped from the output.
|
||||||
|
constexpr char kXmlVersionHeader[] = "<?xml version=\"1.0\"?>\n";
|
||||||
|
|
||||||
|
const char* GetTemplateType(bool two_lines, bool has_icon) {
|
||||||
|
if (has_icon) {
|
||||||
|
return two_lines ? kToastImageAndText02 : kToastImageAndText01;
|
||||||
|
}
|
||||||
|
return two_lines ? kToastText02 : kToastText01;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
@ -171,10 +205,12 @@ HRESULT WindowsToastNotification::ShowInternal(
|
||||||
auto* presenter_win = static_cast<NotificationPresenterWin*>(presenter());
|
auto* presenter_win = static_cast<NotificationPresenterWin*>(presenter());
|
||||||
std::wstring icon_path =
|
std::wstring icon_path =
|
||||||
presenter_win->SaveIconToFilesystem(options.icon, options.icon_url);
|
presenter_win->SaveIconToFilesystem(options.icon, options.icon_url);
|
||||||
|
std::u16string toast_xml_str =
|
||||||
|
GetToastXml(options.title, options.msg, icon_path, options.timeout_type,
|
||||||
|
options.silent);
|
||||||
REPORT_AND_RETURN_IF_FAILED(
|
REPORT_AND_RETURN_IF_FAILED(
|
||||||
GetToastXml(toast_manager_.Get(), options.title, options.msg, icon_path,
|
XmlDocumentFromString(base::as_wcstr(toast_xml_str), &toast_xml),
|
||||||
options.timeout_type, options.silent, &toast_xml),
|
"XML: Invalid XML");
|
||||||
"XML: Failed to create XML document");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedHString toast_str(
|
ScopedHString toast_str(
|
||||||
|
|
@ -214,382 +250,95 @@ HRESULT WindowsToastNotification::ShowInternal(
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::GetToastXml(
|
std::u16string WindowsToastNotification::GetToastXml(
|
||||||
winui::Notifications::IToastNotificationManagerStatics* toastManager,
|
|
||||||
const std::u16string& title,
|
const std::u16string& title,
|
||||||
const std::u16string& msg,
|
const std::u16string& msg,
|
||||||
const std::wstring& icon_path,
|
const std::wstring& icon_path,
|
||||||
const std::u16string& timeout_type,
|
const std::u16string& timeout_type,
|
||||||
bool silent,
|
bool silent) {
|
||||||
IXmlDocument** toast_xml) {
|
XmlWriter xml_writer;
|
||||||
winui::Notifications::ToastTemplateType template_type;
|
xml_writer.StartWriting();
|
||||||
|
|
||||||
|
// <toast ...>
|
||||||
|
xml_writer.StartElement(kToast);
|
||||||
|
|
||||||
|
const bool is_reminder = (timeout_type == u"never");
|
||||||
|
if (is_reminder) {
|
||||||
|
xml_writer.AddAttribute(kScenario, kReminder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// <visual>
|
||||||
|
xml_writer.StartElement(kVisual);
|
||||||
|
// <binding template="<template>">
|
||||||
|
xml_writer.StartElement(kBinding);
|
||||||
|
const bool two_lines = (!title.empty() && !msg.empty());
|
||||||
|
xml_writer.AddAttribute(kTemplate,
|
||||||
|
GetTemplateType(two_lines, !icon_path.empty()));
|
||||||
|
|
||||||
|
// Add text nodes.
|
||||||
|
std::u16string line1;
|
||||||
|
std::u16string line2;
|
||||||
if (title.empty() || msg.empty()) {
|
if (title.empty() || msg.empty()) {
|
||||||
// Single line toast.
|
line1 = title.empty() ? msg : title;
|
||||||
template_type =
|
if (line1.empty())
|
||||||
icon_path.empty()
|
line1 = u"[no message]";
|
||||||
? winui::Notifications::ToastTemplateType_ToastText01
|
xml_writer.StartElement(kText);
|
||||||
: winui::Notifications::ToastTemplateType_ToastImageAndText01;
|
xml_writer.AddAttribute(kID, "1");
|
||||||
REPORT_AND_RETURN_IF_FAILED(
|
xml_writer.AppendElementContent(base::UTF16ToUTF8(line1));
|
||||||
toast_manager_->GetTemplateContent(template_type, toast_xml),
|
xml_writer.EndElement(); // </text>
|
||||||
"XML: Fetching XML ToastImageAndText01 template failed");
|
|
||||||
std::u16string toastMsg = title.empty() ? msg : title;
|
|
||||||
// we can't create an empty notification
|
|
||||||
toastMsg = toastMsg.empty() ? u"[no message]" : toastMsg;
|
|
||||||
REPORT_AND_RETURN_IF_FAILED(
|
|
||||||
SetXmlText(*toast_xml, toastMsg),
|
|
||||||
"XML: Filling XML ToastImageAndText01 template failed");
|
|
||||||
} else {
|
} else {
|
||||||
// Title and body toast.
|
line1 = title;
|
||||||
template_type =
|
line2 = msg;
|
||||||
icon_path.empty()
|
xml_writer.StartElement(kText);
|
||||||
? winui::Notifications::ToastTemplateType_ToastText02
|
xml_writer.AddAttribute(kID, "1");
|
||||||
: winui::Notifications::ToastTemplateType_ToastImageAndText02;
|
xml_writer.AppendElementContent(base::UTF16ToUTF8(line1));
|
||||||
REPORT_AND_RETURN_IF_FAILED(
|
xml_writer.EndElement();
|
||||||
toastManager->GetTemplateContent(template_type, toast_xml),
|
xml_writer.StartElement(kText);
|
||||||
"XML: Fetching XML ToastImageAndText02 template failed");
|
xml_writer.AddAttribute(kID, "2");
|
||||||
REPORT_AND_RETURN_IF_FAILED(
|
xml_writer.AppendElementContent(base::UTF16ToUTF8(line2));
|
||||||
SetXmlText(*toast_xml, title, msg),
|
xml_writer.EndElement();
|
||||||
"XML: Filling XML ToastImageAndText02 template failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the toast's timeout settings
|
// Optional icon as app logo override (small icon).
|
||||||
if (timeout_type == u"never") {
|
|
||||||
REPORT_AND_RETURN_IF_FAILED(
|
|
||||||
(SetXmlScenarioReminder(*toast_xml)),
|
|
||||||
"XML: Setting \"scenario\" option on notification failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure the toast's notification sound
|
|
||||||
if (silent) {
|
|
||||||
REPORT_AND_RETURN_IF_FAILED(
|
|
||||||
SetXmlAudioSilent(*toast_xml),
|
|
||||||
"XML: Setting \"silent\" option on notification failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure the toast's image
|
|
||||||
if (!icon_path.empty()) {
|
if (!icon_path.empty()) {
|
||||||
REPORT_AND_RETURN_IF_FAILED(
|
xml_writer.StartElement(kImage);
|
||||||
SetXmlImage(*toast_xml, icon_path),
|
xml_writer.AddAttribute(kPlacement, kAppLogoOverride);
|
||||||
"XML: Setting \"icon\" option on notification failed");
|
xml_writer.AddAttribute(kHintCrop, kHintCropNone);
|
||||||
|
xml_writer.AddAttribute(kSrc, base::WideToUTF8(icon_path));
|
||||||
|
xml_writer.EndElement(); // </image>
|
||||||
}
|
}
|
||||||
|
|
||||||
return S_OK;
|
xml_writer.EndElement(); // </binding>
|
||||||
|
xml_writer.EndElement(); // </visual>
|
||||||
|
|
||||||
|
// <actions> (only to ensure reminder has a dismiss button).
|
||||||
|
if (is_reminder) {
|
||||||
|
xml_writer.StartElement(kActions);
|
||||||
|
xml_writer.StartElement(kAction);
|
||||||
|
xml_writer.AddAttribute(kActivationType, kSystem);
|
||||||
|
xml_writer.AddAttribute(kArguments, kDismiss);
|
||||||
|
xml_writer.AddAttribute(
|
||||||
|
"content", base::WideToUTF8(l10n_util::GetWideString(IDS_APP_CLOSE)));
|
||||||
|
xml_writer.EndElement(); // </action>
|
||||||
|
xml_writer.EndElement(); // </actions>
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::SetXmlScenarioReminder(IXmlDocument* doc) {
|
// Silent audio if requested.
|
||||||
ScopedHString tag(L"toast");
|
if (silent) {
|
||||||
if (!tag.success())
|
xml_writer.StartElement(kAudio);
|
||||||
return false;
|
xml_writer.AddAttribute(kSilent, kTrue);
|
||||||
|
xml_writer.EndElement(); // </audio>
|
||||||
ComPtr<IXmlNodeList> node_list;
|
|
||||||
RETURN_IF_FAILED(doc->GetElementsByTagName(tag, &node_list));
|
|
||||||
|
|
||||||
// Check that root "toast" node exists
|
|
||||||
ComPtr<IXmlNode> root;
|
|
||||||
RETURN_IF_FAILED(node_list->Item(0, &root));
|
|
||||||
|
|
||||||
// get attributes of root "toast" node
|
|
||||||
ComPtr<IXmlNamedNodeMap> toast_attributes;
|
|
||||||
RETURN_IF_FAILED(root->get_Attributes(&toast_attributes));
|
|
||||||
|
|
||||||
ComPtr<IXmlAttribute> scenario_attribute;
|
|
||||||
ScopedHString scenario_str(L"scenario");
|
|
||||||
RETURN_IF_FAILED(doc->CreateAttribute(scenario_str, &scenario_attribute));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> scenario_attribute_node;
|
|
||||||
RETURN_IF_FAILED(scenario_attribute.As(&scenario_attribute_node));
|
|
||||||
|
|
||||||
ScopedHString scenario_value(L"reminder");
|
|
||||||
if (!scenario_value.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlText> scenario_text;
|
|
||||||
RETURN_IF_FAILED(doc->CreateTextNode(scenario_value, &scenario_text));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> scenario_node;
|
|
||||||
RETURN_IF_FAILED(scenario_text.As(&scenario_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> scenario_backup_node;
|
|
||||||
RETURN_IF_FAILED(scenario_attribute_node->AppendChild(scenario_node.Get(),
|
|
||||||
&scenario_backup_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> scenario_attribute_pnode;
|
|
||||||
RETURN_IF_FAILED(toast_attributes.Get()->SetNamedItem(
|
|
||||||
scenario_attribute_node.Get(), &scenario_attribute_pnode));
|
|
||||||
|
|
||||||
// Create "actions" wrapper
|
|
||||||
ComPtr<IXmlElement> actions_wrapper_element;
|
|
||||||
ScopedHString actions_wrapper_str(L"actions");
|
|
||||||
RETURN_IF_FAILED(
|
|
||||||
doc->CreateElement(actions_wrapper_str, &actions_wrapper_element));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> actions_wrapper_node_tmp;
|
|
||||||
RETURN_IF_FAILED(actions_wrapper_element.As(&actions_wrapper_node_tmp));
|
|
||||||
|
|
||||||
// Append actions wrapper node to toast xml
|
|
||||||
ComPtr<IXmlNode> actions_wrapper_node;
|
|
||||||
RETURN_IF_FAILED(
|
|
||||||
root->AppendChild(actions_wrapper_node_tmp.Get(), &actions_wrapper_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNamedNodeMap> attributes_actions_wrapper;
|
|
||||||
RETURN_IF_FAILED(
|
|
||||||
actions_wrapper_node->get_Attributes(&attributes_actions_wrapper));
|
|
||||||
|
|
||||||
// Add a "Dismiss" button
|
|
||||||
// Create "action" tag
|
|
||||||
ComPtr<IXmlElement> action_element;
|
|
||||||
ScopedHString action_str(L"action");
|
|
||||||
RETURN_IF_FAILED(doc->CreateElement(action_str, &action_element));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> action_node_tmp;
|
|
||||||
RETURN_IF_FAILED(action_element.As(&action_node_tmp));
|
|
||||||
|
|
||||||
// Append action node to actions wrapper in toast xml
|
|
||||||
ComPtr<IXmlNode> action_node;
|
|
||||||
RETURN_IF_FAILED(
|
|
||||||
actions_wrapper_node->AppendChild(action_node_tmp.Get(), &action_node));
|
|
||||||
|
|
||||||
// Setup attributes for action
|
|
||||||
ComPtr<IXmlNamedNodeMap> action_attributes;
|
|
||||||
RETURN_IF_FAILED(action_node->get_Attributes(&action_attributes));
|
|
||||||
|
|
||||||
// Create activationType attribute
|
|
||||||
ComPtr<IXmlAttribute> activation_type_attribute;
|
|
||||||
ScopedHString activation_type_str(L"activationType");
|
|
||||||
RETURN_IF_FAILED(
|
|
||||||
doc->CreateAttribute(activation_type_str, &activation_type_attribute));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> activation_type_attribute_node;
|
|
||||||
RETURN_IF_FAILED(
|
|
||||||
activation_type_attribute.As(&activation_type_attribute_node));
|
|
||||||
|
|
||||||
// Set activationType attribute to system
|
|
||||||
ScopedHString activation_type_value(L"system");
|
|
||||||
if (!activation_type_value.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlText> activation_type_text;
|
|
||||||
RETURN_IF_FAILED(
|
|
||||||
doc->CreateTextNode(activation_type_value, &activation_type_text));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> activation_type_node;
|
|
||||||
RETURN_IF_FAILED(activation_type_text.As(&activation_type_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> activation_type_backup_node;
|
|
||||||
RETURN_IF_FAILED(activation_type_attribute_node->AppendChild(
|
|
||||||
activation_type_node.Get(), &activation_type_backup_node));
|
|
||||||
|
|
||||||
// Add activation type to the action attributes
|
|
||||||
ComPtr<IXmlNode> activation_type_attribute_pnode;
|
|
||||||
RETURN_IF_FAILED(action_attributes.Get()->SetNamedItem(
|
|
||||||
activation_type_attribute_node.Get(), &activation_type_attribute_pnode));
|
|
||||||
|
|
||||||
// Create arguments attribute
|
|
||||||
ComPtr<IXmlAttribute> arguments_attribute;
|
|
||||||
ScopedHString arguments_str(L"arguments");
|
|
||||||
RETURN_IF_FAILED(doc->CreateAttribute(arguments_str, &arguments_attribute));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> arguments_attribute_node;
|
|
||||||
RETURN_IF_FAILED(arguments_attribute.As(&arguments_attribute_node));
|
|
||||||
|
|
||||||
// Set arguments attribute to dismiss
|
|
||||||
ScopedHString arguments_value(L"dismiss");
|
|
||||||
if (!arguments_value.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlText> arguments_text;
|
|
||||||
RETURN_IF_FAILED(doc->CreateTextNode(arguments_value, &arguments_text));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> arguments_node;
|
|
||||||
RETURN_IF_FAILED(arguments_text.As(&arguments_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> arguments_backup_node;
|
|
||||||
RETURN_IF_FAILED(arguments_attribute_node->AppendChild(
|
|
||||||
arguments_node.Get(), &arguments_backup_node));
|
|
||||||
|
|
||||||
// Add arguments to the action attributes
|
|
||||||
ComPtr<IXmlNode> arguments_attribute_pnode;
|
|
||||||
RETURN_IF_FAILED(action_attributes.Get()->SetNamedItem(
|
|
||||||
arguments_attribute_node.Get(), &arguments_attribute_pnode));
|
|
||||||
|
|
||||||
// Create content attribute
|
|
||||||
ComPtr<IXmlAttribute> content_attribute;
|
|
||||||
ScopedHString content_str(L"content");
|
|
||||||
RETURN_IF_FAILED(doc->CreateAttribute(content_str, &content_attribute));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> content_attribute_node;
|
|
||||||
RETURN_IF_FAILED(content_attribute.As(&content_attribute_node));
|
|
||||||
|
|
||||||
// Set content attribute to Dismiss
|
|
||||||
ScopedHString content_value(l10n_util::GetWideString(IDS_APP_CLOSE));
|
|
||||||
if (!content_value.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlText> content_text;
|
|
||||||
RETURN_IF_FAILED(doc->CreateTextNode(content_value, &content_text));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> content_node;
|
|
||||||
RETURN_IF_FAILED(content_text.As(&content_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> content_backup_node;
|
|
||||||
RETURN_IF_FAILED(content_attribute_node->AppendChild(content_node.Get(),
|
|
||||||
&content_backup_node));
|
|
||||||
|
|
||||||
// Add content to the action attributes
|
|
||||||
ComPtr<IXmlNode> content_attribute_pnode;
|
|
||||||
return action_attributes.Get()->SetNamedItem(content_attribute_node.Get(),
|
|
||||||
&content_attribute_pnode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::SetXmlAudioSilent(IXmlDocument* doc) {
|
xml_writer.EndElement(); // </toast>
|
||||||
ScopedHString tag(L"toast");
|
|
||||||
if (!tag.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlNodeList> node_list;
|
xml_writer.StopWriting();
|
||||||
RETURN_IF_FAILED(doc->GetElementsByTagName(tag, &node_list));
|
std::string xml = xml_writer.GetWrittenString();
|
||||||
|
if (base::StartsWith(xml, kXmlVersionHeader, base::CompareCase::SENSITIVE)) {
|
||||||
ComPtr<IXmlNode> root;
|
xml.erase(0, sizeof(kXmlVersionHeader) - 1);
|
||||||
RETURN_IF_FAILED(node_list->Item(0, &root));
|
|
||||||
|
|
||||||
ComPtr<IXmlElement> audio_element;
|
|
||||||
ScopedHString audio_str(L"audio");
|
|
||||||
RETURN_IF_FAILED(doc->CreateElement(audio_str, &audio_element));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> audio_node_tmp;
|
|
||||||
RETURN_IF_FAILED(audio_element.As(&audio_node_tmp));
|
|
||||||
|
|
||||||
// Append audio node to toast xml
|
|
||||||
ComPtr<IXmlNode> audio_node;
|
|
||||||
RETURN_IF_FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node));
|
|
||||||
|
|
||||||
// Create silent attribute
|
|
||||||
ComPtr<IXmlNamedNodeMap> attributes;
|
|
||||||
RETURN_IF_FAILED(audio_node->get_Attributes(&attributes));
|
|
||||||
|
|
||||||
ComPtr<IXmlAttribute> silent_attribute;
|
|
||||||
ScopedHString silent_str(L"silent");
|
|
||||||
RETURN_IF_FAILED(doc->CreateAttribute(silent_str, &silent_attribute));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> silent_attribute_node;
|
|
||||||
RETURN_IF_FAILED(silent_attribute.As(&silent_attribute_node));
|
|
||||||
|
|
||||||
// Set silent attribute to true
|
|
||||||
ScopedHString silent_value(L"true");
|
|
||||||
if (!silent_value.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlText> silent_text;
|
|
||||||
RETURN_IF_FAILED(doc->CreateTextNode(silent_value, &silent_text));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> silent_node;
|
|
||||||
RETURN_IF_FAILED(silent_text.As(&silent_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> child_node;
|
|
||||||
RETURN_IF_FAILED(
|
|
||||||
silent_attribute_node->AppendChild(silent_node.Get(), &child_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> silent_attribute_pnode;
|
|
||||||
return attributes.Get()->SetNamedItem(silent_attribute_node.Get(),
|
|
||||||
&silent_attribute_pnode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc,
|
return base::UTF8ToUTF16(xml);
|
||||||
const std::u16string& text) {
|
|
||||||
ScopedHString tag;
|
|
||||||
ComPtr<IXmlNodeList> node_list;
|
|
||||||
RETURN_IF_FAILED(GetTextNodeList(&tag, doc, &node_list, 1));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> node;
|
|
||||||
RETURN_IF_FAILED(node_list->Item(0, &node));
|
|
||||||
|
|
||||||
return AppendTextToXml(doc, node.Get(), text);
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc,
|
|
||||||
const std::u16string& title,
|
|
||||||
const std::u16string& body) {
|
|
||||||
ScopedHString tag;
|
|
||||||
ComPtr<IXmlNodeList> node_list;
|
|
||||||
RETURN_IF_FAILED(GetTextNodeList(&tag, doc, &node_list, 2));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> node;
|
|
||||||
RETURN_IF_FAILED(node_list->Item(0, &node));
|
|
||||||
RETURN_IF_FAILED(AppendTextToXml(doc, node.Get(), title));
|
|
||||||
RETURN_IF_FAILED(node_list->Item(1, &node));
|
|
||||||
|
|
||||||
return AppendTextToXml(doc, node.Get(), body);
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::SetXmlImage(IXmlDocument* doc,
|
|
||||||
const std::wstring& icon_path) {
|
|
||||||
ScopedHString tag(L"image");
|
|
||||||
if (!tag.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlNodeList> node_list;
|
|
||||||
RETURN_IF_FAILED(doc->GetElementsByTagName(tag, &node_list));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> image_node;
|
|
||||||
RETURN_IF_FAILED(node_list->Item(0, &image_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNamedNodeMap> attrs;
|
|
||||||
RETURN_IF_FAILED(image_node->get_Attributes(&attrs));
|
|
||||||
|
|
||||||
ScopedHString src(L"src");
|
|
||||||
if (!src.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> src_attr;
|
|
||||||
RETURN_IF_FAILED(attrs->GetNamedItem(src, &src_attr));
|
|
||||||
|
|
||||||
const ScopedHString img_path{icon_path};
|
|
||||||
if (!img_path.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlText> src_text;
|
|
||||||
RETURN_IF_FAILED(doc->CreateTextNode(img_path, &src_text));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> src_node;
|
|
||||||
RETURN_IF_FAILED(src_text.As(&src_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> child_node;
|
|
||||||
return src_attr->AppendChild(src_node.Get(), &child_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::GetTextNodeList(ScopedHString* tag,
|
|
||||||
IXmlDocument* doc,
|
|
||||||
IXmlNodeList** node_list,
|
|
||||||
uint32_t req_length) {
|
|
||||||
tag->Reset(L"text");
|
|
||||||
if (!tag->success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
RETURN_IF_FAILED(doc->GetElementsByTagName(*tag, node_list));
|
|
||||||
|
|
||||||
uint32_t node_length;
|
|
||||||
RETURN_IF_FAILED((*node_list)->get_Length(&node_length));
|
|
||||||
|
|
||||||
return node_length >= req_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::AppendTextToXml(IXmlDocument* doc,
|
|
||||||
IXmlNode* node,
|
|
||||||
const std::u16string& text) {
|
|
||||||
ScopedHString str(base::as_wcstr(text));
|
|
||||||
if (!str.success())
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
ComPtr<IXmlText> xml_text;
|
|
||||||
RETURN_IF_FAILED(doc->CreateTextNode(str, &xml_text));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> text_node;
|
|
||||||
RETURN_IF_FAILED(xml_text.As(&text_node));
|
|
||||||
|
|
||||||
ComPtr<IXmlNode> append_node;
|
|
||||||
RETURN_IF_FAILED(node->AppendChild(text_node.Get(), &append_node));
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT WindowsToastNotification::XmlDocumentFromString(
|
HRESULT WindowsToastNotification::XmlDocumentFromString(
|
||||||
|
|
|
||||||
|
|
@ -58,33 +58,11 @@ class WindowsToastNotification : public Notification {
|
||||||
friend class ToastEventHandler;
|
friend class ToastEventHandler;
|
||||||
|
|
||||||
HRESULT ShowInternal(const NotificationOptions& options);
|
HRESULT ShowInternal(const NotificationOptions& options);
|
||||||
HRESULT GetToastXml(
|
std::u16string GetToastXml(const std::u16string& title,
|
||||||
ABI::Windows::UI::Notifications::IToastNotificationManagerStatics*
|
|
||||||
toastManager,
|
|
||||||
const std::u16string& title,
|
|
||||||
const std::u16string& msg,
|
const std::u16string& msg,
|
||||||
const std::wstring& icon_path,
|
const std::wstring& icon_path,
|
||||||
const std::u16string& timeout_type,
|
const std::u16string& timeout_type,
|
||||||
const bool silent,
|
const bool silent);
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlDocument** toast_xml);
|
|
||||||
HRESULT SetXmlAudioSilent(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
|
||||||
HRESULT SetXmlScenarioReminder(
|
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
|
||||||
HRESULT SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
|
||||||
const std::u16string& text);
|
|
||||||
HRESULT SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
|
||||||
const std::u16string& title,
|
|
||||||
const std::u16string& body);
|
|
||||||
HRESULT SetXmlImage(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
|
||||||
const std::wstring& icon_path);
|
|
||||||
HRESULT GetTextNodeList(
|
|
||||||
ScopedHString* tag,
|
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlNodeList** node_list,
|
|
||||||
uint32_t req_length);
|
|
||||||
HRESULT AppendTextToXml(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlNode* node,
|
|
||||||
const std::u16string& text);
|
|
||||||
HRESULT XmlDocumentFromString(
|
HRESULT XmlDocumentFromString(
|
||||||
const wchar_t* xmlString,
|
const wchar_t* xmlString,
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlDocument** doc);
|
ABI::Windows::Data::Xml::Dom::IXmlDocument** doc);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue