| Index: chrome/browser/views/tab_contents/tab_contents_drag_win.cc
|
| ===================================================================
|
| --- chrome/browser/views/tab_contents/tab_contents_drag_win.cc (revision 0)
|
| +++ chrome/browser/views/tab_contents/tab_contents_drag_win.cc (revision 0)
|
| @@ -0,0 +1,328 @@
|
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/views/tab_contents/tab_contents_drag_win.h"
|
| +
|
| +#include <windows.h>
|
| +
|
| +#include "base/file_path.h"
|
| +#include "base/message_loop.h"
|
| +#include "base/task.h"
|
| +#include "base/thread.h"
|
| +#include "base/win_util.h"
|
| +#include "chrome/browser/bookmarks/bookmark_drag_data.h"
|
| +#include "chrome/browser/chrome_thread.h"
|
| +#include "chrome/browser/download/drag_download_file_win.h"
|
| +#include "chrome/browser/profile.h"
|
| +#include "chrome/browser/tab_contents/tab_contents.h"
|
| +#include "chrome/browser/tab_contents/web_drag_source_win.h"
|
| +#include "chrome/browser/tab_contents/web_drop_target_win.h"
|
| +#include "chrome/browser/views/tab_contents/tab_contents_view_win.h"
|
| +#include "chrome/common/url_constants.h"
|
| +#include "net/base/net_util.h"
|
| +#include "webkit/glue/webdropdata.h"
|
| +
|
| +using WebKit::WebDragOperationsMask;
|
| +
|
| +namespace {
|
| +
|
| +HHOOK msg_hook = NULL;
|
| +DWORD drag_out_thread_id = 0;
|
| +bool mouse_up_received = false;
|
| +
|
| +LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) {
|
| + if (code == base::MessagePumpForUI::kMessageFilterCode &&
|
| + !mouse_up_received) {
|
| + MSG* msg = reinterpret_cast<MSG*>(lparam);
|
| + // We do not care about WM_SYSKEYDOWN and WM_SYSKEYUP because when ALT key
|
| + // is pressed down on drag-and-drop, it means to create a link.
|
| + if (msg->message == WM_MOUSEMOVE || msg->message == WM_LBUTTONUP ||
|
| + msg->message == WM_KEYDOWN || msg->message == WM_KEYUP) {
|
| + // Forward the message from the UI thread to the drag-and-drop thread.
|
| + PostThreadMessage(drag_out_thread_id,
|
| + msg->message,
|
| + msg->wParam,
|
| + msg->lParam);
|
| +
|
| + // If the left button is up, we do not need to forward the message any
|
| + // more.
|
| + if (msg->message == WM_LBUTTONUP || !(GetKeyState(VK_LBUTTON) & 0x8000))
|
| + mouse_up_received = true;
|
| +
|
| + return TRUE;
|
| + }
|
| + }
|
| + return CallNextHookEx(msg_hook, code, wparam, lparam);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +class DragDropThread : public base::Thread {
|
| + public:
|
| + explicit DragDropThread(TabContentsDragWin* drag_handler)
|
| + : base::Thread("Chrome_DragDropThread"),
|
| + drag_handler_(drag_handler) {
|
| + }
|
| +
|
| + virtual ~DragDropThread() {
|
| + Thread::Stop();
|
| + }
|
| +
|
| + protected:
|
| + // base::Thread implementations:
|
| + virtual void Init() {
|
| + int ole_result = OleInitialize(NULL);
|
| + DCHECK(ole_result == S_OK);
|
| + }
|
| +
|
| + virtual void CleanUp() {
|
| + OleUninitialize();
|
| + }
|
| +
|
| + private:
|
| + // Hold a reference count to TabContentsDragWin to make sure that it is always
|
| + // alive in the thread lifetime.
|
| + scoped_refptr<TabContentsDragWin> drag_handler_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DragDropThread);
|
| +};
|
| +
|
| +TabContentsDragWin::TabContentsDragWin(TabContentsViewWin* view)
|
| + : view_(view),
|
| + drag_ended_(false),
|
| + old_drop_target_suspended_state_(false) {
|
| +#ifndef NDEBUG
|
| + drag_drop_thread_id_ = 0;
|
| +#endif
|
| +}
|
| +
|
| +TabContentsDragWin::~TabContentsDragWin() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + DCHECK(!drag_source_.get());
|
| + DCHECK(!drag_drop_thread_.get());
|
| +}
|
| +
|
| +void TabContentsDragWin::StartDragging(const WebDropData& drop_data,
|
| + WebDragOperationsMask ops) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +
|
| + drag_source_ = new WebDragSource(view_->GetNativeView(),
|
| + view_->tab_contents());
|
| +
|
| + const GURL& page_url = view_->tab_contents()->GetURL();
|
| + const std::string& page_encoding = view_->tab_contents()->encoding();
|
| +
|
| + // If it is not drag-out, do the drag-and-drop in the current UI thread.
|
| + if (!drop_data.download_url.is_valid()) {
|
| + DoDragging(drop_data, ops, page_url, page_encoding);
|
| + EndDragging(false);
|
| + return;
|
| + }
|
| +
|
| + // We do not want to drag and drop the download to itself.
|
| + old_drop_target_suspended_state_ = view_->drop_target()->suspended();
|
| + view_->drop_target()->set_suspended(true);
|
| +
|
| + // Start a background thread to do the drag-and-drop.
|
| + DCHECK(!drag_drop_thread_.get());
|
| + drag_drop_thread_.reset(new DragDropThread(this));
|
| + base::Thread::Options options;
|
| + options.message_loop_type = MessageLoop::TYPE_UI;
|
| + if (drag_drop_thread_->StartWithOptions(options)) {
|
| + drag_drop_thread_->message_loop()->PostTask(
|
| + FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &TabContentsDragWin::StartBackgroundDragging,
|
| + drop_data,
|
| + ops,
|
| + page_url,
|
| + page_encoding));
|
| + }
|
| +
|
| + // Install a hook procedure to monitor the messages so that we can forward
|
| + // the appropriate ones to the background thread.
|
| + drag_out_thread_id = drag_drop_thread_->thread_id();
|
| + mouse_up_received = false;
|
| + DCHECK(!msg_hook);
|
| + msg_hook = SetWindowsHookEx(WH_MSGFILTER,
|
| + MsgFilterProc,
|
| + NULL,
|
| + GetCurrentThreadId());
|
| +
|
| + // Attach the input state of the background thread to the UI thread so that
|
| + // SetCursor can work from the background thread.
|
| + AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), TRUE);
|
| +}
|
| +
|
| +void TabContentsDragWin::StartBackgroundDragging(
|
| + const WebDropData& drop_data,
|
| + WebDragOperationsMask ops,
|
| + const GURL& page_url,
|
| + const std::string& page_encoding) {
|
| +#ifndef NDEBUG
|
| + drag_drop_thread_id_ = PlatformThread::CurrentId();
|
| +#endif
|
| +
|
| + DoDragging(drop_data, ops, page_url, page_encoding);
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &TabContentsDragWin::EndDragging, true));
|
| +}
|
| +
|
| +void TabContentsDragWin::PrepareDragForDownload(
|
| + const WebDropData& drop_data,
|
| + OSExchangeData* data,
|
| + const GURL& page_url,
|
| + const std::string& page_encoding) {
|
| + // Provide the data as file (CF_HDROP). A temporary download file with the
|
| + // Zone.Identifier ADS (Alternate Data Stream) attached will be created.
|
| + scoped_refptr<DragDownloadFile> download_file =
|
| + new DragDownloadFile(drop_data.download_url,
|
| + page_url,
|
| + page_encoding,
|
| + view_->tab_contents());
|
| + OSExchangeData::DownloadFileInfo* file_download =
|
| + new OSExchangeData::DownloadFileInfo(FilePath(),
|
| + 0,
|
| + download_file.get());
|
| + data->SetDownloadFileInfo(file_download);
|
| +
|
| + // Enable asynchronous operation.
|
| + OSExchangeDataProviderWin::GetIAsyncOperation(*data)->SetAsyncMode(TRUE);
|
| +}
|
| +
|
| +void TabContentsDragWin::PrepareDragForFileContents(
|
| + const WebDropData& drop_data, OSExchangeData* data) {
|
| + // Images without ALT text will only have a file extension so we need to
|
| + // synthesize one from the provided extension and URL.
|
| + FilePath file_name(drop_data.file_description_filename);
|
| + file_name = file_name.BaseName().RemoveExtension();
|
| + if (file_name.value().empty()) {
|
| + // Retrieve the name from the URL.
|
| + file_name = net::GetSuggestedFilename(drop_data.url, "", "", FilePath());
|
| + if (file_name.value().size() + drop_data.file_extension.size() + 1 >
|
| + MAX_PATH) {
|
| + file_name = FilePath(file_name.value().substr(
|
| + 0, MAX_PATH - drop_data.file_extension.size() - 2));
|
| + }
|
| + }
|
| + file_name = file_name.ReplaceExtension(drop_data.file_extension);
|
| + data->SetFileContents(file_name.value(), drop_data.file_contents);
|
| +}
|
| +
|
| +void TabContentsDragWin::PrepareDragForUrl(const WebDropData& drop_data,
|
| + OSExchangeData* data) {
|
| + if (drop_data.url.SchemeIs(chrome::kJavaScriptScheme)) {
|
| + // We don't want to allow javascript URLs to be dragged to the desktop,
|
| + // but we do want to allow them to be added to the bookmarks bar
|
| + // (bookmarklets). So we create a fake bookmark entry (BookmarkDragData
|
| + // object) which explorer.exe cannot handle, and write the entry to data.
|
| + BookmarkDragData::Element bm_elt;
|
| + bm_elt.is_url = true;
|
| + bm_elt.url = drop_data.url;
|
| + bm_elt.title = drop_data.url_title;
|
| +
|
| + BookmarkDragData bm_drag_data;
|
| + bm_drag_data.elements.push_back(bm_elt);
|
| +
|
| + // Pass in NULL as the profile so that the bookmark always adds the url
|
| + // rather than trying to move an existing url.
|
| + bm_drag_data.Write(NULL, data);
|
| + } else {
|
| + data->SetURL(drop_data.url, drop_data.url_title);
|
| + }
|
| +}
|
| +
|
| +void TabContentsDragWin::DoDragging(const WebDropData& drop_data,
|
| + WebDragOperationsMask ops,
|
| + const GURL& page_url,
|
| + const std::string& page_encoding) {
|
| + OSExchangeData data;
|
| +
|
| + // TODO(tc): Generate an appropriate drag image.
|
| +
|
| + if (drop_data.download_url.is_valid()) {
|
| + PrepareDragForDownload(drop_data, &data, page_url, page_encoding);
|
| +
|
| + // Set the observer.
|
| + OSExchangeDataProviderWin::GetDataObjectImpl(data)->set_observer(this);
|
| + } else {
|
| + // We set the file contents before the URL because the URL also sets file
|
| + // contents (to a .URL shortcut). We want to prefer file content data over
|
| + // a shortcut so we add it first.
|
| + if (!drop_data.file_contents.empty())
|
| + PrepareDragForFileContents(drop_data, &data);
|
| + if (!drop_data.text_html.empty())
|
| + data.SetHtml(drop_data.text_html, drop_data.html_base_url);
|
| + if (drop_data.url.is_valid())
|
| + PrepareDragForUrl(drop_data, &data);
|
| + if (!drop_data.plain_text.empty())
|
| + data.SetString(drop_data.plain_text);
|
| + }
|
| +
|
| + DWORD effects = 0;
|
| +
|
| + // We need to enable recursive tasks on the message loop so we can get
|
| + // updates while in the system DoDragDrop loop.
|
| + bool old_state = MessageLoop::current()->NestableTasksAllowed();
|
| + MessageLoop::current()->SetNestableTasksAllowed(true);
|
| + DoDragDrop(OSExchangeDataProviderWin::GetIDataObject(data), drag_source_,
|
| + DROPEFFECT_COPY | DROPEFFECT_LINK, &effects);
|
| + // TODO(snej): Use 'ops' param instead of hardcoding dropeffects
|
| + MessageLoop::current()->SetNestableTasksAllowed(old_state);
|
| +}
|
| +
|
| +void TabContentsDragWin::EndDragging(bool restore_suspended_state) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +
|
| + if (drag_ended_)
|
| + return;
|
| + drag_ended_ = true;
|
| +
|
| + if (restore_suspended_state)
|
| + view_->drop_target()->set_suspended(old_drop_target_suspended_state_);
|
| +
|
| + drag_source_ = NULL;
|
| +
|
| + if (msg_hook) {
|
| + AttachThreadInput(drag_out_thread_id, GetCurrentThreadId(), FALSE);
|
| + UnhookWindowsHookEx(msg_hook);
|
| + msg_hook = NULL;
|
| + }
|
| +
|
| + view_->EndDragging();
|
| +}
|
| +
|
| +void TabContentsDragWin::CancelDrag() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +
|
| + drag_source_->CancelDrag();
|
| +}
|
| +
|
| +void TabContentsDragWin::CloseThread() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +
|
| + drag_drop_thread_.reset();
|
| +}
|
| +
|
| +void TabContentsDragWin::OnWaitForData() {
|
| + DCHECK(drag_drop_thread_id_ == PlatformThread::CurrentId());
|
| +
|
| + // When the left button is release and we start to wait for the data, end
|
| + // the dragging before DoDragDrop returns. This makes the page leave the drag
|
| + // mode so that it can start to process the normal input events.
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &TabContentsDragWin::EndDragging, true));
|
| +}
|
| +
|
| +void TabContentsDragWin::OnDataObjectDisposed() {
|
| + DCHECK(drag_drop_thread_id_ == PlatformThread::CurrentId());
|
| +
|
| + // The drag-and-drop thread is only closed after OLE is done with
|
| + // DataObjectImpl.
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &TabContentsDragWin::CloseThread));
|
| +}
|
|
|
| Property changes on: chrome\browser\views\tab_contents\tab_contents_drag_win.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|