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

Unified Diff: chrome/browser/win/settings_app_monitor.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: fix histograms.xml typo 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/win/settings_app_monitor.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « chrome/browser/win/settings_app_monitor.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698