| Index: app/os_exchange_data_provider_win.cc
|
| ===================================================================
|
| --- app/os_exchange_data_provider_win.cc (revision 35459)
|
| +++ app/os_exchange_data_provider_win.cc (working copy)
|
| @@ -29,6 +29,8 @@
|
| static void CreateValidFileNameFromTitle(const GURL& url,
|
| const std::wstring& title,
|
| std::wstring* validated);
|
| +// Creates a new STGMEDIUM object to hold a file.
|
| +static STGMEDIUM* GetStorageForFileName(const std::wstring& full_path);
|
| // Creates a File Descriptor for the creation of a file to the given URL and
|
| // returns a handle to it.
|
| static STGMEDIUM* GetStorageForFileDescriptor(
|
| @@ -226,12 +228,26 @@
|
| }
|
|
|
| // static
|
| +DataObjectImpl* OSExchangeDataProviderWin::GetDataObjectImpl(
|
| + const OSExchangeData& data) {
|
| + return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
|
| + data_.get();
|
| +}
|
| +
|
| +// static
|
| IDataObject* OSExchangeDataProviderWin::GetIDataObject(
|
| const OSExchangeData& data) {
|
| - return static_cast<const OSExchangeDataProviderWin&>(data.provider()).
|
| + return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
|
| data_object();
|
| }
|
|
|
| +// static
|
| +IAsyncOperation* OSExchangeDataProviderWin::GetIAsyncOperation(
|
| + const OSExchangeData& data) {
|
| + return static_cast<const OSExchangeDataProviderWin*>(&data.provider())->
|
| + async_operation();
|
| +}
|
| +
|
| OSExchangeDataProviderWin::OSExchangeDataProviderWin(IDataObject* source)
|
| : data_(new DataObjectImpl()),
|
| source_object_(source) {
|
| @@ -300,28 +316,7 @@
|
| }
|
|
|
| void OSExchangeDataProviderWin::SetFilename(const std::wstring& full_path) {
|
| - const size_t drop_size = sizeof(DROPFILES);
|
| - const size_t bytes = drop_size + (full_path.length() + 2) * sizeof(wchar_t);
|
| - HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
|
| - if (!hdata)
|
| - return;
|
| -
|
| - ScopedHGlobal<DROPFILES> locked_mem(hdata);
|
| - DROPFILES* drop_files = locked_mem.get();
|
| - drop_files->pFiles = sizeof(DROPFILES);
|
| - drop_files->fWide = TRUE;
|
| - wchar_t* data = reinterpret_cast<wchar_t*>((BYTE*)(drop_files) + drop_size);
|
| - const size_t copy_size = (full_path.length() + 1) * sizeof(wchar_t);
|
| - memcpy(data, full_path.c_str(), copy_size);
|
| - data[full_path.length() + 1] = L'\0'; // Double NULL
|
| -
|
| - // Set up the STGMEDIUM
|
| - STGMEDIUM* storage = new STGMEDIUM;
|
| - storage->tymed = TYMED_HGLOBAL;
|
| - storage->hGlobal = drop_files;
|
| - storage->pUnkForRelease = NULL;
|
| -
|
| - // Set up the StoredDataInfo
|
| + STGMEDIUM* storage = GetStorageForFileName(full_path);
|
| DataObjectImpl::StoredDataInfo* info =
|
| new DataObjectImpl::StoredDataInfo(CF_HDROP, storage);
|
| data_->contents_.push_back(info);
|
| @@ -347,7 +342,7 @@
|
| // Add CFSTR_FILECONTENTS
|
| storage = GetStorageForBytes(file_contents.data(), file_contents.length());
|
| data_->contents_.push_back(new DataObjectImpl::StoredDataInfo(
|
| - ClipboardUtil::GetFileContentFormatZero()->cfFormat, storage));
|
| + ClipboardUtil::GetFileContentFormatZero(), storage));
|
| }
|
|
|
| void OSExchangeDataProviderWin::SetHtml(const std::wstring& html,
|
| @@ -459,6 +454,21 @@
|
| return (source_object_->QueryGetData(&format_etc) == S_OK);
|
| }
|
|
|
| +void OSExchangeDataProviderWin::SetDownloadFileInfo(
|
| + OSExchangeData::DownloadFileInfo* download) {
|
| + // If the filename is not provided, set stoarge to NULL to indicate that
|
| + // the delay rendering will be used.
|
| + STGMEDIUM* storage = NULL;
|
| + if (!download->filename.empty())
|
| + storage = GetStorageForFileName(download->filename.value());
|
| +
|
| + // Add CF_HDROP.
|
| + DataObjectImpl::StoredDataInfo* info = new DataObjectImpl::StoredDataInfo(
|
| + ClipboardUtil::GetCFHDropFormat()->cfFormat, storage);
|
| + info->downloads.push_back(download);
|
| + data_->contents_.push_back(info);
|
| +}
|
| +
|
| ///////////////////////////////////////////////////////////////////////////////
|
| // DataObjectImpl, IDataObject implementation:
|
|
|
| @@ -533,19 +543,131 @@
|
| destination->pUnkForRelease->AddRef();
|
| }
|
|
|
| -DataObjectImpl::DataObjectImpl() : ref_count_(0) {
|
| - STLDeleteContainerPointers(contents_.begin(), contents_.end());
|
| +DataObjectImpl::DataObjectImpl()
|
| + : is_aborting_(false),
|
| + in_async_mode_(false),
|
| + async_operation_started_(false),
|
| + observer_(NULL) {
|
| }
|
|
|
| DataObjectImpl::~DataObjectImpl() {
|
| + StopDownloads();
|
| STLDeleteContainerPointers(contents_.begin(), contents_.end());
|
| + if (observer_)
|
| + observer_->OnDataObjectDisposed();
|
| }
|
|
|
| +void DataObjectImpl::StopDownloads() {
|
| + for (StoredData::iterator iter = contents_.begin();
|
| + iter != contents_.end(); ++iter) {
|
| + for (size_t i = 0; i < (*iter)->downloads.size(); ++i) {
|
| + if ((*iter)->downloads[i]->downloader) {
|
| + (*iter)->downloads[i]->downloader->Stop();
|
| + (*iter)->downloads[i]->downloader = 0;
|
| + }
|
| + delete (*iter)->downloads[i];
|
| + }
|
| + (*iter)->downloads.clear();
|
| + }
|
| +}
|
| +
|
| +void DataObjectImpl::OnDataReady(
|
| + int format,
|
| + const std::vector<OSExchangeData::DownloadFileInfo*>& downloads) {
|
| + // Find and update the data corresponding to the format.
|
| + CLIPFORMAT clip_format = static_cast<CLIPFORMAT>(format);
|
| + DCHECK(clip_format == ClipboardUtil::GetCFHDropFormat()->cfFormat);
|
| + DataObjectImpl::StoredData::iterator iter = contents_.begin();
|
| + for (; iter != contents_.end(); ++iter) {
|
| + if ((*iter)->format_etc.cfFormat == clip_format) {
|
| + // Update the downloads.
|
| + DCHECK(downloads.size() == (*iter)->downloads.size());
|
| + for (size_t i = 0; i < (*iter)->downloads.size(); ++i) {
|
| + OSExchangeData::DownloadFileInfo* old_download = (*iter)->downloads[i];
|
| + (*iter)->downloads[i] = downloads[i];
|
| + (*iter)->downloads[i]->downloader = old_download->downloader;
|
| + delete old_download;
|
| + }
|
| +
|
| + // Release the old storage.
|
| + if ((*iter)->owns_medium) {
|
| + ReleaseStgMedium((*iter)->medium);
|
| + delete (*iter)->medium;
|
| + }
|
| +
|
| + // Update the storage.
|
| + (*iter)->owns_medium = true;
|
| + (*iter)->medium = GetStorageForFileName(downloads[0]->filename.value());
|
| +
|
| + break;
|
| + }
|
| + }
|
| + DCHECK(iter != contents_.end());
|
| +}
|
| +
|
| HRESULT DataObjectImpl::GetData(FORMATETC* format_etc, STGMEDIUM* medium) {
|
| - StoredData::const_iterator iter = contents_.begin();
|
| + if (is_aborting_)
|
| + return DV_E_FORMATETC;
|
| +
|
| + StoredData::iterator iter = contents_.begin();
|
| while (iter != contents_.end()) {
|
| - if ((*iter)->format_etc.cfFormat == format_etc->cfFormat) {
|
| - DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium);
|
| + if ((*iter)->format_etc.cfFormat == format_etc->cfFormat &&
|
| + (*iter)->format_etc.lindex == format_etc->lindex &&
|
| + ((*iter)->format_etc.tymed & format_etc->tymed)) {
|
| + // If medium is NULL, delay-rendering will be used.
|
| + if ((*iter)->medium) {
|
| + DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium);
|
| + } else {
|
| + // Check if the left button is down.
|
| + bool is_left_button_down = (GetKeyState(VK_LBUTTON) & 0x8000) != 0;
|
| +
|
| + bool wait_for_data = false;
|
| + if ((*iter)->in_delay_rendering) {
|
| + // Make sure the left button is up. Sometimes the drop target, like
|
| + // Shell, might be too aggresive in calling GetData when the left
|
| + // button is not released.
|
| + if (is_left_button_down)
|
| + return DV_E_FORMATETC;
|
| +
|
| + wait_for_data = true;
|
| + } else {
|
| + // If the left button is up and the target has not requested the data
|
| + // yet, it probably means that the target does not support delay-
|
| + // rendering. So instead, we wait for the data.
|
| + if (is_left_button_down) {
|
| + (*iter)->in_delay_rendering = true;
|
| + memset(medium, 0, sizeof(STGMEDIUM));
|
| + } else {
|
| + wait_for_data = true;
|
| + }
|
| + }
|
| +
|
| + if (wait_for_data) {
|
| + // Notify the observer we start waiting for the data. This gives
|
| + // an observer a chance to end the drag and drop.
|
| + if (observer_)
|
| + observer_->OnWaitForData();
|
| +
|
| + // Now we can start the downloads. Each download will wait till the
|
| + // necessary data is ready and then return the control.
|
| + for (size_t i = 0; i < (*iter)->downloads.size(); ++i) {
|
| + if ((*iter)->downloads[i]->downloader) {
|
| + if (!(*iter)->downloads[i]->downloader->Start(
|
| + this, format_etc->cfFormat)) {
|
| + // If any of the download fails to start, abort the whole
|
| + // process.
|
| + is_aborting_ = true;
|
| + StopDownloads();
|
| + return DV_E_FORMATETC;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // The stored data should have been updated with the final version.
|
| + // So we just need to call this function again to retrieve it.
|
| + return GetData(format_etc, medium);
|
| + }
|
| + }
|
| return S_OK;
|
| }
|
| ++iter;
|
| @@ -619,13 +741,46 @@
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
| +// DataObjectImpl, IAsyncOperation implementation:
|
| +
|
| +HRESULT DataObjectImpl::EndOperation(
|
| + HRESULT result, IBindCtx* reserved, DWORD effects) {
|
| + async_operation_started_ = false;
|
| + return S_OK;
|
| +}
|
| +
|
| +HRESULT DataObjectImpl::GetAsyncMode(BOOL* is_op_async) {
|
| + *is_op_async = in_async_mode_ ? TRUE : FALSE;
|
| + return S_OK;
|
| +}
|
| +
|
| +HRESULT DataObjectImpl::InOperation(BOOL* in_async_op) {
|
| + *in_async_op = async_operation_started_ ? TRUE : FALSE;
|
| + return S_OK;
|
| +}
|
| +
|
| +HRESULT DataObjectImpl::SetAsyncMode(BOOL do_op_async) {
|
| + in_async_mode_ = (do_op_async == TRUE);
|
| + return S_OK;
|
| +}
|
| +
|
| +HRESULT DataObjectImpl::StartOperation(IBindCtx* reserved) {
|
| + async_operation_started_ = true;
|
| + return S_OK;
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| // DataObjectImpl, IUnknown implementation:
|
|
|
| HRESULT DataObjectImpl::QueryInterface(const IID& iid, void** object) {
|
| - *object = NULL;
|
| - if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDataObject)) {
|
| - *object = this;
|
| + if (!object)
|
| + return E_POINTER;
|
| + if (IsEqualIID(iid, IID_IDataObject) || IsEqualIID(iid, IID_IUnknown)) {
|
| + *object = static_cast<IDataObject*>(this);
|
| + } else if (in_async_mode_ && IsEqualIID(iid, IID_IAsyncOperation)) {
|
| + *object = static_cast<IAsyncOperation*>(this);
|
| } else {
|
| + *object = NULL;
|
| return E_NOINTERFACE;
|
| }
|
| AddRef();
|
| @@ -633,16 +788,13 @@
|
| }
|
|
|
| ULONG DataObjectImpl::AddRef() {
|
| - return InterlockedIncrement(&ref_count_);
|
| + base::RefCounted<OSExchangeData::DownloadFileObserver>::AddRef();
|
| + return 0;
|
| }
|
|
|
| ULONG DataObjectImpl::Release() {
|
| - if (InterlockedDecrement(&ref_count_) == 0) {
|
| - ULONG copied_refcnt = ref_count_;
|
| - delete this;
|
| - return copied_refcnt;
|
| - }
|
| - return ref_count_;
|
| + base::RefCounted<OSExchangeData::DownloadFileObserver>::Release();
|
| + return 0;
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
| @@ -721,6 +873,29 @@
|
| *validated += extension;
|
| }
|
|
|
| +static STGMEDIUM* GetStorageForFileName(const std::wstring& full_path) {
|
| + const size_t kDropSize = sizeof(DROPFILES);
|
| + const size_t kTotalBytes =
|
| + kDropSize + (full_path.length() + 2) * sizeof(wchar_t);
|
| + HANDLE hdata = GlobalAlloc(GMEM_MOVEABLE, kTotalBytes);
|
| +
|
| + ScopedHGlobal<DROPFILES> locked_mem(hdata);
|
| + DROPFILES* drop_files = locked_mem.get();
|
| + drop_files->pFiles = sizeof(DROPFILES);
|
| + drop_files->fWide = TRUE;
|
| + wchar_t* data = reinterpret_cast<wchar_t*>(
|
| + reinterpret_cast<BYTE*>(drop_files) + kDropSize);
|
| + const size_t copy_size = (full_path.length() + 1) * sizeof(wchar_t);
|
| + memcpy(data, full_path.c_str(), copy_size);
|
| + data[full_path.length() + 1] = L'\0'; // Double NULL
|
| +
|
| + STGMEDIUM* storage = new STGMEDIUM;
|
| + storage->tymed = TYMED_HGLOBAL;
|
| + storage->hGlobal = drop_files;
|
| + storage->pUnkForRelease = NULL;
|
| + return storage;
|
| +}
|
| +
|
| static STGMEDIUM* GetStorageForFileDescriptor(
|
| const std::wstring& valid_file_name) {
|
| DCHECK(!valid_file_name.empty() && valid_file_name.size() + 1 <= MAX_PATH);
|
|
|