1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <string>
6
7 #include "base/command_line.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "chrome/browser/extensions/active_script_controller.h"
11 #include "chrome/browser/extensions/extension_action.h"
12 #include "chrome/browser/extensions/extension_action_manager.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/location_bar_controller.h"
15 #include "chrome/browser/extensions/tab_helper.h"
16 #include "chrome/browser/extensions/test_extension_system.h"
17 #include "chrome/browser/sessions/session_tab_helper.h"
18 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
19 #include "chrome/test/base/testing_profile.h"
20 #include "components/crx_file/id_util.h"
21 #include "extensions/common/extension.h"
22 #include "extensions/common/extension_builder.h"
23 #include "extensions/common/feature_switch.h"
24 #include "extensions/common/value_builder.h"
25
26 #if defined(OS_CHROMEOS)
27 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
28 #include "chrome/browser/chromeos/settings/cros_settings.h"
29 #include "chrome/browser/chromeos/settings/device_settings_service.h"
30 #endif
31
32 namespace extensions {
33 namespace {
34
35 class LocationBarControllerUnitTest : public ChromeRenderViewHostTestHarness {
36 protected:
SetUp()37 virtual void SetUp() OVERRIDE {
38 active_script_override_.reset(new FeatureSwitch::ScopedOverride(
39 FeatureSwitch::scripts_require_action(), true));
40
41 ChromeRenderViewHostTestHarness::SetUp();
42 #if defined OS_CHROMEOS
43 test_user_manager_.reset(new chromeos::ScopedTestUserManager());
44 #endif
45 TabHelper::CreateForWebContents(web_contents());
46 // Create an ExtensionService so the LocationBarController can find its
47 // extensions.
48 CommandLine command_line(CommandLine::NO_PROGRAM);
49 Profile* profile =
50 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
51 extension_service_ = static_cast<TestExtensionSystem*>(
52 ExtensionSystem::Get(profile))->CreateExtensionService(
53 &command_line, base::FilePath(), false);
54 }
55
TearDown()56 virtual void TearDown() OVERRIDE {
57 #if defined OS_CHROMEOS
58 test_user_manager_.reset();
59 #endif
60 ChromeRenderViewHostTestHarness::TearDown();
61 }
62
tab_id()63 int tab_id() {
64 return SessionTabHelper::IdForTab(web_contents());
65 }
66
AddExtension(bool has_page_actions,const std::string & name)67 const Extension* AddExtension(bool has_page_actions,
68 const std::string& name) {
69 DictionaryBuilder manifest;
70 manifest.Set("name", name)
71 .Set("version", "1.0.0")
72 .Set("manifest_version", 2)
73 .Set("permissions", ListBuilder().Append("tabs"));
74 if (has_page_actions) {
75 manifest.Set("page_action", DictionaryBuilder()
76 .Set("default_title", "Hello"));
77 }
78 scoped_refptr<const Extension> extension =
79 ExtensionBuilder().SetManifest(manifest.Pass())
80 .SetID(crx_file::id_util::GenerateId(name))
81 .Build();
82 extension_service_->AddExtension(extension.get());
83 return extension.get();
84 }
85
86 ExtensionService* extension_service_;
87
88 private:
89 #if defined OS_CHROMEOS
90 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
91 chromeos::ScopedTestCrosSettings test_cros_settings_;
92 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
93 #endif
94
95 // Since we also test that we show page actions for pending script requests,
96 // we need to enable that feature.
97 scoped_ptr<FeatureSwitch::ScopedOverride> active_script_override_;
98 };
99
100 // Test that the location bar gets the proper current actions.
TEST_F(LocationBarControllerUnitTest,LocationBarDisplaysPageActions)101 TEST_F(LocationBarControllerUnitTest, LocationBarDisplaysPageActions) {
102 // Load up two extensions, one with a page action and one without.
103 const Extension* page_action = AddExtension(true, "page_actions");
104 const Extension* no_action = AddExtension(false, "no_actions");
105
106 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
107 ASSERT_TRUE(tab_helper);
108 LocationBarController* controller = tab_helper->location_bar_controller();
109 ASSERT_TRUE(controller);
110
111 // There should only be one action - the action for the extension with a
112 // page action.
113 std::vector<ExtensionAction*> current_actions =
114 controller->GetCurrentActions();
115 ASSERT_EQ(1u, current_actions.size());
116 EXPECT_EQ(page_action->id(), current_actions[0]->extension_id());
117
118 // If we request a script injection, then the location bar controller should
119 // also show a page action for that extension.
120 ActiveScriptController* active_script_controller =
121 ActiveScriptController::GetForWebContents(web_contents());
122 ASSERT_TRUE(active_script_controller);
123 active_script_controller->RequestScriptInjectionForTesting(no_action,
124 base::Closure());
125 current_actions = controller->GetCurrentActions();
126 ASSERT_EQ(2u, current_actions.size());
127 // Check that each extension is present in the vector.
128 EXPECT_TRUE(current_actions[0]->extension_id() == no_action->id() ||
129 current_actions[1]->extension_id() == no_action->id());
130 EXPECT_TRUE(current_actions[0]->extension_id() == page_action->id() ||
131 current_actions[1]->extension_id() == page_action->id());
132
133 // If we request a script injection for an extension that already has a
134 // page action, only one action should be visible.
135 active_script_controller->RequestScriptInjectionForTesting(page_action,
136 base::Closure());
137 current_actions = controller->GetCurrentActions();
138 ASSERT_EQ(2u, current_actions.size());
139 EXPECT_TRUE(current_actions[0]->extension_id() == no_action->id() ||
140 current_actions[1]->extension_id() == no_action->id());
141 EXPECT_TRUE(current_actions[0]->extension_id() == page_action->id() ||
142 current_actions[1]->extension_id() == page_action->id());
143
144 // Navigating away means that only page actions are shown again.
145 NavigateAndCommit(GURL("http://google.com"));
146 current_actions = controller->GetCurrentActions();
147 ASSERT_EQ(1u, current_actions.size());
148 EXPECT_EQ(page_action->id(), current_actions[0]->extension_id());
149 }
150
151 // Test that navigating clears all state in a page action.
TEST_F(LocationBarControllerUnitTest,NavigationClearsState)152 TEST_F(LocationBarControllerUnitTest, NavigationClearsState) {
153 const Extension* extension = AddExtension(true, "page_actions");
154
155 NavigateAndCommit(GURL("http://www.google.com"));
156
157 ExtensionAction& page_action =
158 *ExtensionActionManager::Get(profile())->GetPageAction(*extension);
159 page_action.SetTitle(tab_id(), "Goodbye");
160 page_action.SetPopupUrl(tab_id(), extension->GetResourceURL("popup.html"));
161
162 EXPECT_EQ("Goodbye", page_action.GetTitle(tab_id()));
163 EXPECT_EQ(extension->GetResourceURL("popup.html"),
164 page_action.GetPopupUrl(tab_id()));
165
166 // Within-page navigation should keep the settings.
167 NavigateAndCommit(GURL("http://www.google.com/#hash"));
168
169 EXPECT_EQ("Goodbye", page_action.GetTitle(tab_id()));
170 EXPECT_EQ(extension->GetResourceURL("popup.html"),
171 page_action.GetPopupUrl(tab_id()));
172
173 // Should discard the settings, and go back to the defaults.
174 NavigateAndCommit(GURL("http://www.yahoo.com"));
175
176 EXPECT_EQ("Hello", page_action.GetTitle(tab_id()));
177 EXPECT_EQ(GURL(), page_action.GetPopupUrl(tab_id()));
178 }
179
180 } // namespace
181 } // namespace extensions
182