• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 #include "chrome/browser/tab_contents/web_drop_target_win.h"
6 
7 #include <windows.h>
8 #include <shlobj.h>
9 
10 #include "chrome/browser/bookmarks/bookmark_node_data.h"
11 #include "chrome/browser/tab_contents/web_drag_utils_win.h"
12 #include "content/browser/renderer_host/render_view_host.h"
13 #include "content/browser/tab_contents/tab_contents.h"
14 #include "googleurl/src/gurl.h"
15 #include "net/base/net_util.h"
16 #include "ui/base/clipboard/clipboard_util_win.h"
17 #include "ui/base/dragdrop/os_exchange_data.h"
18 #include "ui/base/dragdrop/os_exchange_data_provider_win.h"
19 #include "ui/gfx/point.h"
20 #include "webkit/glue/webdropdata.h"
21 #include "webkit/glue/window_open_disposition.h"
22 
23 using WebKit::WebDragOperationNone;
24 using WebKit::WebDragOperationCopy;
25 using WebKit::WebDragOperationLink;
26 using WebKit::WebDragOperationMove;
27 using WebKit::WebDragOperationGeneric;
28 
29 namespace {
30 
31 // A helper method for getting the preferred drop effect.
GetPreferredDropEffect(DWORD effect)32 DWORD GetPreferredDropEffect(DWORD effect) {
33   if (effect & DROPEFFECT_COPY)
34     return DROPEFFECT_COPY;
35   if (effect & DROPEFFECT_LINK)
36     return DROPEFFECT_LINK;
37   if (effect & DROPEFFECT_MOVE)
38     return DROPEFFECT_MOVE;
39   return DROPEFFECT_NONE;
40 }
41 
42 }  // namespace
43 
44 // InterstitialDropTarget is like a app::win::DropTarget implementation that
45 // WebDropTarget passes through to if an interstitial is showing.  Rather than
46 // passing messages on to the renderer, we just check to see if there's a link
47 // in the drop data and handle links as navigations.
48 class InterstitialDropTarget {
49  public:
InterstitialDropTarget(TabContents * tab_contents)50   explicit InterstitialDropTarget(TabContents* tab_contents)
51       : tab_contents_(tab_contents) {}
52 
OnDragEnter(IDataObject * data_object,DWORD effect)53   DWORD OnDragEnter(IDataObject* data_object, DWORD effect) {
54     return ui::ClipboardUtil::HasUrl(data_object) ?
55         GetPreferredDropEffect(effect) : DROPEFFECT_NONE;
56   }
57 
OnDragOver(IDataObject * data_object,DWORD effect)58   DWORD OnDragOver(IDataObject* data_object, DWORD effect) {
59     return ui::ClipboardUtil::HasUrl(data_object) ?
60         GetPreferredDropEffect(effect) : DROPEFFECT_NONE;
61   }
62 
OnDragLeave(IDataObject * data_object)63   void OnDragLeave(IDataObject* data_object) {
64   }
65 
OnDrop(IDataObject * data_object,DWORD effect)66   DWORD OnDrop(IDataObject* data_object, DWORD effect) {
67     if (ui::ClipboardUtil::HasUrl(data_object)) {
68       std::wstring url;
69       std::wstring title;
70       ui::ClipboardUtil::GetUrl(data_object, &url, &title, true);
71       tab_contents_->OpenURL(GURL(url), GURL(), CURRENT_TAB,
72                              PageTransition::AUTO_BOOKMARK);
73       return GetPreferredDropEffect(effect);
74     }
75     return DROPEFFECT_NONE;
76   }
77 
78  private:
79   TabContents* tab_contents_;
80 
81   DISALLOW_COPY_AND_ASSIGN(InterstitialDropTarget);
82 };
83 
WebDropTarget(HWND source_hwnd,TabContents * tab_contents)84 WebDropTarget::WebDropTarget(HWND source_hwnd, TabContents* tab_contents)
85     : ui::DropTarget(source_hwnd),
86       tab_contents_(tab_contents),
87       current_rvh_(NULL),
88       drag_cursor_(WebDragOperationNone),
89       interstitial_drop_target_(new InterstitialDropTarget(tab_contents)) {
90 }
91 
~WebDropTarget()92 WebDropTarget::~WebDropTarget() {
93 }
94 
OnDragEnter(IDataObject * data_object,DWORD key_state,POINT cursor_position,DWORD effects)95 DWORD WebDropTarget::OnDragEnter(IDataObject* data_object,
96                                  DWORD key_state,
97                                  POINT cursor_position,
98                                  DWORD effects) {
99   current_rvh_ = tab_contents_->render_view_host();
100 
101   // Don't pass messages to the renderer if an interstitial page is showing
102   // because we don't want the interstitial page to navigate.  Instead,
103   // pass the messages on to a separate interstitial DropTarget handler.
104   if (tab_contents_->showing_interstitial_page())
105     return interstitial_drop_target_->OnDragEnter(data_object, effects);
106 
107   // TODO(tc): PopulateWebDropData can be slow depending on what is in the
108   // IDataObject.  Maybe we can do this in a background thread.
109   WebDropData drop_data;
110   WebDropData::PopulateWebDropData(data_object, &drop_data);
111 
112   if (drop_data.url.is_empty())
113     ui::OSExchangeDataProviderWin::GetPlainTextURL(data_object, &drop_data.url);
114 
115   drag_cursor_ = WebDragOperationNone;
116 
117   POINT client_pt = cursor_position;
118   ScreenToClient(GetHWND(), &client_pt);
119   tab_contents_->render_view_host()->DragTargetDragEnter(drop_data,
120       gfx::Point(client_pt.x, client_pt.y),
121       gfx::Point(cursor_position.x, cursor_position.y),
122       web_drag_utils_win::WinDragOpMaskToWebDragOpMask(effects));
123 
124   // This is non-null if tab_contents_ is showing an ExtensionWebUI with
125   // support for (at the moment experimental) drag and drop extensions.
126   if (tab_contents_->GetBookmarkDragDelegate()) {
127     ui::OSExchangeData os_exchange_data(
128         new ui::OSExchangeDataProviderWin(data_object));
129     BookmarkNodeData bookmark_drag_data;
130     if (bookmark_drag_data.Read(os_exchange_data))
131       tab_contents_->GetBookmarkDragDelegate()->OnDragEnter(bookmark_drag_data);
132   }
133 
134   // We lie here and always return a DROPEFFECT because we don't want to
135   // wait for the IPC call to return.
136   return web_drag_utils_win::WebDragOpToWinDragOp(drag_cursor_);
137 }
138 
OnDragOver(IDataObject * data_object,DWORD key_state,POINT cursor_position,DWORD effects)139 DWORD WebDropTarget::OnDragOver(IDataObject* data_object,
140                                 DWORD key_state,
141                                 POINT cursor_position,
142                                 DWORD effects) {
143   DCHECK(current_rvh_);
144   if (current_rvh_ != tab_contents_->render_view_host())
145     OnDragEnter(data_object, key_state, cursor_position, effects);
146 
147   if (tab_contents_->showing_interstitial_page())
148     return interstitial_drop_target_->OnDragOver(data_object, effects);
149 
150   POINT client_pt = cursor_position;
151   ScreenToClient(GetHWND(), &client_pt);
152   tab_contents_->render_view_host()->DragTargetDragOver(
153       gfx::Point(client_pt.x, client_pt.y),
154       gfx::Point(cursor_position.x, cursor_position.y),
155       web_drag_utils_win::WinDragOpMaskToWebDragOpMask(effects));
156 
157   if (tab_contents_->GetBookmarkDragDelegate()) {
158     ui::OSExchangeData os_exchange_data(
159         new ui::OSExchangeDataProviderWin(data_object));
160     BookmarkNodeData bookmark_drag_data;
161     if (bookmark_drag_data.Read(os_exchange_data))
162       tab_contents_->GetBookmarkDragDelegate()->OnDragOver(bookmark_drag_data);
163   }
164 
165   return web_drag_utils_win::WebDragOpToWinDragOp(drag_cursor_);
166 }
167 
OnDragLeave(IDataObject * data_object)168 void WebDropTarget::OnDragLeave(IDataObject* data_object) {
169   DCHECK(current_rvh_);
170   if (current_rvh_ != tab_contents_->render_view_host())
171     return;
172 
173   if (tab_contents_->showing_interstitial_page()) {
174     interstitial_drop_target_->OnDragLeave(data_object);
175   } else {
176     tab_contents_->render_view_host()->DragTargetDragLeave();
177   }
178 
179   if (tab_contents_->GetBookmarkDragDelegate()) {
180     ui::OSExchangeData os_exchange_data(
181         new ui::OSExchangeDataProviderWin(data_object));
182     BookmarkNodeData bookmark_drag_data;
183     if (bookmark_drag_data.Read(os_exchange_data))
184       tab_contents_->GetBookmarkDragDelegate()->OnDragLeave(bookmark_drag_data);
185   }
186 }
187 
OnDrop(IDataObject * data_object,DWORD key_state,POINT cursor_position,DWORD effect)188 DWORD WebDropTarget::OnDrop(IDataObject* data_object,
189                             DWORD key_state,
190                             POINT cursor_position,
191                             DWORD effect) {
192   DCHECK(current_rvh_);
193   if (current_rvh_ != tab_contents_->render_view_host())
194     OnDragEnter(data_object, key_state, cursor_position, effect);
195 
196   if (tab_contents_->showing_interstitial_page())
197     interstitial_drop_target_->OnDragOver(data_object, effect);
198 
199   if (tab_contents_->showing_interstitial_page())
200     return interstitial_drop_target_->OnDrop(data_object, effect);
201 
202   POINT client_pt = cursor_position;
203   ScreenToClient(GetHWND(), &client_pt);
204   tab_contents_->render_view_host()->DragTargetDrop(
205       gfx::Point(client_pt.x, client_pt.y),
206       gfx::Point(cursor_position.x, cursor_position.y));
207 
208   if (tab_contents_->GetBookmarkDragDelegate()) {
209     ui::OSExchangeData os_exchange_data(
210         new ui::OSExchangeDataProviderWin(data_object));
211     BookmarkNodeData bookmark_drag_data;
212     if (bookmark_drag_data.Read(os_exchange_data))
213       tab_contents_->GetBookmarkDragDelegate()->OnDrop(bookmark_drag_data);
214   }
215 
216   current_rvh_ = NULL;
217 
218   // This isn't always correct, but at least it's a close approximation.
219   // For now, we always map a move to a copy to prevent potential data loss.
220   DWORD drop_effect = web_drag_utils_win::WebDragOpToWinDragOp(drag_cursor_);
221   return drop_effect != DROPEFFECT_MOVE ? drop_effect : DROPEFFECT_COPY;
222 }
223