Clang roll script: fix error when GYP_DEFINES isn't defined
[chromium-blink-merge.git] / content / browser / web_contents / web_drag_dest_mac.mm
blob8768bed657b59d4bb327364cfd5a66d241d35473
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.
5 #import "content/browser/web_contents/web_drag_dest_mac.h"
7 #import <Carbon/Carbon.h>
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"
21 using blink::WebDragOperationsMask;
22 using content::DropData;
23 using content::OpenURLParams;
24 using content::Referrer;
25 using content::WebContentsImpl;
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;
41 @implementation WebDragDest
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;
54 - (DropData*)currentDropData {
55   return dropData_.get();
58 - (void)setDragDelegate:(content::WebDragDestDelegate*)delegate {
59   delegate_ = delegate;
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;
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;
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;
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();
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.
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();
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]];
115   NSDragOperation mask = [info draggingSourceOperationMask];
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;
125   if ([self onlyAllowsNavigation]) {
126     if ([[info draggingPasteboard] containsURLData])
127       return NSDragOperationCopy;
128     return NSDragOperationNone;
129   }
131   if (delegate_) {
132     delegate_->DragInitialize(webContents_);
133     delegate_->OnDragEnter();
134   }
136   dropData_.swap(dropData);
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());
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_;
156 - (void)draggingExited:(id<NSDraggingInfo>)info {
157   DCHECK(currentRVH_);
158   if (currentRVH_ != webContents_->GetRenderViewHost())
159     return;
161   if (canceled_)
162     return;
164   if ([self onlyAllowsNavigation])
165     return;
167   if (delegate_)
168     delegate_->OnDragLeave();
170   webContents_->GetRenderViewHost()->DragTargetDragLeave();
171   dropData_.reset();
174 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info
175                               view:(NSView*)view {
176   DCHECK(currentRVH_);
177   if (currentRVH_ != webContents_->GetRenderViewHost())
178     [self draggingEntered:info view:view];
180   if (canceled_)
181     return NSDragOperationNone;
183   if ([self onlyAllowsNavigation]) {
184     if ([[info draggingPasteboard] containsURLData])
185       return NSDragOperationCopy;
186     return NSDragOperationNone;
187   }
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());
201   if (delegate_)
202     delegate_->OnDragOver();
204   return currentOperation_;
207 - (BOOL)performDragOperation:(id<NSDraggingInfo>)info
208                               view:(NSView*)view {
209   if (currentRVH_ != webContents_->GetRenderViewHost())
210     [self draggingEntered:info view:view];
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, content::PAGE_TRANSITION_AUTO_BOOKMARK,
220           false));
221       return YES;
222     } else {
223       return NO;
224     }
225   }
227   if (delegate_)
228     delegate_->OnDrop();
230   currentRVH_ = NULL;
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());
242   dropData_.reset();
244   return YES;
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];
256   // Get URL if possible. To avoid exposing file system paths to web content,
257   // filenames in the drag are not converted to file URLs.
258   ui::PopulateURLAndTitleFromPasteboard(&data->url,
259                                         &data->url_title,
260                                         pboard,
261                                         NO);
263   // Get plain text.
264   if ([types containsObject:NSStringPboardType]) {
265     data->text = base::NullableString16(
266         base::SysNSStringToUTF16([pboard stringForType:NSStringPboardType]),
267         false);
268   }
270   // Get HTML. If there's no HTML, try RTF.
271   if ([types containsObject:NSHTMLPboardType]) {
272     NSString* html = [pboard stringForType:NSHTMLPboardType];
273     data->html = base::NullableString16(base::SysNSStringToUTF16(html), false);
274   } else if ([types containsObject:ui::kChromeDragImageHTMLPboardType]) {
275     NSString* html = [pboard stringForType:ui::kChromeDragImageHTMLPboardType];
276     data->html = base::NullableString16(base::SysNSStringToUTF16(html), false);
277   } else if ([types containsObject:NSRTFPboardType]) {
278     NSString* html = [pboard htmlFromRtf];
279     data->html = base::NullableString16(base::SysNSStringToUTF16(html), false);
280   }
282   // Get files.
283   if ([types containsObject:NSFilenamesPboardType]) {
284     NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
285     if ([files isKindOfClass:[NSArray class]] && [files count]) {
286       for (NSUInteger i = 0; i < [files count]; i++) {
287         NSString* filename = [files objectAtIndex:i];
288         BOOL exists = [[NSFileManager defaultManager]
289                            fileExistsAtPath:filename];
290         if (exists) {
291           data->filenames.push_back(
292               DropData::FileInfo(
293                   base::SysNSStringToUTF16(filename), base::string16()));
294         }
295       }
296     }
297   }
299   // TODO(pinkerton): Get file contents. http://crbug.com/34661
301   // Get custom MIME data.
302   if ([types containsObject:ui::kWebCustomDataPboardType]) {
303     NSData* customData = [pboard dataForType:ui::kWebCustomDataPboardType];
304     ui::ReadCustomDataIntoMap([customData bytes],
305                               [customData length],
306                               &data->custom_data);
307   }
310 @end