From 498bff9e5ab5f8faa93614b6047ebe5504fe644a Mon Sep 17 00:00:00 2001 From: Jason Poon Date: Wed, 4 Nov 2015 10:13:52 -0800 Subject: [PATCH 1/3] Windows Toasts: Enable Basic Notifications To test this puppy out, build and open up the Chromium Dev Tools. Then, run `new Notification("Test title", {body: "Test body"});` --- brightray/brightray.gyp | 10 + .../platform_notification_service_impl.cc | 2 - .../browser/win/notification_presenter_win.cc | 75 +++ .../browser/win/notification_presenter_win.h | 38 ++ .../browser/win/windows_toast_notification.cc | 593 ++++++++++++++++++ .../browser/win/windows_toast_notification.h | 99 +++ brightray/filenames.gypi | 4 + 7 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 brightray/browser/win/notification_presenter_win.cc create mode 100644 brightray/browser/win/notification_presenter_win.h create mode 100644 brightray/browser/win/windows_toast_notification.cc create mode 100644 brightray/browser/win/windows_toast_notification.h diff --git a/brightray/brightray.gyp b/brightray/brightray.gyp index 4176e7ddbf6..697a4c962ed 100644 --- a/brightray/brightray.gyp +++ b/brightray/brightray.gyp @@ -161,6 +161,14 @@ ] }], # OS=="mac" ['OS=="win"', { + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalDependencies': [ + 'runtimeobject.lib', + 'windowsapp.lib' + ], + }, + }, 'conditions': [ ['libchromiumcontent_component', { # sandbox, base_static, devtools_discovery, devtools_http_handler, @@ -195,6 +203,8 @@ 'msvs_settings': { 'VCLinkerTool': { 'AdditionalDependencies': [ + 'Shlwapi.lib', + 'Crypt32.lib', 'advapi32.lib', 'dbghelp.lib', 'delayimp.lib', diff --git a/brightray/browser/platform_notification_service_impl.cc b/brightray/browser/platform_notification_service_impl.cc index d2b0f679a6c..db8572ee898 100644 --- a/brightray/browser/platform_notification_service_impl.cc +++ b/brightray/browser/platform_notification_service_impl.cc @@ -20,10 +20,8 @@ PlatformNotificationServiceImpl::PlatformNotificationServiceImpl() {} PlatformNotificationServiceImpl::~PlatformNotificationServiceImpl() {} NotificationPresenter* PlatformNotificationServiceImpl::notification_presenter() { -#if defined(OS_MACOSX) || defined(OS_LINUX) if (!notification_presenter_) notification_presenter_.reset(NotificationPresenter::Create()); -#endif return notification_presenter_.get(); } diff --git a/brightray/browser/win/notification_presenter_win.cc b/brightray/browser/win/notification_presenter_win.cc new file mode 100644 index 00000000000..98cc7d04189 --- /dev/null +++ b/brightray/browser/win/notification_presenter_win.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/win/notification_presenter_win.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/files/file_enumerator.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/desktop_notification_delegate.h" +#include "content/public/common/platform_notification_data.h" +#include "common/application_info.h" +#include +#include +#include +#include "third_party/skia/include/core/SkBitmap.h" +#include "base/win/windows_version.h" + +// Windows Header +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include "windows_toast_notification.h" + +#pragma comment(lib, "runtimeobject.lib") +#pragma comment(lib, "Crypt32.lib") + +using namespace WinToasts; +using namespace Microsoft::WRL; +using namespace ABI::Windows::UI::Notifications; +using namespace ABI::Windows::Data::Xml::Dom; +using namespace ABI::Windows::Foundation; + +namespace brightray { + +// static +NotificationPresenter* NotificationPresenter::Create() { + return new NotificationPresenterWin; +} + +NotificationPresenterWin::NotificationPresenterWin() { +} + +NotificationPresenterWin::~NotificationPresenterWin() { +} + +void NotificationPresenterWin::ShowNotification( + const content::PlatformNotificationData& data, + const SkBitmap& icon, + scoped_ptr delegate_ptr, + base::Closure* cancel_callback) { + + std::wstring title = data.title; + std::wstring body = data.body; + std::string appName = GetApplicationName(); + char* img = NULL; + + WinToasts::WindowsToastNotification* wtn = new WinToasts::WindowsToastNotification(appName.c_str()); + wtn->ShowNotification(title.c_str(), body.c_str(), img); +} + +void NotificationPresenterWin::CancelNotification() { +} + +void NotificationPresenterWin::DeleteNotification() { +} + +} // namespace brightray \ No newline at end of file diff --git a/brightray/browser/win/notification_presenter_win.h b/brightray/browser/win/notification_presenter_win.h new file mode 100644 index 00000000000..0205422f92b --- /dev/null +++ b/brightray/browser/win/notification_presenter_win.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_WIN_H_ +#define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_WIN_H_ + +#include "base/compiler_specific.h" +#include "browser/notification_presenter.h" + +#include + +namespace brightray { + +class NotificationPresenterWin : public NotificationPresenter { + public: + NotificationPresenterWin(); + ~NotificationPresenterWin(); + + // NotificationPresenter: + void ShowNotification( + const content::PlatformNotificationData&, + const SkBitmap& icon, + scoped_ptr delegate, + base::Closure* cancel_callback) override; + + void RemoveNotification(); + + private: + + void CancelNotification(); + void DeleteNotification(); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/win/windows_toast_notification.cc b/brightray/browser/win/windows_toast_notification.cc new file mode 100644 index 00000000000..7a90ac3d3fb --- /dev/null +++ b/brightray/browser/win/windows_toast_notification.cc @@ -0,0 +1,593 @@ +// Copyright (c) 2015 Felix Rieseberg . All rights reserved. +// Copyright (c) 2015 Ryan McShane and Brandon Smith +// Thanks to both of those folks mentioned above who first thought up a bunch of this code +// and released it as MIT to the world. + +#include "windows_toast_notification.h" +#include +#include +#include +#include +#include +#include + +using namespace WinToasts; +using namespace Windows::Foundation; + +#define BREAK_IF_BAD(hr) if(!SUCCEEDED(hr)) break; +#define SHORTCUT_FORMAT "\\Microsoft\\Windows\\Start Menu\\Programs\\%s.lnk" + +char WindowsToastNotification::s_appName[MAX_PATH] = {}; +char WindowsToastNotification::s_tempDirPath[MAX_PATH] = {}; + +namespace WinToasts { + +WindowsToastNotification::WindowsToastNotification(const char* appName) +{ + sprintf_s(s_appName, ARRAYSIZE(s_appName), "%s", appName); + m_eventHandler = NULL; + m_tempFilePath = NULL; +} + +WindowsToastNotification::~WindowsToastNotification() +{ + if (m_eventHandler){ + delete m_eventHandler; + } + + if (m_tempFilePath){ + DeleteTempImageFile(); + delete m_tempFilePath; + } +} + +/*static*/ void WindowsToastNotification::InitSystemProps(char* appName, char* tempDirPath) +{ + + sprintf_s(s_tempDirPath, ARRAYSIZE(s_tempDirPath), "%s", tempDirPath); +} + +void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* msg, const char* img) +{ + // Init using WRL + Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); + + HSTRING toastNotifMgrStr = NULL; + HSTRING appId = NULL; + HSTRING toastNotifStr = NULL; + + // Create a shortcut in the user's app data folder + // (requirement for Windows 8 to show notifications - Windows 10 is fine without it) + // TODO: This shoudl be cleaned up + // HRESULT hr = EnsureShortcut(); + + do{ + HRESULT hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager, &toastNotifMgrStr); + BREAK_IF_BAD(hr); + + ComPtr toastMgr; + hr = Windows::Foundation::GetActivationFactory(toastNotifMgrStr, &toastMgr); + BREAK_IF_BAD(hr); + + ComPtr toastXml; + hr = GetToastXml(toastMgr.Get(), title, msg, img, &toastXml); + BREAK_IF_BAD(hr); + + WCHAR wAppName[MAX_PATH]; + swprintf(wAppName, ARRAYSIZE(wAppName), L"%S", s_appName); + hr = CreateHString(wAppName, &appId); + BREAK_IF_BAD(hr); + + ComPtr notifier; + toastMgr->CreateToastNotifierWithId(appId, ¬ifier); + BREAK_IF_BAD(hr); + + hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotification, &toastNotifStr); + BREAK_IF_BAD(hr); + + ComPtr toastFactory; + hr = Windows::Foundation::GetActivationFactory(toastNotifStr, &toastFactory); + BREAK_IF_BAD(hr); + + ComPtr toast; + hr = toastFactory->CreateToastNotification(toastXml.Get(), &toast); + BREAK_IF_BAD(hr); + + hr = SetupCallbacks(toast.Get()); + BREAK_IF_BAD(hr); + + hr = notifier->Show(toast.Get()); + BREAK_IF_BAD(hr); + + //m_cbFn.Reset(Isolate::GetCurrent(), cbFn); + + //todo: + //cbPtr->NotificationDisplayed(); + } while (FALSE); + + if (toastNotifMgrStr != NULL){ + WindowsDeleteString(toastNotifMgrStr); + } + + if (appId != NULL){ + WindowsDeleteString(appId); + } + + if (toastNotifStr != NULL){ + WindowsDeleteString(toastNotifStr); + } + + // if (!SUCCEEDED(hr)){ + // delete this; + // } +} + + +void WindowsToastNotification::NotificationClicked() +{ + // Local fn = Local::New(Isolate::GetCurrent(), m_cbFn); + // Handle args[1]; + // fn->Call(Isolate::GetCurrent()->GetCurrentContext()->Global(), 0, args); + // delete this; +} + + +void WindowsToastNotification::NotificationDismissed() +{ + delete this; +} + + +HRESULT WindowsToastNotification::GetToastXml(IToastNotificationManagerStatics* toastMgr, const WCHAR* title, const WCHAR* msg, const char* img, IXmlDocument** toastXml) +{ + HRESULT hr; + ToastTemplateType templateType; + if (title == NULL || msg == NULL){ + // Single line toast + templateType = img == NULL ? ToastTemplateType_ToastText01 : ToastTemplateType_ToastImageAndText01; + hr = toastMgr->GetTemplateContent(templateType, toastXml); + if (SUCCEEDED(hr)) { + const WCHAR* text = title != NULL ? title : msg; + hr = SetXmlText(*toastXml, text); + } + } else { + // Title and body toast + templateType = img == NULL ? ToastTemplateType_ToastText02 : ToastTemplateType_ToastImageAndText02; + hr = toastMgr->GetTemplateContent(templateType, toastXml); + if (SUCCEEDED(hr)) { + hr = SetXmlText(*toastXml, title, msg); + } + } + + if (img != NULL && SUCCEEDED(hr)){ + // Toast has image + hr = CreateTempImageFile(img); + if (SUCCEEDED(hr)){ + hr = SetXmlImage(*toastXml); + } + + // Don't stop a notification from showing just because an image couldn't be displayed. By default the app icon will be shown. + hr = S_OK; + } + + return hr; +} + + +HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* text) +{ + HSTRING tag = NULL; + + ComPtr nodeList; + HRESULT hr = GetTextNodeList(&tag, doc, &nodeList, 1); + do{ + BREAK_IF_BAD(hr); + + ComPtr node; + hr = nodeList->Item(0, &node); + BREAK_IF_BAD(hr); + + hr = AppendTextToXml(doc, node.Get(), text); + } while (FALSE); + + if (tag != NULL){ + WindowsDeleteString(tag); + } + + return hr; +} + + +HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* title, const WCHAR* body) +{ + HSTRING tag = NULL; + ComPtr nodeList; + HRESULT hr = GetTextNodeList(&tag, doc, &nodeList, 2); + do{ + BREAK_IF_BAD(hr); + + ComPtr node; + hr = nodeList->Item(0, &node); + BREAK_IF_BAD(hr); + + hr = AppendTextToXml(doc, node.Get(), title); + BREAK_IF_BAD(hr); + + hr = nodeList->Item(1, &node); + BREAK_IF_BAD(hr); + + hr = AppendTextToXml(doc, node.Get(), body); + } while (FALSE); + + if (tag != NULL){ + WindowsDeleteString(tag); + } + + return hr; +} + + +HRESULT WindowsToastNotification::SetXmlImage(IXmlDocument* doc) +{ + HSTRING tag = NULL; + HSTRING src = NULL; + HSTRING imgPath = NULL; + HRESULT hr = CreateHString(L"image", &tag); + do{ + BREAK_IF_BAD(hr); + + ComPtr nodeList; + hr = doc->GetElementsByTagName(tag, &nodeList); + BREAK_IF_BAD(hr); + + ComPtr imageNode; + hr = nodeList->Item(0, &imageNode); + BREAK_IF_BAD(hr); + + ComPtr attrs; + hr = imageNode->get_Attributes(&attrs); + BREAK_IF_BAD(hr); + + hr = CreateHString(L"src", &src); + BREAK_IF_BAD(hr); + + ComPtr srcAttr; + hr = attrs->GetNamedItem(src, &srcAttr); + BREAK_IF_BAD(hr); + + WCHAR xmlPath[MAX_PATH]; + swprintf(xmlPath, ARRAYSIZE(xmlPath), L"file:///%S", m_tempFilePath); + hr = CreateHString(xmlPath, &imgPath); + BREAK_IF_BAD(hr); + + ComPtr srcText; + hr = doc->CreateTextNode(imgPath, &srcText); + BREAK_IF_BAD(hr); + + ComPtr srcNode; + hr = srcText.As(&srcNode); + BREAK_IF_BAD(hr); + + ComPtr childNode; + hr = srcAttr->AppendChild(srcNode.Get(), &childNode); + } while (FALSE); + + if (tag != NULL){ + WindowsDeleteString(tag); + } + if (src != NULL){ + WindowsDeleteString(src); + } + if (imgPath != NULL){ + WindowsDeleteString(imgPath); + } + + return hr; +} + + +HRESULT WindowsToastNotification::GetTextNodeList(HSTRING* tag, IXmlDocument* doc, IXmlNodeList** nodeList, UINT32 reqLength) +{ + HRESULT hr = CreateHString(L"text", tag); + do{ + BREAK_IF_BAD(hr); + + hr = doc->GetElementsByTagName(*tag, nodeList); + BREAK_IF_BAD(hr); + + UINT32 nodeLength; + hr = (*nodeList)->get_Length(&nodeLength); + BREAK_IF_BAD(hr); + + if (nodeLength < reqLength) { + hr = E_INVALIDARG; + } + } while (FALSE); + + if (!SUCCEEDED(hr)){ + // Allow the caller to delete this string on success + WindowsDeleteString(*tag); + } + + return hr; +} + + +HRESULT WindowsToastNotification::AppendTextToXml(IXmlDocument* doc, IXmlNode* node, const WCHAR* text) +{ + HSTRING str = NULL; + HRESULT hr = CreateHString(text, &str); + do{ + BREAK_IF_BAD(hr); + + ComPtr xmlText; + hr = doc->CreateTextNode(str, &xmlText); + BREAK_IF_BAD(hr); + + ComPtr textNode; + hr = xmlText.As(&textNode); + BREAK_IF_BAD(hr); + + ComPtr appendNode; + hr = node->AppendChild(textNode.Get(), &appendNode); + } while (FALSE); + + if (str != NULL){ + WindowsDeleteString(str); + } + + return hr; +} + + +HRESULT WindowsToastNotification::SetupCallbacks(IToastNotification* toast) +{ + EventRegistrationToken activatedToken, dismissedToken; + m_eventHandler = new ToastEventHandler(this); + ComPtr eventHandler(m_eventHandler); + HRESULT hr = toast->add_Activated(eventHandler.Get(), &activatedToken); + if (SUCCEEDED(hr)) { + hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken); + } + + return hr; +} + + +HRESULT WindowsToastNotification::CreateHString(const WCHAR* source, HSTRING* dest) +{ + if (source == NULL || dest == NULL) { + return E_INVALIDARG; + } + + HRESULT hr = WindowsCreateString(source, wcslen(source), dest); + return hr; +} + + +HRESULT WindowsToastNotification::CreateTempImageFile(const char* base64) +{ + BYTE* buff = NULL; + HRESULT hr = EnsureTempDir(); + do{ + BREAK_IF_BAD(hr); + + DWORD buffLen; + hr = ConvertBase64DataToImage(base64, &buff, &buffLen); + BREAK_IF_BAD(hr); + + hr = WriteToFile(buff, buffLen); + } while (FALSE); + + if (buff != NULL){ + delete buff; + } + + return hr; +} + + +HRESULT WindowsToastNotification::ConvertBase64DataToImage(const char* base64, BYTE** buff, DWORD* buffLen) +{ + HRESULT hr = E_FAIL; + *buffLen = 0; + DWORD reqSize; + if (CryptStringToBinary((LPCWSTR)base64, 0, CRYPT_STRING_BASE64, NULL, &reqSize, NULL, NULL)){ + *buff = new BYTE[reqSize]; + if (CryptStringToBinary((LPCWSTR)base64, 0, CRYPT_STRING_BASE64, *buff, &reqSize, NULL, NULL)){ + *buffLen = reqSize; + hr = S_OK; + } + } + + return hr; +} + + +HRESULT WindowsToastNotification::WriteToFile(BYTE* buff, DWORD buffLen) +{ + HRESULT hr = E_FAIL; + + GUID guid; + hr = CoCreateGuid(&guid); + if (SUCCEEDED(hr)){ + WCHAR randomName[MAX_PATH]; + StringFromGUID2(guid, randomName, ARRAYSIZE(randomName)); + + m_tempFilePath = new char[MAX_PATH]; + sprintf(m_tempFilePath, "%s\\%S", s_tempDirPath, randomName); + HANDLE file = CreateFile((LPCWSTR)m_tempFilePath, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE){ + DWORD bytesWritten; + if (WriteFile(file, buff, buffLen, &bytesWritten, NULL)){ + hr = S_OK; + } + else{ + DeleteTempImageFile(); + } + + CloseHandle(file); + } + } + + if (!SUCCEEDED(hr) && m_tempFilePath){ + delete m_tempFilePath; + m_tempFilePath = NULL; + } + + + return hr; +} + + +HRESULT WindowsToastNotification::EnsureTempDir() +{ + // Check for the Dir already existing or easy creation + HRESULT hr = E_FAIL; + if (CreateDirectory((LPCWSTR)s_tempDirPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS){ + hr = S_OK; + } else { + // NOTE: This makes an assumption that the path is a FULL LENGTH PATH. Therefore the first folder starts at the + // 4th character. ie. "c:\path\to\temp\dir" + + // It's possible that multiple directories need to be created. + for (int i = 3; i < ARRAYSIZE(s_tempDirPath); i++) { + char c = s_tempDirPath[i]; + if (c == '\\'){ + //Substring until this char + char* temp = new char[i + 1]; + strncpy(temp, s_tempDirPath, i); + temp[i] = 0; + if (!CreateDirectory((LPCWSTR)temp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS){ + delete temp; + return hr; + } + + delete temp; + } + } + + //Try to create the full path one more time. This will take care of paths that don't end with a slash + if (CreateDirectory((LPCWSTR)s_tempDirPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS){ + hr = S_OK; + } + } + + return hr; +} + + +HRESULT WindowsToastNotification::DeleteTempImageFile() +{ + if (DeleteFile((LPCWSTR)m_tempFilePath)){ + return S_OK; + } + + return E_FAIL; +} + + +HRESULT WindowsToastNotification::EnsureShortcut() +{ + HRESULT hr = E_FAIL; + char shortcut[MAX_PATH]; + DWORD charsWritten = GetEnvironmentVariable(L"APPDATA", (LPWSTR)shortcut, MAX_PATH); + + if (charsWritten > 0) { + char shortcutCat[MAX_PATH]; + sprintf(shortcutCat, SHORTCUT_FORMAT, s_appName); + errno_t concatErr = strcat_s(shortcut, ARRAYSIZE(shortcut), shortcutCat); + if (concatErr == 0) { + DWORD attr = GetFileAttributes((LPCWSTR)shortcut); + bool exists = attr < 0xFFFFFFF; + if (exists){ + hr = S_OK; + } + else{ + WCHAR path[MAX_PATH]; + mbstowcs(path, shortcut, ARRAYSIZE(path)); + hr = CreateShortcut(path); + } + } + } + + return hr; +} + + +HRESULT WindowsToastNotification::CreateShortcut(WCHAR* path) +{ + HRESULT hr = E_FAIL; + char exePath[MAX_PATH]; + DWORD charsWritten = GetModuleFileNameEx(GetCurrentProcess(), nullptr, (LPWSTR)exePath, ARRAYSIZE(exePath)); + if (charsWritten > 0) { + PROPVARIANT propVariant; + ComPtr shellLink; + hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + do{ + BREAK_IF_BAD(hr); + + hr = shellLink->SetPath((LPCWSTR)exePath); + BREAK_IF_BAD(hr); + + ComPtr propStore; + hr = shellLink.As(&propStore); + BREAK_IF_BAD(hr); + + WCHAR wAppName[MAX_PATH]; + swprintf(wAppName, ARRAYSIZE(wAppName), L"%S", s_appName); + hr = InitPropVariantFromString(wAppName, &propVariant); + BREAK_IF_BAD(hr); + + hr = propStore->SetValue(PKEY_AppUserModel_ID, propVariant); + BREAK_IF_BAD(hr); + + hr = propStore->Commit(); + BREAK_IF_BAD(hr); + + ComPtr persistFile; + hr = shellLink.As(&persistFile); + BREAK_IF_BAD(hr); + + hr = persistFile->Save(path, TRUE); + } while (FALSE); + + PropVariantClear(&propVariant); + } + + return hr; +} + +ToastEventHandler::ToastEventHandler(WindowsToastNotification* notification) +{ + m_ref = 1; + m_notification = notification; +} + + +ToastEventHandler::~ToastEventHandler() +{ + // Empty +} + + +IFACEMETHODIMP ToastEventHandler::Invoke(IToastNotification* sender, IInspectable* args) +{ + //Notification "activated" (clicked) + if (m_notification != NULL){ + m_notification->NotificationClicked(); + } + return S_OK; +} + + +IFACEMETHODIMP ToastEventHandler::Invoke(IToastNotification* sender, IToastDismissedEventArgs* e) +{ + //Notification dismissed + if (m_notification != NULL){ + m_notification->NotificationDismissed(); + } + return S_OK; +} + +} //namespace \ No newline at end of file diff --git a/brightray/browser/win/windows_toast_notification.h b/brightray/browser/win/windows_toast_notification.h new file mode 100644 index 00000000000..ff745a8aac1 --- /dev/null +++ b/brightray/browser/win/windows_toast_notification.h @@ -0,0 +1,99 @@ +#ifndef __WINDOWS_TOAST_NOTIFICATION_H__ +#define __WINDOWS_TOAST_NOTIFICATION_H__ + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace ABI::Windows::UI::Notifications; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Data::Xml::Dom; + +namespace WinToasts{ + + typedef ITypedEventHandler DesktopToastActivatedEventHandler; + typedef ITypedEventHandler DesktopToastDismissedEventHandler; + + class ToastEventHandler; + + class WindowsToastNotification + { + private: + static char s_appName[MAX_PATH]; + static char s_tempDirPath[MAX_PATH]; + + char* m_tempFilePath; + ToastEventHandler* m_eventHandler; + + HRESULT GetToastXml(IToastNotificationManagerStatics* toastMgr, const WCHAR* title, const WCHAR* msg, const char* img, IXmlDocument** toastXml); + HRESULT SetXmlText(IXmlDocument* doc, const WCHAR* text); + HRESULT SetXmlText(IXmlDocument* doc, const WCHAR* title, const WCHAR* body); + HRESULT SetXmlImage(IXmlDocument* doc); + HRESULT GetTextNodeList(HSTRING* tag, IXmlDocument* doc, IXmlNodeList** nodeList, UINT32 reqLength); + HRESULT AppendTextToXml(IXmlDocument* doc, IXmlNode* node, const WCHAR* text); + HRESULT SetupCallbacks(IToastNotification* toast); + HRESULT CreateHString(const WCHAR* source, HSTRING* dest); + HRESULT CreateTempImageFile(const char* base64); + HRESULT ConvertBase64DataToImage(const char* base64, BYTE** buff, DWORD* buffLen); + HRESULT WriteToFile(BYTE* buff, DWORD buffLen); + HRESULT EnsureTempDir(); + HRESULT DeleteTempImageFile(); + HRESULT EnsureShortcut(); + HRESULT CreateShortcut(WCHAR* path); + + public: + WindowsToastNotification(const char* appName); + ~WindowsToastNotification(); + void ShowNotification(const WCHAR* title, const WCHAR* msg, const char* img); + void NotificationClicked(); + void NotificationDismissed(); + + static void InitSystemProps(char* appName, char* tempDirPath); + }; + + + class ToastEventHandler : + public Implements < DesktopToastActivatedEventHandler, DesktopToastDismissedEventHandler > + { + private: + ULONG m_ref; + WindowsToastNotification* m_notification; + + public: + ToastEventHandler(WindowsToastNotification* notification); + ~ToastEventHandler(); + IFACEMETHODIMP Invoke(IToastNotification* sender, IInspectable* args); + IFACEMETHODIMP Invoke(IToastNotification* sender, IToastDismissedEventArgs* e); + IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_ref); } + + IFACEMETHODIMP_(ULONG) Release() { + ULONG l = InterlockedDecrement(&m_ref); + if (l == 0) delete this; + return l; + } + + IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) { + if (IsEqualIID(riid, IID_IUnknown)) + *ppv = static_cast(static_cast(this)); + else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler))) + *ppv = static_cast(this); + else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler))) + *ppv = static_cast(this); + else *ppv = nullptr; + + if (*ppv) { + reinterpret_cast(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + }; + +} // namespace + +#endif //__WINDOWS_TOAST_NOTIFICATION_H__ \ No newline at end of file diff --git a/brightray/filenames.gypi b/brightray/filenames.gypi index bbde28694d9..84dc4291ed2 100644 --- a/brightray/filenames.gypi +++ b/brightray/filenames.gypi @@ -63,6 +63,10 @@ 'browser/platform_notification_service_impl.h', 'browser/linux/notification_presenter_linux.h', 'browser/linux/notification_presenter_linux.cc', + 'browser/win/notification_presenter_win.h', + 'browser/win/notification_presenter_win.cc', + 'browser/win/windows_toast_notification.h', + 'browser/win/windows_toast_notification.cc', 'browser/special_storage_policy.cc', 'browser/special_storage_policy.h', 'browser/url_request_context_getter.cc', From 86ea0759d8634b3dab8f222356d459cd475fd8f3 Mon Sep 17 00:00:00 2001 From: Felix Rieseberg Date: Sat, 7 Nov 2015 19:41:29 -0800 Subject: [PATCH 2/3] Windows Toasts: Icons, Events, Cleanup, Documentation - Enable documentations - Add a small usage example to the header - Final cleanups --- .../browser/win/notification_presenter_win.cc | 24 +- .../browser/win/notification_presenter_win.h | 13 +- .../browser/win/windows_toast_notification.cc | 340 ++++-------------- .../browser/win/windows_toast_notification.h | 34 +- 4 files changed, 107 insertions(+), 304 deletions(-) diff --git a/brightray/browser/win/notification_presenter_win.cc b/brightray/browser/win/notification_presenter_win.cc index 98cc7d04189..925fab17e19 100644 --- a/brightray/browser/win/notification_presenter_win.cc +++ b/brightray/browser/win/notification_presenter_win.cc @@ -1,30 +1,26 @@ // Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-CHROMIUM file. #include "browser/win/notification_presenter_win.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "base/files/file_enumerator.h" #include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" +#include "base/win/windows_version.h" #include "content/public/browser/desktop_notification_delegate.h" #include "content/public/common/platform_notification_data.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "common/application_info.h" + #include #include #include -#include "third_party/skia/include/core/SkBitmap.h" -#include "base/win/windows_version.h" // Windows Header #define WIN32_LEAN_AND_MEAN #include -#include #include +#include #include #include #include "windows_toast_notification.h" @@ -58,18 +54,20 @@ void NotificationPresenterWin::ShowNotification( base::Closure* cancel_callback) { std::wstring title = data.title; - std::wstring body = data.body; + std::wstring body = data.body; + std::string iconPath = data.icon.spec(); std::string appName = GetApplicationName(); - char* img = NULL; - WinToasts::WindowsToastNotification* wtn = new WinToasts::WindowsToastNotification(appName.c_str()); - wtn->ShowNotification(title.c_str(), body.c_str(), img); + WinToasts::WindowsToastNotification* wtn = new WinToasts::WindowsToastNotification(appName.c_str(), delegate_ptr.release()); + wtn->ShowNotification(title.c_str(), body.c_str(), iconPath); } void NotificationPresenterWin::CancelNotification() { + // No can do. } void NotificationPresenterWin::DeleteNotification() { + // No can do. } } // namespace brightray \ No newline at end of file diff --git a/brightray/browser/win/notification_presenter_win.h b/brightray/browser/win/notification_presenter_win.h index 0205422f92b..bed6eea9902 100644 --- a/brightray/browser/win/notification_presenter_win.h +++ b/brightray/browser/win/notification_presenter_win.h @@ -1,8 +1,19 @@ // Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-CHROMIUM file. +// Usage Example (JavaScript: +// var windowsNotification = new Notification("Test Title", { +// body: "Hi, I'm an example body. How are you?", +// icon: "file:///C:/Path/To/Your/Image.png" +// }); + +// windowsNotification.onshow = function () { console.log("Notification shown") }; +// windowsNotification.onclick = function () { console.log("Notification clicked") }; +// windowsNotification.onclose = function () { console.log("Notification dismissed") }; + + #ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_WIN_H_ #define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_WIN_H_ diff --git a/brightray/browser/win/windows_toast_notification.cc b/brightray/browser/win/windows_toast_notification.cc index 7a90ac3d3fb..2bf262e2175 100644 --- a/brightray/browser/win/windows_toast_notification.cc +++ b/brightray/browser/win/windows_toast_notification.cc @@ -1,53 +1,47 @@ -// Copyright (c) 2015 Felix Rieseberg . All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. // Copyright (c) 2015 Ryan McShane and Brandon Smith // Thanks to both of those folks mentioned above who first thought up a bunch of this code // and released it as MIT to the world. #include "windows_toast_notification.h" +#include "content/public/browser/desktop_notification_delegate.h" + #include #include #include #include #include #include +#include using namespace WinToasts; using namespace Windows::Foundation; #define BREAK_IF_BAD(hr) if(!SUCCEEDED(hr)) break; -#define SHORTCUT_FORMAT "\\Microsoft\\Windows\\Start Menu\\Programs\\%s.lnk" char WindowsToastNotification::s_appName[MAX_PATH] = {}; -char WindowsToastNotification::s_tempDirPath[MAX_PATH] = {}; namespace WinToasts { -WindowsToastNotification::WindowsToastNotification(const char* appName) +WindowsToastNotification::WindowsToastNotification(const char* appName, content::DesktopNotificationDelegate* delegate) { sprintf_s(s_appName, ARRAYSIZE(s_appName), "%s", appName); m_eventHandler = NULL; - m_tempFilePath = NULL; + n_delegate = delegate; } WindowsToastNotification::~WindowsToastNotification() { - if (m_eventHandler){ + if (m_eventHandler) { delete m_eventHandler; } - - if (m_tempFilePath){ - DeleteTempImageFile(); - delete m_tempFilePath; + + if (n_delegate) { + delete n_delegate; } } -/*static*/ void WindowsToastNotification::InitSystemProps(char* appName, char* tempDirPath) -{ - - sprintf_s(s_tempDirPath, ARRAYSIZE(s_tempDirPath), "%s", tempDirPath); -} - -void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* msg, const char* img) +void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* msg, std::string iconPath) { // Init using WRL Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); @@ -55,14 +49,10 @@ void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* HSTRING toastNotifMgrStr = NULL; HSTRING appId = NULL; HSTRING toastNotifStr = NULL; - - // Create a shortcut in the user's app data folder - // (requirement for Windows 8 to show notifications - Windows 10 is fine without it) - // TODO: This shoudl be cleaned up - // HRESULT hr = EnsureShortcut(); + HRESULT hr; - do{ - HRESULT hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager, &toastNotifMgrStr); + do { + hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager, &toastNotifMgrStr); BREAK_IF_BAD(hr); ComPtr toastMgr; @@ -70,7 +60,7 @@ void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* BREAK_IF_BAD(hr); ComPtr toastXml; - hr = GetToastXml(toastMgr.Get(), title, msg, img, &toastXml); + hr = GetToastXml(toastMgr.Get(), title, msg, iconPath, &toastXml); BREAK_IF_BAD(hr); WCHAR wAppName[MAX_PATH]; @@ -99,52 +89,48 @@ void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* hr = notifier->Show(toast.Get()); BREAK_IF_BAD(hr); - //m_cbFn.Reset(Isolate::GetCurrent(), cbFn); - - //todo: - //cbPtr->NotificationDisplayed(); + n_delegate->NotificationDisplayed(); } while (FALSE); - if (toastNotifMgrStr != NULL){ + if (toastNotifMgrStr != NULL) { WindowsDeleteString(toastNotifMgrStr); } - if (appId != NULL){ + if (appId != NULL) { WindowsDeleteString(appId); } - if (toastNotifStr != NULL){ + if (toastNotifStr != NULL) { WindowsDeleteString(toastNotifStr); } - // if (!SUCCEEDED(hr)){ - // delete this; - // } + if (!SUCCEEDED(hr)) { + delete this; + } } - void WindowsToastNotification::NotificationClicked() { - // Local fn = Local::New(Isolate::GetCurrent(), m_cbFn); - // Handle args[1]; - // fn->Call(Isolate::GetCurrent()->GetCurrentContext()->Global(), 0, args); - // delete this; + delete this; } - void WindowsToastNotification::NotificationDismissed() { delete this; } - -HRESULT WindowsToastNotification::GetToastXml(IToastNotificationManagerStatics* toastMgr, const WCHAR* title, const WCHAR* msg, const char* img, IXmlDocument** toastXml) -{ +HRESULT WindowsToastNotification::GetToastXml( + IToastNotificationManagerStatics* toastMgr, + const WCHAR* title, + const WCHAR* msg, + std::string iconPath, + IXmlDocument** toastXml) { + HRESULT hr; ToastTemplateType templateType; - if (title == NULL || msg == NULL){ + if (title == NULL || msg == NULL) { // Single line toast - templateType = img == NULL ? ToastTemplateType_ToastText01 : ToastTemplateType_ToastImageAndText01; + templateType = iconPath.length() == 0 ? ToastTemplateType_ToastText01 : ToastTemplateType_ToastImageAndText01; hr = toastMgr->GetTemplateContent(templateType, toastXml); if (SUCCEEDED(hr)) { const WCHAR* text = title != NULL ? title : msg; @@ -152,18 +138,17 @@ HRESULT WindowsToastNotification::GetToastXml(IToastNotificationManagerStatics* } } else { // Title and body toast - templateType = img == NULL ? ToastTemplateType_ToastText02 : ToastTemplateType_ToastImageAndText02; + templateType = iconPath.length() == 0 ? ToastTemplateType_ToastText02 : ToastTemplateType_ToastImageAndText02; hr = toastMgr->GetTemplateContent(templateType, toastXml); if (SUCCEEDED(hr)) { hr = SetXmlText(*toastXml, title, msg); } } - if (img != NULL && SUCCEEDED(hr)){ + if (iconPath.length() != 0 && SUCCEEDED(hr)) { // Toast has image - hr = CreateTempImageFile(img); - if (SUCCEEDED(hr)){ - hr = SetXmlImage(*toastXml); + if (SUCCEEDED(hr)) { + hr = SetXmlImage(*toastXml, iconPath); } // Don't stop a notification from showing just because an image couldn't be displayed. By default the app icon will be shown. @@ -173,7 +158,6 @@ HRESULT WindowsToastNotification::GetToastXml(IToastNotificationManagerStatics* return hr; } - HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* text) { HSTRING tag = NULL; @@ -190,14 +174,13 @@ HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* tex hr = AppendTextToXml(doc, node.Get(), text); } while (FALSE); - if (tag != NULL){ + if (tag != NULL) { WindowsDeleteString(tag); } return hr; } - HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* title, const WCHAR* body) { HSTRING tag = NULL; @@ -219,21 +202,21 @@ HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* tit hr = AppendTextToXml(doc, node.Get(), body); } while (FALSE); - if (tag != NULL){ + if (tag != NULL) { WindowsDeleteString(tag); } return hr; } - -HRESULT WindowsToastNotification::SetXmlImage(IXmlDocument* doc) +HRESULT WindowsToastNotification::SetXmlImage(IXmlDocument* doc, std::string iconPath) { HSTRING tag = NULL; HSTRING src = NULL; HSTRING imgPath = NULL; HRESULT hr = CreateHString(L"image", &tag); - do{ + + do { BREAK_IF_BAD(hr); ComPtr nodeList; @@ -256,7 +239,7 @@ HRESULT WindowsToastNotification::SetXmlImage(IXmlDocument* doc) BREAK_IF_BAD(hr); WCHAR xmlPath[MAX_PATH]; - swprintf(xmlPath, ARRAYSIZE(xmlPath), L"file:///%S", m_tempFilePath); + swprintf(xmlPath, ARRAYSIZE(xmlPath), L"%S", iconPath); hr = CreateHString(xmlPath, &imgPath); BREAK_IF_BAD(hr); @@ -272,20 +255,19 @@ HRESULT WindowsToastNotification::SetXmlImage(IXmlDocument* doc) hr = srcAttr->AppendChild(srcNode.Get(), &childNode); } while (FALSE); - if (tag != NULL){ + if (tag != NULL) { WindowsDeleteString(tag); } - if (src != NULL){ + if (src != NULL) { WindowsDeleteString(src); } - if (imgPath != NULL){ + if (imgPath != NULL) { WindowsDeleteString(imgPath); } return hr; } - HRESULT WindowsToastNotification::GetTextNodeList(HSTRING* tag, IXmlDocument* doc, IXmlNodeList** nodeList, UINT32 reqLength) { HRESULT hr = CreateHString(L"text", tag); @@ -304,7 +286,7 @@ HRESULT WindowsToastNotification::GetTextNodeList(HSTRING* tag, IXmlDocument* do } } while (FALSE); - if (!SUCCEEDED(hr)){ + if (!SUCCEEDED(hr)) { // Allow the caller to delete this string on success WindowsDeleteString(*tag); } @@ -312,12 +294,11 @@ HRESULT WindowsToastNotification::GetTextNodeList(HSTRING* tag, IXmlDocument* do return hr; } - HRESULT WindowsToastNotification::AppendTextToXml(IXmlDocument* doc, IXmlNode* node, const WCHAR* text) { HSTRING str = NULL; HRESULT hr = CreateHString(text, &str); - do{ + do { BREAK_IF_BAD(hr); ComPtr xmlText; @@ -332,20 +313,20 @@ HRESULT WindowsToastNotification::AppendTextToXml(IXmlDocument* doc, IXmlNode* n hr = node->AppendChild(textNode.Get(), &appendNode); } while (FALSE); - if (str != NULL){ + if (str != NULL) { WindowsDeleteString(str); } return hr; } - HRESULT WindowsToastNotification::SetupCallbacks(IToastNotification* toast) { EventRegistrationToken activatedToken, dismissedToken; - m_eventHandler = new ToastEventHandler(this); + m_eventHandler = new ToastEventHandler(this, n_delegate); ComPtr eventHandler(m_eventHandler); HRESULT hr = toast->add_Activated(eventHandler.Get(), &activatedToken); + if (SUCCEEDED(hr)) { hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken); } @@ -353,7 +334,6 @@ HRESULT WindowsToastNotification::SetupCallbacks(IToastNotification* toast) return hr; } - HRESULT WindowsToastNotification::CreateHString(const WCHAR* source, HSTRING* dest) { if (source == NULL || dest == NULL) { @@ -364,229 +344,43 @@ HRESULT WindowsToastNotification::CreateHString(const WCHAR* source, HSTRING* de return hr; } - -HRESULT WindowsToastNotification::CreateTempImageFile(const char* base64) -{ - BYTE* buff = NULL; - HRESULT hr = EnsureTempDir(); - do{ - BREAK_IF_BAD(hr); - - DWORD buffLen; - hr = ConvertBase64DataToImage(base64, &buff, &buffLen); - BREAK_IF_BAD(hr); - - hr = WriteToFile(buff, buffLen); - } while (FALSE); - - if (buff != NULL){ - delete buff; - } - - return hr; -} - - -HRESULT WindowsToastNotification::ConvertBase64DataToImage(const char* base64, BYTE** buff, DWORD* buffLen) -{ - HRESULT hr = E_FAIL; - *buffLen = 0; - DWORD reqSize; - if (CryptStringToBinary((LPCWSTR)base64, 0, CRYPT_STRING_BASE64, NULL, &reqSize, NULL, NULL)){ - *buff = new BYTE[reqSize]; - if (CryptStringToBinary((LPCWSTR)base64, 0, CRYPT_STRING_BASE64, *buff, &reqSize, NULL, NULL)){ - *buffLen = reqSize; - hr = S_OK; - } - } - - return hr; -} - - -HRESULT WindowsToastNotification::WriteToFile(BYTE* buff, DWORD buffLen) -{ - HRESULT hr = E_FAIL; - - GUID guid; - hr = CoCreateGuid(&guid); - if (SUCCEEDED(hr)){ - WCHAR randomName[MAX_PATH]; - StringFromGUID2(guid, randomName, ARRAYSIZE(randomName)); - - m_tempFilePath = new char[MAX_PATH]; - sprintf(m_tempFilePath, "%s\\%S", s_tempDirPath, randomName); - HANDLE file = CreateFile((LPCWSTR)m_tempFilePath, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - if (file != INVALID_HANDLE_VALUE){ - DWORD bytesWritten; - if (WriteFile(file, buff, buffLen, &bytesWritten, NULL)){ - hr = S_OK; - } - else{ - DeleteTempImageFile(); - } - - CloseHandle(file); - } - } - - if (!SUCCEEDED(hr) && m_tempFilePath){ - delete m_tempFilePath; - m_tempFilePath = NULL; - } - - - return hr; -} - - -HRESULT WindowsToastNotification::EnsureTempDir() -{ - // Check for the Dir already existing or easy creation - HRESULT hr = E_FAIL; - if (CreateDirectory((LPCWSTR)s_tempDirPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS){ - hr = S_OK; - } else { - // NOTE: This makes an assumption that the path is a FULL LENGTH PATH. Therefore the first folder starts at the - // 4th character. ie. "c:\path\to\temp\dir" - - // It's possible that multiple directories need to be created. - for (int i = 3; i < ARRAYSIZE(s_tempDirPath); i++) { - char c = s_tempDirPath[i]; - if (c == '\\'){ - //Substring until this char - char* temp = new char[i + 1]; - strncpy(temp, s_tempDirPath, i); - temp[i] = 0; - if (!CreateDirectory((LPCWSTR)temp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS){ - delete temp; - return hr; - } - - delete temp; - } - } - - //Try to create the full path one more time. This will take care of paths that don't end with a slash - if (CreateDirectory((LPCWSTR)s_tempDirPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS){ - hr = S_OK; - } - } - - return hr; -} - - -HRESULT WindowsToastNotification::DeleteTempImageFile() -{ - if (DeleteFile((LPCWSTR)m_tempFilePath)){ - return S_OK; - } - - return E_FAIL; -} - - -HRESULT WindowsToastNotification::EnsureShortcut() -{ - HRESULT hr = E_FAIL; - char shortcut[MAX_PATH]; - DWORD charsWritten = GetEnvironmentVariable(L"APPDATA", (LPWSTR)shortcut, MAX_PATH); - - if (charsWritten > 0) { - char shortcutCat[MAX_PATH]; - sprintf(shortcutCat, SHORTCUT_FORMAT, s_appName); - errno_t concatErr = strcat_s(shortcut, ARRAYSIZE(shortcut), shortcutCat); - if (concatErr == 0) { - DWORD attr = GetFileAttributes((LPCWSTR)shortcut); - bool exists = attr < 0xFFFFFFF; - if (exists){ - hr = S_OK; - } - else{ - WCHAR path[MAX_PATH]; - mbstowcs(path, shortcut, ARRAYSIZE(path)); - hr = CreateShortcut(path); - } - } - } - - return hr; -} - - -HRESULT WindowsToastNotification::CreateShortcut(WCHAR* path) -{ - HRESULT hr = E_FAIL; - char exePath[MAX_PATH]; - DWORD charsWritten = GetModuleFileNameEx(GetCurrentProcess(), nullptr, (LPWSTR)exePath, ARRAYSIZE(exePath)); - if (charsWritten > 0) { - PROPVARIANT propVariant; - ComPtr shellLink; - hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - do{ - BREAK_IF_BAD(hr); - - hr = shellLink->SetPath((LPCWSTR)exePath); - BREAK_IF_BAD(hr); - - ComPtr propStore; - hr = shellLink.As(&propStore); - BREAK_IF_BAD(hr); - - WCHAR wAppName[MAX_PATH]; - swprintf(wAppName, ARRAYSIZE(wAppName), L"%S", s_appName); - hr = InitPropVariantFromString(wAppName, &propVariant); - BREAK_IF_BAD(hr); - - hr = propStore->SetValue(PKEY_AppUserModel_ID, propVariant); - BREAK_IF_BAD(hr); - - hr = propStore->Commit(); - BREAK_IF_BAD(hr); - - ComPtr persistFile; - hr = shellLink.As(&persistFile); - BREAK_IF_BAD(hr); - - hr = persistFile->Save(path, TRUE); - } while (FALSE); - - PropVariantClear(&propVariant); - } - - return hr; -} - -ToastEventHandler::ToastEventHandler(WindowsToastNotification* notification) +/* +/ Toast Event Handler +*/ +ToastEventHandler::ToastEventHandler(WindowsToastNotification* notification, content::DesktopNotificationDelegate* delegate) { m_ref = 1; m_notification = notification; + n_delegate = delegate; } - ToastEventHandler::~ToastEventHandler() { // Empty } - IFACEMETHODIMP ToastEventHandler::Invoke(IToastNotification* sender, IInspectable* args) { - //Notification "activated" (clicked) - if (m_notification != NULL){ + // Notification "activated" (clicked) + n_delegate->NotificationClick(); + + if (m_notification != NULL) { m_notification->NotificationClicked(); } + return S_OK; } - IFACEMETHODIMP ToastEventHandler::Invoke(IToastNotification* sender, IToastDismissedEventArgs* e) -{ - //Notification dismissed - if (m_notification != NULL){ +{ + // Notification dismissed + n_delegate->NotificationClosed(); + + if (m_notification != NULL) { m_notification->NotificationDismissed(); + } + return S_OK; } diff --git a/brightray/browser/win/windows_toast_notification.h b/brightray/browser/win/windows_toast_notification.h index ff745a8aac1..8c43326ad05 100644 --- a/brightray/browser/win/windows_toast_notification.h +++ b/brightray/browser/win/windows_toast_notification.h @@ -1,12 +1,22 @@ +// Copyright (c) 2015 Felix Rieseberg . All rights reserved. +// Copyright (c) 2015 Ryan McShane and Brandon Smith +// Thanks to both of those folks mentioned above who first thought up a bunch of this code +// and released it as MIT to the world. + #ifndef __WINDOWS_TOAST_NOTIFICATION_H__ #define __WINDOWS_TOAST_NOTIFICATION_H__ #define WIN32_LEAN_AND_MEAN +#include "content/public/browser/desktop_notification_delegate.h" +#include "content/public/common/platform_notification_data.h" +#include "base/bind.h" + #include #include #include #include +#include using namespace Microsoft::WRL; using namespace ABI::Windows::UI::Notifications; @@ -24,35 +34,24 @@ namespace WinToasts{ { private: static char s_appName[MAX_PATH]; - static char s_tempDirPath[MAX_PATH]; - - char* m_tempFilePath; ToastEventHandler* m_eventHandler; + content::DesktopNotificationDelegate* n_delegate; - HRESULT GetToastXml(IToastNotificationManagerStatics* toastMgr, const WCHAR* title, const WCHAR* msg, const char* img, IXmlDocument** toastXml); + HRESULT GetToastXml(IToastNotificationManagerStatics* toastMgr, const WCHAR* title, const WCHAR* msg, std::string iconPath, IXmlDocument** toastXml); HRESULT SetXmlText(IXmlDocument* doc, const WCHAR* text); HRESULT SetXmlText(IXmlDocument* doc, const WCHAR* title, const WCHAR* body); - HRESULT SetXmlImage(IXmlDocument* doc); + HRESULT SetXmlImage(IXmlDocument* doc, std::string iconPath); HRESULT GetTextNodeList(HSTRING* tag, IXmlDocument* doc, IXmlNodeList** nodeList, UINT32 reqLength); HRESULT AppendTextToXml(IXmlDocument* doc, IXmlNode* node, const WCHAR* text); HRESULT SetupCallbacks(IToastNotification* toast); HRESULT CreateHString(const WCHAR* source, HSTRING* dest); - HRESULT CreateTempImageFile(const char* base64); - HRESULT ConvertBase64DataToImage(const char* base64, BYTE** buff, DWORD* buffLen); - HRESULT WriteToFile(BYTE* buff, DWORD buffLen); - HRESULT EnsureTempDir(); - HRESULT DeleteTempImageFile(); - HRESULT EnsureShortcut(); - HRESULT CreateShortcut(WCHAR* path); public: - WindowsToastNotification(const char* appName); + WindowsToastNotification(const char* appName, content::DesktopNotificationDelegate* delegate); ~WindowsToastNotification(); - void ShowNotification(const WCHAR* title, const WCHAR* msg, const char* img); + void ShowNotification(const WCHAR* title, const WCHAR* msg, std::string iconPath); void NotificationClicked(); void NotificationDismissed(); - - static void InitSystemProps(char* appName, char* tempDirPath); }; @@ -62,9 +61,10 @@ namespace WinToasts{ private: ULONG m_ref; WindowsToastNotification* m_notification; + content::DesktopNotificationDelegate* n_delegate; public: - ToastEventHandler(WindowsToastNotification* notification); + ToastEventHandler(WindowsToastNotification* notification, content::DesktopNotificationDelegate* delegate); ~ToastEventHandler(); IFACEMETHODIMP Invoke(IToastNotification* sender, IInspectable* args); IFACEMETHODIMP Invoke(IToastNotification* sender, IToastDismissedEventArgs* e); From 8dbeca8c7fda975c0204dfa3c693da31f7870adc Mon Sep 17 00:00:00 2001 From: Jason Poon Date: Sat, 7 Nov 2015 22:11:15 -0800 Subject: [PATCH 3/3] Windows Toasts: Hide(), Runtime Check, Header Cleanup --- .../browser/win/notification_presenter_win.cc | 42 ++++---- .../browser/win/notification_presenter_win.h | 16 ++-- .../browser/win/windows_toast_notification.cc | 96 ++++++++----------- .../browser/win/windows_toast_notification.h | 54 +++++------ 4 files changed, 94 insertions(+), 114 deletions(-) diff --git a/brightray/browser/win/notification_presenter_win.cc b/brightray/browser/win/notification_presenter_win.cc index 925fab17e19..8870de36e3e 100644 --- a/brightray/browser/win/notification_presenter_win.cc +++ b/brightray/browser/win/notification_presenter_win.cc @@ -1,30 +1,15 @@ // Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-CHROMIUM file. #include "browser/win/notification_presenter_win.h" -#include "base/strings/string_util.h" #include "base/win/windows_version.h" #include "content/public/browser/desktop_notification_delegate.h" #include "content/public/common/platform_notification_data.h" #include "third_party/skia/include/core/SkBitmap.h" #include "common/application_info.h" -#include -#include -#include - -// Windows Header -#define WIN32_LEAN_AND_MEAN - -#include -#include -#include -#include -#include -#include "windows_toast_notification.h" - #pragma comment(lib, "runtimeobject.lib") #pragma comment(lib, "Crypt32.lib") @@ -42,6 +27,7 @@ NotificationPresenter* NotificationPresenter::Create() { } NotificationPresenterWin::NotificationPresenterWin() { + m_lastNotification = nullptr; } NotificationPresenterWin::~NotificationPresenterWin() { @@ -57,17 +43,25 @@ void NotificationPresenterWin::ShowNotification( std::wstring body = data.body; std::string iconPath = data.icon.spec(); std::string appName = GetApplicationName(); + + // toast notification supported in version >= Windows 8 + // for prior versions, use Tray.displayBalloon + if (base::win::GetVersion() >= base::win::VERSION_WIN8) { + wtn = new WindowsToastNotification(appName.c_str(), delegate_ptr.release()); + wtn->ShowNotification(title.c_str(), body.c_str(), iconPath, m_lastNotification); + } - WinToasts::WindowsToastNotification* wtn = new WinToasts::WindowsToastNotification(appName.c_str(), delegate_ptr.release()); - wtn->ShowNotification(title.c_str(), body.c_str(), iconPath); + if (cancel_callback) { + *cancel_callback = base::Bind( + &NotificationPresenterWin::RemoveNotification, + base::Unretained(this)); + } } -void NotificationPresenterWin::CancelNotification() { - // No can do. -} - -void NotificationPresenterWin::DeleteNotification() { - // No can do. +void NotificationPresenterWin::RemoveNotification() { + if (m_lastNotification != nullptr && wtn != NULL) { + wtn->DismissNotification(m_lastNotification); + } } } // namespace brightray \ No newline at end of file diff --git a/brightray/browser/win/notification_presenter_win.h b/brightray/browser/win/notification_presenter_win.h index bed6eea9902..a2ecd81b952 100644 --- a/brightray/browser/win/notification_presenter_win.h +++ b/brightray/browser/win/notification_presenter_win.h @@ -1,5 +1,5 @@ // Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-CHROMIUM file. @@ -19,8 +19,12 @@ #include "base/compiler_specific.h" #include "browser/notification_presenter.h" +#include "windows_toast_notification.h" #include +#include +#include +#include namespace brightray { @@ -29,7 +33,6 @@ class NotificationPresenterWin : public NotificationPresenter { NotificationPresenterWin(); ~NotificationPresenterWin(); - // NotificationPresenter: void ShowNotification( const content::PlatformNotificationData&, const SkBitmap& icon, @@ -39,11 +42,10 @@ class NotificationPresenterWin : public NotificationPresenter { void RemoveNotification(); private: - - void CancelNotification(); - void DeleteNotification(); + WinToasts::WindowsToastNotification* wtn; + Microsoft::WRL::ComPtr m_lastNotification; }; -} // namespace brightray +} // namespace -#endif +#endif // BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_WIN_H_ diff --git a/brightray/browser/win/windows_toast_notification.cc b/brightray/browser/win/windows_toast_notification.cc index 2bf262e2175..5d306032d68 100644 --- a/brightray/browser/win/windows_toast_notification.cc +++ b/brightray/browser/win/windows_toast_notification.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. // Copyright (c) 2015 Ryan McShane and Brandon Smith // Thanks to both of those folks mentioned above who first thought up a bunch of this code // and released it as MIT to the world. @@ -6,26 +6,41 @@ #include "windows_toast_notification.h" #include "content/public/browser/desktop_notification_delegate.h" -#include -#include -#include -#include -#include -#include -#include - using namespace WinToasts; -using namespace Windows::Foundation; +using namespace Microsoft::WRL; +using namespace ABI::Windows::UI::Notifications; +using namespace ABI::Windows::Data::Xml::Dom; #define BREAK_IF_BAD(hr) if(!SUCCEEDED(hr)) break; -char WindowsToastNotification::s_appName[MAX_PATH] = {}; - namespace WinToasts { +// Initialize Windows Runtime +static HRESULT init = Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); + WindowsToastNotification::WindowsToastNotification(const char* appName, content::DesktopNotificationDelegate* delegate) { - sprintf_s(s_appName, ARRAYSIZE(s_appName), "%s", appName); + HSTRING toastNotifMgrStr = NULL; + HSTRING appId = NULL; + + HRESULT hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager, &toastNotifMgrStr); + + hr = Windows::Foundation::GetActivationFactory(toastNotifMgrStr, &m_toastManager); + + WCHAR wAppName[MAX_PATH]; + swprintf(wAppName, ARRAYSIZE(wAppName), L"%S", appName); + hr = CreateHString(wAppName, &appId); + + m_toastManager->CreateToastNotifierWithId(appId, &m_toastNotifier); + + if (toastNotifMgrStr != NULL) { + WindowsDeleteString(toastNotifMgrStr); + } + + if (appId != NULL) { + WindowsDeleteString(appId); + } + m_eventHandler = NULL; n_delegate = delegate; } @@ -41,35 +56,14 @@ WindowsToastNotification::~WindowsToastNotification() } } -void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* msg, std::string iconPath) +void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* msg, std::string iconPath, ComPtr& toast) { - // Init using WRL - Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); - - HSTRING toastNotifMgrStr = NULL; - HSTRING appId = NULL; - HSTRING toastNotifStr = NULL; HRESULT hr; + HSTRING toastNotifStr = NULL; do { - hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager, &toastNotifMgrStr); - BREAK_IF_BAD(hr); - - ComPtr toastMgr; - hr = Windows::Foundation::GetActivationFactory(toastNotifMgrStr, &toastMgr); - BREAK_IF_BAD(hr); - ComPtr toastXml; - hr = GetToastXml(toastMgr.Get(), title, msg, iconPath, &toastXml); - BREAK_IF_BAD(hr); - - WCHAR wAppName[MAX_PATH]; - swprintf(wAppName, ARRAYSIZE(wAppName), L"%S", s_appName); - hr = CreateHString(wAppName, &appId); - BREAK_IF_BAD(hr); - - ComPtr notifier; - toastMgr->CreateToastNotifierWithId(appId, ¬ifier); + hr = GetToastXml(m_toastManager.Get(), title, msg, iconPath, &toastXml); BREAK_IF_BAD(hr); hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotification, &toastNotifStr); @@ -79,34 +73,26 @@ void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* hr = Windows::Foundation::GetActivationFactory(toastNotifStr, &toastFactory); BREAK_IF_BAD(hr); - ComPtr toast; hr = toastFactory->CreateToastNotification(toastXml.Get(), &toast); BREAK_IF_BAD(hr); hr = SetupCallbacks(toast.Get()); BREAK_IF_BAD(hr); - hr = notifier->Show(toast.Get()); + hr = m_toastNotifier->Show(toast.Get()); BREAK_IF_BAD(hr); n_delegate->NotificationDisplayed(); } while (FALSE); - if (toastNotifMgrStr != NULL) { - WindowsDeleteString(toastNotifMgrStr); - } - - if (appId != NULL) { - WindowsDeleteString(appId); - } - if (toastNotifStr != NULL) { WindowsDeleteString(toastNotifStr); } +} - if (!SUCCEEDED(hr)) { - delete this; - } +void WindowsToastNotification::DismissNotification(ComPtr toast) +{ + m_toastNotifier->Hide(toast.Get()); } void WindowsToastNotification::NotificationClicked() @@ -120,7 +106,7 @@ void WindowsToastNotification::NotificationDismissed() } HRESULT WindowsToastNotification::GetToastXml( - IToastNotificationManagerStatics* toastMgr, + IToastNotificationManagerStatics* toastManager, const WCHAR* title, const WCHAR* msg, std::string iconPath, @@ -131,7 +117,7 @@ HRESULT WindowsToastNotification::GetToastXml( if (title == NULL || msg == NULL) { // Single line toast templateType = iconPath.length() == 0 ? ToastTemplateType_ToastText01 : ToastTemplateType_ToastImageAndText01; - hr = toastMgr->GetTemplateContent(templateType, toastXml); + hr = m_toastManager->GetTemplateContent(templateType, toastXml); if (SUCCEEDED(hr)) { const WCHAR* text = title != NULL ? title : msg; hr = SetXmlText(*toastXml, text); @@ -139,7 +125,7 @@ HRESULT WindowsToastNotification::GetToastXml( } else { // Title and body toast templateType = iconPath.length() == 0 ? ToastTemplateType_ToastText02 : ToastTemplateType_ToastImageAndText02; - hr = toastMgr->GetTemplateContent(templateType, toastXml); + hr = toastManager->GetTemplateContent(templateType, toastXml); if (SUCCEEDED(hr)) { hr = SetXmlText(*toastXml, title, msg); } @@ -164,7 +150,7 @@ HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* tex ComPtr nodeList; HRESULT hr = GetTextNodeList(&tag, doc, &nodeList, 1); - do{ + do { BREAK_IF_BAD(hr); ComPtr node; @@ -186,7 +172,7 @@ HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* tit HSTRING tag = NULL; ComPtr nodeList; HRESULT hr = GetTextNodeList(&tag, doc, &nodeList, 2); - do{ + do { BREAK_IF_BAD(hr); ComPtr node; diff --git a/brightray/browser/win/windows_toast_notification.h b/brightray/browser/win/windows_toast_notification.h index 8c43326ad05..474d55c767d 100644 --- a/brightray/browser/win/windows_toast_notification.h +++ b/brightray/browser/win/windows_toast_notification.h @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Felix Rieseberg . All rights reserved. +// Copyright (c) 2015 Felix Rieseberg and Jason Poon . All rights reserved. // Copyright (c) 2015 Ryan McShane and Brandon Smith // Thanks to both of those folks mentioned above who first thought up a bunch of this code // and released it as MIT to the world. @@ -6,24 +6,19 @@ #ifndef __WINDOWS_TOAST_NOTIFICATION_H__ #define __WINDOWS_TOAST_NOTIFICATION_H__ -#define WIN32_LEAN_AND_MEAN - #include "content/public/browser/desktop_notification_delegate.h" #include "content/public/common/platform_notification_data.h" #include "base/bind.h" #include #include -#include #include -#include using namespace Microsoft::WRL; using namespace ABI::Windows::UI::Notifications; using namespace ABI::Windows::Foundation; -using namespace ABI::Windows::Data::Xml::Dom; -namespace WinToasts{ +namespace WinToasts { typedef ITypedEventHandler DesktopToastActivatedEventHandler; typedef ITypedEventHandler DesktopToastDismissedEventHandler; @@ -32,37 +27,35 @@ namespace WinToasts{ class WindowsToastNotification { - private: - static char s_appName[MAX_PATH]; - ToastEventHandler* m_eventHandler; - content::DesktopNotificationDelegate* n_delegate; - - HRESULT GetToastXml(IToastNotificationManagerStatics* toastMgr, const WCHAR* title, const WCHAR* msg, std::string iconPath, IXmlDocument** toastXml); - HRESULT SetXmlText(IXmlDocument* doc, const WCHAR* text); - HRESULT SetXmlText(IXmlDocument* doc, const WCHAR* title, const WCHAR* body); - HRESULT SetXmlImage(IXmlDocument* doc, std::string iconPath); - HRESULT GetTextNodeList(HSTRING* tag, IXmlDocument* doc, IXmlNodeList** nodeList, UINT32 reqLength); - HRESULT AppendTextToXml(IXmlDocument* doc, IXmlNode* node, const WCHAR* text); - HRESULT SetupCallbacks(IToastNotification* toast); - HRESULT CreateHString(const WCHAR* source, HSTRING* dest); - public: WindowsToastNotification(const char* appName, content::DesktopNotificationDelegate* delegate); ~WindowsToastNotification(); - void ShowNotification(const WCHAR* title, const WCHAR* msg, std::string iconPath); + void ShowNotification(const WCHAR* title, const WCHAR* msg, std::string iconPath, ComPtr& toast); + void DismissNotification(ComPtr toast); void NotificationClicked(); void NotificationDismissed(); + + private: + ToastEventHandler* m_eventHandler; + + content::DesktopNotificationDelegate* n_delegate; + ComPtr m_toastManager; + ComPtr m_toastNotifier; + + HRESULT GetToastXml(IToastNotificationManagerStatics* toastManager, const WCHAR* title, const WCHAR* msg, std::string iconPath, ABI::Windows::Data::Xml::Dom::IXmlDocument** toastXml); + HRESULT SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, const WCHAR* text); + HRESULT SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, const WCHAR* title, const WCHAR* body); + HRESULT SetXmlImage(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, std::string iconPath); + HRESULT GetTextNodeList(HSTRING* tag, ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, ABI::Windows::Data::Xml::Dom::IXmlNodeList** nodeList, UINT32 reqLength); + HRESULT AppendTextToXml(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc, ABI::Windows::Data::Xml::Dom::IXmlNode* node, const WCHAR* text); + HRESULT SetupCallbacks(IToastNotification* toast); + HRESULT CreateHString(const WCHAR* source, HSTRING* dest); }; class ToastEventHandler : - public Implements < DesktopToastActivatedEventHandler, DesktopToastDismissedEventHandler > + public Implements { - private: - ULONG m_ref; - WindowsToastNotification* m_notification; - content::DesktopNotificationDelegate* n_delegate; - public: ToastEventHandler(WindowsToastNotification* notification, content::DesktopNotificationDelegate* delegate); ~ToastEventHandler(); @@ -92,6 +85,11 @@ namespace WinToasts{ return E_NOINTERFACE; } + + private: + ULONG m_ref; + WindowsToastNotification* m_notification; + content::DesktopNotificationDelegate* n_delegate; }; } // namespace