blob: c775443e1c12da7f9b23e48af1fc00587ad76e54 [file] [log] [blame]
// Copyright 2020 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/ui/extensions/extension_installed_bubble_model.h"
#include "base/command_line.h"
#include "base/memory/raw_ptr.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/load_error_reporter.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/common/extensions/api/omnibox.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/api/extension_action/action_info.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/manifest_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/accelerators/accelerator.h"
using extensions::Extension;
class ExtensionInstalledBubbleModelTest : public BrowserWithTestWindowTest {
public:
ExtensionInstalledBubbleModelTest() = default;
~ExtensionInstalledBubbleModelTest() override = default;
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
extensions::LoadErrorReporter::Init(false);
extensions::TestExtensionSystem* extension_system =
static_cast<extensions::TestExtensionSystem*>(
extensions::ExtensionSystem::Get(profile()));
extension_system->CreateExtensionService(
base::CommandLine::ForCurrentProcess(), base::FilePath(), false);
extension_service_ =
extensions::ExtensionSystem::Get(profile())->extension_service();
}
void AddOmniboxKeyword(extensions::ExtensionBuilder* builder,
const std::string& keyword) {
using ManifestKeys = extensions::api::omnibox::ManifestKeys;
base::Value::Dict info;
info.Set(ManifestKeys::Omnibox::kKeyword, keyword);
builder->SetManifestKey(ManifestKeys::kOmnibox, std::move(info));
}
void AddRegularAction(extensions::ExtensionBuilder* builder) {
builder->SetManifestKey(extensions::manifest_keys::kAction,
base::Value::Dict());
}
void AddBrowserActionKeyBinding(extensions::ExtensionBuilder* builder,
const std::string& key) {
base::Value::Dict command;
command.Set("suggested_key", key);
command.Set("description", "Invoke the page action");
base::Value::Dict commands;
commands.Set(extensions::manifest_values::kBrowserActionCommandEvent,
base::Value(std::move(command)));
builder->SetManifestKey(extensions::manifest_keys::kCommands,
std::make_unique<base::Value>(std::move(commands)));
}
extensions::ExtensionService* extension_service() {
return extension_service_;
}
const SkBitmap empty_icon_;
private:
raw_ptr<extensions::ExtensionService> extension_service_ = nullptr;
};
TEST_F(ExtensionInstalledBubbleModelTest, SyntheticPageActionExtension) {
// An extension with no action info in the manifest at all gets a synthesized
// page action.
auto extension = extensions::ExtensionBuilder("Foo").Build();
extension_service()->AddExtension(extension.get());
ExtensionInstalledBubbleModel model(browser()->profile(), extension.get(),
empty_icon_);
// It should anchor to the synthesized action...
EXPECT_TRUE(model.anchor_to_action());
EXPECT_FALSE(model.anchor_to_omnibox());
// ... but there should not be how-to-use text, since it has no actual way to
// activate it other than that synthesized action.
EXPECT_FALSE(model.show_how_to_use());
EXPECT_TRUE(model.show_how_to_manage());
EXPECT_FALSE(model.show_key_binding());
}
TEST_F(ExtensionInstalledBubbleModelTest, OmniboxExtension) {
// An extension with a regular action and an omnibox keyword...
auto builder = extensions::ExtensionBuilder("Foo");
AddOmniboxKeyword(&builder, "fookey");
builder.AddFlags(extensions::Extension::WAS_INSTALLED_BY_DEFAULT);
auto extension = builder.Build();
extension_service()->AddExtension(extension.get());
ExtensionInstalledBubbleModel model(browser()->profile(), extension.get(),
empty_icon_);
// ... should be anchored to the omnibox, not to the action ...
EXPECT_FALSE(model.anchor_to_action());
EXPECT_TRUE(model.anchor_to_omnibox());
// ... and should have how-to-use and how-to-manage text, but not show a key
// binding, since it doesn't have one.
EXPECT_TRUE(model.show_how_to_use());
EXPECT_TRUE(model.show_how_to_manage());
EXPECT_FALSE(model.show_key_binding());
}
TEST_F(ExtensionInstalledBubbleModelTest, PageActionExtension) {
// An extension with a page action...
auto extension = extensions::ExtensionBuilder("Foo")
.SetAction(extensions::ActionInfo::TYPE_PAGE)
.Build();
extension_service()->AddExtension(extension.get());
ExtensionInstalledBubbleModel model(browser()->profile(), extension.get(),
empty_icon_);
// should anchor to that action
EXPECT_TRUE(model.anchor_to_action());
EXPECT_FALSE(model.anchor_to_omnibox());
// and have how-to-use and how-to-manage but no key binding, since it doesn't
// have one.
EXPECT_TRUE(model.show_how_to_use());
EXPECT_TRUE(model.show_how_to_manage());
EXPECT_FALSE(model.show_key_binding());
}
TEST_F(ExtensionInstalledBubbleModelTest, ExtensionWithKeyBinding) {
// An extension with a browser action and a key binding...
auto builder = extensions::ExtensionBuilder("Foo");
builder.SetAction(extensions::ActionInfo::TYPE_BROWSER);
AddBrowserActionKeyBinding(&builder, "Alt+Shift+E");
auto extension = builder.Build();
// Note that we have to OnExtensionInstalled() here rather than just adding it
// - hotkeys are picked up at install time, not add time.
extension_service()->OnExtensionInstalled(extension.get(),
syncer::StringOrdinal());
ExtensionInstalledBubbleModel model(browser()->profile(), extension.get(),
empty_icon_);
// Should have a how-to-use that lists the key, but *not* a how-to-manage,
// since it crowds the UI.
EXPECT_TRUE(model.show_how_to_use());
EXPECT_FALSE(model.show_how_to_manage());
EXPECT_TRUE(model.show_key_binding());
// Note that we can't just check for "Alt+Shift+E" in
// model.GetHowToUseText(), since on Mac, modifier keys are represented by
// special sigils rather than their textual names.
ui::Accelerator accelerator(ui::VKEY_E, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
EXPECT_NE(std::u16string::npos,
model.GetHowToUseText().find(accelerator.GetShortcutText()));
}
TEST_F(ExtensionInstalledBubbleModelTest, OmniboxKeywordAndSyntheticAction) {
auto builder = extensions::ExtensionBuilder("Foo");
AddOmniboxKeyword(&builder, "fookey");
auto extension = builder.Build();
extension_service()->AddExtension(extension.get());
ExtensionInstalledBubbleModel model(browser()->profile(), extension.get(),
empty_icon_);
// This extension has a synthesized action and an omnibox keyword. It should
// have how-to-use text, and be anchored to its (synthesized) page action.
EXPECT_TRUE(model.show_how_to_use());
EXPECT_TRUE(model.anchor_to_action());
}
// TODO(ellyjones): Add a test for a syncable extension with a sync-eligible
// profile, to test model.show_sign_in_promo(). Reference
// ExtensionServiceSyncTest for an example.