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.
5 #include "base/files/file_path.h"
6 #include "base/json/json_file_value_serializer.h"
7 #include "base/memory/ref_counted.h"
8 #include "base/run_loop.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_service_test_base.h"
14 #include "chrome/browser/extensions/extension_util.h"
15 #include "chrome/browser/extensions/permissions_updater.h"
16 #include "chrome/common/chrome_paths.h"
17 #include "chrome/common/extensions/extension_test_util.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "components/crx_file/id_util.h"
20 #include "content/public/browser/notification_observer.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "content/public/browser/notification_service.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/extension_builder.h"
26 #include "extensions/common/feature_switch.h"
27 #include "extensions/common/permissions/permission_set.h"
28 #include "extensions/common/permissions/permissions_data.h"
29 #include "extensions/common/value_builder.h"
30 #include "testing/gtest/include/gtest/gtest.h"
32 using extension_test_util::LoadManifest
;
34 namespace extensions
{
38 scoped_refptr
<const Extension
> CreateExtensionWithOptionalPermissions(
39 scoped_ptr
<base::Value
> optional_permissions
,
40 scoped_ptr
<base::Value
> permissions
,
41 const std::string
& name
) {
42 return ExtensionBuilder()
43 .SetLocation(Manifest::INTERNAL
)
47 .Set("description", "foo")
48 .Set("manifest_version", 2)
49 .Set("version", "0.1.2.3")
50 .Set("permissions", permissions
.Pass())
51 .Set("optional_permissions", optional_permissions
.Pass()))
52 .SetID(crx_file::id_util::GenerateId(name
))
56 scoped_refptr
<const Extension
> CreateExtensionWithPermissions(
57 const std::set
<URLPattern
>& scriptable_hosts
,
58 const std::set
<URLPattern
>& explicit_hosts
,
59 Manifest::Location location
,
60 const std::string
& name
) {
61 ListBuilder scriptable_host_list
;
62 for (std::set
<URLPattern
>::const_iterator pattern
= scriptable_hosts
.begin();
63 pattern
!= scriptable_hosts
.end();
65 scriptable_host_list
.Append(pattern
->GetAsString());
68 ListBuilder explicit_host_list
;
69 for (std::set
<URLPattern
>::const_iterator pattern
= explicit_hosts
.begin();
70 pattern
!= explicit_hosts
.end();
72 explicit_host_list
.Append(pattern
->GetAsString());
75 DictionaryBuilder script
;
76 script
.Set("matches", scriptable_host_list
.Pass())
77 .Set("js", ListBuilder().Append("foo.js"));
79 return ExtensionBuilder()
80 .SetLocation(location
)
84 .Set("description", "foo")
85 .Set("manifest_version", 2)
86 .Set("version", "0.1.2.3")
87 .Set("content_scripts", ListBuilder().Append(script
.Pass()))
88 .Set("permissions", explicit_host_list
.Pass()))
89 .SetID(crx_file::id_util::GenerateId(name
))
93 testing::AssertionResult
SetsAreEqual(const std::set
<URLPattern
>& set1
,
94 const std::set
<URLPattern
>& set2
) {
95 // Take the (set1 - set2) U (set2 - set1). This is then the set of all
96 // elements which are in either set1 or set2, but not both.
97 // If the sets are equal, this is none.
98 std::set
<URLPattern
> difference
= base::STLSetUnion
<std::set
<URLPattern
> >(
99 base::STLSetDifference
<std::set
<URLPattern
> >(set1
, set2
),
100 base::STLSetDifference
<std::set
<URLPattern
> >(set2
, set1
));
103 for (std::set
<URLPattern
>::const_iterator iter
= difference
.begin();
104 iter
!= difference
.end();
106 if (iter
->GetAsString() == "chrome://favicon/*")
107 continue; // Grr... This is auto-added for extensions with <all_urls>
108 error
= base::StringPrintf("%s\n%s contains %s and the other does not.",
110 (set1
.count(*iter
) ? "Set1" : "Set2"),
111 iter
->GetAsString().c_str());
115 return testing::AssertionFailure() << error
;
116 return testing::AssertionSuccess();
119 // A helper class that listens for NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED.
120 class PermissionsUpdaterListener
: public content::NotificationObserver
{
122 PermissionsUpdaterListener()
123 : received_notification_(false), waiting_(false) {
125 extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED
,
126 content::NotificationService::AllSources());
130 received_notification_
= false;
137 if (received_notification_
)
141 base::RunLoop run_loop
;
145 bool received_notification() const { return received_notification_
; }
146 const Extension
* extension() const { return extension_
.get(); }
147 const PermissionSet
* permissions() const { return permissions_
.get(); }
148 UpdatedExtensionPermissionsInfo::Reason
reason() const { return reason_
; }
151 void Observe(int type
,
152 const content::NotificationSource
& source
,
153 const content::NotificationDetails
& details
) override
{
154 received_notification_
= true;
155 UpdatedExtensionPermissionsInfo
* info
=
156 content::Details
<UpdatedExtensionPermissionsInfo
>(details
).ptr();
158 extension_
= info
->extension
;
159 permissions_
= info
->permissions
;
160 reason_
= info
->reason
;
164 base::MessageLoopForUI::current()->Quit();
168 bool received_notification_
;
170 content::NotificationRegistrar registrar_
;
171 scoped_refptr
<const Extension
> extension_
;
172 scoped_refptr
<const PermissionSet
> permissions_
;
173 UpdatedExtensionPermissionsInfo::Reason reason_
;
176 class PermissionsUpdaterTest
: public ExtensionServiceTestBase
{
179 scoped_refptr
<Extension
> LoadOurManifest() {
181 path
= path
.AppendASCII("api_test")
182 .AppendASCII("permissions")
183 .AppendASCII("optional");
184 return LoadManifest(path
.AsUTF8Unsafe(),
187 Extension::NO_FLAGS
);
190 void AddPattern(URLPatternSet
* extent
, const std::string
& pattern
) {
191 int schemes
= URLPattern::SCHEME_ALL
;
192 extent
->AddPattern(URLPattern(schemes
, pattern
));
197 // Test that the PermissionUpdater can correctly add and remove active
198 // permissions. This tests all of PermissionsUpdater's public methods because
199 // GrantActivePermissions and SetPermissions are used by AddPermissions.
200 TEST_F(PermissionsUpdaterTest
, AddAndRemovePermissions
) {
201 InitializeEmptyExtensionService();
203 // Load the test extension.
204 scoped_refptr
<Extension
> extension
= LoadOurManifest();
205 ASSERT_TRUE(extension
.get());
207 APIPermissionSet default_apis
;
208 default_apis
.insert(APIPermission::kManagement
);
209 ManifestPermissionSet empty_manifest_permissions
;
211 URLPatternSet default_hosts
;
212 AddPattern(&default_hosts
, "http://a.com/*");
213 scoped_refptr
<const PermissionSet
> default_permissions
= new PermissionSet(
214 default_apis
, empty_manifest_permissions
, default_hosts
, URLPatternSet());
216 // Make sure it loaded properly.
217 scoped_refptr
<const PermissionSet
> permissions
=
218 extension
->permissions_data()->active_permissions();
219 ASSERT_EQ(*default_permissions
.get(),
220 *extension
->permissions_data()->active_permissions().get());
222 // Add a few permissions.
223 APIPermissionSet apis
;
224 apis
.insert(APIPermission::kNotifications
);
226 AddPattern(&hosts
, "http://*.c.com/*");
228 scoped_refptr
<const PermissionSet
> delta
= new PermissionSet(
229 apis
, empty_manifest_permissions
, hosts
, URLPatternSet());
231 PermissionsUpdaterListener listener
;
232 PermissionsUpdater
updater(profile_
.get());
233 updater
.AddPermissions(extension
.get(), delta
.get());
237 // Verify that the permission notification was sent correctly.
238 ASSERT_TRUE(listener
.received_notification());
239 ASSERT_EQ(extension
.get(), listener
.extension());
240 ASSERT_EQ(UpdatedExtensionPermissionsInfo::ADDED
, listener
.reason());
241 ASSERT_EQ(*delta
.get(), *listener
.permissions());
243 // Make sure the extension's active permissions reflect the change.
244 scoped_refptr
<const PermissionSet
> active_permissions
=
245 PermissionSet::CreateUnion(*default_permissions
, *delta
);
246 ASSERT_EQ(*active_permissions
.get(),
247 *extension
->permissions_data()->active_permissions().get());
249 // Verify that the new granted and active permissions were also stored
250 // in the extension preferences. In this case, the granted permissions should
251 // be equal to the active permissions.
252 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(profile_
.get());
253 scoped_refptr
<const PermissionSet
> granted_permissions
= active_permissions
;
255 scoped_refptr
<const PermissionSet
> from_prefs
=
256 prefs
->GetActivePermissions(extension
->id());
257 ASSERT_EQ(*active_permissions
.get(), *from_prefs
.get());
259 from_prefs
= prefs
->GetGrantedPermissions(extension
->id());
260 ASSERT_EQ(*active_permissions
.get(), *from_prefs
.get());
262 // In the second part of the test, we'll remove the permissions that we
263 // just added except for 'notifications'.
264 apis
.erase(APIPermission::kNotifications
);
265 delta
= new PermissionSet(apis
, empty_manifest_permissions
,
266 hosts
, URLPatternSet());
269 updater
.RemovePermissions(extension
.get(), delta
.get(),
270 PermissionsUpdater::REMOVE_SOFT
);
273 // Verify that the notification was correct.
274 ASSERT_TRUE(listener
.received_notification());
275 ASSERT_EQ(extension
.get(), listener
.extension());
276 ASSERT_EQ(UpdatedExtensionPermissionsInfo::REMOVED
, listener
.reason());
277 ASSERT_EQ(*delta
.get(), *listener
.permissions());
279 // Make sure the extension's active permissions reflect the change.
281 PermissionSet::CreateDifference(*active_permissions
, *delta
);
282 ASSERT_EQ(*active_permissions
.get(),
283 *extension
->permissions_data()->active_permissions().get());
285 // Verify that the extension prefs hold the new active permissions and the
286 // same granted permissions.
287 from_prefs
= prefs
->GetActivePermissions(extension
->id());
288 ASSERT_EQ(*active_permissions
.get(), *from_prefs
.get());
290 from_prefs
= prefs
->GetGrantedPermissions(extension
->id());
291 ASSERT_EQ(*granted_permissions
.get(), *from_prefs
.get());
294 TEST_F(PermissionsUpdaterTest
, WithholdAllHosts
) {
295 InitializeEmptyExtensionService();
297 // Permissions are only withheld with the appropriate switch turned on.
298 scoped_ptr
<FeatureSwitch::ScopedOverride
> switch_override(
299 new FeatureSwitch::ScopedOverride(FeatureSwitch::scripts_require_action(),
300 FeatureSwitch::OVERRIDE_ENABLED
));
302 URLPattern
google(URLPattern::SCHEME_ALL
, "http://www.google.com/*");
303 URLPattern
sub_google(URLPattern::SCHEME_ALL
, "http://*.google.com/*");
304 URLPattern
all_http(URLPattern::SCHEME_ALL
, "http://*/*");
305 URLPattern
all_hosts(URLPattern::SCHEME_ALL
, "<all_urls>");
306 URLPattern
all_com(URLPattern::SCHEME_ALL
, "http://*.com/*");
308 std::set
<URLPattern
> all_host_patterns
;
309 std::set
<URLPattern
> safe_patterns
;
311 all_host_patterns
.insert(all_http
);
312 all_host_patterns
.insert(all_hosts
);
313 all_host_patterns
.insert(all_com
);
315 safe_patterns
.insert(google
);
316 safe_patterns
.insert(sub_google
);
318 std::set
<URLPattern
> all_patterns
= base::STLSetUnion
<std::set
<URLPattern
> >(
319 all_host_patterns
, safe_patterns
);
321 scoped_refptr
<const Extension
> extension
= CreateExtensionWithPermissions(
322 all_patterns
, all_patterns
, Manifest::INTERNAL
, "a");
323 const PermissionsData
* permissions_data
= extension
->permissions_data();
324 PermissionsUpdater
updater(profile_
.get());
325 updater
.InitializePermissions(extension
.get());
327 // At first, the active permissions should have only the safe patterns and
328 // the withheld permissions should have only the all host patterns.
329 EXPECT_TRUE(SetsAreEqual(
330 permissions_data
->active_permissions()->scriptable_hosts().patterns(),
332 EXPECT_TRUE(SetsAreEqual(
333 permissions_data
->active_permissions()->explicit_hosts().patterns(),
335 EXPECT_TRUE(SetsAreEqual(
336 permissions_data
->withheld_permissions()->scriptable_hosts().patterns(),
338 EXPECT_TRUE(SetsAreEqual(
339 permissions_data
->withheld_permissions()->explicit_hosts().patterns(),
342 // Then, we grant the withheld all-hosts permissions.
343 updater
.GrantWithheldImpliedAllHosts(extension
.get());
344 // Now, active permissions should have all patterns, and withheld permissions
346 EXPECT_TRUE(SetsAreEqual(
347 permissions_data
->active_permissions()->scriptable_hosts().patterns(),
349 EXPECT_TRUE(permissions_data
->withheld_permissions()
353 EXPECT_TRUE(SetsAreEqual(
354 permissions_data
->active_permissions()->explicit_hosts().patterns(),
356 EXPECT_TRUE(permissions_data
->withheld_permissions()
361 // Finally, we revoke the all hosts permissions.
362 updater
.WithholdImpliedAllHosts(extension
.get());
364 // We should be back to our initial state - all_hosts should be withheld, and
365 // the safe patterns should be granted.
366 EXPECT_TRUE(SetsAreEqual(
367 permissions_data
->active_permissions()->scriptable_hosts().patterns(),
369 EXPECT_TRUE(SetsAreEqual(
370 permissions_data
->active_permissions()->explicit_hosts().patterns(),
372 EXPECT_TRUE(SetsAreEqual(
373 permissions_data
->withheld_permissions()->scriptable_hosts().patterns(),
375 EXPECT_TRUE(SetsAreEqual(
376 permissions_data
->withheld_permissions()->explicit_hosts().patterns(),
379 // Creating a component extension should result in no withheld permissions.
380 extension
= CreateExtensionWithPermissions(
381 all_patterns
, all_patterns
, Manifest::COMPONENT
, "b");
382 permissions_data
= extension
->permissions_data();
383 updater
.InitializePermissions(extension
.get());
384 EXPECT_TRUE(SetsAreEqual(
385 permissions_data
->active_permissions()->scriptable_hosts().patterns(),
387 EXPECT_TRUE(permissions_data
->withheld_permissions()
391 EXPECT_TRUE(SetsAreEqual(
392 permissions_data
->active_permissions()->explicit_hosts().patterns(),
394 EXPECT_TRUE(permissions_data
->withheld_permissions()
399 // Without the switch, we shouldn't withhold anything.
400 switch_override
.reset();
401 extension
= CreateExtensionWithPermissions(
402 all_patterns
, all_patterns
, Manifest::INTERNAL
, "c");
403 permissions_data
= extension
->permissions_data();
404 updater
.InitializePermissions(extension
.get());
405 EXPECT_TRUE(SetsAreEqual(
406 permissions_data
->active_permissions()->scriptable_hosts().patterns(),
408 EXPECT_TRUE(permissions_data
->withheld_permissions()
412 EXPECT_TRUE(SetsAreEqual(
413 permissions_data
->active_permissions()->explicit_hosts().patterns(),
415 EXPECT_TRUE(permissions_data
->withheld_permissions()
421 // Tests that withholding all hosts behaves properly with extensions installed
422 // when the switch is turned on and off.
423 TEST_F(PermissionsUpdaterTest
, WithholdAllHostsWithTransientSwitch
) {
424 InitializeEmptyExtensionService();
426 URLPattern
all_hosts(URLPattern::SCHEME_ALL
, "<all_urls>");
427 std::set
<URLPattern
> all_host_patterns
;
428 all_host_patterns
.insert(all_hosts
);
430 scoped_refptr
<const Extension
> extension_a
= CreateExtensionWithPermissions(
431 all_host_patterns
, all_host_patterns
, Manifest::INTERNAL
, "a");
432 PermissionsUpdater
updater(profile());
433 updater
.InitializePermissions(extension_a
.get());
434 const PermissionsData
* permissions_data
= extension_a
->permissions_data();
436 // Since the extension was created without the switch on, it should default
437 // to having all urls access.
438 EXPECT_TRUE(SetsAreEqual(
439 permissions_data
->active_permissions()->scriptable_hosts().patterns(),
442 permissions_data
->withheld_permissions()->scriptable_hosts().is_empty());
443 EXPECT_TRUE(util::AllowedScriptingOnAllUrls(extension_a
->id(), profile()));
445 // Enable the switch, and re-init permission for the extension.
446 scoped_ptr
<FeatureSwitch::ScopedOverride
> switch_override(
447 new FeatureSwitch::ScopedOverride(FeatureSwitch::scripts_require_action(),
448 FeatureSwitch::OVERRIDE_ENABLED
));
449 updater
.InitializePermissions(extension_a
.get());
451 // Since the extension was installed when the switch was off, it should still
452 // have the all urls pref.
453 permissions_data
= extension_a
->permissions_data();
454 EXPECT_TRUE(SetsAreEqual(
455 permissions_data
->active_permissions()->scriptable_hosts().patterns(),
458 permissions_data
->withheld_permissions()->scriptable_hosts().is_empty());
459 EXPECT_TRUE(util::AllowedScriptingOnAllUrls(extension_a
->id(), profile()));
461 // Load a new extension, which also has all urls. Since the switch is now on,
462 // the permissions should be withheld.
463 scoped_refptr
<const Extension
> extension_b
= CreateExtensionWithPermissions(
464 all_host_patterns
, all_host_patterns
, Manifest::INTERNAL
, "b");
465 updater
.InitializePermissions(extension_b
.get());
466 permissions_data
= extension_b
->permissions_data();
469 permissions_data
->active_permissions()->scriptable_hosts().is_empty());
470 EXPECT_TRUE(SetsAreEqual(
471 permissions_data
->withheld_permissions()->scriptable_hosts().patterns(),
473 EXPECT_FALSE(util::AllowedScriptingOnAllUrls(extension_b
->id(), profile()));
475 // Disable the switch, and reload the extension.
476 switch_override
.reset();
477 updater
.InitializePermissions(extension_b
.get());
479 // Since the extension was installed with the switch on, it should still be
480 // restricted with the switch off.
481 permissions_data
= extension_b
->permissions_data();
483 permissions_data
->active_permissions()->scriptable_hosts().is_empty());
484 EXPECT_TRUE(SetsAreEqual(
485 permissions_data
->withheld_permissions()->scriptable_hosts().patterns(),
487 EXPECT_FALSE(util::AllowedScriptingOnAllUrls(extension_b
->id(), profile()));
490 TEST_F(PermissionsUpdaterTest
, RevokingPermissions
) {
491 InitializeEmptyExtensionService();
493 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(profile());
495 auto api_permission_set
= [](APIPermission::ID id
) {
496 APIPermissionSet apis
;
498 return make_scoped_refptr(new PermissionSet(
499 apis
, ManifestPermissionSet(), URLPatternSet(), URLPatternSet()));
502 auto url_permission_set
= [](const GURL
& url
) {
504 URLPattern
pattern(URLPattern::SCHEME_ALL
, url
.spec());
505 set
.AddPattern(pattern
);
506 return make_scoped_refptr(new PermissionSet(
507 APIPermissionSet(), ManifestPermissionSet(), set
, URLPatternSet()));
511 // Test revoking optional permissions.
512 ListBuilder optional_permissions
;
513 optional_permissions
.Append("tabs").Append("cookies").Append("management");
514 ListBuilder required_permissions
;
515 required_permissions
.Append("topSites");
516 scoped_refptr
<const Extension
> extension
=
517 CreateExtensionWithOptionalPermissions(optional_permissions
.Build(),
518 required_permissions
.Build(),
521 PermissionsUpdater
updater(profile());
522 EXPECT_TRUE(updater
.GetRevokablePermissions(extension
.get())->IsEmpty());
524 // Add the optional "cookies" permission.
525 updater
.AddPermissions(extension
.get(),
526 api_permission_set(APIPermission::kCookie
).get());
527 const PermissionsData
* permissions
= extension
->permissions_data();
528 // The extension should have the permission in its active permissions and
529 // its granted permissions (stored in prefs). And, the permission should
531 EXPECT_TRUE(permissions
->HasAPIPermission(APIPermission::kCookie
));
532 scoped_refptr
<const PermissionSet
> granted_permissions
=
533 prefs
->GetGrantedPermissions(extension
->id());
534 EXPECT_TRUE(granted_permissions
->HasAPIPermission(APIPermission::kCookie
));
535 EXPECT_TRUE(updater
.GetRevokablePermissions(extension
.get())
536 ->HasAPIPermission(APIPermission::kCookie
));
538 // Repeat with "tabs".
539 updater
.AddPermissions(extension
.get(),
540 api_permission_set(APIPermission::kTab
).get());
541 EXPECT_TRUE(permissions
->HasAPIPermission(APIPermission::kTab
));
542 granted_permissions
= prefs
->GetGrantedPermissions(extension
->id());
543 EXPECT_TRUE(granted_permissions
->HasAPIPermission(APIPermission::kTab
));
544 EXPECT_TRUE(updater
.GetRevokablePermissions(extension
.get())
545 ->HasAPIPermission(APIPermission::kTab
));
547 // Remove the "tabs" permission. The extension should no longer have it
548 // in its active or granted permissions, and it shouldn't be revokable.
549 // The extension should still have the "cookies" permission.
550 updater
.RemovePermissions(extension
.get(),
551 api_permission_set(APIPermission::kTab
).get(),
552 PermissionsUpdater::REMOVE_HARD
);
553 EXPECT_FALSE(permissions
->HasAPIPermission(APIPermission::kTab
));
554 granted_permissions
= prefs
->GetGrantedPermissions(extension
->id());
555 EXPECT_FALSE(granted_permissions
->HasAPIPermission(APIPermission::kTab
));
556 EXPECT_FALSE(updater
.GetRevokablePermissions(extension
.get())
557 ->HasAPIPermission(APIPermission::kTab
));
558 EXPECT_TRUE(permissions
->HasAPIPermission(APIPermission::kCookie
));
559 granted_permissions
= prefs
->GetGrantedPermissions(extension
->id());
560 EXPECT_TRUE(granted_permissions
->HasAPIPermission(APIPermission::kCookie
));
561 EXPECT_TRUE(updater
.GetRevokablePermissions(extension
.get())
562 ->HasAPIPermission(APIPermission::kCookie
));
566 // Test revoking non-optional host permissions with click-to-script.
567 FeatureSwitch::ScopedOverride
scoped_override(
568 FeatureSwitch::scripts_require_action(), true);
569 ListBuilder optional_permissions
;
570 optional_permissions
.Append("tabs");
571 ListBuilder required_permissions
;
572 required_permissions
.Append("topSites")
573 .Append("http://*/*")
574 .Append("http://*.google.com/*");
575 scoped_refptr
<const Extension
> extension
=
576 CreateExtensionWithOptionalPermissions(optional_permissions
.Build(),
577 required_permissions
.Build(),
579 PermissionsUpdater
updater(profile());
580 updater
.InitializePermissions(extension
.get());
582 // By default, all-hosts was withheld, so the extension shouldn't have
583 // access to any site (like foo.com).
584 const GURL
kOrigin("http://foo.com");
585 EXPECT_FALSE(extension
->permissions_data()
586 ->active_permissions()
587 ->HasExplicitAccessToOrigin(kOrigin
));
588 EXPECT_TRUE(extension
->permissions_data()
589 ->withheld_permissions()
590 ->HasExplicitAccessToOrigin(kOrigin
));
592 const GURL
kRequiredOrigin("http://www.google.com/");
593 EXPECT_TRUE(extension
->permissions_data()
594 ->active_permissions()
595 ->HasExplicitAccessToOrigin(kRequiredOrigin
));
596 EXPECT_FALSE(updater
.GetRevokablePermissions(extension
.get())
597 ->HasExplicitAccessToOrigin(kRequiredOrigin
));
599 // Give the extension access to foo.com. Now, the foo.com permission should
601 updater
.AddPermissions(extension
.get(), url_permission_set(kOrigin
).get());
602 EXPECT_TRUE(extension
->permissions_data()
603 ->active_permissions()
604 ->HasExplicitAccessToOrigin(kOrigin
));
605 EXPECT_TRUE(updater
.GetRevokablePermissions(extension
.get())
606 ->HasExplicitAccessToOrigin(kOrigin
));
608 // Revoke the foo.com permission. The extension should no longer have
609 // access to foo.com, and the revokable permissions should be empty.
610 updater
.RemovePermissions(extension
.get(),
611 url_permission_set(kOrigin
).get(),
612 PermissionsUpdater::REMOVE_HARD
);
613 EXPECT_FALSE(extension
->permissions_data()
614 ->active_permissions()
615 ->HasExplicitAccessToOrigin(kOrigin
));
616 EXPECT_TRUE(extension
->permissions_data()
617 ->withheld_permissions()
618 ->HasExplicitAccessToOrigin(kOrigin
));
619 EXPECT_TRUE(updater
.GetRevokablePermissions(extension
.get())->IsEmpty());
623 } // namespace extensions