diff options
author | yoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-30 17:15:40 +0000 |
---|---|---|
committer | yoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-30 17:15:40 +0000 |
commit | a0011476ee7c46bfbab491386f4d987e9ecb05be (patch) | |
tree | c2e69f622b2cb44e434e027f70f6796e78c3fde0 /chrome/browser/extensions/extension_menu_manager.cc | |
parent | 88f2c6e54e50bfa40d4bcf49fc8f953e4d5db424 (diff) | |
download | chromium_src-a0011476ee7c46bfbab491386f4d987e9ecb05be.zip chromium_src-a0011476ee7c46bfbab491386f4d987e9ecb05be.tar.gz chromium_src-a0011476ee7c46bfbab491386f4d987e9ecb05be.tar.bz2 |
Persist context menus for event pages to preferences.
Refactor a whole bunch of URLPatternSet parsing so it doesn't happen in so many places.
BUG=123366
TEST=Check that event pages that add context menus at runtime.onInstalled still have them after browser restart.
Review URL: https://chromiumcodereview.appspot.com/10450028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@139557 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/extension_menu_manager.cc')
-rw-r--r-- | chrome/browser/extensions/extension_menu_manager.cc | 189 |
1 files changed, 179 insertions, 10 deletions
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); } } |