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/ui/toolbar/action_box_button_controller.h" | 5 #include "chrome/browser/ui/toolbar/action_box_button_controller.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
9 #include "base/utf_string_conversions.h" | 9 #include "base/utf_string_conversions.h" |
| 10 #include "chrome/app/chrome_command_ids.h" |
| 11 #include "chrome/browser/extensions/api/page_launcher/page_launcher_api.h" |
10 #include "chrome/browser/extensions/extension_service.h" | 12 #include "chrome/browser/extensions/extension_service.h" |
11 #include "chrome/browser/extensions/extension_system.h" | 13 #include "chrome/browser/extensions/extension_system.h" |
12 #include "chrome/browser/profiles/profile.h" | 14 #include "chrome/browser/profiles/profile.h" |
13 #include "chrome/browser/ui/browser.h" | 15 #include "chrome/browser/ui/browser.h" |
14 #include "chrome/browser/ui/browser_commands.h" | 16 #include "chrome/browser/ui/browser_commands.h" |
15 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 17 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
16 #include "chrome/browser/ui/toolbar/action_box_menu_model.h" | 18 #include "chrome/browser/ui/toolbar/action_box_menu_model.h" |
17 #include "chrome/common/chrome_notification_types.h" | 19 #include "chrome/common/chrome_notification_types.h" |
| 20 #include "chrome/common/extensions/api/extension_action/action_info.h" |
18 #include "chrome/common/extensions/extension.h" | 21 #include "chrome/common/extensions/extension.h" |
19 #include "chrome/common/extensions/extension_set.h" | 22 #include "chrome/common/extensions/extension_set.h" |
20 #include "content/public/browser/notification_service.h" | 23 #include "content/public/browser/notification_service.h" |
21 #include "content/public/browser/notification_source.h" | 24 #include "content/public/browser/notification_source.h" |
22 #include "content/public/browser/user_metrics.h" | 25 #include "content/public/browser/user_metrics.h" |
23 #include "content/public/browser/web_contents.h" | 26 #include "content/public/browser/web_contents.h" |
24 #include "grit/generated_resources.h" | |
25 #include "webkit/glue/webkit_glue.h" | |
26 | |
27 namespace { | |
28 | |
29 // Share intents get command IDs that are beyond the maximal valid command ID | |
30 // (0xDFFF) so that they are not confused with actual commands that appear in | |
31 // the menu. Extensions get a reserved block of commands after share handlers. | |
32 // For more details see: chrome/app/chrome_command_ids.h | |
33 const int kMaxShareItemsToShow = 20; // TODO(skare): Show extras in submenu. | |
34 enum ActionBoxLocalCommandIds { | |
35 CWS_FIND_SHARE_INTENTS_COMMAND = 0xE000, | |
36 SHARE_COMMAND_FIRST, | |
37 SHARE_COMMAND_LAST = | |
38 SHARE_COMMAND_FIRST + kMaxShareItemsToShow - 1, | |
39 EXTENSION_COMMAND_FIRST | |
40 }; | |
41 | |
42 } // namespace | |
43 | 27 |
44 using content::UserMetricsAction; | 28 using content::UserMetricsAction; |
| 29 using content::WebContents; |
| 30 using extensions::ActionInfo; |
| 31 |
| 32 void ActionBoxButtonController::Delegate::ShowMenu( |
| 33 scoped_ptr<ActionBoxMenuModel> menu_model) { |
| 34 } |
45 | 35 |
46 ActionBoxButtonController::ActionBoxButtonController(Browser* browser, | 36 ActionBoxButtonController::ActionBoxButtonController(Browser* browser, |
47 Delegate* delegate) | 37 Delegate* delegate) |
48 : browser_(browser), | 38 : browser_(browser), |
49 delegate_(delegate), | 39 delegate_(delegate), |
50 next_extension_command_id_(EXTENSION_COMMAND_FIRST) { | 40 next_command_id_(0) { |
51 DCHECK(browser_); | 41 DCHECK(browser_); |
52 DCHECK(delegate_); | 42 DCHECK(delegate_); |
53 registrar_.Add(this, | 43 registrar_.Add(this, |
54 chrome::NOTIFICATION_EXTENSION_UNLOADED, | 44 chrome::NOTIFICATION_EXTENSION_UNLOADED, |
55 content::Source<Profile>(browser->profile())); | 45 content::Source<Profile>(browser->profile())); |
56 } | 46 } |
57 | 47 |
58 ActionBoxButtonController::~ActionBoxButtonController() {} | 48 ActionBoxButtonController::~ActionBoxButtonController() {} |
59 | 49 |
60 void ActionBoxButtonController::OnButtonClicked() { | 50 void ActionBoxButtonController::OnButtonClicked() { |
61 // Build a menu model and display the menu. | 51 // Build a menu model and display the menu. |
62 scoped_ptr<ActionBoxMenuModel> menu_model( | 52 scoped_ptr<ActionBoxMenuModel> menu_model( |
63 new ActionBoxMenuModel(browser_, this)); | 53 new ActionBoxMenuModel(browser_, this)); |
64 | 54 |
65 ExtensionService* extension_service = | 55 const ExtensionSet* extensions = |
66 extensions::ExtensionSystem::Get(browser_->profile())-> | 56 extensions::ExtensionSystem::Get(browser_->profile())-> |
67 extension_service(); | 57 extension_service()->extensions(); |
68 | 58 for (ExtensionSet::const_iterator it = extensions->begin(); |
69 // Add Extensions. | 59 it != extensions->end(); ++it) { |
70 next_extension_command_id_ = EXTENSION_COMMAND_FIRST; | 60 const extensions::Extension* extension = *it; |
71 extension_command_ids_.clear(); | 61 if (ActionInfo::GetPageLauncherInfo(extension)) { |
72 const extensions::ExtensionList& extensions = | 62 int command_id = GetCommandIdForExtension(*extension); |
73 extension_service->toolbar_model()->action_box_menu_items(); | 63 menu_model->AddExtension(*extension, command_id); |
74 for (extensions::ExtensionList::const_iterator it = extensions.begin(); | 64 } |
75 it != extensions.end(); ++it) { | |
76 menu_model->AddExtension(**it, GetCommandIdForExtension(**it)); | |
77 } | 65 } |
| 66 content::RecordAction(UserMetricsAction("ActionBox.ClickButton")); |
78 | 67 |
79 // And show the menu. | 68 // And show the menu. |
80 delegate_->ShowMenu(menu_model.Pass()); | 69 delegate_->ShowMenu(menu_model.Pass()); |
81 } | 70 } |
82 | 71 |
83 bool ActionBoxButtonController::IsCommandIdChecked(int command_id) const { | 72 bool ActionBoxButtonController::IsCommandIdChecked(int command_id) const { |
84 return false; | 73 return false; |
85 } | 74 } |
86 | 75 |
87 bool ActionBoxButtonController::IsCommandIdEnabled(int command_id) const { | 76 bool ActionBoxButtonController::IsCommandIdEnabled(int command_id) const { |
88 return true; | 77 return true; |
89 } | 78 } |
90 | 79 |
91 bool ActionBoxButtonController::GetAcceleratorForCommandId( | 80 bool ActionBoxButtonController::GetAcceleratorForCommandId( |
92 int command_id, | 81 int command_id, |
93 ui::Accelerator* accelerator) { | 82 ui::Accelerator* accelerator) { |
94 return false; | 83 return false; |
95 } | 84 } |
96 | 85 |
97 void ActionBoxButtonController::ExecuteCommand(int command_id) { | 86 void ActionBoxButtonController::ExecuteCommand(int command_id) { |
98 // Handle commands associated with extensions. | 87 // If the command id belongs to an extension, dispatch an onClicked event |
99 // Note that the extension might have been uninstalled or disabled while the | 88 // to its pageLauncher. |
100 // menu was open (sync perhaps?) but that will just fall through safely. | 89 ExtensionIdCommandMap::const_iterator it = |
101 const extensions::Extension* extension = | 90 extension_command_ids_.find(command_id); |
102 GetExtensionForCommandId(command_id); | 91 if (it != extension_command_ids_.end()) { |
103 if (extension) { | 92 WebContents* web_contents = |
104 // TODO(kalman): do something with the result. | 93 browser_->tab_strip_model()->GetActiveWebContents(); |
105 extensions::ExtensionSystem::Get(browser_->profile())-> | 94 // TODO(rfevang): Send page title and selected text. |
106 extension_service()->toolbar_model()->ExecuteBrowserAction( | 95 extensions::PageLauncherAPI::DispatchOnClickedEvent( |
107 extension, browser_, NULL); | 96 browser_->profile(), |
| 97 it->second, |
| 98 web_contents->GetURL(), |
| 99 web_contents->GetContentsMimeType(), |
| 100 NULL, |
| 101 NULL); |
108 return; | 102 return; |
109 } | 103 } |
110 | 104 |
111 // Otherwise, let the browser handle the command. | 105 // Otherwise, let the browser handle the command. |
112 chrome::ExecuteCommand(browser_, command_id); | 106 chrome::ExecuteCommand(browser_, command_id); |
113 } | 107 } |
114 | 108 |
115 int ActionBoxButtonController::GetCommandIdForExtension( | 109 int ActionBoxButtonController::GetCommandIdForExtension( |
116 const extensions::Extension& extension) { | 110 const extensions::Extension& extension) { |
117 ExtensionIdCommandMap::iterator it = | 111 for (ExtensionIdCommandMap::const_iterator it = |
118 extension_command_ids_.find(extension.id()); | 112 extension_command_ids_.begin(); |
119 if (it != extension_command_ids_.end()) | 113 it != extension_command_ids_.end(); ++it) { |
120 return it->second; | 114 if (it->second == extension.id()) |
121 int command_id = next_extension_command_id_++; | 115 return it->first; |
| 116 } |
122 | 117 |
123 // Note that we deliberately don't clean up extension IDs here when | 118 int command_id = GetNextCommandId(); |
124 // extensions are unloaded, so that if they're reloaded they get assigned the | 119 extension_command_ids_[command_id] = extension.id(); |
125 // old command ID. This situation could arise if an extension is updated | |
126 // while the menu is open. On the other hand, we leak some memory... but | |
127 // that's unlikely to matter. | |
128 extension_command_ids_[extension.id()] = command_id; | |
129 | 120 |
130 return command_id; | 121 return command_id; |
131 } | 122 } |
132 | 123 |
133 const extensions::Extension* | 124 int ActionBoxButtonController::GetNextCommandId() { |
134 ActionBoxButtonController::GetExtensionForCommandId(int command_id) { | 125 int command_id = next_command_id_; |
135 for (ExtensionIdCommandMap::iterator it = extension_command_ids_.begin(); | 126 // Find an available command id to return next time the function is called. |
136 it != extension_command_ids_.end(); ++it) { | 127 do { |
137 if (it->second == command_id) { | 128 next_command_id_++; |
138 // Note: might be NULL anyway if the extension has been uninstalled. | 129 // Larger command ids are reserved for non-dynamic entries, so we start |
139 return extensions::ExtensionSystem::Get(browser_->profile())-> | 130 // reusing old ids at this point. |
140 extension_service()->extensions()->GetByID(it->first); | 131 if (next_command_id_ >= IDC_MinimumLabelValue) |
141 } | 132 next_command_id_ = 0; |
142 } | 133 } while (extension_command_ids_.find(next_command_id_) != |
| 134 extension_command_ids_.end()); |
143 | 135 |
144 return NULL; | 136 return command_id; |
145 } | 137 } |
146 | 138 |
147 void ActionBoxButtonController::Observe( | 139 void ActionBoxButtonController::Observe( |
148 int type, | 140 int type, |
149 const content::NotificationSource& source, | 141 const content::NotificationSource& source, |
150 const content::NotificationDetails& details) { | 142 const content::NotificationDetails& details) { |
151 DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_UNLOADED); | 143 DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_UNLOADED); |
152 const extensions::Extension* extension = | 144 const extensions::Extension* extension = |
153 content::Details<extensions::UnloadedExtensionInfo>(details)->extension; | 145 content::Details<extensions::UnloadedExtensionInfo>(details)->extension; |
154 | 146 |
| 147 // Remove any entry point command ids associated with the extension. |
| 148 for (ExtensionIdCommandMap::iterator it = extension_command_ids_.begin(); |
| 149 it != extension_command_ids_.end();) { |
| 150 if (it->second== extension->id()) |
| 151 extension_command_ids_.erase(it++); |
| 152 else |
| 153 ++it; |
| 154 } |
155 // TODO(kalman): if there's a menu open, remove it from that too. | 155 // TODO(kalman): if there's a menu open, remove it from that too. |
156 // We may also want to listen to EXTENSION_LOADED to do the opposite. | 156 // We may also want to listen to EXTENSION_LOADED to do the opposite. |
157 extension_command_ids_.erase(extension->id()); | |
158 } | 157 } |
OLD | NEW |