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_dialog_auto_confirm.h"
15 #include "extensions/browser/extension_registry.h"
16 #include "extensions/browser/management_policy.h"
17 #include "extensions/browser/test_extensions_browser_client.h"
18 #include "extensions/common/extension.h"
19 #include "extensions/common/extension_builder.h"
20 #include "extensions/common/value_builder.h"
22 namespace extensions
{
26 // A simple provider that says all extensions must remain disabled.
27 class TestManagementProvider
: public ManagementPolicy::Provider
{
29 TestManagementProvider() {}
30 ~TestManagementProvider() override
{}
33 // MananagementPolicy::Provider:
34 std::string
GetDebugPolicyProviderName() const override
{ return "test"; }
35 bool MustRemainDisabled(const Extension
* extension
,
36 Extension::DisableReason
* reason
,
37 base::string16
* error
) const override
{
41 DISALLOW_COPY_AND_ASSIGN(TestManagementProvider
);
44 // A helper class for all the various callbacks associated with reenabling an
45 // extension. This class also helps store the results of the run.
46 class CallbackHelper
{
51 // Get a callback to run on the completion of the reenable process and reset
53 ExtensionReenabler::Callback
GetCallback() {
55 return base::Bind(&CallbackHelper::OnComplete
,
56 base::Unretained(this));
59 // Check if we have receved any result, and if it matches the expected one.
60 bool has_result() const { return result_
.get() != nullptr; }
61 bool result_matches(ExtensionReenabler::ReenableResult expected
) const {
62 return result_
.get() && *result_
== expected
;
65 // Create a test ExtensionInstallPrompt that will not display any UI (which
66 // causes unit tests to crash), but rather runs the given |quit_closure| (with
67 // the prompt still active|.
68 scoped_ptr
<ExtensionInstallPrompt
> CreateTestPrompt(
69 content::WebContents
* web_contents
,
70 const base::Closure
& quit_closure
) {
71 quit_closure_
= quit_closure
;
72 scoped_ptr
<ExtensionInstallPrompt
> prompt(
73 new ExtensionInstallPrompt(web_contents
));
74 prompt
->set_callback_for_test(base::Bind(&CallbackHelper::OnShow
,
75 base::Unretained(this)));
80 // The callback to run once the reenable process finishes.
81 void OnComplete(ExtensionReenabler::ReenableResult result
) {
82 result_
.reset(new ExtensionReenabler::ReenableResult(result
));
85 // The callback to run when a test ExtensionInstallPrompt is ready to show.
86 void OnShow(ExtensionInstallPromptShowParams
* show_params
,
87 ExtensionInstallPrompt::Delegate
* delegate
,
88 scoped_refptr
<ExtensionInstallPrompt::Prompt
> prompt
) {
89 DCHECK(!quit_closure_
.is_null());
91 quit_closure_
= base::Closure();
94 // The closure to quit the currently-running loop; used with test
95 // ExtensionInstallPrompts.
96 base::Closure quit_closure_
;
98 // The result of the reenable process, or null if the process hasn't finished.
99 scoped_ptr
<ExtensionReenabler::ReenableResult
> result_
;
101 DISALLOW_COPY_AND_ASSIGN(CallbackHelper
);
106 class ExtensionReenablerUnitTest
: public ExtensionServiceTestBase
{
108 ExtensionReenablerUnitTest() {}
109 ~ExtensionReenablerUnitTest() override
{}
112 void SetUp() override
;
113 void TearDown() override
;
115 scoped_ptr
<TestExtensionsBrowserClient
> test_browser_client_
;
117 DISALLOW_COPY_AND_ASSIGN(ExtensionReenablerUnitTest
);
120 void ExtensionReenablerUnitTest::SetUp() {
121 ExtensionServiceTestBase::SetUp();
122 InitializeEmptyExtensionService();
123 // We need a TestExtensionsBrowserClient because the real one tries to
124 // implicitly convert any browser context to a (non-Testing)Profile.
125 test_browser_client_
.reset(new TestExtensionsBrowserClient(profile()));
126 test_browser_client_
->set_extension_system_factory(
127 ExtensionSystemFactory::GetInstance());
128 ExtensionsBrowserClient::Set(test_browser_client_
.get());
131 void ExtensionReenablerUnitTest::TearDown() {
133 ExtensionsBrowserClient::Set(nullptr);
134 test_browser_client_
.reset();
135 ExtensionServiceTestBase::TearDown();
138 // Test that the ExtensionReenabler reenables disabled extensions.
139 TEST_F(ExtensionReenablerUnitTest
, TestReenablingDisabledExtension
) {
140 // Create a simple extension and add it to the service.
141 scoped_refptr
<const Extension
> extension
=
143 SetManifest(DictionaryBuilder().Set("name", "test ext").
144 Set("version", "1.0").
145 Set("manifest_version", 2).
146 Set("description", "a test ext")).
147 SetID(crx_file::id_util::GenerateId("test ext")).
149 service()->AddExtension(extension
.get());
150 EXPECT_TRUE(registry()->enabled_extensions().Contains(extension
->id()));
152 CallbackHelper callback_helper
;
154 // Check that the ExtensionReenabler can re-enable disabled extensions.
156 // Disable the extension due to a permissions increase (the only type of
157 // disablement we handle with the ExtensionReenabler so far).
158 service()->DisableExtension(extension
->id(),
159 Extension::DISABLE_PERMISSIONS_INCREASE
);
160 // Sanity check that it's disabled.
161 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension
->id()));
163 // Automatically confirm install prompts.
164 ScopedTestDialogAutoConfirm
auto_confirm(
165 ScopedTestDialogAutoConfirm::ACCEPT
);
167 // Run the ExtensionReenabler.
168 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
169 ExtensionReenabler::PromptForReenable(extension
,
171 nullptr, // No web contents.
172 GURL(), // No referrer.
173 callback_helper
.GetCallback());
174 base::RunLoop().RunUntilIdle();
176 // The extension should be enabled.
177 EXPECT_TRUE(registry()->enabled_extensions().Contains(extension
->id()));
179 callback_helper
.result_matches(ExtensionReenabler::REENABLE_SUCCESS
));
182 // Check that we don't re-enable extensions that must remain disabled, and
183 // that the re-enabler reports failure correctly.
185 ScopedTestDialogAutoConfirm
auto_confirm(
186 ScopedTestDialogAutoConfirm::ACCEPT
);
188 ManagementPolicy
* management_policy
=
189 ExtensionSystem::Get(browser_context())->management_policy();
190 ASSERT_TRUE(management_policy
);
191 TestManagementProvider test_provider
;
192 management_policy
->RegisterProvider(&test_provider
);
193 service()->DisableExtension(extension
->id(),
194 Extension::DISABLE_PERMISSIONS_INCREASE
);
196 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
197 ExtensionReenabler::PromptForReenable(extension
,
199 nullptr, // No web contents.
200 GURL(), // No referrer.
201 callback_helper
.GetCallback());
202 base::RunLoop().RunUntilIdle();
204 // The extension should be enabled.
205 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension
->id()));
207 callback_helper
.result_matches(ExtensionReenabler::NOT_ALLOWED
));
209 management_policy
->UnregisterProvider(&test_provider
);
212 // Check that canceling the re-enable prompt doesn't re-enable the extension.
214 // Disable it again, and try canceling the prompt.
215 service()->DisableExtension(extension
->id(),
216 Extension::DISABLE_PERMISSIONS_INCREASE
);
217 ScopedTestDialogAutoConfirm
auto_confirm(
218 ScopedTestDialogAutoConfirm::CANCEL
);
219 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
220 ExtensionReenabler::PromptForReenable(extension
,
222 nullptr, // No web contents.
223 GURL(), // No referrer.
224 callback_helper
.GetCallback());
225 base::RunLoop().RunUntilIdle();
227 // The extension should remain disabled.
228 EXPECT_TRUE(registry()->disabled_extensions().Contains(extension
->id()));
230 callback_helper
.result_matches(ExtensionReenabler::USER_CANCELED
));
233 // Test that if the extension is re-enabled while the prompt is active, the
234 // prompt exits and reports success.
236 base::RunLoop run_loop
;
237 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
238 ExtensionReenabler::PromptForReenableWithPromptForTest(
241 callback_helper
.GetCallback(),
242 callback_helper
.CreateTestPrompt(nullptr, run_loop
.QuitClosure()));
245 // We shouldn't have any result yet (the user hasn't confirmed or canceled).
246 EXPECT_FALSE(callback_helper
.has_result());
248 // Reenable the extension. This should count as a success for reenabling.
249 service()->GrantPermissionsAndEnableExtension(extension
.get());
251 callback_helper
.result_matches(ExtensionReenabler::REENABLE_SUCCESS
));
254 // Test that prematurely destroying the re-enable prompt doesn't crash and
255 // reports an "aborted" result.
257 // Disable again, and create another prompt.
258 service()->DisableExtension(extension
->id(),
259 Extension::DISABLE_PERMISSIONS_INCREASE
);
260 base::RunLoop run_loop
;
261 scoped_ptr
<ExtensionReenabler
> extension_reenabler
=
262 ExtensionReenabler::PromptForReenableWithPromptForTest(
265 callback_helper
.GetCallback(),
266 callback_helper
.CreateTestPrompt(nullptr, run_loop
.QuitClosure()));
268 EXPECT_FALSE(callback_helper
.has_result());
269 // Destroy the reenabler to simulate the owning context being shut down
270 // (e.g., the tab closing).
271 extension_reenabler
.reset();
273 callback_helper
.result_matches(ExtensionReenabler::ABORTED
));
277 } // namespace extensions