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