diff --git a/brightray/browser/linux/libnotify_notification.cc b/brightray/browser/linux/libnotify_notification.cc new file mode 100644 index 000000000000..98aa3ffd8a7d --- /dev/null +++ b/brightray/browser/linux/libnotify_notification.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/linux/libnotify_notification.h" + +#include "base/files/file_enumerator.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "browser/notification_delegate.h" +#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h" +#include "common/application_info.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace brightray { + +namespace { + +bool unity_has_result = false; +bool unity_result = false; + +bool UnityIsRunning() { + if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER")) + return true; + + if (unity_has_result) + return unity_result; + + unity_has_result = true; + + // Look for the presence of libunity as our hint that we're under Ubuntu. + base::FileEnumerator enumerator(base::FilePath("/usr/lib"), + false, base::FileEnumerator::FILES); + base::FilePath haystack; + while (!((haystack = enumerator.Next()).empty())) { + if (base::StartsWith(haystack.value(), "/usr/lib/libunity-", + base::CompareCase::SENSITIVE)) { + unity_result = true; + break; + } + } + + return unity_result; +} + +void log_and_clear_error(GError* error, const char* context) { + LOG(ERROR) << context + << ": domain=" << error->domain + << " code=" << error->code + << " message=\"" << error->message << '"'; + g_error_free(error); +} + +} // namespace + +// static +Notification* Notification::Create(NotificationDelegate* delegate, + NotificationPresenter* presenter) { + return new LibnotifyNotification(delegate, presenter); +} + +// static +LibNotifyLoader LibnotifyNotification::libnotify_loader_; + +// static +bool LibnotifyNotification::Initialize() { + if (!libnotify_loader_.Load("libnotify.so.4") && + !libnotify_loader_.Load("libnotify.so.1") && + !libnotify_loader_.Load("libnotify.so")) { + return false; + } + if (!libnotify_loader_.notify_is_initted() && + !libnotify_loader_.notify_init(GetApplicationName().c_str())) { + return false; + } + return true; +} + +LibnotifyNotification::LibnotifyNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter) + : Notification(delegate, presenter), + notification_(nullptr) { +} + +LibnotifyNotification::~LibnotifyNotification() { + g_object_unref(notification_); +} + +void LibnotifyNotification::Show(const base::string16& title, + const base::string16& body, + const GURL& icon_url, + const SkBitmap& icon) { + notification_ = libnotify_loader_.notify_notification_new( + base::UTF16ToUTF8(title).c_str(), + base::UTF16ToUTF8(body).c_str(), + nullptr); + + g_signal_connect( + notification_, "closed", G_CALLBACK(OnNotificationClosedThunk), this); + + // NB: On Unity, adding a notification action will cause the notification + // to display as a modal dialog box. Testing for distros that have "Unity + // Zen Nature" is difficult, we will test for the presence of the indicate + // dbus service + if (!UnityIsRunning()) { + libnotify_loader_.notify_notification_add_action( + notification_, "default", "View", OnNotificationViewThunk, this, + nullptr); + } + + if (!icon.drawsNothing()) { + GdkPixbuf* pixbuf = libgtk2ui::GdkPixbufFromSkBitmap(icon); + libnotify_loader_.notify_notification_set_image_from_pixbuf( + notification_, pixbuf); + libnotify_loader_.notify_notification_set_timeout( + notification_, NOTIFY_EXPIRES_DEFAULT); + g_object_unref(pixbuf); + } + + GError* error = nullptr; + libnotify_loader_.notify_notification_show(notification_, &error); + if (error) { + log_and_clear_error(error, "notify_notification_show"); + NotificationFailed(); + return; + } + + delegate()->NotificationDisplayed(); +} + +void LibnotifyNotification::Dismiss() { + GError* error = nullptr; + libnotify_loader_.notify_notification_close(notification_, &error); + if (error) { + log_and_clear_error(error, "notify_notification_close"); + Destroy(); + } +} + +void LibnotifyNotification::OnNotificationClosed( + NotifyNotification* notification) { + delegate()->NotificationClosed(); + Destroy(); +} + +void LibnotifyNotification::OnNotificationView( + NotifyNotification* notification, char* action) { + delegate()->NotificationClick(); + Destroy(); +} + +void LibnotifyNotification::NotificationFailed() { + delegate()->NotificationFailed(); + Destroy(); +} + +} // namespace brightray diff --git a/brightray/browser/linux/libnotify_notification.h b/brightray/browser/linux/libnotify_notification.h new file mode 100644 index 000000000000..be2f4280bc4f --- /dev/null +++ b/brightray/browser/linux/libnotify_notification.h @@ -0,0 +1,46 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ +#define BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ + +#include "browser/linux/libnotify_loader.h" +#include "browser/notification.h" +#include "ui/base/glib/glib_signal.h" + +namespace brightray { + +class LibnotifyNotification : public Notification { + public: + LibnotifyNotification(NotificationDelegate* delegate, + NotificationPresenter* presenter); + virtual ~LibnotifyNotification(); + + static bool Initialize(); + + // Notification: + void Show(const base::string16& title, + const base::string16& msg, + const GURL& icon_url, + const SkBitmap& icon) override; + void Dismiss() override; + + private: + CHROMEG_CALLBACK_0(LibnotifyNotification, void, OnNotificationClosed, + NotifyNotification*); + CHROMEG_CALLBACK_1(LibnotifyNotification, void, OnNotificationView, + NotifyNotification*, char*); + + void NotificationFailed(); + + static LibNotifyLoader libnotify_loader_; + + NotifyNotification* notification_; + + DISALLOW_COPY_AND_ASSIGN(LibnotifyNotification); +}; + +} // namespace brightray + +#endif // BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ diff --git a/brightray/browser/linux/notification_presenter_linux.cc b/brightray/browser/linux/notification_presenter_linux.cc index 253ec26fe0ca..c846fca6659e 100644 --- a/brightray/browser/linux/notification_presenter_linux.cc +++ b/brightray/browser/linux/notification_presenter_linux.cc @@ -5,175 +5,21 @@ #include "browser/linux/notification_presenter_linux.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 "common/application_info.h" -#include "content/public/browser/desktop_notification_delegate.h" -#include "content/public/common/platform_notification_data.h" -#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h" -#include "third_party/skia/include/core/SkBitmap.h" +#include "browser/linux/libnotify_notification.h" namespace brightray { -namespace { - -bool unity_has_result = false; -bool unity_result = false; - -bool UnityIsRunning() { - if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER")) - return true; - - if (unity_has_result) - return unity_result; - - unity_has_result = true; - - // Look for the presence of libunity as our hint that we're under Ubuntu. - base::FileEnumerator enumerator(base::FilePath("/usr/lib"), - false, base::FileEnumerator::FILES); - base::FilePath haystack; - while (!((haystack = enumerator.Next()).empty())) { - if (base::StartsWith(haystack.value(), "/usr/lib/libunity-", base::CompareCase::SENSITIVE)) { - unity_result = true; - break; - } - } - - return unity_result; -} - -void log_and_clear_error(GError* error, const char* context) { - LOG(ERROR) << context - << ": domain=" << error->domain - << " code=" << error->code - << " message=\"" << error->message << '"'; - g_error_free(error); -} - -content::DesktopNotificationDelegate* GetDelegateFromNotification( - NotifyNotification* notification) { - return static_cast( - g_object_get_data(G_OBJECT(notification), "delegate")); -} - -} // namespace - // static NotificationPresenter* NotificationPresenter::Create() { - scoped_ptr presenter( - new NotificationPresenterLinux); - if (presenter->Init()) - return presenter.release(); - else + if (!LibnotifyNotification::Initialize()) return nullptr; + return new NotificationPresenterLinux; } -NotificationPresenterLinux::NotificationPresenterLinux() - : notifications_(nullptr) { +NotificationPresenterLinux::NotificationPresenterLinux() { } NotificationPresenterLinux::~NotificationPresenterLinux() { - // unref any outstanding notifications, and then free the list. - if (notifications_) - g_list_free_full(notifications_, g_object_unref); -} - -bool NotificationPresenterLinux::Init() { - if (!libnotify_loader_.Load("libnotify.so.4") && - !libnotify_loader_.Load("libnotify.so.1") && - !libnotify_loader_.Load("libnotify.so")) { - return false; - } - if (!libnotify_loader_.notify_is_initted() && - !libnotify_loader_.notify_init(GetApplicationName().c_str())) { - return false; - } - return true; -} - -void NotificationPresenterLinux::ShowNotification( - const content::PlatformNotificationData& data, - const SkBitmap& icon, - scoped_ptr delegate_ptr, - base::Closure* cancel_callback) { - std::string title = base::UTF16ToUTF8(data.title); - std::string body = base::UTF16ToUTF8(data.body); - NotifyNotification* notification = libnotify_loader_.notify_notification_new( - title.c_str(), body.c_str(), nullptr); - - content::DesktopNotificationDelegate* delegate = delegate_ptr.release(); - - g_object_set_data_full(G_OBJECT(notification), "delegate", delegate, operator delete); - g_signal_connect(notification, "closed", G_CALLBACK(OnNotificationClosedThunk), this); - - // NB: On Unity, adding a notification action will cause the notification - // to display as a modal dialog box. Testing for distros that have "Unity - // Zen Nature" is difficult, we will test for the presence of the indicate - // dbus service - if (!UnityIsRunning()) { - libnotify_loader_.notify_notification_add_action( - notification, "default", "View", OnNotificationViewThunk, this, nullptr); - } - - if (!icon.drawsNothing()) { - GdkPixbuf* pixbuf = libgtk2ui::GdkPixbufFromSkBitmap(icon); - libnotify_loader_.notify_notification_set_image_from_pixbuf( - notification, pixbuf); - libnotify_loader_.notify_notification_set_timeout( - notification, NOTIFY_EXPIRES_DEFAULT); - g_object_unref(pixbuf); - } - - GError* error = nullptr; - libnotify_loader_.notify_notification_show(notification, &error); - if (error) { - log_and_clear_error(error, "notify_notification_show"); - g_object_unref(notification); - return; - } - - notifications_ = g_list_append(notifications_, notification); - delegate->NotificationDisplayed(); - - if (cancel_callback) - *cancel_callback = base::Bind( - &NotificationPresenterLinux::CancelNotification, - base::Unretained(this), - notification); -} - -void NotificationPresenterLinux::CancelNotification(NotifyNotification* notification) { - GError* error = nullptr; - libnotify_loader_.notify_notification_close(notification, &error); - if (error) - log_and_clear_error(error, "notify_notification_close"); - - GetDelegateFromNotification(notification)->NotificationClosed(); - DeleteNotification(notification); -} - -void NotificationPresenterLinux::DeleteNotification(NotifyNotification* notification) { - notifications_ = g_list_remove(notifications_, notification); - g_object_unref(notification); -} - -void NotificationPresenterLinux::OnNotificationClosed(NotifyNotification* notification) { - if (!notification) - return; - GetDelegateFromNotification(notification)->NotificationClosed(); - DeleteNotification(notification); -} - -void NotificationPresenterLinux::OnNotificationView( - NotifyNotification* notification, char* action) { - if (!notification) - return; - GetDelegateFromNotification(notification)->NotificationClick(); - DeleteNotification(notification); } } // namespace brightray diff --git a/brightray/browser/linux/notification_presenter_linux.h b/brightray/browser/linux/notification_presenter_linux.h index d70d1c468a00..ef4367994847 100644 --- a/brightray/browser/linux/notification_presenter_linux.h +++ b/brightray/browser/linux/notification_presenter_linux.h @@ -6,12 +6,7 @@ #ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_LINUX_H_ #define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_LINUX_H_ -#include - -#include "base/compiler_specific.h" -#include "browser/linux/libnotify_loader.h" #include "browser/notification_presenter.h" -#include "ui/base/glib/glib_signal.h" namespace brightray { @@ -20,34 +15,8 @@ class NotificationPresenterLinux : public NotificationPresenter { NotificationPresenterLinux(); ~NotificationPresenterLinux(); - bool Init(); - void RemoveNotification(NotifyNotification *notification); - private: - // NotificationPresenter: - void ShowNotification( - const content::PlatformNotificationData&, - const SkBitmap& icon, - scoped_ptr delegate, - base::Closure* cancel_callback) override; - - void CancelNotification(NotifyNotification* notification); - void DeleteNotification(NotifyNotification* notification); - - CHROMEG_CALLBACK_0(NotificationPresenterLinux, void, OnNotificationClosed, NotifyNotification*); - CHROMEG_CALLBACK_1(NotificationPresenterLinux, void, OnNotificationView, NotifyNotification*, - char*); - - LibNotifyLoader libnotify_loader_; - - // A list of all open NotifyNotification objects. - // We do lookups here both by NotifyNotification object (when the user - // clicks a notification) and by the ID - // tuple (when the browser asks to dismiss a notification). So it's not - // a map. - // Entries in this list count as refs, so removal from this list should - // always go with g_object_unref(). - GList* notifications_; + DISALLOW_COPY_AND_ASSIGN(NotificationPresenterLinux); }; } // namespace brightray diff --git a/brightray/browser/mac/cocoa_notification.h b/brightray/browser/mac/cocoa_notification.h index 9f967a5e5f91..b1709568210e 100644 --- a/brightray/browser/mac/cocoa_notification.h +++ b/brightray/browser/mac/cocoa_notification.h @@ -32,7 +32,6 @@ class CocoaNotification : public Notification { NSUserNotification* notification() const { return notification_; } private: - NotificationDelegate* delegate_; base::scoped_nsobject notification_; DISALLOW_COPY_AND_ASSIGN(CocoaNotification); diff --git a/brightray/filenames.gypi b/brightray/filenames.gypi index 4dc441d4b9e0..26c02679c052 100644 --- a/brightray/filenames.gypi +++ b/brightray/filenames.gypi @@ -73,6 +73,8 @@ 'browser/platform_notification_service.h', 'browser/linux/libnotify_loader.h', 'browser/linux/libnotify_loader.cc', + 'browser/linux/libnotify_notification.h', + 'browser/linux/libnotify_notification.cc', 'browser/linux/notification_presenter_linux.h', 'browser/linux/notification_presenter_linux.cc', 'browser/win/notification_presenter_win.h',