Home | History | Annotate | Download | only in tab_contents
      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