| 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/win/settings_app_monitor.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 one and only method that may be called from outside of the automation |
| 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 // The "Web browser" element in the "Default apps" pane. |
| 181 DEFAULT_BROWSER, |
| 182 // The element representing a browser in the "Choose an app" popup. |
| 183 BROWSER_BUTTON, |
| 184 // Any other element. |
| 185 UNKNOWN, |
| 186 }; |
| 187 |
| 188 ElementType DetectElementType(IUIAutomationElement* sender) { |
| 189 base::string16 aid(GetCachedBstrValue(sender, UIA_AutomationIdPropertyId)); |
| 190 if (aid == L"SystemSettings_DefaultApps_Browser_Button") |
| 191 return ElementType::DEFAULT_BROWSER; |
| 192 if (base::MatchPattern(aid, L"SystemSettings_DefaultApps_Browser_*_Button")) |
| 193 return ElementType::BROWSER_BUTTON; |
| 194 return ElementType::UNKNOWN; |
| 195 } |
| 196 |
| 197 // Configures a cache request so that it includes all properties needed by |
| 198 // DetectElementType() to detect the elements of interest. |
| 199 void ConfigureCacheRequest(IUIAutomationCacheRequest* cache_request) { |
| 200 DCHECK(cache_request); |
| 201 cache_request->AddProperty(UIA_AutomationIdPropertyId); |
| 202 cache_request->AddProperty(UIA_NamePropertyId); |
| 203 #if ENABLE_DLOG |
| 204 cache_request->AddProperty(UIA_ClassNamePropertyId); |
| 205 cache_request->AddProperty(UIA_ControlTypePropertyId); |
| 206 cache_request->AddProperty(UIA_IsPeripheralPropertyId); |
| 207 cache_request->AddProperty(UIA_ProcessIdPropertyId); |
| 208 cache_request->AddProperty(UIA_ValueValuePropertyId); |
| 209 cache_request->AddProperty(UIA_RuntimeIdPropertyId); |
| 210 #endif // ENABLE_DLOG |
| 211 } |
| 212 |
| 213 |
| 214 // Debug logging utility functions --------------------------------------------- |
| 215 |
| 216 bool GetCachedBoolValue(IUIAutomationElement* element, PROPERTYID property_id) { |
| 217 #if ENABLE_DLOG |
| 218 base::win::ScopedVariant var; |
| 219 |
| 220 if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE, |
| 221 var.Receive()))) { |
| 222 return false; |
| 223 } |
| 224 |
| 225 if (V_VT(var.ptr()) != VT_BOOL) { |
| 226 LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN) |
| 227 << __FUNCTION__ << " property is not a BOOL: " << V_VT(var.ptr()); |
| 228 return false; |
| 229 } |
| 230 |
| 231 return V_BOOL(var.ptr()) != 0; |
| 232 #else // ENABLE_DLOG |
| 233 return false; |
| 234 #endif // !ENABLE_DLOG |
| 235 } |
| 236 |
| 237 int32_t GetCachedInt32Value(IUIAutomationElement* element, |
| 238 PROPERTYID property_id) { |
| 239 #if ENABLE_DLOG |
| 240 base::win::ScopedVariant var; |
| 241 |
| 242 if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE, |
| 243 var.Receive()))) { |
| 244 return false; |
| 245 } |
| 246 |
| 247 if (V_VT(var.ptr()) != VT_I4) { |
| 248 LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN) |
| 249 << __FUNCTION__ << " property is not an I4: " << V_VT(var.ptr()); |
| 250 return false; |
| 251 } |
| 252 |
| 253 return V_I4(var.ptr()); |
| 254 #else // ENABLE_DLOG |
| 255 return 0; |
| 256 #endif // !ENABLE_DLOG |
| 257 } |
| 258 |
| 259 std::vector<int32_t> GetCachedInt32ArrayValue(IUIAutomationElement* element, |
| 260 PROPERTYID property_id) { |
| 261 std::vector<int32_t> values; |
| 262 #if ENABLE_DLOG |
| 263 base::win::ScopedVariant var; |
| 264 |
| 265 if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE, |
| 266 var.Receive()))) { |
| 267 return values; |
| 268 } |
| 269 |
| 270 if (V_VT(var.ptr()) != (VT_I4 | VT_ARRAY)) { |
| 271 LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN) |
| 272 << __FUNCTION__ << " property is not an I4 array: " << V_VT(var.ptr()); |
| 273 return values; |
| 274 } |
| 275 |
| 276 SAFEARRAY* array = V_ARRAY(var.ptr()); |
| 277 if (SafeArrayGetDim(array) != 1) |
| 278 return values; |
| 279 long lower_bound = 0; |
| 280 long upper_bound = 0; |
| 281 SafeArrayGetLBound(array, 1, &lower_bound); |
| 282 SafeArrayGetUBound(array, 1, &upper_bound); |
| 283 if (lower_bound || upper_bound <= lower_bound) |
| 284 return values; |
| 285 int32_t* data = nullptr; |
| 286 SafeArrayAccessData(array, reinterpret_cast<void**>(&data)); |
| 287 values.assign(data, data + upper_bound + 1); |
| 288 SafeArrayUnaccessData(array); |
| 289 #endif // ENABLE_DLOG |
| 290 return values; |
| 291 } |
| 292 |
| 293 std::string IntArrayToString(const std::vector<int32_t>& values) { |
| 294 #if ENABLE_DLOG |
| 295 std::vector<std::string> value_strings; |
| 296 std::transform(values.begin(), values.end(), |
| 297 std::back_inserter(value_strings), &base::IntToString); |
| 298 return base::JoinString(value_strings, ", "); |
| 299 #else // ENABLE_DLOG |
| 300 return std::string(); |
| 301 #endif // !ENABLE_DLOG |
| 302 } |
| 303 |
| 304 const char* GetEventName(EVENTID event_id) { |
| 305 #if ENABLE_DLOG |
| 306 switch (event_id) { |
| 307 case UIA_ToolTipOpenedEventId: |
| 308 return "UIA_ToolTipOpenedEventId"; |
| 309 case UIA_ToolTipClosedEventId: |
| 310 return "UIA_ToolTipClosedEventId"; |
| 311 case UIA_StructureChangedEventId: |
| 312 return "UIA_StructureChangedEventId"; |
| 313 case UIA_MenuOpenedEventId: |
| 314 return "UIA_MenuOpenedEventId"; |
| 315 case UIA_AutomationPropertyChangedEventId: |
| 316 return "UIA_AutomationPropertyChangedEventId"; |
| 317 case UIA_AutomationFocusChangedEventId: |
| 318 return "UIA_AutomationFocusChangedEventId"; |
| 319 case UIA_AsyncContentLoadedEventId: |
| 320 return "UIA_AsyncContentLoadedEventId"; |
| 321 case UIA_MenuClosedEventId: |
| 322 return "UIA_MenuClosedEventId"; |
| 323 case UIA_LayoutInvalidatedEventId: |
| 324 return "UIA_LayoutInvalidatedEventId"; |
| 325 case UIA_Invoke_InvokedEventId: |
| 326 return "UIA_Invoke_InvokedEventId"; |
| 327 case UIA_SelectionItem_ElementAddedToSelectionEventId: |
| 328 return "UIA_SelectionItem_ElementAddedToSelectionEventId"; |
| 329 case UIA_SelectionItem_ElementRemovedFromSelectionEventId: |
| 330 return "UIA_SelectionItem_ElementRemovedFromSelectionEventId"; |
| 331 case UIA_SelectionItem_ElementSelectedEventId: |
| 332 return "UIA_SelectionItem_ElementSelectedEventId"; |
| 333 case UIA_Selection_InvalidatedEventId: |
| 334 return "UIA_Selection_InvalidatedEventId"; |
| 335 case UIA_Text_TextSelectionChangedEventId: |
| 336 return "UIA_Text_TextSelectionChangedEventId"; |
| 337 case UIA_Text_TextChangedEventId: |
| 338 return "UIA_Text_TextChangedEventId"; |
| 339 case UIA_Window_WindowOpenedEventId: |
| 340 return "UIA_Window_WindowOpenedEventId"; |
| 341 case UIA_Window_WindowClosedEventId: |
| 342 return "UIA_Window_WindowClosedEventId"; |
| 343 case UIA_MenuModeStartEventId: |
| 344 return "UIA_MenuModeStartEventId"; |
| 345 case UIA_MenuModeEndEventId: |
| 346 return "UIA_MenuModeEndEventId"; |
| 347 case UIA_InputReachedTargetEventId: |
| 348 return "UIA_InputReachedTargetEventId"; |
| 349 case UIA_InputReachedOtherElementEventId: |
| 350 return "UIA_InputReachedOtherElementEventId"; |
| 351 case UIA_InputDiscardedEventId: |
| 352 return "UIA_InputDiscardedEventId"; |
| 353 case UIA_SystemAlertEventId: |
| 354 return "UIA_SystemAlertEventId"; |
| 355 case UIA_LiveRegionChangedEventId: |
| 356 return "UIA_LiveRegionChangedEventId"; |
| 357 case UIA_HostedFragmentRootsInvalidatedEventId: |
| 358 return "UIA_HostedFragmentRootsInvalidatedEventId"; |
| 359 case UIA_Drag_DragStartEventId: |
| 360 return "UIA_Drag_DragStartEventId"; |
| 361 case UIA_Drag_DragCancelEventId: |
| 362 return "UIA_Drag_DragCancelEventId"; |
| 363 case UIA_Drag_DragCompleteEventId: |
| 364 return "UIA_Drag_DragCompleteEventId"; |
| 365 case UIA_DropTarget_DragEnterEventId: |
| 366 return "UIA_DropTarget_DragEnterEventId"; |
| 367 case UIA_DropTarget_DragLeaveEventId: |
| 368 return "UIA_DropTarget_DragLeaveEventId"; |
| 369 case UIA_DropTarget_DroppedEventId: |
| 370 return "UIA_DropTarget_DroppedEventId"; |
| 371 case UIA_TextEdit_TextChangedEventId: |
| 372 return "UIA_TextEdit_TextChangedEventId"; |
| 373 case UIA_TextEdit_ConversionTargetChangedEventId: |
| 374 return "UIA_TextEdit_ConversionTargetChangedEventId"; |
| 375 } |
| 376 #endif // ENABLE_DLOG |
| 377 return ""; |
| 378 } |
| 379 |
| 380 const char* GetControlType(long control_type) { |
| 381 #if ENABLE_DLOG |
| 382 switch (control_type) { |
| 383 case UIA_ButtonControlTypeId: |
| 384 return "UIA_ButtonControlTypeId"; |
| 385 case UIA_CalendarControlTypeId: |
| 386 return "UIA_CalendarControlTypeId"; |
| 387 case UIA_CheckBoxControlTypeId: |
| 388 return "UIA_CheckBoxControlTypeId"; |
| 389 case UIA_ComboBoxControlTypeId: |
| 390 return "UIA_ComboBoxControlTypeId"; |
| 391 case UIA_EditControlTypeId: |
| 392 return "UIA_EditControlTypeId"; |
| 393 case UIA_HyperlinkControlTypeId: |
| 394 return "UIA_HyperlinkControlTypeId"; |
| 395 case UIA_ImageControlTypeId: |
| 396 return "UIA_ImageControlTypeId"; |
| 397 case UIA_ListItemControlTypeId: |
| 398 return "UIA_ListItemControlTypeId"; |
| 399 case UIA_ListControlTypeId: |
| 400 return "UIA_ListControlTypeId"; |
| 401 case UIA_MenuControlTypeId: |
| 402 return "UIA_MenuControlTypeId"; |
| 403 case UIA_MenuBarControlTypeId: |
| 404 return "UIA_MenuBarControlTypeId"; |
| 405 case UIA_MenuItemControlTypeId: |
| 406 return "UIA_MenuItemControlTypeId"; |
| 407 case UIA_ProgressBarControlTypeId: |
| 408 return "UIA_ProgressBarControlTypeId"; |
| 409 case UIA_RadioButtonControlTypeId: |
| 410 return "UIA_RadioButtonControlTypeId"; |
| 411 case UIA_ScrollBarControlTypeId: |
| 412 return "UIA_ScrollBarControlTypeId"; |
| 413 case UIA_SliderControlTypeId: |
| 414 return "UIA_SliderControlTypeId"; |
| 415 case UIA_SpinnerControlTypeId: |
| 416 return "UIA_SpinnerControlTypeId"; |
| 417 case UIA_StatusBarControlTypeId: |
| 418 return "UIA_StatusBarControlTypeId"; |
| 419 case UIA_TabControlTypeId: |
| 420 return "UIA_TabControlTypeId"; |
| 421 case UIA_TabItemControlTypeId: |
| 422 return "UIA_TabItemControlTypeId"; |
| 423 case UIA_TextControlTypeId: |
| 424 return "UIA_TextControlTypeId"; |
| 425 case UIA_ToolBarControlTypeId: |
| 426 return "UIA_ToolBarControlTypeId"; |
| 427 case UIA_ToolTipControlTypeId: |
| 428 return "UIA_ToolTipControlTypeId"; |
| 429 case UIA_TreeControlTypeId: |
| 430 return "UIA_TreeControlTypeId"; |
| 431 case UIA_TreeItemControlTypeId: |
| 432 return "UIA_TreeItemControlTypeId"; |
| 433 case UIA_CustomControlTypeId: |
| 434 return "UIA_CustomControlTypeId"; |
| 435 case UIA_GroupControlTypeId: |
| 436 return "UIA_GroupControlTypeId"; |
| 437 case UIA_ThumbControlTypeId: |
| 438 return "UIA_ThumbControlTypeId"; |
| 439 case UIA_DataGridControlTypeId: |
| 440 return "UIA_DataGridControlTypeId"; |
| 441 case UIA_DataItemControlTypeId: |
| 442 return "UIA_DataItemControlTypeId"; |
| 443 case UIA_DocumentControlTypeId: |
| 444 return "UIA_DocumentControlTypeId"; |
| 445 case UIA_SplitButtonControlTypeId: |
| 446 return "UIA_SplitButtonControlTypeId"; |
| 447 case UIA_WindowControlTypeId: |
| 448 return "UIA_WindowControlTypeId"; |
| 449 case UIA_PaneControlTypeId: |
| 450 return "UIA_PaneControlTypeId"; |
| 451 case UIA_HeaderControlTypeId: |
| 452 return "UIA_HeaderControlTypeId"; |
| 453 case UIA_HeaderItemControlTypeId: |
| 454 return "UIA_HeaderItemControlTypeId"; |
| 455 case UIA_TableControlTypeId: |
| 456 return "UIA_TableControlTypeId"; |
| 457 case UIA_TitleBarControlTypeId: |
| 458 return "UIA_TitleBarControlTypeId"; |
| 459 case UIA_SeparatorControlTypeId: |
| 460 return "UIA_SeparatorControlTypeId"; |
| 461 case UIA_SemanticZoomControlTypeId: |
| 462 return "UIA_SemanticZoomControlTypeId"; |
| 463 case UIA_AppBarControlTypeId: |
| 464 return "UIA_AppBarControlTypeId"; |
| 465 } |
| 466 #endif // ENABLE_DLOG |
| 467 return ""; |
| 468 } |
| 469 |
| 470 |
| 471 // SettingsAppMonitor::Context::EventHandler ----------------------------------- |
| 472 |
| 473 SettingsAppMonitor::Context::EventHandler::EventHandler() = default; |
| 474 |
| 475 SettingsAppMonitor::Context::EventHandler::~EventHandler() = default; |
| 476 |
| 477 void SettingsAppMonitor::Context::EventHandler::Initialize( |
| 478 scoped_refptr<base::SingleThreadTaskRunner> context_runner, |
| 479 const base::WeakPtr<SettingsAppMonitor::Context>& context) { |
| 480 context_runner_ = std::move(context_runner); |
| 481 context_ = context; |
| 482 } |
| 483 |
| 484 HRESULT SettingsAppMonitor::Context::EventHandler::HandleAutomationEvent( |
| 485 IUIAutomationElement* sender, |
| 486 EVENTID event_id) { |
| 487 DVLOG(1) << "event id: " << GetEventName(event_id) << ", automation id: " |
| 488 << GetCachedBstrValue(sender, UIA_AutomationIdPropertyId) |
| 489 << ", name: " << GetCachedBstrValue(sender, UIA_NamePropertyId) |
| 490 << ", control type: " << GetControlType(GetCachedInt32Value( |
| 491 sender, UIA_ControlTypePropertyId)) |
| 492 << ", is peripheral: " |
| 493 << GetCachedBoolValue(sender, UIA_IsPeripheralPropertyId) |
| 494 << ", class name: " |
| 495 << GetCachedBstrValue(sender, UIA_ClassNamePropertyId) |
| 496 << ", pid: " << GetCachedInt32Value(sender, UIA_ProcessIdPropertyId) |
| 497 << ", value: " |
| 498 << GetCachedBstrValue(sender, UIA_ValueValuePropertyId) |
| 499 << ", runtime id: " << IntArrayToString(GetCachedInt32ArrayValue( |
| 500 sender, UIA_RuntimeIdPropertyId)); |
| 501 |
| 502 switch (DetectElementType(sender)) { |
| 503 case ElementType::DEFAULT_BROWSER: |
| 504 context_runner_->PostTask( |
| 505 FROM_HERE, |
| 506 base::Bind(&SettingsAppMonitor::Context::HandleChooserInvoked, |
| 507 context_)); |
| 508 break; |
| 509 case ElementType::BROWSER_BUTTON: { |
| 510 base::string16 browser_name( |
| 511 GetCachedBstrValue(sender, UIA_NamePropertyId)); |
| 512 if (!browser_name.empty()) { |
| 513 context_runner_->PostTask( |
| 514 FROM_HERE, |
| 515 base::Bind(&SettingsAppMonitor::Context::HandleBrowserChosen, |
| 516 context_, browser_name)); |
| 517 } |
| 518 break; |
| 519 } |
| 520 case ElementType::UNKNOWN: |
| 521 break; |
| 522 } |
| 523 |
| 524 return S_OK; |
| 525 } |
| 526 |
| 527 HRESULT SettingsAppMonitor::Context::EventHandler::HandleFocusChangedEvent( |
| 528 IUIAutomationElement* sender) { |
| 529 DVLOG(1) << "focus changed for automation id: " |
| 530 << GetCachedBstrValue(sender, UIA_AutomationIdPropertyId) |
| 531 << ", name: " << GetCachedBstrValue(sender, UIA_NamePropertyId) |
| 532 << ", control type: " << GetControlType(GetCachedInt32Value( |
| 533 sender, UIA_ControlTypePropertyId)) |
| 534 << ", is peripheral: " |
| 535 << GetCachedBoolValue(sender, UIA_IsPeripheralPropertyId) |
| 536 << ", class name: " |
| 537 << GetCachedBstrValue(sender, UIA_ClassNamePropertyId) |
| 538 << ", pid: " << GetCachedInt32Value(sender, UIA_ProcessIdPropertyId) |
| 539 << ", value: " |
| 540 << GetCachedBstrValue(sender, UIA_ValueValuePropertyId) |
| 541 << ", runtime id: " << IntArrayToString(GetCachedInt32ArrayValue( |
| 542 sender, UIA_RuntimeIdPropertyId)); |
| 543 context_runner_->PostTask( |
| 544 FROM_HERE, |
| 545 base::Bind(&SettingsAppMonitor::Context::HandleFocusChangedEvent, |
| 546 context_, |
| 547 base::win::ScopedComPtr<IUIAutomationElement>(sender))); |
| 548 |
| 549 return S_OK; |
| 550 } |
| 551 |
| 552 // SettingsAppMonitor::Context ------------------------------------------------- |
| 553 |
| 554 base::WeakPtr<SettingsAppMonitor::Context> |
| 555 SettingsAppMonitor::Context::Create() { |
| 556 Context* context = new Context(); |
| 557 return context->weak_ptr_factory_.GetWeakPtr(); |
| 558 } |
| 559 |
| 560 void SettingsAppMonitor::Context::DeleteOnAutomationThread() { |
| 561 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 562 delete this; |
| 563 } |
| 564 |
| 565 void SettingsAppMonitor::Context::Initialize( |
| 566 base::SingleThreadTaskRunner* task_runner, |
| 567 base::SequencedTaskRunner* monitor_runner, |
| 568 const base::WeakPtr<SettingsAppMonitor>& monitor) { |
| 569 // This and all other methods must be called on the automation thread. |
| 570 DCHECK(task_runner->BelongsToCurrentThread()); |
| 571 DCHECK(!monitor_runner->RunsTasksOnCurrentThread()); |
| 572 |
| 573 task_runner_ = task_runner; |
| 574 monitor_runner_ = monitor_runner; |
| 575 monitor_ = monitor; |
| 576 |
| 577 HRESULT result = automation_.CreateInstance(CLSID_CUIAutomation, nullptr, |
| 578 CLSCTX_INPROC_SERVER); |
| 579 if (SUCCEEDED(result)) |
| 580 result = automation_ ? InstallObservers() : E_FAIL; |
| 581 |
| 582 // Tell the monitor that initialization is complete one way or the other. |
| 583 monitor_runner_->PostTask( |
| 584 FROM_HERE, |
| 585 base::Bind(&SettingsAppMonitor::OnInitialized, monitor_, result)); |
| 586 |
| 587 // Self-destruct immediately if initialization failed to reduce overhead. |
| 588 if (FAILED(result)) |
| 589 delete this; |
| 590 } |
| 591 |
| 592 SettingsAppMonitor::Context::Context() : weak_ptr_factory_(this) {} |
| 593 |
| 594 SettingsAppMonitor::Context::~Context() { |
| 595 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 596 |
| 597 if (event_handler_) { |
| 598 event_handler_.Release(); |
| 599 automation_->RemoveAllEventHandlers(); |
| 600 } |
| 601 } |
| 602 |
| 603 void SettingsAppMonitor::Context::HandleFocusChangedEvent( |
| 604 base::win::ScopedComPtr<IUIAutomationElement> sender) { |
| 605 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 606 |
| 607 if (DetectElementType(sender.get()) == ElementType::DEFAULT_BROWSER) { |
| 608 if (!observed_app_focused_) { |
| 609 observed_app_focused_ = true; |
| 610 monitor_runner_->PostTask( |
| 611 FROM_HERE, base::Bind(&SettingsAppMonitor::OnAppFocused, monitor_)); |
| 612 } |
| 613 } else { |
| 614 observed_app_focused_ = false; |
| 615 } |
| 616 } |
| 617 |
| 618 void SettingsAppMonitor::Context::HandleChooserInvoked() { |
| 619 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 620 monitor_runner_->PostTask( |
| 621 FROM_HERE, base::Bind(&SettingsAppMonitor::OnChooserInvoked, monitor_)); |
| 622 } |
| 623 |
| 624 void SettingsAppMonitor::Context::HandleBrowserChosen( |
| 625 const base::string16& browser_name) { |
| 626 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 627 monitor_runner_->PostTask( |
| 628 FROM_HERE, |
| 629 base::Bind(&SettingsAppMonitor::OnBrowserChosen, monitor_, browser_name)); |
| 630 } |
| 631 |
| 632 base::win::ScopedComPtr<IUnknown> |
| 633 SettingsAppMonitor::Context::GetEventHandler() { |
| 634 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 635 if (!event_handler_) { |
| 636 ATL::CComObject<EventHandler>* obj = nullptr; |
| 637 HRESULT result = ATL::CComObject<EventHandler>::CreateInstance(&obj); |
| 638 if (SUCCEEDED(result)) { |
| 639 obj->Initialize(task_runner_, weak_ptr_factory_.GetWeakPtr()); |
| 640 obj->QueryInterface(event_handler_.Receive()); |
| 641 } |
| 642 } |
| 643 return event_handler_; |
| 644 } |
| 645 |
| 646 base::win::ScopedComPtr<IUIAutomationEventHandler> |
| 647 SettingsAppMonitor::Context::GetAutomationEventHandler() { |
| 648 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 649 base::win::ScopedComPtr<IUIAutomationEventHandler> handler; |
| 650 handler.QueryFrom(GetEventHandler().get()); |
| 651 return handler; |
| 652 } |
| 653 |
| 654 base::win::ScopedComPtr<IUIAutomationFocusChangedEventHandler> |
| 655 SettingsAppMonitor::Context::GetFocusChangedEventHandler() { |
| 656 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 657 base::win::ScopedComPtr<IUIAutomationFocusChangedEventHandler> handler; |
| 658 handler.QueryFrom(GetEventHandler().get()); |
| 659 return handler; |
| 660 } |
| 661 |
| 662 HRESULT SettingsAppMonitor::Context::InstallObservers() { |
| 663 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 664 DCHECK(automation_); |
| 665 |
| 666 // Create a cache request so that elements received by way of events contain |
| 667 // all data needed for procesing. |
| 668 base::win::ScopedComPtr<IUIAutomationCacheRequest> cache_request; |
| 669 HRESULT result = automation_->CreateCacheRequest(cache_request.Receive()); |
| 670 if (FAILED(result)) |
| 671 return result; |
| 672 ConfigureCacheRequest(cache_request.get()); |
| 673 |
| 674 // Observe changes in focus. |
| 675 result = automation_->AddFocusChangedEventHandler( |
| 676 cache_request.get(), GetFocusChangedEventHandler().get()); |
| 677 if (FAILED(result)) |
| 678 return result; |
| 679 |
| 680 // Observe invocations. |
| 681 base::win::ScopedComPtr<IUIAutomationElement> desktop; |
| 682 result = automation_->GetRootElement(desktop.Receive()); |
| 683 if (desktop) { |
| 684 result = automation_->AddAutomationEventHandler( |
| 685 UIA_Invoke_InvokedEventId, desktop.get(), TreeScope_Subtree, |
| 686 cache_request.get(), GetAutomationEventHandler().get()); |
| 687 } |
| 688 |
| 689 return result; |
| 690 } |
| 691 |
| 692 |
| 693 // SettingsAppMonitor ---------------------------------------------------------- |
| 694 |
| 695 SettingsAppMonitor::SettingsAppMonitor(Delegate* delegate) |
| 696 : delegate_(delegate), |
| 697 automation_thread_("SettingsAppMonitorAutomation"), |
| 698 weak_ptr_factory_(this) { |
| 699 ui::win::CreateATLModuleIfNeeded(); |
| 700 // Start the automation thread and initialize the automation client on it. |
| 701 context_ = Context::Create(); |
| 702 automation_thread_.init_com_with_mta(true); |
| 703 automation_thread_.Start(); |
| 704 automation_thread_.task_runner()->PostTask( |
| 705 FROM_HERE, |
| 706 base::Bind(&SettingsAppMonitor::Context::Initialize, context_, |
| 707 base::Unretained(automation_thread_.task_runner().get()), |
| 708 base::Unretained(base::SequencedTaskRunnerHandle::Get().get()), |
| 709 weak_ptr_factory_.GetWeakPtr())); |
| 710 } |
| 711 |
| 712 SettingsAppMonitor::~SettingsAppMonitor() { |
| 713 DCHECK(thread_checker_.CalledOnValidThread()); |
| 714 |
| 715 // context_ is still valid when the caller destroys the instance before the |
| 716 // callback(s) have fired. In this case, delete the context on the automation |
| 717 // thread before joining with it. DeleteSoon is not used because the monitor |
| 718 // has only a WeakPtr to the context that is bound to the automation thread. |
| 719 automation_thread_.task_runner()->PostTask( |
| 720 FROM_HERE, |
| 721 base::Bind(&SettingsAppMonitor::Context::DeleteOnAutomationThread, |
| 722 context_)); |
| 723 } |
| 724 |
| 725 void SettingsAppMonitor::OnInitialized(HRESULT result) { |
| 726 DCHECK(thread_checker_.CalledOnValidThread()); |
| 727 delegate_->OnInitialized(result); |
| 728 } |
| 729 |
| 730 void SettingsAppMonitor::OnAppFocused() { |
| 731 DCHECK(thread_checker_.CalledOnValidThread()); |
| 732 delegate_->OnAppFocused(); |
| 733 } |
| 734 |
| 735 void SettingsAppMonitor::OnChooserInvoked() { |
| 736 DCHECK(thread_checker_.CalledOnValidThread()); |
| 737 delegate_->OnChooserInvoked(); |
| 738 } |
| 739 |
| 740 void SettingsAppMonitor::OnBrowserChosen(const base::string16& browser_name) { |
| 741 DCHECK(thread_checker_.CalledOnValidThread()); |
| 742 delegate_->OnBrowserChosen(browser_name); |
| 743 } |
| 744 |
| 745 } // namespace win |
| 746 } // namespace shell_integration |
| OLD | NEW |