1 // Copyright 2014 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.
5 #include "base/run_loop.h"
6 #include "chrome/browser/extensions/extension_install_prompt.h"
7 #include "chrome/browser/extensions/extension_reenabler.h"
8 #include "chrome/browser/extensions/extension_service.h"
9 #include "chrome/browser/extensions/extension_service_test_base.h"
10 #include "chrome/browser/extensions/extension_system_factory.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/test/base/testing_profile.h"
13 #include "components/crx_file/id_util.h"
14 #include "extensions/browser/extension_registry.h"
15 #include "extensions/browser/management_policy.h"
16 #include "extensions/browser/test_extensions_browser_client.h"
17 #include "extensions/common/extension.h"
18 #include "extensions/common/extension_builder.h"
19 #include "extensions/common/value_builder.h"
21 namespace extensions
{
25 // A simple provider that says all extensions must remain disabled.
26 class TestManagementProvider
: public ManagementPolicy::Provider
{
28 TestManagementProvider() {}
29 ~TestManagementProvider() override
{}
32 // MananagementPolicy::Provider:
33 std::string
GetDebugPolicyProviderName() const override
{ return "test"; }
34 bool MustRemainDisabled(const Extension
* extension
,
35 Extension::DisableReason
* reason
,
36 base::string16
* error
) const override
{
40 DISALLOW_COPY_AND_ASSIGN(TestManagementProvider
);
43 // A helper class for all the various callbacks associated with reenabling an
44 // extension. This class also helps store the results of the run.
45 class CallbackHelper
{
50 // Get a callback to run on the completion of the reenable process and reset
52 ExtensionReenabler::Callback
GetCallback() {
54 return base::Bind(&CallbackHelper::OnComplete
,
55 base::Unretained(this));
58 // Check if we have receved any result, and if it matches the expected one.
59 bool has_result() const { return result_
.get() != nullptr; }
60 bool result_matches(ExtensionReenabler::ReenableResult expected
) const {
61 return result_
.get() && *result_
== expected
;
64 // Create a test ExtensionInstallPrompt that will not display any UI (which
65 // causes unit tests to crash), but rather runs the given |quit_closure| (with
66 // the prompt still active|.
67 scoped_ptr
<ExtensionInstallPrompt
> CreateTestPrompt(
68 content::WebContents
* web_contents
,
69 const base::Closure
& quit_closure
) {
70 quit_closure_
= quit_closure
;
71 scoped_ptr
<ExtensionInstallPrompt
> prompt(
72 new ExtensionInstallPrompt(web_contents
));
73 prompt
->set_callback_for_test(base::Bind(&CallbackHelper::OnShow
,
74 base::Unretained(this)));
79 // The callback to run once the reenable process finishes.
80 void OnComplete(ExtensionReenabler::ReenableResult result
) {
81 result_
.reset(new ExtensionReenabler::ReenableResult(result
));
84 // The callback to run when a test ExtensionInstallPrompt is ready to show.
85 void OnShow(ExtensionInstallPromptShowParams
* show_params
,
86 ExtensionInstallPrompt::Delegate
* delegate
,
87 scoped_refptr
<ExtensionInstallPrompt::Prompt
> prompt
) {
88 DCHECK(!quit_closure_
.is_null());
90 quit_closure_
= base::Closure();
93 // The closure to quit the currently-running loop; used with test
94 // ExtensionInstallPrompts.
95 base::Closure quit_closure_
;
97 // The result of the reenable process, or null if the process hasn't finished.
98 scoped_ptr
<ExtensionReenabler::ReenableResult
> result_
;
100 DISALLOW_COPY_AND_ASSIGN(CallbackHelper
);
105 class ExtensionReenablerUnitTest
: public ExtensionServiceTestBase
{
107 ExtensionReenablerUnitTest() {}
108 ~ExtensionReenablerUnitTest() override
{}
111 void SetUp() override
;
112 void TearDown() override
;
114 scoped_ptr
<TestExtensionsBrowserClient
> test_browser_client_
;
116 DISALLOW_COPY_AND_ASSIGN(ExtensionReenablerUnitTest
);
119 void ExtensionReenablerUnitTest::SetUp() {
120 ExtensionServiceTestBase::SetUp();
121 InitializeEmptyExtensionService();
122 // We need a TestExtensionsBrowserClient because the real one tries to
123 // implicitly convert any browser context to a (non-Testing)Profile.
124 test_browser_client_
.reset(new TestExtensionsBrowserClient(profile()));
125 test_browser_client_
->set_extension_system_factory(
126 ExtensionSystemFactory::GetInstance());
127 ExtensionsBrowserClient::Set(test_browser_client_
.get());
130 void ExtensionReenablerUnitTest::TearDown() {
132 ExtensionsBrowserClient::Set(nullptr);
133 test_browser_client_
.reset();
134 ExtensionServiceTestBase::TearDown();
137 // Test that the ExtensionReenabler reenables disabled extensions.
138 TEST_F(ExtensionReenablerUnitTest
, TestReenablingDisabledExtension
) {
139 // Create a simple extension and add it to the service.
140 scoped_refptr
<const Extension
> extension
=
142 SetManifest(DictionaryBuilder().Set("name", "test ext").
143 Set("version", "1.0").
144 Set("manifest_version", 2).
145 Set("description", "a test ext")).
146 SetID(crx_file::id_util::GenerateId("test ext")).
148 service()->AddExtension(extension
.get());
149 EXPECT_TRUE(registry()->enabled_extensions().Contains(extension
->id()));
151 CallbackHelper callback_helper
;
153 // Check that the ExtensionReenabler can re-enable disabled extensions.
155 // Disable the extension due to a permissions increase (the only type of
156 // disablement we handle with the ExtensionReenabler so far).
157 service()->DisableExtension(extension
->id(),
158 Extension::DISABLE_PERMISSIONS_INCREASE
);
159 // Sanity check that it's disabled.
160 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension
->id()));
162 // Automatically confirm install prompts.
163 ExtensionInstallPrompt::g_auto_confirm_for_tests
=
164 ExtensionInstallPrompt::ACCEPT
;
166 // Run the ExtensionReenabler.
167 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
168 ExtensionReenabler::PromptForReenable(extension
,
170 nullptr, // No web contents.
171 GURL(), // No referrer.
172 callback_helper
.GetCallback());
173 base::RunLoop().RunUntilIdle();
175 // The extension should be enabled.
176 EXPECT_TRUE(registry()->enabled_extensions().Contains(extension
->id()));
178 callback_helper
.result_matches(ExtensionReenabler::REENABLE_SUCCESS
));
181 // Check that we don't re-enable extensions that must remain disabled, and
182 // that the re-enabler reports failure correctly.
184 ManagementPolicy
* management_policy
=
185 ExtensionSystem::Get(browser_context())->management_policy();
186 ASSERT_TRUE(management_policy
);
187 TestManagementProvider test_provider
;
188 management_policy
->RegisterProvider(&test_provider
);
189 service()->DisableExtension(extension
->id(),
190 Extension::DISABLE_PERMISSIONS_INCREASE
);
192 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
193 ExtensionReenabler::PromptForReenable(extension
,
195 nullptr, // No web contents.
196 GURL(), // No referrer.
197 callback_helper
.GetCallback());
198 base::RunLoop().RunUntilIdle();
200 // The extension should be enabled.
201 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension
->id()));
203 callback_helper
.result_matches(ExtensionReenabler::NOT_ALLOWED
));
205 management_policy
->UnregisterProvider(&test_provider
);
208 // Check that canceling the re-enable prompt doesn't re-enable the extension.
210 // Disable it again, and try canceling the prompt.
211 service()->DisableExtension(extension
->id(),
212 Extension::DISABLE_PERMISSIONS_INCREASE
);
213 ExtensionInstallPrompt::g_auto_confirm_for_tests
=
214 ExtensionInstallPrompt::CANCEL
;
215 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
216 ExtensionReenabler::PromptForReenable(extension
,
218 nullptr, // No web contents.
219 GURL(), // No referrer.
220 callback_helper
.GetCallback());
221 base::RunLoop().RunUntilIdle();
223 // The extension should remain disabled.
224 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension
->id()));
226 callback_helper
.result_matches(ExtensionReenabler::USER_CANCELED
));
229 // Test that if the extension is re-enabled while the prompt is active, the
230 // prompt exits and reports success.
232 // Don't auto-confirm, so that the prompt "stays around".
233 ExtensionInstallPrompt::g_auto_confirm_for_tests
=
234 ExtensionInstallPrompt::NONE
;
235 base::RunLoop run_loop
;
236 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
237 ExtensionReenabler::PromptForReenableWithPromptForTest(
240 callback_helper
.GetCallback(),
241 callback_helper
.CreateTestPrompt(nullptr, run_loop
.QuitClosure()));
244 // We shouldn't have any result yet (the user hasn't confirmed or canceled).
245 EXPECT_FALSE(callback_helper
.has_result());
247 // Reenable the extension. This should count as a success for reenabling.
248 service()->GrantPermissionsAndEnableExtension(extension
.get());
250 callback_helper
.result_matches(ExtensionReenabler::REENABLE_SUCCESS
));
253 // Test that prematurely destroying the re-enable prompt doesn't crash and
254 // reports an "aborted" result.
256 // Disable again, and create another prompt.
257 service()->DisableExtension(extension
->id(),
258 Extension::DISABLE_PERMISSIONS_INCREASE
);
259 base::RunLoop run_loop
;
260 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
261 ExtensionReenabler::PromptForReenableWithPromptForTest(
264 callback_helper
.GetCallback(),
265 callback_helper
.CreateTestPrompt(nullptr, run_loop
.QuitClosure()));
267 EXPECT_FALSE(callback_helper
.has_result());
268 // Destroy the reenabler to simulate the owning context being shut down
269 // (e.g., the tab closing).
270 extension_reenabler
.reset();
272 callback_helper
.result_matches(ExtensionReenabler::ABORTED
));
276 } // namespace extensions