| 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());
|
| + }
|
| +}
|
|
|