Commit 82800b5b authored by Giovanni Ortuño Urquidi's avatar Giovanni Ortuño Urquidi Committed by Commit Bot

desktop-pwas: Add extension creation subtask to installation task

Introduces a BookmarkAppInstaller class whose job is to create
a Bookmark App with certain characteristics e.g. name, start url,
launch container, etc.

The BookmarkAppInstaller class uses CrxInstaller to actually
create and install the BookmarkApp. Currently the installer only
takes a WebApplicationInfo but in the future it'll support more
options, for example, if the app will be synced, what container
will the app launch in, etc.

Marks BookmarkAppInstallerTest as a friend in CrxInstaller so that
we can create a fake CrxInstaller that fails to install the app.

Bug: 864904
Change-Id: I105d1e93160f2527ee6cb5c4951fab885a893392
Reviewed-on: https://chromium-review.googlesource.com/1152642
Commit-Queue: Giovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580471}
parent b81718bb
......@@ -7,6 +7,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
......@@ -248,6 +249,7 @@ class CrxInstaller : public SandboxedUnpackerClient {
private:
friend class ::ExtensionServiceTest;
friend class ExtensionUpdaterTest;
friend class BookmarkAppInstallerTest;
CrxInstaller(base::WeakPtr<ExtensionService> service_weak,
std::unique_ptr<ExtensionInstallPrompt> client,
......
......@@ -12,6 +12,8 @@ source_set("extensions") {
"bookmark_app_data_retriever.h",
"bookmark_app_installation_task.cc",
"bookmark_app_installation_task.h",
"bookmark_app_installer.cc",
"bookmark_app_installer.h",
"bookmark_app_shortcut_installation_task.cc",
"bookmark_app_shortcut_installation_task.h",
"pending_bookmark_app_manager.cc",
......@@ -40,6 +42,7 @@ source_set("unit_tests") {
sources = [
"bookmark_app_data_retriever_unittest.cc",
"bookmark_app_installation_task_unittest.cc",
"bookmark_app_installer_unittest.cc",
]
deps = [
......
......@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_data_retriever.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_installer.h"
namespace extensions {
......@@ -20,7 +21,13 @@ void BookmarkAppInstallationTask::SetDataRetrieverForTesting(
data_retriever_ = std::move(data_retriever);
}
BookmarkAppInstallationTask::BookmarkAppInstallationTask()
: data_retriever_(std::make_unique<BookmarkAppDataRetriever>()) {}
void BookmarkAppInstallationTask::SetInstallerForTesting(
std::unique_ptr<BookmarkAppInstaller> installer) {
installer_ = std::move(installer);
}
BookmarkAppInstallationTask::BookmarkAppInstallationTask(Profile* profile)
: data_retriever_(std::make_unique<BookmarkAppDataRetriever>()),
installer_(std::make_unique<BookmarkAppInstaller>(profile)) {}
} // namespace extensions
......@@ -10,9 +10,12 @@
#include "base/callback_forward.h"
#include "base/macros.h"
class Profile;
namespace extensions {
class BookmarkAppDataRetriever;
class BookmarkAppInstaller;
// Class to install a BookmarkApp-based Shortcut or WebApp from a WebContents
// or WebApplicationInfo. Can only be called from the UI thread.
......@@ -21,6 +24,7 @@ class BookmarkAppInstallationTask {
enum class Result {
kSuccess,
kGetWebApplicationInfoFailed,
kInstallationFailed,
};
using ResultCallback = base::OnceCallback<void(Result)>;
......@@ -29,14 +33,17 @@ class BookmarkAppInstallationTask {
void SetDataRetrieverForTesting(
std::unique_ptr<BookmarkAppDataRetriever> data_retriever);
void SetInstallerForTesting(std::unique_ptr<BookmarkAppInstaller> installer);
protected:
BookmarkAppInstallationTask();
explicit BookmarkAppInstallationTask(Profile* profile);
BookmarkAppDataRetriever& data_retriever() { return *data_retriever_; }
BookmarkAppInstaller& installer() { return *installer_; }
private:
std::unique_ptr<BookmarkAppDataRetriever> data_retriever_;
std::unique_ptr<BookmarkAppInstaller> installer_;
DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallationTask);
};
......
......@@ -10,16 +10,18 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/message_loop/message_loop.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_data_retriever.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_installer.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_shortcut_installation_task.h"
#include "chrome/common/web_application_info.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/manifest/manifest.h"
......@@ -44,6 +46,17 @@ class BookmarkAppInstallationTaskTest : public ChromeRenderViewHostTestHarness {
std::move(quit_closure).Run();
}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
// CrxInstaller in BookmarkAppInstaller needs an ExtensionService, so
// create one for the profile.
TestExtensionSystem* test_system =
static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()));
test_system->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
profile()->GetPath(),
false /* autoupdate_enabled */);
}
protected:
bool app_installed() {
return app_installation_result_.value() ==
......@@ -91,29 +104,52 @@ class TestDataRetriever : public BookmarkAppDataRetriever {
DISALLOW_COPY_AND_ASSIGN(TestDataRetriever);
};
class TestInstaller : public BookmarkAppInstaller {
public:
explicit TestInstaller(Profile* profile, bool succeeds)
: BookmarkAppInstaller(profile), succeeds_(succeeds) {}
~TestInstaller() override = default;
void Install(const WebApplicationInfo& web_app_info,
ResultCallback callback) override {
web_app_info_ = web_app_info;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), succeeds_));
}
const WebApplicationInfo& web_app_info() { return web_app_info_.value(); }
private:
bool succeeds_;
base::Optional<WebApplicationInfo> web_app_info_;
DISALLOW_COPY_AND_ASSIGN(TestInstaller);
};
TEST_F(BookmarkAppInstallationTaskTest, ShortcutFromContents_Delete) {
auto installer = std::make_unique<BookmarkAppShortcutInstallationTask>();
installer->SetDataRetrieverForTesting(
auto task = std::make_unique<BookmarkAppShortcutInstallationTask>(profile());
task->SetDataRetrieverForTesting(
std::make_unique<TestDataRetriever>(nullptr));
base::RunLoop run_loop;
installer->InstallFromWebContents(
task->InstallFromWebContents(
web_contents(),
base::BindOnce(&BookmarkAppInstallationTaskTest::OnInstallationTaskResult,
base::Unretained(this), run_loop.QuitClosure()));
installer.reset();
task.reset();
run_loop.RunUntilIdle();
// Shouldn't crash.
}
TEST_F(BookmarkAppInstallationTaskTest, ShortcutFromContents_NoWebAppInfo) {
auto installer = std::make_unique<BookmarkAppShortcutInstallationTask>();
installer->SetDataRetrieverForTesting(
auto task = std::make_unique<BookmarkAppShortcutInstallationTask>(profile());
task->SetDataRetrieverForTesting(
std::make_unique<TestDataRetriever>(nullptr));
base::RunLoop run_loop;
installer->InstallFromWebContents(
task->InstallFromWebContents(
web_contents(),
base::BindOnce(&BookmarkAppInstallationTaskTest::OnInstallationTaskResult,
base::Unretained(this), run_loop.QuitClosure()));
......@@ -125,23 +161,55 @@ TEST_F(BookmarkAppInstallationTaskTest, ShortcutFromContents_NoWebAppInfo) {
}
TEST_F(BookmarkAppInstallationTaskTest, ShortcutFromContents_NoManifest) {
auto installer = std::make_unique<BookmarkAppShortcutInstallationTask>();
auto task = std::make_unique<BookmarkAppShortcutInstallationTask>(profile());
WebApplicationInfo info;
info.app_url = GURL(kWebAppUrl);
info.title = base::UTF8ToUTF16(kWebAppTitle);
installer->SetDataRetrieverForTesting(std::make_unique<TestDataRetriever>(
task->SetDataRetrieverForTesting(std::make_unique<TestDataRetriever>(
std::make_unique<WebApplicationInfo>(std::move(info))));
auto installer =
std::make_unique<TestInstaller>(profile(), true /* succeeds */);
auto* installer_ptr = installer.get();
task->SetInstallerForTesting(std::move(installer));
base::RunLoop run_loop;
installer->InstallFromWebContents(
task->InstallFromWebContents(
web_contents(),
base::BindOnce(&BookmarkAppInstallationTaskTest::OnInstallationTaskResult,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
EXPECT_TRUE(app_installed());
// TODO(crbug.com/864904): Test that the right app got installed.
const auto& installed_info = installer_ptr->web_app_info();
EXPECT_EQ(info.app_url, installed_info.app_url);
EXPECT_EQ(info.title, installed_info.title);
}
TEST_F(BookmarkAppInstallationTaskTest,
ShortcutFromContents_InstallationFails) {
auto task = std::make_unique<BookmarkAppShortcutInstallationTask>(profile());
WebApplicationInfo info;
info.app_url = GURL(kWebAppUrl);
info.title = base::UTF8ToUTF16(kWebAppTitle);
task->SetDataRetrieverForTesting(std::make_unique<TestDataRetriever>(
std::make_unique<WebApplicationInfo>(std::move(info))));
task->SetInstallerForTesting(
std::make_unique<TestInstaller>(profile(), false /* succeeds */));
base::RunLoop run_loop;
task->InstallFromWebContents(
web_contents(),
base::BindOnce(&BookmarkAppInstallationTaskTest::OnInstallationTaskResult,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
EXPECT_FALSE(app_installed());
EXPECT_EQ(BookmarkAppInstallationTask::Result::kInstallationFailed,
app_installation_result());
}
} // namespace extensions
// 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/web_applications/extensions/bookmark_app_installer.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/web_application_info.h"
namespace extensions {
BookmarkAppInstaller::BookmarkAppInstaller(Profile* profile)
: crx_installer_(CrxInstaller::CreateSilent(
ExtensionSystem::Get(profile)->extension_service())) {}
BookmarkAppInstaller::~BookmarkAppInstaller() = default;
void BookmarkAppInstaller::Install(const WebApplicationInfo& web_app_info,
ResultCallback callback) {
crx_installer_->set_installer_callback(
base::BindOnce(&BookmarkAppInstaller::OnInstall,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
crx_installer_->InstallWebApp(web_app_info);
}
void BookmarkAppInstaller::SetCrxInstallerForTesting(
scoped_refptr<CrxInstaller> crx_installer) {
crx_installer_ = crx_installer;
}
void BookmarkAppInstaller::OnInstall(
ResultCallback callback,
const base::Optional<CrxInstallError>& error) {
// TODO(crbug.com/864904): Finish the installation i.e. set launch container.
const bool installation_succeeded = !error;
std::move(callback).Run(installation_succeeded);
}
} // namespace extensions
// 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_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALLER_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALLER_H_
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
class Profile;
struct WebApplicationInfo;
namespace extensions {
class CrxInstaller;
class CrxInstallError;
// Class used by BookmarkAppInstallationTask to actually install the Bookmark
// App in the system.
class BookmarkAppInstaller {
public:
using ResultCallback = base::OnceCallback<void(bool)>;
// Constructs a BookmarkAppInstaller that will install the Bookmark App in
// |profile|.
explicit BookmarkAppInstaller(Profile* profile);
virtual ~BookmarkAppInstaller();
// TODO(crbug.com/864904): This should take more options e.g. what container
// to launch the app in, should the app sync, etc.
virtual void Install(const WebApplicationInfo& web_app_info,
ResultCallback callback);
void SetCrxInstallerForTesting(scoped_refptr<CrxInstaller> crx_installer);
private:
void OnInstall(ResultCallback callback,
const base::Optional<CrxInstallError>& error);
scoped_refptr<CrxInstaller> crx_installer_;
// We need a WeakPtr because CrxInstaller is refcounted and it can run its
// callback after this class has been destroyed.
base::WeakPtrFactory<BookmarkAppInstaller> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstaller);
};
} // namespace extensions
#endif // CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALLER_H_
// 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/web_applications/extensions/bookmark_app_installer.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/common/web_application_info.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "extensions/browser/install/crx_install_error.h"
namespace extensions {
namespace {
const char kWebAppUrl[] = "https://foo.example";
const char kWebAppTitle[] = "Foo Title";
} // namespace
class BookmarkAppInstallerTest : public ChromeRenderViewHostTestHarness {
public:
// Subclass that runs a closure when an extension is unpacked successfully.
// Useful for tests that want to trigger their own succeess/failure events.
class FakeCrxInstaller : public CrxInstaller {
public:
explicit FakeCrxInstaller(Profile* profile)
: CrxInstaller(
ExtensionSystem::Get(profile)->extension_service()->AsWeakPtr(),
nullptr,
nullptr) {
}
void OnUnpackSuccess(
const base::FilePath& temp_dir,
const base::FilePath& extension_dir,
std::unique_ptr<base::DictionaryValue> original_manifest,
const Extension* extension,
const SkBitmap& install_icon,
const base::Optional<int>& dnr_ruleset_checksum) override {
run_loop_.QuitClosure().Run();
}
void WaitForInstallToTrigger() { run_loop_.Run(); }
void SimulateInstallFailed() {
CrxInstallError error(CrxInstallErrorType::DECLINED,
CrxInstallErrorDetail::INSTALL_NOT_ENABLED,
base::ASCIIToUTF16(""));
NotifyCrxInstallComplete(error);
}
private:
~FakeCrxInstaller() override = default;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(FakeCrxInstaller);
};
BookmarkAppInstallerTest() = default;
~BookmarkAppInstallerTest() override = default;
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
// CrxInstaller in BookmarkAppInstaller needs an ExtensionService, so
// create one for the profile.
TestExtensionSystem* test_system =
static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()));
test_system->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
profile()->GetPath(),
false /* autoupdate_enabled */);
}
void InstallCallback(base::OnceClosure quit_closure, bool app_installed) {
app_installed_ = app_installed;
std::move(quit_closure).Run();
}
bool app_installed() { return app_installed_.value(); }
bool install_callback_called() { return app_installed_.has_value(); }
private:
base::Optional<bool> app_installed_;
DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallerTest);
};
TEST_F(BookmarkAppInstallerTest, BasicInstallSucceeds) {
BookmarkAppInstaller installer(profile());
WebApplicationInfo info;
info.app_url = GURL(kWebAppUrl);
info.title = base::ASCIIToUTF16(kWebAppTitle);
base::RunLoop run_loop;
installer.Install(
info, base::BindOnce(&BookmarkAppInstallerTest::InstallCallback,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
EXPECT_TRUE(app_installed());
}
TEST_F(BookmarkAppInstallerTest, BasicInstallFails) {
BookmarkAppInstaller installer(profile());
auto fake_crx_installer =
base::MakeRefCounted<BookmarkAppInstallerTest::FakeCrxInstaller>(
profile());
installer.SetCrxInstallerForTesting(fake_crx_installer);
base::RunLoop run_loop;
WebApplicationInfo info;
info.app_url = GURL(kWebAppUrl);
info.title = base::ASCIIToUTF16(kWebAppTitle);
installer.Install(
info, base::BindOnce(&BookmarkAppInstallerTest::InstallCallback,
base::Unretained(this), run_loop.QuitClosure()));
fake_crx_installer->WaitForInstallToTrigger();
fake_crx_installer->SimulateInstallFailed();
run_loop.Run();
EXPECT_FALSE(app_installed());
}
} // namespace extensions
......@@ -11,13 +11,15 @@
#include "base/bind.h"
#include "base/callback.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_data_retriever.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_installer.h"
#include "chrome/common/web_application_info.h"
#include "content/public/browser/browser_thread.h"
namespace extensions {
BookmarkAppShortcutInstallationTask::BookmarkAppShortcutInstallationTask() =
default;
BookmarkAppShortcutInstallationTask::BookmarkAppShortcutInstallationTask(
Profile* profile)
: BookmarkAppInstallationTask(profile) {}
BookmarkAppShortcutInstallationTask::~BookmarkAppShortcutInstallationTask() =
default;
......@@ -53,15 +55,30 @@ void BookmarkAppShortcutInstallationTask::OnGetWebApplicationInfo(
data_retriever().GetIcons(
web_app_info->app_url, icon_urls,
base::BindOnce(&BookmarkAppShortcutInstallationTask::OnGetIcons,
weak_ptr_factory_.GetWeakPtr(),
std::move(result_callback)));
weak_ptr_factory_.GetWeakPtr(), std::move(result_callback),
std::move(web_app_info)));
}
void BookmarkAppShortcutInstallationTask::OnGetIcons(
ResultCallback result_callback,
std::unique_ptr<WebApplicationInfo> web_app_info,
std::vector<WebApplicationInfo::IconInfo> icons) {
// TODO(crbug.com/864904): Continue the installation process.
std::move(result_callback).Run(Result::kSuccess);
web_app_info->icons = std::move(icons);
// TODO(crbug.com/864904): Make this a WebContents observer and cancel the
// task if the WebContents has been destroyed.
installer().Install(
*web_app_info,
base::BindOnce(&BookmarkAppShortcutInstallationTask::OnInstalled,
weak_ptr_factory_.GetWeakPtr(),
std::move(result_callback)));
}
void BookmarkAppShortcutInstallationTask::OnInstalled(
ResultCallback result_callback,
bool success) {
std::move(result_callback)
.Run(success ? Result::kSuccess : Result::kInstallationFailed);
}
} // namespace extensions
......@@ -13,6 +13,8 @@
#include "chrome/browser/web_applications/extensions/bookmark_app_installation_task.h"
#include "chrome/common/web_application_info.h"
class Profile;
namespace content {
class WebContents;
}
......@@ -20,10 +22,12 @@ class WebContents;
namespace extensions {
// Subclass of BookmarkAppInstallationTask that exclusively installs
// BookmarkApp-based Shortcuts.
// BookmarkApp-based shortcuts.
class BookmarkAppShortcutInstallationTask : public BookmarkAppInstallationTask {
public:
BookmarkAppShortcutInstallationTask();
// Constructs a task that will install a BookmarkApp-based shortcut for
// |profile|.
explicit BookmarkAppShortcutInstallationTask(Profile* profile);
~BookmarkAppShortcutInstallationTask() override;
void InstallFromWebContents(content::WebContents* web_contents,
......@@ -34,7 +38,9 @@ class BookmarkAppShortcutInstallationTask : public BookmarkAppInstallationTask {
ResultCallback result_callback,
std::unique_ptr<WebApplicationInfo> web_app_info);
void OnGetIcons(ResultCallback result_callback,
std::unique_ptr<WebApplicationInfo> web_app_info,
std::vector<WebApplicationInfo::IconInfo> icons);
void OnInstalled(ResultCallback result_callback, bool success);
base::WeakPtrFactory<BookmarkAppShortcutInstallationTask> weak_ptr_factory_{
this};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment