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. 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: 50 explicit InterstitialDropTarget(TabContents* tab_contents) 51 : tab_contents_(tab_contents) {} 52 53 DWORD OnDragEnter(IDataObject* data_object, DWORD effect) { 54 return ui::ClipboardUtil::HasUrl(data_object) ? 55 GetPreferredDropEffect(effect) : DROPEFFECT_NONE; 56 } 57 58 DWORD OnDragOver(IDataObject* data_object, DWORD effect) { 59 return ui::ClipboardUtil::HasUrl(data_object) ? 60 GetPreferredDropEffect(effect) : DROPEFFECT_NONE; 61 } 62 63 void OnDragLeave(IDataObject* data_object) { 64 } 65 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 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 92 WebDropTarget::~WebDropTarget() { 93 } 94 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 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 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 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