Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Unified Diff: chrome/browser/views/tab_contents/tab_contents_drag_win.cc

Issue 351029: Support dragging a virtual file out of the browser. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « chrome/browser/views/tab_contents/tab_contents_drag_win.h ('k') | chrome/browser/views/tab_contents/tab_contents_view_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698