Skip to content

Commit

Permalink
Reland "Add the LaunchUninstallFlow() function"
Browse files Browse the repository at this point in the history
This is a reland of 5e06564

Original change's description:
> Add the LaunchUninstallFlow() function
>
> This function uses automation to automatically write the name of a
> program into the search box of the Apps & Features page.
>
> Bug: 717696
> Change-Id: Ifc6a1be295a3d06ac33f394dcf3fc4688bd4a907
> Reviewed-on: https://chromium-review.googlesource.com/907590
> Reviewed-by: Demetrios Papadopoulos <dpapad@chromium.org>
> Reviewed-by: Greg Thompson <grt@chromium.org>
> Commit-Queue: Patrick Monette <pmonette@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#540209}

TBR: grt@chromium.org, dpapad@chromium.org
Bug: 717696
Change-Id: I9048a36c52113311496abe5d2d5d1ac3fe3c79fc
Reviewed-on: https://chromium-review.googlesource.com/944202
Reviewed-by: Patrick Monette <pmonette@chromium.org>
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Cr-Commit-Position: refs/heads/master@{#540296}
  • Loading branch information
plmonette-zz authored and Commit Bot committed Mar 1, 2018
1 parent 6151dae commit 10fd49a
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 6 deletions.
2 changes: 2 additions & 0 deletions chrome/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -2968,6 +2968,8 @@ jumbo_split_static_library("browser") {
"conflicts/problematic_programs_updater_win.h",
"conflicts/third_party_conflicts_manager_win.cc",
"conflicts/third_party_conflicts_manager_win.h",
"conflicts/uninstall_application_win.cc",
"conflicts/uninstall_application_win.h",
"google/google_update_win.cc",
"google/google_update_win.h",
]
Expand Down
224 changes: 224 additions & 0 deletions chrome/browser/conflicts/uninstall_application_win.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Copyright 2018 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/conflicts/uninstall_application_win.h"

#include <atlbase.h>
#include <wrl/client.h>

#include <memory>
#include <utility>

#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/pattern.h"
#include "base/strings/string_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/win/scoped_variant.h"
#include "chrome/browser/win/automation_controller.h"
#include "chrome/browser/win/ui_automation_util.h"
#include "chrome/installer/util/shell_util.h"

namespace uninstall_application {

namespace {

bool FindSearchBoxElement(IUIAutomation* automation,
IUIAutomationElement* sender,
IUIAutomationElement** search_box) {
// Create a condition that will include only elements with the right
// automation id in the tree walker.
base::win::ScopedVariant search_box_id(
L"SystemSettings_StorageSense_AppSizesListFilter_DisplayStringValue");
Microsoft::WRL::ComPtr<IUIAutomationCondition> condition;
HRESULT result = automation->CreatePropertyCondition(
UIA_AutomationIdPropertyId, search_box_id, condition.GetAddressOf());
if (FAILED(result))
return false;

Microsoft::WRL::ComPtr<IUIAutomationTreeWalker> tree_walker;
result =
automation->CreateTreeWalker(condition.Get(), tree_walker.GetAddressOf());
if (FAILED(result))
return false;

// Setup a cache request so that the element contains the needed property
// afterwards.
Microsoft::WRL::ComPtr<IUIAutomationCacheRequest> cache_request;
result = automation->CreateCacheRequest(cache_request.GetAddressOf());
if (FAILED(result))
return false;
cache_request->AddPattern(UIA_ValuePatternId);

result = tree_walker->GetNextSiblingElementBuildCache(
sender, cache_request.Get(), search_box);
return SUCCEEDED(result) && *search_box;
}

// UninstallAppController ------------------------------------------------------

class UninstallAppController {
public:
// Launches the Apps & Features page, ensuring the |application_name| is
// written into the search box.
static void Launch(const base::string16& application_name);

private:
class AutomationControllerDelegate;

// The unique instance of this class.
static UninstallAppController* instance_;

explicit UninstallAppController(const base::string16& application_name);
~UninstallAppController();

void OnUninstallFinished();

// Allows the use of the UI Automation API.
std::unique_ptr<AutomationController> automation_controller_;

base::WeakPtrFactory<UninstallAppController> weak_ptr_factory_;

DISALLOW_COPY_AND_ASSIGN(UninstallAppController);
};

// static
UninstallAppController* UninstallAppController::instance_ = nullptr;

// static
void UninstallAppController::Launch(const base::string16& application_name) {
// If an instance already exists, the previous controller is deleted to make
// sure it doesn't interfere with the current call.
delete instance_;

// The instance handles its own lifetime.
instance_ = new UninstallAppController(application_name);
}

UninstallAppController::UninstallAppController(
const base::string16& application_name)
: weak_ptr_factory_(this) {
auto automation_controller_delegate =
std::make_unique<AutomationControllerDelegate>(
base::SequencedTaskRunnerHandle::Get(),
base::BindOnce(&UninstallAppController::OnUninstallFinished,
weak_ptr_factory_.GetWeakPtr()),
application_name);

automation_controller_ = std::make_unique<AutomationController>(
std::move(automation_controller_delegate));
}

UninstallAppController::~UninstallAppController() = default;

void UninstallAppController::OnUninstallFinished() {
DCHECK_EQ(this, instance_);

delete this;
instance_ = nullptr;
}

// UninstallAppController::AutomationControllerDelegate ------------------------

class UninstallAppController::AutomationControllerDelegate
: public AutomationController::Delegate {
public:
AutomationControllerDelegate(
scoped_refptr<base::SequencedTaskRunner> controller_runner,
base::OnceClosure on_automation_finished,
const base::string16& application_name);
~AutomationControllerDelegate() override;

// AutomationController::Delegate:
void OnInitialized(HRESULT result) const override;
void ConfigureCacheRequest(
IUIAutomationCacheRequest* cache_request) const override;
void OnAutomationEvent(IUIAutomation* automation,
IUIAutomationElement* sender,
EVENTID event_id) const override;
void OnFocusChangedEvent(IUIAutomation* automation,
IUIAutomationElement* sender) const override;

private:
// The task runner on which the UninstallAppController lives.
scoped_refptr<base::SequencedTaskRunner> controller_runner_;

// Called once when the automation work is done. Only used by
// OnFocusChangedEvent().
mutable base::OnceClosure on_automation_finished_;

const base::string16 application_name_;

DISALLOW_COPY_AND_ASSIGN(AutomationControllerDelegate);
};

UninstallAppController::AutomationControllerDelegate::
AutomationControllerDelegate(
scoped_refptr<base::SequencedTaskRunner> controller_runner,
base::OnceClosure on_automation_finished,
const base::string16& application_name)
: controller_runner_(std::move(controller_runner)),
on_automation_finished_(std::move(on_automation_finished)),
application_name_(application_name) {}

UninstallAppController::AutomationControllerDelegate::
~AutomationControllerDelegate() = default;

void UninstallAppController::AutomationControllerDelegate::OnInitialized(
HRESULT result) const {
// Launch the Apps & Features settings page regardless of the |result| of the
// initialization. An initialization failure only means that the application
// will not be written into the search box.
ShellUtil::LaunchUninstallAppsSettings();
}

void UninstallAppController::AutomationControllerDelegate::
ConfigureCacheRequest(IUIAutomationCacheRequest* cache_request) const {
cache_request->AddPattern(UIA_ValuePatternId);
cache_request->AddProperty(UIA_AutomationIdPropertyId);
cache_request->AddProperty(UIA_IsWindowPatternAvailablePropertyId);
}

void UninstallAppController::AutomationControllerDelegate::OnAutomationEvent(
IUIAutomation* automation,
IUIAutomationElement* sender,
EVENTID event_id) const {}

void UninstallAppController::AutomationControllerDelegate::OnFocusChangedEvent(
IUIAutomation* automation,
IUIAutomationElement* sender) const {
base::string16 combo_box_id(
GetCachedBstrValue(sender, UIA_AutomationIdPropertyId));
if (combo_box_id != L"SystemSettings_AppsFeatures_AppControl_ComboBox")
return;

Microsoft::WRL::ComPtr<IUIAutomationElement> search_box;
if (!FindSearchBoxElement(automation, sender, search_box.GetAddressOf()))
return;

Microsoft::WRL::ComPtr<IUIAutomationValuePattern> value_pattern;
HRESULT result = search_box->GetCachedPatternAs(
UIA_ValuePatternId, IID_PPV_ARGS(value_pattern.GetAddressOf()));
if (FAILED(result))
return;

CComBSTR bstr(application_name_.c_str());
value_pattern->SetValue(bstr);

controller_runner_->PostTask(FROM_HERE, std::move(on_automation_finished_));
}

} // namespace

void LaunchUninstallFlow(const base::string16& application_name) {
UninstallAppController::Launch(application_name);
}

} // namespace uninstall_application
18 changes: 18 additions & 0 deletions chrome/browser/conflicts/uninstall_application_win.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2018 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.

#ifndef CHROME_BROWSER_CONFLICTS_UNINSTALL_APPLICATION_WIN_H_
#define CHROME_BROWSER_CONFLICTS_UNINSTALL_APPLICATION_WIN_H_

#include "base/strings/string16.h"

namespace uninstall_application {

// Uses UI automation to asynchronously open the Apps & Features page with the
// application name written in the search box, to filter out other applications.
void LaunchUninstallFlow(const base::string16& application_name);

} // namespace uninstall_application

#endif // CHROME_BROWSER_CONFLICTS_UNINSTALL_APPLICATION_WIN_H_
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@

#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/conflicts/problematic_programs_updater_win.h"
#include "chrome/browser/conflicts/uninstall_application_win.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

Expand Down Expand Up @@ -63,8 +65,9 @@ void IncompatibleApplicationsHandler::HandleStartProgramUninstallation(
const base::ListValue* args) {
CHECK_EQ(1u, args->GetList().size());

// TODO(pmonette): Open the Apps & Settings page with the program name
// highlighted.
// Open the Apps & Settings page with the program name highlighted.
uninstall_application::LaunchUninstallFlow(
base::UTF8ToUTF16(args->GetList()[0].GetString()));
}

void IncompatibleApplicationsHandler::HandleGetSubtitlePluralString(
Expand Down
8 changes: 4 additions & 4 deletions chrome/browser/win/settings_app_monitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class SettingsAppMonitor::AutomationControllerDelegate
public:
AutomationControllerDelegate(
scoped_refptr<base::SequencedTaskRunner> monitor_runner,
const base::WeakPtr<SettingsAppMonitor> monitor);
base::WeakPtr<SettingsAppMonitor> monitor);
~AutomationControllerDelegate() override;

// AutomationController::Delegate:
Expand All @@ -123,7 +123,7 @@ class SettingsAppMonitor::AutomationControllerDelegate

private:
// The task runner on which the SettingsAppMonitor lives.
scoped_refptr<base::SequencedTaskRunner> monitor_runner_;
const scoped_refptr<base::SequencedTaskRunner> monitor_runner_;

// Only used to post callbacks to |monitor_runner_|;
const base::WeakPtr<SettingsAppMonitor> monitor_;
Expand All @@ -137,9 +137,9 @@ class SettingsAppMonitor::AutomationControllerDelegate

SettingsAppMonitor::AutomationControllerDelegate::AutomationControllerDelegate(
scoped_refptr<base::SequencedTaskRunner> monitor_runner,
const base::WeakPtr<SettingsAppMonitor> monitor)
base::WeakPtr<SettingsAppMonitor> monitor)
: monitor_runner_(monitor_runner),
monitor_(monitor),
monitor_(std::move(monitor)),
last_focused_element_(ElementType::UNKNOWN) {}

SettingsAppMonitor::AutomationControllerDelegate::
Expand Down
23 changes: 23 additions & 0 deletions chrome/installer/util/shell_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,29 @@ bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist,
return ret;
}

#if defined(GOOGLE_CHROME_BUILD)
// static
bool ShellUtil::LaunchUninstallAppsSettings() {
DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN10);

static constexpr wchar_t kControlPanelAppModelId[] =
L"windows.immersivecontrolpanel_cw5n1h2txyewy"
L"!microsoft.windows.immersivecontrolpanel";

Microsoft::WRL::ComPtr<IApplicationActivationManager> activator;
HRESULT hr = ::CoCreateInstance(CLSID_ApplicationActivationManager, nullptr,
CLSCTX_ALL, IID_PPV_ARGS(&activator));
if (FAILED(hr))
return false;

DWORD pid = 0;
CoAllowSetForegroundWindow(activator.Get(), nullptr);
hr = activator->ActivateApplication(
kControlPanelAppModelId, L"page=SettingsPageAppsSizes", AO_NONE, &pid);
return SUCCEEDED(hr);
}
#endif // defined(GOOGLE_CHROME_BUILD)

bool ShellUtil::ShowMakeChromeDefaultSystemUI(
BrowserDistribution* dist,
const base::FilePath& chrome_exe) {
Expand Down
7 changes: 7 additions & 0 deletions chrome/installer/util/shell_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,13 @@ class ShellUtil {
const base::FilePath& chrome_exe,
bool elevate_if_not_admin);

#if defined(GOOGLE_CHROME_BUILD)
// Opens the Apps & Features page in the Windows settings.
//
// This function DCHECKS that it is only called on Windows 10 or higher.
static bool LaunchUninstallAppsSettings();
#endif

// Windows 8: Shows and waits for the "How do you want to open webpages?"
// dialog if Chrome is not already the default HTTP/HTTPS handler. Also does
// XP-era registrations if Chrome is chosen or was already the default. Do
Expand Down

0 comments on commit 10fd49a

Please sign in to comment.