blob: 25ebb863c410d9792068d0dd9192cd57c57b1e3f [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "extensions/common/api/commands/commands_handler.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_test.h"
#include "extensions/common/warnings_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/accelerators/command.h"
namespace extensions {
namespace errors = manifest_errors;
using CommandsManifestTest = ManifestTest;
#if BUILDFLAG(IS_MAC)
constexpr int kControlKey = ui::EF_COMMAND_DOWN;
#else
constexpr int kControlKey = ui::EF_CONTROL_DOWN;
#endif
TEST_F(CommandsManifestTest, CommandManifestParseCommandsBrowserAction) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Browser Action",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
},
"_execute_browser_action": {
"suggested_key": "Alt+Shift+F",
"description": "browser action"
}
},
"browser_action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const ui::CommandMap* commands =
CommandsInfo::GetNamedCommands(extension.get());
ASSERT_TRUE(commands);
EXPECT_EQ(1u, commands->size());
auto iter = commands->begin();
const ui::Command* named_command = &(*iter).second;
EXPECT_EQ("feature1", named_command->command_name());
EXPECT_EQ(u"desc", named_command->description());
const ui::Accelerator ctrl_shift_f =
ui::Accelerator(ui::VKEY_F, kControlKey | ui::EF_SHIFT_DOWN);
EXPECT_EQ(ctrl_shift_f, named_command->accelerator());
const Command* browser_action =
CommandsInfo::GetBrowserActionCommand(extension.get());
ASSERT_TRUE(browser_action);
EXPECT_EQ("_execute_browser_action", browser_action->command_name());
EXPECT_EQ(u"", browser_action->description());
const ui::Accelerator alt_shift_f =
ui::Accelerator(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_EQ(alt_shift_f, browser_action->accelerator());
EXPECT_FALSE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
TEST_F(CommandsManifestTest, CommandManifestParseCommandsPageAction) {
static constexpr char kManifest[] =
R"({
"name": "Command test - page Action",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
},
"_execute_page_action": {
"suggested_key": "Ctrl+F",
"description": ""
}
},
"page_action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const ui::CommandMap* commands =
CommandsInfo::GetNamedCommands(extension.get());
ASSERT_TRUE(commands);
EXPECT_EQ(1u, commands->size());
auto iter = commands->begin();
const ui::Command* named_command = &(*iter).second;
EXPECT_EQ("feature1", named_command->command_name());
EXPECT_EQ(u"desc", named_command->description());
const Command* page_action =
CommandsInfo::GetPageActionCommand(extension.get());
ASSERT_TRUE(page_action);
EXPECT_EQ("_execute_page_action", page_action->command_name());
EXPECT_EQ(u"", page_action->description());
const ui::Accelerator ctrl_f = ui::Accelerator(ui::VKEY_F, kControlKey);
EXPECT_EQ(ctrl_f, page_action->accelerator());
EXPECT_FALSE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
TEST_F(CommandsManifestTest, CommandManifestParseCommandsAction) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Action",
"manifest_version": 3,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
},
"_execute_action": {
"suggested_key": "Ctrl+G",
"description": ""
}
},
"action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const ui::CommandMap* commands =
CommandsInfo::GetNamedCommands(extension.get());
ASSERT_TRUE(commands);
EXPECT_EQ(1u, commands->size());
auto iter = commands->begin();
const ui::Command* named_command = &(*iter).second;
EXPECT_EQ("feature1", named_command->command_name());
EXPECT_EQ(u"desc", named_command->description());
const Command* action = CommandsInfo::GetActionCommand(extension.get());
ASSERT_TRUE(action);
EXPECT_EQ("_execute_action", action->command_name());
EXPECT_EQ(u"", action->description());
const ui::Accelerator ctrl_g = ui::Accelerator(ui::VKEY_G, kControlKey);
EXPECT_EQ(ctrl_g, action->accelerator());
EXPECT_FALSE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
// Tests that when only a custom action command is specified we create a
// default action command for the action type for MV2.
TEST_F(CommandsManifestTest,
CommandManifestParseCommandsOnlyCustomCommandGetsDefault_MV2) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Only Custom Commands",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
}
},
"browser_action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const ui::CommandMap* commands =
CommandsInfo::GetNamedCommands(extension.get());
ASSERT_TRUE(commands);
EXPECT_EQ(1u, commands->size());
auto iter = commands->begin();
const ui::Command* named_command = &(*iter).second;
EXPECT_EQ("feature1", named_command->command_name());
EXPECT_EQ(u"desc", named_command->description());
const Command* browser_action =
CommandsInfo::GetBrowserActionCommand(extension.get());
ASSERT_TRUE(browser_action);
EXPECT_EQ("",
browser_action->AcceleratorToString(browser_action->accelerator()));
EXPECT_FALSE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
// Tests that when only a custom action command is specified we create a
// default action command for the action type for MV3.
TEST_F(CommandsManifestTest,
CommandManifestParseCommandsOnlyCustomCommandGetsDefault_MV3) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Only Custom Commands - MV3",
"manifest_version": 3,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
}
},
"action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const ui::CommandMap* commands =
CommandsInfo::GetNamedCommands(extension.get());
ASSERT_TRUE(commands);
EXPECT_EQ(1u, commands->size());
auto iter = commands->begin();
const ui::Command* named_command = &(*iter).second;
EXPECT_EQ("feature1", named_command->command_name());
EXPECT_EQ(u"desc", named_command->description());
const Command* action = CommandsInfo::GetActionCommand(extension.get());
ASSERT_TRUE(action);
EXPECT_EQ("", action->AcceleratorToString(action->accelerator()));
EXPECT_FALSE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
// Tests that only the correct action command (_execute_browser_action) is
// used from the manifest for MV2, but others are ignored and we install a
// warning for the incorrect command. See https://crbug.com/1353210.
TEST_F(CommandsManifestTest,
CommandManifestIgnoreInvalidActionCommandsAndInstallWarning_MV2) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Multiple Action Commands",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
},
"_execute_browser_action": {
"suggested_key": "Alt+Shift+F",
"description": "browser action"
},
"_execute_page_action": {
"suggested_key": "Ctrl+F",
"description": ""
}
},
"browser_action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const Command* browser_action =
CommandsInfo::GetBrowserActionCommand(extension.get());
ASSERT_TRUE(browser_action);
EXPECT_EQ("_execute_browser_action", browser_action->command_name());
EXPECT_EQ(u"", browser_action->description());
const ui::Accelerator alt_shift_f =
ui::Accelerator(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_EQ(alt_shift_f, browser_action->accelerator());
EXPECT_FALSE(CommandsInfo::GetPageActionCommand(extension.get()));
EXPECT_FALSE(CommandsInfo::GetActionCommand(extension.get()));
EXPECT_TRUE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
// Tests that only the correct action command (_execute_action) is used
// from the manifest for MV3, but others are ignored and we install a warning
// for the incorrect command. See https://crbug.com/1353210.
TEST_F(CommandsManifestTest,
CommandManifestIgnoreInvalidActionCommandsAndInstallWarning_MV3) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Multiple Action Commands - MV3",
"manifest_version": 3,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
},
"_execute_action": {
"suggested_key": "Alt+Shift+F",
"description": "browser action"
},
"_execute_page_action": {
"suggested_key": "Ctrl+F",
"description": ""
}
},
"action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const Command* action = CommandsInfo::GetActionCommand(extension.get());
ASSERT_TRUE(action);
EXPECT_EQ("_execute_action", action->command_name());
EXPECT_EQ(u"", action->description());
const ui::Accelerator alt_shift_f =
ui::Accelerator(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_EQ(alt_shift_f, action->accelerator());
EXPECT_FALSE(CommandsInfo::GetBrowserActionCommand(extension.get()));
EXPECT_FALSE(CommandsInfo::GetPageActionCommand(extension.get()));
EXPECT_TRUE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
// Tests that when only incorrect action commands are specified we install
// a warning and set a default (for MV2). See https://crbug.com/1353210.
TEST_F(CommandsManifestTest,
CommandManifestAllInvalidActionCommandsInstallWarning_MV2) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Multiple Action Commands But None Correct",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
},
"_execute_action": {
"suggested_key": "Alt+Shift+F",
"description": "browser action"
},
"_execute_page_action": {
"suggested_key": "Ctrl+F",
"description": ""
}
},
"browser_action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const Command* browser_action =
CommandsInfo::GetBrowserActionCommand(extension.get());
ASSERT_TRUE(browser_action);
EXPECT_EQ("",
browser_action->AcceleratorToString(browser_action->accelerator()));
EXPECT_FALSE(CommandsInfo::GetActionCommand(extension.get()));
EXPECT_FALSE(CommandsInfo::GetPageActionCommand(extension.get()));
EXPECT_TRUE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
// Tests that when only incorrect execute commands are specified we install
// a warning and set a default (for MV3). See https://crbug.com/1353210.
TEST_F(CommandsManifestTest,
CommandManifestAllInvalidActionCommandsInstallWarning_MV3) {
static constexpr char kManifest[] =
R"({
"name": "Multiple Action Commands But None Correct - MV3",
"manifest_version": 3,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+F",
"description": "desc"
},
"_execute_browser_action": {
"suggested_key": "Alt+Shift+F",
"description": "browser action"
},
"_execute_page_action": {
"suggested_key": "Ctrl+F",
"description": ""
}
},
"action" : {}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
ASSERT_TRUE(extension.get());
const Command* action = CommandsInfo::GetActionCommand(extension.get());
ASSERT_TRUE(action);
EXPECT_EQ("", action->AcceleratorToString(action->accelerator()));
EXPECT_FALSE(CommandsInfo::GetBrowserActionCommand(extension.get()));
EXPECT_FALSE(CommandsInfo::GetPageActionCommand(extension.get()));
EXPECT_TRUE(warnings_test_util::HasInstallWarning(
extension,
manifest_errors::kCommandActionIncorrectForManifestActionType));
}
TEST_F(CommandsManifestTest, CommandManifestShortcutsTooMany) {
static constexpr char kManifest[] =
R"({
"name": "Command test - too many commands with shortcuts",
"manifest_version": 2,
"version": "2",
"commands": {
"feature1": {
"suggested_key": "Ctrl+A",
"description": "feature1"
},
"feature2": {
"suggested_key": "Ctrl+B",
"description": "feature2"
},
"feature3": {
"suggested_key": "Ctrl+C",
"description": "feature3"
},
"feature4": {
"suggested_key": "Ctrl+D",
"description": "feature4"
},
"feature5": {
"suggested_key": "Ctrl+E",
"description": "feature5"
}
}
})";
LoadAndExpectError(ManifestData::FromJSON(kManifest),
errors::kInvalidKeyBindingTooMany);
}
TEST_F(CommandsManifestTest, CommandManifestManyButWithinBounds) {
static constexpr char kManifest[] =
R"({
"name": "Command test - many commands, but not too many shortcuts",
"manifest_version": 2,
"version": "2",
"commands": {
"feature1": {
"suggested_key": "Ctrl+A",
"description": "feature1"
},
"feature2": {
"suggested_key": "Ctrl+B",
"description": "feature2"
},
"feature3": {
"suggested_key": "Ctrl+C",
"description": "feature3"
},
"feature4": {
"suggested_key": "Ctrl+D",
"description": "feature4"
},
"feature5": {
"description": "feature5"
}
}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
}
TEST_F(CommandsManifestTest, CommandManifestAllowNumbers) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Numbers should be allowed",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+1",
"description": "feature1"
}
}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
}
TEST_F(CommandsManifestTest, CommandManifestRejectJustShift) {
static constexpr char kManifest[] =
R"({
"name": "Command test - Shift on its own is not a valid modifier",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Shift+A",
"description": "feature1"
}
}
})";
LoadAndExpectError(ManifestData::FromJSON(kManifest),
errors::kInvalidKeyBinding);
}
TEST_F(CommandsManifestTest, BrowserActionSynthesizesCommand) {
static constexpr char kManifest[] =
R"({
"name": "A simple browser action that defines no extension command",
"version": "1.0",
"manifest_version": 2,
"browser_action": {
"default_title": "Make this page red"
}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
// An extension with a browser action but no extension command specified
// should get a command assigned to it.
const extensions::Command* command =
CommandsInfo::GetBrowserActionCommand(extension.get());
ASSERT_TRUE(command);
EXPECT_EQ(ui::VKEY_UNKNOWN, command->accelerator().key_code());
}
// An extension with an action but no extension command specified should get a
// command assigned to it.
TEST_F(CommandsManifestTest, ActionSynthesizesCommand) {
static constexpr char kManifest[] =
R"({
"name": "Synthesize action shortcut if commands key is missing",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_title": "Test"
}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
const Command* command = CommandsInfo::GetActionCommand(extension.get());
ASSERT_TRUE(command);
EXPECT_EQ(ui::VKEY_UNKNOWN, command->accelerator().key_code());
}
// This test makes sure that the "commands" feature and the "commands.global"
// property load properly.
TEST_F(CommandsManifestTest, LoadsOnStable) {
static constexpr char kManifest1[] =
R"({
"name": "Command test - Extension with Command",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+0",
"description": "desc"
}
}
})";
scoped_refptr<Extension> extension1 =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest1));
static constexpr char kManifest2[] =
R"({
"name": "Command test - App with Command",
"manifest_version": 2,
"version": "1",
"app": {
"background": { "scripts": ["background.js"] }
},
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+0",
"description": "desc"
}
}
})";
scoped_refptr<Extension> extension2 =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest2));
static constexpr char kManifest3[] =
R"({
"name": "Command test - Extension with Command that is global",
"manifest_version": 2,
"version": "1",
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+0",
"description": "desc",
"global": true
}
}
})";
scoped_refptr<Extension> extension3 =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest3));
static constexpr char kManifest4[] =
R"({
"name": "Command test - App with Command that is global",
"manifest_version": 2,
"version": "1",
"app": {
"background": { "scripts": ["background.js"] }
},
"commands": {
"feature1": {
"suggested_key": "Ctrl+Shift+0",
"description": "desc",
"global": true
}
}
})";
scoped_refptr<Extension> extension4 =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest4));
}
TEST_F(CommandsManifestTest, CommandManifestShouldNotCountMediaKeys) {
// Media keys shouldn't count towards the max of four shortcuts per extension.
static constexpr char kManifest[] =
R"({
"name": "mediaKeys",
"manifest_version": 2,
"version": "1",
"commands": {
"MediaNextTrack": {
"suggested_key": "MediaNextTrack",
"description": "MediaNextTrack"
},
"MediaPlayPause": {
"suggested_key": "MediaPlayPause",
"description": "MediaPlayPause"
},
"MediaPrevTrack": {
"suggested_key": "MediaPrevTrack",
"description": "MediaPrevTrack"
},
"MediaStop": {
"suggested_key": "MediaStop",
"description": "MediaStop"
},
"feature1": {
"suggested_key": "Ctrl+A",
"description": "feature1"
},
"feature2": {
"suggested_key": "Ctrl+B",
"description": "feature2"
}
}
})";
scoped_refptr<Extension> extension =
LoadAndExpectSuccess(ManifestData::FromJSON(kManifest));
}
} // namespace extensions