Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/renderer_context_menu/render_view_context_menu_base.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "content/public/browser/render_frame_host.h" | |
| 13 #include "content/public/browser/render_process_host.h" | |
| 14 #include "content/public/browser/render_view_host.h" | |
| 15 #include "content/public/browser/render_widget_host_view.h" | |
| 16 #include "content/public/browser/web_contents.h" | |
| 17 #include "content/public/common/menu_item.h" | |
| 18 #include "extensions/browser/extension_host.h" | |
| 19 #include "extensions/browser/extension_system.h" | |
| 20 #include "extensions/browser/view_type_utils.h" | |
| 21 #include "extensions/common/extension.h" | |
| 22 #include "third_party/WebKit/public/web/WebContextMenuData.h" | |
| 23 | |
| 24 using blink::WebContextMenuData; | |
| 25 using blink::WebString; | |
| 26 using blink::WebURL; | |
| 27 using content::BrowserContext; | |
| 28 using content::OpenURLParams; | |
| 29 using content::RenderFrameHost; | |
| 30 using content::RenderViewHost; | |
| 31 using content::WebContents; | |
| 32 | |
| 33 namespace { | |
| 34 | |
| 35 // The range of command IDs reserved for content's custom menus. | |
|
lazyboy
2014/08/01 07:59:34
The (inclusive) range of ...
| |
| 36 // TODO(oshima): These values will be injected by embedders. | |
|
lazyboy
2014/08/01 07:59:34
This TODO has been taken care of?
oshima
2014/08/01 10:38:45
Done. Thanks
| |
| 37 int content_context_custom_first = -1; | |
|
lazyboy
2014/08/01 07:59:34
These should also have g_ prefix as they are globa
oshima
2014/08/01 10:38:44
same here. they're file scoped, not global.
| |
| 38 int content_context_custom_last = -1; | |
| 39 | |
| 40 bool IsCustomItemEnabledInternal(const std::vector<content::MenuItem>& items, | |
| 41 int id) { | |
| 42 DCHECK(RenderViewContextMenuBase::IsContentCustomCommandId(id)); | |
| 43 for (size_t i = 0; i < items.size(); ++i) { | |
| 44 int action_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId( | |
| 45 items[i].action); | |
| 46 if (action_id == id) | |
| 47 return items[i].enabled; | |
| 48 if (items[i].type == content::MenuItem::SUBMENU) { | |
| 49 if (IsCustomItemEnabledInternal(items[i].submenu, id)) | |
| 50 return true; | |
| 51 } | |
| 52 } | |
| 53 return false; | |
| 54 } | |
| 55 | |
| 56 bool IsCustomItemCheckedInternal(const std::vector<content::MenuItem>& items, | |
| 57 int id) { | |
| 58 DCHECK(RenderViewContextMenuBase::IsContentCustomCommandId(id)); | |
| 59 for (size_t i = 0; i < items.size(); ++i) { | |
| 60 int action_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId( | |
| 61 items[i].action); | |
| 62 if (action_id == id) | |
| 63 return items[i].checked; | |
| 64 if (items[i].type == content::MenuItem::SUBMENU) { | |
| 65 if (IsCustomItemCheckedInternal(items[i].submenu, id)) | |
| 66 return true; | |
| 67 } | |
| 68 } | |
| 69 return false; | |
| 70 } | |
| 71 | |
| 72 const size_t kMaxCustomMenuDepth = 5; | |
| 73 const size_t kMaxCustomMenuTotalItems = 1000; | |
| 74 | |
| 75 void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items, | |
| 76 size_t depth, | |
| 77 size_t* total_items, | |
| 78 ui::SimpleMenuModel::Delegate* delegate, | |
| 79 ui::SimpleMenuModel* menu_model) { | |
| 80 if (depth > kMaxCustomMenuDepth) { | |
| 81 LOG(ERROR) << "Custom menu too deeply nested."; | |
| 82 return; | |
| 83 } | |
| 84 for (size_t i = 0; i < items.size(); ++i) { | |
| 85 int command_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId( | |
| 86 items[i].action); | |
| 87 if (!RenderViewContextMenuBase::IsContentCustomCommandId(command_id)) { | |
| 88 LOG(ERROR) << "Custom menu action value out of range."; | |
| 89 return; | |
| 90 } | |
| 91 if (*total_items >= kMaxCustomMenuTotalItems) { | |
| 92 LOG(ERROR) << "Custom menu too large (too many items)."; | |
| 93 return; | |
| 94 } | |
| 95 (*total_items)++; | |
| 96 switch (items[i].type) { | |
| 97 case content::MenuItem::OPTION: | |
| 98 menu_model->AddItem( | |
| 99 RenderViewContextMenuBase::ConvertToContentCustomCommandId( | |
| 100 items[i].action), | |
| 101 items[i].label); | |
| 102 break; | |
| 103 case content::MenuItem::CHECKABLE_OPTION: | |
| 104 menu_model->AddCheckItem( | |
| 105 RenderViewContextMenuBase::ConvertToContentCustomCommandId( | |
| 106 items[i].action), | |
| 107 items[i].label); | |
| 108 break; | |
| 109 case content::MenuItem::GROUP: | |
| 110 // TODO(viettrungluu): I don't know what this is supposed to do. | |
| 111 NOTREACHED(); | |
| 112 break; | |
| 113 case content::MenuItem::SEPARATOR: | |
| 114 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | |
| 115 break; | |
| 116 case content::MenuItem::SUBMENU: { | |
| 117 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate); | |
| 118 AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate, | |
| 119 submenu); | |
| 120 menu_model->AddSubMenu( | |
| 121 RenderViewContextMenuBase::ConvertToContentCustomCommandId( | |
| 122 items[i].action), | |
| 123 items[i].label, | |
| 124 submenu); | |
| 125 break; | |
| 126 } | |
| 127 default: | |
| 128 NOTREACHED(); | |
| 129 break; | |
| 130 } | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 } // namespace | |
| 135 | |
| 136 // static | |
| 137 void RenderViewContextMenuBase::SetContentCustomCommandIdRange( | |
|
lazyboy
2014/08/01 07:59:34
Add a note that this range is inclusive.
oshima
2014/08/01 10:38:44
Done.
| |
| 138 int first, int last) { | |
| 139 content_context_custom_first = first; | |
| 140 content_context_custom_last = last; | |
| 141 } | |
| 142 | |
| 143 // static | |
| 144 const size_t RenderViewContextMenuBase::kMaxSelectionTextLength = 50; | |
| 145 | |
| 146 // static | |
| 147 int RenderViewContextMenuBase::ConvertToContentCustomCommandId(int id) { | |
| 148 return content_context_custom_first + id; | |
| 149 } | |
| 150 | |
| 151 // static | |
| 152 bool RenderViewContextMenuBase::IsContentCustomCommandId(int id) { | |
| 153 return id >= content_context_custom_first && | |
| 154 id <= content_context_custom_last; | |
| 155 } | |
| 156 | |
| 157 RenderViewContextMenuBase::RenderViewContextMenuBase( | |
| 158 content::RenderFrameHost* render_frame_host, | |
| 159 const content::ContextMenuParams& params) | |
| 160 : params_(params), | |
| 161 source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), | |
| 162 browser_context_(source_web_contents_->GetBrowserContext()), | |
| 163 menu_model_(this), | |
| 164 command_executed_(false), | |
| 165 render_process_id_(render_frame_host->GetProcess()->GetID()), | |
| 166 render_frame_id_(render_frame_host->GetRoutingID()) { | |
| 167 } | |
| 168 | |
| 169 RenderViewContextMenuBase::~RenderViewContextMenuBase() { | |
| 170 } | |
| 171 | |
| 172 // Menu construction functions ------------------------------------------------- | |
| 173 | |
| 174 void RenderViewContextMenuBase::Init() { | |
| 175 // Command id range must have been already initializerd. | |
| 176 DCHECK_NE(-1, content_context_custom_first); | |
| 177 DCHECK_NE(-1, content_context_custom_last); | |
| 178 | |
| 179 InitMenu(); | |
| 180 if (toolkit_delegate_) | |
| 181 toolkit_delegate_->Init(&menu_model_); | |
| 182 } | |
| 183 | |
| 184 void RenderViewContextMenuBase::Cancel() { | |
| 185 if (toolkit_delegate_) | |
| 186 toolkit_delegate_->Cancel(); | |
| 187 } | |
| 188 | |
| 189 void RenderViewContextMenuBase::InitMenu() { | |
| 190 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_CUSTOM)) { | |
| 191 AppendCustomItems(); | |
| 192 | |
| 193 const bool has_selection = !params_.selection_text.empty(); | |
| 194 if (has_selection) { | |
| 195 // We will add more items if there's a selection, so add a separator. | |
| 196 // TODO(lazyboy): Clean up separator logic. | |
| 197 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 void RenderViewContextMenuBase::AddMenuItem(int command_id, | |
| 203 const base::string16& title) { | |
| 204 menu_model_.AddItem(command_id, title); | |
| 205 } | |
| 206 | |
| 207 void RenderViewContextMenuBase::AddCheckItem(int command_id, | |
| 208 const base::string16& title) { | |
| 209 menu_model_.AddCheckItem(command_id, title); | |
| 210 } | |
| 211 | |
| 212 void RenderViewContextMenuBase::AddSeparator() { | |
| 213 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
| 214 } | |
| 215 | |
| 216 void RenderViewContextMenuBase::AddSubMenu(int command_id, | |
| 217 const base::string16& label, | |
| 218 ui::MenuModel* model) { | |
| 219 menu_model_.AddSubMenu(command_id, label, model); | |
| 220 } | |
| 221 | |
| 222 void RenderViewContextMenuBase::UpdateMenuItem(int command_id, | |
| 223 bool enabled, | |
| 224 bool hidden, | |
| 225 const base::string16& label) { | |
| 226 if (toolkit_delegate_) { | |
| 227 toolkit_delegate_->UpdateMenuItem(command_id, | |
| 228 enabled, | |
| 229 hidden, | |
| 230 label); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 RenderViewHost* RenderViewContextMenuBase::GetRenderViewHost() const { | |
| 235 return source_web_contents_->GetRenderViewHost(); | |
| 236 } | |
| 237 | |
| 238 WebContents* RenderViewContextMenuBase::GetWebContents() const { | |
| 239 return source_web_contents_; | |
| 240 } | |
| 241 | |
| 242 BrowserContext* RenderViewContextMenuBase::GetBrowserContext() const { | |
| 243 return browser_context_; | |
| 244 } | |
| 245 | |
| 246 bool RenderViewContextMenuBase::AppendCustomItems() { | |
| 247 size_t total_items = 0; | |
| 248 AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this, | |
| 249 &menu_model_); | |
| 250 return total_items > 0; | |
| 251 } | |
| 252 | |
| 253 // Menu delegate functions ----------------------------------------------------- | |
| 254 | |
| 255 bool RenderViewContextMenuBase::IsCommandIdEnabled(int id) const { | |
| 256 // If this command is is added by one of our observers, we dispatch | |
| 257 // it to the observer. | |
| 258 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); | |
| 259 RenderViewContextMenuObserver* observer; | |
| 260 while ((observer = it.GetNext()) != NULL) { | |
| 261 if (observer->IsCommandIdSupported(id)) | |
| 262 return observer->IsCommandIdEnabled(id); | |
| 263 } | |
| 264 | |
| 265 // Custom items. | |
| 266 if (IsContentCustomCommandId(id)) | |
| 267 return IsCustomItemEnabled(id); | |
| 268 | |
| 269 return false; | |
| 270 } | |
| 271 | |
| 272 bool RenderViewContextMenuBase::IsCommandIdChecked(int id) const { | |
| 273 // If this command is is added by one of our observers, we dispatch it to the | |
| 274 // observer. | |
| 275 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); | |
| 276 RenderViewContextMenuObserver* observer; | |
| 277 while ((observer = it.GetNext()) != NULL) { | |
| 278 if (observer->IsCommandIdSupported(id)) | |
| 279 return observer->IsCommandIdChecked(id); | |
| 280 } | |
| 281 | |
| 282 // Custom items. | |
| 283 if (IsContentCustomCommandId(id)) | |
| 284 return IsCustomItemChecked(id); | |
| 285 | |
| 286 return false; | |
| 287 } | |
| 288 | |
| 289 void RenderViewContextMenuBase::ExecuteCommand(int id, int event_flags) { | |
| 290 command_executed_ = true; | |
| 291 RecordUsedItem(id); | |
| 292 | |
| 293 // If this command is is added by one of our observers, we dispatch | |
| 294 // it to the observer. | |
| 295 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); | |
| 296 RenderViewContextMenuObserver* observer; | |
| 297 while ((observer = it.GetNext()) != NULL) { | |
| 298 if (observer->IsCommandIdSupported(id)) | |
| 299 return observer->ExecuteCommand(id); | |
| 300 } | |
| 301 | |
| 302 // Process custom actions range. | |
| 303 if (IsContentCustomCommandId(id)) { | |
| 304 unsigned action = id - content_context_custom_first; | |
| 305 const content::CustomContextMenuContext& context = params_.custom_context; | |
| 306 #if defined(ENABLE_PLUGINS) | |
| 307 if (context.request_id && !context.is_pepper_menu) | |
| 308 HandleAuthorizeAllPlugins(); | |
| 309 #endif | |
| 310 source_web_contents_->ExecuteCustomContextMenuCommand(action, context); | |
| 311 return; | |
| 312 } | |
| 313 command_executed_ = false; | |
| 314 } | |
| 315 | |
| 316 void RenderViewContextMenuBase::MenuWillShow(ui::SimpleMenuModel* source) { | |
| 317 for (int i = 0; i < source->GetItemCount(); ++i) { | |
| 318 if (source->IsVisibleAt(i) && | |
| 319 source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) { | |
| 320 RecordShownItem(source->GetCommandIdAt(i)); | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 // Ignore notifications from submenus. | |
| 325 if (source != &menu_model_) | |
| 326 return; | |
| 327 | |
| 328 content::RenderWidgetHostView* view = | |
| 329 source_web_contents_->GetRenderWidgetHostView(); | |
| 330 if (view) | |
| 331 view->SetShowingContextMenu(true); | |
| 332 | |
| 333 NotifyMenuShown(); | |
| 334 } | |
| 335 | |
| 336 void RenderViewContextMenuBase::MenuClosed(ui::SimpleMenuModel* source) { | |
| 337 // Ignore notifications from submenus. | |
| 338 if (source != &menu_model_) | |
| 339 return; | |
| 340 | |
| 341 content::RenderWidgetHostView* view = | |
| 342 source_web_contents_->GetRenderWidgetHostView(); | |
| 343 if (view) | |
| 344 view->SetShowingContextMenu(false); | |
| 345 source_web_contents_->NotifyContextMenuClosed(params_.custom_context); | |
| 346 | |
| 347 if (!command_executed_) { | |
| 348 FOR_EACH_OBSERVER(RenderViewContextMenuObserver, | |
| 349 observers_, | |
| 350 OnMenuCancel()); | |
| 351 } | |
| 352 } | |
| 353 | |
| 354 RenderFrameHost* RenderViewContextMenuBase::GetRenderFrameHost() { | |
| 355 return RenderFrameHost::FromID(render_process_id_, render_frame_id_); | |
| 356 } | |
| 357 | |
| 358 // Controller functions -------------------------------------------------------- | |
| 359 | |
| 360 void RenderViewContextMenuBase::OpenURL( | |
| 361 const GURL& url, const GURL& referring_url, | |
| 362 WindowOpenDisposition disposition, | |
| 363 content::PageTransition transition) { | |
| 364 content::Referrer referrer(referring_url.GetAsReferrer(), | |
| 365 params_.referrer_policy); | |
| 366 | |
| 367 if (params_.link_url == url && disposition != OFF_THE_RECORD) | |
| 368 params_.custom_context.link_followed = url; | |
| 369 | |
| 370 WebContents* new_contents = source_web_contents_->OpenURL(OpenURLParams( | |
| 371 url, referrer, disposition, transition, false)); | |
| 372 if (!new_contents) | |
| 373 return; | |
| 374 | |
| 375 NotifyURLOpened(url, new_contents); | |
| 376 } | |
| 377 | |
| 378 bool RenderViewContextMenuBase::IsCustomItemChecked(int id) const { | |
| 379 return IsCustomItemCheckedInternal(params_.custom_items, id); | |
| 380 } | |
| 381 | |
| 382 bool RenderViewContextMenuBase::IsCustomItemEnabled(int id) const { | |
| 383 return IsCustomItemEnabledInternal(params_.custom_items, id); | |
| 384 } | |
| 385 | |
| OLD | NEW |