- Add HTML5 drag&drop support (issue #140).
- Client application must now provide NSApplication implementing CefAppProtocol and call CefRunMessageLoop() (issue #248).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@269 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2011-08-02 16:50:06 +00:00
parent b2100d27bd
commit 7d006a8dd6
28 changed files with 1778 additions and 356 deletions

24
cef.gyp
View File

@ -165,6 +165,7 @@
],
},
'sources': [
'include/cef_application_mac.h',
'tests/cefclient/cefclient_mac.mm',
'tests/cefclient/client_handler_mac.mm',
'tests/cefclient/resource_util_mac.mm',
@ -413,6 +414,11 @@
],
'xcode_settings': {
'INSTALL_PATH': '@executable_path',
# The libcef_static target contains ObjC categories. Passing the -ObjC flag
# is necessary to properly load them and avoid a "selector not recognized"
# runtime error. See http://developer.apple.com/library/mac/#qa/qa1490/_index.html
# for more information.
'OTHER_LDFLAGS': ['-Wl,-ObjC'],
},
'conditions': [
['OS=="win"', {
@ -680,6 +686,8 @@
'libcef/cef_time_util.h',
'libcef/drag_download_file.cc',
'libcef/drag_download_file.h',
'libcef/drag_download_util.cc',
'libcef/drag_download_util.h',
'libcef/dom_storage_area.cc',
'libcef/dom_storage_area.h',
'libcef/dom_storage_common.h',
@ -693,6 +701,8 @@
'libcef/dom_event_impl.h',
'libcef/dom_node_impl.cc',
'libcef/dom_node_impl.h',
'libcef/download_util.cc',
'libcef/download_util.h',
'libcef/external_protocol_handler.h',
'libcef/http_header_utils.cc',
'libcef/http_header_utils.h',
@ -753,6 +763,7 @@
}],
[ 'OS=="mac"', {
'sources': [
'include/cef_application_mac.h',
'include/internal/cef_types_mac.h',
'include/internal/cef_mac.h',
'libcef/browser_impl_mac.mm',
@ -766,6 +777,19 @@
'libcef/external_protocol_handler_mac.mm',
'libcef/webview_host_mac.mm',
'libcef/webwidget_host_mac.mm',
'libcef/web_drag_source_mac.h',
'libcef/web_drag_source_mac.mm',
'libcef/web_drag_utils_mac.h',
'libcef/web_drag_utils_mac.mm',
'libcef/web_drop_target_mac.h',
'libcef/web_drop_target_mac.mm',
# Build necessary Mozilla sources
'../third_party/mozilla/NSPasteboard+Utils.h',
'../third_party/mozilla/NSPasteboard+Utils.mm',
'../third_party/mozilla/NSString+Utils.h',
'../third_party/mozilla/NSString+Utils.mm',
'../third_party/mozilla/NSURL+Utils.h',
'../third_party/mozilla/NSURL+Utils.m',
],
}],
[ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', {

View File

@ -0,0 +1,82 @@
// Copyright (c) 2011 Marshall A. Greenblatt. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the name Chromium Embedded
// Framework nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef _CEF_APPLICATION_MAC_H
#define _CEF_APPLICATION_MAC_H
#pragma once
#include "cef.h"
#if defined(OS_MACOSX) && defined(__OBJC__)
#ifdef BUILDING_CEF_SHARED
// Use the existing CrAppProtocol definition.
#include "base/message_pump_mac.h"
#else // BUILDING_CEF_SHARED
#import <AppKit/AppKit.h>
// Copy of CrAppProtocol definition from base/message_pump_mac.h.
@protocol CrAppProtocol
// Must return true if -[NSApplication sendEvent:] is currently on the stack.
- (BOOL)isHandlingSendEvent;
@end
#endif // BUILDING_CEF_SHARED
// All CEF client applications must subclass NSApplication and implement this
// protocol.
@protocol CefAppProtocol<CrAppProtocol>
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent;
@end
// Controls the state of |isHandlingSendEvent| in the event loop so that it is
// reset properly.
class CefScopedSendingEvent {
public:
CefScopedSendingEvent()
: app_(static_cast<NSApplication<CefAppProtocol>*>(
[NSApplication sharedApplication])),
handling_([app_ isHandlingSendEvent]) {
[app_ setHandlingSendEvent:YES];
}
~CefScopedSendingEvent() {
[app_ setHandlingSendEvent:handling_];
}
private:
NSApplication<CefAppProtocol>* app_;
BOOL handling_;
};
#endif // defined(OS_MACOSX) && defined(__OBJC__)
#endif // _CEF_APPLICATION_MAC_H

View File

@ -9,6 +9,8 @@
#include "browser_webview_delegate.h"
#include "cef_thread.h"
#include "drag_download_file.h"
#include "drag_download_util.h"
#include "download_util.h"
#include "web_drag_source_win.h"
#include "web_drag_utils_win.h"
#include "web_drop_target_win.h"
@ -17,16 +19,8 @@
#include <string>
#include "base/file_path.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "base/task.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/utf_string_conversions.h"
#include "net/base/file_stream.h"
#include "net/base/mime_util.h"
#include "net/base/net_util.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
@ -71,214 +65,6 @@ LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) {
return CallNextHookEx(msg_hook, code, wparam, lparam);
}
// Parse the download metadata set in DataTransfer.setData. The metadata
// consists of a set of the following values separated by ":"
// * MIME type
// * File name
// * URL
// If the file name contains special characters, they need to be escaped
// appropriately.
// For example, we can have
// text/plain:example.txt:http://example.com/example.txt
// From chrome/browser/download/drag_download_util.cc
bool ParseDownloadMetadata(const string16& metadata,
string16* mime_type,
FilePath* file_name,
GURL* url) {
const char16 separator = L':';
size_t mime_type_end_pos = metadata.find(separator);
if (mime_type_end_pos == string16::npos)
return false;
size_t file_name_end_pos = metadata.find(separator, mime_type_end_pos + 1);
if (file_name_end_pos == string16::npos)
return false;
GURL parsed_url = GURL(metadata.substr(file_name_end_pos + 1));
if (!parsed_url.is_valid())
return false;
if (mime_type)
*mime_type = metadata.substr(0, mime_type_end_pos);
if (file_name) {
string16 file_name_str = metadata.substr(
mime_type_end_pos + 1, file_name_end_pos - mime_type_end_pos - 1);
#if defined(OS_WIN)
*file_name = FilePath(file_name_str);
#else
*file_name = FilePath(UTF16ToUTF8(file_name_str));
#endif
}
if (url)
*url = parsed_url;
return true;
}
#if defined(OS_WIN)
// Returns whether the specified extension is automatically integrated into the
// windows shell.
// From chrome/browser/download/download_util.cc
bool IsShellIntegratedExtension(const string16& extension) {
string16 extension_lower = StringToLowerASCII(extension);
static const wchar_t* const integrated_extensions[] = {
// See <http://msdn.microsoft.com/en-us/library/ms811694.aspx>.
L"local",
// Right-clicking on shortcuts can be magical.
L"lnk",
};
for (int i = 0; i < arraysize(integrated_extensions); ++i) {
if (extension_lower == integrated_extensions[i])
return true;
}
// See <http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html>.
// That vulnerability report is not exactly on point, but files become magical
// if their end in a CLSID. Here we block extensions that look like CLSIDs.
if (extension_lower.size() > 0 && extension_lower.at(0) == L'{' &&
extension_lower.at(extension_lower.length() - 1) == L'}')
return true;
return false;
}
// Returns whether the specified file name is a reserved name on windows.
// This includes names like "com2.zip" (which correspond to devices) and
// desktop.ini and thumbs.db which have special meaning to the windows shell.
// From chrome/browser/download/download_util.cc
bool IsReservedName(const string16& filename) {
// This list is taken from the MSDN article "Naming a file"
// http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx
// I also added clock$ because GetSaveFileName seems to consider it as a
// reserved name too.
static const wchar_t* const known_devices[] = {
L"con", L"prn", L"aux", L"nul", L"com1", L"com2", L"com3", L"com4", L"com5",
L"com6", L"com7", L"com8", L"com9", L"lpt1", L"lpt2", L"lpt3", L"lpt4",
L"lpt5", L"lpt6", L"lpt7", L"lpt8", L"lpt9", L"clock$"
};
string16 filename_lower = StringToLowerASCII(filename);
for (int i = 0; i < arraysize(known_devices); ++i) {
// Exact match.
if (filename_lower == known_devices[i])
return true;
// Starts with "DEVICE.".
if (filename_lower.find(string16(known_devices[i]) + L".") == 0)
return true;
}
static const wchar_t* const magic_names[] = {
// These file names are used by the "Customize folder" feature of the shell.
L"desktop.ini",
L"thumbs.db",
};
for (int i = 0; i < arraysize(magic_names); ++i) {
if (filename_lower == magic_names[i])
return true;
}
return false;
}
#endif // OS_WIN
// Create an extension based on the file name and mime type.
// From chrome/browser/download/download_util.cc
void GenerateExtension(const FilePath& file_name,
const std::string& mime_type,
FilePath::StringType* generated_extension) {
// We're worried about two things here:
//
// 1) Usability. If the site fails to provide a file extension, we want to
// guess a reasonable file extension based on the content type.
//
// 2) Shell integration. Some file extensions automatically integrate with
// the shell. We block these extensions to prevent a malicious web site
// from integrating with the user's shell.
// See if our file name already contains an extension.
FilePath::StringType extension = file_name.Extension();
if (!extension.empty())
extension.erase(extension.begin()); // Erase preceding '.'.
#if defined(OS_WIN)
static const FilePath::CharType default_extension[] =
FILE_PATH_LITERAL("download");
// Rename shell-integrated extensions.
if (IsShellIntegratedExtension(extension))
extension.assign(default_extension);
#endif
if (extension.empty()) {
// The GetPreferredExtensionForMimeType call will end up going to disk. Do
// this on another thread to avoid slowing the IO thread.
// http://crbug.com/61827
base::ThreadRestrictions::ScopedAllowIO allow_io;
net::GetPreferredExtensionForMimeType(mime_type, &extension);
}
generated_extension->swap(extension);
}
// Used to make sure we have a safe file extension and filename for a
// download. |file_name| can either be just the file name or it can be a
// full path to a file.
// From chrome/browser/download/download_util.cc
void GenerateSafeFileName(const std::string& mime_type, FilePath* file_name) {
// Make sure we get the right file extension
FilePath::StringType extension;
GenerateExtension(*file_name, mime_type, &extension);
*file_name = file_name->ReplaceExtension(extension);
#if defined(OS_WIN)
// Prepend "_" to the file name if it's a reserved name
FilePath::StringType leaf_name = file_name->BaseName().value();
DCHECK(!leaf_name.empty());
if (IsReservedName(leaf_name)) {
leaf_name = FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name;
*file_name = file_name->DirName();
if (file_name->value() == FilePath::kCurrentDirectory) {
*file_name = FilePath(leaf_name);
} else {
*file_name = file_name->Append(leaf_name);
}
}
#endif
}
// Create a file name based on the response from the server.
// From chrome/browser/download/download_util.cc
void GenerateFileName(const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& mime_type,
FilePath* generated_name) {
string16 new_name = net::GetSuggestedFilename(GURL(url),
content_disposition,
referrer_charset,
"",
string16(L"download"));
// TODO(evan): this code is totally wrong -- we should just generate
// Unicode filenames and do all this encoding switching at the end.
// However, I'm just shuffling wrong code around, at least not adding
// to it.
#if defined(OS_WIN)
*generated_name = FilePath(new_name);
#else
*generated_name = FilePath(
base::SysWideToNativeMB(UTF16ToWide(new_name)));
#endif
DCHECK(!generated_name->empty());
GenerateSafeFileName(mime_type, generated_name);
}
} // namespace
class DragDropThread : public base::Thread {
@ -406,21 +192,22 @@ void BrowserDragDelegate::PrepareDragForDownload(
string16 mime_type;
FilePath file_name;
GURL download_url;
if (!ParseDownloadMetadata(drop_data.download_metadata,
&mime_type,
&file_name,
&download_url))
if (!drag_download_util::ParseDownloadMetadata(drop_data.download_metadata,
&mime_type,
&file_name,
&download_url))
return;
// Generate the download filename.
std::string content_disposition =
"attachment; filename=" + UTF16ToUTF8(file_name.value());
FilePath generated_file_name;
GenerateFileName(download_url,
content_disposition,
std::string(),
UTF16ToUTF8(mime_type),
&generated_file_name);
download_util::GenerateFileName(download_url,
content_disposition,
std::string(),
UTF16ToUTF8(mime_type),
std::string(),
&generated_file_name);
// Provide the data as file (CF_HDROP). A temporary download file with the
// Zone.Identifier ADS (Alternate Data Stream) attached will be created.

View File

@ -700,6 +700,8 @@ void CefBrowserImpl::UIT_DestroyBrowser()
}
}
}
UIT_GetWebViewDelegate()->RevokeDragDrop();
#else
// Call OnBeforeClose() here for platforms that don't support modal dialogs.
if (client_.get()) {
@ -711,8 +713,6 @@ void CefBrowserImpl::UIT_DestroyBrowser()
}
#endif
UIT_GetWebViewDelegate()->RevokeDragDrop();
// If the current browser window is a dev tools client then disconnect from
// the agent and destroy the client before destroying the window.
UIT_DestroyDevToolsClient();

View File

@ -59,7 +59,6 @@ void CefBrowserImpl::UIT_CreateBrowser(const CefString& url)
WebViewHost::Create(window_info_.m_ParentWidget, gfx::Rect(),
delegate_.get(), NULL, dev_tools_agent_.get(),
prefs));
delegate_->RegisterDragDrop();
if (!settings_.developer_tools_disabled)
dev_tools_agent_->SetWebView(webviewhost_->webview());

View File

@ -86,7 +86,6 @@ void CefBrowserImpl::UIT_CreateBrowser(const CefString& url)
webviewhost_.reset(
WebViewHost::Create(parentView, contentRect, delegate_.get(),
NULL, dev_tools_agent_.get(), prefs));
delegate_->RegisterDragDrop();
if (!settings_.developer_tools_disabled)
dev_tools_agent_->SetWebView(webviewhost_->webview());

View File

@ -34,12 +34,10 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragData.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileError.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileSystemCallbacks.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebHistoryItem.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebImage.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKitClient.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
@ -61,7 +59,6 @@
#include "webkit/appcache/web_application_cache_host_impl.h"
#include "webkit/glue/glue_serialize.h"
#include "webkit/glue/media/video_renderer_impl.h"
#include "webkit/glue/webdropdata.h"
#include "webkit/glue/webpreferences.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/glue/webmediaplayer_impl.h"
@ -71,7 +68,6 @@
#include "webkit/plugins/npapi/webplugin_impl.h"
#if defined(OS_WIN)
// TODO(port): make these files work everywhere.
#include "browser_drag_delegate_win.h"
#include "web_drop_target_win.h"
#endif
@ -84,8 +80,6 @@ using WebKit::WebContextMenuData;
using WebKit::WebCookieJar;
using WebKit::WebData;
using WebKit::WebDataSource;
using WebKit::WebDragData;
using WebKit::WebDragOperationsMask;
using WebKit::WebEditingAction;
using WebKit::WebFileChooserParams;
using WebKit::WebFileSystem;
@ -93,7 +87,6 @@ using WebKit::WebFileSystemCallbacks;
using WebKit::WebFormElement;
using WebKit::WebFrame;
using WebKit::WebHistoryItem;
using WebKit::WebImage;
using WebKit::WebMediaPlayer;
using WebKit::WebMediaPlayerClient;
using WebKit::WebNavigationType;
@ -446,27 +439,6 @@ void BrowserWebViewDelegate::setToolTipText(
GetWidgetHost()->SetTooltipText(tooltipStr);
}
void BrowserWebViewDelegate::startDragging(
const WebDragData& data,
WebDragOperationsMask mask,
const WebImage& image,
const WebPoint& image_offset) {
#if defined(OS_WIN)
// Dragging is not supported when window rendering is disabled.
if (browser_->IsWindowRenderingDisabled()) {
EndDragging();
return;
}
drag_delegate_ = new BrowserDragDelegate(this);
drag_delegate_->StartDragging(WebDropData(data), mask, image.getSkBitmap(),
image_offset);
#else
// TODO(port): Support drag and drop.
EndDragging();
#endif
}
void BrowserWebViewDelegate::focusNext() {
CefRefPtr<CefClient> client = browser_->GetClient();
if (client.get()) {
@ -986,22 +958,6 @@ void BrowserWebViewDelegate::SetSelectTrailingWhitespaceEnabled(bool enabled) {
// allows both.
}
void BrowserWebViewDelegate::RegisterDragDrop() {
#if defined(OS_WIN)
// TODO(port): add me once drag and drop works.
DCHECK(!drop_target_);
drop_target_ = new WebDropTarget(browser_->UIT_GetWebViewWndHandle(),
browser_->UIT_GetWebView());
#endif
}
void BrowserWebViewDelegate::RevokeDragDrop() {
#if defined(OS_WIN)
if (drop_target_.get())
::RevokeDragDrop(browser_->UIT_GetWebViewWndHandle());
#endif
}
void BrowserWebViewDelegate::SetCustomPolicyDelegate(bool is_custom,
bool is_permissive) {
policy_delegate_enabled_ = is_custom;
@ -1062,13 +1018,6 @@ WebWidgetHost* BrowserWebViewDelegate::GetWidgetHost() {
return NULL;
}
void BrowserWebViewDelegate::EndDragging() {
browser_->UIT_GetWebView()->dragSourceSystemDragEnded();
#if defined(OS_WIN)
drag_delegate_ = NULL;
#endif
}
void BrowserWebViewDelegate::UpdateForCommittedLoad(WebFrame* frame,
bool is_new_navigation) {
// Code duplicated from RenderView::DidCommitLoadForFrame.

View File

@ -214,6 +214,13 @@ class BrowserWebViewDelegate : public WebKit::WebViewClient,
// Additional accessors
#if defined(OS_WIN)
// Sets the webview as a drop target.
void RegisterDragDrop();
void RevokeDragDrop();
// Called after dragging has finished.
void EndDragging();
BrowserDragDelegate* drag_delegate() { return drag_delegate_.get(); }
WebDropTarget* drop_target() { return drop_target_.get(); }
#endif
@ -222,12 +229,6 @@ class BrowserWebViewDelegate : public WebKit::WebViewClient,
pending_extra_data_.reset(extra_data);
}
// Sets the webview as a drop target.
void RegisterDragDrop();
void RevokeDragDrop();
void ResetDragDrop();
void SetCustomPolicyDelegate(bool is_custom, bool is_permissive);
void WaitForPolicyDelegate();
@ -255,9 +256,6 @@ class BrowserWebViewDelegate : public WebKit::WebViewClient,
void ClosePopupMenu();
#endif
// Called after dragging has finished.
void EndDragging();
protected:
// Default handling of JavaScript messages.
void ShowJavaScriptAlert(WebKit::WebFrame* webframe,

View File

@ -177,6 +177,15 @@ WebRect BrowserWebViewDelegate::windowResizerRect() {
return WebRect();
}
void BrowserWebViewDelegate::startDragging(
const WebDragData& data,
WebDragOperationsMask mask,
const WebImage& image,
const WebPoint& image_offset) {
// TODO(port): Support drag and drop.
browser_->UIT_GetWebView()->dragSourceSystemDragEnded();
}
void BrowserWebViewDelegate::runModal() {
NOTIMPLEMENTED();
}

View File

@ -3,23 +3,35 @@
// found in the LICENSE file.
#include "browser_webview_delegate.h"
#import "browser_webview_mac.h"
#include "browser_impl.h"
#import "include/cef_application_mac.h"
#import <Cocoa/Cocoa.h>
#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/WebCursorInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragData.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebImage.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPoint.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.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::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;
@ -109,6 +121,39 @@ WebRect BrowserWebViewDelegate::windowResizerRect() {
return gfx::Rect(NSRectToCGRect(resize_rect));
}
void BrowserWebViewDelegate::startDragging(const WebDragData& data,
WebDragOperationsMask mask,
const WebImage& image,
const WebPoint& image_offset) {
WebWidgetHost* host = GetWidgetHost();
if (!host)
return;
BrowserWebView *view = static_cast<BrowserWebView*>(host->view_handle());
if (!view)
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());
NSDragOperation op_mask = static_cast<NSDragOperation>(mask);
const SkBitmap& bitmap = gfx::CGImageToSkBitmap(image.getCGImageRef());
CGColorSpaceRef color_space = base::mac::GetSystemColorSpace();
NSImage* ns_image = gfx::SkBitmapToNSImageWithColorSpace(bitmap, color_space);
NSPoint offset = NSPointFromCGPoint(gfx::Point(image_offset).ToCGPoint());
[view startDragWithDropData:WebDropData(data)
dragOperationMask:op_mask
image:ns_image
offset:offset];
}
void BrowserWebViewDelegate::runModal() {
NOTIMPLEMENTED();
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2008-2009 The Chromium Embedded Framework Authors.
// Copyright (c) 2011 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2006-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.
@ -7,10 +7,12 @@
// as the WebViewDelegate for the BrowserWebHost. The host is expected to
// have initialized a MessageLoop before these methods are called.
#include "browser_webview_delegate.h"
#include "browser_drag_delegate_win.h"
#include "browser_navigation_controller.h"
#include "browser_impl.h"
#include "browser_webview_delegate.h"
#include "cef_context.h"
#include "web_drop_target_win.h"
#include <objidl.h>
#include <shlobj.h>
@ -21,7 +23,10 @@
#include "net/base/net_errors.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/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebImage.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPoint.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "ui/gfx/gdi_util.h"
@ -38,10 +43,14 @@
using webkit::npapi::WebPluginDelegateImpl;
using WebKit::WebContextMenuData;
using WebKit::WebCursorInfo;
using WebKit::WebDragData;
using WebKit::WebDragOperationsMask;
using WebKit::WebExternalPopupMenu;
using WebKit::WebExternalPopupMenuClient;
using WebKit::WebFrame;
using WebKit::WebImage;
using WebKit::WebNavigationPolicy;
using WebKit::WebPoint;
using WebKit::WebPopupMenuInfo;
using WebKit::WebRect;
using WebKit::WebWidget;
@ -169,6 +178,22 @@ WebRect BrowserWebViewDelegate::windowResizerRect() {
return WebRect();
}
void BrowserWebViewDelegate::startDragging(
const WebDragData& data,
WebDragOperationsMask mask,
const WebImage& image,
const WebPoint& image_offset) {
// Dragging is not supported when window rendering is disabled.
if (browser_->IsWindowRenderingDisabled()) {
EndDragging();
return;
}
drag_delegate_ = new BrowserDragDelegate(this);
drag_delegate_->StartDragging(WebDropData(data), mask, image.getSkBitmap(),
image_offset);
}
void BrowserWebViewDelegate::runModal() {
WebWidgetHost* host = GetWidgetHost();
if (!host)
@ -535,6 +560,22 @@ end:
// Private methods ------------------------------------------------------------
void BrowserWebViewDelegate::RegisterDragDrop() {
DCHECK(!drop_target_);
drop_target_ = new WebDropTarget(browser_->UIT_GetWebViewWndHandle(),
browser_->UIT_GetWebView());
}
void BrowserWebViewDelegate::RevokeDragDrop() {
if (drop_target_.get())
::RevokeDragDrop(browser_->UIT_GetWebViewWndHandle());
}
void BrowserWebViewDelegate::EndDragging() {
browser_->UIT_GetWebView()->dragSourceSystemDragEnded();
drag_delegate_ = NULL;
}
void BrowserWebViewDelegate::ShowJavaScriptAlert(WebFrame* webframe,
const CefString& message)
{

View File

@ -3,8 +3,12 @@
// found in the LICENSE file.
#import <Cocoa/Cocoa.h>
#include "base/memory/scoped_nsobject.h"
class CefBrowserImpl;
@class WebDragSource;
@class WebDropTarget;
struct WebDropData;
// A view to wrap the WebCore view and help it live in a Cocoa world. The
// (rough) equivalent of Apple's WebView.
@ -13,6 +17,9 @@ class CefBrowserImpl;
@private
CefBrowserImpl *browser_; // weak
NSTrackingArea *trackingArea_;
scoped_nsobject<WebDragSource> dragSource_;
scoped_nsobject<WebDropTarget> dropTarget_;
}
- (void)mouseDown:(NSEvent *)theEvent;
@ -33,6 +40,12 @@ class CefBrowserImpl;
- (BOOL)isOpaque;
- (void)setFrame:(NSRect)frameRect;
// Called from BrowserWebViewDelegate::startDragging() to initiate dragging.
- (void)startDragWithDropData:(const WebDropData&)dropData
dragOperationMask:(NSDragOperation)operationMask
image:(NSImage*)image
offset:(NSPoint)offset;
@property (nonatomic, assign) CefBrowserImpl *browser;
@end

View File

@ -8,9 +8,12 @@
#include "browser_impl.h"
#include "cef_context.h"
#import "web_drag_source_mac.h"
#import "web_drop_target_mac.h"
#include "webwidget_host.h"
#include "base/memory/scoped_ptr.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "ui/gfx/rect.h"
@ -22,13 +25,20 @@
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
dropTarget_.reset([[WebDropTarget alloc] initWithWebView:self]);
// Register the view to handle the appropriate drag types.
NSArray* types = [NSArray arrayWithObjects:NSStringPboardType,
NSHTMLPboardType, NSURLPboardType, nil];
[self registerForDraggedTypes:types];
trackingArea_ =
[[NSTrackingArea alloc] initWithRect:frame
options:NSTrackingMouseMoved |
NSTrackingActiveInActiveApp |
NSTrackingInVisibleRect
owner:self
userInfo:nil];
[[NSTrackingArea alloc] initWithRect:frame
options:NSTrackingMouseMoved |
NSTrackingActiveInActiveApp |
NSTrackingInVisibleRect
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea_];
}
return self;
@ -170,9 +180,91 @@
- (void)setFrame:(NSRect)frameRect {
[super setFrame:frameRect];
if (browser_ && browser_->UIT_GetWebView())
browser_->UIT_GetWebViewHost()->Resize(gfx::Rect(NSRectToCGRect(frameRect)));
if (browser_ && browser_->UIT_GetWebView()) {
browser_->UIT_GetWebViewHost()->Resize(
gfx::Rect(NSRectToCGRect(frameRect)));
}
[self setNeedsDisplay:YES];
}
- (void)startDragWithDropData:(const WebDropData&)dropData
dragOperationMask:(NSDragOperation)operationMask
image:(NSImage*)image
offset:(NSPoint)offset {
dragSource_.reset([[WebDragSource alloc]
initWithWebView:self
dropData:&dropData
image:image
offset:offset
pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
dragOperationMask:operationMask]);
[dragSource_ startDrag];
}
// NSPasteboardOwner methods
- (void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString*)type {
[dragSource_ lazyWriteToPasteboard:sender
forType:type];
}
// NSDraggingSource methods
// Returns what kind of drag operations are available. This is a required
// method for NSDraggingSource.
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
if (dragSource_.get())
return [dragSource_ draggingSourceOperationMaskForLocal:isLocal];
// No web drag source - this is the case for dragging a file from the
// downloads manager. Default to copy operation. Note: It is desirable to
// allow the user to either move or copy, but this requires additional
// plumbing to update the download item's path once its moved.
return NSDragOperationCopy;
}
// Called when a drag initiated in our view ends.
- (void)draggedImage:(NSImage*)anImage
endedAt:(NSPoint)screenPoint
operation:(NSDragOperation)operation {
[dragSource_ endDragAt:screenPoint operation:operation];
// Might as well throw out this object now.
dragSource_.reset();
}
// Called when a drag initiated in our view moves.
- (void)draggedImage:(NSImage*)draggedImage movedTo:(NSPoint)screenPoint {
[dragSource_ moveDragTo:screenPoint];
}
// Called when we're informed where a file should be dropped.
- (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDest {
if (![dropDest isFileURL])
return nil;
NSString* file_name = [dragSource_ dragPromisedFileTo:[dropDest path]];
if (!file_name)
return nil;
return [NSArray arrayWithObject:file_name];
}
// NSDraggingDestination methods
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
return [dropTarget_ draggingEntered:sender view:self];
}
- (void)draggingExited:(id<NSDraggingInfo>)sender {
[dropTarget_ draggingExited:sender];
}
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
return [dropTarget_ draggingUpdated:sender view:self];
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
return [dropTarget_ performDragOperation:sender view:self];
}
@end

View File

@ -3,67 +3,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <AppKit/AppKit.h>
#include "include/cef.h"
#import "include/cef_application_mac.h"
#include "cef_process_ui_thread.h"
#include "browser_webkit_glue.h"
#include "base/message_pump_mac.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "third_party/WebKit/Source/WebKit/mac/WebCoreSupport/WebSystemInterface.h"
// CrAppProtocol implementation.
@interface CrApplication : NSApplication<CrAppProtocol> {
@private
BOOL handlingSendEvent_;
}
- (BOOL)isHandlingSendEvent;
@end
@implementation CrApplication
- (BOOL)isHandlingSendEvent {
return handlingSendEvent_;
}
- (void)sendEvent:(NSEvent*)event {
BOOL wasHandlingSendEvent = handlingSendEvent_;
handlingSendEvent_ = YES;
[super sendEvent:event];
handlingSendEvent_ = wasHandlingSendEvent;
}
@end
namespace {
// Memory autorelease pool.
static NSAutoreleasePool* g_autopool = nil;
void RunLoopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity,
void* info)
{
CefDoMessageLoopWork();
}
} // namespace
void CefProcessUIThread::PlatformInit() {
// Initialize the CrApplication instance.
[CrApplication sharedApplication];
g_autopool = [[NSAutoreleasePool alloc] init];
// The NSApplication instance must implement the CefAppProtocol protocol.
DCHECK([[NSApplication sharedApplication]
conformsToProtocol:@protocol(CefAppProtocol)]);
InitWebCoreSystemInterface();
// Register the run loop observer.
CFRunLoopObserverRef observer =
CFRunLoopObserverCreate(NULL,
kCFRunLoopBeforeWaiting,
YES, /* repeat */
0,
&RunLoopObserver,
NULL);
if (observer) {
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer,
kCFRunLoopCommonModes);
}
webkit_glue::InitializeDataPak();
// On Mac, the select popup menus are rendered by the browser.
@ -71,6 +26,4 @@ void CefProcessUIThread::PlatformInit() {
}
void CefProcessUIThread::PlatformCleanUp() {
[g_autopool release];
}

188
libcef/download_util.cc Normal file
View File

@ -0,0 +1,188 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2011 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 "download_util.h"
#include "base/file_path.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "googleurl/src/gurl.h"
#include "net/base/mime_util.h"
#include "net/base/net_util.h"
namespace {
#if defined(OS_WIN)
// Returns whether the specified extension is automatically integrated into the
// windows shell.
// From chrome/browser/download/download_util.cc
bool IsShellIntegratedExtension(const string16& extension) {
string16 extension_lower = StringToLowerASCII(extension);
static const wchar_t* const integrated_extensions[] = {
// See <http://msdn.microsoft.com/en-us/library/ms811694.aspx>.
L"local",
// Right-clicking on shortcuts can be magical.
L"lnk",
};
for (int i = 0; i < arraysize(integrated_extensions); ++i) {
if (extension_lower == integrated_extensions[i])
return true;
}
// See <http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html>.
// That vulnerability report is not exactly on point, but files become magical
// if their end in a CLSID. Here we block extensions that look like CLSIDs.
if (extension_lower.size() > 0 && extension_lower.at(0) == L'{' &&
extension_lower.at(extension_lower.length() - 1) == L'}')
return true;
return false;
}
// Returns whether the specified file name is a reserved name on windows.
// This includes names like "com2.zip" (which correspond to devices) and
// desktop.ini and thumbs.db which have special meaning to the windows shell.
// From chrome/browser/download/download_util.cc
bool IsReservedName(const string16& filename) {
// This list is taken from the MSDN article "Naming a file"
// http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx
// I also added clock$ because GetSaveFileName seems to consider it as a
// reserved name too.
static const wchar_t* const known_devices[] = {
L"con", L"prn", L"aux", L"nul", L"com1", L"com2", L"com3", L"com4", L"com5",
L"com6", L"com7", L"com8", L"com9", L"lpt1", L"lpt2", L"lpt3", L"lpt4",
L"lpt5", L"lpt6", L"lpt7", L"lpt8", L"lpt9", L"clock$"
};
string16 filename_lower = StringToLowerASCII(filename);
for (int i = 0; i < arraysize(known_devices); ++i) {
// Exact match.
if (filename_lower == known_devices[i])
return true;
// Starts with "DEVICE.".
if (filename_lower.find(string16(known_devices[i]) + L".") == 0)
return true;
}
static const wchar_t* const magic_names[] = {
// These file names are used by the "Customize folder" feature of the shell.
L"desktop.ini",
L"thumbs.db",
};
for (int i = 0; i < arraysize(magic_names); ++i) {
if (filename_lower == magic_names[i])
return true;
}
return false;
}
#endif // OS_WIN
// Create an extension based on the file name and mime type.
// From chrome/browser/download/download_util.cc
void GenerateExtension(const FilePath& file_name,
const std::string& mime_type,
FilePath::StringType* generated_extension) {
// We're worried about two things here:
//
// 1) Usability. If the site fails to provide a file extension, we want to
// guess a reasonable file extension based on the content type.
//
// 2) Shell integration. Some file extensions automatically integrate with
// the shell. We block these extensions to prevent a malicious web site
// from integrating with the user's shell.
// See if our file name already contains an extension.
FilePath::StringType extension = file_name.Extension();
if (!extension.empty())
extension.erase(extension.begin()); // Erase preceding '.'.
#if defined(OS_WIN)
static const FilePath::CharType default_extension[] =
FILE_PATH_LITERAL("download");
// Rename shell-integrated extensions.
if (IsShellIntegratedExtension(extension))
extension.assign(default_extension);
#endif
if (extension.empty()) {
// The GetPreferredExtensionForMimeType call will end up going to disk. Do
// this on another thread to avoid slowing the IO thread.
// http://crbug.com/61827
base::ThreadRestrictions::ScopedAllowIO allow_io;
net::GetPreferredExtensionForMimeType(mime_type, &extension);
}
generated_extension->swap(extension);
}
// Used to make sure we have a safe file extension and filename for a
// download. |file_name| can either be just the file name or it can be a
// full path to a file.
// From chrome/browser/download/download_util.cc
void GenerateSafeFileName(const std::string& mime_type, FilePath* file_name) {
// Make sure we get the right file extension
FilePath::StringType extension;
GenerateExtension(*file_name, mime_type, &extension);
*file_name = file_name->ReplaceExtension(extension);
#if defined(OS_WIN)
// Prepend "_" to the file name if it's a reserved name
FilePath::StringType leaf_name = file_name->BaseName().value();
DCHECK(!leaf_name.empty());
if (IsReservedName(leaf_name)) {
leaf_name = FilePath::StringType(FILE_PATH_LITERAL("_")) + leaf_name;
*file_name = file_name->DirName();
if (file_name->value() == FilePath::kCurrentDirectory) {
*file_name = FilePath(leaf_name);
} else {
*file_name = file_name->Append(leaf_name);
}
}
#endif
}
} // namespace
namespace download_util {
// Create a file name based on the response from the server.
// From chrome/browser/download/download_util.cc
void GenerateFileName(const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& mime_type,
const std::string& suggested_name,
FilePath* generated_name) {
string16 new_name = net::GetSuggestedFilename(GURL(url),
content_disposition,
referrer_charset,
suggested_name,
ASCIIToUTF16("download"));
// TODO(evan): this code is totally wrong -- we should just generate
// Unicode filenames and do all this encoding switching at the end.
// However, I'm just shuffling wrong code around, at least not adding
// to it.
#if defined(OS_WIN)
*generated_name = FilePath(new_name);
#else
*generated_name = FilePath(base::SysWideToNativeMB(UTF16ToWide(new_name)));
#endif
DCHECK(!generated_name->empty());
GenerateSafeFileName(mime_type, generated_name);
}
} // namespace download_util

27
libcef/download_util.h Normal file
View File

@ -0,0 +1,27 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2011 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.
#ifndef _DOWNLOAD_UTIL_H
#define _DOWNLOAD_UTIL_H
#pragma once
#include <string>
class GURL;
class FilePath;
namespace download_util {
// Create a file name based on the response from the server.
void GenerateFileName(const GURL& url,
const std::string& content_disposition,
const std::string& referrer_charset,
const std::string& mime_type,
const std::string& suggested_name,
FilePath* generated_name);
} // namespace download_util
#endif // _DOWNLOAD_UTIL_H

View File

@ -0,0 +1,113 @@
// Copyright (c) 2011 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 "cef_thread.h"
#include "drag_download_util.h"
#include "base/string_util.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/task.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"
#include "net/base/file_stream.h"
#include "net/base/net_errors.h"
using net::FileStream;
namespace drag_download_util {
bool ParseDownloadMetadata(const string16& metadata,
string16* mime_type,
FilePath* file_name,
GURL* url) {
const char16 separator = L':';
size_t mime_type_end_pos = metadata.find(separator);
if (mime_type_end_pos == string16::npos)
return false;
size_t file_name_end_pos = metadata.find(separator, mime_type_end_pos + 1);
if (file_name_end_pos == string16::npos)
return false;
GURL parsed_url = GURL(metadata.substr(file_name_end_pos + 1));
if (!parsed_url.is_valid())
return false;
if (mime_type)
*mime_type = metadata.substr(0, mime_type_end_pos);
if (file_name) {
string16 file_name_str = metadata.substr(
mime_type_end_pos + 1, file_name_end_pos - mime_type_end_pos - 1);
#if defined(OS_WIN)
*file_name = FilePath(file_name_str);
#else
*file_name = FilePath(UTF16ToUTF8(file_name_str));
#endif
}
if (url)
*url = parsed_url;
return true;
}
FileStream* CreateFileStreamForDrop(FilePath* file_path) {
DCHECK(file_path && !file_path->empty());
scoped_ptr<FileStream> file_stream(new FileStream);
const int kMaxSeq = 99;
for (int seq = 0; seq <= kMaxSeq; seq++) {
FilePath new_file_path;
if (seq == 0) {
new_file_path = *file_path;
} else {
#if defined(OS_WIN)
string16 suffix = ASCIIToUTF16("-") + base::IntToString16(seq);
#else
std::string suffix = std::string("-") + base::IntToString(seq);
#endif
new_file_path = file_path->InsertBeforeExtension(suffix);
}
// Explicitly (and redundantly check) for file -- despite the fact that our
// open won't overwrite -- just to avoid log spew.
if (!file_util::PathExists(new_file_path) &&
file_stream->Open(new_file_path, base::PLATFORM_FILE_CREATE |
base::PLATFORM_FILE_WRITE) == net::OK) {
*file_path = new_file_path;
return file_stream.release();
}
}
return NULL;
}
PromiseFileFinalizer::PromiseFileFinalizer(
DragDownloadFile* drag_file_downloader)
: drag_file_downloader_(drag_file_downloader) {
}
PromiseFileFinalizer::~PromiseFileFinalizer() {}
void PromiseFileFinalizer::Cleanup() {
if (drag_file_downloader_.get())
drag_file_downloader_ = NULL;
}
void PromiseFileFinalizer::OnDownloadCompleted(const FilePath& file_path) {
CefThread::PostTask(
CefThread::UI, FROM_HERE,
NewRunnableMethod(this, &PromiseFileFinalizer::Cleanup));
}
void PromiseFileFinalizer::OnDownloadAborted() {
CefThread::PostTask(
CefThread::UI, FROM_HERE,
NewRunnableMethod(this, &PromiseFileFinalizer::Cleanup));
}
} // namespace drag_download_util

View File

@ -0,0 +1,63 @@
// Copyright (c) 2011 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.
#ifndef _DRAG_DOWNLOAD_UTIL_H
#define _DRAG_DOWNLOAD_UTIL_H
#pragma once
#include "drag_download_file.h"
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/string16.h"
#include "ui/base/dragdrop/download_file_interface.h"
class FilePath;
class GURL;
namespace net {
class FileStream;
}
namespace drag_download_util {
// Parse the download metadata set in DataTransfer.setData. The metadata
// consists of a set of the following values separated by ":"
// * MIME type
// * File name
// * URL
// If the file name contains special characters, they need to be escaped
// appropriately.
// For example, we can have
// text/plain:example.txt:http://example.com/example.txt
bool ParseDownloadMetadata(const string16& metadata,
string16* mime_type,
FilePath* file_name,
GURL* url);
// Create a new file at the specified path. If the file already exists, try to
// insert the sequential unifier to produce a new file, like foo-01.txt.
// Return a FileStream if successful.
net::FileStream* CreateFileStreamForDrop(FilePath* file_path);
// Implementation of DownloadFileObserver to finalize the download process.
class PromiseFileFinalizer : public ui::DownloadFileObserver {
public:
explicit PromiseFileFinalizer(DragDownloadFile* drag_file_downloader);
virtual ~PromiseFileFinalizer();
// DownloadFileObserver methods.
virtual void OnDownloadCompleted(const FilePath& file_path);
virtual void OnDownloadAborted();
private:
void Cleanup();
scoped_refptr<DragDownloadFile> drag_file_downloader_;
DISALLOW_COPY_AND_ASSIGN(PromiseFileFinalizer);
};
} // namespace drag_download_util
#endif // _DRAG_DOWNLOAD_UTIL_H

View File

@ -0,0 +1,82 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2011 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.
#import <Cocoa/Cocoa.h>
#include "base/file_path.h"
#include "base/memory/scoped_nsobject.h"
#include "base/memory/scoped_ptr.h"
#include "googleurl/src/gurl.h"
#include "ui/gfx/native_widget_types.h"
struct WebDropData;
@class BrowserWebView;
// A class that handles tracking and event processing for a drag and drop
// originating from the content area.
@interface WebDragSource : NSObject {
@private
// Our web view. Weak reference (owns or co-owns us).
BrowserWebView* view_;
// Our drop data. Should only be initialized once.
scoped_ptr<WebDropData> dropData_;
// The image to show as drag image. Can be nil.
scoped_nsobject<NSImage> dragImage_;
// The offset to draw |dragImage_| at.
NSPoint imageOffset_;
// Our pasteboard.
scoped_nsobject<NSPasteboard> pasteboard_;
// A mask of the allowed drag operations.
NSDragOperation dragOperationMask_;
// The file name to be saved to for a drag-out download.
FilePath downloadFileName_;
// The URL to download from for a drag-out download.
GURL downloadURL_;
}
// Initialize a WebDragSource object for a drag (originating on the given
// BrowserWebView and with the given dropData and pboard). Fill the pasteboard
// with data types appropriate for dropData.
- (id)initWithWebView:(BrowserWebView*)view
dropData:(const WebDropData*)dropData
image:(NSImage*)image
offset:(NSPoint)offset
pasteboard:(NSPasteboard*)pboard
dragOperationMask:(NSDragOperation)dragOperationMask;
// Returns a mask of the allowed drag operations.
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
// Call when asked to do a lazy write to the pasteboard; hook up to
// -pasteboard:provideDataForType: (on the BrowserWebView).
- (void)lazyWriteToPasteboard:(NSPasteboard*)pboard
forType:(NSString*)type;
// Start the drag (on the originally provided BrowserWebView); can do this right
// after -initWithContentsView:....
- (void)startDrag;
// End the drag and clear the pasteboard; hook up to
// -draggedImage:endedAt:operation:.
- (void)endDragAt:(NSPoint)screenPoint
operation:(NSDragOperation)operation;
// Drag moved; hook up to -draggedImage:movedTo:.
- (void)moveDragTo:(NSPoint)screenPoint;
// Call to drag a promised file to the given path (should be called before
// -endDragAt:...); hook up to -namesOfPromisedFilesDroppedAtDestination:.
// Returns the file name (not including path) of the file deposited (or which
// will be deposited).
- (NSString*)dragPromisedFileTo:(NSString*)path;
@end

View File

@ -0,0 +1,456 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2011 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 "browser_impl.h"
#import "browser_webview_mac.h"
#include "drag_download_util.h"
#include "download_util.h"
#import "web_drag_source_mac.h"
#include <sys/param.h>
#include "base/file_path.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/task.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/utf_string_conversions.h"
#include "net/base/file_stream.h"
#include "net/base/net_util.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPoint.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "ui/gfx/mac/nsimage_cache.h"
#include "webkit/glue/webdropdata.h"
using base::SysNSStringToUTF8;
using base::SysUTF8ToNSString;
using base::SysUTF16ToNSString;
using net::FileStream;
using WebKit::WebDragOperationNone;
using WebKit::WebView;
namespace {
// An unofficial standard pasteboard title type to be provided alongside the
// |NSURLPboardType|.
NSString* const kNSURLTitlePboardType = @"public.url-name";
// Converts a string16 into a FilePath. Use this method instead of
// -[NSString fileSystemRepresentation] to prevent exceptions from being thrown.
// See http://crbug.com/78782 for more info.
FilePath FilePathFromFilename(const string16& filename) {
NSString* str = SysUTF16ToNSString(filename);
char buf[MAXPATHLEN];
if (![str getFileSystemRepresentation:buf maxLength:sizeof(buf)])
return FilePath();
return FilePath(buf);
}
// Returns a filename appropriate for the drop data
// TODO(viettrungluu): Refactor to make it common across platforms,
// and move it somewhere sensible.
FilePath GetFileNameFromDragData(const WebDropData& drop_data) {
// Images without ALT text will only have a file extension so we need to
// synthesize one from the provided extension and URL.
FilePath file_name(FilePathFromFilename(drop_data.file_description_filename));
file_name = file_name.BaseName().RemoveExtension();
if (file_name.empty()) {
// Retrieve the name from the URL.
string16 suggested_filename =
net::GetSuggestedFilename(drop_data.url, "", "", "", string16());
file_name = FilePathFromFilename(suggested_filename);
}
file_name = file_name.ReplaceExtension(UTF16ToUTF8(drop_data.file_extension));
return file_name;
}
// This class's sole task is to write out data for a promised file; the caller
// is responsible for opening the file.
class PromiseWriterTask : public Task {
public:
// Assumes ownership of file_stream.
PromiseWriterTask(const WebDropData& drop_data,
FileStream* file_stream);
virtual ~PromiseWriterTask();
virtual void Run();
private:
WebDropData drop_data_;
// This class takes ownership of file_stream_ and will close and delete it.
scoped_ptr<FileStream> file_stream_;
};
// Takes the drop data and an open file stream (which it takes ownership of and
// will close and delete).
PromiseWriterTask::PromiseWriterTask(const WebDropData& drop_data,
FileStream* file_stream) :
drop_data_(drop_data) {
file_stream_.reset(file_stream);
DCHECK(file_stream_.get());
}
PromiseWriterTask::~PromiseWriterTask() {
DCHECK(file_stream_.get());
if (file_stream_.get())
file_stream_->Close();
}
void PromiseWriterTask::Run() {
CHECK(file_stream_.get());
file_stream_->Write(drop_data_.file_contents.data(),
drop_data_.file_contents.length(),
NULL);
// Let our destructor take care of business.
}
} // namespace
@interface WebDragSource(Private)
- (void)fillPasteboard;
- (NSImage*)dragImage;
@end // @interface WebDragSource(Private)
@implementation WebDragSource
- (id)initWithWebView:(BrowserWebView*)view
dropData:(const WebDropData*)dropData
image:(NSImage*)image
offset:(NSPoint)offset
pasteboard:(NSPasteboard*)pboard
dragOperationMask:(NSDragOperation)dragOperationMask {
if ((self = [super init])) {
view_ = view;
DCHECK(view_);
dropData_.reset(new WebDropData(*dropData));
DCHECK(dropData_.get());
dragImage_.reset([image retain]);
imageOffset_ = offset;
pasteboard_.reset([pboard retain]);
DCHECK(pasteboard_.get());
dragOperationMask_ = dragOperationMask;
[self fillPasteboard];
}
return self;
}
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
return dragOperationMask_;
}
- (void)lazyWriteToPasteboard:(NSPasteboard*)pboard forType:(NSString*)type {
// NSHTMLPboardType requires the character set to be declared. Otherwise, it
// assumes US-ASCII. Awesome.
static const string16 kHtmlHeader =
ASCIIToUTF16("<meta http-equiv=\"Content-Type\" "
"content=\"text/html;charset=UTF-8\">");
// Be extra paranoid; avoid crashing.
if (!dropData_.get()) {
NOTREACHED() << "No drag-and-drop data available for lazy write.";
return;
}
// HTML.
if ([type isEqualToString:NSHTMLPboardType]) {
DCHECK(!dropData_->text_html.empty());
// See comment on |kHtmlHeader| above.
[pboard setString:SysUTF16ToNSString(kHtmlHeader + dropData_->text_html)
forType:NSHTMLPboardType];
// URL.
} else if ([type isEqualToString:NSURLPboardType]) {
DCHECK(dropData_->url.is_valid());
NSString* urlStr = SysUTF8ToNSString(dropData_->url.spec());
NSURL* url = [NSURL URLWithString:urlStr];
// If NSURL creation failed, check for a badly-escaped javascript URL.
if (!url && urlStr && dropData_->url.SchemeIs("javascript")) {
NSString *escapedStr =
[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
url = [NSURL URLWithString:escapedStr];
}
[url writeToPasteboard:pboard];
// URL title.
} else if ([type isEqualToString:kNSURLTitlePboardType]) {
[pboard setString:SysUTF16ToNSString(dropData_->url_title)
forType:kNSURLTitlePboardType];
// File contents.
} else if ([type isEqualToString:NSFileContentsPboardType] ||
[type isEqualToString:NSCreateFileContentsPboardType(
SysUTF16ToNSString(dropData_->file_extension))]) {
// TODO(viettrungluu: find something which is known to accept
// NSFileContentsPboardType to check that this actually works!
scoped_nsobject<NSFileWrapper> file_wrapper(
[[NSFileWrapper alloc] initRegularFileWithContents:[NSData
dataWithBytes:dropData_->file_contents.data()
length:dropData_->file_contents.length()]]);
[file_wrapper setPreferredFilename:SysUTF8ToNSString(
GetFileNameFromDragData(*dropData_).value())];
[pboard writeFileWrapper:file_wrapper];
// TIFF.
} else if ([type isEqualToString:NSTIFFPboardType]) {
// TODO(viettrungluu): This is a bit odd since we rely on Cocoa to render
// our image into a TIFF. This is also suboptimal since this is all done
// synchronously. I'm not sure there's much we can easily do about it.
scoped_nsobject<NSImage> image(
[[NSImage alloc] initWithData:[NSData
dataWithBytes:dropData_->file_contents.data()
length:dropData_->file_contents.length()]]);
[pboard setData:[image TIFFRepresentation] forType:NSTIFFPboardType];
// Plain text.
} else if ([type isEqualToString:NSStringPboardType]) {
DCHECK(!dropData_->plain_text.empty());
[pboard setString:SysUTF16ToNSString(dropData_->plain_text)
forType:NSStringPboardType];
// Oops!
} else {
NOTREACHED() << "Asked for a drag pasteboard type we didn't offer.";
}
}
- (NSPoint)convertScreenPoint:(NSPoint)screenPoint {
NSPoint basePoint = [[view_ window] convertScreenToBase:screenPoint];
return [view_ convertPoint:basePoint fromView:nil];
}
- (void)startDrag {
NSEvent* currentEvent = [NSApp currentEvent];
// Synthesize an event for dragging, since we can't be sure that
// [NSApp currentEvent] will return a valid dragging event.
NSWindow* window = [view_ window];
NSPoint position = [window mouseLocationOutsideOfEventStream];
NSTimeInterval eventTime = [currentEvent timestamp];
NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
location:position
modifierFlags:NSLeftMouseDraggedMask
timestamp:eventTime
windowNumber:[window windowNumber]
context:nil
eventNumber:0
clickCount:1
pressure:1.0];
if (dragImage_) {
position.x -= imageOffset_.x;
// Deal with Cocoa's flipped coordinate system.
position.y -= [dragImage_.get() size].height - imageOffset_.y;
}
// Per kwebster, offset arg is ignored, see -_web_DragImageForElement: in
// third_party/WebKit/Source/WebKit/mac/Misc/WebNSViewExtras.m.
[window dragImage:[self dragImage]
at:position
offset:NSZeroSize
event:dragEvent
pasteboard:pasteboard_
source:view_
slideBack:YES];
}
- (void)endDragAt:(NSPoint)screenPoint
operation:(NSDragOperation)operation {
// Convert |screenPoint| to view coordinates and flip it.
NSWindow* window = [view_ window];
NSPoint localPoint = [self convertScreenPoint:screenPoint];
NSRect viewFrame = [window frame];
localPoint.y = viewFrame.size.height - localPoint.y;
// Flip |screenPoint|.
NSRect screenFrame = [[window screen] frame];
screenPoint.y = screenFrame.size.height - screenPoint.y;
// If AppKit returns a copy and move operation, mask off the move bit
// because WebCore does not understand what it means to do both, which
// results in an assertion failure/renderer crash.
if (operation == (NSDragOperationMove | NSDragOperationCopy))
operation &= ~NSDragOperationMove;
// TODO: Figure out why |operation| is always NSDragOperationNone.
if (operation == NSDragOperationNone)
operation = NSDragOperationCopy;
WebView* webview = view_.browser->UIT_GetWebView();
gfx::Point client(localPoint.x, localPoint.y);
gfx::Point screen(screenPoint.x, screenPoint.y);
webview->dragSourceEndedAt(client, screen,
static_cast<WebKit::WebDragOperation>(operation));
// Make sure the pasteboard owner isn't us.
[pasteboard_ declareTypes:[NSArray array] owner:nil];
webview->dragSourceSystemDragEnded();
}
- (void)moveDragTo:(NSPoint)screenPoint {
// Convert |screenPoint| to view coordinates and flip it.
NSWindow* window = [view_ window];
NSPoint localPoint = [self convertScreenPoint:screenPoint];
NSRect viewFrame = [window frame];
localPoint.y = viewFrame.size.height - localPoint.y;
// Flip |screenPoint|.
NSRect screenFrame = [[window screen] frame];
screenPoint.y = screenFrame.size.height - screenPoint.y;
WebView* webview = view_.browser->UIT_GetWebView();
gfx::Point client(localPoint.x, localPoint.y);
gfx::Point screen(screenPoint.x, screenPoint.y);
webview->dragSourceMovedTo(client, screen, WebDragOperationNone);
}
- (NSString*)dragPromisedFileTo:(NSString*)path {
// Be extra paranoid; avoid crashing.
if (!dropData_.get()) {
NOTREACHED() << "No drag-and-drop data available for promised file.";
return nil;
}
FilePath fileName = downloadFileName_.empty() ?
GetFileNameFromDragData(*dropData_) : downloadFileName_;
FilePath filePath(SysNSStringToUTF8(path));
filePath = filePath.Append(fileName);
// CreateFileStreamForDrop() will call file_util::PathExists(),
// which is blocking. Since this operation is already blocking the
// UI thread on OSX, it should be reasonable to let it happen.
base::ThreadRestrictions::ScopedAllowIO allowIO;
FileStream* fileStream =
drag_download_util::CreateFileStreamForDrop(&filePath);
if (!fileStream)
return nil;
if (downloadURL_.is_valid()) {
WebView* webview = view_.browser->UIT_GetWebView();
const GURL& page_url = webview->mainFrame()->document().url();
const std::string& page_encoding =
webview->mainFrame()->document().encoding().utf8();
scoped_refptr<DragDownloadFile> dragFileDownloader(new DragDownloadFile(
filePath,
linked_ptr<net::FileStream>(fileStream),
downloadURL_,
page_url,
page_encoding,
view_.browser->UIT_GetWebViewDelegate()));
// The finalizer will take care of closing and deletion.
dragFileDownloader->Start(
new drag_download_util::PromiseFileFinalizer(dragFileDownloader));
} else {
// The writer will take care of closing and deletion.
CefThread::PostTask(CefThread::FILE, FROM_HERE,
new PromiseWriterTask(*dropData_, fileStream));
}
// Once we've created the file, we should return the file name.
return SysUTF8ToNSString(filePath.BaseName().value());
return nil;
}
@end // @implementation WebDragSource
@implementation WebDragSource (Private)
- (void)fillPasteboard {
DCHECK(pasteboard_.get());
[pasteboard_ declareTypes:[NSArray array] owner:view_];
// HTML.
if (!dropData_->text_html.empty())
[pasteboard_ addTypes:[NSArray arrayWithObject:NSHTMLPboardType]
owner:view_];
// URL (and title).
if (dropData_->url.is_valid())
[pasteboard_ addTypes:[NSArray arrayWithObjects:NSURLPboardType,
kNSURLTitlePboardType, nil]
owner:view_];
// File.
if (!dropData_->file_contents.empty() ||
!dropData_->download_metadata.empty()) {
NSString* fileExtension = 0;
if (dropData_->download_metadata.empty()) {
// |dropData_->file_extension| comes with the '.', which we must strip.
fileExtension = (dropData_->file_extension.length() > 0) ?
SysUTF16ToNSString(dropData_->file_extension.substr(1)) : @"";
} else {
string16 mimeType;
FilePath fileName;
if (drag_download_util::ParseDownloadMetadata(
dropData_->download_metadata,
&mimeType,
&fileName,
&downloadURL_)) {
download_util::GenerateFileName(
downloadURL_,
std::string(),
std::string(),
UTF16ToUTF8(mimeType),
fileName.value(),
&downloadFileName_);
fileExtension = SysUTF8ToNSString(downloadFileName_.Extension());
}
}
if (fileExtension) {
// File contents (with and without specific type), file (HFS) promise,
// TIFF.
// TODO(viettrungluu): others?
[pasteboard_ addTypes:[NSArray arrayWithObjects:
NSFileContentsPboardType,
NSCreateFileContentsPboardType(fileExtension),
NSFilesPromisePboardType,
NSTIFFPboardType,
nil]
owner:view_];
// For the file promise, we need to specify the extension.
[pasteboard_ setPropertyList:[NSArray arrayWithObject:fileExtension]
forType:NSFilesPromisePboardType];
}
}
// Plain text.
if (!dropData_->plain_text.empty())
[pasteboard_ addTypes:[NSArray arrayWithObject:NSStringPboardType]
owner:view_];
}
- (NSImage*)dragImage {
if (dragImage_)
return dragImage_;
// Default to returning a generic image.
return gfx::GetCachedImageWithName(@"nav.pdf");
}
@end // @implementation WebDragSource (Private)

View File

@ -0,0 +1,37 @@
// Copyright (c) 2011 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.
#ifndef _WEB_DRAG_UTILS_MAC
#define _WEB_DRAG_UTILS_MAC
#pragma once
#import <Cocoa/Cocoa.h>
#include "base/basictypes.h"
#include "googleurl/src/gurl.h"
namespace drag_util {
// Populates the |url| and |title| with URL data in |pboard|. There may be more
// than one, but we only handle dropping the first. |url| must not be |NULL|;
// |title| is an optional parameter. Returns |YES| if URL data was obtained from
// the pasteboard, |NO| otherwise. If |convert_filenames| is |YES|, the function
// will also attempt to convert filenames in |pboard| to file URLs.
BOOL PopulateURLAndTitleFromPasteBoard(GURL* url,
string16* title,
NSPasteboard* pboard,
BOOL convert_filenames);
// Returns the first file URL from |info|, if there is one. If |info| doesn't
// contain any file URLs, an empty |GURL| is returned.
GURL GetFileURLFromDropData(id<NSDraggingInfo> info);
// Determines whether the given drag and drop operation contains content that
// is supported by the web view. In particular, if the content is a local file
// URL, this checks if it is of a type that can be shown in the tab contents.
BOOL IsUnsupportedDropData(id<NSDraggingInfo> info);
} // namespace drag_util
#endif // _WEB_DRAG_UTILS_MAC

View File

@ -0,0 +1,101 @@
// Copyright (c) 2011 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.
#import "web_drag_utils_mac.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/sys_string_conversions.h"
#include "googleurl/src/gurl.h"
#include "net/base/mime_util.h"
#include "net/base/net_util.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
#include "webkit/plugins/npapi/plugin_list.h"
namespace drag_util {
BOOL PopulateURLAndTitleFromPasteBoard(GURL* url,
string16* title,
NSPasteboard* pboard,
BOOL convert_filenames) {
CHECK(url);
// Bail out early if there's no URL data.
if (![pboard containsURLData])
return NO;
// -getURLs:andTitles:convertingFilenames: will already validate URIs so we
// don't need to again. The arrays returned are both of NSStrings.
NSArray* url_array = nil;
NSArray* title_array = nil;
[pboard getURLs:&url_array andTitles:&title_array
convertingFilenames:convert_filenames];
DCHECK_EQ([url_array count], [title_array count]);
// It's possible that no URLs were actually provided!
if (![url_array count])
return NO;
NSString* url_string = [url_array objectAtIndex:0];
if ([url_string length]) {
// Check again just to make sure to not assign NULL into a std::string,
// which throws an exception.
const char* utf8_url = [url_string UTF8String];
if (utf8_url) {
*url = GURL(utf8_url);
// Extra paranoia check.
if (title && [title_array count])
*title = base::SysNSStringToUTF16([title_array objectAtIndex:0]);
}
}
return YES;
}
GURL GetFileURLFromDropData(id<NSDraggingInfo> info) {
if ([[info draggingPasteboard] containsURLData]) {
GURL url;
PopulateURLAndTitleFromPasteBoard(&url,
NULL,
[info draggingPasteboard],
YES);
if (url.SchemeIs("file"))
return url;
}
return GURL();
}
static BOOL IsSupportedFileURL(const GURL& url) {
FilePath full_path;
net::FileURLToFilePath(url, &full_path);
std::string mime_type;
net::GetMimeTypeFromFile(full_path, &mime_type);
// This logic mirrors |BufferedResourceHandler::ShouldDownload()|.
// TODO(asvitkine): Refactor this out to a common location instead of
// duplicating code.
if (net::IsSupportedMimeType(mime_type))
return YES;
// Check whether there is a plugin that supports the mime type. (e.g. PDF)
webkit::npapi::PluginList* list = webkit::npapi::PluginList::Singleton();
webkit::npapi::WebPluginInfo info;
if (list->PluginsLoaded() &&
list->GetPluginInfo(GURL(), mime_type, false, &info, NULL)) {
return webkit::npapi::IsPluginEnabled(info);
}
return NO;
}
BOOL IsUnsupportedDropData(id<NSDraggingInfo> info) {
GURL url = GetFileURLFromDropData(info);
if (!url.is_empty()) {
// If dragging a file, only allow dropping supported file types (that the
// web view can display).
return !IsSupportedFileURL(url);
}
return NO;
}
} // namespace drag_util

View File

@ -0,0 +1,69 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2011 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.
#import <Cocoa/Cocoa.h>
#include "base/string16.h"
class GURL;
struct WebDropData;
@class BrowserWebView;
class WebViewHost;
// A class that handles tracking and event processing for a drag and drop
// over the content area. Assumes something else initiates the drag, this is
// only for processing during a drag.
@interface WebDropTarget : NSObject {
@private
// Our associated WebView. Weak reference.
BrowserWebView* view_;
// Updated asynchronously during a drag to tell us whether or not we should
// allow the drop.
NSDragOperation current_operation_;
// Keep track of the WebViewHost we're dragging over. If it changes during a
// drag, we need to re-send the DragEnter message.
WebViewHost* current_wvh_;
}
// |view| is the WebView representing this browser window, used to communicate
// drag&drop messages to WebCore and handle navigation on a successful drop
// (if necessary).
- (id)initWithWebView:(BrowserWebView*)view;
// Sets the current operation negotiated by the source and destination,
// which determines whether or not we should allow the drop. Takes effect the
// next time |-draggingUpdated:| is called.
- (void)setCurrentOperation: (NSDragOperation)operation;
// Messages to send during the tracking of a drag, ususally upon receiving
// calls from the view system. Communicates the drag messages to WebCore.
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info
view:(NSView*)view;
- (void)draggingExited:(id<NSDraggingInfo>)info;
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info
view:(NSView*)view;
- (BOOL)performDragOperation:(id<NSDraggingInfo>)info
view:(NSView*)view;
@end
// Public use only for unit tests.
@interface WebDropTarget(Testing)
// Given |data|, which should not be nil, fill it in using the contents of the
// given pasteboard.
- (void)populateWebDropData:(WebDropData*)data
fromPasteboard:(NSPasteboard*)pboard;
// Given a point in window coordinates and a view in that window, return a
// flipped point in the coordinate system of |view|.
- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint
view:(NSView*)view;
// Given a point in window coordinates and a view in that window, return a
// flipped point in screen coordinates.
- (NSPoint)flipWindowPointToScreen:(const NSPoint&)windowPoint
view:(NSView*)view;
@end

View File

@ -0,0 +1,234 @@
// Copyright (c) 2011 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2011 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 "browser_impl.h"
#import "browser_webview_mac.h"
#include "cef_context.h"
#import "web_drop_target_mac.h"
#import "web_drag_utils_mac.h"
#include "base/logging.h"
#include "base/sys_string_conversions.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragData.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragOperation.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPoint.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "webkit/glue/webdropdata.h"
#include "webkit/glue/window_open_disposition.h"
using WebKit::WebDragOperationsMask;
using WebKit::WebPoint;
using WebKit::WebView;
@implementation WebDropTarget
// |view| is the WebView representing this browser window, used to communicate
// drag&drop messages to WebCore and handle navigation on a successful drop
// (if necessary).
- (id)initWithWebView:(BrowserWebView*)view {
if ((self = [super init]))
view_ = view;
return self;
}
// Call to set whether or not we should allow the drop. Takes effect the
// next time |-draggingUpdated:| is called.
- (void)setCurrentOperation: (NSDragOperation)operation {
current_operation_ = operation;
}
// Given a point in window coordinates and a view in that window, return a
// flipped point in the coordinate system of |view|.
- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint
view:(NSView*)view {
DCHECK(view);
NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil];
NSRect viewFrame = [view frame];
viewPoint.y = viewFrame.size.height - viewPoint.y;
return viewPoint;
}
// Given a point in window coordinates and a view in that window, return a
// flipped point in screen coordinates.
- (NSPoint)flipWindowPointToScreen:(const NSPoint&)windowPoint
view:(NSView*)view {
DCHECK(view);
NSPoint screenPoint = [[view window] convertBaseToScreen:windowPoint];
NSScreen* screen = [[view window] screen];
NSRect screenFrame = [screen frame];
screenPoint.y = screenFrame.size.height - screenPoint.y;
return screenPoint;
}
// Return YES if the drop site only allows drops that would navigate. If this
// is the case, we don't want to pass messages to the renderer because there's
// really no point (i.e., there's nothing that cares about the mouse position or
// entering and exiting). One example is an interstitial page (e.g., safe
// browsing warning).
- (BOOL)onlyAllowsNavigation {
return false;
}
// Messages to send during the tracking of a drag, usually upon receiving
// calls from the view system. Communicates the drag messages to WebCore.
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info
view:(NSView*)view {
// Save off the current WebViewHost so we can tell if it changes during a
// drag. If it does, we need to send a new enter message in draggingUpdated:.
current_wvh_ = _Context->current_webviewhost();
DCHECK(current_wvh_);
if ([self onlyAllowsNavigation]) {
if ([[info draggingPasteboard] containsURLData])
return NSDragOperationCopy;
return NSDragOperationNone;
}
WebView* webview = view_.browser->UIT_GetWebView();
// Fill out a WebDropData from pasteboard.
WebDropData data;
[self populateWebDropData:&data fromPasteboard:[info draggingPasteboard]];
// Create the appropriate mouse locations for WebCore. The draggingLocation
// is in window coordinates. Both need to be flipped.
NSPoint windowPoint = [info draggingLocation];
NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view];
NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view];
NSDragOperation mask = [info draggingSourceOperationMask];
webview->dragTargetDragEnter(data.ToDragData(),
WebPoint(viewPoint.x, viewPoint.y),
WebPoint(screenPoint.x, screenPoint.y),
static_cast<WebDragOperationsMask>(mask));
// We won't know the true operation (whether the drag is allowed) until we
// hear back from the renderer. For now, be optimistic:
current_operation_ = NSDragOperationCopy;
return current_operation_;
}
- (void)draggingExited:(id<NSDraggingInfo>)info {
DCHECK(current_wvh_);
if (current_wvh_ != _Context->current_webviewhost())
return;
WebView* webview = view_.browser->UIT_GetWebView();
// Nothing to do in the interstitial case.
webview->dragTargetDragLeave();
}
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info
view:(NSView*)view {
DCHECK(current_wvh_);
if (current_wvh_ != _Context->current_webviewhost())
[self draggingEntered:info view:view];
if ([self onlyAllowsNavigation]) {
if ([[info draggingPasteboard] containsURLData])
return NSDragOperationCopy;
return NSDragOperationNone;
}
WebView* webview = view_.browser->UIT_GetWebView();
// Create the appropriate mouse locations for WebCore. The draggingLocation
// is in window coordinates.
NSPoint windowPoint = [info draggingLocation];
NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view];
NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view];
NSDragOperation mask = [info draggingSourceOperationMask];
webview->dragTargetDragOver(WebPoint(viewPoint.x, viewPoint.y),
WebPoint(screenPoint.x, screenPoint.y),
static_cast<WebDragOperationsMask>(mask));
return current_operation_;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)info
view:(NSView*)view {
DCHECK(current_wvh_);
if (current_wvh_ != _Context->current_webviewhost())
[self draggingEntered:info view:view];
// Check if we only allow navigation and navigate to a url on the pasteboard.
if ([self onlyAllowsNavigation]) {
NSPasteboard* pboard = [info draggingPasteboard];
if ([pboard containsURLData]) {
GURL url;
drag_util::PopulateURLAndTitleFromPasteBoard(&url, NULL, pboard, YES);
view_.browser->GetMainFrame()->LoadURL(url.spec());
return YES;
}
return NO;
}
current_wvh_ = NULL;
WebView* webview = view_.browser->UIT_GetWebView();
// Create the appropriate mouse locations for WebCore. The draggingLocation
// is in window coordinates. Both need to be flipped.
NSPoint windowPoint = [info draggingLocation];
NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view];
NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view];
webview->dragTargetDrop(gfx::Point(viewPoint.x, viewPoint.y),
gfx::Point(screenPoint.x, screenPoint.y));
return YES;
}
// Given |data|, which should not be nil, fill it in using the contents of the
// given pasteboard.
- (void)populateWebDropData:(WebDropData*)data
fromPasteboard:(NSPasteboard*)pboard {
DCHECK(data);
DCHECK(pboard);
NSArray* types = [pboard types];
// Get URL if possible. To avoid exposing file system paths to web content,
// filenames in the drag are not converted to file URLs.
drag_util::PopulateURLAndTitleFromPasteBoard(&data->url,
&data->url_title,
pboard,
NO);
// Get plain text.
if ([types containsObject:NSStringPboardType]) {
data->plain_text =
base::SysNSStringToUTF16([pboard stringForType:NSStringPboardType]);
}
// Get HTML. If there's no HTML, try RTF.
if ([types containsObject:NSHTMLPboardType]) {
data->text_html =
base::SysNSStringToUTF16([pboard stringForType:NSHTMLPboardType]);
} else if ([types containsObject:NSRTFPboardType]) {
NSString* html = [pboard htmlFromRtf];
data->text_html = base::SysNSStringToUTF16(html);
}
// Get files.
if ([types containsObject:NSFilenamesPboardType]) {
NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
if ([files isKindOfClass:[NSArray class]] && [files count]) {
for (NSUInteger i = 0; i < [files count]; i++) {
NSString* filename = [files objectAtIndex:i];
BOOL isDir = NO;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:filename
isDirectory:&isDir];
if (exists && !isDir)
data->filenames.push_back(base::SysNSStringToUTF16(filename));
}
}
}
// TODO(pinkerton): Get file contents. http://crbug.com/34661
}
@end

View File

@ -50,6 +50,8 @@ class WebViewHost : public WebWidgetHost {
}
#elif defined(OS_MACOSX)
void SetIsActive(bool active);
virtual void MouseEvent(NSEvent *);
virtual void SetFocus(bool enable);
#endif
protected:

View File

@ -7,6 +7,7 @@
#include "webview_host.h"
#include "browser_webview_delegate.h"
#include "browser_webview_mac.h"
#include "cef_context.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSize.h"
@ -56,3 +57,17 @@ WebView* WebViewHost::webview() const {
void WebViewHost::SetIsActive(bool active) {
webview()->setIsActive(active);
}
void WebViewHost::MouseEvent(NSEvent* event) {
_Context->set_current_webviewhost(this);
WebWidgetHost::MouseEvent(event);
}
void WebViewHost::SetFocus(bool enable) {
if (enable) {
// Set the current WebViewHost in case a drag action is started before mouse
// events are detected for the window.
_Context->set_current_webviewhost(this);
}
WebWidgetHost::SetFocus(enable);
}

View File

@ -151,10 +151,10 @@ class WebWidgetHost {
// These need to be called from a non-subclass, so they need to be public.
public:
void Resize(const gfx::Rect& rect);
void MouseEvent(NSEvent *);
virtual void MouseEvent(NSEvent *);
void WheelEvent(NSEvent *);
void KeyEvent(NSEvent *);
void SetFocus(bool enable);
virtual void SetFocus(bool enable);
protected:
#elif defined(TOOLKIT_USES_GTK)
public:

View File

@ -4,6 +4,7 @@
// found in the LICENSE file.
#include "include/cef.h"
#import "include/cef_application_mac.h"
#include "cefclient.h"
#include "binding_test.h"
#include "client_handler.h"
@ -29,6 +30,31 @@ char szWorkingDir[512]; // The current working directory
const int kWindowWidth = 800;
const int kWindowHeight = 600;
// Memory AutoRelease pool.
static NSAutoreleasePool* g_autopool = nil;
// Provide the CefAppProtocol implementation required by CEF.
@interface ClientApplication : NSApplication<CefAppProtocol> {
@private
BOOL handlingSendEvent_;
}
@end
@implementation ClientApplication
- (BOOL)isHandlingSendEvent {
return handlingSendEvent_;
}
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
handlingSendEvent_ = handlingSendEvent;
}
- (void)sendEvent:(NSEvent*)event {
CefScopedSendingEvent sendingEventScoper;
[super sendEvent:event];
}
@end
// Receives notifications from controls and the browser window. Will delete
// itself when done.
@ -174,6 +200,7 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
- (IBAction)testAcceleratedLayers:(id)sender;
- (IBAction)testWebGL:(id)sender;
- (IBAction)testHTML5Video:(id)sender;
- (IBAction)testDragDrop:(id)sender;
- (IBAction)testZoomIn:(id)sender;
- (IBAction)testZoomOut:(id)sender;
- (IBAction)testZoomReset:(id)sender;
@ -243,6 +270,9 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
[testMenu addItemWithTitle:@"HTML5 Video"
action:@selector(testHTML5Video:)
keyEquivalent:@""];
[testMenu addItemWithTitle:@"Drag & Drop"
action:@selector(testDragDrop:)
keyEquivalent:@""];
[testMenu addItemWithTitle:@"Zoom In"
action:@selector(testZoomIn:)
keyEquivalent:@""];
@ -419,6 +449,11 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
RunHTML5VideoTest(g_handler->GetBrowser());
}
- (IBAction)testDragDrop:(id)sender {
if(g_handler.get() && g_handler->GetBrowserHwnd())
RunDragDropTest(g_handler->GetBrowser());
}
- (IBAction)testZoomIn:(id)sender {
if(g_handler.get() && g_handler->GetBrowserHwnd()) {
CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
@ -448,6 +483,9 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
CefShutdown();
[self release];
// Release the AutoRelease pool.
[g_autopool release];
}
@end
@ -458,7 +496,13 @@ int main(int argc, char* argv[])
// Retrieve the current working directory.
getcwd(szWorkingDir, sizeof(szWorkingDir));
// Initialize CEF. This will also create the NSApplication instance.
// Initialize the AutoRelease pool.
g_autopool = [[NSAutoreleasePool alloc] init];
// Initialize the ClientApplication instance.
[ClientApplication sharedApplication];
// Initialize CEF.
CefSettings settings;
CefInitialize(settings);
@ -472,7 +516,7 @@ int main(int argc, char* argv[])
waitUntilDone:NO];
// Run the application message loop.
[NSApp run];
CefRunMessageLoop();
// Don't put anything below this line because it won't be executed.
return 0;