• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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