Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / status_icons / status_tray_state_changer_win.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h"
6
7 namespace {
8
9 ////////////////////////////////////////////////////////////////////////////////
10 // Status Tray API
11
12 // The folowing describes the interface to the undocumented Windows Exporer APIs
13 // for manipulating with the status tray area.  This code should be used with
14 // care as it can change with versions (even minor versions) of Windows.
15
16 // ITrayNotify is an interface describing the API for manipulating the state of
17 // the Windows notification area, as well as for registering for change
18 // notifications.
19 class __declspec(uuid("FB852B2C-6BAD-4605-9551-F15F87830935")) ITrayNotify
20     : public IUnknown {
21  public:
22   virtual HRESULT STDMETHODCALLTYPE
23       RegisterCallback(INotificationCB* callback) = 0;
24   virtual HRESULT STDMETHODCALLTYPE
25       SetPreference(const NOTIFYITEM* notify_item) = 0;
26   virtual HRESULT STDMETHODCALLTYPE EnableAutoTray(BOOL enabled) = 0;
27 };
28
29 // ITrayNotifyWin8 is the interface that replaces ITrayNotify for newer versions
30 // of Windows.
31 class __declspec(uuid("D133CE13-3537-48BA-93A7-AFCD5D2053B4")) ITrayNotifyWin8
32     : public IUnknown {
33  public:
34   virtual HRESULT STDMETHODCALLTYPE
35       RegisterCallback(INotificationCB* callback, unsigned long*) = 0;
36   virtual HRESULT STDMETHODCALLTYPE UnregisterCallback(unsigned long*) = 0;
37   virtual HRESULT STDMETHODCALLTYPE SetPreference(NOTIFYITEM const*) = 0;
38   virtual HRESULT STDMETHODCALLTYPE EnableAutoTray(BOOL) = 0;
39   virtual HRESULT STDMETHODCALLTYPE DoAction(BOOL) = 0;
40 };
41
42 const CLSID CLSID_TrayNotify = {
43     0x25DEAD04,
44     0x1EAC,
45     0x4911,
46     {0x9E, 0x3A, 0xAD, 0x0A, 0x4A, 0xB5, 0x60, 0xFD}};
47
48 }  // namespace
49
50 StatusTrayStateChangerWin::StatusTrayStateChangerWin(UINT icon_id, HWND window)
51     : interface_version_(INTERFACE_VERSION_UNKNOWN),
52       icon_id_(icon_id),
53       window_(window) {
54   wchar_t module_name[MAX_PATH];
55   ::GetModuleFileName(NULL, module_name, MAX_PATH);
56
57   file_name_ = module_name;
58 }
59
60 void StatusTrayStateChangerWin::EnsureTrayIconVisible() {
61   DCHECK(CalledOnValidThread());
62
63   if (!CreateTrayNotify()) {
64     VLOG(1) << "Unable to create COM object for ITrayNotify.";
65     return;
66   }
67
68   scoped_ptr<NOTIFYITEM> notify_item = RegisterCallback();
69
70   // If the user has already hidden us explicitly, try to honor their choice by
71   // not changing anything.
72   if (notify_item->preference == PREFERENCE_SHOW_NEVER)
73     return;
74
75   // If we are already on the taskbar, return since nothing needs to be done.
76   if (notify_item->preference == PREFERENCE_SHOW_ALWAYS)
77     return;
78
79   notify_item->preference = PREFERENCE_SHOW_ALWAYS;
80
81   SendNotifyItemUpdate(notify_item.Pass());
82 }
83
84 STDMETHODIMP_(ULONG) StatusTrayStateChangerWin::AddRef() {
85   DCHECK(CalledOnValidThread());
86   return base::win::IUnknownImpl::AddRef();
87 }
88
89 STDMETHODIMP_(ULONG) StatusTrayStateChangerWin::Release() {
90   DCHECK(CalledOnValidThread());
91   return base::win::IUnknownImpl::Release();
92 }
93
94 STDMETHODIMP StatusTrayStateChangerWin::QueryInterface(REFIID riid,
95                                                        PVOID* ptr_void) {
96   DCHECK(CalledOnValidThread());
97   if (riid == __uuidof(INotificationCB)) {
98     *ptr_void = static_cast<INotificationCB*>(this);
99     AddRef();
100     return S_OK;
101   }
102
103   return base::win::IUnknownImpl::QueryInterface(riid, ptr_void);
104 }
105
106 STDMETHODIMP StatusTrayStateChangerWin::Notify(ULONG event,
107                                                NOTIFYITEM* notify_item) {
108   DCHECK(CalledOnValidThread());
109   DCHECK(notify_item);
110   if (notify_item->hwnd != window_ || notify_item->id != icon_id_ ||
111       base::string16(notify_item->exe_name) != file_name_) {
112     return S_OK;
113   }
114
115   notify_item_.reset(new NOTIFYITEM(*notify_item));
116   return S_OK;
117 }
118
119 StatusTrayStateChangerWin::~StatusTrayStateChangerWin() {
120   DCHECK(CalledOnValidThread());
121 }
122
123 bool StatusTrayStateChangerWin::CreateTrayNotify() {
124   DCHECK(CalledOnValidThread());
125
126   tray_notify_.Release();  // Release so this method can be called more than
127                            // once.
128
129   HRESULT hr = tray_notify_.CreateInstance(CLSID_TrayNotify);
130   if (FAILED(hr))
131     return false;
132
133   base::win::ScopedComPtr<ITrayNotifyWin8> tray_notify_win8;
134   hr = tray_notify_win8.QueryFrom(tray_notify_);
135   if (SUCCEEDED(hr)) {
136     interface_version_ = INTERFACE_VERSION_WIN8;
137     return true;
138   }
139
140   base::win::ScopedComPtr<ITrayNotify> tray_notify_legacy;
141   hr = tray_notify_legacy.QueryFrom(tray_notify_);
142   if (SUCCEEDED(hr)) {
143     interface_version_ = INTERFACE_VERSION_LEGACY;
144     return true;
145   }
146
147   return false;
148 }
149
150 scoped_ptr<NOTIFYITEM> StatusTrayStateChangerWin::RegisterCallback() {
151   // |notify_item_| is used to store the result of the callback from
152   // Explorer.exe, which happens synchronously during
153   // RegisterCallbackWin8 or RegisterCallbackLegacy.
154   DCHECK(notify_item_.get() == NULL);
155
156   // TODO(dewittj): Add UMA logging here to report if either of our strategies
157   // has a tendency to fail on particular versions of Windows.
158   switch (interface_version_) {
159     case INTERFACE_VERSION_WIN8:
160       if (!RegisterCallbackWin8())
161         VLOG(1) << "Unable to successfully run RegisterCallbackWin8.";
162       break;
163     case INTERFACE_VERSION_LEGACY:
164       if (!RegisterCallbackLegacy())
165         VLOG(1) << "Unable to successfully run RegisterCallbackLegacy.";
166       break;
167     default:
168       NOTREACHED();
169   }
170
171   // Adding an intermediate scoped pointer here so that |notify_item_| is reset
172   // to NULL.
173   scoped_ptr<NOTIFYITEM> rv(notify_item_.release());
174   return rv.Pass();
175 }
176
177 bool StatusTrayStateChangerWin::RegisterCallbackWin8() {
178   base::win::ScopedComPtr<ITrayNotifyWin8> tray_notify_win8;
179   HRESULT hr = tray_notify_win8.QueryFrom(tray_notify_);
180   if (FAILED(hr))
181     return false;
182
183   // The following two lines cause Windows Explorer to call us back with all the
184   // existing tray icons and their preference.  It would also presumably notify
185   // us if changes were made in realtime while we registered as a callback, but
186   // we just want to modify our own entry so we immediately unregister.
187   unsigned long callback_id = 0;
188   hr = tray_notify_win8->RegisterCallback(this, &callback_id);
189   tray_notify_win8->UnregisterCallback(&callback_id);
190   if (FAILED(hr)) {
191     return false;
192   }
193
194   return true;
195 }
196
197 bool StatusTrayStateChangerWin::RegisterCallbackLegacy() {
198   base::win::ScopedComPtr<ITrayNotify> tray_notify;
199   HRESULT hr = tray_notify.QueryFrom(tray_notify_);
200   if (FAILED(hr)) {
201     return false;
202   }
203
204   // The following two lines cause Windows Explorer to call us back with all the
205   // existing tray icons and their preference.  It would also presumably notify
206   // us if changes were made in realtime while we registered as a callback.  In
207   // this version of the API, there can be only one registered callback so it is
208   // better to unregister as soon as possible.
209   // TODO(dewittj): Try to notice if the notification area icon customization
210   // window is open and postpone this call until the user closes it;
211   // registering the callback while the window is open can cause stale data to
212   // be displayed to the user.
213   hr = tray_notify->RegisterCallback(this);
214   tray_notify->RegisterCallback(NULL);
215   if (FAILED(hr)) {
216     return false;
217   }
218
219   return true;
220 }
221
222 void StatusTrayStateChangerWin::SendNotifyItemUpdate(
223     scoped_ptr<NOTIFYITEM> notify_item) {
224   if (interface_version_ == INTERFACE_VERSION_LEGACY) {
225     base::win::ScopedComPtr<ITrayNotify> tray_notify;
226     HRESULT hr = tray_notify.QueryFrom(tray_notify_);
227     if (SUCCEEDED(hr))
228       tray_notify->SetPreference(notify_item.get());
229   } else if (interface_version_ == INTERFACE_VERSION_WIN8) {
230     base::win::ScopedComPtr<ITrayNotifyWin8> tray_notify;
231     HRESULT hr = tray_notify.QueryFrom(tray_notify_);
232     if (SUCCEEDED(hr))
233       tray_notify->SetPreference(notify_item.get());
234   }
235 }