summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/extension_menu_manager.cc
diff options
context:
space:
mode:
authoryoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-30 17:15:40 +0000
committeryoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-30 17:15:40 +0000
commita0011476ee7c46bfbab491386f4d987e9ecb05be (patch)
treec2e69f622b2cb44e434e027f70f6796e78c3fde0 /chrome/browser/extensions/extension_menu_manager.cc
parent88f2c6e54e50bfa40d4bcf49fc8f953e4d5db424 (diff)
downloadchromium_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.cc189
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);
}
}