diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-31 20:53:47 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-31 20:53:47 +0000 |
commit | 7418edfa1ad3eada8a12a225579fae3f45f382d5 (patch) | |
tree | 5a0387088ef18bc9a261853964fdf4a801976c08 | |
parent | bca8899ca1e49c182dcfcf17b6b94202e67f5e4b (diff) | |
download | chromium_src-7418edfa1ad3eada8a12a225579fae3f45f382d5.zip chromium_src-7418edfa1ad3eada8a12a225579fae3f45f382d5.tar.gz chromium_src-7418edfa1ad3eada8a12a225579fae3f45f382d5.tar.bz2 |
Enable dragging of images to desktop (Finder), Preview, etc. (on Mac).
This hooks up drag-and-drop of file promises, lazy writing to the drag
pasteboard, drag-sourcing of TIFF images (via Cocoa) and file contents.
Patch Set 5 improvements: Adds asynchronous writing of promised files.
Patch Set 4 improvements: Big refactoring -- drag source stuff is now
handled by the WebDragSource (Cocoa) object, with messages proxied
through the TabContentsViewCocoa object. The WebDragSource object
carries a weak reference to the TCVC, owns the WebDropData, and keeps
track of the drag pasteboard types/promises.
Patch Set 3 improvements over Patch Set 2: It shouldn't crash anymore. Made
drop_data_ reference counted, in anticipation of asynchronous file
writing.
TODO #1: Testing. Still need a unit test, maybe. Should make sure that
dragging by file contents actually works.
TODO #2 (in some other patch): Refactor some of the WebDropData
extraction code out, e.g., file name extraction should be made common
with other platforms.
TODO #3 (in some other patch): We really should make WebDropData cheaper
to copy around and retain. I'm not convinced it's a good idea to push
out the entire thing over IPC, especially since the data pushed could be
very big and may not even be used.
BUG=15640
TEST=drag images to various applications
Patch by viettrungluu@gmail.com
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22187 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/cocoa/web_drag_source.h | 57 | ||||
-rw-r--r-- | chrome/browser/cocoa/web_drag_source.mm | 346 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents_view_mac.h | 13 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents_view_mac.mm | 177 | ||||
-rw-r--r-- | chrome/chrome.gyp | 6 |
5 files changed, 458 insertions, 141 deletions
diff --git a/chrome/browser/cocoa/web_drag_source.h b/chrome/browser/cocoa/web_drag_source.h new file mode 100644 index 0000000..6532878 --- /dev/null +++ b/chrome/browser/cocoa/web_drag_source.h @@ -0,0 +1,57 @@ +// Copyright (c) 2009 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/scoped_nsobject.h" +#include "base/scoped_ptr.h" + +struct WebDropData; +@class TabContentsViewCocoa; + +// A class that handles tracking and event processing for a drag and drop +// originating from the content area. +@interface WebDragSource : NSObject { + @private + // Our tab. Weak reference (owns or co-owns us). + TabContentsViewCocoa* contentsView_; + + // Our drop data. Should only be initialized once. + scoped_ptr<WebDropData> dropData_; + + // Our pasteboard. + scoped_nsobject<NSPasteboard> pasteboard_; +} + +// Initialize a WebDragSource object for a drag (originating on the given +// contentsView and with the given dropData and pboard). Fill the pasteboard +// with data types appropriate for dropData. +- (id)initWithContentsView:(TabContentsViewCocoa*)contentsView + dropData:(const WebDropData*)dropData + pasteboard:(NSPasteboard*)pboard; + +// Call when asked to do a lazy write to the pasteboard; hook up to +// -pasteboard:provideDataForType: (on the contentsView). +- (void)lazyWriteToPasteboard:(NSPasteboard*)pboard + forType:(NSString*)type; + +// Start the drag (on the originally provided contentsView); 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 + isCancelled:(BOOL)cancelled; + +// 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 diff --git a/chrome/browser/cocoa/web_drag_source.mm b/chrome/browser/cocoa/web_drag_source.mm new file mode 100644 index 0000000..6fd44a7 --- /dev/null +++ b/chrome/browser/cocoa/web_drag_source.mm @@ -0,0 +1,346 @@ +// Copyright (c) 2009 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 "chrome/browser/cocoa/web_drag_source.h" + +#include "base/file_util.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/task.h" +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/cocoa/nsimage_cache.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view_mac.h" +#include "net/base/file_stream.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#import "third_party/mozilla/include/NSPasteboard+Utils.h" +#include "webkit/glue/webdropdata.h" + +using base::SysNSStringToUTF8; +using base::SysUTF8ToNSString; +using base::SysUTF16ToNSString; +using net::FileStream; + + +namespace { + +// Make a drag image from the drop data. +// TODO(viettrungluu@gmail.com): Move this somewhere more sensible. +NSImage* MakeDragImage(const WebDropData* drop_data) { + // TODO(viettrungluu@gmail.com): Just a stub for now. Make it do something. + + // Default to returning a generic image. + return nsimage_cache::ImageNamed(@"nav.pdf"); +} + +// Returns a filename appropriate for the drop data (of form "FILENAME-seq.EXT" +// if seq > 0). +// TODO(viettrungluu@gmail.com): Refactor to make it common across platforms, +// and move it somewhere sensible. +FilePath GetFileNameFromDragData( + const WebDropData& drop_data, unsigned seq) { + // 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([SysUTF16ToNSString(drop_data.file_description_filename) + fileSystemRepresentation]); + file_name = file_name.BaseName().RemoveExtension(); + + if (file_name.empty()) { + // Retrieve the name from the URL. + file_name = FilePath::FromWStringHack( + net::GetSuggestedFilename(drop_data.url, "", "", L"")); + } + + file_name = file_name.ReplaceExtension([SysUTF16ToNSString( + drop_data.file_extension) fileSystemRepresentation]); + + if (seq > 0) { + file_name = + file_name.InsertBeforeExtension(std::string("-")+UintToString(seq)); + } + + 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)initWithContentsView:(TabContentsViewCocoa*)contentsView + dropData:(const WebDropData*)dropData + pasteboard:(NSPasteboard*)pboard { + if ((self = [super init])) { + contentsView_ = contentsView; + DCHECK(contentsView_); + + dropData_.reset(new WebDropData(*dropData)); + DCHECK(dropData_.get()); + + pasteboard_.reset([pboard retain]); + DCHECK(pasteboard_.get()); + + [self fillPasteboard]; + } + + return self; +} + +- (void)lazyWriteToPasteboard:(NSPasteboard*)pboard forType:(NSString*)type { + // 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()); + [pboard setString:SysUTF16ToNSString(dropData_->text_html) + forType:NSHTMLPboardType]; + + // URL. + } else if ([type isEqualToString:NSURLPboardType]) { + DCHECK(dropData_->url.is_valid()); + [pboard setURLs:[NSArray + arrayWithObject:SysUTF8ToNSString(dropData_->url.spec())] + withTitles:[NSArray arrayWithObject: + SysUTF16ToNSString(dropData_->url_title)]]; + + // File contents. + } else if ([type isEqualToString:NSFileContentsPboardType] || + [type isEqualToString:NSCreateFileContentsPboardType( + SysUTF16ToNSString(dropData_->file_extension))]) { + // TODO(viettrungluu@gmail.com): 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_, 0).value())]; + [pboard writeFileWrapper:file_wrapper]; + + // TIFF. + } else if ([type isEqualToString:NSTIFFPboardType]) { + // FIXME(viettrungluu@gmail.com): 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."; + } +} + +- (void)startDrag { + NSEvent* currentEvent = [NSApp currentEvent]; + [contentsView_ dragImage:[self dragImage] + at:[contentsView_ + convertPoint:[currentEvent locationInWindow] + fromView:nil] + offset:NSZeroSize + event:currentEvent + pasteboard:pasteboard_ + source:contentsView_ + slideBack:YES]; +} + +- (void)endDragAt:(NSPoint)screenPoint + isCancelled:(BOOL)cancelled { + RenderViewHost* rvh = [contentsView_ tabContents]->render_view_host(); + if (rvh) { + rvh->DragSourceSystemDragEnded(); + + // Convert |screenPoint| to view coordinates and flip it. + NSPoint localPoint = [contentsView_ convertPointFromBase:screenPoint]; + NSRect viewFrame = [contentsView_ frame]; + localPoint.y = viewFrame.size.height - localPoint.y; + // Flip |screenPoint|. + NSRect screenFrame = [[[contentsView_ window] screen] frame]; + screenPoint.y = screenFrame.size.height - screenPoint.y; + + if (cancelled) { + rvh->DragSourceCancelledAt(localPoint.x, localPoint.y, + screenPoint.x, screenPoint.y); + } else { + rvh->DragSourceEndedAt(localPoint.x, localPoint.y, + screenPoint.x, screenPoint.y); + } + } + + // Make sure the pasteboard owner isn't us. + [pasteboard_ declareTypes:[NSArray array] owner:nil]; +} + +- (void)moveDragTo:(NSPoint)screenPoint { + RenderViewHost* rvh = [contentsView_ tabContents]->render_view_host(); + if (rvh) { + // Convert |screenPoint| to view coordinates and flip it. + NSPoint localPoint = [contentsView_ convertPointFromBase:screenPoint]; + NSRect viewFrame = [contentsView_ frame]; + localPoint.y = viewFrame.size.height - localPoint.y; + // Flip |screenPoint|. + NSRect screenFrame = [[[contentsView_ window] screen] frame]; + screenPoint.y = screenFrame.size.height - screenPoint.y; + + rvh->DragSourceMovedTo(localPoint.x, localPoint.y, + screenPoint.x, screenPoint.y); + } +} + +- (NSString*)dragPromisedFileTo:(NSString*)path { + // Be extra paranoid; avoid crashing. + if (!dropData_.get()) { + NOTREACHED() << "No drag-and-drop data available for promised file."; + return nil; + } + + FileStream* file_stream = new FileStream; + DCHECK(file_stream); + if (!file_stream) + return nil; + + FilePath path_name(SysNSStringToUTF8(path)); + FilePath file_name; + unsigned seq; + const unsigned k_max_seq = 99; + for (seq = 0; seq <= k_max_seq; seq++) { + file_name = GetFileNameFromDragData(*dropData_, seq); + FilePath file_path = path_name.Append(file_name); + + // Explicitly (and redundantly check) for file -- despite the fact that our + // open won't overwrite -- just to avoid log spew. + if (!file_util::PathExists(file_path) && + file_stream->Open(path_name.Append(file_name), + base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE) == net::OK) + break; + } + if (seq > k_max_seq) { + delete file_stream; + return nil; + } + + // The writer will take care of closing and deletion. + g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, + new PromiseWriterTask(*dropData_, file_stream)); + + // Once we've created the file, we should return the file name. + return SysUTF8ToNSString(file_name.value()); +} + +@end // @implementation WebDragSource + + +@implementation WebDragSource (Private) + +- (void)fillPasteboard { + DCHECK(pasteboard_.get()); + + [pasteboard_ declareTypes:[NSArray array] owner:contentsView_]; + + // HTML. + if (!dropData_->text_html.empty()) + [pasteboard_ addTypes:[NSArray arrayWithObject:NSHTMLPboardType] + owner:contentsView_]; + + // URL. + if (dropData_->url.is_valid()) + [pasteboard_ addTypes:[NSArray arrayWithObject:NSURLPboardType] + owner:contentsView_]; + + // File. + if (!dropData_->file_contents.empty()) { + NSString* file_ext = SysUTF16ToNSString(dropData_->file_extension); + + // File contents (with and without specific type), file (HFS) promise, TIFF. + // TODO(viettrungluu@gmail.com): others? + [pasteboard_ addTypes:[NSArray arrayWithObjects: + NSFileContentsPboardType, + NSCreateFileContentsPboardType(file_ext), + NSFilesPromisePboardType, + NSTIFFPboardType, + nil] + owner:contentsView_]; + + // For the file promise, we need to specify the extension. + [pasteboard_ setPropertyList:[NSArray arrayWithObject:file_ext] + forType:NSFilesPromisePboardType]; + } + + // Plain text. + if (!dropData_->plain_text.empty()) + [pasteboard_ addTypes:[NSArray arrayWithObject:NSStringPboardType] + owner:contentsView_]; +} + +- (NSImage*)dragImage { + return MakeDragImage(dropData_.get()); +} + +@end // @implementation WebDragSource (Private) diff --git a/chrome/browser/tab_contents/tab_contents_view_mac.h b/chrome/browser/tab_contents/tab_contents_view_mac.h index 29471d5..a8ccaf6 100644 --- a/chrome/browser/tab_contents/tab_contents_view_mac.h +++ b/chrome/browser/tab_contents/tab_contents_view_mac.h @@ -7,6 +7,8 @@ #import <Cocoa/Cocoa.h> +#include <string> + #include "base/gfx/size.h" #include "base/scoped_ptr.h" #include "base/scoped_nsobject.h" @@ -14,16 +16,22 @@ #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/common/notification_registrar.h" +class FilePath; class FindBarMac; @class SadTabView; class TabContentsViewMac; +@class WebDragSource; @class WebDropTarget; @interface TabContentsViewCocoa : BaseView { @private TabContentsViewMac* tabContentsView_; // WEAK; owns us + scoped_nsobject<WebDragSource> dragSource_; scoped_nsobject<WebDropTarget> dropTarget_; } + +// Expose this, since sometimes one needs both the NSView and the TabContents. +- (TabContents*)tabContents; @end // Mac-specific implementation of the TabContentsView. It owns an NSView that @@ -35,7 +43,6 @@ class TabContentsViewMac : public TabContentsView, // lifetime. This doesn't need to be the case, but is this way currently // because that's what was easiest when they were split. explicit TabContentsViewMac(TabContents* web_contents); - virtual ~TabContentsViewMac(); // TabContentsView implementation -------------------------------------------- @@ -75,10 +82,6 @@ class TabContentsViewMac : public TabContentsView, const NotificationDetails& details); private: - // Returns a drag pasteboard filled with the appropriate data. The types are - // populated in decending order of richness. - NSPasteboard* FillDragData(const WebDropData& drop_data); - // The Cocoa NSView that lives in the view hierarchy. scoped_nsobject<TabContentsViewCocoa> cocoa_view_; diff --git a/chrome/browser/tab_contents/tab_contents_view_mac.mm b/chrome/browser/tab_contents/tab_contents_view_mac.mm index bfcb94b..f0ea3e2 100644 --- a/chrome/browser/tab_contents/tab_contents_view_mac.mm +++ b/chrome/browser/tab_contents/tab_contents_view_mac.mm @@ -4,10 +4,11 @@ #include "chrome/browser/tab_contents/tab_contents_view_mac.h" -#include "base/sys_string_conversions.h" +#include <string> + #include "chrome/browser/browser.h" // TODO(beng): this dependency is awful. -#include "chrome/browser/cocoa/nsimage_cache.h" #include "chrome/browser/cocoa/sad_tab_view.h" +#import "chrome/browser/cocoa/web_drag_source.h" #import "chrome/browser/cocoa/web_drop_target.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" @@ -16,7 +17,6 @@ #include "chrome/common/notification_type.h" #include "chrome/common/notification_service.h" #include "chrome/common/render_messages.h" -#include "net/base/net_util.h" #import "third_party/mozilla/include/NSPasteboard+Utils.h" #include "chrome/common/temp_scaffolding_stubs.h" @@ -24,9 +24,9 @@ @interface TabContentsViewCocoa (Private) - (id)initWithTabContentsViewMac:(TabContentsViewMac*)w; - (void)processKeyboardEvent:(NSEvent*)event; -- (TabContents*)tabContents; - (void)registerDragTypes; - (void)setIsDropTarget:(BOOL)isTarget; +- (void)startDragWithDropData:(const WebDropData&)dropData; @end // static @@ -40,9 +40,6 @@ TabContentsViewMac::TabContentsViewMac(TabContents* tab_contents) Source<TabContents>(tab_contents)); } -TabContentsViewMac::~TabContentsViewMac() { -} - void TabContentsViewMac::CreateView() { TabContentsViewCocoa* view = [[TabContentsViewCocoa alloc] initWithTabContentsViewMac:this]; @@ -86,84 +83,11 @@ void TabContentsViewMac::GetContainerBounds(gfx::Rect* out) const { *out = [cocoa_view_.get() NSRectToRect:[cocoa_view_.get() bounds]]; } -// Returns a drag pasteboard filled with the appropriate data. The types are -// populated in decending order of richness. -NSPasteboard* TabContentsViewMac::FillDragData( - const WebDropData& drop_data) { - NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - [pasteboard declareTypes:[NSArray array] owner:nil]; - - // HTML. - if (!drop_data.text_html.empty()) { - [pasteboard addTypes:[NSArray arrayWithObject:NSHTMLPboardType] - owner:nil]; - [pasteboard setString:base::SysUTF16ToNSString(drop_data.text_html) - forType:NSHTMLPboardType]; - } - - // URL. - if (drop_data.url.is_valid()) { - // TODO(pinkerton/jrg): special javascript: handling for bookmark bar. Win - // doesn't allow you to drop js: bookmarks on the desktop (since they're - // meaningless) but does allow you to drop them on the bookmark bar (where - // they're intended to go generally). We need to figure out a private - // flavor for Bookmark dragging and then flag this down in the drag source. - [pasteboard addTypes:[NSArray arrayWithObject:NSURLPboardType] - owner:nil]; - NSString* url = base::SysUTF8ToNSString(drop_data.url.spec()); - NSString* title = base::SysUTF16ToNSString(drop_data.url_title); - [pasteboard setURLs:[NSArray arrayWithObject:url] - withTitles:[NSArray arrayWithObject:title]]; - } - - // Files. - // TODO(pinkerton): Hook up image drags, data is in drop_data.file_contents. - if (!drop_data.file_contents.empty()) { -#if 0 - // Images without ALT text will only have a file extension so we need to - // synthesize one from the provided extension and URL. - std::string filename_utf8 = - [base::SysUTF16ToNSString(drop_data.file_description_filename) - fileSystemRepresentation]; - FilePath file_name(filename_utf8); - file_name = file_name.BaseName().RemoveExtension(); - if (file_name.value().empty()) { - // Retrieve the name from the URL. - file_name = FilePath::FromWStringHack( - net::GetSuggestedFilename(drop_data.url, "", "", L"")); - } - std::string file_extension_utf8 = - [base::SysUTF16ToNSString(drop_data.file_extension) - fileSystemRepresentation]; - file_name = file_name.ReplaceExtension(file_extension_utf8); - NSArray* types = [NSArray arrayWithObjects:NSFileContentsPboardType, - NSFilenamesPboardType, - nil]; - [pasteboard addTypes:types owner:nil]; - NSArray* file_name_array = - [NSArray arrayWithObject:base::SysUTF8ToNSString(file_name.value())]; - [pasteboard setPropertyList:file_name_array forType:NSFilenamesPboardType]; - NSData* data = [NSData dataWithBytes:drop_data.file_contents.data() - length:drop_data.file_contents.length()]; - [pasteboard setData:data forType:NSFileContentsPboardType]; -#endif - } - - // Plain text. - if (!drop_data.plain_text.empty()) { - [pasteboard addTypes:[NSArray arrayWithObject:NSStringPboardType] - owner:nil]; - [pasteboard setString:base::SysUTF16ToNSString(drop_data.plain_text) - forType:NSStringPboardType]; - } - return pasteboard; -} - void TabContentsViewMac::StartDragging(const WebDropData& drop_data) { // We are only allowed to call dragImage:... from inside mouseDragged:, which // we will never be (we're called back async), but it seems that the mouse // event is still always the proper left mouse drag, so everything works out - // in the end. However, we occasionally get spurrious "start drag" messages + // in the end. However, we occasionally get spurious "start drag" messages // from the back-end when we shouldn't. If we go through with the drag, Cocoa // asserts in a bad way. Just bail for now until we can figure out the root of // why we're getting the messages. @@ -177,26 +101,10 @@ void TabContentsViewMac::StartDragging(const WebDropData& drop_data) { return; } - // Create an image to use for the drag. - // TODO(pinkerton): Generate the proper image. This one will do in a pinch. - NSImage* dragImage = nsimage_cache::ImageNamed(@"nav.pdf"); - - NSPasteboard* pasteboard = FillDragData(drop_data); - - // Tell the view to start a drag using |cocoa_view_| as the drag source. The - // source will get notified when the drag completes (success or failure) so - // it can tell the render view host the drag is done. The drag invokes a - // nested event loop, but we need to continue processing events. - NSPoint mousePoint = [currentEvent locationInWindow]; - mousePoint = [cocoa_view_ convertPoint:mousePoint fromView:nil]; + // The drag invokes a nested event loop, but we need to continue processing + // events. MessageLoop::current()->SetNestableTasksAllowed(true); - [cocoa_view_ dragImage:dragImage - at:mousePoint - offset:NSZeroSize - event:currentEvent - pasteboard:pasteboard - source:cocoa_view_ - slideBack:YES]; + [cocoa_view_ startDragWithDropData:drop_data]; MessageLoop::current()->SetNestableTasksAllowed(false); } @@ -425,6 +333,19 @@ void TabContentsViewMac::Observe(NotificationType type, [self tabContents]->Paste(); } +- (void)pasteboard:(NSPasteboard*)sender provideDataForType:(NSString*)type { + [dragSource_ lazyWriteToPasteboard:sender + forType:type]; +} + +- (void)startDragWithDropData:(const WebDropData&)dropData { + dragSource_.reset([[WebDragSource alloc] + initWithContentsView:self + dropData:&dropData + pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]]); + [dragSource_ startDrag]; +} + // NSDraggingSource methods // Returns what kind of drag operations are available. This is a required @@ -434,48 +355,32 @@ void TabContentsViewMac::Observe(NotificationType type, return NSDragOperationCopy; } -// Called when a drag initiated in our view ends. We need to make sure that -// we tell WebCore so that it can go about processing things as normal. +// Called when a drag initiated in our view ends. - (void)draggedImage:(NSImage*)anImage endedAt:(NSPoint)screenPoint operation:(NSDragOperation)operation { - RenderViewHost* rvh = [self tabContents]->render_view_host(); - if (rvh) { - rvh->DragSourceSystemDragEnded(); - - // Convert |screenPoint| to view coordinates and flip it. - NSPoint localPoint = [self convertPointFromBase:screenPoint]; - NSRect viewFrame = [self frame]; - localPoint.y = viewFrame.size.height - localPoint.y; - // Flip |screenPoint|. - NSRect screenFrame = [[[self window] screen] frame]; - screenPoint.y = screenFrame.size.height - screenPoint.y; - - if (operation != NSDragOperationNone) - rvh->DragSourceEndedAt(localPoint.x, localPoint.y, - screenPoint.x, screenPoint.y); - else - rvh->DragSourceCancelledAt(localPoint.x, localPoint.y, - screenPoint.x, screenPoint.y); - } + [dragSource_ endDragAt:screenPoint + isCancelled:(operation == NSDragOperationNone)]; + + // Might as well throw out this object now. + dragSource_.reset(); } -// Called when a drag initiated in our view moves. We need to tell WebCore -// so it can update anything watching for drag events. +// Called when a drag initiated in our view moves. - (void)draggedImage:(NSImage*)draggedImage movedTo:(NSPoint)screenPoint { - RenderViewHost* rvh = [self tabContents]->render_view_host(); - if (rvh) { - // Convert |screenPoint| to view coordinates and flip it. - NSPoint localPoint = [self convertPointFromBase:screenPoint]; - NSRect viewFrame = [self frame]; - localPoint.y = viewFrame.size.height - localPoint.y; - // Flip |screenPoint|. - NSRect screenFrame = [[[self window] screen] frame]; - screenPoint.y = screenFrame.size.height - screenPoint.y; - - rvh->DragSourceMovedTo(localPoint.x, localPoint.y, - screenPoint.x, screenPoint.y); - } + [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 diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index f623c1e..1050298 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -763,11 +763,15 @@ 'browser/cocoa/browser_window_controller.mm', 'browser/cocoa/clear_browsing_data_controller.h', 'browser/cocoa/clear_browsing_data_controller.mm', + 'browser/cocoa/clickhold_button_cell.h', + 'browser/cocoa/clickhold_button_cell.mm', 'browser/cocoa/cocoa_test_helper.h', 'browser/cocoa/command_observer_bridge.h', 'browser/cocoa/command_observer_bridge.mm', 'browser/cocoa/custom_home_pages_model.h', 'browser/cocoa/custom_home_pages_model.mm', + 'browser/cocoa/delayedlist_button.h', + 'browser/cocoa/delayedlist_button.mm', 'browser/cocoa/download_item_cell.h', 'browser/cocoa/download_item_cell.mm', 'browser/cocoa/download_item_controller.h', @@ -857,6 +861,8 @@ 'browser/cocoa/toolbar_view.mm', 'browser/cocoa/ui_localizer.h', 'browser/cocoa/ui_localizer.mm', + 'browser/cocoa/web_drag_source.h', + 'browser/cocoa/web_drag_source.mm', 'browser/cocoa/web_drop_target.h', 'browser/cocoa/web_drop_target.mm', 'browser/command_updater.cc', |