| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/menu_manager.h" | 5 #include "chrome/browser/extensions/menu_manager.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
| 12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
| 13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
| 14 #include "base/values.h" | 14 #include "base/values.h" |
| 15 #include "chrome/browser/chrome_notification_types.h" | 15 #include "chrome/browser/chrome_notification_types.h" |
| 16 #include "chrome/browser/extensions/event_names.h" | 16 #include "chrome/browser/extensions/event_names.h" |
| 17 #include "chrome/browser/extensions/extension_service.h" | 17 #include "chrome/browser/extensions/extension_service.h" |
| 18 #include "chrome/browser/extensions/extension_tab_util.h" | 18 #include "chrome/browser/extensions/extension_tab_util.h" |
| 19 #include "chrome/browser/extensions/menu_manager_factory.h" | 19 #include "chrome/browser/extensions/menu_manager_factory.h" |
| 20 #include "chrome/browser/extensions/state_store.h" | 20 #include "chrome/browser/extensions/state_store.h" |
| 21 #include "chrome/browser/extensions/tab_helper.h" | 21 #include "chrome/browser/extensions/tab_helper.h" |
| 22 #include "chrome/browser/guestview/webview/webview_guest.h" |
| 22 #include "chrome/browser/profiles/profile.h" | 23 #include "chrome/browser/profiles/profile.h" |
| 23 #include "chrome/common/extensions/api/context_menus.h" | 24 #include "chrome/common/extensions/api/context_menus.h" |
| 24 #include "content/public/browser/notification_details.h" | 25 #include "content/public/browser/notification_details.h" |
| 25 #include "content/public/browser/notification_service.h" | 26 #include "content/public/browser/notification_service.h" |
| 26 #include "content/public/browser/notification_source.h" | 27 #include "content/public/browser/notification_source.h" |
| 27 #include "content/public/browser/web_contents.h" | 28 #include "content/public/browser/web_contents.h" |
| 28 #include "content/public/common/context_menu_params.h" | 29 #include "content/public/common/context_menu_params.h" |
| 29 #include "extensions/browser/event_router.h" | 30 #include "extensions/browser/event_router.h" |
| 30 #include "extensions/browser/extension_system.h" | 31 #include "extensions/browser/extension_system.h" |
| 31 #include "extensions/common/extension.h" | 32 #include "extensions/common/extension.h" |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 215 return value.Pass(); | 216 return value.Pass(); |
| 216 } | 217 } |
| 217 | 218 |
| 218 // static | 219 // static |
| 219 MenuItem* MenuItem::Populate(const std::string& extension_id, | 220 MenuItem* MenuItem::Populate(const std::string& extension_id, |
| 220 const base::DictionaryValue& value, | 221 const base::DictionaryValue& value, |
| 221 std::string* error) { | 222 std::string* error) { |
| 222 bool incognito = false; | 223 bool incognito = false; |
| 223 if (!value.GetBoolean(kIncognitoKey, &incognito)) | 224 if (!value.GetBoolean(kIncognitoKey, &incognito)) |
| 224 return NULL; | 225 return NULL; |
| 225 Id id(incognito, extension_id); | 226 Id id(incognito, MenuItem::ExtensionKey(extension_id)); |
| 226 if (!value.GetString(kStringUIDKey, &id.string_uid)) | 227 if (!value.GetString(kStringUIDKey, &id.string_uid)) |
| 227 return NULL; | 228 return NULL; |
| 228 int type_int; | 229 int type_int; |
| 229 Type type = NORMAL; | 230 Type type = NORMAL; |
| 230 if (!value.GetInteger(kTypeKey, &type_int)) | 231 if (!value.GetInteger(kTypeKey, &type_int)) |
| 231 return NULL; | 232 return NULL; |
| 232 type = static_cast<Type>(type_int); | 233 type = static_cast<Type>(type_int); |
| 233 std::string title; | 234 std::string title; |
| 234 if (type != SEPARATOR && !value.GetString(kTitleKey, &title)) | 235 if (type != SEPARATOR && !value.GetString(kTitleKey, &title)) |
| 235 return NULL; | 236 return NULL; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 259 return NULL; | 260 return NULL; |
| 260 | 261 |
| 261 if (!result->PopulateURLPatterns(&document_url_patterns, | 262 if (!result->PopulateURLPatterns(&document_url_patterns, |
| 262 &target_url_patterns, | 263 &target_url_patterns, |
| 263 error)) { | 264 error)) { |
| 264 return NULL; | 265 return NULL; |
| 265 } | 266 } |
| 266 | 267 |
| 267 // parent_id is filled in from the value, but it might not be valid. It's left | 268 // parent_id is filled in from the value, but it might not be valid. It's left |
| 268 // to be validated upon being added (via AddChildItem) to the menu manager. | 269 // to be validated upon being added (via AddChildItem) to the menu manager. |
| 269 scoped_ptr<Id> parent_id(new Id(incognito, extension_id)); | 270 scoped_ptr<Id> parent_id( |
| 271 new Id(incognito, MenuItem::ExtensionKey(extension_id))); |
| 270 if (value.HasKey(kParentUIDKey)) { | 272 if (value.HasKey(kParentUIDKey)) { |
| 271 if (!value.GetString(kParentUIDKey, &parent_id->string_uid)) | 273 if (!value.GetString(kParentUIDKey, &parent_id->string_uid)) |
| 272 return NULL; | 274 return NULL; |
| 273 result->parent_id_.swap(parent_id); | 275 result->parent_id_.swap(parent_id); |
| 274 } | 276 } |
| 275 return result.release(); | 277 return result.release(); |
| 276 } | 278 } |
| 277 | 279 |
| 278 bool MenuItem::PopulateURLPatterns( | 280 bool MenuItem::PopulateURLPatterns( |
| 279 std::vector<std::string>* document_url_patterns, | 281 std::vector<std::string>* document_url_patterns, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 for (i = context_items_.begin(); i != context_items_.end(); ++i) { | 313 for (i = context_items_.begin(); i != context_items_.end(); ++i) { |
| 312 STLDeleteElements(&(i->second)); | 314 STLDeleteElements(&(i->second)); |
| 313 } | 315 } |
| 314 } | 316 } |
| 315 | 317 |
| 316 // static | 318 // static |
| 317 MenuManager* MenuManager::Get(Profile* profile) { | 319 MenuManager* MenuManager::Get(Profile* profile) { |
| 318 return MenuManagerFactory::GetForProfile(profile); | 320 return MenuManagerFactory::GetForProfile(profile); |
| 319 } | 321 } |
| 320 | 322 |
| 321 std::set<std::string> MenuManager::ExtensionIds() { | 323 std::set<MenuItem::ExtensionKey> MenuManager::ExtensionIds() { |
| 322 std::set<std::string> id_set; | 324 std::set<MenuItem::ExtensionKey> id_set; |
| 323 for (MenuItemMap::const_iterator i = context_items_.begin(); | 325 for (MenuItemMap::const_iterator i = context_items_.begin(); |
| 324 i != context_items_.end(); ++i) { | 326 i != context_items_.end(); ++i) { |
| 325 id_set.insert(i->first); | 327 id_set.insert(i->first); |
| 326 } | 328 } |
| 327 return id_set; | 329 return id_set; |
| 328 } | 330 } |
| 329 | 331 |
| 330 const MenuItem::List* MenuManager::MenuItems( | 332 const MenuItem::List* MenuManager::MenuItems( |
| 331 const std::string& extension_id) { | 333 const MenuItem::ExtensionKey& key) { |
| 332 MenuItemMap::iterator i = context_items_.find(extension_id); | 334 MenuItemMap::iterator i = context_items_.find(key); |
| 333 if (i != context_items_.end()) { | 335 if (i != context_items_.end()) { |
| 334 return &(i->second); | 336 return &(i->second); |
| 335 } | 337 } |
| 336 return NULL; | 338 return NULL; |
| 337 } | 339 } |
| 338 | 340 |
| 339 bool MenuManager::AddContextItem( | 341 bool MenuManager::AddContextItem(const Extension* extension, MenuItem* item) { |
| 340 const Extension* extension, | 342 const MenuItem::ExtensionKey& key = item->id().extension_key; |
| 341 MenuItem* item) { | |
| 342 const std::string& extension_id = item->extension_id(); | |
| 343 // The item must have a non-empty extension id, and not have already been | 343 // The item must have a non-empty extension id, and not have already been |
| 344 // added. | 344 // added. |
| 345 if (extension_id.empty() || ContainsKey(items_by_id_, item->id())) | 345 if (key.empty() || ContainsKey(items_by_id_, item->id())) |
| 346 return false; | 346 return false; |
| 347 | 347 |
| 348 DCHECK_EQ(extension->id(), extension_id); | 348 DCHECK_EQ(extension->id(), key.extension_id); |
| 349 | 349 |
| 350 bool first_item = !ContainsKey(context_items_, extension_id); | 350 bool first_item = !ContainsKey(context_items_, key); |
| 351 context_items_[extension_id].push_back(item); | 351 context_items_[key].push_back(item); |
| 352 items_by_id_[item->id()] = item; | 352 items_by_id_[item->id()] = item; |
| 353 | 353 |
| 354 if (item->type() == MenuItem::RADIO) { | 354 if (item->type() == MenuItem::RADIO) { |
| 355 if (item->checked()) | 355 if (item->checked()) |
| 356 RadioItemSelected(item); | 356 RadioItemSelected(item); |
| 357 else | 357 else |
| 358 SanitizeRadioList(context_items_[extension_id]); | 358 SanitizeRadioList(context_items_[key]); |
| 359 } | 359 } |
| 360 | 360 |
| 361 // If this is the first item for this extension, start loading its icon. | 361 // If this is the first item for this extension, start loading its icon. |
| 362 if (first_item) | 362 if (first_item) |
| 363 icon_manager_.LoadIcon(profile_, extension); | 363 icon_manager_.LoadIcon(profile_, extension); |
| 364 | 364 |
| 365 return true; | 365 return true; |
| 366 } | 366 } |
| 367 | 367 |
| 368 bool MenuManager::AddChildItem(const MenuItem::Id& parent_id, | 368 bool MenuManager::AddChildItem(const MenuItem::Id& parent_id, |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 417 NOTREACHED(); | 417 NOTREACHED(); |
| 418 return false; | 418 return false; |
| 419 } | 419 } |
| 420 MenuItem* taken = | 420 MenuItem* taken = |
| 421 old_parent->ReleaseChild(child_id, false /* non-recursive search*/); | 421 old_parent->ReleaseChild(child_id, false /* non-recursive search*/); |
| 422 DCHECK(taken == child); | 422 DCHECK(taken == child); |
| 423 SanitizeRadioList(old_parent->children()); | 423 SanitizeRadioList(old_parent->children()); |
| 424 } else { | 424 } else { |
| 425 // This is a top-level item, so we need to pull it out of our list of | 425 // This is a top-level item, so we need to pull it out of our list of |
| 426 // top-level items. | 426 // top-level items. |
| 427 MenuItemMap::iterator i = context_items_.find(child->extension_id()); | 427 const MenuItem::ExtensionKey& child_key = child->id().extension_key; |
| 428 MenuItemMap::iterator i = context_items_.find(child_key); |
| 428 if (i == context_items_.end()) { | 429 if (i == context_items_.end()) { |
| 429 NOTREACHED(); | 430 NOTREACHED(); |
| 430 return false; | 431 return false; |
| 431 } | 432 } |
| 432 MenuItem::List& list = i->second; | 433 MenuItem::List& list = i->second; |
| 433 MenuItem::List::iterator j = std::find(list.begin(), list.end(), | 434 MenuItem::List::iterator j = std::find(list.begin(), list.end(), child); |
| 434 child); | |
| 435 if (j == list.end()) { | 435 if (j == list.end()) { |
| 436 NOTREACHED(); | 436 NOTREACHED(); |
| 437 return false; | 437 return false; |
| 438 } | 438 } |
| 439 list.erase(j); | 439 list.erase(j); |
| 440 SanitizeRadioList(list); | 440 SanitizeRadioList(list); |
| 441 } | 441 } |
| 442 | 442 |
| 443 if (new_parent) { | 443 if (new_parent) { |
| 444 new_parent->AddChild(child); | 444 new_parent->AddChild(child); |
| 445 SanitizeRadioList(new_parent->children()); | 445 SanitizeRadioList(new_parent->children()); |
| 446 } else { | 446 } else { |
| 447 context_items_[child->extension_id()].push_back(child); | 447 const MenuItem::ExtensionKey& child_key = child->id().extension_key; |
| 448 context_items_[child_key].push_back(child); |
| 448 child->parent_id_.reset(NULL); | 449 child->parent_id_.reset(NULL); |
| 449 SanitizeRadioList(context_items_[child->extension_id()]); | 450 SanitizeRadioList(context_items_[child_key]); |
| 450 } | 451 } |
| 451 return true; | 452 return true; |
| 452 } | 453 } |
| 453 | 454 |
| 454 bool MenuManager::RemoveContextMenuItem(const MenuItem::Id& id) { | 455 bool MenuManager::RemoveContextMenuItem(const MenuItem::Id& id) { |
| 455 if (!ContainsKey(items_by_id_, id)) | 456 if (!ContainsKey(items_by_id_, id)) |
| 456 return false; | 457 return false; |
| 457 | 458 |
| 458 MenuItem* menu_item = GetItemById(id); | 459 MenuItem* menu_item = GetItemById(id); |
| 459 DCHECK(menu_item); | 460 DCHECK(menu_item); |
| 460 std::string extension_id = menu_item->extension_id(); | 461 const MenuItem::ExtensionKey extension_key = id.extension_key; |
| 461 MenuItemMap::iterator i = context_items_.find(extension_id); | 462 MenuItemMap::iterator i = context_items_.find(extension_key); |
| 462 if (i == context_items_.end()) { | 463 if (i == context_items_.end()) { |
| 463 NOTREACHED(); | 464 NOTREACHED(); |
| 464 return false; | 465 return false; |
| 465 } | 466 } |
| 466 | 467 |
| 467 bool result = false; | 468 bool result = false; |
| 468 std::set<MenuItem::Id> items_removed; | 469 std::set<MenuItem::Id> items_removed; |
| 469 MenuItem::List& list = i->second; | 470 MenuItem::List& list = i->second; |
| 470 MenuItem::List::iterator j; | 471 MenuItem::List::iterator j; |
| 471 for (j = list.begin(); j < list.end(); ++j) { | 472 for (j = list.begin(); j < list.end(); ++j) { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 496 | 497 |
| 497 // Clear entries from the items_by_id_ map. | 498 // Clear entries from the items_by_id_ map. |
| 498 std::set<MenuItem::Id>::iterator removed_iter; | 499 std::set<MenuItem::Id>::iterator removed_iter; |
| 499 for (removed_iter = items_removed.begin(); | 500 for (removed_iter = items_removed.begin(); |
| 500 removed_iter != items_removed.end(); | 501 removed_iter != items_removed.end(); |
| 501 ++removed_iter) { | 502 ++removed_iter) { |
| 502 items_by_id_.erase(*removed_iter); | 503 items_by_id_.erase(*removed_iter); |
| 503 } | 504 } |
| 504 | 505 |
| 505 if (list.empty()) { | 506 if (list.empty()) { |
| 506 context_items_.erase(extension_id); | 507 context_items_.erase(extension_key); |
| 507 icon_manager_.RemoveIcon(extension_id); | 508 icon_manager_.RemoveIcon(extension_key.extension_id); |
| 508 } | 509 } |
| 509 return result; | 510 return result; |
| 510 } | 511 } |
| 511 | 512 |
| 512 void MenuManager::RemoveAllContextItems(const std::string& extension_id) { | 513 void MenuManager::RemoveAllContextItems( |
| 514 const MenuItem::ExtensionKey& extension_key) { |
| 513 MenuItem::List::iterator i; | 515 MenuItem::List::iterator i; |
| 514 for (i = context_items_[extension_id].begin(); | 516 for (i = context_items_[extension_key].begin(); |
| 515 i != context_items_[extension_id].end(); ++i) { | 517 i != context_items_[extension_key].end(); |
| 518 ++i) { |
| 516 MenuItem* item = *i; | 519 MenuItem* item = *i; |
| 517 items_by_id_.erase(item->id()); | 520 items_by_id_.erase(item->id()); |
| 518 | 521 |
| 519 // Remove descendants from this item and erase them from the lookup cache. | 522 // Remove descendants from this item and erase them from the lookup cache. |
| 520 std::set<MenuItem::Id> removed_ids = item->RemoveAllDescendants(); | 523 std::set<MenuItem::Id> removed_ids = item->RemoveAllDescendants(); |
| 521 std::set<MenuItem::Id>::const_iterator j; | 524 std::set<MenuItem::Id>::const_iterator j; |
| 522 for (j = removed_ids.begin(); j != removed_ids.end(); ++j) { | 525 for (j = removed_ids.begin(); j != removed_ids.end(); ++j) { |
| 523 items_by_id_.erase(*j); | 526 items_by_id_.erase(*j); |
| 524 } | 527 } |
| 525 } | 528 } |
| 526 STLDeleteElements(&context_items_[extension_id]); | 529 STLDeleteElements(&context_items_[extension_key]); |
| 527 context_items_.erase(extension_id); | 530 context_items_.erase(extension_key); |
| 528 icon_manager_.RemoveIcon(extension_id); | 531 icon_manager_.RemoveIcon(extension_key.extension_id); |
| 529 } | 532 } |
| 530 | 533 |
| 531 MenuItem* MenuManager::GetItemById(const MenuItem::Id& id) const { | 534 MenuItem* MenuManager::GetItemById(const MenuItem::Id& id) const { |
| 532 std::map<MenuItem::Id, MenuItem*>::const_iterator i = | 535 std::map<MenuItem::Id, MenuItem*>::const_iterator i = |
| 533 items_by_id_.find(id); | 536 items_by_id_.find(id); |
| 534 if (i != items_by_id_.end()) | 537 if (i != items_by_id_.end()) |
| 535 return i->second; | 538 return i->second; |
| 536 else | 539 else |
| 537 return NULL; | 540 return NULL; |
| 538 } | 541 } |
| 539 | 542 |
| 540 void MenuManager::RadioItemSelected(MenuItem* item) { | 543 void MenuManager::RadioItemSelected(MenuItem* item) { |
| 541 // If this is a child item, we need to get a handle to the list from its | 544 // If this is a child item, we need to get a handle to the list from its |
| 542 // parent. Otherwise get a handle to the top-level list. | 545 // parent. Otherwise get a handle to the top-level list. |
| 543 const MenuItem::List* list = NULL; | 546 const MenuItem::List* list = NULL; |
| 544 if (item->parent_id()) { | 547 if (item->parent_id()) { |
| 545 MenuItem* parent = GetItemById(*item->parent_id()); | 548 MenuItem* parent = GetItemById(*item->parent_id()); |
| 546 if (!parent) { | 549 if (!parent) { |
| 547 NOTREACHED(); | 550 NOTREACHED(); |
| 548 return; | 551 return; |
| 549 } | 552 } |
| 550 list = &(parent->children()); | 553 list = &(parent->children()); |
| 551 } else { | 554 } else { |
| 552 if (context_items_.find(item->extension_id()) == context_items_.end()) { | 555 const MenuItem::ExtensionKey& key = item->id().extension_key; |
| 556 if (context_items_.find(key) == context_items_.end()) { |
| 553 NOTREACHED(); | 557 NOTREACHED(); |
| 554 return; | 558 return; |
| 555 } | 559 } |
| 556 list = &context_items_[item->extension_id()]; | 560 list = &context_items_[key]; |
| 557 } | 561 } |
| 558 | 562 |
| 559 // Find where |item| is in the list. | 563 // Find where |item| is in the list. |
| 560 MenuItem::List::const_iterator item_location; | 564 MenuItem::List::const_iterator item_location; |
| 561 for (item_location = list->begin(); item_location != list->end(); | 565 for (item_location = list->begin(); item_location != list->end(); |
| 562 ++item_location) { | 566 ++item_location) { |
| 563 if (*item_location == item) | 567 if (*item_location == item) |
| 564 break; | 568 break; |
| 565 } | 569 } |
| 566 if (item_location == list->end()) { | 570 if (item_location == list->end()) { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 603 if (!event_router) | 607 if (!event_router) |
| 604 return; | 608 return; |
| 605 | 609 |
| 606 MenuItem* item = GetItemById(menu_item_id); | 610 MenuItem* item = GetItemById(menu_item_id); |
| 607 if (!item) | 611 if (!item) |
| 608 return; | 612 return; |
| 609 | 613 |
| 610 // ExtensionService/Extension can be NULL in unit tests :( | 614 // ExtensionService/Extension can be NULL in unit tests :( |
| 611 ExtensionService* service = | 615 ExtensionService* service = |
| 612 ExtensionSystem::Get(profile_)->extension_service(); | 616 ExtensionSystem::Get(profile_)->extension_service(); |
| 613 const Extension* extension = service ? | 617 const Extension* extension = |
| 614 service->extensions()->GetByID(menu_item_id.extension_id) : NULL; | 618 service ? service->extensions()->GetByID(item->extension_id()) : NULL; |
| 615 | 619 |
| 616 if (item->type() == MenuItem::RADIO) | 620 if (item->type() == MenuItem::RADIO) |
| 617 RadioItemSelected(item); | 621 RadioItemSelected(item); |
| 618 | 622 |
| 619 scoped_ptr<base::ListValue> args(new base::ListValue()); | 623 scoped_ptr<base::ListValue> args(new base::ListValue()); |
| 620 | 624 |
| 621 base::DictionaryValue* properties = new base::DictionaryValue(); | 625 base::DictionaryValue* properties = new base::DictionaryValue(); |
| 622 SetIdKeyValue(properties, "menuItemId", item->id()); | 626 SetIdKeyValue(properties, "menuItemId", item->id()); |
| 623 if (item->parent_id()) | 627 if (item->parent_id()) |
| 624 SetIdKeyValue(properties, "parentMenuItemId", *item->parent_id()); | 628 SetIdKeyValue(properties, "parentMenuItemId", *item->parent_id()); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 639 AddURLProperty(properties, "linkUrl", params.unfiltered_link_url); | 643 AddURLProperty(properties, "linkUrl", params.unfiltered_link_url); |
| 640 AddURLProperty(properties, "srcUrl", params.src_url); | 644 AddURLProperty(properties, "srcUrl", params.src_url); |
| 641 AddURLProperty(properties, "pageUrl", params.page_url); | 645 AddURLProperty(properties, "pageUrl", params.page_url); |
| 642 AddURLProperty(properties, "frameUrl", params.frame_url); | 646 AddURLProperty(properties, "frameUrl", params.frame_url); |
| 643 | 647 |
| 644 if (params.selection_text.length() > 0) | 648 if (params.selection_text.length() > 0) |
| 645 properties->SetString("selectionText", params.selection_text); | 649 properties->SetString("selectionText", params.selection_text); |
| 646 | 650 |
| 647 properties->SetBoolean("editable", params.is_editable); | 651 properties->SetBoolean("editable", params.is_editable); |
| 648 | 652 |
| 653 WebViewGuest* webview_guest = WebViewGuest::FromWebContents(web_contents); |
| 654 if (webview_guest) { |
| 655 // This is used in webview_custom_bindings.js. |
| 656 // The property is not exposed to developer API. |
| 657 properties->SetInteger("webviewInstanceId", |
| 658 webview_guest->view_instance_id()); |
| 659 } |
| 660 |
| 649 args->Append(properties); | 661 args->Append(properties); |
| 650 | 662 |
| 651 // Add the tab info to the argument list. | 663 // Add the tab info to the argument list. |
| 652 // No tab info in a platform app. | 664 // No tab info in a platform app. |
| 653 if (!extension || !extension->is_platform_app()) { | 665 if (!extension || !extension->is_platform_app()) { |
| 654 // Note: web_contents are NULL in unit tests :( | 666 // Note: web_contents are NULL in unit tests :( |
| 655 if (web_contents) { | 667 if (web_contents) { |
| 656 args->Append(ExtensionTabUtil::CreateTabValue(web_contents)); | 668 args->Append(ExtensionTabUtil::CreateTabValue(web_contents)); |
| 657 } else { | 669 } else { |
| 658 args->Append(new base::DictionaryValue()); | 670 args->Append(new base::DictionaryValue()); |
| 659 } | 671 } |
| 660 } | 672 } |
| 661 | 673 |
| 662 if (item->type() == MenuItem::CHECKBOX || | 674 if (item->type() == MenuItem::CHECKBOX || |
| 663 item->type() == MenuItem::RADIO) { | 675 item->type() == MenuItem::RADIO) { |
| 664 bool was_checked = item->checked(); | 676 bool was_checked = item->checked(); |
| 665 properties->SetBoolean("wasChecked", was_checked); | 677 properties->SetBoolean("wasChecked", was_checked); |
| 666 | 678 |
| 667 // RADIO items always get set to true when you click on them, but CHECKBOX | 679 // RADIO items always get set to true when you click on them, but CHECKBOX |
| 668 // items get their state toggled. | 680 // items get their state toggled. |
| 669 bool checked = | 681 bool checked = |
| 670 (item->type() == MenuItem::RADIO) ? true : !was_checked; | 682 (item->type() == MenuItem::RADIO) ? true : !was_checked; |
| 671 | 683 |
| 672 item->SetChecked(checked); | 684 item->SetChecked(checked); |
| 673 properties->SetBoolean("checked", item->checked()); | 685 properties->SetBoolean("checked", item->checked()); |
| 674 | 686 |
| 675 if (extension) | 687 if (extension) |
| 676 WriteToStorage(extension); | 688 WriteToStorage(extension, item->id().extension_key); |
| 677 } | 689 } |
| 678 | 690 |
| 679 // Note: web_contents are NULL in unit tests :( | 691 // Note: web_contents are NULL in unit tests :( |
| 680 if (web_contents && extensions::TabHelper::FromWebContents(web_contents)) { | 692 if (web_contents && extensions::TabHelper::FromWebContents(web_contents)) { |
| 681 extensions::TabHelper::FromWebContents(web_contents)-> | 693 extensions::TabHelper::FromWebContents(web_contents)-> |
| 682 active_tab_permission_granter()->GrantIfRequested(extension); | 694 active_tab_permission_granter()->GrantIfRequested(extension); |
| 683 } | 695 } |
| 684 | 696 |
| 685 { | 697 { |
| 698 // Dispatch to menu item's .onclick handler. |
| 686 scoped_ptr<Event> event(new Event( | 699 scoped_ptr<Event> event(new Event( |
| 687 event_names::kOnContextMenus, | 700 event_names::kOnContextMenus, |
| 688 scoped_ptr<base::ListValue>(args->DeepCopy()))); | 701 scoped_ptr<base::ListValue>(args->DeepCopy()))); |
| 689 event->restrict_to_browser_context = profile; | 702 event->restrict_to_browser_context = profile; |
| 690 event->user_gesture = EventRouter::USER_GESTURE_ENABLED; | 703 event->user_gesture = EventRouter::USER_GESTURE_ENABLED; |
| 691 event_router->DispatchEventToExtension(item->extension_id(), event.Pass()); | 704 event_router->DispatchEventToExtension(item->extension_id(), event.Pass()); |
| 692 } | 705 } |
| 693 { | 706 { |
| 694 scoped_ptr<Event> event(new Event(context_menus::OnClicked::kEventName, | 707 // Dispatch to .contextMenus.onClicked handler. |
| 695 args.Pass())); | 708 scoped_ptr<Event> event( |
| 709 new Event(webview_guest ? event_names::kOnWebviewContextMenus |
| 710 : context_menus::OnClicked::kEventName, |
| 711 args.Pass())); |
| 696 event->restrict_to_browser_context = profile; | 712 event->restrict_to_browser_context = profile; |
| 697 event->user_gesture = EventRouter::USER_GESTURE_ENABLED; | 713 event->user_gesture = EventRouter::USER_GESTURE_ENABLED; |
| 714 if (webview_guest) |
| 715 event->filter_info.SetInstanceID(webview_guest->view_instance_id()); |
| 698 event_router->DispatchEventToExtension(item->extension_id(), event.Pass()); | 716 event_router->DispatchEventToExtension(item->extension_id(), event.Pass()); |
| 699 } | 717 } |
| 700 } | 718 } |
| 701 | 719 |
| 702 void MenuManager::SanitizeRadioList(const MenuItem::List& item_list) { | 720 void MenuManager::SanitizeRadioList(const MenuItem::List& item_list) { |
| 703 MenuItem::List::const_iterator i = item_list.begin(); | 721 MenuItem::List::const_iterator i = item_list.begin(); |
| 704 while (i != item_list.end()) { | 722 while (i != item_list.end()) { |
| 705 if ((*i)->type() != MenuItem::RADIO) { | 723 if ((*i)->type() != MenuItem::RADIO) { |
| 706 ++i; | 724 ++i; |
| 707 break; | 725 break; |
| (...skipping 28 matching lines...) Expand all Loading... |
| 736 bool MenuManager::ItemUpdated(const MenuItem::Id& id) { | 754 bool MenuManager::ItemUpdated(const MenuItem::Id& id) { |
| 737 if (!ContainsKey(items_by_id_, id)) | 755 if (!ContainsKey(items_by_id_, id)) |
| 738 return false; | 756 return false; |
| 739 | 757 |
| 740 MenuItem* menu_item = GetItemById(id); | 758 MenuItem* menu_item = GetItemById(id); |
| 741 DCHECK(menu_item); | 759 DCHECK(menu_item); |
| 742 | 760 |
| 743 if (menu_item->parent_id()) { | 761 if (menu_item->parent_id()) { |
| 744 SanitizeRadioList(GetItemById(*menu_item->parent_id())->children()); | 762 SanitizeRadioList(GetItemById(*menu_item->parent_id())->children()); |
| 745 } else { | 763 } else { |
| 746 std::string extension_id = menu_item->extension_id(); | 764 MenuItemMap::iterator i = |
| 747 MenuItemMap::iterator i = context_items_.find(extension_id); | 765 context_items_.find(menu_item->id().extension_key); |
| 748 if (i == context_items_.end()) { | 766 if (i == context_items_.end()) { |
| 749 NOTREACHED(); | 767 NOTREACHED(); |
| 750 return false; | 768 return false; |
| 751 } | 769 } |
| 752 SanitizeRadioList(i->second); | 770 SanitizeRadioList(i->second); |
| 753 } | 771 } |
| 754 | 772 |
| 755 return true; | 773 return true; |
| 756 } | 774 } |
| 757 | 775 |
| 758 void MenuManager::WriteToStorage(const Extension* extension) { | 776 void MenuManager::WriteToStorage(const Extension* extension, |
| 777 const MenuItem::ExtensionKey& extension_key) { |
| 759 if (!BackgroundInfo::HasLazyBackgroundPage(extension)) | 778 if (!BackgroundInfo::HasLazyBackgroundPage(extension)) |
| 760 return; | 779 return; |
| 761 const MenuItem::List* top_items = MenuItems(extension->id()); | 780 // <webview> menu items are transient and not stored in storage. |
| 781 if (extension_key.webview_instance_id) |
| 782 return; |
| 783 const MenuItem::List* top_items = MenuItems(extension_key); |
| 762 MenuItem::List all_items; | 784 MenuItem::List all_items; |
| 763 if (top_items) { | 785 if (top_items) { |
| 764 for (MenuItem::List::const_iterator i = top_items->begin(); | 786 for (MenuItem::List::const_iterator i = top_items->begin(); |
| 765 i != top_items->end(); ++i) { | 787 i != top_items->end(); ++i) { |
| 788 DCHECK(!(*i)->id().extension_key.webview_instance_id); |
| 766 (*i)->GetFlattenedSubtree(&all_items); | 789 (*i)->GetFlattenedSubtree(&all_items); |
| 767 } | 790 } |
| 768 } | 791 } |
| 769 | 792 |
| 770 if (store_) | 793 if (store_) { |
| 771 store_->SetExtensionValue(extension->id(), kContextMenusKey, | 794 store_->SetExtensionValue(extension->id(), kContextMenusKey, |
| 772 MenuItemsToValue(all_items)); | 795 MenuItemsToValue(all_items)); |
| 796 } |
| 773 } | 797 } |
| 774 | 798 |
| 775 void MenuManager::ReadFromStorage(const std::string& extension_id, | 799 void MenuManager::ReadFromStorage(const std::string& extension_id, |
| 776 scoped_ptr<base::Value> value) { | 800 scoped_ptr<base::Value> value) { |
| 777 const Extension* extension = | 801 const Extension* extension = |
| 778 ExtensionSystem::Get(profile_)->extension_service()->extensions()-> | 802 ExtensionSystem::Get(profile_)->extension_service()->extensions()-> |
| 779 GetByID(extension_id); | 803 GetByID(extension_id); |
| 780 if (!extension) | 804 if (!extension) |
| 781 return; | 805 return; |
| 782 | 806 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 797 } | 821 } |
| 798 | 822 |
| 799 void MenuManager::Observe(int type, | 823 void MenuManager::Observe(int type, |
| 800 const content::NotificationSource& source, | 824 const content::NotificationSource& source, |
| 801 const content::NotificationDetails& details) { | 825 const content::NotificationDetails& details) { |
| 802 switch (type) { | 826 switch (type) { |
| 803 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | 827 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { |
| 804 // Remove menu items for disabled/uninstalled extensions. | 828 // Remove menu items for disabled/uninstalled extensions. |
| 805 const Extension* extension = | 829 const Extension* extension = |
| 806 content::Details<UnloadedExtensionInfo>(details)->extension; | 830 content::Details<UnloadedExtensionInfo>(details)->extension; |
| 807 if (ContainsKey(context_items_, extension->id())) { | 831 MenuItem::ExtensionKey extension_key(extension->id()); |
| 808 RemoveAllContextItems(extension->id()); | 832 if (ContainsKey(context_items_, extension_key)) { |
| 833 RemoveAllContextItems(extension_key); |
| 809 } | 834 } |
| 810 break; | 835 break; |
| 811 } | 836 } |
| 812 case chrome::NOTIFICATION_EXTENSION_LOADED: { | 837 case chrome::NOTIFICATION_EXTENSION_LOADED: { |
| 813 const Extension* extension = | 838 const Extension* extension = |
| 814 content::Details<const Extension>(details).ptr(); | 839 content::Details<const Extension>(details).ptr(); |
| 815 if (store_ && BackgroundInfo::HasLazyBackgroundPage(extension)) { | 840 if (store_ && BackgroundInfo::HasLazyBackgroundPage(extension)) { |
| 816 store_->GetExtensionValue(extension->id(), kContextMenusKey, | 841 store_->GetExtensionValue(extension->id(), kContextMenusKey, |
| 817 base::Bind(&MenuManager::ReadFromStorage, | 842 base::Bind(&MenuManager::ReadFromStorage, |
| 818 AsWeakPtr(), extension->id())); | 843 AsWeakPtr(), extension->id())); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 852 items_to_remove.insert(iter->first); | 877 items_to_remove.insert(iter->first); |
| 853 } | 878 } |
| 854 | 879 |
| 855 std::set<MenuItem::Id>::iterator remove_iter; | 880 std::set<MenuItem::Id>::iterator remove_iter; |
| 856 for (remove_iter = items_to_remove.begin(); | 881 for (remove_iter = items_to_remove.begin(); |
| 857 remove_iter != items_to_remove.end(); | 882 remove_iter != items_to_remove.end(); |
| 858 ++remove_iter) | 883 ++remove_iter) |
| 859 RemoveContextMenuItem(*remove_iter); | 884 RemoveContextMenuItem(*remove_iter); |
| 860 } | 885 } |
| 861 | 886 |
| 887 MenuItem::ExtensionKey::ExtensionKey() : webview_instance_id(0) {} |
| 888 |
| 889 MenuItem::ExtensionKey::ExtensionKey(const std::string& extension_id, |
| 890 int webview_instance_id) |
| 891 : extension_id(extension_id), webview_instance_id(webview_instance_id) {} |
| 892 |
| 893 MenuItem::ExtensionKey::ExtensionKey(const std::string& extension_id) |
| 894 : extension_id(extension_id), webview_instance_id(0) {} |
| 895 |
| 896 bool MenuItem::ExtensionKey::operator==(const ExtensionKey& other) const { |
| 897 return extension_id == other.extension_id && |
| 898 webview_instance_id == other.webview_instance_id; |
| 899 } |
| 900 |
| 901 bool MenuItem::ExtensionKey::operator<(const ExtensionKey& other) const { |
| 902 if (extension_id != other.extension_id) |
| 903 return extension_id < other.extension_id; |
| 904 |
| 905 return webview_instance_id < other.webview_instance_id; |
| 906 } |
| 907 |
| 908 bool MenuItem::ExtensionKey::operator!=(const ExtensionKey& other) const { |
| 909 return !(*this == other); |
| 910 } |
| 911 |
| 912 bool MenuItem::ExtensionKey::empty() const { |
| 913 return extension_id.empty() && !webview_instance_id; |
| 914 } |
| 915 |
| 862 MenuItem::Id::Id() : incognito(false), uid(0) {} | 916 MenuItem::Id::Id() : incognito(false), uid(0) {} |
| 863 | 917 |
| 864 MenuItem::Id::Id(bool incognito, const std::string& extension_id) | 918 MenuItem::Id::Id(bool incognito, const MenuItem::ExtensionKey& extension_key) |
| 865 : incognito(incognito), extension_id(extension_id), uid(0) {} | 919 : incognito(incognito), extension_key(extension_key), uid(0) {} |
| 866 | 920 |
| 867 MenuItem::Id::~Id() { | 921 MenuItem::Id::~Id() { |
| 868 } | 922 } |
| 869 | 923 |
| 870 bool MenuItem::Id::operator==(const Id& other) const { | 924 bool MenuItem::Id::operator==(const Id& other) const { |
| 871 return (incognito == other.incognito && | 925 return (incognito == other.incognito && |
| 872 extension_id == other.extension_id && | 926 extension_key == other.extension_key && uid == other.uid && |
| 873 uid == other.uid && | |
| 874 string_uid == other.string_uid); | 927 string_uid == other.string_uid); |
| 875 } | 928 } |
| 876 | 929 |
| 877 bool MenuItem::Id::operator!=(const Id& other) const { | 930 bool MenuItem::Id::operator!=(const Id& other) const { |
| 878 return !(*this == other); | 931 return !(*this == other); |
| 879 } | 932 } |
| 880 | 933 |
| 881 bool MenuItem::Id::operator<(const Id& other) const { | 934 bool MenuItem::Id::operator<(const Id& other) const { |
| 882 if (incognito < other.incognito) | 935 if (incognito < other.incognito) |
| 883 return true; | 936 return true; |
| 884 if (incognito == other.incognito) { | 937 if (incognito == other.incognito) { |
| 885 if (extension_id < other.extension_id) | 938 if (extension_key < other.extension_key) |
| 886 return true; | 939 return true; |
| 887 if (extension_id == other.extension_id) { | 940 if (extension_key == other.extension_key) { |
| 888 if (uid < other.uid) | 941 if (uid < other.uid) |
| 889 return true; | 942 return true; |
| 890 if (uid == other.uid) | 943 if (uid == other.uid) |
| 891 return string_uid < other.string_uid; | 944 return string_uid < other.string_uid; |
| 892 } | 945 } |
| 893 } | 946 } |
| 894 return false; | 947 return false; |
| 895 } | 948 } |
| 896 | 949 |
| 897 } // namespace extensions | 950 } // namespace extensions |
| OLD | NEW |