summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/generated_resources.grd20
-rw-r--r--chrome/browser/automation/testing_automation_provider.cc5
-rw-r--r--chrome/browser/background/background_mode_manager.cc24
-rw-r--r--chrome/browser/extensions/convert_web_app_browsertest.cc2
-rw-r--r--chrome/browser/extensions/convert_web_app_unittest.cc4
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc7
-rw-r--r--chrome/browser/extensions/extension_install_ui.cc35
-rw-r--r--chrome/browser/extensions/extension_install_ui.h14
-rw-r--r--chrome/browser/extensions/extension_management_api.cc4
-rw-r--r--chrome/browser/extensions/extension_permissions_api.cc374
-rw-r--r--chrome/browser/extensions/extension_permissions_api.h117
-rw-r--r--chrome/browser/extensions/extension_permissions_api_constants.cc23
-rw-r--r--chrome/browser/extensions/extension_permissions_api_constants.h28
-rw-r--r--chrome/browser/extensions/extension_prefs.cc171
-rw-r--r--chrome/browser/extensions/extension_prefs.h22
-rw-r--r--chrome/browser/extensions/extension_prefs_unittest.cc84
-rw-r--r--chrome/browser/extensions/extension_service.cc67
-rw-r--r--chrome/browser/extensions/extension_service.h19
-rw-r--r--chrome/browser/extensions/extension_service_unittest.cc15
-rw-r--r--chrome/browser/extensions/permissions_apitest.cc39
-rw-r--r--chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h1
-rw-r--r--chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm4
-rw-r--r--chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc7
-rw-r--r--chrome/browser/ui/views/extensions/extension_install_dialog_view.cc6
-rw-r--r--chrome/chrome_browser.gypi4
-rw-r--r--chrome/common/chrome_notification_types.h4
-rw-r--r--chrome/common/extensions/api/extension_api.json137
-rw-r--r--chrome/common/extensions/docs/experimental.html1
-rw-r--r--chrome/common/extensions/docs/experimental.permissions.html1784
-rw-r--r--chrome/common/extensions/docs/samples.json6
-rw-r--r--chrome/common/extensions/extension.cc345
-rw-r--r--chrome/common/extensions/extension.h94
-rw-r--r--chrome/common/extensions/extension_constants.cc1
-rw-r--r--chrome/common/extensions/extension_constants.h1
-rw-r--r--chrome/common/extensions/extension_messages.cc52
-rw-r--r--chrome/common/extensions/extension_messages.h28
-rw-r--r--chrome/common/extensions/extension_permission_set.cc87
-rw-r--r--chrome/common/extensions/extension_permission_set.h24
-rw-r--r--chrome/common/extensions/extension_permission_set_unittest.cc337
-rw-r--r--chrome/common/extensions/extension_unittest.cc4
-rw-r--r--chrome/common/extensions/url_pattern_set.cc28
-rw-r--r--chrome/common/extensions/url_pattern_set.h14
-rw-r--r--chrome/common/extensions/url_pattern_set_unittest.cc211
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.cc18
-rw-r--r--chrome/renderer/extensions/extension_dispatcher.h5
-rw-r--r--chrome/renderer/resources/extension_apitest.js2
-rw-r--r--chrome/renderer/resources/renderer_extension_bindings.js1
-rw-r--r--chrome/test/data/extensions/api_test/permissions/experimental_disabled/background.html2
-rw-r--r--chrome/test/data/extensions/api_test/permissions/optional/background.html188
-rw-r--r--chrome/test/data/extensions/api_test/permissions/optional/manifest.json19
-rw-r--r--chrome/test/data/extensions/api_test/permissions/optional_deny/background.html36
-rw-r--r--chrome/test/data/extensions/api_test/permissions/optional_deny/manifest.json8
52 files changed, 4107 insertions, 426 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 404e58b..7382d99 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3672,6 +3672,9 @@ are declared in build/common.gypi.
<message name="IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE" desc="Titlebar of the extension or app prompt window when re-enabling an extension that requires additional permissions">
Confirm Re-enable
</message>
+ <message name="IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE" desc="Titlebar of the extension or app permissions prompt window">
+ Confirm Permissions
+ </message>
<message name="IDS_EXTENSION_INSTALL_PROMPT_HEADING" desc="First bold line in the content area of the extension or app installation prompt. Asks the user if they want to install a particular extension or app.">
Install <ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph>?
</message>
@@ -3681,6 +3684,9 @@ are declared in build/common.gypi.
<message name="IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING" desc="First bold line in the content area of the extension or app installation prompt. Asks the user if they want to re-enable a particular extension or app.">
The newest version of "<ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph>" has been disabled because it requires more permissions.
</message>
+ <message name="IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING" desc="First bold line in the content area of the extension or app permissions prompt. Asks the user if they want to grant the permissions to the particular extension or app.">
+ The extension "<ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph>" has requested additional permissions.
+ </message>
<!-- Extension/App install dialog strings -->
<message name="IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO" desc="Second line in the content area of the extension or app installation prompt. Note that the exact wording is important. This should mean that the extension or app _can_ access the listed privileges, but not necessarily that it will or needs to.">
@@ -3689,6 +3695,10 @@ are declared in build/common.gypi.
<message name="IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO" desc="Second line in the content area of the extension or app re-enable prompt. Note that the exact wording is important. This should mean that the extension _can now_ access the listed privileges, but not necessarily that it will or needs to. This message appeared because the user must approve new permissions of the extension or app.">
It can now access:
</message>
+ <message name="IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO" desc="Second line in the content area of the extension or app permissions prompt. Note that the exact wording is important. This should mean that the extension _wants to_ access the listed privileges, but not necessarily that it will or needs to. This message appeared because the user must approve new permissions of the extension or app.">
+ It could access:
+ </message>
+
<message name="IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS" desc="Permission string for full access to the computer and all websites.">
All data on your computer and the websites you visit
</message>
@@ -4033,6 +4043,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON" desc="Text for the enable button on the extension re-enable prompt">
Re-enable
</message>
+ <message name="IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON" desc="Text for the allow button on the extension permissions prompt">
+ Allow
+ </message>
+ <message name="IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON" desc="Text for the deny button on the extension permissions prompt">
+ Deny
+ </message>
<message name="IDS_EXTENSION_WEB_STORE_TITLE" desc="Text for the Chrome Web Store">
Web Store
</message>
@@ -7736,7 +7752,7 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_AUTOFILL_HELP_LABEL" desc="The label of the Help link on the Autofill dialog.">
About Autofill
</message>
-
+
<message name="IDS_AUTOFILL_SHOW_PREDICTIONS_TITLE" desc="The title for form elements when annotated with Autofill predictions.">
heuristic type: <ph name="HEURISTIC_TYPE">$1<ex>NAME_FIRST</ex></ph>\nserver type: <ph name="SERVER_TYPE">$2<ex>NAME_FIRST</ex></ph>\nfield signature: <ph name="FIELD_SIGNATURE">$3<ex>12345678</ex></ph>\nform signature: <ph name="FORM_SIGNATURE">$4<ex>1234567812345678</ex></ph>\nexperiment id: "<ph name="EXPERIMENT_ID">$5<ex>ar1</ex></ph>"
</message>
@@ -8287,7 +8303,7 @@ Keep your key file in a safe place. You will need it to create new versions of y
</message>
<message name="IDS_APP_DEFAULT_PAGE_NAME"
desc="Default name for the first 'Apps' page on the New Tab Page.">
- Apps
+ Apps
</message>
<message name="IDS_APP_CONTEXT_MENU_OPEN_PINNED"
desc="Text for the button that opens the app in a pinned tab.">
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index 44a3b5b..d1a523f 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -4111,7 +4111,7 @@ ListValue* GetHostPermissions(const Extension* ext, bool effective_perm) {
if (effective_perm)
pattern_set = ext->GetEffectiveHostPermissions();
else
- pattern_set = ext->permission_set()->explicit_hosts();
+ pattern_set = ext->GetActivePermissions()->explicit_hosts();
ListValue* permissions = new ListValue;
for (URLPatternSet::const_iterator perm = pattern_set.begin();
@@ -4124,7 +4124,8 @@ ListValue* GetHostPermissions(const Extension* ext, bool effective_perm) {
ListValue* GetAPIPermissions(const Extension* ext) {
ListValue* permissions = new ListValue;
- std::set<std::string> perm_list = ext->permission_set()->GetAPIsAsStrings();
+ std::set<std::string> perm_list =
+ ext->GetActivePermissions()->GetAPIsAsStrings();
for (std::set<std::string>::const_iterator perm = perm_list.begin();
perm != perm_list.end(); ++perm) {
permissions->Append(new StringValue(perm->c_str()));
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc
index c44362c..33eecf1 100644
--- a/chrome/browser/background/background_mode_manager.cc
+++ b/chrome/browser/background/background_mode_manager.cc
@@ -196,13 +196,15 @@ void BackgroundModeManager::RegisterProfile(Profile* profile) {
profile, this));
background_mode_data_[profile] = bmd;
- // Listen for when extensions are loaded/unloaded so we can track the
- // number of background apps and modify our keep-alive and launch-on-startup
- // state appropriately.
+ // Listen for when extensions are loaded/unloaded or add/remove the
+ // background permission so we can track the number of background apps and
+ // modify our keep-alive and launch-on-startup state appropriately.
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
Source<Profile>(profile));
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
+ Source<Profile>(profile));
// Check for the presence of background apps after all extensions have been
// loaded, to handle the case where an extension has been manually removed
@@ -274,6 +276,22 @@ void BackgroundModeManager::Observe(int type,
OnBackgroundAppUninstalled();
}
break;
+ case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED: {
+ UpdatedExtensionPermissionsInfo* info =
+ Details<UpdatedExtensionPermissionsInfo>(details).ptr();
+ if (!info->permissions->HasAPIPermission(
+ ExtensionAPIPermission::kBackground))
+ break;
+
+ if (info->reason == UpdatedExtensionPermissionsInfo::ADDED) {
+ OnBackgroundAppInstalled(info->extension);
+ OnBackgroundAppLoaded();
+ } else {
+ OnBackgroundAppUnloaded();
+ OnBackgroundAppUninstalled();
+ }
+ }
+ break;
case content::NOTIFICATION_APP_TERMINATING:
// Make sure we aren't still keeping the app alive (only happens if we
// don't receive an EXTENSIONS_READY notification for some reason).
diff --git a/chrome/browser/extensions/convert_web_app_browsertest.cc b/chrome/browser/extensions/convert_web_app_browsertest.cc
index a8405b1..7fe6f94 100644
--- a/chrome/browser/extensions/convert_web_app_browsertest.cc
+++ b/chrome/browser/extensions/convert_web_app_browsertest.cc
@@ -70,7 +70,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionFromWebAppTest, Basic) {
EXPECT_EQ(extension_misc::LAUNCH_PANEL,
installed_extension_->launch_container());
- ASSERT_EQ(2u, installed_extension_->permission_set()->apis().size());
+ ASSERT_EQ(2u, installed_extension_->GetActivePermissions()->apis().size());
EXPECT_TRUE(installed_extension_->HasAPIPermission(
ExtensionAPIPermission::kGeolocation));
EXPECT_TRUE(installed_extension_->HasAPIPermission(
diff --git a/chrome/browser/extensions/convert_web_app_unittest.cc b/chrome/browser/extensions/convert_web_app_unittest.cc
index 0f3b537..640b31b 100644
--- a/chrome/browser/extensions/convert_web_app_unittest.cc
+++ b/chrome/browser/extensions/convert_web_app_unittest.cc
@@ -123,7 +123,7 @@ TEST(ExtensionFromWebApp, Basic) {
EXPECT_EQ(UTF16ToUTF8(web_app.title), extension->name());
EXPECT_EQ(UTF16ToUTF8(web_app.description), extension->description());
EXPECT_EQ(web_app.app_url, extension->GetFullLaunchURL());
- EXPECT_EQ(2u, extension->permission_set()->apis().size());
+ EXPECT_EQ(2u, extension->GetActivePermissions()->apis().size());
EXPECT_TRUE(extension->HasAPIPermission("geolocation"));
EXPECT_TRUE(extension->HasAPIPermission("notifications"));
ASSERT_EQ(1u, extension->web_extent().patterns().size());
@@ -167,7 +167,7 @@ TEST(ExtensionFromWebApp, Minimal) {
EXPECT_EQ("", extension->description());
EXPECT_EQ(web_app.app_url, extension->GetFullLaunchURL());
EXPECT_EQ(0u, extension->icons().map().size());
- EXPECT_EQ(0u, extension->permission_set()->apis().size());
+ EXPECT_EQ(0u, extension->GetActivePermissions()->apis().size());
ASSERT_EQ(1u, extension->web_extent().patterns().size());
EXPECT_EQ("*://aaronboodman.com/*",
extension->web_extent().patterns().begin()->GetAsString());
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index c8cbf9e..15529b2 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -32,6 +32,7 @@
#include "chrome/browser/extensions/extension_module.h"
#include "chrome/browser/extensions/extension_omnibox_api.h"
#include "chrome/browser/extensions/extension_page_actions_module.h"
+#include "chrome/browser/extensions/extension_permissions_api.h"
#include "chrome/browser/extensions/extension_preference_api.h"
#include "chrome/browser/extensions/extension_processes_api.h"
#include "chrome/browser/extensions/extension_proxy_api.h"
@@ -393,6 +394,12 @@ void FactoryRegistry::ResetFunctions() {
// Experimental App API.
RegisterFunction<AppNotifyFunction>();
RegisterFunction<AppClearAllNotificationsFunction>();
+
+ // Permissions
+ RegisterFunction<ContainsPermissionsFunction>();
+ RegisterFunction<GetAllPermissionsFunction>();
+ RegisterFunction<RemovePermissionsFunction>();
+ RegisterFunction<RequestPermissionsFunction>();
}
void FactoryRegistry::GetAllNames(std::vector<std::string>* names) {
diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc
index 62d904a..bdbd839 100644
--- a/chrome/browser/extensions/extension_install_ui.cc
+++ b/chrome/browser/extensions/extension_install_ui.cc
@@ -39,22 +39,32 @@
// static
const int ExtensionInstallUI::kTitleIds[NUM_PROMPT_TYPES] = {
IDS_EXTENSION_INSTALL_PROMPT_TITLE,
- IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE
+ IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE,
+ IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE
};
// static
const int ExtensionInstallUI::kHeadingIds[NUM_PROMPT_TYPES] = {
IDS_EXTENSION_INSTALL_PROMPT_HEADING,
- IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING
+ IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING,
+ IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING
};
// static
const int ExtensionInstallUI::kButtonIds[NUM_PROMPT_TYPES] = {
IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
- IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON
+ IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON,
+ IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON
+};
+// static
+const int ExtensionInstallUI::kAbortButtonIds[NUM_PROMPT_TYPES] = {
+ 0,
+ 0,
+ IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON
};
// static
const int ExtensionInstallUI::kWarningIds[NUM_PROMPT_TYPES] = {
IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
- IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO
+ IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO,
+ IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO,
};
namespace {
@@ -112,6 +122,7 @@ void ExtensionInstallUI::ConfirmInstall(Delegate* delegate,
const Extension* extension) {
DCHECK(ui_loop_ == MessageLoop::current());
extension_ = extension;
+ permissions_ = extension->GetActivePermissions();
delegate_ = delegate;
// We special-case themes to not show any confirm UI. Instead they are
@@ -129,11 +140,24 @@ void ExtensionInstallUI::ConfirmReEnable(Delegate* delegate,
const Extension* extension) {
DCHECK(ui_loop_ == MessageLoop::current());
extension_ = extension;
+ permissions_ = extension->GetActivePermissions();
delegate_ = delegate;
ShowConfirmation(RE_ENABLE_PROMPT);
}
+void ExtensionInstallUI::ConfirmPermissions(
+ Delegate* delegate,
+ const Extension* extension,
+ const ExtensionPermissionSet* permissions) {
+ DCHECK(ui_loop_ == MessageLoop::current());
+ extension_ = extension;
+ permissions_ = permissions;
+ delegate_ = delegate;
+
+ ShowConfirmation(PERMISSIONS_PROMPT);
+}
+
void ExtensionInstallUI::OnInstallSuccess(const Extension* extension,
SkBitmap* icon) {
extension_ = extension;
@@ -193,6 +217,7 @@ void ExtensionInstallUI::OnImageLoaded(
SetIcon(image);
switch (prompt_type_) {
+ case PERMISSIONS_PROMPT:
case RE_ENABLE_PROMPT:
case INSTALL_PROMPT: {
// TODO(jcivelli): http://crbug.com/44771 We should not show an install
@@ -203,7 +228,7 @@ void ExtensionInstallUI::OnImageLoaded(
NotificationService::NoDetails());
std::vector<string16> warnings =
- extension_->GetPermissionMessageStrings();
+ permissions_->GetWarningMessages();
ShowExtensionInstallDialog(
profile_, delegate_, extension_, &icon_, warnings, prompt_type_);
break;
diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h
index 222f6a3..19965e5 100644
--- a/chrome/browser/extensions/extension_install_ui.h
+++ b/chrome/browser/extensions/extension_install_ui.h
@@ -16,6 +16,7 @@
#include "ui/gfx/native_widget_types.h"
class Extension;
+class ExtensionPermissionSet;
class MessageLoop;
class Profile;
class InfoBarDelegate;
@@ -28,6 +29,7 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer {
UNSET_PROMPT_TYPE = -1,
INSTALL_PROMPT = 0,
RE_ENABLE_PROMPT,
+ PERMISSIONS_PROMPT,
NUM_PROMPT_TYPES
};
@@ -36,6 +38,7 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer {
static const int kHeadingIds[NUM_PROMPT_TYPES];
static const int kButtonIds[NUM_PROMPT_TYPES];
static const int kWarningIds[NUM_PROMPT_TYPES];
+ static const int kAbortButtonIds[NUM_PROMPT_TYPES];
class Delegate {
public:
@@ -65,6 +68,14 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer {
// We *MUST* eventually call either Proceed() or Abort() on |delegate|.
virtual void ConfirmReEnable(Delegate* delegate, const Extension* extension);
+ // This is called by the extension permissions API to verify whether an
+ // extension may be granted additional permissions.
+ //
+ // We *MUST* eventually call either Proceed() or Abort() on |delegate|.
+ virtual void ConfirmPermissions(Delegate* delegate,
+ const Extension* extension,
+ const ExtensionPermissionSet* permissions);
+
// Installation was successful. This is declared virtual for testing.
virtual void OnInstallSuccess(const Extension* extension, SkBitmap* icon);
@@ -122,6 +133,9 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer {
// The extension we are showing the UI for.
const Extension* extension_;
+ // The permissions being prompted for.
+ scoped_refptr<const ExtensionPermissionSet> permissions_;
+
// The delegate we will call Proceed/Abort on after confirmation UI.
Delegate* delegate_;
diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc
index 3955361..8037f56 100644
--- a/chrome/browser/extensions/extension_management_api.cc
+++ b/chrome/browser/extensions/extension_management_api.cc
@@ -92,7 +92,7 @@ static DictionaryValue* CreateExtensionInfo(const Extension& extension,
}
const std::set<std::string> perms =
- extension.permission_set()->GetAPIsAsStrings();
+ extension.GetActivePermissions()->GetAPIsAsStrings();
ListValue* permission_list = new ListValue();
if (!perms.empty()) {
std::set<std::string>::const_iterator perms_iter;
@@ -107,7 +107,7 @@ static DictionaryValue* CreateExtensionInfo(const Extension& extension,
if (!extension.is_hosted_app()) {
// Skip host permissions for hosted apps.
const URLPatternSet host_perms =
- extension.permission_set()->explicit_hosts();
+ extension.GetActivePermissions()->explicit_hosts();
if (!host_perms.is_empty()) {
URLPatternSet::const_iterator host_perms_iter;
for (host_perms_iter = host_perms.begin();
diff --git a/chrome/browser/extensions/extension_permissions_api.cc b/chrome/browser/extensions/extension_permissions_api.cc
new file mode 100644
index 0000000..a9a6d23
--- /dev/null
+++ b/chrome/browser/extensions/extension_permissions_api.cc
@@ -0,0 +1,374 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_permissions_api.h"
+
+#include "base/json/json_writer.h"
+#include "base/stringprintf.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_event_router.h"
+#include "chrome/browser/extensions/extension_permissions_api_constants.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/extension_permission_set.h"
+#include "chrome/common/extensions/url_pattern_set.h"
+#include "content/common/notification_service.h"
+#include "googleurl/src/gurl.h"
+
+
+namespace keys = extension_permissions_module_constants;
+
+namespace {
+
+enum AutoConfirmForTest {
+ DO_NOT_SKIP = 0,
+ PROCEED,
+ ABORT
+};
+AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP;
+
+DictionaryValue* PackPermissionsToValue(const ExtensionPermissionSet* set) {
+ DictionaryValue* value = new DictionaryValue();
+
+ // Generate the list of API permissions.
+ ListValue* apis = new ListValue();
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ for (ExtensionAPIPermissionSet::const_iterator i = set->apis().begin();
+ i != set->apis().end(); ++i)
+ apis->Append(Value::CreateStringValue(info->GetByID(*i)->name()));
+
+ // TODO(jstritar): Include hosts once the API supports them. At that point,
+ // we could also shared this code with ExtensionPermissionSet methods in
+ // ExtensionPrefs.
+
+ value->Set(keys::kApisKey, apis);
+ return value;
+}
+
+// Creates a new ExtensionPermissionSet from its |value| and passes ownership to
+// the caller through |ptr|. Sets |bad_message| to true if the message is badly
+// formed. Returns false if the method fails to unpack a permission set.
+bool UnpackPermissionsFromValue(DictionaryValue* value,
+ scoped_refptr<ExtensionPermissionSet>* ptr,
+ bool* bad_message,
+ std::string* error) {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ ExtensionAPIPermissionSet apis;
+ if (value->HasKey(keys::kApisKey)) {
+ ListValue* api_list = NULL;
+ if (!value->GetList(keys::kApisKey, &api_list)) {
+ *bad_message = true;
+ return false;
+ }
+ for (size_t i = 0; i < api_list->GetSize(); ++i) {
+ std::string api_name;
+ if (!api_list->GetString(i, &api_name)) {
+ *bad_message = true;
+ return false;
+ }
+
+ ExtensionAPIPermission* permission = info->GetByName(api_name);
+ if (!permission) {
+ *error = base::StringPrintf(
+ keys::kUnknownPermissionError, api_name.c_str());
+ return false;
+ }
+ apis.insert(permission->id());
+ }
+ }
+
+ // Ignore host permissions for now.
+ URLPatternSet empty_set;
+ *ptr = new ExtensionPermissionSet(apis, empty_set, empty_set);
+ return true;
+}
+
+} // namespace
+
+ExtensionPermissionsManager::ExtensionPermissionsManager(
+ ExtensionService* extension_service)
+ : extension_service_(extension_service) {
+ RegisterWhitelist();
+}
+
+ExtensionPermissionsManager::~ExtensionPermissionsManager() {
+}
+
+void ExtensionPermissionsManager::AddPermissions(
+ const Extension* extension, const ExtensionPermissionSet* permissions) {
+ scoped_refptr<const ExtensionPermissionSet> existing(
+ extension->GetActivePermissions());
+ scoped_refptr<ExtensionPermissionSet> total(
+ ExtensionPermissionSet::CreateUnion(existing, permissions));
+ scoped_refptr<ExtensionPermissionSet> added(
+ ExtensionPermissionSet::CreateDifference(total.get(), existing));
+
+ extension_service_->UpdateActivePermissions(extension, total.get());
+
+ // Update the granted permissions so we don't auto-disable the extension.
+ extension_service_->GrantPermissions(extension);
+
+ NotifyPermissionsUpdated(extension, total.get(), added.get(), ADDED);
+}
+
+void ExtensionPermissionsManager::RemovePermissions(
+ const Extension* extension, const ExtensionPermissionSet* permissions) {
+ scoped_refptr<const ExtensionPermissionSet> existing(
+ extension->GetActivePermissions());
+ scoped_refptr<ExtensionPermissionSet> total(
+ ExtensionPermissionSet::CreateDifference(existing, permissions));
+ scoped_refptr<ExtensionPermissionSet> removed(
+ ExtensionPermissionSet::CreateDifference(existing, total.get()));
+
+ // We update the active permissions, and not the granted permissions, because
+ // the extension, not the user, removed the permissions. This allows the
+ // extension to add them again without prompting the user.
+ extension_service_->UpdateActivePermissions(extension, total.get());
+
+ NotifyPermissionsUpdated(extension, total.get(), removed.get(), REMOVED);
+}
+
+void ExtensionPermissionsManager::DispatchEvent(
+ const std::string& extension_id,
+ const char* event_name,
+ const ExtensionPermissionSet* changed_permissions) {
+ Profile* profile = extension_service_->profile();
+ if (profile && profile->GetExtensionEventRouter()) {
+ ListValue value;
+ value.Append(PackPermissionsToValue(changed_permissions));
+ std::string json_value;
+ base::JSONWriter::Write(&value, false, &json_value);
+ profile->GetExtensionEventRouter()->DispatchEventToExtension(
+ extension_id, event_name, json_value, profile, GURL());
+ }
+}
+
+void ExtensionPermissionsManager::NotifyPermissionsUpdated(
+ const Extension* extension,
+ const ExtensionPermissionSet* active,
+ const ExtensionPermissionSet* changed,
+ EventType event_type) {
+ if (!changed || changed->IsEmpty())
+ return;
+
+ UpdatedExtensionPermissionsInfo::Reason reason;
+ const char* event_name = NULL;
+
+ if (event_type == REMOVED) {
+ reason = UpdatedExtensionPermissionsInfo::REMOVED;
+ event_name = keys::kOnRemoved;
+ } else {
+ CHECK_EQ(ADDED, event_type);
+ reason = UpdatedExtensionPermissionsInfo::ADDED;
+ event_name = keys::kOnAdded;
+ }
+
+ // Notify other APIs or interested parties.
+ UpdatedExtensionPermissionsInfo info = UpdatedExtensionPermissionsInfo(
+ extension, changed, reason);
+ NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
+ Source<Profile>(extension_service_->profile()),
+ Details<UpdatedExtensionPermissionsInfo>(&info));
+
+ // Trigger the onAdded and onRemoved events in the extension.
+ DispatchEvent(extension->id(), event_name, changed);
+
+ // Send the new permissions to the renderers.
+ for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ RenderProcessHost* host = i.GetCurrentValue();
+ if (extension_service_->profile()->IsSameProfile(host->profile()))
+ host->Send(new ExtensionMsg_UpdatePermissions(
+ extension->id(),
+ active->apis(),
+ active->explicit_hosts(),
+ active->scriptable_hosts()));
+ }
+}
+
+void ExtensionPermissionsManager::RegisterWhitelist() {
+ // TODO(jstritar): This could be a field on ExtensionAPIPermission.
+ ExtensionAPIPermissionSet api_whitelist;
+ api_whitelist.insert(ExtensionAPIPermission::kClipboardRead);
+ api_whitelist.insert(ExtensionAPIPermission::kClipboardWrite);
+ api_whitelist.insert(ExtensionAPIPermission::kNotification);
+ api_whitelist.insert(ExtensionAPIPermission::kBookmark);
+ api_whitelist.insert(ExtensionAPIPermission::kContextMenus);
+ api_whitelist.insert(ExtensionAPIPermission::kCookie);
+ api_whitelist.insert(ExtensionAPIPermission::kDebugger);
+ api_whitelist.insert(ExtensionAPIPermission::kHistory);
+ api_whitelist.insert(ExtensionAPIPermission::kIdle);
+ api_whitelist.insert(ExtensionAPIPermission::kTab);
+ api_whitelist.insert(ExtensionAPIPermission::kManagement);
+ api_whitelist.insert(ExtensionAPIPermission::kBackground);
+ whitelist_ = new ExtensionPermissionSet(
+ api_whitelist, URLPatternSet(), URLPatternSet());
+}
+
+bool ContainsPermissionsFunction::RunImpl() {
+ DictionaryValue* args = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
+ std::string error;
+ if (!args)
+ return false;
+
+ scoped_refptr<ExtensionPermissionSet> permissions;
+ if (!UnpackPermissionsFromValue(args, &permissions, &bad_message_, &error_))
+ return false;
+ CHECK(permissions.get());
+
+ result_.reset(Value::CreateBooleanValue(
+ GetExtension()->GetActivePermissions()->Contains(*permissions)));
+ return true;
+}
+
+bool GetAllPermissionsFunction::RunImpl() {
+ result_.reset(PackPermissionsToValue(
+ GetExtension()->GetActivePermissions()));
+ return true;
+}
+
+bool RemovePermissionsFunction::RunImpl() {
+ DictionaryValue* args = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
+ if (!args)
+ return false;
+
+ scoped_refptr<ExtensionPermissionSet> permissions;
+ if (!UnpackPermissionsFromValue(args, &permissions, &bad_message_, &error_))
+ return false;
+ CHECK(permissions.get());
+
+ const Extension* extension = GetExtension();
+ ExtensionPermissionsManager* perms_manager =
+ profile()->GetExtensionService()->permissions_manager();
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+
+ // Make sure they're only trying to remove permissions supported by this API.
+ scoped_refptr<ExtensionPermissionSet> unsupported(
+ ExtensionPermissionSet::CreateDifference(
+ permissions.get(), &perms_manager->whitelist()));
+ if (unsupported->apis().size()) {
+ std::string api_name = info->GetByID(*unsupported->apis().begin())->name();
+ error_ = base::StringPrintf(keys::kNotWhitelistedError, api_name.c_str());
+ return false;
+ }
+
+ // Make sure we don't remove any required pemissions.
+ const ExtensionPermissionSet* required = extension->required_permission_set();
+ scoped_refptr<ExtensionPermissionSet> intersection(
+ ExtensionPermissionSet::CreateIntersection(permissions.get(), required));
+ if (!intersection->IsEmpty()) {
+ error_ = keys::kCantRemoveRequiredPermissionsError;
+ result_.reset(Value::CreateBooleanValue(false));
+ return false;
+ }
+
+ perms_manager->RemovePermissions(extension, permissions.get());
+ result_.reset(Value::CreateBooleanValue(true));
+ return true;
+}
+
+void RequestPermissionsFunction::SetAutoConfirmForTests(bool should_proceed) {
+ auto_confirm_for_tests = should_proceed ? PROCEED : ABORT;
+}
+
+bool RequestPermissionsFunction::RunImpl() {
+ DictionaryValue* args = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
+ if (!args)
+ return false;
+
+ if (!UnpackPermissionsFromValue(
+ args, &requested_permissions_, &bad_message_, &error_))
+ return false;
+ CHECK(requested_permissions_.get());
+
+ extension_ = GetExtension();
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ ExtensionPermissionsManager* perms_manager =
+ profile()->GetExtensionService()->permissions_manager();
+ ExtensionPrefs* prefs = profile()->GetExtensionService()->extension_prefs();
+
+ // Make sure only white listed permissions have been requested.
+ scoped_refptr<ExtensionPermissionSet> unsupported(
+ ExtensionPermissionSet::CreateDifference(
+ requested_permissions_.get(), &perms_manager->whitelist()));
+ if (unsupported->apis().size()) {
+ std::string api_name = info->GetByID(*unsupported->apis().begin())->name();
+ error_ = base::StringPrintf(keys::kNotWhitelistedError, api_name.c_str());
+ return false;
+ }
+
+ // The requested permissions must be defined as optional in the manifest.
+ if (!extension_->optional_permission_set()->Contains(
+ *requested_permissions_)) {
+ error_ = keys::kNotInOptionalPermissionsError;
+ result_.reset(Value::CreateBooleanValue(false));
+ return false;
+ }
+
+ // We don't need to prompt the user if the requested permissions are a subset
+ // of the granted permissions set.
+ const ExtensionPermissionSet* granted =
+ prefs->GetGrantedPermissions(extension_->id());
+ if (granted && granted->Contains(*requested_permissions_)) {
+ perms_manager->AddPermissions(extension_, requested_permissions_.get());
+ result_.reset(Value::CreateBooleanValue(true));
+ SendResponse(true);
+ return true;
+ }
+
+ // Filter out the granted permissions so we only prompt for new ones.
+ requested_permissions_ = ExtensionPermissionSet::CreateDifference(
+ requested_permissions_.get(), granted);
+
+ // Balanced with Release() in InstallUIProceed() and InstallUIAbort().
+ AddRef();
+
+ // We don't need to show the prompt if there are no new warnings, or if
+ // we're skipping the confirmation UI. All extension types but INTERNAL
+ // are allowed to silently increase their permission level.
+ if (auto_confirm_for_tests == PROCEED ||
+ requested_permissions_->GetWarningMessages().size() == 0) {
+ InstallUIProceed();
+ } else if (auto_confirm_for_tests == ABORT) {
+ // Pretend the user clicked cancel.
+ InstallUIAbort(true);
+ } else {
+ CHECK_EQ(DO_NOT_SKIP, auto_confirm_for_tests);
+ install_ui_.reset(new ExtensionInstallUI(profile()));
+ install_ui_->ConfirmPermissions(
+ this, extension_, requested_permissions_.get());
+ }
+
+ return true;
+}
+
+void RequestPermissionsFunction::InstallUIProceed() {
+ ExtensionPermissionsManager* perms_manager =
+ profile()->GetExtensionService()->permissions_manager();
+
+ install_ui_.reset();
+ result_.reset(Value::CreateBooleanValue(true));
+ perms_manager->AddPermissions(extension_, requested_permissions_.get());
+
+ SendResponse(true);
+
+ Release();
+}
+
+void RequestPermissionsFunction::InstallUIAbort(bool user_initiated) {
+ install_ui_.reset();
+ result_.reset(Value::CreateBooleanValue(false));
+ requested_permissions_ = NULL;
+
+ SendResponse(true);
+ Release();
+}
diff --git a/chrome/browser/extensions/extension_permissions_api.h b/chrome/browser/extensions/extension_permissions_api.h
new file mode 100644
index 0000000..15b0769
--- /dev/null
+++ b/chrome/browser/extensions/extension_permissions_api.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_H__
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_H__
+#pragma once
+
+#include "chrome/browser/extensions/extension_function.h"
+#include "chrome/browser/extensions/extension_install_ui.h"
+#include "chrome/common/extensions/extension_permission_set.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/common/notification_service.h"
+
+namespace base {
+class DictionaryValue;
+}
+class Extension;
+class ExtensionPermissionSet;
+class ExtensionService;
+class Profile;
+
+class ExtensionPermissionsManager {
+ public:
+ explicit ExtensionPermissionsManager(ExtensionService* extension_service);
+ ~ExtensionPermissionsManager();
+
+ // Adds the set of |permissions| to the |extension|'s active permission set
+ // and sends the relevant messages and notifications. This method assumes the
+ // user has already been prompted, if necessary, for the extra permissions.
+ void AddPermissions(const Extension* extension,
+ const ExtensionPermissionSet* permissions);
+
+ // Removes the set of |permissions| from the |extension|'s active permission
+ // set and sends the relevant messages and notifications.
+ void RemovePermissions(const Extension* extension,
+ const ExtensionPermissionSet* permissions);
+
+ // Returns the list of API permissions that are supported by the optional
+ // permissions API.
+ const ExtensionPermissionSet& whitelist() const { return *whitelist_; }
+
+ private:
+ enum EventType {
+ ADDED,
+ REMOVED,
+ };
+
+ // Dispatches specified event to the extension.
+ void DispatchEvent(const std::string& extension_id,
+ const char* event_name,
+ const ExtensionPermissionSet* changed_permissions);
+
+ // Issues the relevant events, messages and notifications when the permissions
+ // have changed for the |extension| (|changed| is the permission delta, while
+ // |active| is the new permission set). Specifically, this sends the
+ // EXTENSION_PERMISSIONS_UPDATED notification, the
+ // ExtensionMsg_UpdatePermissions IPC message, and fires the onAdded/onRemoved
+ // events in the extension.
+ void NotifyPermissionsUpdated(const Extension* extension,
+ const ExtensionPermissionSet* active,
+ const ExtensionPermissionSet* changed,
+ EventType event_type);
+
+ // Registers the list of APIs supported by the optional permissions API.
+ void RegisterWhitelist();
+
+ ExtensionService* extension_service_;
+ scoped_refptr<ExtensionPermissionSet> whitelist_;
+};
+
+
+// chrome.permissions.contains
+class ContainsPermissionsFunction : public SyncExtensionFunction {
+ virtual ~ContainsPermissionsFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.permissions.contains")
+};
+
+// chrome.permissions.getAll
+class GetAllPermissionsFunction : public SyncExtensionFunction {
+ virtual ~GetAllPermissionsFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.permissions.getAll")
+};
+
+// chrome.permissions.remove
+class RemovePermissionsFunction : public SyncExtensionFunction {
+ virtual ~RemovePermissionsFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.permissions.remove")
+};
+
+// chrome.permissions.request
+class RequestPermissionsFunction : public AsyncExtensionFunction,
+ public ExtensionInstallUI::Delegate {
+ public:
+ // FOR TESTS ONLY to bypass the confirmation UI.
+ static void SetAutoConfirmForTests(bool should_proceed);
+
+ // Implementing ExtensionInstallUI::Delegate interface.
+ virtual void InstallUIProceed() OVERRIDE;
+ virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
+
+ protected:
+ virtual ~RequestPermissionsFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+
+ private:
+ scoped_ptr<ExtensionInstallUI> install_ui_;
+ scoped_refptr<ExtensionPermissionSet> requested_permissions_;
+ const Extension* extension_;
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.permissions.request")
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_H__
diff --git a/chrome/browser/extensions/extension_permissions_api_constants.cc b/chrome/browser/extensions/extension_permissions_api_constants.cc
new file mode 100644
index 0000000..5d75ccd
--- /dev/null
+++ b/chrome/browser/extensions/extension_permissions_api_constants.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_permissions_api_constants.h"
+
+namespace extension_permissions_module_constants {
+
+const char kApisKey[] = "permissions";
+
+const char kCantRemoveRequiredPermissionsError[] =
+ "You cannot remove required permissions.";
+const char kNotInOptionalPermissionsError[] =
+ "Optional permissions must be listed in extension manifest.";
+const char kNotWhitelistedError[] =
+ "The optional permissions API does not support '%s'.";
+const char kUnknownPermissionError[] =
+ "'%s' is not a recognized permission.";
+
+const char kOnAdded[] = "experimental.permissions.onAdded";
+const char kOnRemoved[] = "experimental.permissions.onRemoved";
+
+}; // namespace extension_permissions_module_constants
diff --git a/chrome/browser/extensions/extension_permissions_api_constants.h b/chrome/browser/extensions/extension_permissions_api_constants.h
new file mode 100644
index 0000000..64b5116
--- /dev/null
+++ b/chrome/browser/extensions/extension_permissions_api_constants.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Constants used for the Tabs API and the Windows API.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_CONSTANTS_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_CONSTANTS_H_
+#pragma once
+
+namespace extension_permissions_module_constants {
+
+// Keys used in serializing permissions data and events.
+extern const char kApisKey[];
+
+// Error messages.
+extern const char kCantRemoveRequiredPermissionsError[];
+extern const char kNotInOptionalPermissionsError[];
+extern const char kNotWhitelistedError[];
+extern const char kUnknownPermissionError[];
+
+// Event names.
+extern const char kOnAdded[];
+extern const char kOnRemoved[];
+
+}; // namespace extension_permissions_module_constants
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PERMISSIONS_API_CONSTANTS_H_
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 334b64f..e0a8d6e 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -102,11 +102,15 @@ const char kBrowserActionVisible[] = "browser_action_visible";
// Preferences that hold which permissions the user has granted the extension.
// We explicitly keep track of these so that extensions can contain unknown
// permissions, for backwards compatibility reasons, and we can still prompt
-// the user to accept them once recognized.
-const char kPrefGrantedAPIs[] = "granted_permissions.api";
-const char kPrefGrantedExplicitHosts[] = "granted_permissions.explicit_host";
-const char kPrefGrantedScriptableHosts[] =
- "granted_permissions.scriptable_host";
+// the user to accept them once recognized. We store the active permission
+// permissions because they may differ from those defined in the manifest.
+const char kPrefActivePermissions[] = "active_permissions";
+const char kPrefGrantedPermissions[] = "granted_permissions";
+
+// The preference names for ExtensionPermissionSet values.
+const char kPrefAPIs[] = "api";
+const char kPrefExplicitHosts[] = "explicit_host";
+const char kPrefScriptableHosts[] = "scriptable_host";
// The preference names for the old granted permissions scheme.
const char kPrefOldGrantedFullAccess[] = "granted_permissions.full";
@@ -206,6 +210,10 @@ class ScopedExtensionControlledPrefUpdate : public DictionaryPrefUpdate {
DISALLOW_COPY_AND_ASSIGN(ScopedExtensionControlledPrefUpdate);
};
+std::string JoinPrefs(std::string parent, const char* child) {
+ return parent + "." + child;
+}
+
} // namespace
ExtensionPrefs::ExtensionPrefs(
@@ -423,6 +431,75 @@ void ExtensionPrefs::SetExtensionPrefURLPatternSet(
UpdateExtensionPref(extension_id, pref_key, value);
}
+ExtensionPermissionSet* ExtensionPrefs::ReadExtensionPrefPermissionSet(
+ const std::string& extension_id,
+ const std::string& pref_key) {
+ if (!GetExtensionPref(extension_id))
+ return NULL;
+
+ // Retrieve the API permissions.
+ ExtensionAPIPermissionSet apis;
+ const ListValue* api_values = NULL;
+ std::string api_pref = JoinPrefs(pref_key, kPrefAPIs);
+ if (ReadExtensionPrefList(extension_id, api_pref, &api_values)) {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ for (size_t i = 0; i < api_values->GetSize(); ++i) {
+ std::string permission_name;
+ if (api_values->GetString(i, &permission_name)) {
+ ExtensionAPIPermission *permission = info->GetByName(permission_name);
+ if (permission)
+ apis.insert(permission->id());
+ }
+ }
+ }
+
+ // Retrieve the explicit host permissions.
+ URLPatternSet explicit_hosts;
+ ReadExtensionPrefURLPatternSet(
+ extension_id, JoinPrefs(pref_key, kPrefExplicitHosts),
+ &explicit_hosts, Extension::kValidHostPermissionSchemes);
+
+ // Retrieve the scriptable host permissions.
+ URLPatternSet scriptable_hosts;
+ ReadExtensionPrefURLPatternSet(
+ extension_id, JoinPrefs(pref_key, kPrefScriptableHosts),
+ &scriptable_hosts, UserScript::kValidUserScriptSchemes);
+
+ return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts);
+}
+
+void ExtensionPrefs::SetExtensionPrefPermissionSet(
+ const std::string& extension_id,
+ const std::string& pref_key,
+ const ExtensionPermissionSet* new_value) {
+ // Set the API permissions.
+ ListValue* api_values = new ListValue();
+ ExtensionAPIPermissionSet apis = new_value->apis();
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ std::string api_pref = JoinPrefs(pref_key, kPrefAPIs);
+ for (ExtensionAPIPermissionSet::const_iterator i = apis.begin();
+ i != apis.end(); ++i) {
+ ExtensionAPIPermission* perm = info->GetByID(*i);
+ if (perm)
+ api_values->Append(Value::CreateStringValue(perm->name()));
+ }
+ UpdateExtensionPref(extension_id, api_pref, api_values);
+
+ // Set the explicit host permissions.
+ if (!new_value->explicit_hosts().is_empty()) {
+ SetExtensionPrefURLPatternSet(extension_id,
+ JoinPrefs(pref_key, kPrefExplicitHosts),
+ new_value->explicit_hosts());
+ }
+
+ // Set the scriptable host permissions.
+ if (!new_value->scriptable_hosts().is_empty()) {
+ SetExtensionPrefURLPatternSet(extension_id,
+ JoinPrefs(pref_key, kPrefScriptableHosts),
+ new_value->scriptable_hosts());
+ }
+}
+
void ExtensionPrefs::SavePrefs() {
prefs_->ScheduleSavePersistentPrefs();
}
@@ -638,7 +715,9 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) {
ListValue* apis = NULL;
ListValue* new_apis = NULL;
- if (ext->GetList(kPrefGrantedAPIs, &apis))
+ std::string granted_apis =
+ JoinPrefs(kPrefGrantedPermissions, kPrefAPIs);
+ if (ext->GetList(kPrefOldGrantedAPIs, &apis))
new_apis = apis->DeepCopy();
else
new_apis = new ListValue();
@@ -646,7 +725,7 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) {
std::string plugin_name = info->GetByID(
ExtensionAPIPermission::kPlugin)->name();
new_apis->Append(Value::CreateStringValue(plugin_name));
- UpdateExtensionPref(*ext_id, kPrefGrantedAPIs, new_apis);
+ UpdateExtensionPref(*ext_id, granted_apis, new_apis);
}
// The granted permissions originally only held the effective hosts,
@@ -656,9 +735,11 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) {
// new effective hosts will be the same, so we move them to explicit
// host permissions.
ListValue* hosts;
+ std::string explicit_hosts =
+ JoinPrefs(kPrefGrantedPermissions, kPrefExplicitHosts);
if (ext->GetList(kPrefOldGrantedHosts, &hosts)) {
UpdateExtensionPref(
- *ext_id, kPrefGrantedExplicitHosts, hosts->DeepCopy());
+ *ext_id, explicit_hosts, hosts->DeepCopy());
// We can get rid of the old one by setting it to an empty list.
UpdateExtensionPref(*ext_id, kPrefOldGrantedHosts, new ListValue());
@@ -669,39 +750,7 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) {
ExtensionPermissionSet* ExtensionPrefs::GetGrantedPermissions(
const std::string& extension_id) {
CHECK(Extension::IdIsValid(extension_id));
-
- const DictionaryValue* ext = GetExtensionPref(extension_id);
- if (!ext)
- return NULL;
-
- // Retrieve the API permissions.
- ExtensionAPIPermissionSet apis;
- const ListValue* api_values = NULL;
- if (ReadExtensionPrefList(extension_id, kPrefGrantedAPIs, &api_values)) {
- ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
- for (size_t i = 0; i < api_values->GetSize(); ++i) {
- std::string permission_name;
- if (api_values->GetString(i, &permission_name)) {
- ExtensionAPIPermission *permission = info->GetByName(permission_name);
- if (permission)
- apis.insert(permission->id());
- }
- }
- }
-
- // Retrieve the explicit host permissions.
- URLPatternSet explicit_hosts;
- ReadExtensionPrefURLPatternSet(
- extension_id, kPrefGrantedExplicitHosts,
- &explicit_hosts, Extension::kValidHostPermissionSchemes);
-
- // Retrieve the scriptable host permissions.
- URLPatternSet scriptable_hosts;
- ReadExtensionPrefURLPatternSet(
- extension_id, kPrefGrantedScriptableHosts,
- &scriptable_hosts, UserScript::kValidUserScriptSchemes);
-
- return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts);
+ return ReadExtensionPrefPermissionSet(extension_id, kPrefGrantedPermissions);
}
void ExtensionPrefs::AddGrantedPermissions(
@@ -709,40 +758,30 @@ void ExtensionPrefs::AddGrantedPermissions(
const ExtensionPermissionSet* permissions) {
CHECK(Extension::IdIsValid(extension_id));
- scoped_ptr<ExtensionPermissionSet> granted_permissions(
+ scoped_refptr<ExtensionPermissionSet> granted_permissions(
GetGrantedPermissions(extension_id));
// The new granted permissions are the union of the already granted
// permissions and the newly granted permissions.
- scoped_ptr<ExtensionPermissionSet> new_perms(
+ scoped_refptr<ExtensionPermissionSet> new_perms(
ExtensionPermissionSet::CreateUnion(
permissions, granted_permissions.get()));
- // Set the API permissions.
- ListValue* api_values = new ListValue();
- ExtensionAPIPermissionSet apis = new_perms->apis();
- ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
- for (ExtensionAPIPermissionSet::const_iterator i = apis.begin();
- i != apis.end(); ++i) {
- ExtensionAPIPermission* perm = info->GetByID(*i);
- if (perm)
- api_values->Append(Value::CreateStringValue(perm->name()));
- }
- UpdateExtensionPref(extension_id, kPrefGrantedAPIs, api_values);
+ SetExtensionPrefPermissionSet(
+ extension_id, kPrefGrantedPermissions, new_perms.get());
+}
- // Set the explicit host permissions.
- if (!new_perms->explicit_hosts().is_empty()) {
- SetExtensionPrefURLPatternSet(extension_id,
- kPrefGrantedExplicitHosts,
- new_perms->explicit_hosts());
- }
+ExtensionPermissionSet* ExtensionPrefs::GetActivePermissions(
+ const std::string& extension_id) {
+ CHECK(Extension::IdIsValid(extension_id));
+ return ReadExtensionPrefPermissionSet(extension_id, kPrefActivePermissions);
+}
- // Set the scriptable host permissions.
- if (!new_perms->scriptable_hosts().is_empty()) {
- SetExtensionPrefURLPatternSet(extension_id,
- kPrefGrantedScriptableHosts,
- new_perms->scriptable_hosts());
- }
+void ExtensionPrefs::SetActivePermissions(
+ const std::string& extension_id,
+ const ExtensionPermissionSet* permissions) {
+ SetExtensionPrefPermissionSet(
+ extension_id, kPrefActivePermissions, permissions);
}
bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) {
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index 6b5ffd4..92f8672 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -187,6 +187,16 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
void AddGrantedPermissions(const std::string& extension_id,
const ExtensionPermissionSet* permissions);
+ // Gets the active permission set for the specified extension. This may
+ // differ from the permissions in the manifest due to the optional
+ // permissions API. This passes ownership of the set to the caller.
+ ExtensionPermissionSet* GetActivePermissions(
+ const std::string& extension_id);
+
+ // Sets the active |permissions| for the extension with |extension_id|.
+ void SetActivePermissions(const std::string& extension_id,
+ const ExtensionPermissionSet* permissions);
+
// Returns true if the user enabled this extension to be loaded in incognito
// mode.
bool IsIncognitoEnabled(const std::string& extension_id);
@@ -409,6 +419,18 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
const std::string& pref_key,
const URLPatternSet& new_value);
+ // Interprets |pref_key| in |extension_id|'s preferences as an
+ // ExtensionPermissionSet, and passes ownership of the set to the caller.
+ ExtensionPermissionSet* ReadExtensionPrefPermissionSet(
+ const std::string& extension_id,
+ const std::string& pref_key);
+
+ // Converts the |new_value| to its value and sets the |pref_key| pref
+ // belonging to |extension_id|.
+ void SetExtensionPrefPermissionSet(const std::string& extension_id,
+ const std::string& pref_key,
+ const ExtensionPermissionSet* new_value);
+
// Returns a dictionary for extension |id|'s prefs or NULL if it doesn't
// exist.
const base::DictionaryValue* GetExtensionPref(const std::string& id) const;
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index f582077..2ed6588 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -219,32 +219,32 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest {
ExtensionAPIPermissionSet empty_set;
URLPatternSet empty_extent;
- scoped_ptr<ExtensionPermissionSet> permissions;
- scoped_ptr<ExtensionPermissionSet> granted_permissions;
+ scoped_refptr<ExtensionPermissionSet> permissions;
+ scoped_refptr<ExtensionPermissionSet> granted_permissions;
// Make sure both granted api and host permissions start empty.
- granted_permissions.reset(
- prefs()->GetGrantedPermissions(extension_id_));
+ granted_permissions =
+ prefs()->GetGrantedPermissions(extension_id_);
EXPECT_TRUE(granted_permissions->IsEmpty());
- permissions.reset(new ExtensionPermissionSet(
- api_perm_set1_, empty_extent, empty_extent));
+ permissions = new ExtensionPermissionSet(
+ api_perm_set1_, empty_extent, empty_extent);
// Add part of the api permissions.
prefs()->AddGrantedPermissions(extension_id_, permissions.get());
- granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_));
+ granted_permissions = prefs()->GetGrantedPermissions(extension_id_);
EXPECT_TRUE(granted_permissions.get());
EXPECT_FALSE(granted_permissions->IsEmpty());
EXPECT_EQ(expected_apis, granted_permissions->apis());
EXPECT_TRUE(granted_permissions->effective_hosts().is_empty());
EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess());
- granted_permissions.reset();
+ granted_permissions = NULL;
// Add part of the explicit host permissions.
- permissions.reset(new ExtensionPermissionSet(
- empty_set, ehost_perm_set1_, empty_extent));
+ permissions = new ExtensionPermissionSet(
+ empty_set, ehost_perm_set1_, empty_extent);
prefs()->AddGrantedPermissions(extension_id_, permissions.get());
- granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_));
+ granted_permissions = prefs()->GetGrantedPermissions(extension_id_);
EXPECT_FALSE(granted_permissions->IsEmpty());
EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess());
EXPECT_EQ(expected_apis, granted_permissions->apis());
@@ -254,10 +254,10 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest {
granted_permissions->effective_hosts());
// Add part of the scriptable host permissions.
- permissions.reset(new ExtensionPermissionSet(
- empty_set, empty_extent, shost_perm_set1_));
+ permissions = new ExtensionPermissionSet(
+ empty_set, empty_extent, shost_perm_set1_);
prefs()->AddGrantedPermissions(extension_id_, permissions.get());
- granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_));
+ granted_permissions = prefs()->GetGrantedPermissions(extension_id_);
EXPECT_FALSE(granted_permissions->IsEmpty());
EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess());
EXPECT_EQ(expected_apis, granted_permissions->apis());
@@ -271,15 +271,15 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest {
EXPECT_EQ(effective_permissions_, granted_permissions->effective_hosts());
// Add the rest of both the permissions.
- permissions.reset(new ExtensionPermissionSet(
- api_perm_set2_, ehost_perm_set2_, shost_perm_set2_));
+ permissions = new ExtensionPermissionSet(
+ api_perm_set2_, ehost_perm_set2_, shost_perm_set2_);
std::set_union(expected_apis.begin(), expected_apis.end(),
api_perm_set2_.begin(), api_perm_set2_.end(),
std::inserter(api_permissions_, api_permissions_.begin()));
prefs()->AddGrantedPermissions(extension_id_, permissions.get());
- granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_));
+ granted_permissions = prefs()->GetGrantedPermissions(extension_id_);
EXPECT_TRUE(granted_permissions.get());
EXPECT_FALSE(granted_permissions->IsEmpty());
EXPECT_EQ(api_permissions_, granted_permissions->apis());
@@ -294,7 +294,7 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest {
}
virtual void Verify() {
- scoped_ptr<ExtensionPermissionSet> permissions(
+ scoped_refptr<ExtensionPermissionSet> permissions(
prefs()->GetGrantedPermissions(extension_id_));
EXPECT_TRUE(permissions.get());
EXPECT_FALSE(permissions->HasEffectiveFullAccess());
@@ -321,6 +321,54 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest {
};
TEST_F(ExtensionPrefsGrantedPermissions, GrantedPermissions) {}
+// Tests the SetActivePermissions / GetActivePermissions functions.
+class ExtensionPrefsActivePermissions : public ExtensionPrefsTest {
+ public:
+ virtual void Initialize() {
+ extension_id_ = prefs_.AddExtensionAndReturnId("test");
+
+ ExtensionAPIPermissionSet api_perms;
+ api_perms.insert(ExtensionAPIPermission::kTab);
+ api_perms.insert(ExtensionAPIPermission::kBookmark);
+ api_perms.insert(ExtensionAPIPermission::kHistory);
+
+ URLPatternSet ehosts;
+ AddPattern(&ehosts, "http://*.google.com/*");
+ AddPattern(&ehosts, "http://example.com/*");
+ AddPattern(&ehosts, "chrome://favicon/*");
+
+ URLPatternSet shosts;
+ AddPattern(&shosts, "https://*.google.com/*");
+ AddPattern(&shosts, "http://reddit.com/r/test/*");
+
+ active_perms_ = new ExtensionPermissionSet(api_perms, ehosts, shosts);
+
+ // Make sure the active permissions start empty.
+ scoped_refptr<ExtensionPermissionSet> active(
+ prefs()->GetActivePermissions(extension_id_));
+ EXPECT_TRUE(active->IsEmpty());
+
+ // Set the active permissions.
+ prefs()->SetActivePermissions(extension_id_, active_perms_.get());
+ active = prefs()->GetActivePermissions(extension_id_);
+ EXPECT_EQ(active_perms_->apis(), active->apis());
+ EXPECT_EQ(active_perms_->explicit_hosts(), active->explicit_hosts());
+ EXPECT_EQ(active_perms_->scriptable_hosts(), active->scriptable_hosts());
+ EXPECT_EQ(*active_perms_, *active);
+ }
+
+ virtual void Verify() {
+ scoped_refptr<ExtensionPermissionSet> permissions(
+ prefs()->GetActivePermissions(extension_id_));
+ EXPECT_EQ(*active_perms_, *permissions);
+ }
+
+ private:
+ std::string extension_id_;
+ scoped_refptr<ExtensionPermissionSet> active_perms_;
+};
+TEST_F(ExtensionPrefsActivePermissions, SetAndGetActivePermissions) {}
+
// Tests the GetVersionString function.
class ExtensionPrefsVersionString : public ExtensionPrefsTest {
public:
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 5ff972e..72667a1 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -552,6 +552,7 @@ ExtensionService::ExtensionService(Profile* profile,
show_extensions_prompts_(true),
ready_(false),
toolbar_model_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ permissions_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
apps_promo_(profile->GetPrefs()),
event_routers_initialized_(false) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -974,11 +975,13 @@ void ExtensionService::DisableExtension(const std::string& extension_id) {
void ExtensionService::GrantPermissions(const Extension* extension) {
CHECK(extension);
- // We only maintain the granted permissions prefs for INTERNAL extensions.
- CHECK_EQ(Extension::INTERNAL, extension->location());
+ // We only maintain the granted permissions prefs for extensions that can't
+ // silently increase their permissions.
+ if (extension->CanSilentlyIncreasePermissions())
+ return;
extension_prefs_->AddGrantedPermissions(extension->id(),
- extension->permission_set());
+ extension->GetActivePermissions());
}
void ExtensionService::GrantPermissionsAndEnableExtension(
@@ -991,6 +994,13 @@ void ExtensionService::GrantPermissionsAndEnableExtension(
EnableExtension(extension->id());
}
+void ExtensionService::UpdateActivePermissions(
+ const Extension* extension,
+ const ExtensionPermissionSet* permissions) {
+ extension_prefs()->SetActivePermissions(extension->id(), permissions);
+ extension->SetActivePermissions(permissions);
+}
+
void ExtensionService::LoadExtension(const FilePath& extension_path) {
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
NewRunnableMethod(backend_.get(),
@@ -1356,7 +1366,8 @@ void ExtensionService::NotifyExtensionLoaded(const Extension* extension) {
if (host->profile()->GetOriginalProfile() ==
profile_->GetOriginalProfile()) {
host->Send(
- new ExtensionMsg_Loaded(ExtensionMsg_Loaded_Params(extension)));
+ new ExtensionMsg_Loaded(ExtensionMsg_Loaded_Params(
+ extension, extension->GetActivePermissions())));
}
}
@@ -1964,7 +1975,7 @@ void ExtensionService::AddExtension(const Extension* extension) {
// Check if the extension's privileges have changed and disable the
// extension if necessary.
- DisableIfPrivilegeIncrease(extension);
+ InitializePermissions(extension);
bool disabled = Extension::UserMayDisable(extension->location()) &&
extension_prefs_->GetExtensionState(extension->id()) ==
@@ -1985,7 +1996,35 @@ void ExtensionService::AddExtension(const Extension* extension) {
NotifyExtensionLoaded(extension);
}
-void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) {
+void ExtensionService::InitializePermissions(const Extension* extension) {
+ // If the extension has used the optional permissions API, it will have a
+ // custom set of active permissions defined in the extension prefs. Here,
+ // we update the extension's active permissions based on the prefs.
+ scoped_refptr<ExtensionPermissionSet> active_permissions =
+ extension_prefs()->GetActivePermissions(extension->id());
+
+ if (active_permissions.get()) {
+ // We restrict the active permissions to be within the bounds defined in the
+ // extension's manifest.
+ // a) active permissions must be a subset of optional + default permissions
+ // b) active permissions must contains all default permissions
+ scoped_refptr<ExtensionPermissionSet> total_permissions =
+ ExtensionPermissionSet::CreateUnion(
+ extension->required_permission_set(),
+ extension->optional_permission_set());
+
+ // Make sure the active permissions contain no more than optional + default.
+ scoped_refptr<ExtensionPermissionSet> adjusted_active =
+ ExtensionPermissionSet::CreateIntersection(
+ total_permissions.get(), active_permissions.get());
+
+ // Make sure the active permissions contain the default permissions.
+ adjusted_active = ExtensionPermissionSet::CreateUnion(
+ extension->required_permission_set(), adjusted_active.get());
+
+ UpdateActivePermissions(extension, adjusted_active);
+ }
+
// We keep track of all permissions the user has granted each extension.
// This allows extensions to gracefully support backwards compatibility
// by including unknown permissions in their manifests. When the user
@@ -2012,13 +2051,13 @@ void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) {
bool is_extension_upgrade = old != NULL;
bool is_privilege_increase = false;
- // We only record the granted permissions for INTERNAL extensions, since
- // they can't silently increase privileges.
- if (extension->location() == Extension::INTERNAL) {
+ // We only need to compare the granted permissions to the current permissions
+ // if the extension is not allowed to silently increase its permissions.
+ if (!extension->CanSilentlyIncreasePermissions()) {
// Add all the recognized permissions if the granted permissions list
// hasn't been initialized yet.
- scoped_ptr<ExtensionPermissionSet> granted_permissions(
- extension_prefs_->GetGrantedPermissions(extension->id()));
+ scoped_refptr<ExtensionPermissionSet> granted_permissions =
+ extension_prefs_->GetGrantedPermissions(extension->id());
CHECK(granted_permissions.get());
// Here, we check if an extension's privileges have increased in a manner
@@ -2026,7 +2065,8 @@ void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) {
// upgraded and recognized additional privileges, or an extension upgrades
// to a version that requires additional privileges.
is_privilege_increase =
- granted_permissions->HasLessPrivilegesThan(extension->permission_set());
+ granted_permissions->HasLessPrivilegesThan(
+ extension->GetActivePermissions());
}
if (is_extension_upgrade) {
@@ -2378,7 +2418,8 @@ void ExtensionService::Observe(int type,
// Loaded extensions.
for (size_t i = 0; i < extensions_.size(); ++i) {
process->Send(new ExtensionMsg_Loaded(
- ExtensionMsg_Loaded_Params(extensions_[i])));
+ ExtensionMsg_Loaded_Params(
+ extensions_[i], extensions_[i]->GetActivePermissions())));
}
break;
}
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 4c74f0a..901726f 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -26,6 +26,7 @@
#include "chrome/browser/extensions/extension_icon_manager.h"
#include "chrome/browser/extensions/extension_menu_manager.h"
#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/extensions/extension_permissions_api.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/extensions/extensions_quota_service.h"
@@ -320,6 +321,10 @@ class ExtensionService
// extension.
void GrantPermissionsAndEnableExtension(const Extension* extension);
+ // Sets the |extension|'s active permissions to |permissions|.
+ void UpdateActivePermissions(const Extension* extension,
+ const ExtensionPermissionSet* permissions);
+
// Loads the extension from the directory |extension_path|.
void LoadExtension(const FilePath& extension_path);
@@ -405,9 +410,9 @@ class ExtensionService
void OnExtensionInstalled(
const Extension* extension, bool from_webstore);
- // Checks if the privileges requested by |extension| have increased, and if
- // so, disables the extension and prompts the user to approve the change.
- void DisableIfPrivilegeIncrease(const Extension* extension);
+ // Initializes the |extension|'s active permission set and disables the
+ // extension if the privilege level has increased (e.g., due to an upgrade).
+ void InitializePermissions(const Extension* extension);
// Go through each extensions in pref, unload blacklisted extensions
// and update the blacklist state in pref.
@@ -468,6 +473,10 @@ class ExtensionService
return &app_notification_manager_;
}
+ ExtensionPermissionsManager* permissions_manager() {
+ return &permissions_manager_;
+ }
+
ExtensionBrowserEventRouter* browser_event_router() {
return browser_event_router_.get();
}
@@ -586,7 +595,6 @@ class ExtensionService
bool include_disabled,
bool include_terminated) const;
-
// Adds the given extension to the list of terminated extensions if
// it is not already there and unloads it.
void TrackTerminatedExtension(const Extension* extension);
@@ -708,6 +716,9 @@ class ExtensionService
// Keeps track of app notifications.
AppNotificationManager app_notification_manager_;
+ // Keeps track of extension permissions.
+ ExtensionPermissionsManager permissions_manager_;
+
// Keeps track of favicon-sized omnibox icons for extensions.
ExtensionIconManager omnibox_icon_manager_;
ExtensionIconManager omnibox_popup_icon_manager_;
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index bad22a1..c1a18d1 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -1023,7 +1023,8 @@ TEST_F(ExtensionServiceTest, LoadAllExtensionsFromDirectorySuccess) {
expected_patterns.ClearPatterns();
AddPattern(&expected_patterns, "http://*.google.com/*");
AddPattern(&expected_patterns, "https://*.google.com/*");
- EXPECT_EQ(expected_patterns, extension->permission_set()->explicit_hosts());
+ EXPECT_EQ(expected_patterns,
+ extension->GetActivePermissions()->explicit_hosts());
EXPECT_EQ(std::string(good1), loaded_[1]->id());
EXPECT_EQ(std::string("My extension 2"), loaded_[1]->name());
@@ -1364,7 +1365,7 @@ TEST_F(ExtensionServiceTest, GrantedPermissions) {
// Make sure there aren't any granted permissions before the
// extension is installed.
- scoped_ptr<ExtensionPermissionSet> known_perms(
+ scoped_refptr<ExtensionPermissionSet> known_perms(
prefs->GetGrantedPermissions(permissions_crx));
EXPECT_FALSE(known_perms.get());
@@ -1383,7 +1384,7 @@ TEST_F(ExtensionServiceTest, GrantedPermissions) {
AddPattern(&expected_host_perms, "http://*.google.com.hk/*");
AddPattern(&expected_host_perms, "http://www.example.com/*");
- known_perms.reset(prefs->GetGrantedPermissions(extension_id));
+ known_perms = prefs->GetGrantedPermissions(extension_id);
EXPECT_TRUE(known_perms.get());
EXPECT_FALSE(known_perms->IsEmpty());
EXPECT_EQ(expected_api_perms, known_perms->apis());
@@ -1412,7 +1413,7 @@ TEST_F(ExtensionServiceTest, GrantedFullAccessPermissions) {
std::string extension_id = extension->id();
ExtensionPrefs* prefs = service_->extension_prefs();
- scoped_ptr<ExtensionPermissionSet> permissions(
+ scoped_refptr<ExtensionPermissionSet> permissions(
prefs->GetGrantedPermissions(extension_id));
EXPECT_FALSE(permissions->IsEmpty());
EXPECT_TRUE(permissions->HasEffectiveFullAccess());
@@ -1477,7 +1478,7 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) {
ASSERT_TRUE(service_->IsExtensionEnabled(extension_id));
ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id));
- scoped_ptr<ExtensionPermissionSet> current_perms(
+ scoped_refptr<ExtensionPermissionSet> current_perms(
prefs->GetGrantedPermissions(extension_id));
ASSERT_TRUE(current_perms.get());
ASSERT_FALSE(current_perms->IsEmpty());
@@ -1490,7 +1491,7 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) {
// updating the browser to a version which recognizes additional host
// permissions).
host_permissions.clear();
- current_perms.reset();
+ current_perms = NULL;
host_permissions.insert("http://*.google.com/*");
host_permissions.insert("https://*.google.com/*");
@@ -1519,7 +1520,7 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) {
ASSERT_TRUE(service_->IsExtensionEnabled(extension_id));
ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id));
- current_perms.reset(prefs->GetGrantedPermissions(extension_id));
+ current_perms = prefs->GetGrantedPermissions(extension_id);
ASSERT_TRUE(current_perms.get());
ASSERT_FALSE(current_perms->IsEmpty());
ASSERT_FALSE(current_perms->HasEffectiveFullAccess());
diff --git a/chrome/browser/extensions/permissions_apitest.cc b/chrome/browser/extensions/permissions_apitest.cc
index e298a093..def0595 100644
--- a/chrome/browser/extensions/permissions_apitest.cc
+++ b/chrome/browser/extensions/permissions_apitest.cc
@@ -1,9 +1,14 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_permission_set.h"
class ExperimentalApiTest : public ExtensionApiTest {
public:
@@ -47,3 +52,35 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FaviconPermission) {
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, AlwaysAllowed) {
ASSERT_TRUE(RunExtensionTest("permissions/always_allowed")) << message_;
}
+
+// Tests that the optional permissions API works correctly.
+IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsGranted) {
+ // Mark all the tested APIs as granted to bypass the confirmation UI.
+ ExtensionAPIPermissionSet apis;
+ apis.insert(ExtensionAPIPermission::kTab);
+ apis.insert(ExtensionAPIPermission::kManagement);
+ apis.insert(ExtensionAPIPermission::kPermissions);
+ scoped_refptr<ExtensionPermissionSet> granted_permissions =
+ new ExtensionPermissionSet(apis, URLPatternSet(), URLPatternSet());
+
+ ExtensionPrefs* prefs =
+ browser()->profile()->GetExtensionService()->extension_prefs();
+ prefs->AddGrantedPermissions("kjmkgkdkpedkejedfhmfcenooemhbpbo",
+ granted_permissions);
+
+ EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_;
+}
+
+// Tests that the optional permissions API works correctly.
+IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsAutoConfirm) {
+ // Rather than setting the granted permissions, set the UI autoconfirm flag
+ // and run the same tests.
+ RequestPermissionsFunction::SetAutoConfirmForTests(true);
+ EXPECT_TRUE(RunExtensionTest("permissions/optional")) << message_;
+}
+
+// Test that denying the optional permissions confirmation dialog works.
+IN_PROC_BROWSER_TEST_F(ExperimentalApiTest, OptionalPermissionsDeny) {
+ RequestPermissionsFunction::SetAutoConfirmForTests(false);
+ EXPECT_TRUE(RunExtensionTest("permissions/optional_deny")) << message_;
+}
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h
index 8c7818d..2617a53 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h
+++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h
@@ -37,6 +37,7 @@ class Profile;
scoped_nsobject<NSString> title_;
scoped_nsobject<NSString> warnings_;
scoped_nsobject<NSString> button_;
+ scoped_nsobject<NSString> cancel_button_;
scoped_nsobject<NSString> subtitle_;
SkBitmap icon_;
}
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
index 1c8306a..0e79076f 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm
@@ -92,6 +92,9 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) {
retain]);
button_.reset([l10n_util::GetNSString(ExtensionInstallUI::kButtonIds[type])
retain]);
+ int cancel_id = ExtensionInstallUI::kAbortButtonIds[type];
+ cancel_button_.reset([l10n_util::GetNSString(
+ cancel_id > 0 ? cancel_id : IDS_CANCEL) retain]);
// We display the warnings as a simple text string, separated by newlines.
if (!warnings.empty()) {
@@ -132,6 +135,7 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) {
[titleField_ setStringValue:title_.get()];
[subtitleField_ setStringValue:subtitle_.get()];
[okButton_ setTitle:button_.get()];
+ [cancelButton_ setTitle:cancel_button_.get()];
NSImage* image = gfx::SkBitmapToNSImage(icon_);
[iconView_ setImage:image];
diff --git a/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc b/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc
index cd2e99a..a49bc0a 100644
--- a/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc
+++ b/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc
@@ -62,8 +62,11 @@ void ShowInstallDialog(GtkWindow* parent,
parent,
GTK_DIALOG_MODAL,
NULL);
- GtkWidget* close_button = gtk_dialog_add_button(GTK_DIALOG(dialog),
- GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE);
+ int cancel = ExtensionInstallUI::kAbortButtonIds[type];
+ GtkWidget* close_button = gtk_dialog_add_button(
+ GTK_DIALOG(dialog),
+ cancel > 0 ? l10n_util::GetStringUTF8(cancel).c_str(): GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CLOSE);
gtk_dialog_add_button(
GTK_DIALOG(dialog),
l10n_util::GetStringUTF8(ExtensionInstallUI::kButtonIds[type]).c_str(),
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
index 1bf6d8f..b5c8a1ee 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -275,8 +275,10 @@ std::wstring ExtensionInstallDialogView::GetDialogButtonLabel(
case MessageBoxFlags::DIALOGBUTTON_OK:
return UTF16ToWide(
l10n_util::GetStringUTF16(ExtensionInstallUI::kButtonIds[type_]));
- case MessageBoxFlags::DIALOGBUTTON_CANCEL:
- return UTF16ToWide(l10n_util::GetStringUTF16(IDS_CANCEL));
+ case MessageBoxFlags::DIALOGBUTTON_CANCEL: {
+ int id = ExtensionInstallUI::kAbortButtonIds[type_];
+ return UTF16ToWide(l10n_util::GetStringUTF16(id > 0 ? id : IDS_CANCEL));
+ }
default:
NOTREACHED();
return std::wstring();
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 8b6d0a4..56ce107 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1002,6 +1002,10 @@
'browser/extensions/extension_page_actions_module.h',
'browser/extensions/extension_page_actions_module_constants.cc',
'browser/extensions/extension_page_actions_module_constants.h',
+ 'browser/extensions/extension_permissions_api.cc',
+ 'browser/extensions/extension_permissions_api.h',
+ 'browser/extensions/extension_permissions_api_constants.cc',
+ 'browser/extensions/extension_permissions_api_constants.h',
'browser/extensions/extension_preference_api.cc',
'browser/extensions/extension_preference_api.h',
'browser/extensions/extension_preference_api_constants.cc',
diff --git a/chrome/common/chrome_notification_types.h b/chrome/common/chrome_notification_types.h
index 2472eb5..515373b 100644
--- a/chrome/common/chrome_notification_types.h
+++ b/chrome/common/chrome_notification_types.h
@@ -428,6 +428,10 @@ enum {
// details are an Extension*, and the source is a Profile*.
NOTIFICATION_EXTENSION_UPDATE_DISABLED,
+ // Sent when an extension's permissions change. The details are an
+ // UpdatedExtensionPermissionsInfo, and the source is a Profile.
+ NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
+
// Sent when an extension is about to be installed so we can (in the case of
// themes) alert the user with a loading dialog. The source is the download
// manager and the details are the download url.
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json
index f3f8559..3e032aa 100644
--- a/chrome/common/extensions/api/extension_api.json
+++ b/chrome/common/extensions/api/extension_api.json
@@ -1060,6 +1060,143 @@
]
},
{
+ "namespace": "experimental.permissions",
+ "types": [
+ {
+ "id": "Permissions",
+ "type": "object",
+ "properties": {
+ "permissions": {
+ "type": "array",
+ "items": {"type": "string"},
+ "optional": true,
+ "description": "List of named permissions (does not include hosts or origins)."
+ }
+ }
+ }
+ ],
+ "events": [
+ {
+ "name": "onAdded",
+ "type": "function",
+ "unprivileged": true,
+ "description": "Fired when the extension acquires new permissions.",
+ "parameters": [
+ {
+ "$ref": "Permissions",
+ "name": "permissions",
+ "description": "The newly acquired permissions."
+ }
+ ]
+ },
+ {
+ "name": "onRemoved",
+ "type": "function",
+ "unprivileged": true,
+ "description": "Fired when access to permissions has been removed from the extension.",
+ "parameters": [
+ {
+ "$ref": "Permissions",
+ "name": "permissions",
+ "description": "The permissions that have been removed."
+ }
+ ]
+ }
+ ],
+ "functions": [
+ {
+ "name": "getAll",
+ "type": "function",
+ "unprivileged": true,
+ "description": "Gets the extension's current set of permissions.",
+ "parameters": [
+ {
+ "name": "callback",
+ "type": "function",
+ "parameters": [
+ {
+ "name": "permissions",
+ "$ref": "Permissions",
+ "description": "The extension's active permissions."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "contains",
+ "type": "function",
+ "unprivileged": true,
+ "description": "Checks if the extension has the specified permissions.",
+ "parameters": [
+ {
+ "name": "permissions",
+ "$ref": "Permissions"
+ },
+ {
+ "name": "callback",
+ "type": "function",
+ "parameters": [
+ {
+ "name": "result",
+ "type": "boolean",
+ "description": "True if the extension has the specified permissions."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "request",
+ "type": "function",
+ "unprivileged": true,
+ "description": "Requests access to the specified permissions. These permissions must be defined in the optional_permissions field of the manifest.",
+ "parameters": [
+ {
+ "name": "permissions",
+ "$ref": "Permissions"
+ },
+ {
+ "name": "callback",
+ "type": "function",
+ "optional": true,
+ "parameters": [
+ {
+ "name": "granted",
+ "type": "boolean",
+ "description": "True if the user granted the specified permissions."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "remove",
+ "type": "function",
+ "unprivileged": true,
+ "description": "Removes access to the specified permissions.",
+ "parameters": [
+ {
+ "name": "permissions",
+ "$ref": "Permissions"
+ },
+ {
+ "name": "callback",
+ "type": "function",
+ "optional": true,
+ "parameters": [
+ {
+ "name": "removed",
+ "type": "boolean",
+ "description": "True if the permissions were removed."
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
"namespace": "tabs",
"types": [
{
diff --git a/chrome/common/extensions/docs/experimental.html b/chrome/common/extensions/docs/experimental.html
index 49aa374..8c62aec 100644
--- a/chrome/common/extensions/docs/experimental.html
+++ b/chrome/common/extensions/docs/experimental.html
@@ -328,6 +328,7 @@ on the following experimental APIs:
<a href="experimental.devtools.panels.html">experimental.devtools.panels</a></li><li>
<a href="experimental.devtools.resources.html">experimental.devtools.resources</a></li><li>
<a href="experimental.infobars.html">experimental.infobars</a></li><li>
+ <a href="experimental.permissions.html">experimental.permissions</a></li><li>
<a href="experimental.processes.html">experimental.processes</a></li><li>
<a href="experimental.sidebar.html">experimental.sidebar</a></li><li>
<a href="experimental.webNavigation.html">experimental.webNavigation</a></li><li>
diff --git a/chrome/common/extensions/docs/experimental.permissions.html b/chrome/common/extensions/docs/experimental.permissions.html
new file mode 100644
index 0000000..b167ee9
--- /dev/null
+++ b/chrome/common/extensions/docs/experimental.permissions.html
@@ -0,0 +1,1784 @@
+<!DOCTYPE html><!-- This page is a placeholder for generated extensions api doc. Note:
+ 1) The <head> information in this page is significant, should be uniform
+ across api docs and should be edited only with knowledge of the
+ templating mechanism.
+ 3) All <body>.innerHTML is genereated as an rendering step. If viewed in a
+ browser, it will be re-generated from the template, json schema and
+ authored overview content.
+ 4) The <body>.innerHTML is also generated by an offline step so that this
+ page may easily be indexed by search engines.
+--><html xmlns="http://www.w3.org/1999/xhtml"><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <link href="css/ApiRefStyles.css" rel="stylesheet" type="text/css">
+ <link href="css/print.css" rel="stylesheet" type="text/css" media="print">
+ <script type="text/javascript" src="../../../third_party/jstemplate/jstemplate_compiled.js">
+ </script>
+ <script type="text/javascript" src="js/api_page_generator.js"></script>
+ <script type="text/javascript" src="js/bootstrap.js"></script>
+ <script type="text/javascript" src="js/sidebar.js"></script>
+ <title>chrome.experimental.permissions - Google Chrome Extensions - Google Code</title></head>
+ <body> <div id="gc-container" class="labs">
+ <div id="devModeWarning">
+ You are viewing extension docs in chrome via the 'file:' scheme: are you expecting to see local changes when you refresh? You'll need run chrome with --allow-file-access-from-files.
+ </div>
+ <!-- SUBTEMPLATES: DO NOT MOVE FROM THIS LOCATION -->
+ <!-- In particular, sub-templates that recurse, must be used by allowing
+ jstemplate to make a copy of the template in this section which
+ are not operated on by way of the jsskip="true" -->
+ <div style="display:none">
+
+ <!-- VALUE -->
+ <div id="valueTemplate">
+ <dt>
+ <var>paramName</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span class="enum">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <a> Type</a>
+ </span>
+ <span>
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ <span></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd>
+ Description of this parameter from the json schema.
+ </dd>
+ <dd>
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd>
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd>
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd>
+ <div></div>
+ </dd>
+
+ </div> <!-- /VALUE -->
+
+ <div id="functionParametersTemplate">
+ <h5>Parameters</h5>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div> <!-- /SUBTEMPLATES -->
+
+ <a id="top"></a>
+ <div id="skipto">
+ <a href="#gc-pagecontent">Skip to page content</a>
+ <a href="#gc-toc">Skip to main navigation</a>
+ </div>
+ <!-- API HEADER -->
+ <table id="header" width="100%" cellspacing="0" border="0">
+ <tbody><tr>
+ <td valign="middle"><a href="http://code.google.com/"><img src="images/code_labs_logo.gif" height="43" width="161" alt="Google Code Labs" style="border:0; margin:0;"></a></td>
+ <td valign="middle" width="100%" style="padding-left:0.6em;">
+ <form action="http://www.google.com/cse" id="cse" style="margin-top:0.5em">
+ <div id="gsc-search-box">
+ <input type="hidden" name="cx" value="002967670403910741006:61_cvzfqtno">
+ <input type="hidden" name="ie" value="UTF-8">
+ <input type="text" name="q" value="" size="55">
+ <input class="gsc-search-button" type="submit" name="sa" value="Search">
+ <br>
+ <span class="greytext">e.g. "page action" or "tabs"</span>
+ </div>
+ </form>
+
+ <script type="text/javascript" src="http://www.google.com/jsapi"></script>
+ <script type="text/javascript">google.load("elements", "1", {packages: "transliteration"});</script>
+ <script type="text/javascript" src="http://www.google.com/coop/cse/t13n?form=cse&amp;t13n_langs=en"></script>
+ <script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=cse&amp;lang=en"></script>
+ </td>
+ </tr>
+ </tbody></table>
+
+ <div id="codesiteContent" class="">
+
+ <a id="gc-topnav-anchor"></a>
+ <div id="gc-topnav">
+ <h1>Google Chrome Extensions (<a href="http://code.google.com/labs/">Labs</a>)</h1>
+ <ul id="home" class="gc-topnav-tabs">
+ <li id="home_link">
+ <a href="index.html" title="Google Chrome Extensions home page">Home</a>
+ </li>
+ <li id="docs_link">
+ <a href="docs.html" title="Official Google Chrome Extensions documentation">Docs</a>
+ </li>
+ <li id="faq_link">
+ <a href="faq.html" title="Answers to frequently asked questions about Google Chrome Extensions">FAQ</a>
+ </li>
+ <li id="samples_link">
+ <a href="samples.html" title="Sample extensions (with source code)">Samples</a>
+ </li>
+ <li id="group_link">
+ <a href="http://groups.google.com/a/chromium.org/group/chromium-extensions" title="Google Chrome Extensions developer forum">Group</a>
+ </li>
+ </ul>
+ </div> <!-- end gc-topnav -->
+
+ <div class="g-section g-tpl-170">
+ <!-- SIDENAV -->
+ <div class="g-unit g-first" id="gc-toc">
+ <ul>
+ <li><a href="getstarted.html">Getting Started</a></li>
+ <li><a href="overview.html">Overview</a></li>
+ <li><a href="whats_new.html">What's New?</a></li>
+ <li><h2><a href="devguide.html">Developer's Guide</a></h2>
+ <ul>
+ <li>Browser UI
+ <ul>
+ <li><a href="browserAction.html">Browser Actions</a></li>
+ <li><a href="contextMenus.html">Context Menus</a></li>
+ <li><a href="notifications.html">Desktop Notifications</a></li>
+ <li><a href="omnibox.html">Omnibox</a></li>
+ <li><a href="options.html">Options Pages</a></li>
+ <li><a href="override.html">Override Pages</a></li>
+ <li><a href="pageAction.html">Page Actions</a></li>
+ </ul>
+ </li>
+ <li>Browser Interaction
+ <ul>
+ <li><a href="bookmarks.html">Bookmarks</a></li>
+ <li><a href="cookies.html">Cookies</a></li>
+ <li><a href="events.html">Events</a></li>
+ <li><a href="history.html">History</a></li>
+ <li><a href="management.html">Management</a></li>
+ <li><a href="tabs.html">Tabs</a></li>
+ <li><a href="windows.html">Windows</a></li>
+ </ul>
+ </li>
+ <li>Implementation
+ <ul>
+ <li><a href="a11y.html">Accessibility</a></li>
+ <li><a href="background_pages.html">Background Pages</a></li>
+ <li><a href="content_scripts.html">Content Scripts</a></li>
+ <li><a href="xhr.html">Cross-Origin XHR</a></li>
+ <li><a href="idle.html">Idle</a></li>
+ <li><a href="i18n.html">Internationalization</a></li>
+ <li><a href="messaging.html">Message Passing</a></li>
+ <li><a href="npapi.html">NPAPI Plugins</a></li>
+ </ul>
+ </li>
+ <li>Finishing
+ <ul>
+ <li><a href="hosting.html">Hosting</a></li>
+ <li><a href="external_extensions.html">Other Deployment Options</a></li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ <li><h2><a href="apps.html">Packaged Apps</a></h2></li>
+ <li><h2><a href="tutorials.html">Tutorials</a></h2>
+ <ul>
+ <li><a href="tut_debugging.html">Debugging</a></li>
+ <li><a href="tut_analytics.html">Google Analytics</a></li>
+ <li><a href="tut_oauth.html">OAuth</a></li>
+ </ul>
+ </li>
+ <li><h2>Reference</h2>
+ <ul>
+ <li>Formats
+ <ul>
+ <li><a href="manifest.html">Manifest Files</a></li>
+ <li><a href="match_patterns.html">Match Patterns</a></li>
+ </ul>
+ </li>
+ <li><a href="permission_warnings.html">Permission Warnings</a></li>
+ <li><a href="api_index.html">chrome.* APIs</a></li>
+ <li><a href="api_other.html">Other APIs</a></li>
+ </ul>
+ </li>
+ <li><h2><a href="samples.html">Samples</a></h2></li>
+ <div class="line"> </div>
+ <li><h2>More</h2>
+ <ul>
+ <li><a href="http://code.google.com/chrome/webstore/docs/index.html">Chrome Web Store</a></li>
+ <li><a href="http://code.google.com/chrome/apps/docs/developers_guide.html">Hosted Apps</a></li>
+ <li><a href="themes.html">Themes</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <script>
+ initToggles();
+ </script>
+
+ <div class="g-unit" id="gc-pagecontent">
+ <div id="pageTitle">
+ <h1 class="page_title">chrome.experimental.permissions</h1>
+ </div>
+ <!-- TABLE OF CONTENTS -->
+ <div id="toc">
+ <h2>Contents</h2>
+ <ol>
+ <li style="display: none; ">
+ <a>h2Name</a>
+ <ol>
+ <li>
+ <a>h3Name</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#apiReference">API reference: chrome.experimental.permissions</a>
+ <ol>
+ <li style="display: none; ">
+ <a href="#properties">Properties</a>
+ <ol>
+ <li>
+ <a href="#property-anchor">propertyName</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#global-methods">Methods</a>
+ <ol>
+ <li>
+ <a href="#method-contains">contains</a>
+ </li><li>
+ <a href="#method-getAll">getAll</a>
+ </li><li>
+ <a href="#method-remove">remove</a>
+ </li><li>
+ <a href="#method-request">request</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#global-events">Events</a>
+ <ol>
+ <li>
+ <a href="#event-onAdded">onAdded</a>
+ </li><li>
+ <a href="#event-onRemoved">onRemoved</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#types">Types</a>
+ <ol>
+ <li>
+ <a href="#type-Permissions">Permissions</a>
+ </li>
+ </ol>
+ </li>
+ </ol>
+ </li>
+ </ol>
+ </div>
+ <!-- /TABLE OF CONTENTS -->
+
+ <!-- Standard content lead-in for experimental API pages -->
+ <p id="classSummary">
+ For information on how to use experimental APIs, see the <a href="experimental.html">chrome.experimental.* APIs</a> page.
+ </p>
+
+ <!-- STATIC CONTENT PLACEHOLDER -->
+ <div id="static"></div>
+
+ <!-- API PAGE -->
+ <div class="apiPage">
+ <a name="apiReference"></a>
+ <h2>API reference: chrome.experimental.permissions</h2>
+
+ <!-- PROPERTIES -->
+ <div class="apiGroup" style="display: none; ">
+ <a name="properties"></a>
+ <h3 id="properties">Properties</h3>
+
+ <div>
+ <a></a>
+ <h4>getLastError</h4>
+ <div class="summary">
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.extension</span><span>lastError</span>
+ </div>
+ <div>
+ </div>
+ </div>
+
+ </div> <!-- /apiGroup -->
+
+ <!-- METHODS -->
+ <div id="methodsTemplate" class="apiGroup">
+ <a name="global-methods"></a>
+ <h3>Methods</h3>
+
+ <!-- iterates over all functions -->
+ <div class="apiItem">
+ <a name="method-contains"></a> <!-- method-anchor -->
+ <h4>contains</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.experimental.permissions.contains</span>(<span class="null"><span style="display: none; ">, </span><span>Permissions</span>
+ <var><span>permissions</span></var></span><span class="null"><span>, </span><span>function</span>
+ <var><span>callback</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Checks if the extension has the specified permissions.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>permissions</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="experimental.permissions.html#type-Permissions">Permissions</a>
+ </span>
+ <span style="display: none; ">
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ <span></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>callback</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>function</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div>
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ The callback <em>parameter</em> should specify a function
+ that looks like this:
+ </p>
+ <p style="display: none; ">
+ If you specify the <em>callback</em> parameter, it should
+ specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>boolean result</span>) <span class="subdued">{...}</span>;</pre>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>result</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>boolean</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>True if the extension has the specified permissions.</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ <!-- MIN_VERSION -->
+ <p style="display: none; ">
+ This function was added in version <b><span></span></b>.
+ If you require this function, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </p>
+ </div> <!-- /description -->
+
+ </div><div class="apiItem">
+ <a name="method-getAll"></a> <!-- method-anchor -->
+ <h4>getAll</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.experimental.permissions.getAll</span>(<span class="null"><span style="display: none; ">, </span><span>function</span>
+ <var><span>callback</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Gets the extension's current set of permissions.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>callback</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>function</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div>
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ The callback <em>parameter</em> should specify a function
+ that looks like this:
+ </p>
+ <p style="display: none; ">
+ If you specify the <em>callback</em> parameter, it should
+ specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>Permissions permissions</span>) <span class="subdued">{...}</span>;</pre>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>permissions</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="experimental.permissions.html#type-Permissions">Permissions</a>
+ </span>
+ <span style="display: none; ">
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ <span></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The extension's active permissions.</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ <!-- MIN_VERSION -->
+ <p style="display: none; ">
+ This function was added in version <b><span></span></b>.
+ If you require this function, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </p>
+ </div> <!-- /description -->
+
+ </div><div class="apiItem">
+ <a name="method-remove"></a> <!-- method-anchor -->
+ <h4>remove</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.experimental.permissions.remove</span>(<span class="null"><span style="display: none; ">, </span><span>Permissions</span>
+ <var><span>permissions</span></var></span><span class="optional"><span>, </span><span>function</span>
+ <var><span>callback</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Removes access to the specified permissions.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>permissions</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="experimental.permissions.html#type-Permissions">Permissions</a>
+ </span>
+ <span style="display: none; ">
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ <span></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>callback</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>function</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div>
+ <div>
+ <h4>Callback function</h4>
+ <p style="display: none; ">
+ The callback <em>parameter</em> should specify a function
+ that looks like this:
+ </p>
+ <p>
+ If you specify the <em>callback</em> parameter, it should
+ specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>boolean removed</span>) <span class="subdued">{...}</span>;</pre>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>removed</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>boolean</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>True if the permissions were removed.</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ <!-- MIN_VERSION -->
+ <p style="display: none; ">
+ This function was added in version <b><span></span></b>.
+ If you require this function, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </p>
+ </div> <!-- /description -->
+
+ </div><div class="apiItem">
+ <a name="method-request"></a> <!-- method-anchor -->
+ <h4>request</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.experimental.permissions.request</span>(<span class="null"><span style="display: none; ">, </span><span>Permissions</span>
+ <var><span>permissions</span></var></span><span class="optional"><span>, </span><span>function</span>
+ <var><span>callback</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Requests access to the specified permissions. These permissions must be defined in the optional_permissions field of the manifest.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>permissions</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="experimental.permissions.html#type-Permissions">Permissions</a>
+ </span>
+ <span style="display: none; ">
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ <span></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>callback</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>function</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div>
+ <div>
+ <h4>Callback function</h4>
+ <p style="display: none; ">
+ The callback <em>parameter</em> should specify a function
+ that looks like this:
+ </p>
+ <p>
+ If you specify the <em>callback</em> parameter, it should
+ specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>boolean granted</span>) <span class="subdued">{...}</span>;</pre>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>granted</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>boolean</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>True if the user granted the specified permissions.</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ <!-- MIN_VERSION -->
+ <p style="display: none; ">
+ This function was added in version <b><span></span></b>.
+ If you require this function, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </p>
+ </div> <!-- /description -->
+
+ </div> <!-- /apiItem -->
+
+ </div> <!-- /apiGroup -->
+
+ <!-- EVENTS -->
+ <div id="eventsTemplate" class="apiGroup">
+ <a name="global-events"></a>
+ <h3>Events</h3>
+ <!-- iterates over all events -->
+ <div class="apiItem">
+ <a name="event-onAdded"></a>
+ <h4>onAdded</h4>
+
+ <div class="summary">
+ <!-- Note: intentionally longer 80 columns -->
+ <span class="subdued">chrome.experimental.permissions.</span><span>onAdded</span><span class="subdued">.addListener</span>(function(<span>Permissions permissions</span>) <span class="subdued">{...}</span><span></span>));
+ </div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Fired when the extension acquires new permissions.</p>
+
+ <!-- LISTENER PARAMETERS -->
+ <div>
+ <h4>Listener parameters</h4>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>permissions</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="experimental.permissions.html#type-Permissions">Permissions</a>
+ </span>
+ <span style="display: none; ">
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ <span></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The newly acquired permissions.</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+ </div>
+
+ <!-- EXTRA PARAMETERS -->
+ <div style="display: none; ">
+ <h4>Extra parameters to addListener</h4>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+
+ <!-- LISTENER RETURN VALUE -->
+ <h4 style="display: none; ">Listener returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ </div> <!-- /description -->
+ </div><div class="apiItem">
+ <a name="event-onRemoved"></a>
+ <h4>onRemoved</h4>
+
+ <div class="summary">
+ <!-- Note: intentionally longer 80 columns -->
+ <span class="subdued">chrome.experimental.permissions.</span><span>onRemoved</span><span class="subdued">.addListener</span>(function(<span>Permissions permissions</span>) <span class="subdued">{...}</span><span></span>));
+ </div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Fired when access to permissions has been removed from the extension.</p>
+
+ <!-- LISTENER PARAMETERS -->
+ <div>
+ <h4>Listener parameters</h4>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>permissions</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="experimental.permissions.html#type-Permissions">Permissions</a>
+ </span>
+ <span style="display: none; ">
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ <span></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The permissions that have been removed.</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+ </div>
+
+ <!-- EXTRA PARAMETERS -->
+ <div style="display: none; ">
+ <h4>Extra parameters to addListener</h4>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+
+ <!-- LISTENER RETURN VALUE -->
+ <h4 style="display: none; ">Listener returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ </div> <!-- /description -->
+ </div> <!-- /apiItem -->
+
+ </div> <!-- /apiGroup -->
+
+ <!-- TYPES -->
+ <div class="apiGroup">
+ <a name="types"></a>
+ <h3 id="types">Types</h3>
+
+ <!-- iterates over all types -->
+ <div class="apiItem">
+ <a name="type-Permissions"></a>
+ <h4>Permissions</h4>
+
+ <div>
+ <dt>
+ <var style="display: none; ">paramName</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>object</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>permissions</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span>
+ array of <span><span>
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>string</span>
+ <span style="display: none; "></span>
+ </span>
+ </span></span>
+ </span>
+ <span style="display: none; ">paramType</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>List of named permissions (does not include hosts or origins).</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+
+ </div> <!-- /apiItem -->
+
+ </div> <!-- /apiGroup -->
+
+ </div> <!-- /apiPage -->
+ </div> <!-- /gc-pagecontent -->
+ </div> <!-- /g-section -->
+ </div> <!-- /codesiteContent -->
+ <div id="gc-footer" --="">
+ <div class="text">
+ <p>
+ Except as otherwise <a href="http://code.google.com/policies.html#restrictions">noted</a>,
+ the content of this page is licensed under the <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
+ Attribution 3.0 License</a>, and code samples are licensed under the
+ <a rel="license" href="http://code.google.com/google_bsd_license.html">BSD License</a>.
+ </p>
+ <p>
+ ©2011 Google
+ </p>
+
+<!-- begin analytics -->
+<script src="http://www.google-analytics.com/urchin.js" type="text/javascript"></script>
+<script src="http://www.google-analytics.com/ga.js" type="text/javascript"></script>
+
+<script type="text/javascript">
+ // chrome doc tracking
+ try {
+ var engdocs = _gat._getTracker("YT-10763712-2");
+ engdocs._trackPageview();
+ } catch(err) {}
+
+ // code.google.com site-wide tracking
+ try {
+ _uacct="UA-18071-1";
+ _uanchor=1;
+ _uff=0;
+ urchinTracker();
+ }
+ catch(e) {/* urchinTracker not available. */}
+</script>
+<!-- end analytics -->
+ </div>
+ </div> <!-- /gc-footer -->
+ </div> <!-- /gc-container -->
+</body></html>
diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json
index 862059d..56dbf21 100644
--- a/chrome/common/extensions/docs/samples.json
+++ b/chrome/common/extensions/docs/samples.json
@@ -51,6 +51,12 @@
"chrome.experimental.devtools.resources.onFinished": "experimental.devtools.resources.html#event-onFinished",
"chrome.experimental.devtools.resources.onNavigation": "experimental.devtools.resources.html#event-onNavigation",
"chrome.experimental.infobars.show": "experimental.infobars.html#method-show",
+ "chrome.experimental.permissions.contains": "experimental.permissions.html#method-contains",
+ "chrome.experimental.permissions.getAll": "experimental.permissions.html#method-getAll",
+ "chrome.experimental.permissions.onAdded": "experimental.permissions.html#event-onAdded",
+ "chrome.experimental.permissions.onRemoved": "experimental.permissions.html#event-onRemoved",
+ "chrome.experimental.permissions.remove": "experimental.permissions.html#method-remove",
+ "chrome.experimental.permissions.request": "experimental.permissions.html#method-request",
"chrome.experimental.processes.getProcessIdForTab": "experimental.processes.html#method-getProcessIdForTab",
"chrome.experimental.processes.onUpdated": "experimental.processes.html#event-onUpdated",
"chrome.experimental.sidebar.collapse": "experimental.sidebar.html#method-collapse",
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 111ed76..5488349 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -286,20 +286,6 @@ Extension::Location Extension::GetHigherPriorityLocation(
return (loc1_rank > loc2_rank ? loc1 : loc2 );
}
-ExtensionPermissionMessages Extension::GetPermissionMessages() const {
- if (IsTrustedId(id_))
- return ExtensionPermissionMessages();
- else
- return permission_set_->GetPermissionMessages();
-}
-
-std::vector<string16> Extension::GetPermissionMessageStrings() const {
- if (IsTrustedId(id_))
- return std::vector<string16>();
- else
- return permission_set_->GetWarningMessages();
-}
-
void Extension::OverrideLaunchUrl(const GURL& override_url) {
GURL new_url(override_url);
if (!new_url.is_valid()) {
@@ -1202,6 +1188,7 @@ bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest,
if (!IsBaseCrxKey(*key) &&
*key != keys::kApp &&
*key != keys::kPermissions &&
+ *key != keys::kOptionalPermissions &&
*key != keys::kOptionsPage &&
*key != keys::kBackground) {
*error = ExtensionErrorUtils::FormatErrorMessage(
@@ -1385,13 +1372,16 @@ GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
bool Extension::InitFromValue(const DictionaryValue& source, int flags,
std::string* error) {
+ base::AutoLock auto_lock(runtime_data_lock_);
// When strict error checks are enabled, make URL pattern parsing strict.
URLPattern::ParseOption parse_strictness =
(flags & STRICT_ERROR_CHECKS ? URLPattern::ERROR_ON_PORTS
: URLPattern::IGNORE_PORTS);
// Initialize permissions with an empty, default permission set.
- permission_set_.reset(new ExtensionPermissionSet());
+ runtime_data_.SetActivePermissions(new ExtensionPermissionSet());
+ optional_permission_set_ = new ExtensionPermissionSet();
+ required_permission_set_ = new ExtensionPermissionSet();
if (source.HasKey(keys::kPublicKey)) {
std::string public_key_bytes;
@@ -1911,104 +1901,30 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
}
+ // Initialize the permissions (optional).
ExtensionAPIPermissionSet api_permissions;
URLPatternSet host_permissions;
+ if (!ParsePermissions(&source,
+ keys::kPermissions,
+ flags,
+ error,
+ &api_permissions,
+ &host_permissions)) {
+ return false;
+ }
- // Initialize the permissions (optional).
- if (source.HasKey(keys::kPermissions)) {
- ListValue* permissions = NULL;
- if (!source.GetList(keys::kPermissions, &permissions)) {
- *error = ExtensionErrorUtils::FormatErrorMessage(
- errors::kInvalidPermissions, "");
- return false;
- }
-
- for (size_t i = 0; i < permissions->GetSize(); ++i) {
- std::string permission_str;
- if (!permissions->GetString(i, &permission_str)) {
- *error = ExtensionErrorUtils::FormatErrorMessage(
- errors::kInvalidPermission, base::IntToString(i));
- return false;
- }
-
- ExtensionAPIPermission* permission =
- ExtensionPermissionsInfo::GetInstance()->GetByName(permission_str);
-
- // Only COMPONENT extensions can use private APIs.
- // TODO(asargent) - We want a more general purpose mechanism for this,
- // and better error messages. (http://crbug.com/54013)
- if (!IsComponentOnlyPermission(permission)
-#ifndef NDEBUG
- && !CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kExposePrivateExtensionApi)
-#endif
- ) {
- continue;
- }
-
- if (web_extent().is_empty() || location() == Extension::COMPONENT) {
- // Check if it's a module permission. If so, enable that permission.
- if (permission != NULL) {
- // Only allow the experimental API permission if the command line
- // flag is present, or if the extension is a component of Chrome.
- if (IsDisallowedExperimentalPermission(permission->id()) &&
- location() != Extension::COMPONENT) {
- *error = errors::kExperimentalFlagRequired;
- return false;
- }
- api_permissions.insert(permission->id());
- continue;
- }
- } else {
- // Hosted apps only get access to a subset of the valid permissions.
- if (permission != NULL && permission->is_hosted_app()) {
- if (IsDisallowedExperimentalPermission(permission->id())) {
- *error = errors::kExperimentalFlagRequired;
- return false;
- }
- api_permissions.insert(permission->id());
- continue;
- }
- }
-
- // Check if it's a host pattern permission.
- URLPattern pattern = URLPattern(CanExecuteScriptEverywhere() ?
- URLPattern::SCHEME_ALL : kValidHostPermissionSchemes);
-
- URLPattern::ParseResult parse_result = pattern.Parse(permission_str,
- parse_strictness);
- if (parse_result == URLPattern::PARSE_SUCCESS) {
- if (!CanSpecifyHostPermission(pattern)) {
- *error = ExtensionErrorUtils::FormatErrorMessage(
- errors::kInvalidPermissionScheme, base::IntToString(i));
- return false;
- }
-
- // The path component is not used for host permissions, so we force it
- // to match all paths.
- pattern.SetPath("/*");
-
- if (pattern.MatchesScheme(chrome::kFileScheme) &&
- !CanExecuteScriptEverywhere()) {
- wants_file_access_ = true;
- if (!(flags & ALLOW_FILE_ACCESS))
- pattern.SetValidSchemes(
- pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
- }
-
- host_permissions.AddPattern(pattern);
- }
-
- // If it's not a host permission, then it's probably an unknown API
- // permission. Do not throw an error so extensions can retain
- // backwards compatability (http://crbug.com/42742).
- // TODO(jstritar): We can improve error messages by adding better
- // validation of API permissions here.
- // TODO(skerner): Consider showing the reason |permission_str| is not
- // a valid URL pattern if it is almost valid. For example, if it has
- // a valid scheme, and failed to parse because it has a port, show an
- // error.
- }
+ // Initialize the optional permissions (optional).
+ ExtensionAPIPermissionSet optional_api_permissions;
+ URLPatternSet optional_host_permissions;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalExtensionApis) &&
+ !ParsePermissions(&source,
+ keys::kOptionalPermissions,
+ flags,
+ error,
+ &optional_api_permissions,
+ &optional_host_permissions)) {
+ return false;
}
// Initialize background url (optional).
@@ -2394,8 +2310,12 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
return false;
}
- permission_set_.reset(
- new ExtensionPermissionSet(this, api_permissions, host_permissions));
+ runtime_data_.SetActivePermissions(new ExtensionPermissionSet(
+ this, api_permissions, host_permissions));
+ required_permission_set_ = new ExtensionPermissionSet(
+ this, api_permissions, host_permissions);
+ optional_permission_set_ = new ExtensionPermissionSet(
+ optional_api_permissions, optional_host_permissions, URLPatternSet());
// Although |source| is passed in as a const, it's still possible to modify
// it. This is dangerous since the utility process re-uses |source| after
@@ -2573,6 +2493,118 @@ GURL Extension::GetIconURL(int size,
return GetResourceURL(path);
}
+bool Extension::ParsePermissions(const DictionaryValue* source,
+ const char* key,
+ int flags,
+ std::string* error,
+ ExtensionAPIPermissionSet* api_permissions,
+ URLPatternSet* host_permissions) {
+ if (source->HasKey(key)) {
+ // When strict error checks are enabled, make URL pattern parsing strict.
+ URLPattern::ParseOption parse_strictness =
+ (flags & STRICT_ERROR_CHECKS ? URLPattern::ERROR_ON_PORTS
+ : URLPattern::IGNORE_PORTS);
+ ListValue* permissions = NULL;
+ if (!source->GetList(key, &permissions)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPermissions, "");
+ return false;
+ }
+
+ for (size_t i = 0; i < permissions->GetSize(); ++i) {
+ std::string permission_str;
+ if (!permissions->GetString(i, &permission_str)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPermission, base::IntToString(i));
+ return false;
+ }
+
+ ExtensionAPIPermission* permission =
+ ExtensionPermissionsInfo::GetInstance()->GetByName(permission_str);
+
+ // Only COMPONENT extensions can use private APIs.
+ // TODO(asargent) - We want a more general purpose mechanism for this,
+ // and better error messages. (http://crbug.com/54013)
+ if (!IsComponentOnlyPermission(permission)
+#ifndef NDEBUG
+ && !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kExposePrivateExtensionApi)
+#endif
+ ) {
+ continue;
+ }
+
+ if (web_extent().is_empty() || location() == Extension::COMPONENT) {
+ // Check if it's a module permission. If so, enable that permission.
+ if (permission != NULL) {
+ // Only allow the experimental API permission if the command line
+ // flag is present, or if the extension is a component of Chrome.
+ if (IsDisallowedExperimentalPermission(permission->id()) &&
+ location() != Extension::COMPONENT) {
+ *error = errors::kExperimentalFlagRequired;
+ return false;
+ }
+ api_permissions->insert(permission->id());
+ continue;
+ }
+ } else {
+ // Hosted apps only get access to a subset of the valid permissions.
+ if (permission != NULL && permission->is_hosted_app()) {
+ if (IsDisallowedExperimentalPermission(permission->id())) {
+ *error = errors::kExperimentalFlagRequired;
+ return false;
+ }
+ api_permissions->insert(permission->id());
+ continue;
+ }
+ }
+
+ // Check if it's a host pattern permission.
+ URLPattern pattern = URLPattern(CanExecuteScriptEverywhere() ?
+ URLPattern::SCHEME_ALL : kValidHostPermissionSchemes);
+
+ URLPattern::ParseResult parse_result = pattern.Parse(permission_str,
+ parse_strictness);
+ if (parse_result == URLPattern::PARSE_SUCCESS) {
+ if (!CanSpecifyHostPermission(pattern)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPermissionScheme, base::IntToString(i));
+ return false;
+ }
+
+ // The path component is not used for host permissions, so we force it
+ // to match all paths.
+ pattern.SetPath("/*");
+
+ if (pattern.MatchesScheme(chrome::kFileScheme) &&
+ !CanExecuteScriptEverywhere()) {
+ wants_file_access_ = true;
+ if (!(flags & ALLOW_FILE_ACCESS))
+ pattern.SetValidSchemes(
+ pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
+ }
+
+ host_permissions->AddPattern(pattern);
+ }
+
+ // If it's not a host permission, then it's probably an unknown API
+ // permission. Do not throw an error so extensions can retain
+ // backwards compatability (http://crbug.com/42742).
+ // TODO(jstritar): We can improve error messages by adding better
+ // validation of API permissions here.
+ // TODO(skerner): Consider showing the reason |permission_str| is not
+ // a valid URL pattern if it is almost valid. For example, if it has
+ // a valid scheme, and failed to parse because it has a port, show an
+ // error.
+ }
+ }
+ return true;
+}
+
+bool Extension::CanSilentlyIncreasePermissions() const {
+ return location() != INTERNAL;
+}
+
bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const {
if (!pattern.match_all_urls() &&
pattern.MatchesScheme(chrome::kChromeUIScheme)) {
@@ -2588,24 +2620,68 @@ bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const {
bool Extension::HasAPIPermission(
ExtensionAPIPermission::ID permission) const {
- return permission_set()->HasAPIPermission(permission);
+ base::AutoLock auto_lock(runtime_data_lock_);
+ return runtime_data_.GetActivePermissions()->HasAPIPermission(permission);
}
bool Extension::HasAPIPermission(
const std::string& function_name) const {
- return permission_set()->HasAccessToFunction(function_name);
+ base::AutoLock auto_lock(runtime_data_lock_);
+ return runtime_data_.GetActivePermissions()->
+ HasAccessToFunction(function_name);
}
const URLPatternSet& Extension::GetEffectiveHostPermissions() const {
- return permission_set()->effective_hosts();
+ base::AutoLock auto_lock(runtime_data_lock_);
+ return runtime_data_.GetActivePermissions()->effective_hosts();
}
bool Extension::HasHostPermission(const GURL& url) const {
+ base::AutoLock auto_lock(runtime_data_lock_);
if (url.SchemeIs(chrome::kChromeUIScheme) &&
url.host() != chrome::kChromeUIFaviconHost &&
location() != Extension::COMPONENT)
return false;
- return permission_set()->HasExplicitAccessToOrigin(url);
+ return runtime_data_.GetActivePermissions()->
+ HasExplicitAccessToOrigin(url);
+}
+
+bool Extension::HasEffectiveAccessToAllHosts() const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ return runtime_data_.GetActivePermissions()->HasEffectiveAccessToAllHosts();
+}
+
+bool Extension::HasFullPermissions() const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ return runtime_data_.GetActivePermissions()->HasEffectiveFullAccess();
+}
+
+ExtensionPermissionMessages Extension::GetPermissionMessages() const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ if (IsTrustedId(id_))
+ return ExtensionPermissionMessages();
+ else
+ return runtime_data_.GetActivePermissions()->GetPermissionMessages();
+}
+
+std::vector<string16> Extension::GetPermissionMessageStrings() const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ if (IsTrustedId(id_))
+ return std::vector<string16>();
+ else
+ return runtime_data_.GetActivePermissions()->GetWarningMessages();
+}
+
+void Extension::SetActivePermissions(
+ const ExtensionPermissionSet* permissions) const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ runtime_data_.SetActivePermissions(permissions);
+}
+
+scoped_refptr<const ExtensionPermissionSet>
+ Extension::GetActivePermissions() const {
+ base::AutoLock auto_lock(runtime_data_lock_);
+ return runtime_data_.GetActivePermissions();
}
bool Extension::IsComponentOnlyPermission(
@@ -2637,6 +2713,7 @@ bool Extension::HasMultipleUISurfaces() const {
bool Extension::CanExecuteScriptOnPage(const GURL& page_url,
const UserScript* script,
std::string* error) const {
+ base::AutoLock auto_lock(runtime_data_lock_);
// The gallery is special-cased as a restricted URL for scripting to prevent
// access to special JS bindings we expose to the gallery (and avoid things
// like extensions removing the "report abuse" link).
@@ -2661,7 +2738,8 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url,
// Otherwise, see if this extension has permission to execute script
// programmatically on pages.
- if (permission_set()->HasExplicitAccessToOrigin(page_url))
+ if (runtime_data_.GetActivePermissions()->HasExplicitAccessToOrigin(
+ page_url))
return true;
if (error) {
@@ -2672,14 +2750,6 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url,
return false;
}
-bool Extension::HasEffectiveAccessToAllHosts() const {
- return permission_set_->HasEffectiveAccessToAllHosts();
-}
-
-bool Extension::HasFullPermissions() const {
- return permission_set_->HasEffectiveFullAccess();
-}
-
bool Extension::ShowConfigureContextMenus() const {
// Don't show context menu for component extensions. We might want to show
// options for component extension button but now there is no component
@@ -2767,11 +2837,26 @@ ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest,
ExtensionInfo::~ExtensionInfo() {}
+Extension::RuntimeData::RuntimeData() {}
+Extension::RuntimeData::RuntimeData(const ExtensionPermissionSet* active)
+ : active_permissions_(active) {}
+Extension::RuntimeData::~RuntimeData() {}
+
+scoped_refptr<const ExtensionPermissionSet>
+ Extension::RuntimeData::GetActivePermissions() const {
+ return active_permissions_;
+}
+
+void Extension::RuntimeData::SetActivePermissions(
+ const ExtensionPermissionSet* active) {
+ active_permissions_ = active;
+}
+
UninstalledExtensionInfo::UninstalledExtensionInfo(
const Extension& extension)
: extension_id(extension.id()),
extension_api_permissions(
- extension.permission_set()->GetAPIsAsStrings()),
+ extension.GetActivePermissions()->GetAPIsAsStrings()),
extension_type(extension.GetType()),
update_url(extension.update_url()) {}
@@ -2784,3 +2869,11 @@ UnloadedExtensionInfo::UnloadedExtensionInfo(
: reason(reason),
already_disabled(false),
extension(extension) {}
+
+UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
+ const Extension* extension,
+ const ExtensionPermissionSet* permissions,
+ Reason reason)
+ : reason(reason),
+ extension(extension),
+ permissions(permissions) {}
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index a2ee0e7..e46cba1 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -16,6 +16,7 @@
#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_icon_set.h"
#include "chrome/common/extensions/extension_permission_set.h"
@@ -209,15 +210,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// its install source should be set to GetHigherPriorityLocation(A, B).
static Location GetHigherPriorityLocation(Location loc1, Location loc2);
- // Returns the full list of permission messages that this extension
- // should display at install time.
- ExtensionPermissionMessages GetPermissionMessages() const;
-
- // Returns the full list of permission messages that this extension
- // should display at install time. The messages are returned as strings
- // for convenience.
- std::vector<string16> GetPermissionMessageStrings() const;
-
// Icon sizes used by the extension system.
static const int kIconSizes[];
@@ -375,11 +367,25 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
static void SetScriptingWhitelist(const ScriptingWhitelist& whitelist);
static const ScriptingWhitelist* GetScriptingWhitelist();
+ // Parses the host and api permissions from the specified permission |key|
+ // in the manifest |source|.
+ bool ParsePermissions(const base::DictionaryValue* source,
+ const char* key,
+ int flags,
+ std::string* error,
+ ExtensionAPIPermissionSet* api_permissions,
+ URLPatternSet* host_permissions);
+
bool HasAPIPermission(ExtensionAPIPermission::ID permission) const;
bool HasAPIPermission(const std::string& function_name) const;
const URLPatternSet& GetEffectiveHostPermissions() const;
+ // Returns true if the extension can silently increase its permission level.
+ // Extensions that can silently increase permissions are installed through
+ // mechanisms that are implicitly trusted.
+ bool CanSilentlyIncreasePermissions() const;
+
// Whether or not the extension is allowed permission for a URL pattern from
// the manifest. http, https, and chrome://favicon/ is allowed for all
// extensions, while component extensions are allowed access to
@@ -400,6 +406,21 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// having an NPAPI plugin).
bool HasFullPermissions() const;
+ // Returns the full list of permission messages that this extension
+ // should display at install time.
+ ExtensionPermissionMessages GetPermissionMessages() const;
+
+ // Returns the full list of permission messages that this extension
+ // should display at install time. The messages are returned as strings
+ // for convenience.
+ std::vector<string16> GetPermissionMessageStrings() const;
+
+ // Sets the active |permissions|.
+ void SetActivePermissions(const ExtensionPermissionSet* permissions) const;
+
+ // Gets the extension's active permission set.
+ scoped_refptr<const ExtensionPermissionSet> GetActivePermissions() const;
+
// Whether context menu should be shown for page and browser actions.
bool ShowConfigureContextMenus() const;
@@ -494,8 +515,11 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
const GURL& options_url() const { return options_url_; }
const GURL& devtools_url() const { return devtools_url_; }
const std::vector<GURL>& toolstrips() const { return toolstrips_; }
- const ExtensionPermissionSet* permission_set() const {
- return permission_set_.get();
+ const ExtensionPermissionSet* optional_permission_set() const {
+ return optional_permission_set_.get();
+ }
+ const ExtensionPermissionSet* required_permission_set() const {
+ return required_permission_set_.get();
}
const GURL& update_url() const { return update_url_; }
const ExtensionIconSet& icons() const { return icons_; }
@@ -551,6 +575,20 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
typedef std::pair<FilePath, std::string> ImageCacheKey;
typedef std::map<ImageCacheKey, SkBitmap> ImageCache;
+ class RuntimeData {
+ public:
+ RuntimeData();
+ explicit RuntimeData(const ExtensionPermissionSet* active);
+ ~RuntimeData();
+
+ void SetActivePermissions(const ExtensionPermissionSet* active);
+ scoped_refptr<const ExtensionPermissionSet> GetActivePermissions() const;
+
+ private:
+ friend class base::RefCountedThreadSafe<RuntimeData>;
+ scoped_refptr<const ExtensionPermissionSet> active_permissions_;
+ };
+
// Normalize the path for use by the extension. On Windows, this will make
// sure the drive letter is uppercase.
static FilePath MaybeNormalizePath(const FilePath& path);
@@ -673,8 +711,15 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// Defines the set of URLs in the extension's web content.
URLPatternSet extent_;
- // The set of permissions that the extension effectively has access to.
- scoped_ptr<ExtensionPermissionSet> permission_set_;
+ // The extension runtime data.
+ mutable base::Lock runtime_data_lock_;
+ mutable RuntimeData runtime_data_;
+
+ // The set of permissions the extension can request at runtime.
+ scoped_refptr<const ExtensionPermissionSet> optional_permission_set_;
+
+ // The extension's required / default set of permissions.
+ scoped_refptr<const ExtensionPermissionSet> required_permission_set_;
// The icons for the extension.
ExtensionIconSet icons_;
@@ -870,4 +915,27 @@ struct UnloadedExtensionInfo {
UnloadedExtensionInfo(const Extension* extension, Reason reason);
};
+// The details sent for EXTENSION_PERMISSIONS_UPDATED notifications.
+struct UpdatedExtensionPermissionsInfo {
+ enum Reason {
+ ADDED, // The permissions were added to the extension.
+ REMOVED, // The permissions were removed from the extension.
+ };
+
+ Reason reason;
+
+ // The extension who's permissions have changed.
+ const Extension* extension;
+
+ // The permissions that have changed. For Reason::ADDED, this would contain
+ // only the permissions that have added, and for Reason::REMOVED, this would
+ // only contain the removed permissions.
+ const ExtensionPermissionSet* permissions;
+
+ UpdatedExtensionPermissionsInfo(
+ const Extension* extension,
+ const ExtensionPermissionSet* permissions,
+ Reason reason);
+};
+
#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_H_
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index df532ad..a0eb53f 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -50,6 +50,7 @@ const char* kNaClModulesMIMEType = "mime_type";
const char* kNaClModulesPath = "path";
const char* kOmnibox = "omnibox";
const char* kOmniboxKeyword = "omnibox.keyword";
+const char* kOptionalPermissions = "optional_permissions";
const char* kOptionsPage = "options_page";
const char* kPageAction = "page_action";
const char* kPageActionDefaultIcon = "default_icon";
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index fe2c7bf..5c980e0f 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -55,6 +55,7 @@ namespace extension_manifest_keys {
extern const char* kName;
extern const char* kOmnibox;
extern const char* kOmniboxKeyword;
+ extern const char* kOptionalPermissions;
extern const char* kOptionsPage;
extern const char* kPageAction;
extern const char* kPageActionDefaultIcon;
diff --git a/chrome/common/extensions/extension_messages.cc b/chrome/common/extensions/extension_messages.cc
index b388234..fe88896 100644
--- a/chrome/common/extensions/extension_messages.cc
+++ b/chrome/common/extensions/extension_messages.cc
@@ -24,10 +24,14 @@ ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params(
}
ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params(
- const Extension* extension)
+ const Extension* extension,
+ const ExtensionPermissionSet* active)
: manifest(new DictionaryValue()),
location(extension->location()),
path(extension->path()),
+ apis(active->apis()),
+ explicit_hosts(active->explicit_hosts()),
+ scriptable_hosts(active->scriptable_hosts()),
id(extension->id()),
creation_flags(extension->creation_flags()) {
// As we need more bits of extension data in the renderer, add more keys to
@@ -65,6 +69,11 @@ scoped_refptr<Extension>
return extension;
}
+const ExtensionPermissionSet*
+ ExtensionMsg_Loaded_Params::GetActivePermissions() const {
+ return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts);
+}
+
namespace IPC {
template <>
@@ -101,8 +110,15 @@ bool ParamTraits<URLPattern>::Read(const Message* m, void** iter,
!ReadParam(m, iter, &spec))
return false;
+ // TODO(jstritar): We don't want the URLPattern to fail parsing when the
+ // scheme is invalid. Instead, the pattern should parse but it should not
+ // match the invalid patterns. We get around this by setting the valid
+ // schemes after parsing the pattern. Update these method calls once we can
+ // ignore scheme validation with URLPattern parse options. crbug.com/90544
+ p->SetValidSchemes(URLPattern::SCHEME_ALL);
+ URLPattern::ParseResult result = p->Parse(spec, URLPattern::IGNORE_PORTS);
p->SetValidSchemes(valid_schemes);
- return URLPattern::PARSE_SUCCESS == p->Parse(spec, URLPattern::IGNORE_PORTS);
+ return URLPattern::PARSE_SUCCESS == result;
}
void ParamTraits<URLPattern>::Log(const param_type& p, std::string* l) {
@@ -116,9 +132,7 @@ void ParamTraits<URLPatternSet>::Write(Message* m, const param_type& p) {
bool ParamTraits<URLPatternSet>::Read(const Message* m, void** iter,
param_type* p) {
std::set<URLPattern> patterns;
- bool success =
- ReadParam(m, iter, &patterns);
- if (!success)
+ if (!ReadParam(m, iter, &patterns))
return false;
for (std::set<URLPattern>::iterator i = patterns.begin();
@@ -131,12 +145,35 @@ void ParamTraits<URLPatternSet>::Log(const param_type& p, std::string* l) {
LogParam(p.patterns(), l);
}
+void ParamTraits<ExtensionAPIPermission::ID>::Write(
+ Message* m, const param_type& p) {
+ WriteParam(m, static_cast<int>(p));
+}
+
+bool ParamTraits<ExtensionAPIPermission::ID>::Read(
+ const Message* m, void** iter, param_type* p) {
+ int api_id = -2;
+ if (!ReadParam(m, iter, &api_id))
+ return false;
+
+ *p = static_cast<ExtensionAPIPermission::ID>(api_id);
+ return true;
+}
+
+void ParamTraits<ExtensionAPIPermission::ID>::Log(
+ const param_type& p, std::string* l) {
+ LogParam(static_cast<int>(p), l);
+}
+
void ParamTraits<ExtensionMsg_Loaded_Params>::Write(Message* m,
const param_type& p) {
WriteParam(m, p.location);
WriteParam(m, p.path);
WriteParam(m, *(p.manifest));
WriteParam(m, p.creation_flags);
+ WriteParam(m, p.apis);
+ WriteParam(m, p.explicit_hosts);
+ WriteParam(m, p.scriptable_hosts);
}
bool ParamTraits<ExtensionMsg_Loaded_Params>::Read(const Message* m,
@@ -146,7 +183,10 @@ bool ParamTraits<ExtensionMsg_Loaded_Params>::Read(const Message* m,
return ReadParam(m, iter, &p->location) &&
ReadParam(m, iter, &p->path) &&
ReadParam(m, iter, p->manifest.get()) &&
- ReadParam(m, iter, &p->creation_flags);
+ ReadParam(m, iter, &p->creation_flags) &&
+ ReadParam(m, iter, &p->apis) &&
+ ReadParam(m, iter, &p->explicit_hosts) &&
+ ReadParam(m, iter, &p->scriptable_hosts);
}
void ParamTraits<ExtensionMsg_Loaded_Params>::Log(const param_type& p,
diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h
index 53abba6..6d41b9c 100644
--- a/chrome/common/extensions/extension_messages.h
+++ b/chrome/common/extensions/extension_messages.h
@@ -8,6 +8,7 @@
#include "base/shared_memory.h"
#include "base/values.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_permission_set.h"
#include "chrome/common/extensions/url_pattern.h"
#include "chrome/common/extensions/url_pattern_set.h"
#include "chrome/common/web_apps.h"
@@ -90,7 +91,9 @@ typedef std::map<std::string, std::string> SubstitutionMap;
struct ExtensionMsg_Loaded_Params {
ExtensionMsg_Loaded_Params();
~ExtensionMsg_Loaded_Params();
- explicit ExtensionMsg_Loaded_Params(const Extension* extension);
+ explicit ExtensionMsg_Loaded_Params(
+ const Extension* extension,
+ const ExtensionPermissionSet* active_permissions);
// A copy constructor is needed because this structure can end up getting
// copied inside the IPC machinery on gcc <= 4.2.
@@ -99,6 +102,9 @@ struct ExtensionMsg_Loaded_Params {
// Creates a new extension from the data in this object.
scoped_refptr<Extension> ConvertToExtension() const;
+ // Passes ownership to the caller.
+ const ExtensionPermissionSet* GetActivePermissions() const;
+
// The subset of the extension manifest data we send to renderers.
scoped_ptr<DictionaryValue> manifest;
@@ -109,6 +115,11 @@ struct ExtensionMsg_Loaded_Params {
// to generate the extension ID for extensions that are loaded unpacked.
FilePath path;
+ // The extension's current active permissions.
+ ExtensionAPIPermissionSet apis;
+ URLPatternSet explicit_hosts;
+ URLPatternSet scriptable_hosts;
+
// We keep this separate so that it can be used in logging.
std::string id;
@@ -135,6 +146,14 @@ struct ParamTraits<URLPatternSet> {
};
template <>
+struct ParamTraits<ExtensionAPIPermission::ID> {
+ typedef ExtensionAPIPermission::ID param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* p);
+ static void Log(const param_type& p, std::string* l);
+};
+
+template <>
struct ParamTraits<ExtensionMsg_Loaded_Params> {
typedef ExtensionMsg_Loaded_Params param_type;
static void Write(Message* m, const param_type& p);
@@ -214,6 +233,13 @@ IPC_MESSAGE_ROUTED1(ExtensionMsg_GetApplicationInfo,
IPC_MESSAGE_ROUTED1(ExtensionMsg_UpdateBrowserWindowId,
int /* id of browser window */)
+// Tell the renderer to update an extension's permission set.
+IPC_MESSAGE_CONTROL4(ExtensionMsg_UpdatePermissions,
+ std::string /* extension_id*/,
+ ExtensionAPIPermissionSet,
+ URLPatternSet,
+ URLPatternSet)
+
// Tell the renderer which type this view is.
IPC_MESSAGE_ROUTED1(ExtensionMsg_NotifyRenderViewType,
ViewType::Type /* view_type */)
diff --git a/chrome/common/extensions/extension_permission_set.cc b/chrome/common/extensions/extension_permission_set.cc
index 0fbf2cd..462e022 100644
--- a/chrome/common/extensions/extension_permission_set.cc
+++ b/chrome/common/extensions/extension_permission_set.cc
@@ -247,6 +247,9 @@ ExtensionPermissionsInfo::ExtensionPermissionsInfo()
RegisterHostedAppPermission(
ExtensionAPIPermission::kUnlimitedStorage, "unlimitedStorage", 0,
ExtensionPermissionMessage::kNone);
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kPermissions, "permissions", 0,
+ ExtensionPermissionMessage::kNone);
// Hosted app and private permissions.
RegisterPermission(
@@ -433,12 +436,62 @@ ExtensionPermissionSet::~ExtensionPermissionSet() {
}
// static
+ExtensionPermissionSet* ExtensionPermissionSet::CreateDifference(
+ const ExtensionPermissionSet* set1,
+ const ExtensionPermissionSet* set2) {
+ scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet();
+ const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1;
+ const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2;
+
+ ExtensionAPIPermissionSet apis;
+ std::set_difference(set1_safe->apis().begin(), set1_safe->apis().end(),
+ set2_safe->apis().begin(), set2_safe->apis().end(),
+ std::insert_iterator<ExtensionAPIPermissionSet>(
+ apis, apis.begin()));
+
+ URLPatternSet explicit_hosts;
+ URLPatternSet::CreateDifference(set1_safe->explicit_hosts(),
+ set2_safe->explicit_hosts(),
+ &explicit_hosts);
+
+ URLPatternSet scriptable_hosts;
+ URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(),
+ set2_safe->scriptable_hosts(),
+ &scriptable_hosts);
+ return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts);
+}
+
+// static
+ExtensionPermissionSet* ExtensionPermissionSet::CreateIntersection(
+ const ExtensionPermissionSet* set1,
+ const ExtensionPermissionSet* set2) {
+ scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet();
+ const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1;
+ const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2;
+
+ ExtensionAPIPermissionSet apis;
+ std::set_intersection(set1_safe->apis().begin(), set1_safe->apis().end(),
+ set2_safe->apis().begin(), set2_safe->apis().end(),
+ std::insert_iterator<ExtensionAPIPermissionSet>(
+ apis, apis.begin()));
+ URLPatternSet explicit_hosts;
+ URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(),
+ set2_safe->explicit_hosts(),
+ &explicit_hosts);
+
+ URLPatternSet scriptable_hosts;
+ URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(),
+ set2_safe->scriptable_hosts(),
+ &scriptable_hosts);
+ return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts);
+}
+// static
ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion(
const ExtensionPermissionSet* set1,
const ExtensionPermissionSet* set2) {
- ExtensionPermissionSet empty;
- const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? &empty : set1;
- const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? &empty : set2;
+ scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet();
+ const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? empty : set1;
+ const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? empty : set2;
ExtensionAPIPermissionSet apis;
std::set_union(set1_safe->apis().begin(), set1_safe->apis().end(),
@@ -447,10 +500,11 @@ ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion(
apis, apis.begin()));
URLPatternSet explicit_hosts;
- URLPatternSet scriptable_hosts;
URLPatternSet::CreateUnion(set1_safe->explicit_hosts(),
set2_safe->explicit_hosts(),
&explicit_hosts);
+
+ URLPatternSet scriptable_hosts;
URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(),
set2_safe->scriptable_hosts(),
&scriptable_hosts);
@@ -458,6 +512,31 @@ ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion(
return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts);
}
+bool ExtensionPermissionSet::operator==(
+ const ExtensionPermissionSet& rhs) const {
+ return apis_ == rhs.apis_ &&
+ scriptable_hosts_ == rhs.scriptable_hosts_ &&
+ explicit_hosts_ == rhs.explicit_hosts_;
+}
+
+bool ExtensionPermissionSet::Contains(const ExtensionPermissionSet& set) const {
+ // Every set includes the empty set.
+ if (set.IsEmpty())
+ return true;
+
+ if (!std::includes(apis_.begin(), apis_.end(),
+ set.apis().begin(), set.apis().end()))
+ return false;
+
+ if (!explicit_hosts().Contains(set.explicit_hosts()))
+ return false;
+
+ if (!scriptable_hosts().Contains(set.scriptable_hosts()))
+ return false;
+
+ return true;
+}
+
std::set<std::string> ExtensionPermissionSet::GetAPIsAsStrings() const {
ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
std::set<std::string> apis_str;
diff --git a/chrome/common/extensions/extension_permission_set.h b/chrome/common/extensions/extension_permission_set.h
index c9a67e9..3d4d353 100644
--- a/chrome/common/extensions/extension_permission_set.h
+++ b/chrome/common/extensions/extension_permission_set.h
@@ -13,6 +13,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/singleton.h"
+#include "base/memory/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/string16.h"
#include "chrome/common/extensions/url_pattern_set.h"
@@ -121,6 +122,7 @@ class ExtensionAPIPermission {
kWebstorePrivate,
kDevtools,
kPlugin,
+ kPermissions,
kEnumBoundary
};
@@ -266,7 +268,8 @@ class ExtensionPermissionsInfo {
// The ExtensionPermissionSet is an immutable class that encapsulates an
// extension's permissions. The class exposes set operations for combining and
// manipulating the permissions.
-class ExtensionPermissionSet {
+class ExtensionPermissionSet
+ : public base::RefCountedThreadSafe<ExtensionPermissionSet> {
public:
// Creates an empty permission set (e.g. default permissions).
ExtensionPermissionSet();
@@ -286,11 +289,26 @@ class ExtensionPermissionSet {
~ExtensionPermissionSet();
+ // Creates a new permission set equal to |set1| - |set2|, passing ownership of
+ // the new set to the caller.
+ static ExtensionPermissionSet* CreateDifference(
+ const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2);
+
+ // Creates a new permission set equal to the intersection of |set1| and
+ // |set2|, passing ownership of the new set to the caller.
+ static ExtensionPermissionSet* CreateIntersection(
+ const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2);
+
// Creates a new permission set equal to the union of |set1| and |set2|.
// Passes ownership of the new set to the caller.
static ExtensionPermissionSet* CreateUnion(
const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2);
+ bool operator==(const ExtensionPermissionSet& rhs) const;
+
+ // Returns true if |set| is a subset of this.
+ bool Contains(const ExtensionPermissionSet& set) const;
+
// Gets the API permissions in this set as a set of strings.
std::set<std::string> GetAPIsAsStrings() const;
@@ -353,6 +371,8 @@ class ExtensionPermissionSet {
FRIEND_TEST_ALL_PREFIXES(ExtensionPermissionSetTest,
HasLessHostPrivilegesThan);
+ friend class base::RefCountedThreadSafe<ExtensionPermissionSet>;
+
static std::set<std::string> GetDistinctHosts(
const URLPatternSet& host_patterns, bool include_rcd);
@@ -380,9 +400,11 @@ class ExtensionPermissionSet {
ExtensionAPIPermissionSet apis_;
// The list of hosts that can be accessed directly from the extension.
+ // TODO(jstritar): Rename to "hosts_"?
URLPatternSet explicit_hosts_;
// The list of hosts that can be scripted by content scripts.
+ // TODO(jstritar): Rename to "user_script_hosts_"?
URLPatternSet scriptable_hosts_;
// The list of hosts this effectively grants access to.
diff --git a/chrome/common/extensions/extension_permission_set_unittest.cc b/chrome/common/extensions/extension_permission_set_unittest.cc
index b1a08a3..7bba33a 100644
--- a/chrome/common/extensions/extension_permission_set_unittest.cc
+++ b/chrome/common/extensions/extension_permission_set_unittest.cc
@@ -152,6 +152,7 @@ TEST(ExtensionAPIPermissionTest, HostedAppPermissions) {
hosted_perms.insert(ExtensionAPIPermission::kNotification);
hosted_perms.insert(ExtensionAPIPermission::kUnlimitedStorage);
hosted_perms.insert(ExtensionAPIPermission::kWebstorePrivate);
+ hosted_perms.insert(ExtensionAPIPermission::kPermissions);
ExtensionAPIPermissionSet perms = info->GetAll();
size_t count = 0;
@@ -161,8 +162,8 @@ TEST(ExtensionAPIPermissionTest, HostedAppPermissions) {
EXPECT_EQ(hosted_perms.count(*i) > 0, info->GetByID(*i)->is_hosted_app());
}
- EXPECT_EQ(10u, count);
- EXPECT_EQ(10u, info->get_hosted_app_permission_count());
+ EXPECT_EQ(hosted_perms.size(), count);
+ EXPECT_EQ(hosted_perms.size(), info->get_hosted_app_permission_count());
}
TEST(ExtensionAPIPermissionTest, ComponentOnlyPermissions) {
@@ -188,17 +189,17 @@ TEST(ExtensionAPIPermissionTest, ComponentOnlyPermissions) {
TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) {
scoped_refptr<Extension> extension;
- const ExtensionPermissionSet* permissions = NULL;
+ scoped_refptr<const ExtensionPermissionSet> permissions;
extension = LoadManifest("effective_host_permissions", "empty.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().patterns().size());
EXPECT_FALSE(permissions->HasEffectiveAccessToURL(
GURL("http://www.google.com")));
EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
extension = LoadManifest("effective_host_permissions", "one_host.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
GURL("http://www.google.com")));
EXPECT_FALSE(permissions->HasEffectiveAccessToURL(
@@ -207,14 +208,14 @@ TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) {
extension = LoadManifest("effective_host_permissions",
"one_host_wildcard.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
GURL("http://foo.google.com")));
EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
extension = LoadManifest("effective_host_permissions", "two_hosts.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
GURL("http://www.google.com")));
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
@@ -223,14 +224,14 @@ TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) {
extension = LoadManifest("effective_host_permissions",
"https_not_considered.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://google.com")));
EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
extension = LoadManifest("effective_host_permissions",
"two_content_scripts.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
GURL("http://www.reddit.com")));
@@ -239,7 +240,7 @@ TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) {
EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
extension = LoadManifest("effective_host_permissions", "all_hosts.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("https://test/")));
EXPECT_TRUE(
@@ -247,14 +248,14 @@ TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) {
EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts());
extension = LoadManifest("effective_host_permissions", "all_hosts2.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
EXPECT_TRUE(
permissions->HasEffectiveAccessToURL(GURL("http://www.google.com")));
EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts());
extension = LoadManifest("effective_host_permissions", "all_hosts3.json");
- permissions = extension->permission_set();
+ permissions = extension->GetActivePermissions();
EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://test/")));
EXPECT_TRUE(
@@ -271,16 +272,17 @@ TEST(ExtensionPermissionSetTest, ExplicitAccessToOrigin) {
// The explicit host paths should get set to /*.
AddPattern(&explicit_hosts, "http://www.example.com/a/particular/path/*");
- ExtensionPermissionSet perm_set(apis, explicit_hosts, scriptable_hosts);
- ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin(
+ scoped_refptr<ExtensionPermissionSet> perm_set = new ExtensionPermissionSet(
+ apis, explicit_hosts, scriptable_hosts);
+ ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin(
GURL("http://www.google.com/")));
- ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin(
+ ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin(
GURL("http://test.google.com/")));
- ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin(
+ ASSERT_TRUE(perm_set->HasExplicitAccessToOrigin(
GURL("http://www.example.com")));
- ASSERT_TRUE(perm_set.HasEffectiveAccessToURL(
+ ASSERT_TRUE(perm_set->HasEffectiveAccessToURL(
GURL("http://www.example.com")));
- ASSERT_FALSE(perm_set.HasExplicitAccessToOrigin(
+ ASSERT_FALSE(perm_set->HasExplicitAccessToOrigin(
GURL("http://test.example.com")));
}
@@ -299,9 +301,9 @@ TEST(ExtensionPermissionSetTest, CreateUnion) {
URLPatternSet effective_hosts;
- scoped_ptr<ExtensionPermissionSet> set1;
- scoped_ptr<ExtensionPermissionSet> set2;
- scoped_ptr<ExtensionPermissionSet> union_set;
+ scoped_refptr<ExtensionPermissionSet> set1;
+ scoped_refptr<ExtensionPermissionSet> set2;
+ scoped_refptr<ExtensionPermissionSet> union_set;
// Union with an empty set.
apis1.insert(ExtensionAPIPermission::kTab);
@@ -313,11 +315,15 @@ TEST(ExtensionPermissionSetTest, CreateUnion) {
AddPattern(&expected_explicit_hosts, "http://*.google.com/*");
AddPattern(&effective_hosts, "http://*.google.com/*");
- set1.reset(new ExtensionPermissionSet(
- apis1, explicit_hosts1, scriptable_hosts1));
- set2.reset(new ExtensionPermissionSet(
- apis2, explicit_hosts2, scriptable_hosts2));
- union_set.reset(ExtensionPermissionSet::CreateUnion(set1.get(), set2.get()));
+ set1 = new ExtensionPermissionSet(apis1, explicit_hosts1, scriptable_hosts1);
+ set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2);
+ union_set = ExtensionPermissionSet::CreateUnion(set1.get(), set2.get());
+ EXPECT_TRUE(set1->Contains(*set2));
+ EXPECT_TRUE(set1->Contains(*union_set));
+ EXPECT_FALSE(set2->Contains(*set1));
+ EXPECT_FALSE(set2->Contains(*union_set));
+ EXPECT_TRUE(union_set->Contains(*set1));
+ EXPECT_TRUE(union_set->Contains(*set2));
EXPECT_FALSE(union_set->HasEffectiveFullAccess());
EXPECT_EQ(expected_apis, union_set->apis());
@@ -343,9 +349,16 @@ TEST(ExtensionPermissionSetTest, CreateUnion) {
effective_hosts.ClearPatterns();
AddPattern(&effective_hosts, "<all_urls>");
- set2.reset(new ExtensionPermissionSet(
- apis2, explicit_hosts2, scriptable_hosts2));
- union_set.reset(ExtensionPermissionSet::CreateUnion(set1.get(), set2.get()));
+ set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2);
+ union_set = ExtensionPermissionSet::CreateUnion(set1.get(), set2.get());
+
+ EXPECT_FALSE(set1->Contains(*set2));
+ EXPECT_FALSE(set1->Contains(*union_set));
+ EXPECT_FALSE(set2->Contains(*set1));
+ EXPECT_FALSE(set2->Contains(*union_set));
+ EXPECT_TRUE(union_set->Contains(*set1));
+ EXPECT_TRUE(union_set->Contains(*set2));
+
EXPECT_TRUE(union_set->HasEffectiveFullAccess());
EXPECT_TRUE(union_set->HasEffectiveAccessToAllHosts());
EXPECT_EQ(expected_apis, union_set->apis());
@@ -354,6 +367,146 @@ TEST(ExtensionPermissionSetTest, CreateUnion) {
EXPECT_EQ(effective_hosts, union_set->effective_hosts());
}
+TEST(ExtensionPermissionSetTest, CreateIntersection) {
+ ExtensionAPIPermissionSet apis1;
+ ExtensionAPIPermissionSet apis2;
+ ExtensionAPIPermissionSet expected_apis;
+
+ URLPatternSet explicit_hosts1;
+ URLPatternSet explicit_hosts2;
+ URLPatternSet expected_explicit_hosts;
+
+ URLPatternSet scriptable_hosts1;
+ URLPatternSet scriptable_hosts2;
+ URLPatternSet expected_scriptable_hosts;
+
+ URLPatternSet effective_hosts;
+
+ scoped_refptr<ExtensionPermissionSet> set1;
+ scoped_refptr<ExtensionPermissionSet> set2;
+ scoped_refptr<ExtensionPermissionSet> new_set;
+
+ // Intersection with an empty set.
+ apis1.insert(ExtensionAPIPermission::kTab);
+ apis1.insert(ExtensionAPIPermission::kBackground);
+
+ AddPattern(&explicit_hosts1, "http://*.google.com/*");
+ AddPattern(&scriptable_hosts1, "http://www.reddit.com/*");
+
+ set1 = new ExtensionPermissionSet(apis1, explicit_hosts1, scriptable_hosts1);
+ set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2);
+ new_set = ExtensionPermissionSet::CreateIntersection(set1.get(), set2.get());
+ EXPECT_TRUE(set1->Contains(*new_set));
+ EXPECT_TRUE(set2->Contains(*new_set));
+ EXPECT_TRUE(set1->Contains(*set2));
+ EXPECT_FALSE(set2->Contains(*set1));
+ EXPECT_FALSE(new_set->Contains(*set1));
+ EXPECT_TRUE(new_set->Contains(*set2));
+
+ EXPECT_TRUE(new_set->IsEmpty());
+ EXPECT_FALSE(new_set->HasEffectiveFullAccess());
+ EXPECT_EQ(expected_apis, new_set->apis());
+ EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts());
+ EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts());
+ EXPECT_EQ(expected_explicit_hosts, new_set->effective_hosts());
+
+ // Now use a real second set.
+ apis2.insert(ExtensionAPIPermission::kTab);
+ apis2.insert(ExtensionAPIPermission::kProxy);
+ apis2.insert(ExtensionAPIPermission::kClipboardWrite);
+ apis2.insert(ExtensionAPIPermission::kPlugin);
+ expected_apis.insert(ExtensionAPIPermission::kTab);
+
+ AddPattern(&explicit_hosts2, "http://*.example.com/*");
+ AddPattern(&explicit_hosts2, "http://*.google.com/*");
+ AddPattern(&scriptable_hosts2, "http://*.google.com/*");
+ AddPattern(&expected_explicit_hosts, "http://*.google.com/*");
+
+ effective_hosts.ClearPatterns();
+ AddPattern(&effective_hosts, "http://*.google.com/*");
+
+ set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2);
+ new_set = ExtensionPermissionSet::CreateIntersection(set1.get(), set2.get());
+
+ EXPECT_TRUE(set1->Contains(*new_set));
+ EXPECT_TRUE(set2->Contains(*new_set));
+ EXPECT_FALSE(set1->Contains(*set2));
+ EXPECT_FALSE(set2->Contains(*set1));
+ EXPECT_FALSE(new_set->Contains(*set1));
+ EXPECT_FALSE(new_set->Contains(*set2));
+
+ EXPECT_FALSE(new_set->HasEffectiveFullAccess());
+ EXPECT_FALSE(new_set->HasEffectiveAccessToAllHosts());
+ EXPECT_EQ(expected_apis, new_set->apis());
+ EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts());
+ EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts());
+ EXPECT_EQ(effective_hosts, new_set->effective_hosts());
+}
+
+TEST(ExtensionPermissionSetTest, CreateDifference) {
+ ExtensionAPIPermissionSet apis1;
+ ExtensionAPIPermissionSet apis2;
+ ExtensionAPIPermissionSet expected_apis;
+
+ URLPatternSet explicit_hosts1;
+ URLPatternSet explicit_hosts2;
+ URLPatternSet expected_explicit_hosts;
+
+ URLPatternSet scriptable_hosts1;
+ URLPatternSet scriptable_hosts2;
+ URLPatternSet expected_scriptable_hosts;
+
+ URLPatternSet effective_hosts;
+
+ scoped_refptr<ExtensionPermissionSet> set1;
+ scoped_refptr<ExtensionPermissionSet> set2;
+ scoped_refptr<ExtensionPermissionSet> new_set;
+
+ // Difference with an empty set.
+ apis1.insert(ExtensionAPIPermission::kTab);
+ apis1.insert(ExtensionAPIPermission::kBackground);
+
+ AddPattern(&explicit_hosts1, "http://*.google.com/*");
+ AddPattern(&scriptable_hosts1, "http://www.reddit.com/*");
+
+ set1 = new ExtensionPermissionSet(apis1, explicit_hosts1, scriptable_hosts1);
+ set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2);
+ new_set = ExtensionPermissionSet::CreateDifference(set1.get(), set2.get());
+ EXPECT_EQ(*set1, *new_set);
+
+ // Now use a real second set.
+ apis2.insert(ExtensionAPIPermission::kTab);
+ apis2.insert(ExtensionAPIPermission::kProxy);
+ apis2.insert(ExtensionAPIPermission::kClipboardWrite);
+ apis2.insert(ExtensionAPIPermission::kPlugin);
+ expected_apis.insert(ExtensionAPIPermission::kBackground);
+
+ AddPattern(&explicit_hosts2, "http://*.example.com/*");
+ AddPattern(&explicit_hosts2, "http://*.google.com/*");
+ AddPattern(&scriptable_hosts2, "http://*.google.com/*");
+ AddPattern(&expected_scriptable_hosts, "http://www.reddit.com/*");
+
+ effective_hosts.ClearPatterns();
+ AddPattern(&effective_hosts, "http://www.reddit.com/*");
+
+ set2 = new ExtensionPermissionSet(apis2, explicit_hosts2, scriptable_hosts2);
+ new_set = ExtensionPermissionSet::CreateDifference(set1.get(), set2.get());
+
+ EXPECT_TRUE(set1->Contains(*new_set));
+ EXPECT_FALSE(set2->Contains(*new_set));
+
+ EXPECT_FALSE(new_set->HasEffectiveFullAccess());
+ EXPECT_FALSE(new_set->HasEffectiveAccessToAllHosts());
+ EXPECT_EQ(expected_apis, new_set->apis());
+ EXPECT_EQ(expected_explicit_hosts, new_set->explicit_hosts());
+ EXPECT_EQ(expected_scriptable_hosts, new_set->scriptable_hosts());
+ EXPECT_EQ(effective_hosts, new_set->effective_hosts());
+
+ // |set3| = |set1| - |set2| --> |set3| intersect |set2| == empty_set
+ set1 = ExtensionPermissionSet::CreateIntersection(new_set.get(), set2.get());
+ EXPECT_TRUE(set1->IsEmpty());
+}
+
TEST(ExtensionPermissionSetTest, HasLessPrivilegesThan) {
const struct {
const char* base_name;
@@ -435,11 +588,13 @@ TEST(ExtensionPermissionSetTest, HasLessPrivilegesThan) {
if (!new_extension.get())
continue;
- const ExtensionPermissionSet* old_p = old_extension->permission_set();
- const ExtensionPermissionSet* new_p = new_extension->permission_set();
+ scoped_refptr<const ExtensionPermissionSet> old_p(
+ old_extension->GetActivePermissions());
+ scoped_refptr<const ExtensionPermissionSet> new_p(
+ new_extension->GetActivePermissions());
- EXPECT_EQ(kTests[i].expect_increase, old_p->HasLessPrivilegesThan(new_p))
- << kTests[i].base_name;
+ EXPECT_EQ(kTests[i].expect_increase,
+ old_p->HasLessPrivilegesThan(new_p)) << kTests[i].base_name;
}
}
@@ -491,6 +646,10 @@ TEST(ExtensionPermissionSetTest, PermissionMessages) {
// Warned as part of host permissions.
skip.insert(ExtensionAPIPermission::kDevtools);
+
+ // This will warn users later, when they request new permissions.
+ skip.insert(ExtensionAPIPermission::kPermissions);
+
ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
ExtensionAPIPermissionSet permissions = info->GetAll();
for (ExtensionAPIPermissionSet::const_iterator i = permissions.begin();
@@ -538,10 +697,10 @@ TEST(ExtensionPermissionSetTest, DefaultFunctionAccess) {
{ "tabs.getSelected", false},
};
- ExtensionPermissionSet permissions;
+ scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet();
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
EXPECT_EQ(kTests[i].expect_success,
- permissions.HasAccessToFunction(kTests[i].permission_name));
+ empty->HasAccessToFunction(kTests[i].permission_name));
}
}
@@ -550,7 +709,7 @@ TEST(ExtensionPermissionSetTest, GetWarningMessages_ManyHosts) {
extension = LoadManifest("permissions", "many-hosts.json");
std::vector<string16> warnings =
- extension->permission_set()->GetWarningMessages();
+ extension->GetActivePermissions()->GetWarningMessages();
ASSERT_EQ(1u, warnings.size());
EXPECT_EQ("Your data on encrypted.google.com and www.google.com",
UTF16ToUTF8(warnings[0]));
@@ -558,11 +717,11 @@ TEST(ExtensionPermissionSetTest, GetWarningMessages_ManyHosts) {
TEST(ExtensionPermissionSetTest, GetWarningMessages_Plugins) {
scoped_refptr<Extension> extension;
- scoped_ptr<ExtensionPermissionSet> permissions;
+ scoped_refptr<ExtensionPermissionSet> permissions;
extension = LoadManifest("permissions", "plugins.json");
std::vector<string16> warnings =
- extension->permission_set()->GetWarningMessages();
+ extension->GetActivePermissions()->GetWarningMessages();
// We don't parse the plugins key on Chrome OS, so it should not ask for any
// permissions.
#if defined(OS_CHROMEOS)
@@ -575,7 +734,7 @@ TEST(ExtensionPermissionSetTest, GetWarningMessages_Plugins) {
}
TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
- scoped_ptr<ExtensionPermissionSet> perm_set;
+ scoped_refptr<ExtensionPermissionSet> perm_set;
ExtensionAPIPermissionSet empty_perms;
std::set<std::string> expected;
expected.insert("www.foo.com");
@@ -594,8 +753,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path"));
explicit_hosts.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
@@ -607,8 +766,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
explicit_hosts.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
@@ -618,8 +777,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
// Add a pattern that differs only by scheme. This should be filtered out.
explicit_hosts.AddPattern(
URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path"));
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
@@ -629,8 +788,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
// Add some dupes by path.
explicit_hosts.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath"));
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
@@ -646,8 +805,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
expected.insert("monkey.www.bar.com");
expected.insert("bar.com");
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
@@ -677,8 +836,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
expected.insert("www.foo.xyzzy");
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
@@ -690,8 +849,8 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
expected.insert("*.google.com");
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
@@ -709,14 +868,14 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
expected.insert("*.google.com");
expected.insert("*.example.com");
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
}
TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_ComIsBestRcd) {
- scoped_ptr<ExtensionPermissionSet> perm_set;
+ scoped_refptr<ExtensionPermissionSet> perm_set;
ExtensionAPIPermissionSet empty_perms;
URLPatternSet explicit_hosts;
URLPatternSet scriptable_hosts;
@@ -735,13 +894,13 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_ComIsBestRcd) {
std::set<std::string> expected;
expected.insert("www.foo.com");
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) {
- scoped_ptr<ExtensionPermissionSet> perm_set;
+ scoped_refptr<ExtensionPermissionSet> perm_set;
ExtensionAPIPermissionSet empty_perms;
URLPatternSet explicit_hosts;
URLPatternSet scriptable_hosts;
@@ -759,14 +918,14 @@ TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) {
std::set<std::string> expected;
expected.insert("www.foo.net");
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
TEST(ExtensionPermissionSetTest,
GetDistinctHostsForDisplay_OrgIs3rdBestRcd) {
- scoped_ptr<ExtensionPermissionSet> perm_set;
+ scoped_refptr<ExtensionPermissionSet> perm_set;
ExtensionAPIPermissionSet empty_perms;
URLPatternSet explicit_hosts;
URLPatternSet scriptable_hosts;
@@ -783,14 +942,14 @@ TEST(ExtensionPermissionSetTest,
std::set<std::string> expected;
expected.insert("www.foo.org");
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
TEST(ExtensionPermissionSetTest,
GetDistinctHostsForDisplay_FirstInListIs4thBestRcd) {
- scoped_ptr<ExtensionPermissionSet> perm_set;
+ scoped_refptr<ExtensionPermissionSet> perm_set;
ExtensionAPIPermissionSet empty_perms;
URLPatternSet explicit_hosts;
URLPatternSet scriptable_hosts;
@@ -806,8 +965,8 @@ TEST(ExtensionPermissionSetTest,
std::set<std::string> expected;
expected.insert("www.foo.ca");
- perm_set.reset(new ExtensionPermissionSet(
- empty_perms, explicit_hosts, scriptable_hosts));
+ perm_set = new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts);
EXPECT_EQ(expected, perm_set->GetDistinctHostsForDisplay());
}
@@ -816,8 +975,8 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) {
URLPatternSet elist2;
URLPatternSet slist1;
URLPatternSet slist2;
- scoped_ptr<ExtensionPermissionSet> set1;
- scoped_ptr<ExtensionPermissionSet> set2;
+ scoped_refptr<ExtensionPermissionSet> set1;
+ scoped_refptr<ExtensionPermissionSet> set2;
ExtensionAPIPermissionSet empty_perms;
elist1.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
@@ -830,8 +989,8 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) {
elist2.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
- set1.reset(new ExtensionPermissionSet(empty_perms, elist1, slist1));
- set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ set1 = new ExtensionPermissionSet(empty_perms, elist1, slist1);
+ set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2);
EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get()));
EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
@@ -840,7 +999,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) {
elist2.ClearPatterns();
elist2.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*"));
- set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2);
EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get()));
EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
@@ -848,7 +1007,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) {
elist2.ClearPatterns();
elist2.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*"));
- set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2);
EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get()));
EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
@@ -856,7 +1015,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) {
elist2.ClearPatterns();
elist2.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*"));
- set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2);
EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get()));
//TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337
//EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
@@ -867,7 +1026,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) {
URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
elist2.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path"));
- set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2);
EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get()));
EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
@@ -875,7 +1034,7 @@ TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) {
elist2.ClearPatterns();
elist2.AddPattern(
URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*"));
- set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ set2 = new ExtensionPermissionSet(empty_perms, elist2, slist2);
EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get()));
EXPECT_TRUE(set2->HasLessHostPrivilegesThan(set1.get()));
}
@@ -889,8 +1048,9 @@ TEST(ExtensionPermissionSetTest, GetAPIsAsStrings) {
apis.insert(ExtensionAPIPermission::kNotification);
apis.insert(ExtensionAPIPermission::kTab);
- ExtensionPermissionSet perm_set(apis, empty_set, empty_set);
- std::set<std::string> api_names = perm_set.GetAPIsAsStrings();
+ scoped_refptr<ExtensionPermissionSet> perm_set = new ExtensionPermissionSet(
+ apis, empty_set, empty_set);
+ std::set<std::string> api_names = perm_set->GetAPIsAsStrings();
// The result is correct if it has the same number of elements
// and we can convert it back to the id set.
@@ -903,27 +1063,28 @@ TEST(ExtensionPermissionSetTest, IsEmpty) {
ExtensionAPIPermissionSet empty_apis;
URLPatternSet empty_extent;
- ExtensionPermissionSet perm_set;
- EXPECT_TRUE(perm_set.IsEmpty());
+ scoped_refptr<ExtensionPermissionSet> empty = new ExtensionPermissionSet();
+ EXPECT_TRUE(empty->IsEmpty());
+ scoped_refptr<ExtensionPermissionSet> perm_set;
- perm_set = ExtensionPermissionSet(empty_apis, empty_extent, empty_extent);
- EXPECT_TRUE(perm_set.IsEmpty());
+ perm_set = new ExtensionPermissionSet(empty_apis, empty_extent, empty_extent);
+ EXPECT_TRUE(perm_set->IsEmpty());
ExtensionAPIPermissionSet non_empty_apis;
non_empty_apis.insert(ExtensionAPIPermission::kBackground);
- perm_set = ExtensionPermissionSet(
+ perm_set = new ExtensionPermissionSet(
non_empty_apis, empty_extent, empty_extent);
- EXPECT_FALSE(perm_set.IsEmpty());
+ EXPECT_FALSE(perm_set->IsEmpty());
// Try non standard host
URLPatternSet non_empty_extent;
AddPattern(&non_empty_extent, "http://www.google.com/*");
- perm_set = ExtensionPermissionSet(
+ perm_set = new ExtensionPermissionSet(
empty_apis, non_empty_extent, empty_extent);
- EXPECT_FALSE(perm_set.IsEmpty());
+ EXPECT_FALSE(perm_set->IsEmpty());
- perm_set = ExtensionPermissionSet(
+ perm_set = new ExtensionPermissionSet(
empty_apis, empty_extent, non_empty_extent);
- EXPECT_FALSE(perm_set.IsEmpty());
+ EXPECT_FALSE(perm_set->IsEmpty());
}
diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc
index 79cad1a..5115573 100644
--- a/chrome/common/extensions/extension_unittest.cc
+++ b/chrome/common/extensions/extension_unittest.cc
@@ -389,10 +389,10 @@ TEST(ExtensionTest, EffectiveHostPermissions) {
hosts = extension->GetEffectiveHostPermissions();
EXPECT_TRUE(hosts.MatchesURL(GURL("http://google.com")));
EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.reddit.com")));
- EXPECT_TRUE(extension->permission_set()->HasEffectiveAccessToURL(
+ EXPECT_TRUE(extension->GetActivePermissions()->HasEffectiveAccessToURL(
GURL("http://www.reddit.com")));
EXPECT_TRUE(hosts.MatchesURL(GURL("http://news.ycombinator.com")));
- EXPECT_TRUE(extension->permission_set()->HasEffectiveAccessToURL(
+ EXPECT_TRUE(extension->GetActivePermissions()->HasEffectiveAccessToURL(
GURL("http://news.ycombinator.com")));
EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
diff --git a/chrome/common/extensions/url_pattern_set.cc b/chrome/common/extensions/url_pattern_set.cc
index 3c8f52c..cf2b9a6 100644
--- a/chrome/common/extensions/url_pattern_set.cc
+++ b/chrome/common/extensions/url_pattern_set.cc
@@ -10,6 +10,29 @@
#include "chrome/common/extensions/url_pattern.h"
#include "googleurl/src/gurl.h"
+
+// static
+void URLPatternSet::CreateDifference(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out) {
+ out->ClearPatterns();
+ std::set_difference(set1.patterns_.begin(), set1.patterns_.end(),
+ set2.patterns_.begin(), set2.patterns_.end(),
+ std::inserter<std::set<URLPattern> >(
+ out->patterns_, out->patterns_.begin()));
+}
+
+// static
+void URLPatternSet::CreateIntersection(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out) {
+ out->ClearPatterns();
+ std::set_intersection(set1.patterns_.begin(), set1.patterns_.end(),
+ set2.patterns_.begin(), set2.patterns_.end(),
+ std::inserter<std::set<URLPattern> >(
+ out->patterns_, out->patterns_.begin()));
+}
+
// static
void URLPatternSet::CreateUnion(const URLPatternSet& set1,
const URLPatternSet& set2,
@@ -52,6 +75,11 @@ void URLPatternSet::ClearPatterns() {
patterns_.clear();
}
+bool URLPatternSet::Contains(const URLPatternSet& set) const {
+ return std::includes(patterns_.begin(), patterns_.end(),
+ set.patterns_.begin(), set.patterns_.end());
+}
+
bool URLPatternSet::MatchesURL(const GURL& url) const {
for (URLPatternSet::const_iterator pattern = patterns_.begin();
pattern != patterns_.end(); ++pattern) {
diff --git a/chrome/common/extensions/url_pattern_set.h b/chrome/common/extensions/url_pattern_set.h
index 1848e74..d26321c 100644
--- a/chrome/common/extensions/url_pattern_set.h
+++ b/chrome/common/extensions/url_pattern_set.h
@@ -18,6 +18,17 @@ class URLPatternSet {
typedef std::set<URLPattern>::const_iterator const_iterator;
typedef std::set<URLPattern>::iterator iterator;
+ // Clears |out| and populates the set with |set1| - |set2|.
+ static void CreateDifference(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out);
+
+ // Clears |out| and populates the set with the intersection of |set1|
+ // and |set2|.
+ static void CreateIntersection(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out);
+
// Clears |out| and populates the set with the union of |set1| and |set2|.
static void CreateUnion(const URLPatternSet& set1,
const URLPatternSet& set2,
@@ -39,6 +50,9 @@ class URLPatternSet {
void AddPattern(const URLPattern& pattern);
void ClearPatterns();
+ // Returns true if the permission |set| is a subset of this.
+ bool Contains(const URLPatternSet& set) const;
+
// Test if the extent contains a URL.
bool MatchesURL(const GURL& url) const;
diff --git a/chrome/common/extensions/url_pattern_set_unittest.cc b/chrome/common/extensions/url_pattern_set_unittest.cc
index f9717ef..ff78d90 100644
--- a/chrome/common/extensions/url_pattern_set_unittest.cc
+++ b/chrome/common/extensions/url_pattern_set_unittest.cc
@@ -7,88 +7,195 @@
#include "googleurl/src/gurl.h"
#include "testing/gtest/include/gtest/gtest.h"
-static const int kAllSchemes =
- URLPattern::SCHEME_HTTP |
- URLPattern::SCHEME_HTTPS |
- URLPattern::SCHEME_FILE |
- URLPattern::SCHEME_FTP |
- URLPattern::SCHEME_CHROMEUI;
+namespace {
+static void AddPattern(URLPatternSet* set, const std::string& pattern) {
+ int schemes = URLPattern::SCHEME_ALL;
+ set->AddPattern(URLPattern(schemes, pattern));
+}
+
+}
TEST(URLPatternSetTest, Empty) {
- URLPatternSet extent;
- EXPECT_FALSE(extent.MatchesURL(GURL("http://www.foo.com/bar")));
- EXPECT_FALSE(extent.MatchesURL(GURL()));
- EXPECT_FALSE(extent.MatchesURL(GURL("invalid")));
+ URLPatternSet set;
+ EXPECT_FALSE(set.MatchesURL(GURL("http://www.foo.com/bar")));
+ EXPECT_FALSE(set.MatchesURL(GURL()));
+ EXPECT_FALSE(set.MatchesURL(GURL("invalid")));
}
TEST(URLPatternSetTest, One) {
- URLPatternSet extent;
- extent.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/*"));
+ URLPatternSet set;
+ AddPattern(&set, "http://www.google.com/*");
- EXPECT_TRUE(extent.MatchesURL(GURL("http://www.google.com/")));
- EXPECT_TRUE(extent.MatchesURL(GURL("http://www.google.com/monkey")));
- EXPECT_FALSE(extent.MatchesURL(GURL("https://www.google.com/")));
- EXPECT_FALSE(extent.MatchesURL(GURL("https://www.microsoft.com/")));
+ EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/")));
+ EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/monkey")));
+ EXPECT_FALSE(set.MatchesURL(GURL("https://www.google.com/")));
+ EXPECT_FALSE(set.MatchesURL(GURL("https://www.microsoft.com/")));
}
TEST(URLPatternSetTest, Two) {
- URLPatternSet extent;
- extent.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/*"));
- extent.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/*"));
+ URLPatternSet set;
+ AddPattern(&set, "http://www.google.com/*");
+ AddPattern(&set, "http://www.yahoo.com/*");
- EXPECT_TRUE(extent.MatchesURL(GURL("http://www.google.com/monkey")));
- EXPECT_TRUE(extent.MatchesURL(GURL("http://www.yahoo.com/monkey")));
- EXPECT_FALSE(extent.MatchesURL(GURL("https://www.apple.com/monkey")));
+ EXPECT_TRUE(set.MatchesURL(GURL("http://www.google.com/monkey")));
+ EXPECT_TRUE(set.MatchesURL(GURL("http://www.yahoo.com/monkey")));
+ EXPECT_FALSE(set.MatchesURL(GURL("https://www.apple.com/monkey")));
}
TEST(URLPatternSetTest, OverlapsWith) {
- URLPatternSet extent1;
- extent1.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
- extent1.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*"));
+ URLPatternSet set1;
+ AddPattern(&set1, "http://www.google.com/f*");
+ AddPattern(&set1, "http://www.yahoo.com/b*");
+
+ URLPatternSet set2;
+ AddPattern(&set2, "http://www.reddit.com/f*");
+ AddPattern(&set2, "http://www.yahoo.com/z*");
+
+ URLPatternSet set3;
+ AddPattern(&set3, "http://www.google.com/q/*");
+ AddPattern(&set3, "http://www.yahoo.com/b/*");
+
+ EXPECT_FALSE(set1.OverlapsWith(set2));
+ EXPECT_FALSE(set2.OverlapsWith(set1));
+
+ EXPECT_TRUE(set1.OverlapsWith(set3));
+ EXPECT_TRUE(set3.OverlapsWith(set1));
+}
+
+TEST(URLPatternSetTest, CreateDifference) {
+ URLPatternSet expected;
+ URLPatternSet set1;
+ URLPatternSet set2;
+ AddPattern(&set1, "http://www.google.com/f*");
+ AddPattern(&set1, "http://www.yahoo.com/b*");
- URLPatternSet extent2;
- extent2.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*"));
- extent2.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*"));
+ // Subtract an empty set.
+ URLPatternSet result;
+ URLPatternSet::CreateDifference(set1, set2, &result);
+ EXPECT_EQ(set1, result);
- URLPatternSet extent3;
- extent3.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/q/*"));
- extent3.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b/*"));
+ // Subtract a real set.
+ AddPattern(&set2, "http://www.reddit.com/f*");
+ AddPattern(&set2, "http://www.yahoo.com/z*");
+ AddPattern(&set2, "http://www.google.com/f*");
- EXPECT_FALSE(extent1.OverlapsWith(extent2));
- EXPECT_FALSE(extent2.OverlapsWith(extent1));
+ AddPattern(&expected, "http://www.yahoo.com/b*");
- EXPECT_TRUE(extent1.OverlapsWith(extent3));
- EXPECT_TRUE(extent3.OverlapsWith(extent1));
+ result.ClearPatterns();
+ URLPatternSet::CreateDifference(set1, set2, &result);
+ EXPECT_EQ(expected, result);
+ EXPECT_FALSE(result.is_empty());
+ EXPECT_TRUE(set1.Contains(result));
+ EXPECT_FALSE(result.Contains(set2));
+ EXPECT_FALSE(set2.Contains(result));
+
+ URLPatternSet intersection;
+ URLPatternSet::CreateIntersection(result, set2, &intersection);
+ EXPECT_TRUE(intersection.is_empty());
+}
+
+TEST(URLPatternSetTest, CreateIntersection) {
+ URLPatternSet empty_set;
+ URLPatternSet expected;
+ URLPatternSet set1;
+ AddPattern(&set1, "http://www.google.com/f*");
+ AddPattern(&set1, "http://www.yahoo.com/b*");
+
+ // Intersection with an empty set.
+ URLPatternSet result;
+ URLPatternSet::CreateIntersection(set1, empty_set, &result);
+ EXPECT_EQ(expected, result);
+ EXPECT_TRUE(result.is_empty());
+ EXPECT_TRUE(empty_set.Contains(result));
+ EXPECT_TRUE(result.Contains(empty_set));
+ EXPECT_TRUE(set1.Contains(result));
+
+ // Intersection with a real set.
+ URLPatternSet set2;
+ AddPattern(&set2, "http://www.reddit.com/f*");
+ AddPattern(&set2, "http://www.yahoo.com/z*");
+ AddPattern(&set2, "http://www.google.com/f*");
+
+ AddPattern(&expected, "http://www.google.com/f*");
+
+ result.ClearPatterns();
+ URLPatternSet::CreateIntersection(set1, set2, &result);
+ EXPECT_EQ(expected, result);
+ EXPECT_FALSE(result.is_empty());
+ EXPECT_TRUE(set1.Contains(result));
+ EXPECT_TRUE(set2.Contains(result));
}
TEST(URLPatternSetTest, CreateUnion) {
- URLPatternSet empty_extent;
+ URLPatternSet empty_set;
- URLPatternSet extent1;
- extent1.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
- extent1.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*"));
+ URLPatternSet set1;
+ AddPattern(&set1, "http://www.google.com/f*");
+ AddPattern(&set1, "http://www.yahoo.com/b*");
URLPatternSet expected;
- expected.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
- expected.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*"));
+ AddPattern(&expected, "http://www.google.com/f*");
+ AddPattern(&expected, "http://www.yahoo.com/b*");
// Union with an empty set.
URLPatternSet result;
- URLPatternSet::CreateUnion(extent1, empty_extent, &result);
+ URLPatternSet::CreateUnion(set1, empty_set, &result);
EXPECT_EQ(expected, result);
- // Union with a real set (including a duplicate).
- URLPatternSet extent2;
- extent2.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*"));
- extent2.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*"));
- extent2.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
+ // Union with a real set.
+ URLPatternSet set2;
+ AddPattern(&set2, "http://www.reddit.com/f*");
+ AddPattern(&set2, "http://www.yahoo.com/z*");
+ AddPattern(&set2, "http://www.google.com/f*");
- expected.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*"));
- expected.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*"));
- // CreateUnion does not filter out duplicates right now.
- expected.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
+ AddPattern(&expected, "http://www.reddit.com/f*");
+ AddPattern(&expected, "http://www.yahoo.com/z*");
result.ClearPatterns();
- URLPatternSet::CreateUnion(extent1, extent2, &result);
+ URLPatternSet::CreateUnion(set1, set2, &result);
EXPECT_EQ(expected, result);
}
+
+TEST(URLPatternSetTest, Contains) {
+ URLPatternSet set1;
+ URLPatternSet set2;
+ URLPatternSet empty_set;
+
+ AddPattern(&set1, "http://www.google.com/*");
+ AddPattern(&set1, "http://www.yahoo.com/*");
+
+ AddPattern(&set2, "http://www.reddit.com/*");
+
+ EXPECT_FALSE(set1.Contains(set2));
+ EXPECT_TRUE(set1.Contains(empty_set));
+ EXPECT_FALSE(empty_set.Contains(set1));
+
+ AddPattern(&set2, "http://www.yahoo.com/*");
+
+ EXPECT_FALSE(set1.Contains(set2));
+ EXPECT_FALSE(set2.Contains(set1));
+
+ AddPattern(&set2, "http://www.google.com/*");
+
+ EXPECT_FALSE(set1.Contains(set2));
+ EXPECT_TRUE(set2.Contains(set1));
+
+ // Note that this just checks pattern equality, and not if individual patterns
+ // contain other patterns. For example:
+ AddPattern(&set1, "http://*.reddit.com/*");
+ EXPECT_FALSE(set1.Contains(set2));
+ EXPECT_FALSE(set2.Contains(set1));
+}
+
+TEST(URLPatternSetTest, Duplicates) {
+ URLPatternSet set1;
+ URLPatternSet set2;
+
+ AddPattern(&set1, "http://www.google.com/*");
+ AddPattern(&set2, "http://www.google.com/*");
+
+ AddPattern(&set1, "http://www.google.com/*");
+
+ // The sets should still be equal after adding a duplicate.
+ EXPECT_EQ(set2, set1);
+}
diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc
index 098e3e5..d994d92 100644
--- a/chrome/renderer/extensions/extension_dispatcher.cc
+++ b/chrome/renderer/extensions/extension_dispatcher.cc
@@ -9,6 +9,7 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/extension_permission_set.h"
#include "chrome/common/url_constants.h"
#include "chrome/renderer/extensions/chrome_app_bindings.h"
#include "chrome/renderer/extensions/event_bindings.h"
@@ -65,6 +66,7 @@ bool ExtensionDispatcher::OnControlMessageReceived(
OnSetScriptingWhitelist)
IPC_MESSAGE_HANDLER(ExtensionMsg_ActivateExtension, OnActivateExtension)
IPC_MESSAGE_HANDLER(ExtensionMsg_ActivateApplication, OnActivateApplication)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@@ -149,6 +151,7 @@ void ExtensionDispatcher::OnLoaded(const ExtensionMsg_Loaded_Params& params) {
}
extensions_.Insert(extension);
+ extension->SetActivePermissions(params.GetActivePermissions());
}
void ExtensionDispatcher::OnUnloaded(const std::string& id) {
@@ -244,7 +247,7 @@ void ExtensionDispatcher::InitHostPermissions(const Extension* extension) {
}
const URLPatternSet& permissions =
- extension->permission_set()->explicit_hosts();
+ extension->GetActivePermissions()->explicit_hosts();
for (URLPatternSet::const_iterator i = permissions.begin();
i != permissions.end(); ++i) {
const char* schemes[] = {
@@ -265,6 +268,19 @@ void ExtensionDispatcher::InitHostPermissions(const Extension* extension) {
}
}
+void ExtensionDispatcher::OnUpdatePermissions(
+ const std::string& extension_id,
+ const ExtensionAPIPermissionSet& apis,
+ const URLPatternSet& explicit_hosts,
+ const URLPatternSet& scriptable_hosts) {
+ const Extension* extension = extensions_.GetByID(extension_id);
+ if (!extension)
+ return;
+
+ extension->SetActivePermissions(
+ new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts));
+}
+
void ExtensionDispatcher::OnUpdateUserScripts(
base::SharedMemoryHandle scripts) {
DCHECK(base::SharedMemory::IsHandleValid(scripts)) << "Bad scripts handle";
diff --git a/chrome/renderer/extensions/extension_dispatcher.h b/chrome/renderer/extensions/extension_dispatcher.h
index f2ae6fe9..f1fb8e4 100644
--- a/chrome/renderer/extensions/extension_dispatcher.h
+++ b/chrome/renderer/extensions/extension_dispatcher.h
@@ -20,6 +20,7 @@ class RenderThread;
class URLPattern;
class UserScriptSlave;
struct ExtensionMsg_Loaded_Params;
+struct ExtensionMsg_UpdatePermissions_Params;
namespace base {
class ListValue;
@@ -77,6 +78,10 @@ class ExtensionDispatcher : public RenderProcessObserver {
const std::vector<std::string>& page_actions);
void OnActivateApplication(const std::string& extension_id);
void OnActivateExtension(const std::string& extension_id);
+ void OnUpdatePermissions(const std::string& extension_id,
+ const ExtensionAPIPermissionSet& apis,
+ const URLPatternSet& explicit_hosts,
+ const URLPatternSet& scriptable_hosts);
void OnUpdateUserScripts(base::SharedMemoryHandle table);
// Update the list of active extensions that will be reported when we crash.
diff --git a/chrome/renderer/resources/extension_apitest.js b/chrome/renderer/resources/extension_apitest.js
index c09ed57..7e7e62f 100644
--- a/chrome/renderer/resources/extension_apitest.js
+++ b/chrome/renderer/resources/extension_apitest.js
@@ -71,7 +71,7 @@ var chrome = chrome || {};
if (pendingCallbacks == 0) {
chrome.test.succeed();
}
- }
+ };
};
chrome.test.runNextTest = function() {
diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js
index 6935c1d..b78b4c1 100644
--- a/chrome/renderer/resources/renderer_extension_bindings.js
+++ b/chrome/renderer/resources/renderer_extension_bindings.js
@@ -310,6 +310,7 @@ var chrome = chrome || {};
"experimental.input",
"experimental.inputUI",
"experimental.metrics",
+ "experimental.permissions",
"experimental.popup",
"experimental.processes",
"experimental.rlz",
diff --git a/chrome/test/data/extensions/api_test/permissions/experimental_disabled/background.html b/chrome/test/data/extensions/api_test/permissions/experimental_disabled/background.html
index c46b2e8..4f27694 100644
--- a/chrome/test/data/extensions/api_test/permissions/experimental_disabled/background.html
+++ b/chrome/test/data/extensions/api_test/permissions/experimental_disabled/background.html
@@ -6,7 +6,7 @@ chrome.test.runTests([
function experimental() {
chrome.tabs.getSelected(null, function(tab) {
try {
- chrome.experimental.processes.getProcessForTab(tab.id,
+ chrome.experimental.processes.getProcessForTab(tab.id,
function(process) {
chrome.test.fail();
});
diff --git a/chrome/test/data/extensions/api_test/permissions/optional/background.html b/chrome/test/data/extensions/api_test/permissions/optional/background.html
new file mode 100644
index 0000000..ba9a1c8
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/permissions/optional/background.html
@@ -0,0 +1,188 @@
+<script>
+
+var assertFalse = chrome.test.assertFalse;
+var assertTrue = chrome.test.assertTrue;
+var fail = chrome.test.callbackFail;
+var pass = chrome.test.callbackPass;
+var listenOnce = chrome.test.listenOnce;
+
+var NOT_OPTIONAL_ERROR =
+ "Optional permissions must be listed in extension manifest.";
+
+var NO_TABS_PERMISSION =
+ "You do not have permission to use 'windows.getAll'.";
+
+var REQUIRED_ERROR =
+ "You cannot remove required permissions.";
+
+var NOT_WHITE_LISTED_ERROR =
+ "The optional permissions API does not support '*'.";
+
+var UNKNOWN_PERMISSIONS_ERROR =
+ "'*' is not a recognized permission.";
+
+var emptyPermissions = {permissions: []};
+
+var initialPermissions = {
+ permissions: ['experimental', 'management', 'permissions']
+};
+
+var permissionsWithTabs = {
+ permissions: ['experimental', 'management', 'permissions', 'tabs']
+}
+
+function checkEqualSets(set1, set2) {
+ if (set1.length != set2.length)
+ return false;
+
+ for (var x = 0; x < set1.length; x++) {
+ if (!set2.some(function(v) { return v == set1[x]; }))
+ return false;
+ }
+
+ return true;
+}
+
+function checkPermSetsEq(set1, set2) {
+ return checkEqualSets(set1.permissions, set2.permissions);
+}
+
+chrome.test.runTests([
+ function contains() {
+ chrome.experimental.permissions.contains(
+ {permissions: ['management']},
+ pass(function(result) {
+ assertTrue(result);
+ }));
+ chrome.experimental.permissions.contains(
+ {permissions: ['devtools']},
+ pass(function(result) {
+ assertFalse(result);
+ }));
+ chrome.experimental.permissions.contains(
+ {permissions: ['permissions', 'management']},
+ pass(function(result) {
+ assertTrue(result);
+ }));
+ chrome.experimental.permissions.contains(
+ {permissions: ['management', 'permissions']},
+ pass(function(result) {
+ assertTrue(result);
+ }));
+ },
+
+ function getAll() {
+ chrome.experimental.permissions.getAll(pass(function(permissions) {
+ assertTrue(checkPermSetsEq(initialPermissions, permissions));
+ }));
+ },
+
+ // Nothing should happen if we request permission we already have
+ function requestNoOp() {
+ chrome.experimental.permissions.request(
+ {permissions:['management']},
+ pass(function(granted) {
+ assertTrue(granted);
+ }));
+ },
+
+ // We should get an error when requesting permissions that haven't been
+ // defined in "optional_permissions".
+ function requestNonOptional() {
+ chrome.experimental.permissions.request(
+ {permissions:['debugger']}, fail(NOT_OPTIONAL_ERROR));
+ },
+
+ // We should be able to request the tabs API since it's in the granted
+ // permissions list (see permissions_apitest.cc).
+ function requestTabs() {
+ try {
+ chrome.windows.getAll({populate: true}, function() {
+ chrome.test.fail("Should not have tabs API permission.");
+ });
+ } catch (e) {
+ assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0);
+ }
+ listenOnce(chrome.experimental.permissions.onAdded, function(permissions) {
+ assertTrue(permissions.permissions.length == 1);
+ assertTrue(permissions.permissions[0] == 'tabs');
+ });
+ chrome.experimental.permissions.request(
+ {permissions:['tabs']},
+ pass(function(granted) {
+ assertTrue(granted);
+ chrome.windows.getAll({populate: true}, pass(function(windows) {
+ assertTrue(true);
+ }));
+ chrome.experimental.permissions.getAll(pass(function(permissions) {
+ assertTrue(checkPermSetsEq(permissionsWithTabs, permissions));
+ }));
+ }));
+ },
+
+ // You can't remove required permissions.
+ function removeRequired() {
+ chrome.experimental.permissions.remove(
+ {permissions:['management']}, fail(REQUIRED_ERROR));
+ },
+
+ // You can remove permissions you don't have (nothing happens).
+ function removeNoOp() {
+ chrome.experimental.permissions.remove(
+ {permissions:['background']},
+ pass(function(removed) {
+ assertTrue(removed);
+ }));
+ },
+
+ function removeTabs() {
+ chrome.windows.getAll({populate: true}, pass(function(windows) {
+ assertTrue(true);
+ }));
+ listenOnce(chrome.experimental.permissions.onRemoved,
+ function(permissions) {
+ assertTrue(permissions.permissions.length == 1);
+ assertTrue(permissions.permissions[0] == 'tabs');
+ });
+ chrome.experimental.permissions.remove(
+ {permissions:['tabs']},
+ pass(function() {
+ chrome.experimental.permissions.getAll(pass(function(permissions) {
+ assertTrue(checkPermSetsEq(initialPermissions, permissions));
+ }));
+ try {
+ chrome.windows.getAll({populate: true}, function() {
+ chrome.test.fail("Should not have tabs API permission.");
+ });
+ } catch (e) {
+ assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0);
+ }
+ }));
+ },
+
+ // The user shouldn't have to approve permissions that have no warnings.
+ // TODO(jstritar): move to its own test and set a low timeout
+ function noPromptForNoWarnings() {
+ chrome.experimental.permissions.request(
+ {permissions: ['notifications']},
+ pass(function(granted) {
+ assertTrue(granted);
+ }));
+ },
+
+ // Make sure you can only access the white listed permissions.
+ function whitelist() {
+ var error_msg = NOT_WHITE_LISTED_ERROR.replace('*', 'chromeAuthPrivate');
+ chrome.experimental.permissions.request(
+ {permissions: ['chromeAuthPrivate']}, fail(error_msg));
+ chrome.experimental.permissions.remove(
+ {permissions: ['chromeAuthPrivate']}, fail(error_msg));
+ },
+
+ function unknownPermission() {
+ var error_msg = UNKNOWN_PERMISSIONS_ERROR.replace('*', 'asdf');
+ chrome.experimental.permissions.request(
+ {permissions: ['asdf']}, fail(error_msg));
+ }
+]);
+</script>
diff --git a/chrome/test/data/extensions/api_test/permissions/optional/manifest.json b/chrome/test/data/extensions/api_test/permissions/optional/manifest.json
new file mode 100644
index 0000000..55ea80b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/permissions/optional/manifest.json
@@ -0,0 +1,19 @@
+{
+ "name": "permissions/optional",
+ "description": "permissions/optional",
+ "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkprt3BRSqoikAhSygI6VUzDLt18cKODYmkaa/dwPp4dboyz93RSB+v76grbqsNWrJjkrEwRD3QFeBYBq7h27XAMV4X5XvWjmWQBkRTBQyQI8cZd7M9dgfKrI3EqX9OJvd/wTJkC0dgF47nwWRa/Tvwl7Y66GwEEUjpn2MTv4klwIDAQAB",
+ "version": "0.1",
+ "background_page": "background.html",
+ "permissions": [
+ "experimental",
+ "management",
+ "permissions",
+ "http://example.org/"
+ ],
+ "optional_permissions": [
+ "tabs",
+ "management",
+ "notifications",
+ "background"
+ ]
+}
diff --git a/chrome/test/data/extensions/api_test/permissions/optional_deny/background.html b/chrome/test/data/extensions/api_test/permissions/optional_deny/background.html
new file mode 100644
index 0000000..ca02382
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/permissions/optional_deny/background.html
@@ -0,0 +1,36 @@
+<script>
+
+var assertFalse = chrome.test.assertFalse;
+var assertTrue = chrome.test.assertTrue;
+var pass = chrome.test.callbackPass;
+
+var NO_TABS_PERMISSION =
+ "You do not have permission to use 'windows.getAll'.";
+
+chrome.test.runTests([
+ function denyRequest() {
+ chrome.experimental.permissions.request(
+ {permissions: ['tabs']},
+ pass(function(granted) {
+ // They were not granted, and there should be no error.
+ assertFalse(granted);
+ assertTrue(chrome.extension.lastError === undefined);
+
+ // Make sure they weren't granted...
+ chrome.experimental.permissions.contains(
+ {permissions: ['tabs']},
+ pass(function(result) {
+ assertFalse(result);
+ }));
+
+ try {
+ chrome.windows.getAll({populate: true}, function() {
+ chrome.test.fail("Should not have tabs API permission.");
+ });
+ } catch (e) {
+ assertTrue(e.message.indexOf(NO_TABS_PERMISSION) == 0);
+ }
+ }));
+ }
+]);
+</script>
diff --git a/chrome/test/data/extensions/api_test/permissions/optional_deny/manifest.json b/chrome/test/data/extensions/api_test/permissions/optional_deny/manifest.json
new file mode 100644
index 0000000..e3b668c
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/permissions/optional_deny/manifest.json
@@ -0,0 +1,8 @@
+{
+ "name": "permissions/optional_deny",
+ "description": "permissions/optional_deny",
+ "version": "0.1",
+ "background_page": "background.html",
+ "permissions": ["experimental", "permissions", "http://example.org/"],
+ "optional_permissions": ["tabs", "management"]
+}