Home | History | Annotate | Download | only in tab_contents
      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 
     16 using 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