blob: 59175d7958605dfda6b3333c56891d9dafe35616 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/webstore_reinstaller.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/webstore_data_fetcher.h"
#include "chrome/browser/extensions/webstore_installer_test.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/extensions/webstore_install_result.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_features.h"
namespace extensions {
namespace {
constexpr char kWebstoreDomain[] = "cws.com";
constexpr char kAppDomain[] = "app.com";
constexpr char kNonAppDomain[] = "nonapp.com";
constexpr char kTestExtensionId[] = "ecglahbcnmdpdciemllbhojghbkagdje";
constexpr char kTestDataPath[] = "extensions/api_test/webstore_inline_install";
constexpr char kCrxFilename[] = "extension.crx";
// The values of the mock protobuf response should match those in the JSON API
// that's stored in "chrome/test/data/extensions/api_test/" +
// "webstore_inline_install/inlineinstall/detail/" +
// "ecglahbcnmdpdciemllbhojghbkagdje".
constexpr char kMockTitle[] = "Inline Install Test Extension";
constexpr char kMockUserCountString[] = "371,674";
constexpr double kMockAverageRating = 4.36;
constexpr int kMockRatingCount = 788;
constexpr char kMockRatingCountString[] = "788";
constexpr char kMockLogoUri[] = "webstore_inline_install/extension/icon.png";
constexpr char kMockManifest[] = R"({
"name": "Inline Install Test Extension",
"version": "0.1",
"manifest_version": 2,
"icons": {"128": "icon.png"},
"permissions":["tabs"]
})";
std::unique_ptr<FetchItemSnippetResponse> CreateMockResponse(
const ExtensionId& id) {
auto mock_response = std::make_unique<FetchItemSnippetResponse>();
mock_response->set_item_id(id);
mock_response->set_title(kMockTitle);
mock_response->set_manifest(kMockManifest);
mock_response->set_logo_uri(kMockLogoUri);
mock_response->set_user_count_string(kMockUserCountString);
mock_response->set_rating_count_string(kMockRatingCountString);
mock_response->set_rating_count(kMockRatingCount);
mock_response->set_average_rating(kMockAverageRating);
return mock_response;
}
} // namespace
class WebstoreReinstallerBrowserTest : public WebstoreInstallerTest {
public:
WebstoreReinstallerBrowserTest()
: WebstoreInstallerTest(
kWebstoreDomain,
kTestDataPath,
kCrxFilename,
kAppDomain,
kNonAppDomain) {}
~WebstoreReinstallerBrowserTest() override {}
void OnInstallCompletion(base::OnceClosure quit_closure,
bool success,
const std::string& error,
webstore_install::Result result);
bool last_install_result() const { return last_install_result_; }
private:
bool last_install_result_;
};
void WebstoreReinstallerBrowserTest::OnInstallCompletion(
base::OnceClosure quit_closure,
bool success,
const std::string& error,
webstore_install::Result result) {
last_install_result_ = success;
std::move(quit_closure).Run();
}
// TODO(crbug.com/325314721): Remove this test once we stop using the old item
// JSON API to fetch webstore data.
IN_PROC_BROWSER_TEST_F(WebstoreReinstallerBrowserTest, TestWebstoreReinstall) {
// Build an extension with the same id as our test extension and add it.
const std::string kExtensionName("ReinstallerExtension");
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetLocation(mojom::ManifestLocation::kInternal)
.SetID(kTestExtensionId)
.SetManifest(base::Value::Dict()
.Set("name", kExtensionName)
.Set("description", "Foo")
.Set("manifest_version", 2)
.Set("version", "1.0"))
.Build();
extension_service()->AddExtension(extension.get());
ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
ASSERT_TRUE(registry->enabled_extensions().GetByID(kTestExtensionId));
// WebstoreReinstaller expects corrupted extension.
extension_service()->DisableExtension(kTestExtensionId,
disable_reason::DISABLE_CORRUPTED);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
// Start by canceling the repair prompt.
AutoCancelInstall();
// Create and run a WebstoreReinstaller.
base::RunLoop run_loop;
auto reinstaller = base::MakeRefCounted<WebstoreReinstaller>(
active_web_contents, kTestExtensionId,
base::BindOnce(&WebstoreReinstallerBrowserTest::OnInstallCompletion,
base::Unretained(this), run_loop.QuitClosure()));
reinstaller->BeginReinstall();
run_loop.Run();
// We should have failed, and the old extension should still be present.
EXPECT_FALSE(last_install_result());
extension = registry->disabled_extensions().GetByID(kTestExtensionId);
ASSERT_TRUE(extension.get());
EXPECT_EQ(kExtensionName, extension->name());
// Now accept the repair prompt.
AutoAcceptInstall();
base::RunLoop run_loop2;
reinstaller = base::MakeRefCounted<WebstoreReinstaller>(
active_web_contents, kTestExtensionId,
base::BindOnce(&WebstoreReinstallerBrowserTest::OnInstallCompletion,
base::Unretained(this), run_loop2.QuitClosure()));
reinstaller->BeginReinstall();
run_loop2.Run();
// The reinstall should have succeeded, and the extension should have been
// "updated" (which in this case means that it should have been replaced with
// the inline install test extension, since that's the id we used).
EXPECT_TRUE(last_install_result());
extension = registry->enabled_extensions().GetByID(kTestExtensionId);
ASSERT_TRUE(extension.get());
// The name should not match, since the extension changed.
EXPECT_NE(kExtensionName, extension->name());
}
class WebstoreReinstallerItemSnippetsBrowserTest
: public WebstoreReinstallerBrowserTest {
public:
WebstoreReinstallerItemSnippetsBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(
extensions_features::kUseItemSnippetsAPI);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// A version of the WebstoreReinstallerBrowserTest test with the same name
// except this tests that reinstalls for corrupted extensions work when using
// the new item snippets API which returns a protobuf object for web store data
// (the API return is mocked for this test).
IN_PROC_BROWSER_TEST_F(WebstoreReinstallerItemSnippetsBrowserTest,
TestWebstoreReinstall) {
// Build an extension with the same id as our test extension and add it.
const std::string kExtensionName("ReinstallerExtension");
scoped_refptr<const Extension> extension =
ExtensionBuilder()
.SetLocation(mojom::ManifestLocation::kInternal)
.SetID(kTestExtensionId)
.SetManifest(base::Value::Dict()
.Set("name", kExtensionName)
.Set("description", "Foo")
.Set("manifest_version", 2)
.Set("version", "1.0"))
.Build();
extension_service()->AddExtension(extension.get());
auto mock_response = CreateMockResponse(kTestExtensionId);
WebstoreDataFetcher::SetMockItemSnippetReponseForTesting(mock_response.get());
ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
ASSERT_TRUE(registry->enabled_extensions().GetByID(kTestExtensionId));
// WebstoreReinstaller expects corrupted extension.
extension_service()->DisableExtension(kTestExtensionId,
disable_reason::DISABLE_CORRUPTED);
content::WebContents* active_web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(active_web_contents);
// Start by canceling the repair prompt.
AutoCancelInstall();
// Create and run a WebstoreReinstaller.
base::RunLoop run_loop;
auto reinstaller = base::MakeRefCounted<WebstoreReinstaller>(
active_web_contents, kTestExtensionId,
base::BindOnce(&WebstoreReinstallerBrowserTest::OnInstallCompletion,
base::Unretained(this), run_loop.QuitClosure()));
reinstaller->BeginReinstall();
run_loop.Run();
// We should have failed, and the old extension should still be present.
EXPECT_FALSE(last_install_result());
extension = registry->disabled_extensions().GetByID(kTestExtensionId);
ASSERT_TRUE(extension.get());
EXPECT_EQ(kExtensionName, extension->name());
// Now accept the repair prompt.
AutoAcceptInstall();
base::RunLoop run_loop2;
reinstaller = base::MakeRefCounted<WebstoreReinstaller>(
active_web_contents, kTestExtensionId,
base::BindOnce(&WebstoreReinstallerBrowserTest::OnInstallCompletion,
base::Unretained(this), run_loop2.QuitClosure()));
reinstaller->BeginReinstall();
run_loop2.Run();
// The reinstall should have succeeded, and the extension should have been
// "updated" (which in this case means that it should have been replaced with
// the inline install test extension, since that's the id we used).
EXPECT_TRUE(last_install_result());
extension = registry->enabled_extensions().GetByID(kTestExtensionId);
ASSERT_TRUE(extension.get());
// The name should not match, since the extension changed.
EXPECT_NE(kExtensionName, extension->name());
}
} // namespace extensions