| Index: chrome/browser/win/settings_app_monitor.cc
|
| diff --git a/chrome/browser/win/settings_app_monitor.cc b/chrome/browser/win/settings_app_monitor.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..af0676669e15276cbad73a6e6142c9e4f57e1a89
|
| --- /dev/null
|
| +++ b/chrome/browser/win/settings_app_monitor.cc
|
| @@ -0,0 +1,746 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/win/settings_app_monitor.h"
|
| +
|
| +#include <atlbase.h>
|
| +#include <atlcom.h>
|
| +#include <oleauto.h>
|
| +#include <stdint.h>
|
| +#include <uiautomation.h>
|
| +
|
| +#include <algorithm>
|
| +#include <iterator>
|
| +#include <string>
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/location.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/sequenced_task_runner.h"
|
| +#include "base/single_thread_task_runner.h"
|
| +#include "base/strings/pattern.h"
|
| +#include "base/strings/string16.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/strings/string_util.h"
|
| +#include "base/threading/sequenced_task_runner_handle.h"
|
| +#include "base/win/scoped_comptr.h"
|
| +#include "base/win/scoped_variant.h"
|
| +#include "ui/base/win/atl_module.h"
|
| +
|
| +namespace shell_integration {
|
| +namespace win {
|
| +
|
| +// SettingsAppMonitor::Context -------------------------------------------------
|
| +
|
| +// The guts of the monitor which runs on a dedicated thread in the
|
| +// multi-threaded COM apartment. An instance may be constructed on any thread,
|
| +// but Initialize() must be invoked on a thread in the MTA.
|
| +class SettingsAppMonitor::Context {
|
| + public:
|
| + // Returns a new instance ready for initialization and use on another thread.
|
| + static base::WeakPtr<Context> Create();
|
| +
|
| + // Deletes the instance.
|
| + void DeleteOnAutomationThread();
|
| +
|
| + // Initializes the context, invoking the monitor's |OnInitialized| method via
|
| + // |monitor_runner| when done. On success, the monitor's other On* methods
|
| + // will be invoked as events are observed. On failure, this instance
|
| + // self-destructs after posting |init_callback|. |task_runner| is the runner
|
| + // for the dedicated thread on which the context lives (owned by its
|
| + // UIAutomationClient).
|
| + void Initialize(base::SingleThreadTaskRunner* task_runner,
|
| + base::SequencedTaskRunner* monitor_runner,
|
| + const base::WeakPtr<SettingsAppMonitor>& monitor);
|
| +
|
| + private:
|
| + class EventHandler;
|
| +
|
| + // The one and only method that may be called from outside of the automation
|
| + // thread.
|
| + Context();
|
| + ~Context();
|
| +
|
| + // Method(s) invoked by event handlers via weak pointers.
|
| +
|
| + // Handles a focus change event on |sender|. Dispatches OnAppFocused if
|
| + // |sender| is the settings app.
|
| + void HandleFocusChangedEvent(
|
| + base::win::ScopedComPtr<IUIAutomationElement> sender);
|
| +
|
| + // Handles the invocation of the element that opens the browser chooser.
|
| + void HandleChooserInvoked();
|
| +
|
| + // Handles the invocation of an element in the browser chooser.
|
| + void HandleBrowserChosen(const base::string16& browser_name);
|
| +
|
| + // Returns an event handler for all event types of interest.
|
| + base::win::ScopedComPtr<IUnknown> GetEventHandler();
|
| +
|
| + // Returns a pointer to the event handler's generic interface.
|
| + base::win::ScopedComPtr<IUIAutomationEventHandler>
|
| + GetAutomationEventHandler();
|
| +
|
| + // Returns a pointer to the event handler's focus changed interface.
|
| + base::win::ScopedComPtr<IUIAutomationFocusChangedEventHandler>
|
| + GetFocusChangedEventHandler();
|
| +
|
| + // Installs an event handler to observe events of interest.
|
| + HRESULT InstallObservers();
|
| +
|
| + // The task runner for the automation thread.
|
| + base::SingleThreadTaskRunner* task_runner_ = nullptr;
|
| +
|
| + // The task runner on which the owning monitor lives.
|
| + base::SequencedTaskRunner* monitor_runner_ = nullptr;
|
| +
|
| + // The monitor that owns this context.
|
| + base::WeakPtr<SettingsAppMonitor> monitor_;
|
| +
|
| + // The automation client.
|
| + base::win::ScopedComPtr<IUIAutomation> automation_;
|
| +
|
| + // The event handler.
|
| + base::win::ScopedComPtr<IUnknown> event_handler_;
|
| +
|
| + // State to suppress duplicate OnAppFocused notifications.
|
| + bool observed_app_focused_ = false;
|
| +
|
| + // Weak pointers to the context are given to event handlers.
|
| + base::WeakPtrFactory<Context> weak_ptr_factory_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(Context);
|
| +};
|
| +
|
| +
|
| +// SettingsAppMonitor::Context::EventHandler -----------------------------------
|
| +
|
| +// A handler of events on arbitrary threads in the MTA.
|
| +class SettingsAppMonitor::Context::EventHandler
|
| + : public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>,
|
| + public IUIAutomationEventHandler,
|
| + public IUIAutomationFocusChangedEventHandler {
|
| + public:
|
| + BEGIN_COM_MAP(SettingsAppMonitor::Context::EventHandler)
|
| + COM_INTERFACE_ENTRY(IUIAutomationEventHandler)
|
| + COM_INTERFACE_ENTRY(IUIAutomationFocusChangedEventHandler)
|
| + END_COM_MAP()
|
| +
|
| + EventHandler();
|
| + ~EventHandler();
|
| +
|
| + // Initializes the object. Events will be dispatched back to |context| via
|
| + // |context_runner|.
|
| + void Initialize(scoped_refptr<base::SingleThreadTaskRunner> context_runner,
|
| + const base::WeakPtr<SettingsAppMonitor::Context>& context);
|
| +
|
| + // IUIAutomationEventHandler:
|
| + STDMETHOD(HandleAutomationEvent)(IUIAutomationElement *sender,
|
| + EVENTID eventId) override;
|
| +
|
| + // IUIAutomationFocusChangedEventHandler:
|
| + STDMETHOD(HandleFocusChangedEvent)(IUIAutomationElement* sender) override;
|
| +
|
| + private:
|
| + // The task runner for the monitor client context.
|
| + scoped_refptr<base::SingleThreadTaskRunner> context_runner_;
|
| +
|
| + // The monitor context that owns this event handler.
|
| + base::WeakPtr<SettingsAppMonitor::Context> context_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(EventHandler);
|
| +};
|
| +
|
| +
|
| +// Utility functions -----------------------------------------------------------
|
| +
|
| +base::string16 GetCachedBstrValue(IUIAutomationElement* element,
|
| + PROPERTYID property_id) {
|
| + HRESULT result = S_OK;
|
| + base::win::ScopedVariant var;
|
| +
|
| + result = element->GetCachedPropertyValueEx(property_id, TRUE, var.Receive());
|
| + if (FAILED(result))
|
| + return base::string16();
|
| +
|
| + if (V_VT(var.ptr()) != VT_BSTR) {
|
| + LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
|
| + << __FUNCTION__ << " property is not a BSTR: " << V_VT(var.ptr());
|
| + return base::string16();
|
| + }
|
| +
|
| + return base::string16(V_BSTR(var.ptr()));
|
| +}
|
| +
|
| +enum class ElementType {
|
| + // The "Web browser" element in the "Default apps" pane.
|
| + DEFAULT_BROWSER,
|
| + // The element representing a browser in the "Choose an app" popup.
|
| + BROWSER_BUTTON,
|
| + // Any other element.
|
| + UNKNOWN,
|
| +};
|
| +
|
| +ElementType DetectElementType(IUIAutomationElement* sender) {
|
| + base::string16 aid(GetCachedBstrValue(sender, UIA_AutomationIdPropertyId));
|
| + if (aid == L"SystemSettings_DefaultApps_Browser_Button")
|
| + return ElementType::DEFAULT_BROWSER;
|
| + if (base::MatchPattern(aid, L"SystemSettings_DefaultApps_Browser_*_Button"))
|
| + return ElementType::BROWSER_BUTTON;
|
| + return ElementType::UNKNOWN;
|
| +}
|
| +
|
| +// Configures a cache request so that it includes all properties needed by
|
| +// DetectElementType() to detect the elements of interest.
|
| +void ConfigureCacheRequest(IUIAutomationCacheRequest* cache_request) {
|
| + DCHECK(cache_request);
|
| + cache_request->AddProperty(UIA_AutomationIdPropertyId);
|
| + cache_request->AddProperty(UIA_NamePropertyId);
|
| +#if ENABLE_DLOG
|
| + cache_request->AddProperty(UIA_ClassNamePropertyId);
|
| + cache_request->AddProperty(UIA_ControlTypePropertyId);
|
| + cache_request->AddProperty(UIA_IsPeripheralPropertyId);
|
| + cache_request->AddProperty(UIA_ProcessIdPropertyId);
|
| + cache_request->AddProperty(UIA_ValueValuePropertyId);
|
| + cache_request->AddProperty(UIA_RuntimeIdPropertyId);
|
| +#endif // ENABLE_DLOG
|
| +}
|
| +
|
| +
|
| +// Debug logging utility functions ---------------------------------------------
|
| +
|
| +bool GetCachedBoolValue(IUIAutomationElement* element, PROPERTYID property_id) {
|
| +#if ENABLE_DLOG
|
| + base::win::ScopedVariant var;
|
| +
|
| + if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
|
| + var.Receive()))) {
|
| + return false;
|
| + }
|
| +
|
| + if (V_VT(var.ptr()) != VT_BOOL) {
|
| + LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
|
| + << __FUNCTION__ << " property is not a BOOL: " << V_VT(var.ptr());
|
| + return false;
|
| + }
|
| +
|
| + return V_BOOL(var.ptr()) != 0;
|
| +#else // ENABLE_DLOG
|
| + return false;
|
| +#endif // !ENABLE_DLOG
|
| +}
|
| +
|
| +int32_t GetCachedInt32Value(IUIAutomationElement* element,
|
| + PROPERTYID property_id) {
|
| +#if ENABLE_DLOG
|
| + base::win::ScopedVariant var;
|
| +
|
| + if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
|
| + var.Receive()))) {
|
| + return false;
|
| + }
|
| +
|
| + if (V_VT(var.ptr()) != VT_I4) {
|
| + LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
|
| + << __FUNCTION__ << " property is not an I4: " << V_VT(var.ptr());
|
| + return false;
|
| + }
|
| +
|
| + return V_I4(var.ptr());
|
| +#else // ENABLE_DLOG
|
| + return 0;
|
| +#endif // !ENABLE_DLOG
|
| +}
|
| +
|
| +std::vector<int32_t> GetCachedInt32ArrayValue(IUIAutomationElement* element,
|
| + PROPERTYID property_id) {
|
| + std::vector<int32_t> values;
|
| +#if ENABLE_DLOG
|
| + base::win::ScopedVariant var;
|
| +
|
| + if (FAILED(element->GetCachedPropertyValueEx(property_id, TRUE,
|
| + var.Receive()))) {
|
| + return values;
|
| + }
|
| +
|
| + if (V_VT(var.ptr()) != (VT_I4 | VT_ARRAY)) {
|
| + LOG_IF(ERROR, V_VT(var.ptr()) != VT_UNKNOWN)
|
| + << __FUNCTION__ << " property is not an I4 array: " << V_VT(var.ptr());
|
| + return values;
|
| + }
|
| +
|
| + SAFEARRAY* array = V_ARRAY(var.ptr());
|
| + if (SafeArrayGetDim(array) != 1)
|
| + return values;
|
| + long lower_bound = 0;
|
| + long upper_bound = 0;
|
| + SafeArrayGetLBound(array, 1, &lower_bound);
|
| + SafeArrayGetUBound(array, 1, &upper_bound);
|
| + if (lower_bound || upper_bound <= lower_bound)
|
| + return values;
|
| + int32_t* data = nullptr;
|
| + SafeArrayAccessData(array, reinterpret_cast<void**>(&data));
|
| + values.assign(data, data + upper_bound + 1);
|
| + SafeArrayUnaccessData(array);
|
| +#endif // ENABLE_DLOG
|
| + return values;
|
| +}
|
| +
|
| +std::string IntArrayToString(const std::vector<int32_t>& values) {
|
| +#if ENABLE_DLOG
|
| + std::vector<std::string> value_strings;
|
| + std::transform(values.begin(), values.end(),
|
| + std::back_inserter(value_strings), &base::IntToString);
|
| + return base::JoinString(value_strings, ", ");
|
| +#else // ENABLE_DLOG
|
| + return std::string();
|
| +#endif // !ENABLE_DLOG
|
| +}
|
| +
|
| +const char* GetEventName(EVENTID event_id) {
|
| +#if ENABLE_DLOG
|
| + switch (event_id) {
|
| + case UIA_ToolTipOpenedEventId:
|
| + return "UIA_ToolTipOpenedEventId";
|
| + case UIA_ToolTipClosedEventId:
|
| + return "UIA_ToolTipClosedEventId";
|
| + case UIA_StructureChangedEventId:
|
| + return "UIA_StructureChangedEventId";
|
| + case UIA_MenuOpenedEventId:
|
| + return "UIA_MenuOpenedEventId";
|
| + case UIA_AutomationPropertyChangedEventId:
|
| + return "UIA_AutomationPropertyChangedEventId";
|
| + case UIA_AutomationFocusChangedEventId:
|
| + return "UIA_AutomationFocusChangedEventId";
|
| + case UIA_AsyncContentLoadedEventId:
|
| + return "UIA_AsyncContentLoadedEventId";
|
| + case UIA_MenuClosedEventId:
|
| + return "UIA_MenuClosedEventId";
|
| + case UIA_LayoutInvalidatedEventId:
|
| + return "UIA_LayoutInvalidatedEventId";
|
| + case UIA_Invoke_InvokedEventId:
|
| + return "UIA_Invoke_InvokedEventId";
|
| + case UIA_SelectionItem_ElementAddedToSelectionEventId:
|
| + return "UIA_SelectionItem_ElementAddedToSelectionEventId";
|
| + case UIA_SelectionItem_ElementRemovedFromSelectionEventId:
|
| + return "UIA_SelectionItem_ElementRemovedFromSelectionEventId";
|
| + case UIA_SelectionItem_ElementSelectedEventId:
|
| + return "UIA_SelectionItem_ElementSelectedEventId";
|
| + case UIA_Selection_InvalidatedEventId:
|
| + return "UIA_Selection_InvalidatedEventId";
|
| + case UIA_Text_TextSelectionChangedEventId:
|
| + return "UIA_Text_TextSelectionChangedEventId";
|
| + case UIA_Text_TextChangedEventId:
|
| + return "UIA_Text_TextChangedEventId";
|
| + case UIA_Window_WindowOpenedEventId:
|
| + return "UIA_Window_WindowOpenedEventId";
|
| + case UIA_Window_WindowClosedEventId:
|
| + return "UIA_Window_WindowClosedEventId";
|
| + case UIA_MenuModeStartEventId:
|
| + return "UIA_MenuModeStartEventId";
|
| + case UIA_MenuModeEndEventId:
|
| + return "UIA_MenuModeEndEventId";
|
| + case UIA_InputReachedTargetEventId:
|
| + return "UIA_InputReachedTargetEventId";
|
| + case UIA_InputReachedOtherElementEventId:
|
| + return "UIA_InputReachedOtherElementEventId";
|
| + case UIA_InputDiscardedEventId:
|
| + return "UIA_InputDiscardedEventId";
|
| + case UIA_SystemAlertEventId:
|
| + return "UIA_SystemAlertEventId";
|
| + case UIA_LiveRegionChangedEventId:
|
| + return "UIA_LiveRegionChangedEventId";
|
| + case UIA_HostedFragmentRootsInvalidatedEventId:
|
| + return "UIA_HostedFragmentRootsInvalidatedEventId";
|
| + case UIA_Drag_DragStartEventId:
|
| + return "UIA_Drag_DragStartEventId";
|
| + case UIA_Drag_DragCancelEventId:
|
| + return "UIA_Drag_DragCancelEventId";
|
| + case UIA_Drag_DragCompleteEventId:
|
| + return "UIA_Drag_DragCompleteEventId";
|
| + case UIA_DropTarget_DragEnterEventId:
|
| + return "UIA_DropTarget_DragEnterEventId";
|
| + case UIA_DropTarget_DragLeaveEventId:
|
| + return "UIA_DropTarget_DragLeaveEventId";
|
| + case UIA_DropTarget_DroppedEventId:
|
| + return "UIA_DropTarget_DroppedEventId";
|
| + case UIA_TextEdit_TextChangedEventId:
|
| + return "UIA_TextEdit_TextChangedEventId";
|
| + case UIA_TextEdit_ConversionTargetChangedEventId:
|
| + return "UIA_TextEdit_ConversionTargetChangedEventId";
|
| + }
|
| +#endif // ENABLE_DLOG
|
| + return "";
|
| +}
|
| +
|
| +const char* GetControlType(long control_type) {
|
| +#if ENABLE_DLOG
|
| + switch (control_type) {
|
| + case UIA_ButtonControlTypeId:
|
| + return "UIA_ButtonControlTypeId";
|
| + case UIA_CalendarControlTypeId:
|
| + return "UIA_CalendarControlTypeId";
|
| + case UIA_CheckBoxControlTypeId:
|
| + return "UIA_CheckBoxControlTypeId";
|
| + case UIA_ComboBoxControlTypeId:
|
| + return "UIA_ComboBoxControlTypeId";
|
| + case UIA_EditControlTypeId:
|
| + return "UIA_EditControlTypeId";
|
| + case UIA_HyperlinkControlTypeId:
|
| + return "UIA_HyperlinkControlTypeId";
|
| + case UIA_ImageControlTypeId:
|
| + return "UIA_ImageControlTypeId";
|
| + case UIA_ListItemControlTypeId:
|
| + return "UIA_ListItemControlTypeId";
|
| + case UIA_ListControlTypeId:
|
| + return "UIA_ListControlTypeId";
|
| + case UIA_MenuControlTypeId:
|
| + return "UIA_MenuControlTypeId";
|
| + case UIA_MenuBarControlTypeId:
|
| + return "UIA_MenuBarControlTypeId";
|
| + case UIA_MenuItemControlTypeId:
|
| + return "UIA_MenuItemControlTypeId";
|
| + case UIA_ProgressBarControlTypeId:
|
| + return "UIA_ProgressBarControlTypeId";
|
| + case UIA_RadioButtonControlTypeId:
|
| + return "UIA_RadioButtonControlTypeId";
|
| + case UIA_ScrollBarControlTypeId:
|
| + return "UIA_ScrollBarControlTypeId";
|
| + case UIA_SliderControlTypeId:
|
| + return "UIA_SliderControlTypeId";
|
| + case UIA_SpinnerControlTypeId:
|
| + return "UIA_SpinnerControlTypeId";
|
| + case UIA_StatusBarControlTypeId:
|
| + return "UIA_StatusBarControlTypeId";
|
| + case UIA_TabControlTypeId:
|
| + return "UIA_TabControlTypeId";
|
| + case UIA_TabItemControlTypeId:
|
| + return "UIA_TabItemControlTypeId";
|
| + case UIA_TextControlTypeId:
|
| + return "UIA_TextControlTypeId";
|
| + case UIA_ToolBarControlTypeId:
|
| + return "UIA_ToolBarControlTypeId";
|
| + case UIA_ToolTipControlTypeId:
|
| + return "UIA_ToolTipControlTypeId";
|
| + case UIA_TreeControlTypeId:
|
| + return "UIA_TreeControlTypeId";
|
| + case UIA_TreeItemControlTypeId:
|
| + return "UIA_TreeItemControlTypeId";
|
| + case UIA_CustomControlTypeId:
|
| + return "UIA_CustomControlTypeId";
|
| + case UIA_GroupControlTypeId:
|
| + return "UIA_GroupControlTypeId";
|
| + case UIA_ThumbControlTypeId:
|
| + return "UIA_ThumbControlTypeId";
|
| + case UIA_DataGridControlTypeId:
|
| + return "UIA_DataGridControlTypeId";
|
| + case UIA_DataItemControlTypeId:
|
| + return "UIA_DataItemControlTypeId";
|
| + case UIA_DocumentControlTypeId:
|
| + return "UIA_DocumentControlTypeId";
|
| + case UIA_SplitButtonControlTypeId:
|
| + return "UIA_SplitButtonControlTypeId";
|
| + case UIA_WindowControlTypeId:
|
| + return "UIA_WindowControlTypeId";
|
| + case UIA_PaneControlTypeId:
|
| + return "UIA_PaneControlTypeId";
|
| + case UIA_HeaderControlTypeId:
|
| + return "UIA_HeaderControlTypeId";
|
| + case UIA_HeaderItemControlTypeId:
|
| + return "UIA_HeaderItemControlTypeId";
|
| + case UIA_TableControlTypeId:
|
| + return "UIA_TableControlTypeId";
|
| + case UIA_TitleBarControlTypeId:
|
| + return "UIA_TitleBarControlTypeId";
|
| + case UIA_SeparatorControlTypeId:
|
| + return "UIA_SeparatorControlTypeId";
|
| + case UIA_SemanticZoomControlTypeId:
|
| + return "UIA_SemanticZoomControlTypeId";
|
| + case UIA_AppBarControlTypeId:
|
| + return "UIA_AppBarControlTypeId";
|
| + }
|
| +#endif // ENABLE_DLOG
|
| + return "";
|
| +}
|
| +
|
| +
|
| +// SettingsAppMonitor::Context::EventHandler -----------------------------------
|
| +
|
| +SettingsAppMonitor::Context::EventHandler::EventHandler() = default;
|
| +
|
| +SettingsAppMonitor::Context::EventHandler::~EventHandler() = default;
|
| +
|
| +void SettingsAppMonitor::Context::EventHandler::Initialize(
|
| + scoped_refptr<base::SingleThreadTaskRunner> context_runner,
|
| + const base::WeakPtr<SettingsAppMonitor::Context>& context) {
|
| + context_runner_ = std::move(context_runner);
|
| + context_ = context;
|
| +}
|
| +
|
| +HRESULT SettingsAppMonitor::Context::EventHandler::HandleAutomationEvent(
|
| + IUIAutomationElement* sender,
|
| + EVENTID event_id) {
|
| + DVLOG(1) << "event id: " << GetEventName(event_id) << ", automation id: "
|
| + << GetCachedBstrValue(sender, UIA_AutomationIdPropertyId)
|
| + << ", name: " << GetCachedBstrValue(sender, UIA_NamePropertyId)
|
| + << ", control type: " << GetControlType(GetCachedInt32Value(
|
| + sender, UIA_ControlTypePropertyId))
|
| + << ", is peripheral: "
|
| + << GetCachedBoolValue(sender, UIA_IsPeripheralPropertyId)
|
| + << ", class name: "
|
| + << GetCachedBstrValue(sender, UIA_ClassNamePropertyId)
|
| + << ", pid: " << GetCachedInt32Value(sender, UIA_ProcessIdPropertyId)
|
| + << ", value: "
|
| + << GetCachedBstrValue(sender, UIA_ValueValuePropertyId)
|
| + << ", runtime id: " << IntArrayToString(GetCachedInt32ArrayValue(
|
| + sender, UIA_RuntimeIdPropertyId));
|
| +
|
| + switch (DetectElementType(sender)) {
|
| + case ElementType::DEFAULT_BROWSER:
|
| + context_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&SettingsAppMonitor::Context::HandleChooserInvoked,
|
| + context_));
|
| + break;
|
| + case ElementType::BROWSER_BUTTON: {
|
| + base::string16 browser_name(
|
| + GetCachedBstrValue(sender, UIA_NamePropertyId));
|
| + if (!browser_name.empty()) {
|
| + context_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&SettingsAppMonitor::Context::HandleBrowserChosen,
|
| + context_, browser_name));
|
| + }
|
| + break;
|
| + }
|
| + case ElementType::UNKNOWN:
|
| + break;
|
| + }
|
| +
|
| + return S_OK;
|
| +}
|
| +
|
| +HRESULT SettingsAppMonitor::Context::EventHandler::HandleFocusChangedEvent(
|
| + IUIAutomationElement* sender) {
|
| + DVLOG(1) << "focus changed for automation id: "
|
| + << GetCachedBstrValue(sender, UIA_AutomationIdPropertyId)
|
| + << ", name: " << GetCachedBstrValue(sender, UIA_NamePropertyId)
|
| + << ", control type: " << GetControlType(GetCachedInt32Value(
|
| + sender, UIA_ControlTypePropertyId))
|
| + << ", is peripheral: "
|
| + << GetCachedBoolValue(sender, UIA_IsPeripheralPropertyId)
|
| + << ", class name: "
|
| + << GetCachedBstrValue(sender, UIA_ClassNamePropertyId)
|
| + << ", pid: " << GetCachedInt32Value(sender, UIA_ProcessIdPropertyId)
|
| + << ", value: "
|
| + << GetCachedBstrValue(sender, UIA_ValueValuePropertyId)
|
| + << ", runtime id: " << IntArrayToString(GetCachedInt32ArrayValue(
|
| + sender, UIA_RuntimeIdPropertyId));
|
| + context_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&SettingsAppMonitor::Context::HandleFocusChangedEvent,
|
| + context_,
|
| + base::win::ScopedComPtr<IUIAutomationElement>(sender)));
|
| +
|
| + return S_OK;
|
| +}
|
| +
|
| +// SettingsAppMonitor::Context -------------------------------------------------
|
| +
|
| +base::WeakPtr<SettingsAppMonitor::Context>
|
| +SettingsAppMonitor::Context::Create() {
|
| + Context* context = new Context();
|
| + return context->weak_ptr_factory_.GetWeakPtr();
|
| +}
|
| +
|
| +void SettingsAppMonitor::Context::DeleteOnAutomationThread() {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + delete this;
|
| +}
|
| +
|
| +void SettingsAppMonitor::Context::Initialize(
|
| + base::SingleThreadTaskRunner* task_runner,
|
| + base::SequencedTaskRunner* monitor_runner,
|
| + const base::WeakPtr<SettingsAppMonitor>& monitor) {
|
| + // This and all other methods must be called on the automation thread.
|
| + DCHECK(task_runner->BelongsToCurrentThread());
|
| + DCHECK(!monitor_runner->RunsTasksOnCurrentThread());
|
| +
|
| + task_runner_ = task_runner;
|
| + monitor_runner_ = monitor_runner;
|
| + monitor_ = monitor;
|
| +
|
| + HRESULT result = automation_.CreateInstance(CLSID_CUIAutomation, nullptr,
|
| + CLSCTX_INPROC_SERVER);
|
| + if (SUCCEEDED(result))
|
| + result = automation_ ? InstallObservers() : E_FAIL;
|
| +
|
| + // Tell the monitor that initialization is complete one way or the other.
|
| + monitor_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&SettingsAppMonitor::OnInitialized, monitor_, result));
|
| +
|
| + // Self-destruct immediately if initialization failed to reduce overhead.
|
| + if (FAILED(result))
|
| + delete this;
|
| +}
|
| +
|
| +SettingsAppMonitor::Context::Context() : weak_ptr_factory_(this) {}
|
| +
|
| +SettingsAppMonitor::Context::~Context() {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| +
|
| + if (event_handler_) {
|
| + event_handler_.Release();
|
| + automation_->RemoveAllEventHandlers();
|
| + }
|
| +}
|
| +
|
| +void SettingsAppMonitor::Context::HandleFocusChangedEvent(
|
| + base::win::ScopedComPtr<IUIAutomationElement> sender) {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| +
|
| + if (DetectElementType(sender.get()) == ElementType::DEFAULT_BROWSER) {
|
| + if (!observed_app_focused_) {
|
| + observed_app_focused_ = true;
|
| + monitor_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&SettingsAppMonitor::OnAppFocused, monitor_));
|
| + }
|
| + } else {
|
| + observed_app_focused_ = false;
|
| + }
|
| +}
|
| +
|
| +void SettingsAppMonitor::Context::HandleChooserInvoked() {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + monitor_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&SettingsAppMonitor::OnChooserInvoked, monitor_));
|
| +}
|
| +
|
| +void SettingsAppMonitor::Context::HandleBrowserChosen(
|
| + const base::string16& browser_name) {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + monitor_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&SettingsAppMonitor::OnBrowserChosen, monitor_, browser_name));
|
| +}
|
| +
|
| +base::win::ScopedComPtr<IUnknown>
|
| +SettingsAppMonitor::Context::GetEventHandler() {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + if (!event_handler_) {
|
| + ATL::CComObject<EventHandler>* obj = nullptr;
|
| + HRESULT result = ATL::CComObject<EventHandler>::CreateInstance(&obj);
|
| + if (SUCCEEDED(result)) {
|
| + obj->Initialize(task_runner_, weak_ptr_factory_.GetWeakPtr());
|
| + obj->QueryInterface(event_handler_.Receive());
|
| + }
|
| + }
|
| + return event_handler_;
|
| +}
|
| +
|
| +base::win::ScopedComPtr<IUIAutomationEventHandler>
|
| +SettingsAppMonitor::Context::GetAutomationEventHandler() {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + base::win::ScopedComPtr<IUIAutomationEventHandler> handler;
|
| + handler.QueryFrom(GetEventHandler().get());
|
| + return handler;
|
| +}
|
| +
|
| +base::win::ScopedComPtr<IUIAutomationFocusChangedEventHandler>
|
| +SettingsAppMonitor::Context::GetFocusChangedEventHandler() {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + base::win::ScopedComPtr<IUIAutomationFocusChangedEventHandler> handler;
|
| + handler.QueryFrom(GetEventHandler().get());
|
| + return handler;
|
| +}
|
| +
|
| +HRESULT SettingsAppMonitor::Context::InstallObservers() {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + DCHECK(automation_);
|
| +
|
| + // Create a cache request so that elements received by way of events contain
|
| + // all data needed for procesing.
|
| + base::win::ScopedComPtr<IUIAutomationCacheRequest> cache_request;
|
| + HRESULT result = automation_->CreateCacheRequest(cache_request.Receive());
|
| + if (FAILED(result))
|
| + return result;
|
| + ConfigureCacheRequest(cache_request.get());
|
| +
|
| + // Observe changes in focus.
|
| + result = automation_->AddFocusChangedEventHandler(
|
| + cache_request.get(), GetFocusChangedEventHandler().get());
|
| + if (FAILED(result))
|
| + return result;
|
| +
|
| + // Observe invocations.
|
| + base::win::ScopedComPtr<IUIAutomationElement> desktop;
|
| + result = automation_->GetRootElement(desktop.Receive());
|
| + if (desktop) {
|
| + result = automation_->AddAutomationEventHandler(
|
| + UIA_Invoke_InvokedEventId, desktop.get(), TreeScope_Subtree,
|
| + cache_request.get(), GetAutomationEventHandler().get());
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +
|
| +// SettingsAppMonitor ----------------------------------------------------------
|
| +
|
| +SettingsAppMonitor::SettingsAppMonitor(Delegate* delegate)
|
| + : delegate_(delegate),
|
| + automation_thread_("SettingsAppMonitorAutomation"),
|
| + weak_ptr_factory_(this) {
|
| + ui::win::CreateATLModuleIfNeeded();
|
| + // Start the automation thread and initialize the automation client on it.
|
| + context_ = Context::Create();
|
| + automation_thread_.init_com_with_mta(true);
|
| + automation_thread_.Start();
|
| + automation_thread_.task_runner()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&SettingsAppMonitor::Context::Initialize, context_,
|
| + base::Unretained(automation_thread_.task_runner().get()),
|
| + base::Unretained(base::SequencedTaskRunnerHandle::Get().get()),
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +SettingsAppMonitor::~SettingsAppMonitor() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| +
|
| + // context_ is still valid when the caller destroys the instance before the
|
| + // callback(s) have fired. In this case, delete the context on the automation
|
| + // thread before joining with it. DeleteSoon is not used because the monitor
|
| + // has only a WeakPtr to the context that is bound to the automation thread.
|
| + automation_thread_.task_runner()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&SettingsAppMonitor::Context::DeleteOnAutomationThread,
|
| + context_));
|
| +}
|
| +
|
| +void SettingsAppMonitor::OnInitialized(HRESULT result) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + delegate_->OnInitialized(result);
|
| +}
|
| +
|
| +void SettingsAppMonitor::OnAppFocused() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + delegate_->OnAppFocused();
|
| +}
|
| +
|
| +void SettingsAppMonitor::OnChooserInvoked() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + delegate_->OnChooserInvoked();
|
| +}
|
| +
|
| +void SettingsAppMonitor::OnBrowserChosen(const base::string16& browser_name) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + delegate_->OnBrowserChosen(browser_name);
|
| +}
|
| +
|
| +} // namespace win
|
| +} // namespace shell_integration
|
|
|