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

Side by Side Diff: chrome/browser/settings_app_monitor_win.cc

Issue 2003553003: Report user actions for interactions with the Windows Settings app. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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/settings_app_monitor_win.h"
6
7 #include <atlbase.h>
8 #include <atlcom.h>
9 #include <oleauto.h>
10 #include <stdint.h>
11 #include <uiautomation.h>
12
13 #include <algorithm>
14 #include <iterator>
15 #include <string>
16 #include <utility>
17 #include <vector>
18
19 #include "base/bind.h"
20 #include "base/callback.h"
21 #include "base/location.h"
22 #include "base/memory/ref_counted.h"
23 #include "base/sequenced_task_runner.h"
24 #include "base/single_thread_task_runner.h"
25 #include "base/strings/pattern.h"
26 #include "base/strings/string16.h"
27 #include "base/strings/string_number_conversions.h"
28 #include "base/strings/string_util.h"
29 #include "base/threading/sequenced_task_runner_handle.h"
30 #include "base/win/scoped_comptr.h"
31 #include "base/win/scoped_variant.h"
32 #include "ui/base/win/atl_module.h"
33
34 namespace shell_integration {
35 namespace win {
36
37 // SettingsAppMonitor::Context -------------------------------------------------
38
39 // The guts of the monitor which runs on a dedicated thread in the
40 // multi-threaded COM apartment. An instance may be constructed on any thread,
41 // but Initialize() must be invoked on a thread in the MTA.
42 class SettingsAppMonitor::Context {
43 public:
44 // Returns a new instance ready for initialization and use on another thread.
45 static base::WeakPtr<Context> Create();
46
47 // Deletes the instance.
48 void DeleteOnAutomationThread();
49
50 // Initializes the context, invoking the monitor's |OnInitialized| method via
51 // |monitor_runner| when done. On success, the monitor's other On* methods
52 // will be invoked as events are observed. On failure, this instance
53 // self-destructs after posting |init_callback|. |task_runner| is the runner
54 // for the dedicated thread on which the context lives (owned by its
55 // UIAutomationClient).
56 void Initialize(base::SingleThreadTaskRunner* task_runner,
57 base::SequencedTaskRunner* monitor_runner,
58 const base::WeakPtr<SettingsAppMonitor>& monitor);
59
60 private:
61 class EventHandler;
62
63 // The only and only method that may be called from outside of the automation
Patrick Monette 2016/05/24 18:13:13 It's funny phrased that way, but did you mean "The
grt (UTC plus 2) 2016/05/24 18:48:35 Done.
64 // thread.
65 Context();
66 ~Context();
67
68 // Method(s) invoked by event handlers via weak pointers.
69
70 // Handles a focus change event on |sender|. Dispatches OnAppFocused if
71 // |sender| is the settings app.
72 void HandleFocusChangedEvent(
73 base::win::ScopedComPtr<IUIAutomationElement> sender);
74
75 // Handles the invocation of the element that opens the browser chooser.
76 void HandleChooserInvoked();
77
78 // Handles the invocation of an element in the browser chooser.
79 void HandleBrowserChosen(const base::string16& browser_name);
80
81 // Returns an event handler for all event types of interest.
82 base::win::ScopedComPtr<IUnknown> GetEventHandler();
83
84 // Returns a pointer to the event handler's generic interface.
85 base::win::ScopedComPtr<IUIAutomationEventHandler>
86 GetAutomationEventHandler();
87
88 // Returns a pointer to the event handler's focus changed interface.
89 base::win::ScopedComPtr<IUIAutomationFocusChangedEventHandler>
90 GetFocusChangedEventHandler();
91
92 // Installs an event handler to observe events of interest.
93 HRESULT InstallObservers();
94
95 // The task runner for the automation thread.
96 base::SingleThreadTaskRunner* task_runner_ = nullptr;
97
98 // The task runner on which the owning monitor lives.
99 base::SequencedTaskRunner* monitor_runner_ = nullptr;
100
101 // The monitor that owns this context.
102 base::WeakPtr<SettingsAppMonitor> monitor_;
103
104 // The automation client.
105 base::win::ScopedComPtr<IUIAutomation> automation_;
106
107 // The event handler.
108 base::win::ScopedComPtr<IUnknown> event_handler_;
109
110 // State to suppress duplicate OnAppFocused notifications.
111 bool observed_app_focused_ = false;
112
113 // Weak pointers to the context are given to event handlers.
114 base::WeakPtrFactory<Context> weak_ptr_factory_;
115
116 DISALLOW_COPY_AND_ASSIGN(Context);
117 };
118
119
120 // SettingsAppMonitor::Context::EventHandler -----------------------------------
121
122 // A handler of events on arbitrary threads in the MTA.
123 class SettingsAppMonitor::Context::EventHandler
124 : public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>,
125 public IUIAutomationEventHandler,
126 public IUIAutomationFocusChangedEventHandler {
127 public:
128 BEGIN_COM_MAP(SettingsAppMonitor::Context::EventHandler)
129 COM_INTERFACE_ENTRY(IUIAutomationEventHandler)
130 COM_INTERFACE_ENTRY(IUIAutomationFocusChangedEventHandler)
131 END_COM_MAP()
132
133 EventHandler();
134 ~EventHandler();
135
136 // Initializes the object. Events will be dispatched back to |context| via
137 // |context_runner|.
138 void Initialize(scoped_refptr<base::SingleThreadTaskRunner> context_runner,
139 const base::WeakPtr<SettingsAppMonitor::Context>& context);
140
141 // IUIAutomationEventHandler:
142 STDMETHOD(HandleAutomationEvent)(IUIAutomationElement *sender,
143 EVENTID eventId) override;
144
145 // IUIAutomationFocusChangedEventHandler:
146 STDMETHOD(HandleFocusChangedEvent)(IUIAutomationElement* sender) override;
147
148 private:
149 // The task runner for the monitor client context.
150 scoped_refptr<base::SingleThreadTaskRunner> context_runner_;
151
152 // The monitor context that owns this event handler.
153 base::WeakPtr<SettingsAppMonitor::Context> context_;
154
155 DISALLOW_COPY_AND_ASSIGN(EventHandler);
156 };
157
158
159 // Utility functions -----------------------------------------------------------
160
161 base::string16 GetCachedBstrValue(IUIAutomationElement* element,
162 PROPERTYID property_id) {
163 HRESULT result = S_OK;
164 base::win::ScopedVariant var;
165
166 result = element->GetCachedPropertyValueEx(property_id, TRUE, var.Receive());
167 if (FAILED(result))
168 return base::string16();
169
170 if (V_VT(var.ptr()) != VT_BSTR) {
171 LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
172 << __FUNCTION__ << " property is not a BSTR: " << V_VT(var.ptr());
173 return base::string16();
174 }
175
176 return base::string16(V_BSTR(var.ptr()));
177 }
178
179 enum class ElementType {
180 DEFAULT_BROWSER,
Patrick Monette 2016/05/24 18:13:13 Add a comment for each values
grt (UTC plus 2) 2016/05/24 18:48:35 Done.
181 BROWSER_BUTTON,
182 UNKNOWN,
183 };
184
185 ElementType DetectElementType(IUIAutomationElement* sender) {
186 base::string16 aid(GetCachedBstrValue(sender, UIA_AutomationIdPropertyId));
187 if (aid == L"SystemSettings_DefaultApps_Browser_Button")
188 return ElementType::DEFAULT_BROWSER;
189 if (base::MatchPattern(aid, L"SystemSettings_DefaultApps_Browser_*_Button"))
190 return ElementType::BROWSER_BUTTON;
191 return ElementType::UNKNOWN;
192 }
193
194 // Configures a cache request so that it includes all properties needed by
195 // DetectElementType to detect the elements of interest.
Patrick Monette 2016/05/24 18:13:13 nit: DetectElementType()
grt (UTC plus 2) 2016/05/24 18:48:35 Done.
196 void ConfigureCacheRequest(IUIAutomationCacheRequest* cache_request) {
197 DCHECK(cache_request);
198 cache_request->AddProperty(UIA_AutomationIdPropertyId);
199 cache_request->AddProperty(UIA_NamePropertyId);
200 #ifndef NDEBUG
201 cache_request->AddProperty(UIA_ClassNamePropertyId);
202 cache_request->AddProperty(UIA_ControlTypePropertyId);
203 cache_request->AddProperty(UIA_IsPeripheralPropertyId);
204 cache_request->AddProperty(UIA_ProcessIdPropertyId);
205 cache_request->AddProperty(UIA_ValueValuePropertyId);
206 cache_request->AddProperty(UIA_RuntimeIdPropertyId);
207 #endif // !NDEBUG
Patrick Monette 2016/05/24 18:13:12 nit: Remove exclamation point
grt (UTC plus 2) 2016/05/24 18:48:35 Done.
208 }
209
210
211 // Debug logging utility functions ---------------------------------------------
212
213 bool GetCachedBoolValue(IUIAutomationElement* element, PROPERTYID property_id) {
214 #ifndef NDEBUG
215 base::win::ScopedVariant var;
216
217 if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
218 var.Receive()))) {
219 return false;
220 }
221
222 if (V_VT(var.ptr()) != VT_BOOL) {
223 LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
224 << __FUNCTION__ << " property is not a BOOL: " << V_VT(var.ptr());
225 return false;
226 }
227
228 return V_BOOL(var.ptr()) != 0;
229 #else
230 return false;
231 #endif
232 }
233
234 int32_t GetCachedInt32Value(IUIAutomationElement* element,
235 PROPERTYID property_id) {
236 #ifndef NDEBUG
237 base::win::ScopedVariant var;
238
239 if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
240 var.Receive()))) {
241 return false;
242 }
243
244 if (V_VT(var.ptr()) != VT_I4) {
245 LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
246 << __FUNCTION__ << " property is not an I4: " << V_VT(var.ptr());
247 return false;
248 }
249
250 return V_I4(var.ptr());
251 #else
252 return 0;
253 #endif
254 }
255
256 std::vector<int32_t> GetCachedInt32ArrayValue(IUIAutomationElement* element,
257 PROPERTYID property_id) {
258 std::vector<int32_t> values;
259 #ifndef NDEBUG
260 base::win::ScopedVariant var;
261
262 if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
263 var.Receive()))) {
264 return values;
265 }
266
267 if (V_VT(var.ptr()) != (VT_I4 | VT_ARRAY)) {
268 LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
269 << __FUNCTION__ << " property is not an I4 array: " << V_VT(var.ptr());
270 return values;
271 }
272
273 SAFEARRAY* array = V_ARRAY(var.ptr());
274 if (SafeArrayGetDim(array) != 1)
275 return values;
276 long lower_bound = 0;
277 long upper_bound = 0;
278 SafeArrayGetLBound(array, 1, &lower_bound);
279 SafeArrayGetUBound(array, 1, &upper_bound);
280 if (lower_bound || upper_bound <= lower_bound)
281 return values;
282 int32_t* data = nullptr;
283 SafeArrayAccessData(array, reinterpret_cast<void**>(&data));
284 values.assign(data, data + upper_bound + 1);
285 SafeArrayUnaccessData(array);
286 #endif
287 return values;
288 }
289
290 std::string IntArrayToString(const std::vector<int32_t>& values) {
291 #ifndef NDEBUG
292 std::vector<std::string> value_strings;
293 std::transform(values.begin(), values.end(),
294 std::back_inserter(value_strings), &base::IntToString);
295 return base::JoinString(value_strings, ", ");
296 #else
297 return std::string();
298 #endif
299 }
300
301 const char* GetEventName(EVENTID event_id) {
302 #ifndef NDEBUG
303 switch (event_id) {
304 case UIA_ToolTipOpenedEventId:
305 return "UIA_ToolTipOpenedEventId";
306 case UIA_ToolTipClosedEventId:
307 return "UIA_ToolTipClosedEventId";
308 case UIA_StructureChangedEventId:
309 return "UIA_StructureChangedEventId";
310 case UIA_MenuOpenedEventId:
311 return "UIA_MenuOpenedEventId";
312 case UIA_AutomationPropertyChangedEventId:
313 return "UIA_AutomationPropertyChangedEventId";
314 case UIA_AutomationFocusChangedEventId:
315 return "UIA_AutomationFocusChangedEventId";
316 case UIA_AsyncContentLoadedEventId:
317 return "UIA_AsyncContentLoadedEventId";
318 case UIA_MenuClosedEventId:
319 return "UIA_MenuClosedEventId";
320 case UIA_LayoutInvalidatedEventId:
321 return "UIA_LayoutInvalidatedEventId";
322 case UIA_Invoke_InvokedEventId:
323 return "UIA_Invoke_InvokedEventId";
324 case UIA_SelectionItem_ElementAddedToSelectionEventId:
325 return "UIA_SelectionItem_ElementAddedToSelectionEventId";
326 case UIA_SelectionItem_ElementRemovedFromSelectionEventId:
327 return "UIA_SelectionItem_ElementRemovedFromSelectionEventId";
328 case UIA_SelectionItem_ElementSelectedEventId:
329 return "UIA_SelectionItem_ElementSelectedEventId";
330 case UIA_Selection_InvalidatedEventId:
331 return "UIA_Selection_InvalidatedEventId";
332 case UIA_Text_TextSelectionChangedEventId:
333 return "UIA_Text_TextSelectionChangedEventId";
334 case UIA_Text_TextChangedEventId:
335 return "UIA_Text_TextChangedEventId";
336 case UIA_Window_WindowOpenedEventId:
337 return "UIA_Window_WindowOpenedEventId";
338 case UIA_Window_WindowClosedEventId:
339 return "UIA_Window_WindowClosedEventId";
340 case UIA_MenuModeStartEventId:
341 return "UIA_MenuModeStartEventId";
342 case UIA_MenuModeEndEventId:
343 return "UIA_MenuModeEndEventId";
344 case UIA_InputReachedTargetEventId:
345 return "UIA_InputReachedTargetEventId";
346 case UIA_InputReachedOtherElementEventId:
347 return "UIA_InputReachedOtherElementEventId";
348 case UIA_InputDiscardedEventId:
349 return "UIA_InputDiscardedEventId";
350 case UIA_SystemAlertEventId:
351 return "UIA_SystemAlertEventId";
352 case UIA_LiveRegionChangedEventId:
353 return "UIA_LiveRegionChangedEventId";
354 case UIA_HostedFragmentRootsInvalidatedEventId:
355 return "UIA_HostedFragmentRootsInvalidatedEventId";
356 case UIA_Drag_DragStartEventId:
357 return "UIA_Drag_DragStartEventId";
358 case UIA_Drag_DragCancelEventId:
359 return "UIA_Drag_DragCancelEventId";
360 case UIA_Drag_DragCompleteEventId:
361 return "UIA_Drag_DragCompleteEventId";
362 case UIA_DropTarget_DragEnterEventId:
363 return "UIA_DropTarget_DragEnterEventId";
364 case UIA_DropTarget_DragLeaveEventId:
365 return "UIA_DropTarget_DragLeaveEventId";
366 case UIA_DropTarget_DroppedEventId:
367 return "UIA_DropTarget_DroppedEventId";
368 case UIA_TextEdit_TextChangedEventId:
369 return "UIA_TextEdit_TextChangedEventId";
370 case UIA_TextEdit_ConversionTargetChangedEventId:
371 return "UIA_TextEdit_ConversionTargetChangedEventId";
372 }
373 #endif
374 return "";
375 }
376
377 const char* GetControlType(long control_type) {
378 #ifndef NDEBUG
379 switch (control_type) {
380 case UIA_ButtonControlTypeId:
381 return "UIA_ButtonControlTypeId";
382 case UIA_CalendarControlTypeId:
383 return "UIA_CalendarControlTypeId";
384 case UIA_CheckBoxControlTypeId:
385 return "UIA_CheckBoxControlTypeId";
386 case UIA_ComboBoxControlTypeId:
387 return "UIA_ComboBoxControlTypeId";
388 case UIA_EditControlTypeId:
389 return "UIA_EditControlTypeId";
390 case UIA_HyperlinkControlTypeId:
391 return "UIA_HyperlinkControlTypeId";
392 case UIA_ImageControlTypeId:
393 return "UIA_ImageControlTypeId";
394 case UIA_ListItemControlTypeId:
395 return "UIA_ListItemControlTypeId";
396 case UIA_ListControlTypeId:
397 return "UIA_ListControlTypeId";
398 case UIA_MenuControlTypeId:
399 return "UIA_MenuControlTypeId";
400 case UIA_MenuBarControlTypeId:
401 return "UIA_MenuBarControlTypeId";
402 case UIA_MenuItemControlTypeId:
403 return "UIA_MenuItemControlTypeId";
404 case UIA_ProgressBarControlTypeId:
405 return "UIA_ProgressBarControlTypeId";
406 case UIA_RadioButtonControlTypeId:
407 return "UIA_RadioButtonControlTypeId";
408 case UIA_ScrollBarControlTypeId:
409 return "UIA_ScrollBarControlTypeId";
410 case UIA_SliderControlTypeId:
411 return "UIA_SliderControlTypeId";
412 case UIA_SpinnerControlTypeId:
413 return "UIA_SpinnerControlTypeId";
414 case UIA_StatusBarControlTypeId:
415 return "UIA_StatusBarControlTypeId";
416 case UIA_TabControlTypeId:
417 return "UIA_TabControlTypeId";
418 case UIA_TabItemControlTypeId:
419 return "UIA_TabItemControlTypeId";
420 case UIA_TextControlTypeId:
421 return "UIA_TextControlTypeId";
422 case UIA_ToolBarControlTypeId:
423 return "UIA_ToolBarControlTypeId";
424 case UIA_ToolTipControlTypeId:
425 return "UIA_ToolTipControlTypeId";
426 case UIA_TreeControlTypeId:
427 return "UIA_TreeControlTypeId";
428 case UIA_TreeItemControlTypeId:
429 return "UIA_TreeItemControlTypeId";
430 case UIA_CustomControlTypeId:
431 return "UIA_CustomControlTypeId";
432 case UIA_GroupControlTypeId:
433 return "UIA_GroupControlTypeId";
434 case UIA_ThumbControlTypeId:
435 return "UIA_ThumbControlTypeId";
436 case UIA_DataGridControlTypeId:
437 return "UIA_DataGridControlTypeId";
438 case UIA_DataItemControlTypeId:
439 return "UIA_DataItemControlTypeId";
440 case UIA_DocumentControlTypeId:
441 return "UIA_DocumentControlTypeId";
442 case UIA_SplitButtonControlTypeId:
443 return "UIA_SplitButtonControlTypeId";
444 case UIA_WindowControlTypeId:
445 return "UIA_WindowControlTypeId";
446 case UIA_PaneControlTypeId:
447 return "UIA_PaneControlTypeId";
448 case UIA_HeaderControlTypeId:
449 return "UIA_HeaderControlTypeId";
450 case UIA_HeaderItemControlTypeId:
451 return "UIA_HeaderItemControlTypeId";
452 case UIA_TableControlTypeId:
453 return "UIA_TableControlTypeId";
454 case UIA_TitleBarControlTypeId:
455 return "UIA_TitleBarControlTypeId";
456 case UIA_SeparatorControlTypeId:
457 return "UIA_SeparatorControlTypeId";
458 case UIA_SemanticZoomControlTypeId:
459 return "UIA_SemanticZoomControlTypeId";
460 case UIA_AppBarControlTypeId:
461 return "UIA_AppBarControlTypeId";
462 }
463 #endif
464 return "";
465 }
466
467
468 // SettingsAppMonitor::Context::EventHandler -----------------------------------
469
470 SettingsAppMonitor::Context::EventHandler::EventHandler() = default;
471
472 SettingsAppMonitor::Context::EventHandler::~EventHandler() = default;
473
474 void SettingsAppMonitor::Context::EventHandler::Initialize(
475 scoped_refptr<base::SingleThreadTaskRunner> context_runner,
476 const base::WeakPtr<SettingsAppMonitor::Context>& context) {
477 context_runner_ = std::move(context_runner);
478 context_ = context;
479 }
480
481 HRESULT SettingsAppMonitor::Context::EventHandler::HandleAutomationEvent(
482 IUIAutomationElement* sender,
483 EVENTID event_id) {
484 DVLOG(1) << "event id: " << GetEventName(event_id) << ", automation id: "
485 << GetCachedBstrValue(sender, UIA_AutomationIdPropertyId)
486 << ", name: " << GetCachedBstrValue(sender, UIA_NamePropertyId)
487 << ", control type: " << GetControlType(GetCachedInt32Value(
488 sender, UIA_ControlTypePropertyId))
489 << ", is peripheral: "
490 << GetCachedBoolValue(sender, UIA_IsPeripheralPropertyId)
491 << ", class name: "
492 << GetCachedBstrValue(sender, UIA_ClassNamePropertyId)
493 << ", pid: " << GetCachedInt32Value(sender, UIA_ProcessIdPropertyId)
494 << ", value: "
495 << GetCachedBstrValue(sender, UIA_ValueValuePropertyId)
496 << ", runtime id: " << IntArrayToString(GetCachedInt32ArrayValue(
497 sender, UIA_RuntimeIdPropertyId));
498
499 if (event_id != UIA_Invoke_InvokedEventId)
Patrick Monette 2016/05/24 18:13:12 This should never happen no?
grt (UTC plus 2) 2016/05/24 18:48:35 Correct. I had hooked this up to other event types
500 return S_OK;
501
502 ElementType type = DetectElementType(sender);
503 switch (type) {
504 case ElementType::DEFAULT_BROWSER:
505 context_runner_->PostTask(
506 FROM_HERE,
507 base::Bind(&SettingsAppMonitor::Context::HandleChooserInvoked,
508 context_));
509 break;
510 case ElementType::BROWSER_BUTTON: {
511 base::string16 browser_name(
512 GetCachedBstrValue(sender, UIA_NamePropertyId));
513 if (!browser_name.empty()) {
514 context_runner_->PostTask(
515 FROM_HERE,
516 base::Bind(&SettingsAppMonitor::Context::HandleBrowserChosen,
517 context_, browser_name));
518 }
519 break;
520 }
521 case ElementType::UNKNOWN:
522 break;
523 }
524
525 return S_OK;
526 }
527
528 HRESULT SettingsAppMonitor::Context::EventHandler::HandleFocusChangedEvent(
529 IUIAutomationElement* sender) {
530 DVLOG(1) << "focus changed for automation id: "
531 << GetCachedBstrValue(sender, UIA_AutomationIdPropertyId)
532 << ", name: " << GetCachedBstrValue(sender, UIA_NamePropertyId)
533 << ", control type: " << GetControlType(GetCachedInt32Value(
534 sender, UIA_ControlTypePropertyId))
535 << ", is peripheral: "
536 << GetCachedBoolValue(sender, UIA_IsPeripheralPropertyId)
537 << ", class name: "
538 << GetCachedBstrValue(sender, UIA_ClassNamePropertyId)
539 << ", pid: " << GetCachedInt32Value(sender, UIA_ProcessIdPropertyId)
540 << ", value: "
541 << GetCachedBstrValue(sender, UIA_ValueValuePropertyId)
542 << ", runtime id: " << IntArrayToString(GetCachedInt32ArrayValue(
543 sender, UIA_RuntimeIdPropertyId));
544 context_runner_->PostTask(
545 FROM_HERE,
546 base::Bind(&SettingsAppMonitor::Context::HandleFocusChangedEvent,
547 context_,
548 base::win::ScopedComPtr<IUIAutomationElement>(sender)));
549
550 return S_OK;
551 }
552
553 // SettingsAppMonitor::Context -------------------------------------------------
554
555 base::WeakPtr<SettingsAppMonitor::Context>
556 SettingsAppMonitor::Context::Create() {
557 Context* context = new Context();
558 return context->weak_ptr_factory_.GetWeakPtr();
559 }
560
561 void SettingsAppMonitor::Context::DeleteOnAutomationThread() {
562 DCHECK(task_runner_->BelongsToCurrentThread());
563 delete this;
564 }
565
566 void SettingsAppMonitor::Context::Initialize(
567 base::SingleThreadTaskRunner* task_runner,
568 base::SequencedTaskRunner* monitor_runner,
569 const base::WeakPtr<SettingsAppMonitor>& monitor) {
570 // This and all other methods must be called on the automation thread.
571 DCHECK(task_runner->BelongsToCurrentThread());
572 DCHECK(!monitor_runner->RunsTasksOnCurrentThread());
573
574 task_runner_ = task_runner;
575 monitor_runner_ = monitor_runner;
576 monitor_ = monitor;
577
578 HRESULT result = automation_.CreateInstance(CLSID_CUIAutomation, nullptr,
579 CLSCTX_INPROC_SERVER);
580 if (SUCCEEDED(result))
581 result = automation_ ? InstallObservers() : E_FAIL;
582
583 // Tell the monitor that initialization is complete one way or the other.
584 monitor_runner_->PostTask(
585 FROM_HERE,
586 base::Bind(&SettingsAppMonitor::OnInitialized, monitor_, result));
587
588 // Self-destruct immediately if initialization failed to reduce overhead.
589 if (FAILED(result))
590 delete this;
591 }
592
593 SettingsAppMonitor::Context::Context() : weak_ptr_factory_(this) {}
594
595 SettingsAppMonitor::Context::~Context() {
596 DCHECK(task_runner_->BelongsToCurrentThread());
597
598 if (event_handler_) {
599 event_handler_.Release();
600 automation_->RemoveAllEventHandlers();
601 }
602 }
603
604 void SettingsAppMonitor::Context::HandleFocusChangedEvent(
605 base::win::ScopedComPtr<IUIAutomationElement> sender) {
606 DCHECK(task_runner_->BelongsToCurrentThread());
607
608 if (DetectElementType(sender.get()) == ElementType::DEFAULT_BROWSER) {
609 if (!observed_app_focused_) {
610 observed_app_focused_ = true;
611 monitor_runner_->PostTask(
612 FROM_HERE, base::Bind(&SettingsAppMonitor::OnAppFocused, monitor_));
613 }
614 } else {
615 observed_app_focused_ = false;
Patrick Monette 2016/05/24 18:13:12 Why is this reset every time the focus change to s
grt (UTC plus 2) 2016/05/24 18:48:35 It's intended to collapse multiple focus events fo
Patrick Monette 2016/05/24 20:33:32 ¯\_(ツ)_/¯
616 }
617 }
618
619 void SettingsAppMonitor::Context::HandleChooserInvoked() {
620 DCHECK(task_runner_->BelongsToCurrentThread());
621 monitor_runner_->PostTask(
622 FROM_HERE, base::Bind(&SettingsAppMonitor::OnChooserInvoked, monitor_));
623 }
624
625 void SettingsAppMonitor::Context::HandleBrowserChosen(
626 const base::string16& browser_name) {
627 DCHECK(task_runner_->BelongsToCurrentThread());
628 monitor_runner_->PostTask(
629 FROM_HERE,
630 base::Bind(&SettingsAppMonitor::OnBrowserChosen, monitor_, browser_name));
631 }
632
633 base::win::ScopedComPtr<IUnknown>
634 SettingsAppMonitor::Context::GetEventHandler() {
635 DCHECK(task_runner_->BelongsToCurrentThread());
636 if (!event_handler_) {
637 ATL::CComObject<EventHandler>* obj = nullptr;
638 HRESULT result = ATL::CComObject<EventHandler>::CreateInstance(&obj);
639 if (SUCCEEDED(result)) {
640 obj->Initialize(task_runner_, weak_ptr_factory_.GetWeakPtr());
641 obj->QueryInterface(event_handler_.Receive());
642 }
643 }
644 return event_handler_;
645 }
646
647 base::win::ScopedComPtr<IUIAutomationEventHandler>
648 SettingsAppMonitor::Context::GetAutomationEventHandler() {
649 DCHECK(task_runner_->BelongsToCurrentThread());
650 base::win::ScopedComPtr<IUIAutomationEventHandler> handler;
651 handler.QueryFrom(GetEventHandler().get());
652 return handler;
653 }
654
655 base::win::ScopedComPtr<IUIAutomationFocusChangedEventHandler>
656 SettingsAppMonitor::Context::GetFocusChangedEventHandler() {
657 DCHECK(task_runner_->BelongsToCurrentThread());
658 base::win::ScopedComPtr<IUIAutomationFocusChangedEventHandler> handler;
659 handler.QueryFrom(GetEventHandler().get());
660 return handler;
661 }
662
663 HRESULT SettingsAppMonitor::Context::InstallObservers() {
664 DCHECK(task_runner_->BelongsToCurrentThread());
665 DCHECK(automation_);
666
667 // Create a cache request so that elements received by way of events contain
668 // all data needed for procesing.
669 base::win::ScopedComPtr<IUIAutomationCacheRequest> cache_request;
670 HRESULT result = automation_->CreateCacheRequest(cache_request.Receive());
671 if (FAILED(result))
672 return result;
673 ConfigureCacheRequest(cache_request.get());
674
675 // Observe changes in focus.
676 result = automation_->AddFocusChangedEventHandler(
677 cache_request.get(), GetFocusChangedEventHandler().get());
678 if (FAILED(result))
679 return result;
680
681 // Observe invocations.
682 base::win::ScopedComPtr<IUIAutomationElement> desktop;
683 result = automation_->GetRootElement(desktop.Receive());
684 if (desktop) {
685 result = automation_->AddAutomationEventHandler(
686 UIA_Invoke_InvokedEventId, desktop.get(), TreeScope_Subtree,
687 cache_request.get(), GetAutomationEventHandler().get());
688 }
689
690 return result;
691 }
692
693
694 // SettingsAppMonitor ----------------------------------------------------------
695
696 SettingsAppMonitor::SettingsAppMonitor(Delegate* delegate)
697 : delegate_(delegate),
698 automation_thread_("SettingsAppMonitorAutomation"),
699 weak_ptr_factory_(this) {
700 ui::win::CreateATLModuleIfNeeded();
701 // Start the automation thread and initialize the automation client on it.
702 context_ = Context::Create();
703 automation_thread_.init_com_with_mta(true);
704 automation_thread_.Start();
705 automation_thread_.task_runner()->PostTask(
706 FROM_HERE,
707 base::Bind(&SettingsAppMonitor::Context::Initialize, context_,
708 base::Unretained(automation_thread_.task_runner().get()),
709 base::Unretained(base::SequencedTaskRunnerHandle::Get().get()),
710 weak_ptr_factory_.GetWeakPtr()));
711 }
712
713 SettingsAppMonitor::~SettingsAppMonitor() {
714 DCHECK(thread_checker_.CalledOnValidThread());
715
716 // context_ is still valid when the caller destroys the instance before the
717 // callback(s) have fired. In this case, delete the context on the automation
718 // thread before joining with it. DeleteSoon is not used because the monitor
719 // has only a WeakPtr to the context that is bound to the automation thread.
720 automation_thread_.task_runner()->PostTask(
721 FROM_HERE,
722 base::Bind(&SettingsAppMonitor::Context::DeleteOnAutomationThread,
723 context_));
724 }
725
726 void SettingsAppMonitor::OnInitialized(HRESULT result) {
727 DCHECK(thread_checker_.CalledOnValidThread());
728 delegate_->OnInitialized(result);
729 }
730
731 void SettingsAppMonitor::OnAppFocused() {
732 DCHECK(thread_checker_.CalledOnValidThread());
733 delegate_->OnAppFocused();
734 }
735
736 void SettingsAppMonitor::OnChooserInvoked() {
737 DCHECK(thread_checker_.CalledOnValidThread());
738 delegate_->OnChooserInvoked();
739 }
740
741 void SettingsAppMonitor::OnBrowserChosen(const base::string16& browser_name) {
742 DCHECK(thread_checker_.CalledOnValidThread());
743 delegate_->OnBrowserChosen(browser_name);
744 }
745
746 } // namespace win
747 } // namespace shell_integration
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698