summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/api/context_menu/context_menu_api.cc86
-rw-r--r--chrome/browser/extensions/api/context_menu/context_menu_api.h11
-rw-r--r--chrome/browser/extensions/extension_menu_manager.cc189
-rw-r--r--chrome/browser/extensions/extension_menu_manager.h43
-rw-r--r--chrome/browser/extensions/extension_prefs.cc65
-rw-r--r--chrome/browser/extensions/extension_prefs.h6
-rw-r--r--chrome/common/extensions/url_pattern_set.cc45
-rw-r--r--chrome/common/extensions/url_pattern_set.h13
8 files changed, 342 insertions, 116 deletions
diff --git a/chrome/browser/extensions/api/context_menu/context_menu_api.cc b/chrome/browser/extensions/api/context_menu/context_menu_api.cc
index 588b382..2976b06 100644
--- a/chrome/browser/extensions/api/context_menu/context_menu_api.cc
+++ b/chrome/browser/extensions/api/context_menu/context_menu_api.cc
@@ -13,6 +13,8 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension_error_utils.h"
+namespace {
+
const char kCheckedKey[] = "checked";
const char kContextsKey[] = "contexts";
const char kDocumentUrlPatternsKey[] = "documentUrlPatterns";
@@ -31,7 +33,6 @@ const char kDuplicateIDError[] =
"Cannot create item with duplicate id *";
const char kIdRequiredError[] = "Extensions using event pages must pass an "
"id parameter to chrome.contextMenus.create";
-const char kInvalidURLPatternError[] = "Invalid url pattern '*'";
const char kInvalidValueError[] = "Invalid value for *";
const char kInvalidTypeStringError[] = "Invalid type string '*'";
const char kParentsMustBeNormalError[] =
@@ -39,10 +40,6 @@ const char kParentsMustBeNormalError[] =
const char kTitleNeededError[] =
"All menu items except for separators must have a title";
-namespace extensions {
-
-namespace {
-
std::string GetIDString(const ExtensionMenuItem::Id& id) {
if (id.uid == 0)
return id.string_uid;
@@ -52,6 +49,8 @@ std::string GetIDString(const ExtensionMenuItem::Id& id) {
} // namespace
+namespace extensions {
+
bool ExtensionContextMenuFunction::ParseContexts(
const DictionaryValue& properties,
const char* key,
@@ -143,68 +142,11 @@ bool ExtensionContextMenuFunction::ParseChecked(
return true;
}
-bool ExtensionContextMenuFunction::ParseURLPatterns(
- const DictionaryValue& properties,
- const char* key,
- URLPatternSet* result) {
- if (!properties.HasKey(key))
- return true;
- ListValue* list = NULL;
- if (!properties.GetList(key, &list))
- return false;
- for (ListValue::iterator i = list->begin(); i != list->end(); ++i) {
- std::string tmp;
- if (!(*i)->GetAsString(&tmp))
- return false;
-
- URLPattern pattern(URLPattern::SCHEME_ALL);
- // TODO(skerner): Consider enabling strict pattern parsing
- // if this extension's location indicates that it is under development.
- if (URLPattern::PARSE_SUCCESS != pattern.Parse(tmp)) {
- error_ = ExtensionErrorUtils::FormatErrorMessage(kInvalidURLPatternError,
- tmp);
- return false;
- }
- result->AddPattern(pattern);
- }
- return true;
-}
-
-bool ExtensionContextMenuFunction::SetURLPatterns(
- const DictionaryValue& properties,
- ExtensionMenuItem* item) {
- // Process the documentUrlPattern value.
- URLPatternSet document_url_patterns;
- if (!ParseURLPatterns(properties, kDocumentUrlPatternsKey,
- &document_url_patterns))
- return false;
-
- if (!document_url_patterns.is_empty()) {
- item->set_document_url_patterns(document_url_patterns);
- }
-
- // Process the targetUrlPattern value.
- URLPatternSet target_url_patterns;
- if (!ParseURLPatterns(properties, kTargetUrlPatternsKey,
- &target_url_patterns))
- return false;
-
- if (!target_url_patterns.is_empty()) {
- item->set_target_url_patterns(target_url_patterns);
- }
-
- return true;
-}
-
bool ExtensionContextMenuFunction::ParseID(
const Value* value,
ExtensionMenuItem::Id* result) {
- if (value->GetAsInteger(&result->uid) ||
- value->GetAsString(&result->string_uid)) {
- return true;
- } else {
- return false;
- }
+ return (value->GetAsInteger(&result->uid) ||
+ value->GetAsString(&result->string_uid));
}
bool ExtensionContextMenuFunction::GetParent(
@@ -289,7 +231,8 @@ bool CreateContextMenuFunction::RunImpl() {
scoped_ptr<ExtensionMenuItem> item(
new ExtensionMenuItem(id, title, checked, enabled, type, contexts));
- if (!SetURLPatterns(*properties, item.get()))
+ if (!item->PopulateURLPatterns(
+ *properties, kDocumentUrlPatternsKey, kTargetUrlPatternsKey, &error_))
return false;
bool success = true;
@@ -305,6 +248,7 @@ bool CreateContextMenuFunction::RunImpl() {
if (!success)
return false;
+ menu_manager->WriteToPrefs(GetExtension());
return true;
}
@@ -382,7 +326,8 @@ bool UpdateContextMenuFunction::RunImpl() {
if (parent && !manager->ChangeParent(item->id(), &parent->id()))
return false;
- if (!SetURLPatterns(*properties, item))
+ if (!item->PopulateURLPatterns(
+ *properties, kDocumentUrlPatternsKey, kTargetUrlPatternsKey, &error_))
return false;
// There is no need to call ItemUpdated if ChangeParent is called because
@@ -390,6 +335,7 @@ bool UpdateContextMenuFunction::RunImpl() {
if (!parent && radioItemUpdated && !manager->ItemUpdated(item->id()))
return false;
+ manager->WriteToPrefs(GetExtension());
return true;
}
@@ -409,13 +355,17 @@ bool RemoveContextMenuFunction::RunImpl() {
return false;
}
- return manager->RemoveContextMenuItem(id);
+ if (!manager->RemoveContextMenuItem(id))
+ return false;
+ manager->WriteToPrefs(GetExtension());
+ return true;
}
bool RemoveAllContextMenusFunction::RunImpl() {
ExtensionService* service = profile()->GetExtensionService();
ExtensionMenuManager* manager = service->menu_manager();
- manager->RemoveAllContextItems(extension_id());
+ manager->RemoveAllContextItems(GetExtension()->id());
+ manager->WriteToPrefs(GetExtension());
return true;
}
diff --git a/chrome/browser/extensions/api/context_menu/context_menu_api.h b/chrome/browser/extensions/api/context_menu/context_menu_api.h
index 6117e3e..fe09370 100644
--- a/chrome/browser/extensions/api/context_menu/context_menu_api.h
+++ b/chrome/browser/extensions/api/context_menu/context_menu_api.h
@@ -41,17 +41,6 @@ class ExtensionContextMenuFunction : public SyncExtensionFunction {
bool default_value,
bool* checked);
- // Helper to read in a set of url patterns from a property with the given key
- // name.
- bool ParseURLPatterns(const base::DictionaryValue& properties,
- const char* key,
- URLPatternSet* result);
-
- // Reads in any document and targetUrl patterns from |properties| and sets
- // them on |item|.
- bool SetURLPatterns(const base::DictionaryValue& properties,
- ExtensionMenuItem* item);
-
// Helper to read an ID from the Value*. The ID can be either a string or
// integer.
bool ParseID(const Value* value,
diff --git a/chrome/browser/extensions/extension_menu_manager.cc b/chrome/browser/extensions/extension_menu_manager.cc
index 500efcf..99d6fc4 100644
--- a/chrome/browser/extensions/extension_menu_manager.cc
+++ b/chrome/browser/extensions/extension_menu_manager.cc
@@ -13,6 +13,9 @@
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_event_router.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_notification_types.h"
@@ -27,6 +30,18 @@ using content::WebContents;
namespace {
+// Keys for serialization to and from Value to store in the preferences.
+const char kCheckedKey[] = "checked";
+const char kContextsKey[] = "contexts";
+const char kDocumentURLPatternsKey[] = "document_url_patterns";
+const char kEnabledKey[] = "enabled";
+const char kIncognitoKey[] = "incognito";
+const char kParentUIDKey[] = "parent_uid";
+const char kStringUIDKey[] = "string_uid";
+const char kTargetURLPatternsKey[] = "target_url_patterns";
+const char kTitleKey[] = "title";
+const char kTypeKey[] = "type";
+
void SetIdKeyValue(base::DictionaryValue* properties,
const char* key,
const ExtensionMenuItem::Id& id) {
@@ -74,6 +89,12 @@ ExtensionMenuItem* ExtensionMenuItem::ReleaseChild(const Id& child_id,
return NULL;
}
+void ExtensionMenuItem::GetFlattenedSubtree(ExtensionMenuItem::List* list) {
+ list->push_back(this);
+ for (List::iterator i = children_.begin(); i != children_.end(); ++i)
+ (*i)->GetFlattenedSubtree(list);
+}
+
std::set<ExtensionMenuItem::Id> ExtensionMenuItem::RemoveAllDescendants() {
std::set<Id> result;
for (List::iterator i = children_.begin(); i != children_.end(); ++i) {
@@ -110,7 +131,111 @@ void ExtensionMenuItem::AddChild(ExtensionMenuItem* item) {
children_.push_back(item);
}
-ExtensionMenuManager::ExtensionMenuManager(Profile* profile) {
+scoped_ptr<DictionaryValue> ExtensionMenuItem::ToValue() const {
+ scoped_ptr<DictionaryValue> value(new DictionaryValue);
+ // Should only be called for extensions with event pages, which only have
+ // string IDs for items.
+ DCHECK_EQ(0, id_.uid);
+ value->SetString(kStringUIDKey, id_.string_uid);
+ value->SetBoolean(kIncognitoKey, id_.incognito);
+ value->SetInteger(kTypeKey, type_);
+ if (type_ != SEPARATOR)
+ value->SetString(kTitleKey, title_);
+ if (type_ == CHECKBOX || type_ == RADIO)
+ value->SetBoolean(kCheckedKey, checked_);
+ value->SetBoolean(kEnabledKey, enabled_);
+ value->Set(kContextsKey, contexts_.ToValue().release());
+ if (parent_id_.get()) {
+ DCHECK_EQ(0, parent_id_->uid);
+ value->SetString(kParentUIDKey, parent_id_->string_uid);
+ }
+ value->Set(kDocumentURLPatternsKey,
+ document_url_patterns_.ToValue().release());
+ value->Set(kTargetURLPatternsKey, target_url_patterns_.ToValue().release());
+ return value.Pass();
+}
+
+// static
+ExtensionMenuItem* ExtensionMenuItem::Populate(const std::string& extension_id,
+ const DictionaryValue& value,
+ std::string* error) {
+ bool incognito = false;
+ if (!value.GetBoolean(kIncognitoKey, &incognito))
+ return NULL;
+ Id id(incognito, extension_id);
+ if (!value.GetString(kStringUIDKey, &id.string_uid))
+ return NULL;
+ int type_int;
+ Type type;
+ if (!value.GetInteger(kTypeKey, &type_int))
+ return NULL;
+ type = static_cast<Type>(type_int);
+ std::string title;
+ if (type != SEPARATOR && !value.GetString(kTitleKey, &title))
+ return NULL;
+ bool checked;
+ if ((type == CHECKBOX || type == RADIO) &&
+ !value.GetBoolean(kCheckedKey, &checked)) {
+ return NULL;
+ }
+ bool enabled = true;
+ if (!value.GetBoolean(kEnabledKey, &enabled))
+ return NULL;
+ ContextList contexts;
+ Value* contexts_value = NULL;
+ if (!value.Get(kContextsKey, &contexts_value))
+ return NULL;
+ if (!contexts.Populate(*contexts_value))
+ return NULL;
+
+ scoped_ptr<ExtensionMenuItem> result(new ExtensionMenuItem(
+ id, title, checked, enabled, type, contexts));
+
+ if (!result->PopulateURLPatterns(
+ value, kDocumentURLPatternsKey, kTargetURLPatternsKey, error))
+ return NULL;
+
+ // parent_id is filled in from the value, but it might not be valid. It's left
+ // to be validated upon being added (via AddChildItem) to the menu manager.
+ scoped_ptr<Id> parent_id(new Id(incognito, extension_id));
+ if (value.HasKey(kParentUIDKey)) {
+ if (!value.GetString(kParentUIDKey, &parent_id->string_uid))
+ return NULL;
+ result->parent_id_.swap(parent_id);
+ }
+ return result.release();
+}
+
+bool ExtensionMenuItem::PopulateURLPatterns(
+ const DictionaryValue& properties,
+ const char* document_url_patterns_key,
+ const char* target_url_patterns_key,
+ std::string* error) {
+ if (properties.HasKey(document_url_patterns_key)) {
+ ListValue* list = NULL;
+ if (!properties.GetList(document_url_patterns_key, &list))
+ return false;
+ if (!document_url_patterns_.Populate(
+ *list, URLPattern::SCHEME_ALL, true, error)) {
+ return false;
+ }
+ }
+ if (properties.HasKey(target_url_patterns_key)) {
+ ListValue* list = NULL;
+ if (!properties.GetList(target_url_patterns_key, &list))
+ return false;
+ if (!target_url_patterns_.Populate(
+ *list, URLPattern::SCHEME_ALL, true, error)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+ExtensionMenuManager::ExtensionMenuManager(Profile* profile)
+ : profile_(profile) {
+ registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
+ content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
content::Source<Profile>(profile));
}
@@ -182,7 +307,6 @@ bool ExtensionMenuManager::AddChildItem(const ExtensionMenuItem::Id& parent_id,
if (child->type() == ExtensionMenuItem::RADIO)
SanitizeRadioList(parent->children());
-
return true;
}
@@ -314,7 +438,6 @@ bool ExtensionMenuManager::RemoveContextMenuItem(
context_items_.erase(extension_id);
icon_manager_.RemoveIcon(extension_id);
}
-
return result;
}
@@ -472,6 +595,10 @@ void ExtensionMenuManager::ExecuteCommand(
item->SetChecked(checked);
properties->SetBoolean("checked", item->checked());
+
+ const extensions::Extension* extension = ExtensionSystem::Get(profile_)->
+ extension_service()->GetExtensionById(menuItemId.extension_id, false);
+ WriteToPrefs(extension);
}
std::string json_args;
@@ -540,17 +667,59 @@ bool ExtensionMenuManager::ItemUpdated(const ExtensionMenuItem::Id& id) {
return true;
}
+void ExtensionMenuManager::WriteToPrefs(
+ const extensions::Extension* extension) {
+ if (!extension->has_lazy_background_page())
+ return;
+ const ExtensionMenuItem::List* top_items = MenuItems(extension->id());
+ ExtensionMenuItem::List all_items;
+ if (top_items) {
+ for (ExtensionMenuItem::List::const_iterator i = top_items->begin();
+ i != top_items->end(); ++i) {
+ (*i)->GetFlattenedSubtree(&all_items);
+ }
+ }
+ ExtensionSystem::Get(profile_)->extension_service()->extension_prefs()->
+ SetContextMenuItems(extension->id(), all_items);
+}
+
+void ExtensionMenuManager::ReadFromPrefs(
+ const extensions::Extension* extension) {
+ if (!extension->has_lazy_background_page())
+ return;
+ ExtensionMenuItem::List items =
+ ExtensionSystem::Get(profile_)->extension_service()->extension_prefs()->
+ GetContextMenuItems(extension->id());
+ for (size_t i = 0; i < items.size(); ++i) {
+ if (items[i]->parent_id()) {
+ // Parent IDs are stored in the parent_id field for convenience, but
+ // they have not yet been validated. Separate them out here.
+ // Because of the order in which we store items in the prefs, parents will
+ // precede children, so we should already know about any parent items.
+ scoped_ptr<ExtensionMenuItem::Id> parent_id;
+ parent_id.swap(items[i]->parent_id_);
+ AddChildItem(*parent_id, items[i]);
+ } else {
+ AddContextItem(extension, items[i]);
+ }
+ }
+}
+
void ExtensionMenuManager::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
- DCHECK(type == chrome::NOTIFICATION_EXTENSION_UNLOADED);
-
- // Remove menu items for disabled/uninstalled extensions.
- const extensions::Extension* extension =
- content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
- if (ContainsKey(context_items_, extension->id())) {
- RemoveAllContextItems(extension->id());
+ if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
+ // Remove menu items for disabled/uninstalled extensions.
+ const extensions::Extension* extension =
+ content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
+ if (ContainsKey(context_items_, extension->id())) {
+ RemoveAllContextItems(extension->id());
+ }
+ } else if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
+ const extensions::Extension* extension =
+ content::Details<const extensions::Extension>(details).ptr();
+ ReadFromPrefs(extension);
}
}
diff --git a/chrome/browser/extensions/extension_menu_manager.h b/chrome/browser/extensions/extension_menu_manager.h
index d55ebca..1197c27 100644
--- a/chrome/browser/extensions/extension_menu_manager.h
+++ b/chrome/browser/extensions/extension_menu_manager.h
@@ -16,6 +16,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
+#include "base/values.h"
#include "chrome/browser/extensions/extension_icon_manager.h"
#include "chrome/common/extensions/url_pattern_set.h"
#include "content/public/browser/notification_observer.h"
@@ -108,6 +109,18 @@ class ExtensionMenuItem {
value_ |= context;
}
+ scoped_ptr<Value> ToValue() const {
+ return scoped_ptr<Value>(Value::CreateIntegerValue(value_));
+ }
+
+ bool Populate(const Value& value) {
+ int int_value;
+ if (!value.GetAsInteger(&int_value) || int_value < 0)
+ return false;
+ value_ = int_value;
+ return true;
+ }
+
private:
uint32 value_; // A bitmask of Context values.
};
@@ -156,9 +169,24 @@ class ExtensionMenuItem {
string16 TitleWithReplacement(const string16& selection,
size_t max_length) const;
- // Set the checked state to |checked|. Returns true if successful.
+ // Sets the checked state to |checked|. Returns true if successful.
bool SetChecked(bool checked);
+ // Converts to Value for serialization to preferences.
+ scoped_ptr<base::DictionaryValue> ToValue() const;
+
+ // Returns a new ExtensionMenuItem created from |value|, or NULL if there is
+ // an error. The caller takes ownership of the ExtensionMenuItem.
+ static ExtensionMenuItem* Populate(const std::string& extension_id,
+ const DictionaryValue& value,
+ std::string* error);
+
+ // Sets any document and target URL patterns from |properties|.
+ bool PopulateURLPatterns(const base::DictionaryValue& properties,
+ const char* document_url_patterns_key,
+ const char* target_url_patterns_key,
+ std::string* error);
+
protected:
friend class ExtensionMenuManager;
@@ -169,6 +197,10 @@ class ExtensionMenuItem {
// then owns the pointer.
ExtensionMenuItem* ReleaseChild(const Id& child_id, bool recursive);
+ // Recursively appends all descendant items (children, grandchildren, etc.)
+ // to the output |list|.
+ void GetFlattenedSubtree(ExtensionMenuItem::List* list);
+
// Recursively removes all descendant items (children, grandchildren, etc.),
// returning the ids of the removed items.
std::set<Id> RemoveAllDescendants();
@@ -275,6 +307,13 @@ class ExtensionMenuManager : public content::NotificationObserver {
virtual void Observe(int type, const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
+ // Stores the menu items for the extension in the preferences.
+ void WriteToPrefs(const extensions::Extension* extension);
+
+ // Reads menu items for the extension from the preferences. Any invalid
+ // items are ignored.
+ void ReadFromPrefs(const extensions::Extension* extension);
+
private:
FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, DeleteParent);
FRIEND_TEST_ALL_PREFIXES(ExtensionMenuManagerTest, RemoveOneByOne);
@@ -306,6 +345,8 @@ class ExtensionMenuManager : public content::NotificationObserver {
ExtensionIconManager icon_manager_;
+ Profile* profile_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionMenuManager);
};
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index faf3bd5..52af11b 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -9,6 +9,7 @@
#include "base/utf_string_conversions.h"
#include "chrome/browser/extensions/api/alarms/alarm_manager.h"
#include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
+#include "chrome/browser/extensions/extension_menu_manager.h"
#include "chrome/browser/extensions/extension_pref_store.h"
#include "chrome/browser/extensions/extension_sorting.h"
#include "chrome/browser/prefs/pref_notifier.h"
@@ -174,6 +175,9 @@ const char kAlarmScheduledRunTime[] = "scheduled_run_time";
// Persisted value for omnibox.setDefaultSuggestion.
const char kOmniboxDefaultSuggestion[] = "omnibox_default_suggestion";
+// A preference containing context menu items, persisted for event pages.
+const char kPrefContextMenus[] = "context_menus";
+
// Provider of write access to a dictionary storing extension prefs.
class ScopedExtensionPrefUpdate : public DictionaryPrefUpdate {
public:
@@ -440,38 +444,15 @@ bool ExtensionPrefs::ReadExtensionPrefURLPatternSet(
if (!ReadExtensionPrefList(extension_id, pref_key, &value))
return false;
- result->ClearPatterns();
bool allow_file_access = AllowFileAccess(extension_id);
-
- for (size_t i = 0; i < value->GetSize(); ++i) {
- std::string item;
- if (!value->GetString(i, &item))
- return false;
- URLPattern pattern(valid_schemes);
- if (pattern.Parse(item) != URLPattern::PARSE_SUCCESS) {
- LOG(ERROR) << "Invaid permission pattern: " << item;
- return false;
- }
- if (!allow_file_access && pattern.MatchesScheme(chrome::kFileScheme)) {
- pattern.SetValidSchemes(
- pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
- }
- result->AddPattern(pattern);
- }
-
- return true;
+ return result->Populate(*value, valid_schemes, allow_file_access, NULL);
}
void ExtensionPrefs::SetExtensionPrefURLPatternSet(
const std::string& extension_id,
const std::string& pref_key,
const URLPatternSet& new_value) {
- ListValue* value = new ListValue();
- for (URLPatternSet::const_iterator i = new_value.begin();
- i != new_value.end(); ++i)
- value->AppendIfNotPresent(Value::CreateStringValue(i->GetAsString()));
-
- UpdateExtensionPref(extension_id, pref_key, value);
+ UpdateExtensionPref(extension_id, pref_key, new_value.ToValue().release());
}
ExtensionPermissionSet* ExtensionPrefs::ReadExtensionPrefPermissionSet(
@@ -1032,6 +1013,37 @@ void ExtensionPrefs::SetOmniboxDefaultSuggestion(
UpdateExtensionPref(extension_id, kOmniboxDefaultSuggestion, dict.release());
}
+ExtensionMenuItem::List ExtensionPrefs::GetContextMenuItems(
+ const std::string& extension_id) {
+ ExtensionMenuItem::List items;
+ const base::ListValue* list = NULL;
+ if (!ReadExtensionPrefList(extension_id, kPrefContextMenus, &list))
+ return items;
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ base::DictionaryValue* dict = NULL;
+ if (!list->GetDictionary(i, &dict))
+ continue;
+ ExtensionMenuItem* item = ExtensionMenuItem::Populate(
+ extension_id, *dict, NULL);
+ if (!item)
+ continue;
+ items.push_back(item);
+ }
+ return items;
+}
+
+void ExtensionPrefs::SetContextMenuItems(
+ const std::string& extension_id,
+ const ExtensionMenuItem::List& items) {
+ base::ListValue* list = NULL;
+ if (items.size() > 0) {
+ list = new base::ListValue;
+ for (size_t i = 0; i < items.size(); ++i)
+ list->Append(items[i]->ToValue().release());
+ }
+ UpdateExtensionPref(extension_id, kPrefContextMenus, list);
+}
+
bool ExtensionPrefs::IsIncognitoEnabled(const std::string& extension_id) {
return ReadExtensionPrefBoolean(extension_id, kPrefIncognitoEnabled);
}
@@ -1227,9 +1239,10 @@ void ExtensionPrefs::OnExtensionInstalled(
extension->manifest()->value()->DeepCopy());
}
- // Clear any events and alarms that may be registered from a previous install.
+ // Clear state that may be registered from a previous install.
extension_dict->Remove(kRegisteredEvents, NULL);
extension_dict->Remove(kRegisteredAlarms, NULL);
+ extension_dict->Remove(kPrefContextMenus, NULL);
if (extension->is_app()) {
StringOrdinal new_page_ordinal = page_ordinal.IsValid() ?
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index adfb45f..bef7f2a 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -14,6 +14,7 @@
#include "base/time.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/content_settings/content_settings_store.h"
+#include "chrome/browser/extensions/extension_menu_manager.h"
#include "chrome/browser/extensions/extension_prefs_scope.h"
#include "chrome/browser/extensions/extension_scoped_prefs.h"
#include "chrome/common/extensions/extension.h"
@@ -283,6 +284,11 @@ class ExtensionPrefs : public extensions::ContentSettingsStore::Observer,
const std::string& extension_id,
const extensions::ExtensionOmniboxSuggestion& suggestion);
+ // Controls the context menu items for this extension.
+ ExtensionMenuItem::List GetContextMenuItems(const std::string& extension_id);
+ void SetContextMenuItems(const std::string& extension_id,
+ const ExtensionMenuItem::List& items);
+
// Returns true if the user enabled this extension to be loaded in incognito
// mode.
bool IsIncognitoEnabled(const std::string& extension_id);
diff --git a/chrome/common/extensions/url_pattern_set.cc b/chrome/common/extensions/url_pattern_set.cc
index b2f5f25..c84df14 100644
--- a/chrome/common/extensions/url_pattern_set.cc
+++ b/chrome/common/extensions/url_pattern_set.cc
@@ -7,9 +7,18 @@
#include <algorithm>
#include <iterator>
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/url_pattern.h"
+#include "content/public/common/url_constants.h"
#include "googleurl/src/gurl.h"
+namespace {
+
+const char kInvalidURLPatternError[] = "Invalid url pattern '*'";
+
+} // namespace
// static
void URLPatternSet::CreateDifference(const URLPatternSet& set1,
@@ -114,3 +123,39 @@ bool URLPatternSet::OverlapsWith(const URLPatternSet& other) const {
return false;
}
+
+scoped_ptr<base::ListValue> URLPatternSet::ToValue() const {
+ scoped_ptr<ListValue> value(new ListValue);
+ for (URLPatternSet::const_iterator i = patterns_.begin();
+ i != patterns_.end(); ++i)
+ value->AppendIfNotPresent(Value::CreateStringValue(i->GetAsString()));
+ return value.Pass();
+}
+
+bool URLPatternSet::Populate(const base::ListValue& value,
+ int valid_schemes,
+ bool allow_file_access,
+ std::string* error) {
+ ClearPatterns();
+ for (size_t i = 0; i < value.GetSize(); ++i) {
+ std::string item;
+ if (!value.GetString(i, &item))
+ return false;
+ URLPattern pattern(valid_schemes);
+ if (pattern.Parse(item) != URLPattern::PARSE_SUCCESS) {
+ if (error) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ kInvalidURLPatternError, item);
+ } else {
+ LOG(ERROR) << "Invalid url pattern: " << item;
+ }
+ return false;
+ }
+ if (!allow_file_access && pattern.MatchesScheme(chrome::kFileScheme)) {
+ pattern.SetValidSchemes(
+ pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
+ }
+ AddPattern(pattern);
+ }
+ return true;
+}
diff --git a/chrome/common/extensions/url_pattern_set.h b/chrome/common/extensions/url_pattern_set.h
index 3629f0b..c912558 100644
--- a/chrome/common/extensions/url_pattern_set.h
+++ b/chrome/common/extensions/url_pattern_set.h
@@ -8,10 +8,16 @@
#include <set>
+#include "base/memory/scoped_ptr.h"
#include "chrome/common/extensions/url_pattern.h"
class GURL;
+namespace base {
+class ListValue;
+class Value;
+}
+
// Represents the set of URLs an extension uses for web content.
class URLPatternSet {
public:
@@ -61,6 +67,13 @@ class URLPatternSet {
// Returns true if there is a single URL that would be in two extents.
bool OverlapsWith(const URLPatternSet& other) const;
+ // Converts to and from Value for serialization to preferences.
+ scoped_ptr<base::ListValue> ToValue() const;
+ bool Populate(const base::ListValue& value,
+ int valid_schemes,
+ bool allow_file_access,
+ std::string* error);
+
private:
// The list of URL patterns that comprise the extent.
std::set<URLPattern> patterns_;