Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(585)

Unified Diff: chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc

Issue 252513004: Reland r265807: "Moves the notification icon out of th..." (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Disable tray size test on Vista since it won't have any effect on a new icon. Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc
diff --git a/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc b/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5be4f732d3da016bed13415c03a73727e0ce4606
--- /dev/null
+++ b/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.cc
@@ -0,0 +1,235 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h"
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+// Status Tray API
+
+// The folowing describes the interface to the undocumented Windows Exporer APIs
+// for manipulating with the status tray area. This code should be used with
+// care as it can change with versions (even minor versions) of Windows.
+
+// ITrayNotify is an interface describing the API for manipulating the state of
+// the Windows notification area, as well as for registering for change
+// notifications.
+class __declspec(uuid("FB852B2C-6BAD-4605-9551-F15F87830935")) ITrayNotify
+ : public IUnknown {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE
+ RegisterCallback(INotificationCB* callback) = 0;
+ virtual HRESULT STDMETHODCALLTYPE
+ SetPreference(const NOTIFYITEM* notify_item) = 0;
+ virtual HRESULT STDMETHODCALLTYPE EnableAutoTray(BOOL enabled) = 0;
+};
+
+// ITrayNotifyWin8 is the interface that replaces ITrayNotify for newer versions
+// of Windows.
+class __declspec(uuid("D133CE13-3537-48BA-93A7-AFCD5D2053B4")) ITrayNotifyWin8
+ : public IUnknown {
+ public:
+ virtual HRESULT STDMETHODCALLTYPE
+ RegisterCallback(INotificationCB* callback, unsigned long*) = 0;
+ virtual HRESULT STDMETHODCALLTYPE UnregisterCallback(unsigned long*) = 0;
+ virtual HRESULT STDMETHODCALLTYPE SetPreference(NOTIFYITEM const*) = 0;
+ virtual HRESULT STDMETHODCALLTYPE EnableAutoTray(BOOL) = 0;
+ virtual HRESULT STDMETHODCALLTYPE DoAction(BOOL) = 0;
+};
+
+const CLSID CLSID_TrayNotify = {
+ 0x25DEAD04,
+ 0x1EAC,
+ 0x4911,
+ {0x9E, 0x3A, 0xAD, 0x0A, 0x4A, 0xB5, 0x60, 0xFD}};
+
+} // namespace
+
+StatusTrayStateChangerWin::StatusTrayStateChangerWin(UINT icon_id, HWND window)
+ : interface_version_(INTERFACE_VERSION_UNKNOWN),
+ icon_id_(icon_id),
+ window_(window) {
+ wchar_t module_name[MAX_PATH];
+ ::GetModuleFileName(NULL, module_name, MAX_PATH);
+
+ file_name_ = module_name;
+}
+
+void StatusTrayStateChangerWin::EnsureTrayIconVisible() {
+ DCHECK(CalledOnValidThread());
+
+ if (!CreateTrayNotify()) {
+ VLOG(1) << "Unable to create COM object for ITrayNotify.";
+ return;
+ }
+
+ scoped_ptr<NOTIFYITEM> notify_item = RegisterCallback();
+
+ // If the user has already hidden us explicitly, try to honor their choice by
+ // not changing anything.
+ if (notify_item->preference == PREFERENCE_SHOW_NEVER)
+ return;
+
+ // If we are already on the taskbar, return since nothing needs to be done.
+ if (notify_item->preference == PREFERENCE_SHOW_ALWAYS)
+ return;
+
+ notify_item->preference = PREFERENCE_SHOW_ALWAYS;
+
+ SendNotifyItemUpdate(notify_item.Pass());
+}
+
+STDMETHODIMP_(ULONG) StatusTrayStateChangerWin::AddRef() {
+ DCHECK(CalledOnValidThread());
+ return base::win::IUnknownImpl::AddRef();
+}
+
+STDMETHODIMP_(ULONG) StatusTrayStateChangerWin::Release() {
+ DCHECK(CalledOnValidThread());
+ return base::win::IUnknownImpl::Release();
+}
+
+STDMETHODIMP StatusTrayStateChangerWin::QueryInterface(REFIID riid,
+ PVOID* ptr_void) {
+ DCHECK(CalledOnValidThread());
+ if (riid == __uuidof(INotificationCB)) {
+ *ptr_void = static_cast<INotificationCB*>(this);
+ AddRef();
+ return S_OK;
+ }
+
+ return base::win::IUnknownImpl::QueryInterface(riid, ptr_void);
+}
+
+STDMETHODIMP StatusTrayStateChangerWin::Notify(ULONG event,
+ NOTIFYITEM* notify_item) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(notify_item);
+ if (notify_item->hwnd != window_ || notify_item->id != icon_id_ ||
+ base::string16(notify_item->exe_name) != file_name_) {
+ return S_OK;
+ }
+
+ notify_item_.reset(new NOTIFYITEM(*notify_item));
+ return S_OK;
+}
+
+StatusTrayStateChangerWin::~StatusTrayStateChangerWin() {
+ DCHECK(CalledOnValidThread());
+}
+
+bool StatusTrayStateChangerWin::CreateTrayNotify() {
+ DCHECK(CalledOnValidThread());
+
+ tray_notify_.Release(); // Release so this method can be called more than
+ // once.
+
+ HRESULT hr = tray_notify_.CreateInstance(CLSID_TrayNotify);
+ if (FAILED(hr))
+ return false;
+
+ base::win::ScopedComPtr<ITrayNotifyWin8> tray_notify_win8;
+ hr = tray_notify_win8.QueryFrom(tray_notify_);
+ if (SUCCEEDED(hr)) {
+ interface_version_ = INTERFACE_VERSION_WIN8;
+ return true;
+ }
+
+ base::win::ScopedComPtr<ITrayNotify> tray_notify_legacy;
+ hr = tray_notify_legacy.QueryFrom(tray_notify_);
+ if (SUCCEEDED(hr)) {
+ interface_version_ = INTERFACE_VERSION_LEGACY;
+ return true;
+ }
+
+ return false;
+}
+
+scoped_ptr<NOTIFYITEM> StatusTrayStateChangerWin::RegisterCallback() {
+ // |notify_item_| is used to store the result of the callback from
+ // Explorer.exe, which happens synchronously during
+ // RegisterCallbackWin8 or RegisterCallbackLegacy.
+ DCHECK(notify_item_.get() == NULL);
+
+ // TODO(dewittj): Add UMA logging here to report if either of our strategies
+ // has a tendency to fail on particular versions of Windows.
+ switch (interface_version_) {
+ case INTERFACE_VERSION_WIN8:
+ if (!RegisterCallbackWin8())
+ VLOG(1) << "Unable to successfully run RegisterCallbackWin8.";
+ break;
+ case INTERFACE_VERSION_LEGACY:
+ if (!RegisterCallbackLegacy())
+ VLOG(1) << "Unable to successfully run RegisterCallbackLegacy.";
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // Adding an intermediate scoped pointer here so that |notify_item_| is reset
+ // to NULL.
+ scoped_ptr<NOTIFYITEM> rv(notify_item_.release());
+ return rv.Pass();
+}
+
+bool StatusTrayStateChangerWin::RegisterCallbackWin8() {
+ base::win::ScopedComPtr<ITrayNotifyWin8> tray_notify_win8;
+ HRESULT hr = tray_notify_win8.QueryFrom(tray_notify_);
+ if (FAILED(hr))
+ return false;
+
+ // The following two lines cause Windows Explorer to call us back with all the
+ // existing tray icons and their preference. It would also presumably notify
+ // us if changes were made in realtime while we registered as a callback, but
+ // we just want to modify our own entry so we immediately unregister.
+ unsigned long callback_id = 0;
+ hr = tray_notify_win8->RegisterCallback(this, &callback_id);
+ tray_notify_win8->UnregisterCallback(&callback_id);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool StatusTrayStateChangerWin::RegisterCallbackLegacy() {
+ base::win::ScopedComPtr<ITrayNotify> tray_notify;
+ HRESULT hr = tray_notify.QueryFrom(tray_notify_);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ // The following two lines cause Windows Explorer to call us back with all the
+ // existing tray icons and their preference. It would also presumably notify
+ // us if changes were made in realtime while we registered as a callback. In
+ // this version of the API, there can be only one registered callback so it is
+ // better to unregister as soon as possible.
+ // TODO(dewittj): Try to notice if the notification area icon customization
+ // window is open and postpone this call until the user closes it;
+ // registering the callback while the window is open can cause stale data to
+ // be displayed to the user.
+ hr = tray_notify->RegisterCallback(this);
+ tray_notify->RegisterCallback(NULL);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ return true;
+}
+
+void StatusTrayStateChangerWin::SendNotifyItemUpdate(
+ scoped_ptr<NOTIFYITEM> notify_item) {
+ if (interface_version_ == INTERFACE_VERSION_LEGACY) {
+ base::win::ScopedComPtr<ITrayNotify> tray_notify;
+ HRESULT hr = tray_notify.QueryFrom(tray_notify_);
+ if (SUCCEEDED(hr))
+ tray_notify->SetPreference(notify_item.get());
+ } else if (interface_version_ == INTERFACE_VERSION_WIN8) {
+ base::win::ScopedComPtr<ITrayNotifyWin8> tray_notify;
+ HRESULT hr = tray_notify.QueryFrom(tray_notify_);
+ if (SUCCEEDED(hr))
+ tray_notify->SetPreference(notify_item.get());
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698