1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "chrome/browser/ui/cocoa/tab_contents/web_drop_target.h" 6 7#include "base/sys_string_conversions.h" 8#include "chrome/browser/bookmarks/bookmark_node_data.h" 9#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" 10#include "content/browser/renderer_host/render_view_host.h" 11#include "content/browser/tab_contents/tab_contents.h" 12#import "third_party/mozilla/NSPasteboard+Utils.h" 13#include "webkit/glue/webdropdata.h" 14#include "webkit/glue/window_open_disposition.h" 15 16using WebKit::WebDragOperationsMask; 17 18@implementation WebDropTarget 19 20// |contents| is the TabContents representing this tab, used to communicate 21// drag&drop messages to WebCore and handle navigation on a successful drop 22// (if necessary). 23- (id)initWithTabContents:(TabContents*)contents { 24 if ((self = [super init])) { 25 tabContents_ = contents; 26 } 27 return self; 28} 29 30// Call to set whether or not we should allow the drop. Takes effect the 31// next time |-draggingUpdated:| is called. 32- (void)setCurrentOperation: (NSDragOperation)operation { 33 current_operation_ = operation; 34} 35 36// Given a point in window coordinates and a view in that window, return a 37// flipped point in the coordinate system of |view|. 38- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint 39 view:(NSView*)view { 40 DCHECK(view); 41 NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil]; 42 NSRect viewFrame = [view frame]; 43 viewPoint.y = viewFrame.size.height - viewPoint.y; 44 return viewPoint; 45} 46 47// Given a point in window coordinates and a view in that window, return a 48// flipped point in screen coordinates. 49- (NSPoint)flipWindowPointToScreen:(const NSPoint&)windowPoint 50 view:(NSView*)view { 51 DCHECK(view); 52 NSPoint screenPoint = [[view window] convertBaseToScreen:windowPoint]; 53 NSScreen* screen = [[view window] screen]; 54 NSRect screenFrame = [screen frame]; 55 screenPoint.y = screenFrame.size.height - screenPoint.y; 56 return screenPoint; 57} 58 59// Return YES if the drop site only allows drops that would navigate. If this 60// is the case, we don't want to pass messages to the renderer because there's 61// really no point (i.e., there's nothing that cares about the mouse position or 62// entering and exiting). One example is an interstitial page (e.g., safe 63// browsing warning). 64- (BOOL)onlyAllowsNavigation { 65 return tabContents_->showing_interstitial_page(); 66} 67 68// Messages to send during the tracking of a drag, ususally upon recieving 69// calls from the view system. Communicates the drag messages to WebCore. 70 71- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info 72 view:(NSView*)view { 73 // Save off the RVH so we can tell if it changes during a drag. If it does, 74 // we need to send a new enter message in draggingUpdated:. 75 currentRVH_ = tabContents_->render_view_host(); 76 77 if ([self onlyAllowsNavigation]) { 78 if ([[info draggingPasteboard] containsURLData]) 79 return NSDragOperationCopy; 80 return NSDragOperationNone; 81 } 82 83 // If the tab is showing the boomark manager, send BookmarkDrag events 84 RenderViewHostDelegate::BookmarkDrag* dragDelegate = 85 tabContents_->GetBookmarkDragDelegate(); 86 BookmarkNodeData dragData; 87 if(dragDelegate && dragData.ReadFromDragClipboard()) 88 dragDelegate->OnDragEnter(dragData); 89 90 // Fill out a WebDropData from pasteboard. 91 WebDropData data; 92 [self populateWebDropData:&data fromPasteboard:[info draggingPasteboard]]; 93 94 // Create the appropriate mouse locations for WebCore. The draggingLocation 95 // is in window coordinates. Both need to be flipped. 96 NSPoint windowPoint = [info draggingLocation]; 97 NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view]; 98 NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view]; 99 NSDragOperation mask = [info draggingSourceOperationMask]; 100 tabContents_->render_view_host()->DragTargetDragEnter(data, 101 gfx::Point(viewPoint.x, viewPoint.y), 102 gfx::Point(screenPoint.x, screenPoint.y), 103 static_cast<WebDragOperationsMask>(mask)); 104 105 // We won't know the true operation (whether the drag is allowed) until we 106 // hear back from the renderer. For now, be optimistic: 107 current_operation_ = NSDragOperationCopy; 108 return current_operation_; 109} 110 111- (void)draggingExited:(id<NSDraggingInfo>)info { 112 DCHECK(currentRVH_); 113 if (currentRVH_ != tabContents_->render_view_host()) 114 return; 115 116 // Nothing to do in the interstitial case. 117 118 tabContents_->render_view_host()->DragTargetDragLeave(); 119} 120 121- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info 122 view:(NSView*)view { 123 DCHECK(currentRVH_); 124 if (currentRVH_ != tabContents_->render_view_host()) 125 [self draggingEntered:info view:view]; 126 127 if ([self onlyAllowsNavigation]) { 128 if ([[info draggingPasteboard] containsURLData]) 129 return NSDragOperationCopy; 130 return NSDragOperationNone; 131 } 132 133 // Create the appropriate mouse locations for WebCore. The draggingLocation 134 // is in window coordinates. 135 NSPoint windowPoint = [info draggingLocation]; 136 NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view]; 137 NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view]; 138 NSDragOperation mask = [info draggingSourceOperationMask]; 139 tabContents_->render_view_host()->DragTargetDragOver( 140 gfx::Point(viewPoint.x, viewPoint.y), 141 gfx::Point(screenPoint.x, screenPoint.y), 142 static_cast<WebDragOperationsMask>(mask)); 143 144 // If the tab is showing the boomark manager, send BookmarkDrag events 145 RenderViewHostDelegate::BookmarkDrag* dragDelegate = 146 tabContents_->GetBookmarkDragDelegate(); 147 BookmarkNodeData dragData; 148 if(dragDelegate && dragData.ReadFromDragClipboard()) 149 dragDelegate->OnDragOver(dragData); 150 return current_operation_; 151} 152 153- (BOOL)performDragOperation:(id<NSDraggingInfo>)info 154 view:(NSView*)view { 155 if (currentRVH_ != tabContents_->render_view_host()) 156 [self draggingEntered:info view:view]; 157 158 // Check if we only allow navigation and navigate to a url on the pasteboard. 159 if ([self onlyAllowsNavigation]) { 160 NSPasteboard* pboard = [info draggingPasteboard]; 161 if ([pboard containsURLData]) { 162 GURL url; 163 [self populateURL:&url 164 andTitle:NULL 165 fromPasteboard:pboard 166 convertingFilenames:YES]; 167 tabContents_->OpenURL(url, GURL(), CURRENT_TAB, 168 PageTransition::AUTO_BOOKMARK); 169 return YES; 170 } 171 return NO; 172 } 173 174 // If the tab is showing the boomark manager, send BookmarkDrag events 175 RenderViewHostDelegate::BookmarkDrag* dragDelegate = 176 tabContents_->GetBookmarkDragDelegate(); 177 BookmarkNodeData dragData; 178 if(dragDelegate && dragData.ReadFromDragClipboard()) 179 dragDelegate->OnDrop(dragData); 180 181 currentRVH_ = NULL; 182 183 // Create the appropriate mouse locations for WebCore. The draggingLocation 184 // is in window coordinates. Both need to be flipped. 185 NSPoint windowPoint = [info draggingLocation]; 186 NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view]; 187 NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view]; 188 tabContents_->render_view_host()->DragTargetDrop( 189 gfx::Point(viewPoint.x, viewPoint.y), 190 gfx::Point(screenPoint.x, screenPoint.y)); 191 192 return YES; 193} 194 195// Populate the |url| and |title| with URL data in |pboard|. There may be more 196// than one, but we only handle dropping the first. |url| must not be |NULL|; 197// |title| is an optional parameter. Returns |YES| if URL data was obtained from 198// the pasteboard, |NO| otherwise. If |convertFilenames| is |YES|, the function 199// will also attempt to convert filenames in |pboard| to file URLs. 200- (BOOL)populateURL:(GURL*)url 201 andTitle:(string16*)title 202 fromPasteboard:(NSPasteboard*)pboard 203 convertingFilenames:(BOOL)convertFilenames { 204 DCHECK(url); 205 DCHECK(title); 206 207 // Bail out early if there's no URL data. 208 if (![pboard containsURLData]) 209 return NO; 210 211 // |-getURLs:andTitles:convertingFilenames:| will already validate URIs so we 212 // don't need to again. The arrays returned are both of NSString's. 213 NSArray* urls = nil; 214 NSArray* titles = nil; 215 [pboard getURLs:&urls andTitles:&titles convertingFilenames:convertFilenames]; 216 DCHECK_EQ([urls count], [titles count]); 217 // It's possible that no URLs were actually provided! 218 if (![urls count]) 219 return NO; 220 NSString* urlString = [urls objectAtIndex:0]; 221 if ([urlString length]) { 222 // Check again just to make sure to not assign NULL into a std::string, 223 // which throws an exception. 224 const char* utf8Url = [urlString UTF8String]; 225 if (utf8Url) { 226 *url = GURL(utf8Url); 227 // Extra paranoia check. 228 if (title && [titles count]) 229 *title = base::SysNSStringToUTF16([titles objectAtIndex:0]); 230 } 231 } 232 return YES; 233} 234 235// Given |data|, which should not be nil, fill it in using the contents of the 236// given pasteboard. 237- (void)populateWebDropData:(WebDropData*)data 238 fromPasteboard:(NSPasteboard*)pboard { 239 DCHECK(data); 240 DCHECK(pboard); 241 NSArray* types = [pboard types]; 242 243 // Get URL if possible. To avoid exposing file system paths to web content, 244 // filenames in the drag are not converted to file URLs. 245 [self populateURL:&data->url 246 andTitle:&data->url_title 247 fromPasteboard:pboard 248 convertingFilenames:NO]; 249 250 // Get plain text. 251 if ([types containsObject:NSStringPboardType]) { 252 data->plain_text = 253 base::SysNSStringToUTF16([pboard stringForType:NSStringPboardType]); 254 } 255 256 // Get HTML. If there's no HTML, try RTF. 257 if ([types containsObject:NSHTMLPboardType]) { 258 data->text_html = 259 base::SysNSStringToUTF16([pboard stringForType:NSHTMLPboardType]); 260 } else if ([types containsObject:NSRTFPboardType]) { 261 NSString* html = [pboard htmlFromRtf]; 262 data->text_html = base::SysNSStringToUTF16(html); 263 } 264 265 // Get files. 266 if ([types containsObject:NSFilenamesPboardType]) { 267 NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; 268 if ([files isKindOfClass:[NSArray class]] && [files count]) { 269 for (NSUInteger i = 0; i < [files count]; i++) { 270 NSString* filename = [files objectAtIndex:i]; 271 BOOL isDir = NO; 272 BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:filename 273 isDirectory:&isDir]; 274 if (exists && !isDir) 275 data->filenames.push_back(base::SysNSStringToUTF16(filename)); 276 } 277 } 278 } 279 280 // TODO(pinkerton): Get file contents. http://crbug.com/34661 281} 282 283@end 284