Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |