Skip to content

Commit

Permalink
Merge pull request #101 from brave/chrome-profile-lock
Browse files Browse the repository at this point in the history
Prompt user to close Chrome before importing from a Chrome profile
  • Loading branch information
darkdh authored May 15, 2018
2 parents 693cf45 + 3e68ed7 commit 88525af
Show file tree
Hide file tree
Showing 13 changed files with 552 additions and 3 deletions.
8 changes: 8 additions & 0 deletions app/generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ If you update this file, be sure also to update google_chrome_strings.grd. -->
<message name="IDS_SHOW_BRAVE_PAYMENTS" desc="The show brave payments menu in the app menu">
Brave Payments
</message>

<!-- Importer Lock Dialog -->
<message name="IDS_CHROME_IMPORTER_LOCK_TITLE" desc="Dialog title for Chrome importer lock dialog">
Close Chrome
</message>
<message name="IDS_CHROME_IMPORTER_LOCK_TEXT" desc="The message to be displayed in the Chrome importer lock dialog">
To finish importing, close all Chrome windows.
</message>
</messages>
<includes>
<include name="IDR_BRAVE_TAG_SERVICES_POLYFILL" file="resources/js/tag_services_polyfill.js" type="BINDATA" />
Expand Down
5 changes: 4 additions & 1 deletion browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ source_set("browser_process") {
"importer/brave_external_process_importer_client.h",
"importer/brave_external_process_importer_host.cc",
"importer/brave_external_process_importer_host.h",
"importer/brave_importer_lock_dialog.h",
"importer/brave_in_process_importer_bridge.cc",
"importer/brave_in_process_importer_bridge.h",
"importer/brave_profile_writer.cc",
"importer/brave_profile_writer.h"
"importer/brave_profile_writer.h",
"importer/chrome_profile_lock.cc",
"importer/chrome_profile_lock.h",
]

deps = [
Expand Down
87 changes: 85 additions & 2 deletions browser/importer/brave_external_process_importer_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,42 @@
#include "brave/browser/importer/brave_external_process_importer_host.h"
#include "brave/browser/importer/brave_in_process_importer_bridge.h"

#include "brave/browser/importer/brave_importer_lock_dialog.h"

BraveExternalProcessImporterHost::BraveExternalProcessImporterHost()
: ExternalProcessImporterHost() {}
: ExternalProcessImporterHost(),
weak_ptr_factory_(this) {}

BraveExternalProcessImporterHost::~BraveExternalProcessImporterHost() {}

void BraveExternalProcessImporterHost::StartImportSettings(
const importer::SourceProfile& source_profile,
Profile* target_profile,
uint16_t items,
ProfileWriter* writer) {
// We really only support importing from one host at a time.
DCHECK(!profile_);
DCHECK(target_profile);

profile_ = target_profile;
writer_ = writer;
source_profile_ = source_profile;
items_ = items;

if (!ExternalProcessImporterHost::CheckForFirefoxLock(source_profile)) {
Cancel();
return;
}

if (!CheckForChromeLock(source_profile)) {
Cancel();
return;
}

ExternalProcessImporterHost::CheckForLoadedModels(items);

LaunchImportIfReady();
}

void BraveExternalProcessImporterHost::LaunchImportIfReady() {
if (waiting_for_bookmarkbar_model_ || template_service_subscription_.get() ||
Expand All @@ -27,4 +61,53 @@ void BraveExternalProcessImporterHost::LaunchImportIfReady() {
client_->Start();
}

BraveExternalProcessImporterHost::~BraveExternalProcessImporterHost() {}
void BraveExternalProcessImporterHost::ShowWarningDialog() {
DCHECK(!headless_);
brave::importer::ShowImportLockDialog(
parent_window_,
base::Bind(&BraveExternalProcessImporterHost::OnImportLockDialogEnd,
weak_ptr_factory_.GetWeakPtr()));
}

void BraveExternalProcessImporterHost::OnImportLockDialogEnd(bool is_continue) {
if (is_continue) {
// User chose to continue, then we check the lock again to make
// sure that Chrome has been closed. Try to import the settings
// if successful. Otherwise, show a warning dialog.
chrome_lock_->Lock();
if (chrome_lock_->HasAcquired()) {
is_source_readable_ = true;
LaunchImportIfReady();
} else {
ShowWarningDialog();
}
} else {
NotifyImportEnded();
}
}

bool BraveExternalProcessImporterHost::CheckForChromeLock(
const importer::SourceProfile& source_profile) {
if (source_profile.importer_type != importer::TYPE_CHROME)
return true;

// Extract the user data directory from the path of the profile to be
// imported, because we can only lock/unlock the entire user directory with
// ProcessSingleton.
base::FilePath user_data_dir = source_profile.source_path.DirName();

DCHECK(!chrome_lock_.get());
chrome_lock_.reset(new ChromeProfileLock(user_data_dir));
if (chrome_lock_->HasAcquired())
return true;

// If fail to acquire the lock, we set the source unreadable and
// show a warning dialog, unless running without UI (in which case the import
// must be aborted).
is_source_readable_ = false;
if (headless_)
return false;

ShowWarningDialog();
return true;
}
31 changes: 31 additions & 0 deletions browser/importer/brave_external_process_importer_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@
#ifndef BRAVE_BROWSER_IMPORTER_BRAVE_EXTERNAL_PROCESS_IMPORTER_HOST_H_
#define BRAVE_BROWSER_IMPORTER_BRAVE_EXTERNAL_PROCESS_IMPORTER_HOST_H_

#include "base/memory/weak_ptr.h"
#include "brave/browser/importer/chrome_profile_lock.h"
#include "chrome/browser/importer/external_process_importer_host.h"

class BraveExternalProcessImporterHost : public ExternalProcessImporterHost {
public:
BraveExternalProcessImporterHost();

void StartImportSettings(
const importer::SourceProfile& source_profile,
Profile* target_profile,
uint16_t items,
ProfileWriter* writer) override;

private:
~BraveExternalProcessImporterHost() override;

Expand All @@ -20,6 +28,29 @@ class BraveExternalProcessImporterHost : public ExternalProcessImporterHost {
// complete.
void LaunchImportIfReady() override;

// Make sure that Chrome isn't running, if import browser is Chrome. Show
// to the user a dialog that notifies that is necessary to close Chrome
// prior to continuing the import.
// |source_profile| - importer profile to import.
// Returns false iff import should be aborted.
bool CheckForChromeLock(const importer::SourceProfile& source_profile);

// ShowWarningDialog() asks user to close the application that is owning the
// lock. They can retry or skip the importing process.
// This method should not be called if the importer is in headless mode.
void ShowWarningDialog();

// This is called when when user ends the lock dialog by clicking on either
// the "Skip" or "Continue" buttons. |is_continue| is true when user clicked
// the "Continue" button.
void OnImportLockDialogEnd(bool is_continue);

// Chrome profile lock.
std::unique_ptr<ChromeProfileLock> chrome_lock_;

// Vends weak pointers for the importer to call us back.
base::WeakPtrFactory<BraveExternalProcessImporterHost> weak_ptr_factory_;

DISALLOW_COPY_AND_ASSIGN(BraveExternalProcessImporterHost);
};

Expand Down
23 changes: 23 additions & 0 deletions browser/importer/brave_importer_lock_dialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_IMPORTER_BRAVE_IMPORTER_LOCK_DIALOG_H_
#define BRAVE_BROWSER_IMPORTER_BRAVE_IMPORTER_LOCK_DIALOG_H_

#include "base/callback_forward.h"
#include "ui/gfx/native_widget_types.h"

namespace brave {
namespace importer {

// This function is called by an ImporterHost, and presents the Chrome profile
// warning dialog. After closing the dialog, the ImportHost receives a callback
// with the message either to skip the import, or to continue the process.
void ShowImportLockDialog(gfx::NativeWindow parent,
const base::Callback<void(bool)>& callback);

} // namespace importer
} // namespace brave

#endif // BRAVE_BROWSER_IMPORTER_BRAVE_IMPORTER_LOCK_DIALOG_H_
48 changes: 48 additions & 0 deletions browser/importer/chrome_profile_lock.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "brave/browser/importer/chrome_profile_lock.h"

#include "base/bind.h"
#include "base/bind_helpers.h"

ChromeProfileLock::ChromeProfileLock(
const base::FilePath& user_data_dir)
: lock_acquired_(false),
user_data_dir_(user_data_dir),
process_singleton_(new ProcessSingleton(user_data_dir,
base::Bind(&ChromeProfileLock::NotificationCallback,
base::Unretained(this)))) {
Lock();
}

ChromeProfileLock::~ChromeProfileLock() {
Unlock();
}

void ChromeProfileLock::Lock() {
if (HasAcquired())
return;
lock_acquired_ = process_singleton_->Create();
}

void ChromeProfileLock::Unlock() {
if (!HasAcquired())
return;
process_singleton_->Cleanup();
process_singleton_.reset(new ProcessSingleton(user_data_dir_,
base::Bind(&ChromeProfileLock::NotificationCallback,
base::Unretained(this))));
lock_acquired_ = false;
}

bool ChromeProfileLock::HasAcquired() {
return lock_acquired_;
}

bool ChromeProfileLock::NotificationCallback(
const base::CommandLine& command_line,
const base::FilePath& current_directory) {
return false;
}
35 changes: 35 additions & 0 deletions browser/importer/chrome_profile_lock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_IMPORTER_CHROME_PROFILE_LOCK_H__
#define BRAVE_BROWSER_IMPORTER_CHROME_PROFILE_LOCK_H__

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "chrome/browser/process_singleton.h"

class ChromeProfileLock {
public:
explicit ChromeProfileLock(const base::FilePath& user_data_dir);
~ChromeProfileLock();

// Locks and releases the profile.
void Lock();
void Unlock();

// Returns true if we lock the profile successfully.
bool HasAcquired();

private:
bool lock_acquired_;
base::FilePath user_data_dir_;
std::unique_ptr<ProcessSingleton> process_singleton_;

bool NotificationCallback(const base::CommandLine& command_line,
const base::FilePath& current_directory);

DISALLOW_COPY_AND_ASSIGN(ChromeProfileLock);
};

#endif // BRAVE_BROWSER_IMPORTER_CHROME_PROFILE_LOCK_H__
110 changes: 110 additions & 0 deletions browser/importer/chrome_profile_lock_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "brave/browser/importer/chrome_profile_lock.h"

#if defined(OS_POSIX)
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

#include <memory>

#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/string_util.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "testing/gtest/include/gtest/gtest.h"

#if defined(OS_WIN)
const char kLockFile[] = "lockfile";
#endif

class ChromeProfileLockTest : public testing::Test {
public:
ChromeProfileLockTest ()
: task_runner_(base::MakeRefCounted<base::TestSimpleTaskRunner>()),
thread_task_runner_handle_override_(
base::ThreadTaskRunnerHandle::OverrideForTesting(task_runner_)) {}
~ChromeProfileLockTest () override {
task_runner_->RunUntilIdle();
}
protected:
void SetUp() override {
testing::Test::SetUp();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
user_data_path_ = temp_dir_.GetPath();
#if defined(OS_POSIX)
lock_file_path_ =
user_data_path_.Append(chrome::kSingletonLockFilename);
#elif defined(OS_WIN)
lock_file_path_ = user_data_path_.AppendASCII(kLockFile);
#else
#error unsupport platforms
#endif
}

void LockFileExists(bool expect);

base::ScopedTempDir temp_dir_;
base::FilePath user_data_path_;
base::FilePath lock_file_path_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ScopedClosureRunner thread_task_runner_handle_override_;
};

void ChromeProfileLockTest::LockFileExists(bool expect) {
#if defined(OS_POSIX)
struct stat statbuf;
if (expect) {
ASSERT_EQ(0, lstat(lock_file_path_.value().c_str(), &statbuf));
ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
char buf[PATH_MAX];
ssize_t len = readlink(lock_file_path_.value().c_str(), buf, PATH_MAX);
ASSERT_GT(len, 0);
} else {
ASSERT_GT(0, lstat(lock_file_path_.value().c_str(), &statbuf));
}
#elif defined(OS_WIN)
if (expect)
EXPECT_TRUE(base::PathExists(lock_file_path_));
else
EXPECT_FALSE(base::PathExists(lock_file_path_));
#endif
}

TEST_F(ChromeProfileLockTest, LockTest) {
ChromeProfileLock lock(user_data_path_);
ASSERT_TRUE(lock.HasAcquired());
lock.Unlock();
ASSERT_FALSE(lock.HasAcquired());
lock.Lock();
ASSERT_TRUE(lock.HasAcquired());
}

// Tests basic functionality and verifies that the lock file is deleted after
// use.
TEST_F(ChromeProfileLockTest, ProfileLock) {
std::unique_ptr<ChromeProfileLock> lock;
EXPECT_EQ(static_cast<ChromeProfileLock*>(NULL), lock.get());
LockFileExists(false);
lock.reset(new ChromeProfileLock(user_data_path_));
EXPECT_TRUE(lock->HasAcquired());
LockFileExists(true);
lock->Unlock();
EXPECT_FALSE(lock->HasAcquired());

lock->Lock();
EXPECT_TRUE(lock->HasAcquired());
LockFileExists(true);
lock->Lock();
EXPECT_TRUE(lock->HasAcquired());
lock->Unlock();
EXPECT_FALSE(lock->HasAcquired());
}
Loading

0 comments on commit 88525af

Please sign in to comment.