Roll libvpx 861f35:1fff3e
[chromium-blink-merge.git] / components / renderer_context_menu / render_view_context_menu_base.cc
blob3d9487ac288aa543fd2fc81f001f28747b4bf1ec
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.
5 #include "components/renderer_context_menu/render_view_context_menu_base.h"
7 #include <algorithm>
8 #include <utility>
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 #if defined(ENABLE_EXTENSIONS)
19 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
20 #endif
21 #include "third_party/WebKit/public/web/WebContextMenuData.h"
23 using blink::WebContextMenuData;
24 using blink::WebString;
25 using blink::WebURL;
26 using content::BrowserContext;
27 using content::OpenURLParams;
28 using content::RenderFrameHost;
29 using content::RenderViewHost;
30 using content::WebContents;
32 namespace {
34 // The (inclusive) range of command IDs reserved for content's custom menus.
35 int content_context_custom_first = -1;
36 int content_context_custom_last = -1;
38 bool IsCustomItemEnabledInternal(const std::vector<content::MenuItem>& items,
39 int id) {
40 DCHECK(RenderViewContextMenuBase::IsContentCustomCommandId(id));
41 for (size_t i = 0; i < items.size(); ++i) {
42 int action_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId(
43 items[i].action);
44 if (action_id == id)
45 return items[i].enabled;
46 if (items[i].type == content::MenuItem::SUBMENU) {
47 if (IsCustomItemEnabledInternal(items[i].submenu, id))
48 return true;
51 return false;
54 bool IsCustomItemCheckedInternal(const std::vector<content::MenuItem>& items,
55 int id) {
56 DCHECK(RenderViewContextMenuBase::IsContentCustomCommandId(id));
57 for (size_t i = 0; i < items.size(); ++i) {
58 int action_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId(
59 items[i].action);
60 if (action_id == id)
61 return items[i].checked;
62 if (items[i].type == content::MenuItem::SUBMENU) {
63 if (IsCustomItemCheckedInternal(items[i].submenu, id))
64 return true;
67 return false;
70 const size_t kMaxCustomMenuDepth = 5;
71 const size_t kMaxCustomMenuTotalItems = 1000;
73 void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items,
74 size_t depth,
75 size_t* total_items,
76 ui::SimpleMenuModel::Delegate* delegate,
77 ui::SimpleMenuModel* menu_model) {
78 if (depth > kMaxCustomMenuDepth) {
79 LOG(ERROR) << "Custom menu too deeply nested.";
80 return;
82 for (size_t i = 0; i < items.size(); ++i) {
83 int command_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId(
84 items[i].action);
85 if (!RenderViewContextMenuBase::IsContentCustomCommandId(command_id)) {
86 LOG(ERROR) << "Custom menu action value out of range.";
87 return;
89 if (*total_items >= kMaxCustomMenuTotalItems) {
90 LOG(ERROR) << "Custom menu too large (too many items).";
91 return;
93 (*total_items)++;
94 switch (items[i].type) {
95 case content::MenuItem::OPTION:
96 menu_model->AddItem(
97 RenderViewContextMenuBase::ConvertToContentCustomCommandId(
98 items[i].action),
99 items[i].label);
100 break;
101 case content::MenuItem::CHECKABLE_OPTION:
102 menu_model->AddCheckItem(
103 RenderViewContextMenuBase::ConvertToContentCustomCommandId(
104 items[i].action),
105 items[i].label);
106 break;
107 case content::MenuItem::GROUP:
108 // TODO(viettrungluu): I don't know what this is supposed to do.
109 NOTREACHED();
110 break;
111 case content::MenuItem::SEPARATOR:
112 menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
113 break;
114 case content::MenuItem::SUBMENU: {
115 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate);
116 AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate,
117 submenu);
118 menu_model->AddSubMenu(
119 RenderViewContextMenuBase::ConvertToContentCustomCommandId(
120 items[i].action),
121 items[i].label,
122 submenu);
123 break;
125 default:
126 NOTREACHED();
127 break;
132 content::WebContents* GetWebContentsToUse(content::WebContents* web_contents) {
133 // If we're viewing in a MimeHandlerViewGuest, use its embedder WebContents.
134 #if defined(ENABLE_EXTENSIONS)
135 auto guest_view =
136 extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
137 if (guest_view)
138 return guest_view->embedder_web_contents();
139 #endif
140 return web_contents;
143 } // namespace
145 // static
146 void RenderViewContextMenuBase::SetContentCustomCommandIdRange(
147 int first, int last) {
148 // The range is inclusive.
149 content_context_custom_first = first;
150 content_context_custom_last = last;
153 // static
154 const size_t RenderViewContextMenuBase::kMaxSelectionTextLength = 50;
156 // static
157 int RenderViewContextMenuBase::ConvertToContentCustomCommandId(int id) {
158 return content_context_custom_first + id;
161 // static
162 bool RenderViewContextMenuBase::IsContentCustomCommandId(int id) {
163 return id >= content_context_custom_first &&
164 id <= content_context_custom_last;
167 RenderViewContextMenuBase::RenderViewContextMenuBase(
168 content::RenderFrameHost* render_frame_host,
169 const content::ContextMenuParams& params)
170 : params_(params),
171 source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
172 embedder_web_contents_(GetWebContentsToUse(source_web_contents_)),
173 browser_context_(source_web_contents_->GetBrowserContext()),
174 menu_model_(this),
175 render_frame_id_(render_frame_host->GetRoutingID()),
176 command_executed_(false),
177 render_process_id_(render_frame_host->GetProcess()->GetID()) {
180 RenderViewContextMenuBase::~RenderViewContextMenuBase() {
183 // Menu construction functions -------------------------------------------------
185 void RenderViewContextMenuBase::Init() {
186 // Command id range must have been already initializerd.
187 DCHECK_NE(-1, content_context_custom_first);
188 DCHECK_NE(-1, content_context_custom_last);
190 InitMenu();
191 if (toolkit_delegate_)
192 toolkit_delegate_->Init(&menu_model_);
195 void RenderViewContextMenuBase::Cancel() {
196 if (toolkit_delegate_)
197 toolkit_delegate_->Cancel();
200 void RenderViewContextMenuBase::InitMenu() {
201 if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_CUSTOM)) {
202 AppendCustomItems();
204 const bool has_selection = !params_.selection_text.empty();
205 if (has_selection) {
206 // We will add more items if there's a selection, so add a separator.
207 // TODO(lazyboy): Clean up separator logic.
208 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
213 void RenderViewContextMenuBase::AddMenuItem(int command_id,
214 const base::string16& title) {
215 menu_model_.AddItem(command_id, title);
218 void RenderViewContextMenuBase::AddCheckItem(int command_id,
219 const base::string16& title) {
220 menu_model_.AddCheckItem(command_id, title);
223 void RenderViewContextMenuBase::AddSeparator() {
224 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
227 void RenderViewContextMenuBase::AddSubMenu(int command_id,
228 const base::string16& label,
229 ui::MenuModel* model) {
230 menu_model_.AddSubMenu(command_id, label, model);
233 void RenderViewContextMenuBase::UpdateMenuItem(int command_id,
234 bool enabled,
235 bool hidden,
236 const base::string16& label) {
237 if (toolkit_delegate_) {
238 toolkit_delegate_->UpdateMenuItem(command_id,
239 enabled,
240 hidden,
241 label);
245 RenderViewHost* RenderViewContextMenuBase::GetRenderViewHost() const {
246 return source_web_contents_->GetRenderViewHost();
249 WebContents* RenderViewContextMenuBase::GetWebContents() const {
250 return source_web_contents_;
253 BrowserContext* RenderViewContextMenuBase::GetBrowserContext() const {
254 return browser_context_;
257 bool RenderViewContextMenuBase::AppendCustomItems() {
258 size_t total_items = 0;
259 AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this,
260 &menu_model_);
261 return total_items > 0;
264 bool RenderViewContextMenuBase::IsCommandIdKnown(
265 int id,
266 bool* enabled) const {
267 // If this command is is added by one of our observers, we dispatch
268 // it to the observer.
269 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(&observers_);
270 RenderViewContextMenuObserver* observer;
271 while ((observer = it.GetNext()) != NULL) {
272 if (observer->IsCommandIdSupported(id)) {
273 *enabled = observer->IsCommandIdEnabled(id);
274 return true;
278 // Custom items.
279 if (IsContentCustomCommandId(id)) {
280 *enabled = IsCustomItemEnabled(id);
281 return true;
284 return false;
287 // Menu delegate functions -----------------------------------------------------
289 bool RenderViewContextMenuBase::IsCommandIdChecked(int id) const {
290 // If this command is is added by one of our observers, we dispatch it to the
291 // observer.
292 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(&observers_);
293 RenderViewContextMenuObserver* observer;
294 while ((observer = it.GetNext()) != NULL) {
295 if (observer->IsCommandIdSupported(id))
296 return observer->IsCommandIdChecked(id);
299 // Custom items.
300 if (IsContentCustomCommandId(id))
301 return IsCustomItemChecked(id);
303 return false;
306 void RenderViewContextMenuBase::ExecuteCommand(int id, int event_flags) {
307 command_executed_ = true;
308 RecordUsedItem(id);
310 // If this command is is added by one of our observers, we dispatch
311 // it to the observer.
312 ObserverListBase<RenderViewContextMenuObserver>::Iterator it(&observers_);
313 RenderViewContextMenuObserver* observer;
314 while ((observer = it.GetNext()) != NULL) {
315 if (observer->IsCommandIdSupported(id))
316 return observer->ExecuteCommand(id);
319 // Process custom actions range.
320 if (IsContentCustomCommandId(id)) {
321 unsigned action = id - content_context_custom_first;
322 const content::CustomContextMenuContext& context = params_.custom_context;
323 #if defined(ENABLE_PLUGINS)
324 if (context.request_id && !context.is_pepper_menu)
325 HandleAuthorizeAllPlugins();
326 #endif
327 source_web_contents_->ExecuteCustomContextMenuCommand(action, context);
328 return;
330 command_executed_ = false;
333 void RenderViewContextMenuBase::MenuWillShow(ui::SimpleMenuModel* source) {
334 for (int i = 0; i < source->GetItemCount(); ++i) {
335 if (source->IsVisibleAt(i) &&
336 source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) {
337 RecordShownItem(source->GetCommandIdAt(i));
341 // Ignore notifications from submenus.
342 if (source != &menu_model_)
343 return;
345 content::RenderWidgetHostView* view =
346 source_web_contents_->GetRenderWidgetHostView();
347 if (view)
348 view->SetShowingContextMenu(true);
350 NotifyMenuShown();
353 void RenderViewContextMenuBase::MenuClosed(ui::SimpleMenuModel* source) {
354 // Ignore notifications from submenus.
355 if (source != &menu_model_)
356 return;
358 content::RenderWidgetHostView* view =
359 source_web_contents_->GetRenderWidgetHostView();
360 if (view)
361 view->SetShowingContextMenu(false);
362 source_web_contents_->NotifyContextMenuClosed(params_.custom_context);
364 if (!command_executed_) {
365 FOR_EACH_OBSERVER(RenderViewContextMenuObserver,
366 observers_,
367 OnMenuCancel());
371 RenderFrameHost* RenderViewContextMenuBase::GetRenderFrameHost() {
372 return RenderFrameHost::FromID(render_process_id_, render_frame_id_);
375 // Controller functions --------------------------------------------------------
377 void RenderViewContextMenuBase::OpenURL(
378 const GURL& url, const GURL& referring_url,
379 WindowOpenDisposition disposition,
380 ui::PageTransition transition) {
381 OpenURLWithExtraHeaders(url, referring_url, disposition, transition, "");
384 void RenderViewContextMenuBase::OpenURLWithExtraHeaders(
385 const GURL& url,
386 const GURL& referring_url,
387 WindowOpenDisposition disposition,
388 ui::PageTransition transition,
389 const std::string& extra_headers) {
390 content::Referrer referrer = content::Referrer::SanitizeForRequest(
391 url,
392 content::Referrer(referring_url.GetAsReferrer(),
393 params_.referrer_policy));
395 if (params_.link_url == url && disposition != OFF_THE_RECORD)
396 params_.custom_context.link_followed = url;
398 OpenURLParams open_url_params(url, referrer, disposition, transition, false);
399 if (!extra_headers.empty())
400 open_url_params.extra_headers = extra_headers;
402 WebContents* new_contents = source_web_contents_->OpenURL(open_url_params);
403 if (!new_contents)
404 return;
406 NotifyURLOpened(url, new_contents);
409 bool RenderViewContextMenuBase::IsCustomItemChecked(int id) const {
410 return IsCustomItemCheckedInternal(params_.custom_items, id);
413 bool RenderViewContextMenuBase::IsCustomItemEnabled(int id) const {
414 return IsCustomItemEnabledInternal(params_.custom_items, id);