Mac: Add context menu support (issue #293).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@286 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2011-09-12 19:49:38 +00:00
parent 588a69ed9b
commit 44946a7752
6 changed files with 320 additions and 143 deletions

View File

@ -1145,3 +1145,77 @@ void BrowserWebViewDelegate::UpdateSessionHistory(WebFrame* frame) {
entry->SetContentState(webkit_glue::HistoryItemToString(history_item));
}
bool BrowserWebViewDelegate::OnBeforeMenu(
const WebKit::WebContextMenuData& data, int mouse_x, int mouse_y,
int& edit_flags, int& type_flags) {
// Populate the edit flags values.
edit_flags = data.editFlags;
if(browser_->UIT_CanGoBack())
edit_flags |= MENU_CAN_GO_BACK;
if(browser_->UIT_CanGoForward())
edit_flags |= MENU_CAN_GO_FORWARD;
// Populate the type flags values.
type_flags = MENUTYPE_NONE;
if(!data.pageURL.isEmpty())
type_flags |= MENUTYPE_PAGE;
if(!data.frameURL.isEmpty())
type_flags |= MENUTYPE_FRAME;
if(!data.linkURL.isEmpty())
type_flags |= MENUTYPE_LINK;
if(data.mediaType == WebContextMenuData::MediaTypeImage)
type_flags |= MENUTYPE_IMAGE;
if(!data.selectedText.isEmpty())
type_flags |= MENUTYPE_SELECTION;
if(data.isEditable)
type_flags |= MENUTYPE_EDITABLE;
if(data.isSpellCheckingEnabled && !data.misspelledWord.isEmpty())
type_flags |= MENUTYPE_MISSPELLED_WORD;
if(data.mediaType == WebContextMenuData::MediaTypeVideo)
type_flags |= MENUTYPE_VIDEO;
if(data.mediaType == WebContextMenuData::MediaTypeAudio)
type_flags |= MENUTYPE_AUDIO;
CefRefPtr<CefClient> client = browser_->GetClient();
CefRefPtr<CefMenuHandler> handler;
if (client.get())
handler = client->GetMenuHandler();
if (handler.get()) {
// Gather menu information.
cef_handler_menuinfo_t menuInfo;
memset(&menuInfo, 0, sizeof(menuInfo));
CefString linkStr(std::string(data.linkURL.spec()));
CefString imageStr(std::string(data.srcURL.spec()));
CefString pageStr(std::string(data.pageURL.spec()));
CefString frameStr(std::string(data.frameURL.spec()));
CefString selectedTextStr(string16(data.selectedText));
CefString misspelledWordStr(string16(data.misspelledWord));
CefString securityInfoStr(std::string(data.securityInfo));
menuInfo.typeFlags = type_flags;
menuInfo.x = mouse_x;
menuInfo.y = mouse_y;
cef_string_set(linkStr.c_str(), linkStr.length(), &menuInfo.linkUrl, false);
cef_string_set(imageStr.c_str(), imageStr.length(), &menuInfo.imageUrl,
false);
cef_string_set(pageStr.c_str(), pageStr.length(), &menuInfo.pageUrl, false);
cef_string_set(frameStr.c_str(), frameStr.length(), &menuInfo.frameUrl,
false);
cef_string_set(selectedTextStr.c_str(), selectedTextStr.length(),
&menuInfo.selectionText, false);
cef_string_set(misspelledWordStr.c_str(), misspelledWordStr.length(),
&menuInfo.misspelledWord, false);
menuInfo.editFlags = edit_flags;
cef_string_set(securityInfoStr.c_str(), securityInfoStr.length(),
&menuInfo.securityInfo, false);
// Notify the handler that a context menu is requested.
if (handler->OnBeforeMenu(browser_, menuInfo))
return true;
}
return false;
}

View File

@ -288,6 +288,12 @@ class BrowserWebViewDelegate : public WebKit::WebViewClient,
void UpdateURL(WebKit::WebFrame* frame);
void UpdateSessionHistory(WebKit::WebFrame* frame);
bool OnBeforeMenu(const WebKit::WebContextMenuData& data,
int mouse_x,
int mouse_y,
int& edit_flags,
int& type_flags);
private:
// Causes navigation actions just printout the intended navigation instead
// of taking you to the page. This is used for cases like mailto, where you

View File

@ -13,6 +13,7 @@
#include "base/mac/mac_util.h"
#include "base/sys_string_conversions.h"
#include "skia/ext/skia_utils_mac.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebContextMenuData.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragData.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebImage.h"
@ -26,6 +27,7 @@
#include "webkit/glue/webmenurunner_mac.h"
using webkit::npapi::WebPluginDelegateImpl;
using WebKit::WebContextMenuData;
using WebKit::WebCursorInfo;
using WebKit::WebDragData;
using WebKit::WebDragOperationsMask;
@ -38,6 +40,42 @@ using WebKit::WebPopupMenuInfo;
using WebKit::WebRect;
using WebKit::WebWidget;
namespace {
void AddMenuItem(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefMenuHandler> handler,
NSMenu* menu,
cef_handler_menuid_t menuId,
const std::string& label,
bool enabled) {
std::string disp_str;
if (handler.get()) {
// Let the handler change the label if desired.
CefString actual_label(label);
handler->GetMenuLabel(browser, menuId, actual_label);
disp_str = actual_label;
} else {
disp_str = label;
}
NSString* str = base::SysUTF8ToNSString(disp_str);
NSMenuItem* item =
[[[NSMenuItem alloc] initWithTitle:str
action:enabled?@selector(menuItemSelected:):nil
keyEquivalent:@""] autorelease];
[item setTag:menuId];
[menu addItem:item];
}
void AddMenuSeparator(NSMenu* menu) {
NSMenuItem* item = [NSMenuItem separatorItem];
[menu addItem:item];
}
} // namespace
// WebViewClient --------------------------------------------------------------
WebExternalPopupMenu* BrowserWebViewDelegate::createExternalPopupMenu(
@ -58,7 +96,97 @@ void BrowserWebViewDelegate::ClosePopupMenu() {
void BrowserWebViewDelegate::showContextMenu(
WebKit::WebFrame* frame, const WebKit::WebContextMenuData& data) {
NOTIMPLEMENTED();
WebWidgetHost* host = GetWidgetHost();
if (!host)
return;
BrowserWebView *view = static_cast<BrowserWebView*>(host->view_handle());
if (!view)
return;
NSWindow* window = [view window];
NSPoint position = [window mouseLocationOutsideOfEventStream];
int edit_flags = 0;
int type_flags = 0;
NSMenu* menu = nil;
// Make sure events can be pumped while the menu is up.
MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
// Give the client a chance to handle the menu.
if (OnBeforeMenu(data, position.x, position.y, edit_flags, type_flags))
return;
CefRefPtr<CefClient> client = browser_->GetClient();
CefRefPtr<CefMenuHandler> handler;
if (client.get())
handler = client->GetMenuHandler();
// Build the correct default context menu
if (type_flags & MENUTYPE_EDITABLE) {
menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
AddMenuItem(browser_, handler, menu, MENU_ID_UNDO, "Undo",
!!(edit_flags & MENU_CAN_UNDO));
AddMenuItem(browser_, handler, menu, MENU_ID_REDO, "Redo",
!!(edit_flags & MENU_CAN_REDO));
AddMenuSeparator(menu);
AddMenuItem(browser_, handler, menu, MENU_ID_CUT, "Cut",
!!(edit_flags & MENU_CAN_CUT));
AddMenuItem(browser_, handler, menu, MENU_ID_COPY, "Copy",
!!(edit_flags & MENU_CAN_COPY));
AddMenuItem(browser_, handler, menu, MENU_ID_PASTE, "Paste",
!!(edit_flags & MENU_CAN_PASTE));
AddMenuItem(browser_, handler, menu, MENU_ID_DELETE, "Delete",
!!(edit_flags & MENU_CAN_DELETE));
AddMenuSeparator(menu);
AddMenuItem(browser_, handler, menu, MENU_ID_SELECTALL, "Select All",
!!(edit_flags & MENU_CAN_SELECT_ALL));
} else if(type_flags & MENUTYPE_SELECTION) {
menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
AddMenuItem(browser_, handler, menu, MENU_ID_COPY, "Copy",
!!(edit_flags & MENU_CAN_COPY));
} else if(type_flags & (MENUTYPE_PAGE | MENUTYPE_FRAME)) {
menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
AddMenuItem(browser_, handler, menu, MENU_ID_NAV_BACK, "Back",
!!(edit_flags & MENU_CAN_GO_BACK));
AddMenuItem(browser_, handler, menu, MENU_ID_NAV_FORWARD, "Forward",
!!(edit_flags & MENU_CAN_GO_FORWARD));
// TODO(port): Enable the below menu items when supported.
//AddMenuSeparator(menu);
//AddMenuItem(browser_, handler, menu, MENU_ID_PRINT, "Print", true);
//AddMenuItem(browser_, handler, menu, MENU_ID_VIEWSOURCE, "View Source",
// true);
}
if (!menu)
return;
// Synthesize an event for the click, as there is no certainty that
// [NSApp currentEvent] will return a valid event.
NSEvent* currentEvent = [NSApp currentEvent];
NSTimeInterval eventTime = [currentEvent timestamp];
NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown
location:position
modifierFlags:NSRightMouseDownMask
timestamp:eventTime
windowNumber:[window windowNumber]
context:nil
eventNumber:0
clickCount:1
pressure:1.0];
// Menu selection events go to the BrowserWebView.
[menu setDelegate:view];
// Show the menu.
[NSMenu popUpContextMenu:menu
withEvent:clickEvent
forView:view];
}
// WebWidgetClient ------------------------------------------------------------

View File

@ -56,8 +56,53 @@ using WebKit::WebPopupMenuInfo;
using WebKit::WebRect;
using WebKit::WebWidget;
namespace {
static const wchar_t kPluginWindowClassName[] = L"WebPluginHost";
void AddMenuItem(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefMenuHandler> handler,
HMENU menu,
cef_handler_menuid_t menuId,
const wchar_t* label,
bool enabled,
std::list<std::wstring>& label_list)
{
CefString actual_label(label);
if (handler.get()) {
// Let the handler change the label if desired,
handler->GetMenuLabel(browser, menuId, actual_label);
}
// Store the label in a list to simplify memory management.
label_list.push_back(actual_label);
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
mii.fType = MFT_STRING;
if (!enabled) {
mii.fMask |= MIIM_STATE;
mii.fState = MFS_GRAYED;
}
mii.wID = menuId;
mii.dwTypeData = const_cast<wchar_t*>(label_list.back().c_str());
InsertMenuItem(menu, -1, TRUE, &mii);
}
void AddMenuSeparator(HMENU menu)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE;
mii.fType = MFT_SEPARATOR;
InsertMenuItem(menu, -1, TRUE, &mii);
}
} // namespace
// WebViewClient --------------------------------------------------------------
WebExternalPopupMenu* BrowserWebViewDelegate::createExternalPopupMenu(
@ -371,47 +416,6 @@ void BrowserWebViewDelegate::DidMovePlugin(
}
}
static void AddMenuItem(CefRefPtr<CefBrowser> browser, HMENU menu, int index,
cef_handler_menuid_t id, const wchar_t* label,
bool enabled, std::list<std::wstring>& label_list)
{
CefString actual_label(label);
CefRefPtr<CefClient> client = browser->GetClient();
if (client.get()) {
CefRefPtr<CefMenuHandler> handler = client->GetMenuHandler();
if(handler.get()) {
// Let the handler change the label if desired
handler->GetMenuLabel(browser, id, actual_label);
}
}
// store the label in a list to simplify memory management
label_list.push_back(actual_label);
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
mii.fType = MFT_STRING;
if(!enabled) {
mii.fMask |= MIIM_STATE;
mii.fState = MFS_GRAYED;
}
mii.wID = id;
mii.dwTypeData = const_cast<wchar_t*>(label_list.back().c_str());
InsertMenuItem(menu, index, TRUE, &mii);
}
static void AddMenuSeparator(HMENU menu, int index)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE;
mii.fType = MFT_SEPARATOR;
InsertMenuItem(menu, index, TRUE, &mii);
}
void BrowserWebViewDelegate::showContextMenu(
WebFrame* frame, const WebContextMenuData& data)
{
@ -427,127 +431,73 @@ void BrowserWebViewDelegate::showContextMenu(
screenY = mouse_pt.y;
}
int edit_flags = 0;
int type_flags = 0;
HMENU menu = NULL;
std::list<std::wstring> label_list;
// Enable recursive tasks on the message loop so we can get updates while
// the context menu is being displayed.
bool old_state = MessageLoop::current()->NestableTasksAllowed();
MessageLoop::current()->SetNestableTasksAllowed(true);
// Make sure events can be pumped while the menu is up.
MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
int edit_flags = data.editFlags;
if(browser_->UIT_CanGoBack())
edit_flags |= MENU_CAN_GO_BACK;
if(browser_->UIT_CanGoForward())
edit_flags |= MENU_CAN_GO_FORWARD;
int type_flags = MENUTYPE_NONE;
if(!data.pageURL.isEmpty())
type_flags |= MENUTYPE_PAGE;
if(!data.frameURL.isEmpty())
type_flags |= MENUTYPE_FRAME;
if(!data.linkURL.isEmpty())
type_flags |= MENUTYPE_LINK;
if(data.mediaType == WebContextMenuData::MediaTypeImage)
type_flags |= MENUTYPE_IMAGE;
if(!data.selectedText.isEmpty())
type_flags |= MENUTYPE_SELECTION;
if(data.isEditable)
type_flags |= MENUTYPE_EDITABLE;
if(data.isSpellCheckingEnabled && !data.misspelledWord.isEmpty())
type_flags |= MENUTYPE_MISSPELLED_WORD;
if(data.mediaType == WebContextMenuData::MediaTypeVideo)
type_flags |= MENUTYPE_VIDEO;
if(data.mediaType == WebContextMenuData::MediaTypeAudio)
type_flags |= MENUTYPE_AUDIO;
// Give the client a chance to handle the menu.
if (OnBeforeMenu(data, mouse_pt.x, mouse_pt.y, edit_flags, type_flags))
return;
CefRefPtr<CefClient> client = browser_->GetClient();
CefRefPtr<CefMenuHandler> handler;
if (client.get())
handler = client->GetMenuHandler();
if (handler.get()) {
// Gather menu information
cef_handler_menuinfo_t menuInfo;
memset(&menuInfo, 0, sizeof(menuInfo));
CefString linkStr(std::string(data.linkURL.spec()));
CefString imageStr(std::string(data.srcURL.spec()));
CefString pageStr(std::string(data.pageURL.spec()));
CefString frameStr(std::string(data.frameURL.spec()));
CefString selectedTextStr(string16(data.selectedText));
CefString misspelledWordStr(string16(data.misspelledWord));
CefString securityInfoStr(std::string(data.securityInfo));
menuInfo.typeFlags = type_flags;
menuInfo.x = mouse_pt.x;
menuInfo.y = mouse_pt.y;
cef_string_set(linkStr.c_str(), linkStr.length(), &menuInfo.linkUrl, false);
cef_string_set(imageStr.c_str(), imageStr.length(), &menuInfo.imageUrl,
false);
cef_string_set(pageStr.c_str(), pageStr.length(), &menuInfo.pageUrl, false);
cef_string_set(frameStr.c_str(), frameStr.length(), &menuInfo.frameUrl,
false);
cef_string_set(selectedTextStr.c_str(), selectedTextStr.length(),
&menuInfo.selectionText, false);
cef_string_set(misspelledWordStr.c_str(), misspelledWordStr.length(),
&menuInfo.misspelledWord, false);
menuInfo.editFlags = edit_flags;
cef_string_set(securityInfoStr.c_str(), securityInfoStr.length(),
&menuInfo.securityInfo, false);
// Notify the handler that a context menu is requested
if (handler->OnBeforeMenu(browser_, menuInfo))
goto end;
}
if (client.get() && browser_->IsWindowRenderingDisabled()) {
// Retrieve the screen coordinates.
CefRefPtr<CefRenderHandler> render_handler = client->GetRenderHandler();
if (render_handler.get() &&
!render_handler->GetScreenPoint(browser_, mouse_pt.x, mouse_pt.y,
screenX, screenY)) {
goto end;
return;
}
}
// Build the correct default context menu
if (type_flags & MENUTYPE_EDITABLE) {
menu = CreatePopupMenu();
AddMenuItem(browser_, menu, -1, MENU_ID_UNDO, L"Undo",
AddMenuItem(browser_, handler, menu, MENU_ID_UNDO, L"Undo",
!!(edit_flags & MENU_CAN_UNDO), label_list);
AddMenuItem(browser_, menu, -1, MENU_ID_REDO, L"Redo",
AddMenuItem(browser_, handler, menu, MENU_ID_REDO, L"Redo",
!!(edit_flags & MENU_CAN_REDO), label_list);
AddMenuSeparator(menu, -1);
AddMenuItem(browser_, menu, -1, MENU_ID_CUT, L"Cut",
AddMenuSeparator(menu);
AddMenuItem(browser_, handler, menu, MENU_ID_CUT, L"Cut",
!!(edit_flags & MENU_CAN_CUT), label_list);
AddMenuItem(browser_, menu, -1, MENU_ID_COPY, L"Copy",
AddMenuItem(browser_, handler, menu, MENU_ID_COPY, L"Copy",
!!(edit_flags & MENU_CAN_COPY), label_list);
AddMenuItem(browser_, menu, -1, MENU_ID_PASTE, L"Paste",
AddMenuItem(browser_, handler, menu, MENU_ID_PASTE, L"Paste",
!!(edit_flags & MENU_CAN_PASTE), label_list);
AddMenuItem(browser_, menu, -1, MENU_ID_DELETE, L"Delete",
AddMenuItem(browser_, handler, menu, MENU_ID_DELETE, L"Delete",
!!(edit_flags & MENU_CAN_DELETE), label_list);
AddMenuSeparator(menu, -1);
AddMenuItem(browser_, menu, -1, MENU_ID_SELECTALL, L"Select All",
AddMenuSeparator(menu);
AddMenuItem(browser_, handler, menu, MENU_ID_SELECTALL, L"Select All",
!!(edit_flags & MENU_CAN_SELECT_ALL), label_list);
} else if(type_flags & MENUTYPE_SELECTION) {
menu = CreatePopupMenu();
AddMenuItem(browser_, menu, -1, MENU_ID_COPY, L"Copy",
AddMenuItem(browser_, handler, menu, MENU_ID_COPY, L"Copy",
!!(edit_flags & MENU_CAN_COPY), label_list);
} else if(type_flags & (MENUTYPE_PAGE | MENUTYPE_FRAME)) {
menu = CreatePopupMenu();
AddMenuItem(browser_, menu, -1, MENU_ID_NAV_BACK, L"Back",
AddMenuItem(browser_, handler, menu, MENU_ID_NAV_BACK, L"Back",
!!(edit_flags & MENU_CAN_GO_BACK), label_list);
AddMenuItem(browser_, menu, -1, MENU_ID_NAV_FORWARD, L"Forward",
AddMenuItem(browser_, handler, menu, MENU_ID_NAV_FORWARD, L"Forward",
!!(edit_flags & MENU_CAN_GO_FORWARD), label_list);
AddMenuSeparator(menu, -1);
AddMenuItem(browser_, menu, -1, MENU_ID_PRINT, L"Print",
AddMenuSeparator(menu);
AddMenuItem(browser_, handler, menu, MENU_ID_PRINT, L"Print",
true, label_list);
AddMenuItem(browser_, menu, -1, MENU_ID_VIEWSOURCE, L"View Source",
AddMenuItem(browser_, handler, menu, MENU_ID_VIEWSOURCE, L"View Source",
true, label_list);
}
if (menu) {
// show the context menu
if (!menu)
return;
// Show the context menu
int selected_id = TrackPopupMenu(menu,
TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_RECURSE,
screenX, screenY, 0, browser_->UIT_GetMainWndHandle(), NULL);
@ -567,12 +517,8 @@ void BrowserWebViewDelegate::showContextMenu(
browser_->UIT_HandleAction(menuId, browser_->GetFocusedFrame());
}
}
}
DestroyMenu(menu);
end:
MessageLoop::current()->SetNestableTasksAllowed(old_state);
}
// Private methods ------------------------------------------------------------

View File

@ -40,6 +40,9 @@ struct WebDropData;
- (BOOL)isOpaque;
- (void)setFrame:(NSRect)frameRect;
// Called when a context menu item is selected by the user.
- (void)menuItemSelected:(id)sender;
// Register this WebView as a drag/drop target.
- (void)registerDragDrop;

View File

@ -223,6 +223,26 @@
browser_->GetFocusedFrame()->SelectAll();
}
- (void)menuItemSelected:(id)sender {
cef_handler_menuid_t menuId =
static_cast<cef_handler_menuid_t>([sender tag]);
bool handled = false;
CefRefPtr<CefClient> client = browser_->GetClient();
if (client.get()) {
CefRefPtr<CefMenuHandler> handler = client->GetMenuHandler();
if (handler.get()) {
// Ask the handler if it wants to handle the action.
handled = handler->OnMenuAction(browser_, menuId);
}
}
if(!handled) {
// Execute the action.
browser_->UIT_HandleAction(menuId, browser_->GetFocusedFrame());
}
}
- (void)registerDragDrop {
dropTarget_.reset([[WebDropTarget alloc] initWithWebView:self]);