1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <string>
6
7 #include "win8/metro_driver/stdafx.h"
8 #include "win8/metro_driver/toast_notification_handler.h"
9
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/installer/util/browser_distribution.h"
15 #include "chrome/installer/util/install_util.h"
16 #include "chrome/installer/util/shell_util.h"
17
18 #include "win8/metro_driver/winrt_utils.h"
19
20 typedef winfoundtn::ITypedEventHandler<
21 winui::Notifications::ToastNotification*, IInspectable*>
22 ToastActivationHandler;
23
24 typedef winfoundtn::ITypedEventHandler<
25 winui::Notifications::ToastNotification*,
26 winui::Notifications::ToastDismissedEventArgs*> ToastDismissedHandler;
27
28 namespace {
29
30 // Helper function to return the text node root identified by the index passed
31 // in.
GetTextNodeRoot(unsigned int index,winxml::Dom::IXmlDocument * xml_doc,winxml::Dom::IXmlNode ** node)32 HRESULT GetTextNodeRoot(
33 unsigned int index,
34 winxml::Dom::IXmlDocument* xml_doc,
35 winxml::Dom::IXmlNode** node) {
36 DCHECK(xml_doc);
37 DCHECK(node);
38
39 mswr::ComPtr<winxml::Dom::IXmlElement> document_element;
40 HRESULT hr = xml_doc->get_DocumentElement(&document_element);
41 CheckHR(hr);
42
43 mswr::ComPtr<winxml::Dom::IXmlNodeList> elements;
44 mswrw::HString tag_name;
45 tag_name.Attach(MakeHString(L"text"));
46 hr = document_element->GetElementsByTagName(tag_name.Get(),
47 &elements);
48 CheckHR(hr);
49
50 unsigned int count = 0;
51 elements->get_Length(&count);
52
53 if (index > count) {
54 DVLOG(1) << "Invalid text node index passed in : " << index;
55 return E_FAIL;
56 }
57 hr = elements->Item(index, node);
58 CheckHR(hr);
59 return hr;
60 }
61
62 // Helper function to append a text element to the text section in the
63 // XML document passed in.
64 // The index parameter identifies which text node we append to.
CreateTextNode(winxml::Dom::IXmlDocument * xml_doc,int index,const base::string16 & text_string)65 HRESULT CreateTextNode(winxml::Dom::IXmlDocument* xml_doc,
66 int index,
67 const base::string16& text_string) {
68 DCHECK(xml_doc);
69
70 mswr::ComPtr<winxml::Dom::IXmlElement> document_element;
71 HRESULT hr = xml_doc->get_DocumentElement(&document_element);
72 CheckHR(hr);
73
74 mswr::ComPtr<winxml::Dom::IXmlText> xml_text_node;
75 mswrw::HString data_hstring;
76 data_hstring.Attach(MakeHString(text_string.c_str()));
77 hr = xml_doc->CreateTextNode(data_hstring.Get(), &xml_text_node);
78 CheckHR(hr);
79
80 mswr::ComPtr<winxml::Dom::IXmlNode> created_node;
81 hr = xml_text_node.CopyTo(
82 winxml::Dom::IID_IXmlNode,
83 reinterpret_cast<void**>(created_node.GetAddressOf()));
84 CheckHR(hr);
85
86 mswr::ComPtr<winxml::Dom::IXmlNode> text_node_root;
87 hr = GetTextNodeRoot(index, xml_doc, &text_node_root);
88 CheckHR(hr);
89
90 mswr::ComPtr<winxml::Dom::IXmlNode> appended_node;
91 hr = text_node_root->AppendChild(created_node.Get(), &appended_node);
92 CheckHR(hr);
93 return hr;
94 }
95
96 } // namespace
97
DesktopNotification(const char * notification_origin,const char * notification_icon,const wchar_t * notification_title,const wchar_t * notification_body,const wchar_t * notification_display_source,const char * notification_id,base::win::MetroNotificationClickedHandler handler,const wchar_t * handler_context)98 ToastNotificationHandler::DesktopNotification::DesktopNotification(
99 const char* notification_origin,
100 const char* notification_icon,
101 const wchar_t* notification_title,
102 const wchar_t* notification_body,
103 const wchar_t* notification_display_source,
104 const char* notification_id,
105 base::win::MetroNotificationClickedHandler handler,
106 const wchar_t* handler_context)
107 : origin_url(notification_origin),
108 icon_url(notification_icon),
109 title(notification_title),
110 body(notification_body),
111 display_source(notification_display_source),
112 id(notification_id),
113 notification_handler(handler) {
114 if (handler_context)
115 notification_context = handler_context;
116 }
117
DesktopNotification()118 ToastNotificationHandler::DesktopNotification::DesktopNotification()
119 : notification_handler(NULL) {
120 }
121
ToastNotificationHandler()122 ToastNotificationHandler::ToastNotificationHandler() {
123 DVLOG(1) << __FUNCTION__;
124 }
125
~ToastNotificationHandler()126 ToastNotificationHandler::~ToastNotificationHandler() {
127 DVLOG(1) << __FUNCTION__;
128
129 if (notifier_ && notification_)
130 CancelNotification();
131 }
132
DisplayNotification(const DesktopNotification & notification)133 void ToastNotificationHandler::DisplayNotification(
134 const DesktopNotification& notification) {
135 DVLOG(1) << __FUNCTION__;
136
137 DCHECK(notifier_.Get() == NULL);
138 DCHECK(notification_.Get() == NULL);
139
140 notification_info_ = notification;
141
142 mswr::ComPtr<winui::Notifications::IToastNotificationManagerStatics>
143 toast_manager;
144
145 HRESULT hr = winrt_utils::CreateActivationFactory(
146 RuntimeClass_Windows_UI_Notifications_ToastNotificationManager,
147 toast_manager.GetAddressOf());
148 CheckHR(hr);
149
150 mswr::ComPtr<winxml::Dom::IXmlDocument> toast_xml;
151 hr = toast_manager->GetTemplateContent(
152 winui::Notifications::ToastTemplateType_ToastText02,
153 &toast_xml);
154 CheckHR(hr);
155
156 if (!toast_xml)
157 return;
158
159 mswr::ComPtr<winxml::Dom::IXmlElement> document_element;
160 hr = toast_xml->get_DocumentElement(&document_element);
161 CheckHR(hr);
162
163 if (!document_element)
164 return;
165
166 hr = CreateTextNode(toast_xml.Get(), 0, notification.title);
167 CheckHR(hr);
168
169 hr = CreateTextNode(toast_xml.Get(), 1, notification.body);
170 CheckHR(hr);
171
172 mswrw::HString duration_attribute_name;
173 duration_attribute_name.Attach(MakeHString(L"duration"));
174 mswrw::HString duration_attribute_value;
175 duration_attribute_value.Attach(MakeHString(L"long"));
176
177 hr = document_element->SetAttribute(duration_attribute_name.Get(),
178 duration_attribute_value.Get());
179 CheckHR(hr);
180
181 // TODO(ananta)
182 // We should set the image and launch params attribute in the notification
183 // XNL as described here: http://msdn.microsoft.com/en-us/library/hh465448
184 // To set the image we may have to extract the image and specify it in the
185 // following url form. ms-appx:///images/foo.png
186 // The launch params as described don't get passed back to us via the
187 // winapp::Activation::ILaunchActivatedEventArgs argument. Needs to be
188 // investigated.
189 mswr::ComPtr<winui::Notifications::IToastNotificationFactory>
190 toast_notification_factory;
191 hr = winrt_utils::CreateActivationFactory(
192 RuntimeClass_Windows_UI_Notifications_ToastNotification,
193 toast_notification_factory.GetAddressOf());
194 CheckHR(hr);
195
196 hr = toast_notification_factory->CreateToastNotification(
197 toast_xml.Get(), ¬ification_);
198 CheckHR(hr);
199
200 base::FilePath chrome_path;
201 if (!PathService::Get(base::FILE_EXE, &chrome_path)) {
202 NOTREACHED() << "Failed to get chrome exe path";
203 return;
204 }
205
206 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
207 bool is_per_user_install = InstallUtil::IsPerUserInstall(
208 chrome_path.value().c_str());
209 base::string16 appid =
210 ShellUtil::GetBrowserModelId(dist, is_per_user_install);
211 DVLOG(1) << "Chrome Appid is " << appid.c_str();
212
213 mswrw::HString app_user_model_id;
214 app_user_model_id.Attach(MakeHString(appid));
215
216 hr = toast_manager->CreateToastNotifierWithId(app_user_model_id.Get(),
217 ¬ifier_);
218 CheckHR(hr);
219
220 hr = notification_->add_Activated(
221 mswr::Callback<ToastActivationHandler>(
222 this, &ToastNotificationHandler::OnActivate).Get(),
223 &activated_token_);
224 CheckHR(hr);
225
226 hr = notifier_->Show(notification_.Get());
227 CheckHR(hr);
228 }
229
CancelNotification()230 void ToastNotificationHandler::CancelNotification() {
231 DVLOG(1) << __FUNCTION__;
232
233 DCHECK(notifier_);
234 DCHECK(notification_);
235
236 notifier_->Hide(notification_.Get());
237 }
238
OnActivate(winui::Notifications::IToastNotification * notification,IInspectable * inspectable)239 HRESULT ToastNotificationHandler::OnActivate(
240 winui::Notifications::IToastNotification* notification,
241 IInspectable* inspectable) {
242 // TODO(ananta)
243 // We should pass back information from the notification like the source url
244 // etc to ChromeAppView which would enable it to ensure that the
245 // correct tab in chrome is activated.
246 DVLOG(1) << __FUNCTION__;
247
248 if (notification_info_.notification_handler) {
249 notification_info_.notification_handler(
250 notification_info_.notification_context.c_str());
251 }
252 return S_OK;
253 }
254