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