| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/extension_menu_manager.h" | 5 #include "chrome/browser/extensions/extension_menu_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "app/resource_bundle.h" |
| 9 #include "base/logging.h" | 10 #include "base/logging.h" |
| 10 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 11 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
| 12 #include "base/values.h" | 13 #include "base/values.h" |
| 13 #include "base/json/json_writer.h" | 14 #include "base/json/json_writer.h" |
| 14 #include "chrome/app/chrome_dll_resource.h" | 15 #include "chrome/app/chrome_dll_resource.h" |
| 15 #include "chrome/browser/extensions/extension_message_service.h" | 16 #include "chrome/browser/extensions/extension_message_service.h" |
| 16 #include "chrome/browser/extensions/extension_tabs_module.h" | 17 #include "chrome/browser/extensions/extension_tabs_module.h" |
| 17 #include "chrome/browser/profile.h" | 18 #include "chrome/browser/profile.h" |
| 18 #include "chrome/common/extensions/extension.h" | 19 #include "chrome/common/extensions/extension.h" |
| 20 #include "chrome/common/extensions/extension_resource.h" |
| 21 #include "gfx/favicon_size.h" |
| 22 #include "gfx/size.h" |
| 23 #include "grit/theme_resources.h" |
| 24 #include "skia/ext/image_operations.h" |
| 19 #include "webkit/glue/context_menu.h" | 25 #include "webkit/glue/context_menu.h" |
| 20 | 26 |
| 21 ExtensionMenuItem::ExtensionMenuItem(const std::string& extension_id, | 27 ExtensionMenuItem::ExtensionMenuItem(const std::string& extension_id, |
| 22 std::string title, | 28 std::string title, |
| 23 bool checked, Type type, | 29 bool checked, Type type, |
| 24 const ContextList& contexts, | 30 const ContextList& contexts, |
| 25 const ContextList& enabled_contexts) | 31 const ContextList& enabled_contexts) |
| 26 : extension_id_(extension_id), | 32 : extension_id_(extension_id), |
| 27 title_(title), | 33 title_(title), |
| 28 id_(0), | 34 id_(0), |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 return false; | 95 return false; |
| 90 checked_ = checked; | 96 checked_ = checked; |
| 91 return true; | 97 return true; |
| 92 } | 98 } |
| 93 | 99 |
| 94 void ExtensionMenuItem::AddChild(ExtensionMenuItem* item) { | 100 void ExtensionMenuItem::AddChild(ExtensionMenuItem* item) { |
| 95 item->parent_id_ = id_; | 101 item->parent_id_ = id_; |
| 96 children_.push_back(item); | 102 children_.push_back(item); |
| 97 } | 103 } |
| 98 | 104 |
| 99 ExtensionMenuManager::ExtensionMenuManager() : next_item_id_(1) { | 105 ExtensionMenuManager::ExtensionMenuManager() |
| 106 : next_item_id_(1), |
| 107 ALLOW_THIS_IN_INITIALIZER_LIST(image_tracker_(this)) { |
| 100 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, | 108 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, |
| 101 NotificationService::AllSources()); | 109 NotificationService::AllSources()); |
| 102 } | 110 } |
| 103 | 111 |
| 104 ExtensionMenuManager::~ExtensionMenuManager() { | 112 ExtensionMenuManager::~ExtensionMenuManager() { |
| 105 MenuItemMap::iterator i; | 113 MenuItemMap::iterator i; |
| 106 for (i = context_items_.begin(); i != context_items_.end(); ++i) { | 114 for (i = context_items_.begin(); i != context_items_.end(); ++i) { |
| 107 STLDeleteElements(&(i->second)); | 115 STLDeleteElements(&(i->second)); |
| 108 } | 116 } |
| 109 } | 117 } |
| 110 | 118 |
| 111 std::set<std::string> ExtensionMenuManager::ExtensionIds() { | 119 std::set<std::string> ExtensionMenuManager::ExtensionIds() { |
| 112 std::set<std::string> id_set; | 120 std::set<std::string> id_set; |
| 113 for (MenuItemMap::const_iterator i = context_items_.begin(); | 121 for (MenuItemMap::const_iterator i = context_items_.begin(); |
| 114 i != context_items_.end(); ++i) { | 122 i != context_items_.end(); ++i) { |
| 115 id_set.insert(i->first); | 123 id_set.insert(i->first); |
| 116 } | 124 } |
| 117 return id_set; | 125 return id_set; |
| 118 } | 126 } |
| 119 | 127 |
| 120 const ExtensionMenuItem::List* ExtensionMenuManager::MenuItems( | 128 const ExtensionMenuItem::List* ExtensionMenuManager::MenuItems( |
| 121 const std::string& extension_id) { | 129 const std::string& extension_id) { |
| 122 MenuItemMap::iterator i = context_items_.find(extension_id); | 130 MenuItemMap::iterator i = context_items_.find(extension_id); |
| 123 if (i != context_items_.end()) { | 131 if (i != context_items_.end()) { |
| 124 return &(i->second); | 132 return &(i->second); |
| 125 } | 133 } |
| 126 return NULL; | 134 return NULL; |
| 127 } | 135 } |
| 128 | 136 |
| 129 int ExtensionMenuManager::AddContextItem(ExtensionMenuItem* item) { | 137 int ExtensionMenuManager::AddContextItem(Extension* extension, |
| 138 ExtensionMenuItem* item) { |
| 130 const std::string& extension_id = item->extension_id(); | 139 const std::string& extension_id = item->extension_id(); |
| 131 // The item must have a non-empty extension id. | 140 // The item must have a non-empty extension id. |
| 132 if (extension_id.empty()) | 141 if (extension_id.empty()) |
| 133 return 0; | 142 return 0; |
| 134 | 143 |
| 144 DCHECK_EQ(extension->id(), extension_id); |
| 145 |
| 135 DCHECK_EQ(0, item->id()); | 146 DCHECK_EQ(0, item->id()); |
| 136 item->set_id(next_item_id_++); | 147 item->set_id(next_item_id_++); |
| 137 | 148 |
| 149 bool first_item = !ContainsKey(context_items_, extension_id); |
| 138 context_items_[extension_id].push_back(item); | 150 context_items_[extension_id].push_back(item); |
| 139 items_by_id_[item->id()] = item; | 151 items_by_id_[item->id()] = item; |
| 140 | 152 |
| 141 if (item->type() == ExtensionMenuItem::RADIO && item->checked()) | 153 if (item->type() == ExtensionMenuItem::RADIO && item->checked()) |
| 142 RadioItemSelected(item); | 154 RadioItemSelected(item); |
| 143 | 155 |
| 156 // If this is the first item for this extension, start loading its icon. |
| 157 if (first_item) { |
| 158 ExtensionResource icon_resource; |
| 159 extension->GetIconPathAllowLargerSize(&icon_resource, |
| 160 Extension::EXTENSION_ICON_BITTY); |
| 161 if (!icon_resource.extension_root().empty()) { |
| 162 image_tracker_.LoadImage(extension, |
| 163 icon_resource, |
| 164 gfx::Size(kFavIconSize, kFavIconSize), |
| 165 ImageLoadingTracker::CACHE); |
| 166 } |
| 167 } |
| 168 |
| 144 return item->id(); | 169 return item->id(); |
| 145 } | 170 } |
| 146 | 171 |
| 147 int ExtensionMenuManager::AddChildItem(int parent_id, | 172 int ExtensionMenuManager::AddChildItem(int parent_id, |
| 148 ExtensionMenuItem* child) { | 173 ExtensionMenuItem* child) { |
| 149 ExtensionMenuItem* parent = GetItemById(parent_id); | 174 ExtensionMenuItem* parent = GetItemById(parent_id); |
| 150 if (!parent || parent->type() != ExtensionMenuItem::NORMAL || | 175 if (!parent || parent->type() != ExtensionMenuItem::NORMAL || |
| 151 parent->extension_id() != child->extension_id()) | 176 parent->extension_id() != child->extension_id()) |
| 152 return 0; | 177 return 0; |
| 153 child->set_id(next_item_id_++); | 178 child->set_id(next_item_id_++); |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 226 if (!ContainsKey(items_by_id_, id)) | 251 if (!ContainsKey(items_by_id_, id)) |
| 227 return false; | 252 return false; |
| 228 | 253 |
| 229 std::string extension_id = GetItemById(id)->extension_id(); | 254 std::string extension_id = GetItemById(id)->extension_id(); |
| 230 MenuItemMap::iterator i = context_items_.find(extension_id); | 255 MenuItemMap::iterator i = context_items_.find(extension_id); |
| 231 if (i == context_items_.end()) { | 256 if (i == context_items_.end()) { |
| 232 NOTREACHED(); | 257 NOTREACHED(); |
| 233 return false; | 258 return false; |
| 234 } | 259 } |
| 235 | 260 |
| 261 bool result = false; |
| 236 ExtensionMenuItem::List& list = i->second; | 262 ExtensionMenuItem::List& list = i->second; |
| 237 ExtensionMenuItem::List::iterator j; | 263 ExtensionMenuItem::List::iterator j; |
| 238 for (j = list.begin(); j < list.end(); ++j) { | 264 for (j = list.begin(); j < list.end(); ++j) { |
| 239 // See if the current item is a match, or if one of its children was. | 265 // See if the current item is a match, or if one of its children was. |
| 240 if ((*j)->id() == id) { | 266 if ((*j)->id() == id) { |
| 241 delete *j; | 267 delete *j; |
| 242 list.erase(j); | 268 list.erase(j); |
| 243 items_by_id_.erase(id); | 269 items_by_id_.erase(id); |
| 244 return true; | 270 result = true; |
| 271 break; |
| 245 } else if ((*j)->RemoveChild(id)) { | 272 } else if ((*j)->RemoveChild(id)) { |
| 246 items_by_id_.erase(id); | 273 items_by_id_.erase(id); |
| 247 return true; | 274 result = true; |
| 275 break; |
| 248 } | 276 } |
| 249 } | 277 } |
| 250 NOTREACHED(); // The check at the very top should prevent getting here. | 278 DCHECK(result); // The check at the very top should have prevented this. |
| 251 return false; | 279 |
| 280 if (list.empty() && ContainsKey(extension_icons_, extension_id)) |
| 281 extension_icons_.erase(extension_id); |
| 282 |
| 283 return result; |
| 252 } | 284 } |
| 253 | 285 |
| 254 void ExtensionMenuManager::RemoveAllContextItems(std::string extension_id) { | 286 void ExtensionMenuManager::RemoveAllContextItems(std::string extension_id) { |
| 255 ExtensionMenuItem::List::iterator i; | 287 ExtensionMenuItem::List::iterator i; |
| 256 for (i = context_items_[extension_id].begin(); | 288 for (i = context_items_[extension_id].begin(); |
| 257 i != context_items_[extension_id].end(); ++i) { | 289 i != context_items_[extension_id].end(); ++i) { |
| 258 ExtensionMenuItem* item = *i; | 290 ExtensionMenuItem* item = *i; |
| 259 items_by_id_.erase(item->id()); | 291 items_by_id_.erase(item->id()); |
| 260 | 292 |
| 261 // Remove descendants from this item and erase them from the lookup cache. | 293 // Remove descendants from this item and erase them from the lookup cache. |
| 262 std::set<int> removed_ids = item->RemoveAllDescendants(); | 294 std::set<int> removed_ids = item->RemoveAllDescendants(); |
| 263 for (std::set<int>::const_iterator j = removed_ids.begin(); | 295 for (std::set<int>::const_iterator j = removed_ids.begin(); |
| 264 j != removed_ids.end(); ++j) { | 296 j != removed_ids.end(); ++j) { |
| 265 items_by_id_.erase(*j); | 297 items_by_id_.erase(*j); |
| 266 } | 298 } |
| 267 } | 299 } |
| 268 STLDeleteElements(&context_items_[extension_id]); | 300 STLDeleteElements(&context_items_[extension_id]); |
| 269 context_items_.erase(extension_id); | 301 context_items_.erase(extension_id); |
| 302 |
| 303 if (ContainsKey(extension_icons_, extension_id)) |
| 304 extension_icons_.erase(extension_id); |
| 270 } | 305 } |
| 271 | 306 |
| 272 ExtensionMenuItem* ExtensionMenuManager::GetItemById(int id) const { | 307 ExtensionMenuItem* ExtensionMenuManager::GetItemById(int id) const { |
| 273 std::map<int, ExtensionMenuItem*>::const_iterator i = items_by_id_.find(id); | 308 std::map<int, ExtensionMenuItem*>::const_iterator i = items_by_id_.find(id); |
| 274 if (i != items_by_id_.end()) | 309 if (i != items_by_id_.end()) |
| 275 return i->second; | 310 return i->second; |
| 276 else | 311 else |
| 277 return NULL; | 312 return NULL; |
| 278 } | 313 } |
| 279 | 314 |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 415 // Remove menu items for disabled/uninstalled extensions. | 450 // Remove menu items for disabled/uninstalled extensions. |
| 416 if (type != NotificationType::EXTENSION_UNLOADED) { | 451 if (type != NotificationType::EXTENSION_UNLOADED) { |
| 417 NOTREACHED(); | 452 NOTREACHED(); |
| 418 return; | 453 return; |
| 419 } | 454 } |
| 420 Extension* extension = Details<Extension>(details).ptr(); | 455 Extension* extension = Details<Extension>(details).ptr(); |
| 421 if (ContainsKey(context_items_, extension->id())) { | 456 if (ContainsKey(context_items_, extension->id())) { |
| 422 RemoveAllContextItems(extension->id()); | 457 RemoveAllContextItems(extension->id()); |
| 423 } | 458 } |
| 424 } | 459 } |
| 460 |
| 461 const SkBitmap& ExtensionMenuManager::GetIconForExtension( |
| 462 const std::string& extension_id) { |
| 463 const SkBitmap* result = NULL; |
| 464 if (ContainsKey(extension_icons_, extension_id)) { |
| 465 result = &(extension_icons_[extension_id]); |
| 466 } else { |
| 467 EnsureDefaultIcon(); |
| 468 result = &default_icon_; |
| 469 } |
| 470 DCHECK(result); |
| 471 DCHECK(result->width() == kFavIconSize); |
| 472 DCHECK(result->height() == kFavIconSize); |
| 473 return *result; |
| 474 } |
| 475 |
| 476 void ExtensionMenuManager::OnImageLoaded(SkBitmap* image, |
| 477 ExtensionResource resource, |
| 478 int index) { |
| 479 if (!image) |
| 480 return; |
| 481 |
| 482 const std::string extension_id = resource.extension_id(); |
| 483 |
| 484 // Make sure we still have menu items for this extension (since image loading |
| 485 // is asynchronous, there's a slight chance they may have all been removed |
| 486 // while the icon was loading). |
| 487 if (!ContainsKey(context_items_, extension_id)) |
| 488 return; |
| 489 |
| 490 if (image->width() == kFavIconSize && image->height() == kFavIconSize) |
| 491 extension_icons_[extension_id] = *image; |
| 492 else |
| 493 extension_icons_[extension_id] = ScaleToFavIconSize(*image); |
| 494 } |
| 495 |
| 496 void ExtensionMenuManager::EnsureDefaultIcon() { |
| 497 if (default_icon_.empty()) { |
| 498 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 499 SkBitmap* src = rb.GetBitmapNamed(IDR_EXTENSIONS_SECTION); |
| 500 if (src->width() == kFavIconSize && src->height() == kFavIconSize) { |
| 501 default_icon_ = *src; |
| 502 } else { |
| 503 default_icon_ = SkBitmap(ScaleToFavIconSize(*src)); |
| 504 } |
| 505 } |
| 506 } |
| 507 |
| 508 SkBitmap ExtensionMenuManager::ScaleToFavIconSize(const SkBitmap& source) { |
| 509 return skia::ImageOperations::Resize(source, |
| 510 skia::ImageOperations::RESIZE_LANCZOS3, |
| 511 kFavIconSize, |
| 512 kFavIconSize); |
| 513 } |
| OLD | NEW |