web_drag_dest_mac.mm revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 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 "content/browser/web_contents/web_drag_dest_mac.h" 6 7#import <Carbon/Carbon.h> 8 9#include "base/strings/sys_string_conversions.h" 10#include "content/browser/renderer_host/render_view_host_impl.h" 11#include "content/browser/web_contents/web_contents_impl.h" 12#include "content/public/browser/web_drag_dest_delegate.h" 13#import "third_party/mozilla/NSPasteboard+Utils.h" 14#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" 15#include "ui/base/clipboard/custom_data_helper.h" 16#import "ui/base/dragdrop/cocoa_dnd_util.h" 17#include "ui/base/window_open_disposition.h" 18#include "webkit/glue/webdropdata.h" 19 20using WebKit::WebDragOperationsMask; 21using content::OpenURLParams; 22using content::Referrer; 23using content::WebContentsImpl; 24 25int GetModifierFlags() { 26 int modifier_state = 0; 27 UInt32 currentModifiers = GetCurrentKeyModifiers(); 28 if (currentModifiers & ::shiftKey) 29 modifier_state |= WebKit::WebInputEvent::ShiftKey; 30 if (currentModifiers & ::controlKey) 31 modifier_state |= WebKit::WebInputEvent::ControlKey; 32 if (currentModifiers & ::optionKey) 33 modifier_state |= WebKit::WebInputEvent::AltKey; 34 if (currentModifiers & ::cmdKey) 35 modifier_state |= WebKit::WebInputEvent::MetaKey; 36 return modifier_state; 37} 38 39@implementation WebDragDest 40 41// |contents| is the WebContentsImpl representing this tab, used to communicate 42// drag&drop messages to WebCore and handle navigation on a successful drop 43// (if necessary). 44- (id)initWithWebContentsImpl:(WebContentsImpl*)contents { 45 if ((self = [super init])) { 46 webContents_ = contents; 47 } 48 return self; 49} 50 51- (WebDropData*)currentDropData { 52 return dropData_.get(); 53} 54 55- (void)setDragDelegate:(content::WebDragDestDelegate*)delegate { 56 delegate_ = delegate; 57} 58 59// Call to set whether or not we should allow the drop. Takes effect the 60// next time |-draggingUpdated:| is called. 61- (void)setCurrentOperation:(NSDragOperation)operation { 62 currentOperation_ = operation; 63} 64 65// Given a point in window coordinates and a view in that window, return a 66// flipped point in the coordinate system of |view|. 67- (NSPoint)flipWindowPointToView:(const NSPoint&)windowPoint 68 view:(NSView*)view { 69 DCHECK(view); 70 NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil]; 71 NSRect viewFrame = [view frame]; 72 viewPoint.y = viewFrame.size.height - viewPoint.y; 73 return viewPoint; 74} 75 76// Given a point in window coordinates and a view in that window, return a 77// flipped point in screen coordinates. 78- (NSPoint)flipWindowPointToScreen:(const NSPoint&)windowPoint 79 view:(NSView*)view { 80 DCHECK(view); 81 NSPoint screenPoint = [[view window] convertBaseToScreen:windowPoint]; 82 NSScreen* screen = [[view window] screen]; 83 NSRect screenFrame = [screen frame]; 84 screenPoint.y = screenFrame.size.height - screenPoint.y; 85 return screenPoint; 86} 87 88// Return YES if the drop site only allows drops that would navigate. If this 89// is the case, we don't want to pass messages to the renderer because there's 90// really no point (i.e., there's nothing that cares about the mouse position or 91// entering and exiting). One example is an interstitial page (e.g., safe 92// browsing warning). 93- (BOOL)onlyAllowsNavigation { 94 return webContents_->ShowingInterstitialPage(); 95} 96 97// Messages to send during the tracking of a drag, usually upon receiving 98// calls from the view system. Communicates the drag messages to WebCore. 99 100- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info 101 view:(NSView*)view { 102 // Save off the RVH so we can tell if it changes during a drag. If it does, 103 // we need to send a new enter message in draggingUpdated:. 104 currentRVH_ = webContents_->GetRenderViewHost(); 105 106 if ([self onlyAllowsNavigation]) { 107 if ([[info draggingPasteboard] containsURLData]) 108 return NSDragOperationCopy; 109 return NSDragOperationNone; 110 } 111 112 if (delegate_) { 113 delegate_->DragInitialize(webContents_); 114 delegate_->OnDragEnter(); 115 } 116 117 // Fill out a WebDropData from pasteboard. 118 dropData_.reset(new WebDropData()); 119 [self populateWebDropData:dropData_.get() 120 fromPasteboard:[info draggingPasteboard]]; 121 122 // Create the appropriate mouse locations for WebCore. The draggingLocation 123 // is in window coordinates. Both need to be flipped. 124 NSPoint windowPoint = [info draggingLocation]; 125 NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view]; 126 NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view]; 127 NSDragOperation mask = [info draggingSourceOperationMask]; 128 webContents_->GetRenderViewHost()->DragTargetDragEnter( 129 *dropData_, 130 gfx::Point(viewPoint.x, viewPoint.y), 131 gfx::Point(screenPoint.x, screenPoint.y), 132 static_cast<WebDragOperationsMask>(mask), 133 GetModifierFlags()); 134 135 // We won't know the true operation (whether the drag is allowed) until we 136 // hear back from the renderer. For now, be optimistic: 137 currentOperation_ = NSDragOperationCopy; 138 return currentOperation_; 139} 140 141- (void)draggingExited:(id<NSDraggingInfo>)info { 142 DCHECK(currentRVH_); 143 if (currentRVH_ != webContents_->GetRenderViewHost()) 144 return; 145 146 if ([self onlyAllowsNavigation]) 147 return; 148 149 if (delegate_) 150 delegate_->OnDragLeave(); 151 152 webContents_->GetRenderViewHost()->DragTargetDragLeave(); 153 dropData_.reset(); 154} 155 156- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info 157 view:(NSView*)view { 158 DCHECK(currentRVH_); 159 if (currentRVH_ != webContents_->GetRenderViewHost()) 160 [self draggingEntered:info view:view]; 161 162 if ([self onlyAllowsNavigation]) { 163 if ([[info draggingPasteboard] containsURLData]) 164 return NSDragOperationCopy; 165 return NSDragOperationNone; 166 } 167 168 // Create the appropriate mouse locations for WebCore. The draggingLocation 169 // is in window coordinates. 170 NSPoint windowPoint = [info draggingLocation]; 171 NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view]; 172 NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view]; 173 NSDragOperation mask = [info draggingSourceOperationMask]; 174 webContents_->GetRenderViewHost()->DragTargetDragOver( 175 gfx::Point(viewPoint.x, viewPoint.y), 176 gfx::Point(screenPoint.x, screenPoint.y), 177 static_cast<WebDragOperationsMask>(mask), 178 GetModifierFlags()); 179 180 if (delegate_) 181 delegate_->OnDragOver(); 182 183 return currentOperation_; 184} 185 186- (BOOL)performDragOperation:(id<NSDraggingInfo>)info 187 view:(NSView*)view { 188 if (currentRVH_ != webContents_->GetRenderViewHost()) 189 [self draggingEntered:info view:view]; 190 191 // Check if we only allow navigation and navigate to a url on the pasteboard. 192 if ([self onlyAllowsNavigation]) { 193 NSPasteboard* pboard = [info draggingPasteboard]; 194 if ([pboard containsURLData]) { 195 GURL url; 196 ui::PopulateURLAndTitleFromPasteboard(&url, NULL, pboard, YES); 197 webContents_->OpenURL(OpenURLParams( 198 url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_AUTO_BOOKMARK, 199 false)); 200 return YES; 201 } else { 202 return NO; 203 } 204 } 205 206 if (delegate_) 207 delegate_->OnDrop(); 208 209 currentRVH_ = NULL; 210 211 // Create the appropriate mouse locations for WebCore. The draggingLocation 212 // is in window coordinates. Both need to be flipped. 213 NSPoint windowPoint = [info draggingLocation]; 214 NSPoint viewPoint = [self flipWindowPointToView:windowPoint view:view]; 215 NSPoint screenPoint = [self flipWindowPointToScreen:windowPoint view:view]; 216 webContents_->GetRenderViewHost()->DragTargetDrop( 217 gfx::Point(viewPoint.x, viewPoint.y), 218 gfx::Point(screenPoint.x, screenPoint.y), 219 GetModifierFlags()); 220 221 dropData_.reset(); 222 223 return YES; 224} 225 226// Given |data|, which should not be nil, fill it in using the contents of the 227// given pasteboard. The types handled by this method should be kept in sync 228// with [WebContentsViewCocoa registerDragTypes]. 229- (void)populateWebDropData:(WebDropData*)data 230 fromPasteboard:(NSPasteboard*)pboard { 231 DCHECK(data); 232 DCHECK(pboard); 233 NSArray* types = [pboard types]; 234 235 // Get URL if possible. To avoid exposing file system paths to web content, 236 // filenames in the drag are not converted to file URLs. 237 ui::PopulateURLAndTitleFromPasteboard(&data->url, 238 &data->url_title, 239 pboard, 240 NO); 241 242 // Get plain text. 243 if ([types containsObject:NSStringPboardType]) { 244 data->text = NullableString16( 245 base::SysNSStringToUTF16([pboard stringForType:NSStringPboardType]), 246 false); 247 } 248 249 // Get HTML. If there's no HTML, try RTF. 250 if ([types containsObject:NSHTMLPboardType]) { 251 NSString* html = [pboard stringForType:NSHTMLPboardType]; 252 data->html = NullableString16(base::SysNSStringToUTF16(html), false); 253 } else if ([types containsObject:ui::kChromeDragImageHTMLPboardType]) { 254 NSString* html = [pboard stringForType:ui::kChromeDragImageHTMLPboardType]; 255 data->html = NullableString16(base::SysNSStringToUTF16(html), false); 256 } else if ([types containsObject:NSRTFPboardType]) { 257 NSString* html = [pboard htmlFromRtf]; 258 data->html = NullableString16(base::SysNSStringToUTF16(html), false); 259 } 260 261 // Get files. 262 if ([types containsObject:NSFilenamesPboardType]) { 263 NSArray* files = [pboard propertyListForType:NSFilenamesPboardType]; 264 if ([files isKindOfClass:[NSArray class]] && [files count]) { 265 for (NSUInteger i = 0; i < [files count]; i++) { 266 NSString* filename = [files objectAtIndex:i]; 267 BOOL exists = [[NSFileManager defaultManager] 268 fileExistsAtPath:filename]; 269 if (exists) { 270 data->filenames.push_back( 271 WebDropData::FileInfo( 272 base::SysNSStringToUTF16(filename), string16())); 273 } 274 } 275 } 276 } 277 278 // TODO(pinkerton): Get file contents. http://crbug.com/34661 279 280 // Get custom MIME data. 281 if ([types containsObject:ui::kWebCustomDataPboardType]) { 282 NSData* customData = [pboard dataForType:ui::kWebCustomDataPboardType]; 283 ui::ReadCustomDataIntoMap([customData bytes], 284 [customData length], 285 &data->custom_data); 286 } 287} 288 289@end 290