diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-01 16:34:49 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-01 16:34:49 +0000 |
commit | 7d791652c7ede4209a2014d885148e2713f49bce (patch) | |
tree | c26baf12593bed381c631b81c736106809d46b44 /chrome/browser/ui/cocoa/web_drag_source.mm | |
parent | 3b94427c99bdf12836fd455eeb1499fdde511e26 (diff) | |
download | chromium_src-7d791652c7ede4209a2014d885148e2713f49bce.zip chromium_src-7d791652c7ede4209a2014d885148e2713f49bce.tar.gz chromium_src-7d791652c7ede4209a2014d885148e2713f49bce.tar.bz2 |
Move browser/cocoa to browser/ui/cocoa
BUG=none
TEST=none
TBR=brettw
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67854 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/cocoa/web_drag_source.mm')
-rw-r--r-- | chrome/browser/ui/cocoa/web_drag_source.mm | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/chrome/browser/ui/cocoa/web_drag_source.mm b/chrome/browser/ui/cocoa/web_drag_source.mm new file mode 100644 index 0000000..cad5014 --- /dev/null +++ b/chrome/browser/ui/cocoa/web_drag_source.mm @@ -0,0 +1,412 @@ +// 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/ui/cocoa/web_drag_source.h" + +#include "base/file_path.h" +#include "base/nsimage_cache_mac.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/task.h" +#include "base/thread.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/download/download_manager.h" +#include "chrome/browser/download/download_util.h" +#include "chrome/browser/download/drag_download_file.h" +#include "chrome/browser/download/drag_download_util.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_util.h" +#import "third_party/mozilla/NSPasteboard+Utils.h" +#include "webkit/glue/webdropdata.h" + +using base::SysNSStringToUTF8; +using base::SysUTF8ToNSString; +using base::SysUTF16ToNSString; +using net::FileStream; + + +namespace { + +// An unofficial standard pasteboard title type to be provided alongside the +// |NSURLPboardType|. +NSString* const kNSURLTitlePboardType = @"public.url-name"; + +// 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([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 = net::GetSuggestedFilename(drop_data.url, "", "", FilePath()); + } + + file_name = file_name.ReplaceExtension([SysUTF16ToNSString( + drop_data.file_extension) fileSystemRepresentation]); + + 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 + image:(NSImage*)image + offset:(NSPoint)offset + pasteboard:(NSPasteboard*)pboard + dragOperationMask:(NSDragOperation)dragOperationMask { + if ((self = [super init])) { + contentsView_ = contentsView; + DCHECK(contentsView_); + + 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()); + NSURL* url = [NSURL URLWithString:SysUTF8ToNSString(dropData_->url.spec())]; + [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 { + DCHECK([contentsView_ window]); + NSPoint basePoint = [[contentsView_ window] convertScreenToBase:screenPoint]; + return [contentsView_ 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 = [contentsView_ 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/WebKit/mac/Misc/WebNSViewExtras.m. + [window dragImage:[self dragImage] + at:position + offset:NSZeroSize + event:dragEvent + pasteboard:pasteboard_ + source:contentsView_ + slideBack:YES]; +} + +- (void)endDragAt:(NSPoint)screenPoint + operation:(NSDragOperation)operation { + RenderViewHost* rvh = [contentsView_ tabContents]->render_view_host(); + if (rvh) { + rvh->DragSourceSystemDragEnded(); + + // Convert |screenPoint| to view coordinates and flip it. + NSPoint localPoint = [self convertScreenPoint: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->DragSourceEndedAt(localPoint.x, localPoint.y, + screenPoint.x, screenPoint.y, + static_cast<WebKit::WebDragOperation>(operation)); + } + + // 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 = [self convertScreenPoint: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; + } + + FilePath fileName = downloadFileName_.empty() ? + GetFileNameFromDragData(*dropData_) : downloadFileName_; + FilePath filePath(SysNSStringToUTF8(path)); + filePath = filePath.Append(fileName); + FileStream* fileStream = + drag_download_util::CreateFileStreamForDrop(&filePath); + if (!fileStream) + return nil; + + if (downloadURL_.is_valid()) { + TabContents* tabContents = [contentsView_ tabContents]; + scoped_refptr<DragDownloadFile> dragFileDownloader(new DragDownloadFile( + filePath, + linked_ptr<net::FileStream>(fileStream), + downloadURL_, + tabContents->GetURL(), + tabContents->encoding(), + tabContents)); + + // 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. + g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, + new PromiseWriterTask(*dropData_, fileStream)); + } + + // Once we've created the file, we should return the file name. + return SysUTF8ToNSString(filePath.BaseName().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 (and title). + if (dropData_->url.is_valid()) + [pasteboard_ addTypes:[NSArray arrayWithObjects:NSURLPboardType, + kNSURLTitlePboardType, nil] + owner:contentsView_]; + + // 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_)) { + std::string contentDisposition = + "attachment; filename=" + fileName.value(); + download_util::GenerateFileName(downloadURL_, + contentDisposition, + std::string(), + UTF16ToUTF8(mimeType), + &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:contentsView_]; + + // 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:contentsView_]; +} + +- (NSImage*)dragImage { + if (dragImage_) + return dragImage_; + + // Default to returning a generic image. + return nsimage_cache::ImageNamed(@"nav.pdf"); +} + +@end // @implementation WebDragSource (Private) |