Chrome Cleaner: Add a skeleton controller class.
Adds a skeleton ChromeCleanerController class intended to control the
execution of the Chrome Cleaner. The full implementation will be added
in subsequent CLs.
BUG=690020
Review-Url: https://codereview.chromium.org/2865413002
Cr-Commit-Position: refs/heads/master@{#471932}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index cf3bf21c..d49c04d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1155,6 +1155,8 @@
"resource_delegate_mac.mm",
"resources_util.cc",
"resources_util.h",
+ "safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc",
+ "safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h",
"safe_browsing/chrome_cleaner/chrome_cleaner_fetcher_win.cc",
"safe_browsing/chrome_cleaner/chrome_cleaner_fetcher_win.h",
"safe_browsing/chrome_cleaner/reporter_runner_win.cc",
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc
new file mode 100644
index 0000000..0dcb88d
--- /dev/null
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc
@@ -0,0 +1,87 @@
+// Copyright 2017 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/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h"
+
+#include "base/logging.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace safe_browsing {
+
+namespace {
+
+// Global instance that is set and unset by ChromeCleanerController's
+// constructor and destructor.
+ChromeCleanerController* g_chrome_cleaner_controller = nullptr;
+
+} // namespace
+
+// static
+scoped_refptr<ChromeCleanerController> ChromeCleanerController::GetInstance() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (g_chrome_cleaner_controller)
+ return make_scoped_refptr(g_chrome_cleaner_controller);
+
+ return make_scoped_refptr(new ChromeCleanerController());
+}
+
+ChromeCleanerController* ChromeCleanerController::GetRawInstanceForTesting() {
+ return g_chrome_cleaner_controller;
+}
+
+void ChromeCleanerController::AddObserver(Observer* observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // TODO(alito): Implement this. Add to |observer_list_| and notify |observer|
+ // of the current state.
+}
+
+void ChromeCleanerController::RemoveObserver(Observer* observer) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // TODO(alito): Implement this.
+}
+
+ChromeCleanerController::ChromeCleanerController() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(!g_chrome_cleaner_controller);
+
+ g_chrome_cleaner_controller = this;
+}
+
+ChromeCleanerController::~ChromeCleanerController() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // An instance of ChromeCleanerController must keep itself alive while in any
+ // state other than |kIdle| and |kRebootRequired|.
+ DCHECK(state_ == State::kIdle || state_ == State::kRebootRequired);
+ DCHECK_EQ(this, g_chrome_cleaner_controller);
+
+ g_chrome_cleaner_controller = nullptr;
+}
+
+void ChromeCleanerController::NotifyObserver(Observer* observer) const {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ // TODO(alito): Implement this. Call |observer|'s function corresponding to
+ // the current state.
+}
+
+void ChromeCleanerController::NotifyAllObservers() const {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ for (auto& observer : observer_list_)
+ NotifyObserver(&observer);
+}
+
+void ChromeCleanerController::SetKeepAlive(bool keep_alive) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (keep_alive == keep_alive_)
+ return;
+
+ keep_alive_ = keep_alive;
+ if (keep_alive_)
+ AddRef();
+ else
+ Release();
+}
+
+} // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
new file mode 100644
index 0000000..418fcaf2
--- /dev/null
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
@@ -0,0 +1,101 @@
+// Copyright 2017 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_SAFE_BROWSING_CHROME_CLEANER_CHROME_CLEANER_CONTROLLER_WIN_H_
+#define CHROME_BROWSER_SAFE_BROWSING_CHROME_CLEANER_CHROME_CLEANER_CONTROLLER_WIN_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+
+namespace safe_browsing {
+
+// Controller class that keeps track of the execution of the Chrome Cleaner and
+// the various states through which the execution will transition. Observers can
+// register themselves to be notified of state changes. Intended to be used by
+// the Chrome Cleaner webui page and the Chrome Cleaner prompt dialog.
+//
+// This class lives on, and all its members should be called only on, the UI
+// thread.
+class ChromeCleanerController
+ : public base::RefCounted<ChromeCleanerController> {
+ public:
+ enum class State {
+ // The default state where there is no Chrome Cleaner process or IPC to keep
+ // track of and a reboot of the machine is not required to complete previous
+ // cleaning attempts. This is also the state the controller will end up in
+ // if any errors occur during execution of the Chrome Cleaner process.
+ kIdle,
+ // All steps up to and including scanning the machine occur in this
+ // state. The steps include downloading the Chrome Cleaner binary, setting
+ // up an IPC between Chrome and the Cleaner process, and the actual
+ // scanning done by the Cleaner.
+ kScanning,
+ // Scanning has been completed and harmful or unwanted software was found.
+ kInfected,
+ // The Cleaner process is cleaning the machine.
+ kCleaning,
+ // The cleaning has finished, but a reboot of the machine is required to
+ // complete cleanup. This state will persist across restarts of Chrome until
+ // a reboot is detected.
+ kRebootRequired,
+ };
+
+ class Observer {
+ public:
+ virtual void OnIdle() {}
+ virtual void OnScanning() {}
+ virtual void OnInfected() {}
+ virtual void OnCleaning() {}
+ virtual void OnRebootRequired() {}
+
+ protected:
+ virtual ~Observer() = default;
+ };
+
+ // Returns an existing instance of the controller if there is one, or else
+ // will create a new instance. There is at most one instance of the controller
+ // class at any time.
+ static scoped_refptr<ChromeCleanerController> GetInstance();
+
+ State state() const { return state_; }
+
+ // |AddObserver()| immediately notifies |observer| of the controller's state
+ // by calling the corresponding |On*()| function.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // TODO(alito): add other functions, such as Scan(), Clean() etc.
+
+ static ChromeCleanerController* GetRawInstanceForTesting();
+
+ private:
+ friend class base::RefCounted<ChromeCleanerController>;
+
+ ChromeCleanerController();
+ ~ChromeCleanerController();
+
+ void NotifyObserver(Observer* observer) const;
+ void NotifyAllObservers() const;
+ void SetKeepAlive(bool keep_alive);
+
+ State state_ = State::kIdle;
+
+ base::ObserverList<Observer> observer_list_;
+
+ // Used to indicate that this instance needs to be kept alive. The instance
+ // should not be destroyed while in any of the |kScanning|, |kInfected|, or
+ // |kCleaning| states, which are the states where we have an active IPC to the
+ // Chrome Cleaner process.
+ bool keep_alive_ = false;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeCleanerController);
+};
+
+} // namespace safe_browsing
+
+#endif // CHROME_BROWSER_SAFE_BROWSING_CHROME_CLEANER_CHROME_CLEANER_CONTROLLER_WIN_H_
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win_unittest.cc
new file mode 100644
index 0000000..8485cfa
--- /dev/null
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win_unittest.cc
@@ -0,0 +1,51 @@
+// Copyright 2017 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/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h"
+
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+namespace {
+
+class ChromeCleanerControllerTest : public ::testing::Test {
+ private:
+ // We need this because we need a UI thread during tests. The thread bundle
+ // must be the first member of the class so that it will be destroyed last.
+ content::TestBrowserThreadBundle thread_bundle_;
+};
+
+TEST_F(ChromeCleanerControllerTest, GetInstance) {
+ // Ensure that GetInstance() returns the same controller object between
+ // invocations as long someone is holding on to a reference to the controller.
+ //
+ // Also ensure that the controller object is destroyed if it is in the IDLE
+ // state and there are no more references to it.
+
+ {
+ scoped_refptr<ChromeCleanerController> controller =
+ ChromeCleanerController::GetInstance();
+ EXPECT_TRUE(controller);
+ EXPECT_EQ(controller, ChromeCleanerController::GetRawInstanceForTesting());
+ EXPECT_EQ(controller->state(), ChromeCleanerController::State::kIdle);
+
+ // The same controller instance is returned as long as there are references
+ // to it.
+ EXPECT_EQ(controller, ChromeCleanerController::GetInstance());
+ }
+ // All references have now gone out of scope and the controller object should
+ // be destroyed.
+ EXPECT_FALSE(ChromeCleanerController::GetRawInstanceForTesting());
+
+ // GetInstance() always returns a valid object.
+ scoped_refptr<ChromeCleanerController> controller =
+ ChromeCleanerController::GetInstance();
+ EXPECT_TRUE(controller);
+ EXPECT_EQ(controller, ChromeCleanerController::GetRawInstanceForTesting());
+}
+
+} // namespace
+} // namespace safe_browsing