550 lines
18 KiB
Plaintext
550 lines
18 KiB
Plaintext
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "libcef/browser_webview_delegate.h"
|
|
#import "libcef/browser_webview_mac.h"
|
|
#include "libcef/browser_impl.h"
|
|
#include "libcef/drag_data_impl.h"
|
|
#import "include/cef_application_mac.h"
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
#include "base/file_util.h"
|
|
#include "base/mac/mac_util.h"
|
|
#include "base/sys_string_conversions.h"
|
|
#include "base/threading/thread_restrictions.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/platform/WebDragData.h"
|
|
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebImage.h"
|
|
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPoint.h"
|
|
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h"
|
|
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
|
|
#include "ui/base/layout.h"
|
|
#include "ui/gfx/image/image_skia.h"
|
|
#include "ui/gfx/image/image_skia_util_mac.h"
|
|
#include "webkit/glue/webcursor.h"
|
|
#include "webkit/glue/webdropdata.h"
|
|
#include "webkit/plugins/npapi/plugin_list.h"
|
|
#include "webkit/plugins/npapi/webplugin_delegate_impl.h"
|
|
#include "webkit/glue/webmenurunner_mac.h"
|
|
|
|
using webkit::npapi::WebPluginDelegateImpl;
|
|
using WebKit::WebContextMenuData;
|
|
using WebKit::WebCursorInfo;
|
|
using WebKit::WebDragData;
|
|
using WebKit::WebDragOperationsMask;
|
|
using WebKit::WebExternalPopupMenu;
|
|
using WebKit::WebExternalPopupMenuClient;
|
|
using WebKit::WebImage;
|
|
using WebKit::WebNavigationPolicy;
|
|
using WebKit::WebPoint;
|
|
using WebKit::WebPopupMenuInfo;
|
|
using WebKit::WebRect;
|
|
using WebKit::WebWidget;
|
|
|
|
|
|
namespace {
|
|
|
|
void AddMenuItem(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefMenuHandler> handler,
|
|
NSMenu* menu,
|
|
id target,
|
|
cef_menu_id_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 setTarget:target];
|
|
[item setTag:menuId];
|
|
[menu addItem:item];
|
|
}
|
|
|
|
void AddMenuSeparator(NSMenu* menu) {
|
|
NSMenuItem* item = [NSMenuItem separatorItem];
|
|
[menu addItem:item];
|
|
}
|
|
|
|
} // namespace
|
|
|
|
@interface BrowserMenuDelegate : NSObject <NSMenuDelegate> {
|
|
@private
|
|
CefRefPtr<CefBrowserImpl> browser_;
|
|
}
|
|
|
|
- (id)initWithBrowser:(CefBrowserImpl*)browser;
|
|
- (void)menuItemSelected:(id)sender;
|
|
@end
|
|
|
|
@implementation BrowserMenuDelegate
|
|
|
|
- (id)initWithBrowser:(CefBrowserImpl*)browser {
|
|
self = [super init];
|
|
if (self)
|
|
browser_ = browser;
|
|
return self;
|
|
|
|
}
|
|
|
|
// Called when a context menu item is selected by the user.
|
|
- (void)menuItemSelected:(id)sender {
|
|
cef_menu_id_t menuId = static_cast<cef_menu_id_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_.get(), menuId);
|
|
}
|
|
}
|
|
|
|
if(!handled) {
|
|
// Execute the action.
|
|
browser_->UIT_HandleAction(menuId, browser_->GetFocusedFrame());
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
// WebViewClient --------------------------------------------------------------
|
|
|
|
WebExternalPopupMenu* BrowserWebViewDelegate::createExternalPopupMenu(
|
|
const WebPopupMenuInfo& info,
|
|
WebExternalPopupMenuClient* client) {
|
|
DCHECK(!external_popup_menu_.get());
|
|
external_popup_menu_.reset(new ExternalPopupMenu(this, info, client));
|
|
return external_popup_menu_.get();
|
|
}
|
|
|
|
void BrowserWebViewDelegate::ClosePopupMenu() {
|
|
if (external_popup_menu_ == NULL) {
|
|
NOTREACHED();
|
|
return;
|
|
}
|
|
external_popup_menu_.reset();
|
|
}
|
|
|
|
void BrowserWebViewDelegate::showContextMenu(
|
|
WebKit::WebFrame* frame, const WebKit::WebContextMenuData& data) {
|
|
WebWidgetHost* host = GetWidgetHost();
|
|
if (!host)
|
|
return;
|
|
|
|
NSView *view = browser_->UIT_GetMainWndHandle();
|
|
if (!view)
|
|
return;
|
|
|
|
NSWindow* window = [view window];
|
|
|
|
int screenX = -1;
|
|
int screenY = -1;
|
|
NSPoint mouse_pt = {data.mousePosition.x, data.mousePosition.y};
|
|
if (!browser_->IsWindowRenderingDisabled()) {
|
|
mouse_pt = [window mouseLocationOutsideOfEventStream];
|
|
NSPoint screen_pt = [window convertBaseToScreen:mouse_pt];
|
|
screenX = screen_pt.x;
|
|
screenY = screen_pt.y;
|
|
}
|
|
|
|
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, 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 (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)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
BrowserMenuDelegate* delegate =
|
|
[[[BrowserMenuDelegate alloc] initWithBrowser:browser_] autorelease];
|
|
|
|
// Build the correct default context menu
|
|
if (type_flags & MENUTYPE_EDITABLE) {
|
|
menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
|
|
|
|
AddMenuItem(browser_, handler, menu, delegate, MENU_ID_UNDO, "Undo",
|
|
!!(edit_flags & MENU_CAN_UNDO));
|
|
AddMenuItem(browser_, handler, menu, delegate, MENU_ID_REDO, "Redo",
|
|
!!(edit_flags & MENU_CAN_REDO));
|
|
AddMenuSeparator(menu);
|
|
AddMenuItem(browser_, handler, menu, delegate, MENU_ID_CUT, "Cut",
|
|
!!(edit_flags & MENU_CAN_CUT));
|
|
AddMenuItem(browser_, handler, menu, delegate, MENU_ID_COPY, "Copy",
|
|
!!(edit_flags & MENU_CAN_COPY));
|
|
AddMenuItem(browser_, handler, menu, delegate, MENU_ID_PASTE, "Paste",
|
|
!!(edit_flags & MENU_CAN_PASTE));
|
|
AddMenuItem(browser_, handler, menu, delegate, MENU_ID_DELETE, "Delete",
|
|
!!(edit_flags & MENU_CAN_DELETE));
|
|
AddMenuSeparator(menu);
|
|
AddMenuItem(browser_, handler, menu, delegate, 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, delegate, 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, delegate, MENU_ID_NAV_BACK, "Back",
|
|
!!(edit_flags & MENU_CAN_GO_BACK));
|
|
AddMenuItem(browser_, handler, menu, delegate, 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, delegate, MENU_ID_PRINT, "Print",
|
|
// true);
|
|
//AddMenuItem(browser_, handler, menu, delegate, 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.
|
|
NSPoint screen_pt = {screenX, screenY};
|
|
NSPoint window_pt = [window convertScreenToBase:screen_pt];
|
|
|
|
NSEvent* currentEvent = [NSApp currentEvent];
|
|
NSTimeInterval eventTime = [currentEvent timestamp];
|
|
NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown
|
|
location:window_pt
|
|
modifierFlags:NSRightMouseDownMask
|
|
timestamp:eventTime
|
|
windowNumber:[window windowNumber]
|
|
context:nil
|
|
eventNumber:0
|
|
clickCount:1
|
|
pressure:1.0];
|
|
|
|
|
|
// Menu selection events go to the BrowserMenuDelegate.
|
|
[menu setDelegate:delegate];
|
|
|
|
// Show the menu.
|
|
[NSMenu popUpContextMenu:menu
|
|
withEvent:clickEvent
|
|
forView:view];
|
|
}
|
|
|
|
// WebWidgetClient ------------------------------------------------------------
|
|
|
|
void BrowserWebViewDelegate::show(WebNavigationPolicy policy) {
|
|
DCHECK(this != browser_->UIT_GetPopupDelegate());
|
|
}
|
|
|
|
void BrowserWebViewDelegate::didChangeCursor(const WebCursorInfo& cursor_info) {
|
|
NSCursor* ns_cursor = WebCursor(cursor_info).GetNativeCursor();
|
|
|
|
if (!browser_->IsWindowRenderingDisabled()) {
|
|
[ns_cursor set];
|
|
} else {
|
|
// Notify the handler of cursor change.
|
|
CefRefPtr<CefClient> client = browser_->GetClient();
|
|
if (client.get()) {
|
|
CefRefPtr<CefRenderHandler> handler = client->GetRenderHandler();
|
|
if (handler.get())
|
|
handler->OnCursorChange(browser_, ns_cursor);
|
|
}
|
|
}
|
|
}
|
|
|
|
WebRect BrowserWebViewDelegate::windowRect() {
|
|
if (WebWidgetHost* host = GetWidgetHost()) {
|
|
if (!browser_->IsWindowRenderingDisabled()) {
|
|
NSView *view = host->view_handle();
|
|
NSRect rect = [view frame];
|
|
return gfx::Rect(NSRectToCGRect(rect));
|
|
} else {
|
|
// Retrieve the view rectangle from the handler.
|
|
CefRefPtr<CefClient> client = browser_->GetClient();
|
|
if (client.get()) {
|
|
CefRefPtr<CefRenderHandler> handler = client->GetRenderHandler();
|
|
if (handler.get()) {
|
|
CefRect rect(0, 0, 0, 0);
|
|
if (handler->GetViewRect(browser_, rect))
|
|
return WebRect(rect.x, rect.y, rect.width, rect.height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return WebRect();
|
|
}
|
|
|
|
void BrowserWebViewDelegate::setWindowRect(const WebRect& rect) {
|
|
if (this == browser_->UIT_GetWebViewDelegate()) {
|
|
// TODO(port): Set the window rectangle.
|
|
} else if (this == browser_->UIT_GetPopupDelegate()) {
|
|
NOTREACHED();
|
|
}
|
|
}
|
|
|
|
WebRect BrowserWebViewDelegate::rootWindowRect() {
|
|
if (WebWidgetHost* host = GetWidgetHost()) {
|
|
NSView *view = host->view_handle();
|
|
NSRect rect = [[[view window] contentView] frame];
|
|
return gfx::Rect(NSRectToCGRect(rect));
|
|
}
|
|
return WebRect();
|
|
}
|
|
|
|
@interface NSWindow(OSInternals)
|
|
- (NSRect)_growBoxRect;
|
|
@end
|
|
|
|
WebRect BrowserWebViewDelegate::windowResizerRect() {
|
|
NSRect resize_rect = NSMakeRect(0, 0, 0, 0);
|
|
WebWidgetHost* host = GetWidgetHost();
|
|
if (host) {
|
|
NSView *view = host->view_handle();
|
|
NSWindow* window = [view window];
|
|
if (window == nil)
|
|
return gfx::Rect();
|
|
|
|
resize_rect = [window _growBoxRect];
|
|
// The scrollbar assumes that the resizer goes all the way down to the
|
|
// bottom corner, so we ignore any y offset to the rect itself and use the
|
|
// entire bottom corner.
|
|
resize_rect.origin.y = 0;
|
|
// Convert to view coordinates from window coordinates.
|
|
resize_rect = [view convertRect:resize_rect fromView:nil];
|
|
// Flip the rect in view coordinates
|
|
resize_rect.origin.y =
|
|
[view frame].size.height - resize_rect.origin.y -
|
|
resize_rect.size.height;
|
|
}
|
|
return gfx::Rect(NSRectToCGRect(resize_rect));
|
|
}
|
|
|
|
void BrowserWebViewDelegate::startDragging(
|
|
WebKit::WebFrame* frame,
|
|
const WebDragData& data,
|
|
WebDragOperationsMask mask,
|
|
const WebImage& image,
|
|
const WebPoint& image_offset) {
|
|
if (browser_->settings().drag_drop_disabled ||
|
|
browser_->IsWindowRenderingDisabled()) {
|
|
browser_->UIT_GetWebView()->dragSourceSystemDragEnded();
|
|
return;
|
|
}
|
|
|
|
WebWidgetHost* host = GetWidgetHost();
|
|
if (!host)
|
|
return;
|
|
|
|
BrowserWebView *view = static_cast<BrowserWebView*>(host->view_handle());
|
|
if (!view)
|
|
return;
|
|
|
|
WebDropData drop_data(data);
|
|
|
|
CefRefPtr<CefClient> client = browser_->GetClient();
|
|
if (client.get()) {
|
|
CefRefPtr<CefDragHandler> handler = client->GetDragHandler();
|
|
if (handler.get()) {
|
|
CefRefPtr<CefDragData> data(new CefDragDataImpl(drop_data));
|
|
if (handler->OnDragStart(browser_, data,
|
|
static_cast<CefDragHandler::DragOperationsMask>(mask))) {
|
|
browser_->UIT_GetWebView()->dragSourceSystemDragEnded();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// By allowing nested tasks, the code below also allows Close(),
|
|
// which would deallocate |this|. The same problem can occur while
|
|
// processing -sendEvent:, so Close() is deferred in that case.
|
|
// Drags from web content do not come via -sendEvent:, this sets the
|
|
// same flag -sendEvent: would.
|
|
CefScopedSendingEvent sendingEventScoper;
|
|
|
|
// The drag invokes a nested event loop, arrange to continue
|
|
// processing events.
|
|
MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
|
|
|
|
NSImage* ns_image = nil;
|
|
if (!image.isNull()) {
|
|
ui::ScaleFactor scale_factor =
|
|
ui::GetScaleFactorForNativeView(host->view_handle());
|
|
gfx::ImageSkia image_skia(
|
|
gfx::ImageSkiaRep(image.getSkBitmap(), scale_factor));
|
|
ns_image = gfx::NSImageFromImageSkia(image_skia);
|
|
}
|
|
NSPoint offset = NSPointFromCGPoint(gfx::Point(image_offset).ToCGPoint());
|
|
|
|
// Keep a reference to the NSView so that it won't be destroyed until after
|
|
// the drag operation has completed.
|
|
[view retain];
|
|
|
|
[view startDragWithDropData:drop_data
|
|
dragOperationMask:static_cast<NSDragOperation>(mask)
|
|
image:ns_image
|
|
offset:offset];
|
|
|
|
[view release];
|
|
}
|
|
|
|
void BrowserWebViewDelegate::runModal() {
|
|
NOTIMPLEMENTED();
|
|
}
|
|
|
|
// WebPluginPageDelegate ------------------------------------------------------
|
|
|
|
webkit::npapi::WebPluginDelegate* BrowserWebViewDelegate::CreatePluginDelegate(
|
|
const FilePath& path,
|
|
const std::string& mime_type) {
|
|
WebWidgetHost *host = GetWidgetHost();
|
|
if (!host)
|
|
return NULL;
|
|
|
|
WebPluginDelegateImpl* delegate = WebPluginDelegateImpl::Create(path, mime_type);
|
|
if (delegate)
|
|
delegate->SetNoBufferContext();
|
|
return delegate;
|
|
}
|
|
|
|
void BrowserWebViewDelegate::CreatedPluginWindow(
|
|
gfx::PluginWindowHandle handle) {
|
|
if (browser_->IsWindowRenderingDisabled()) {
|
|
WebViewHost* host = browser_->UIT_GetWebViewHost();
|
|
if (host)
|
|
host->AddWindowedPlugin(handle);
|
|
}
|
|
}
|
|
|
|
void BrowserWebViewDelegate::WillDestroyPluginWindow(
|
|
gfx::PluginWindowHandle handle) {
|
|
if (browser_->IsWindowRenderingDisabled()) {
|
|
WebViewHost* host = browser_->UIT_GetWebViewHost();
|
|
if (host)
|
|
host->RemoveWindowedPlugin(handle);
|
|
}
|
|
}
|
|
|
|
void BrowserWebViewDelegate::DidMovePlugin(
|
|
const webkit::npapi::WebPluginGeometry& move) {
|
|
if (browser_->IsWindowRenderingDisabled()) {
|
|
WebViewHost* host = browser_->UIT_GetWebViewHost();
|
|
if (host) {
|
|
host->MoveWindowedPlugin(move);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Protected methods ----------------------------------------------------------
|
|
|
|
void BrowserWebViewDelegate::ShowJavaScriptAlert(
|
|
WebKit::WebFrame* webframe, const CefString& message) {
|
|
std::string messageStr(message);
|
|
NSString *text = [NSString stringWithUTF8String:messageStr.c_str()];
|
|
NSAlert *alert = [NSAlert alertWithMessageText:@"JavaScript Alert"
|
|
defaultButton:@"OK"
|
|
alternateButton:nil
|
|
otherButton:nil
|
|
informativeTextWithFormat:text];
|
|
[alert runModal];
|
|
}
|
|
|
|
bool BrowserWebViewDelegate::ShowJavaScriptConfirm(
|
|
WebKit::WebFrame* webframe, const CefString& message) {
|
|
NOTIMPLEMENTED();
|
|
return false;
|
|
}
|
|
|
|
bool BrowserWebViewDelegate::ShowJavaScriptPrompt(
|
|
WebKit::WebFrame* webframe, const CefString& message,
|
|
const CefString& default_value, CefString* result) {
|
|
NOTIMPLEMENTED();
|
|
return false;
|
|
}
|
|
|
|
// Called to show the file chooser dialog.
|
|
bool BrowserWebViewDelegate::ShowFileChooser(std::vector<FilePath>& file_names,
|
|
const bool multi_select,
|
|
const WebKit::WebString& title,
|
|
const FilePath& default_file) {
|
|
NSOpenPanel* dialog = [NSOpenPanel openPanel];
|
|
if (!title.isNull())
|
|
[dialog setTitle:base::SysUTF16ToNSString(title)];
|
|
|
|
NSString* default_dir = nil;
|
|
NSString* default_filename = nil;
|
|
if (!default_file.empty()) {
|
|
// The file dialog is going to do a ton of stats anyway. Not much
|
|
// point in eliminating this one.
|
|
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
|
if (file_util::DirectoryExists(default_file)) {
|
|
default_dir = base::SysUTF8ToNSString(default_file.value());
|
|
} else {
|
|
default_dir = base::SysUTF8ToNSString(default_file.DirName().value());
|
|
default_filename =
|
|
base::SysUTF8ToNSString(default_file.BaseName().value());
|
|
}
|
|
}
|
|
|
|
[dialog setAllowsOtherFileTypes:YES];
|
|
[dialog setAllowsMultipleSelection:multi_select];
|
|
[dialog setCanChooseFiles:YES];
|
|
[dialog setCanChooseDirectories:NO];
|
|
|
|
// [NSOpenPanel runModalForDirectory:file:] was deprecated on OS-X 10.7.
|
|
NSInteger result;
|
|
if ([NSOpenPanel respondsToSelector:@selector(runModalForDirectory:file:)]) {
|
|
result = (NSInteger) [dialog performSelector:@selector(runModalForDirectory:file:)
|
|
withObject:default_dir
|
|
withObject:default_filename];
|
|
} else {
|
|
[dialog setDirectoryURL:[NSURL URLWithString:default_dir]];
|
|
[dialog setNameFieldStringValue:default_filename];
|
|
result = [dialog runModal];
|
|
}
|
|
|
|
if (result == NSFileHandlingPanelCancelButton)
|
|
return false;
|
|
|
|
NSArray *urls = [dialog URLs];
|
|
int i, count = [urls count];
|
|
for (i=0; i<count; i++) {
|
|
NSURL* url = [urls objectAtIndex:i];
|
|
if ([url isFileURL])
|
|
file_names.push_back(FilePath(base::SysNSStringToUTF8([url path])));
|
|
}
|
|
|
|
return true;
|
|
}
|