cef/cef1/tests/cefclient/download_handler.cpp

219 lines
5.8 KiB
C++

// Copyright (c) 2010 The Chromium Embedded Framework 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 "cefclient/download_handler.h"
#include <stdio.h>
#include <sstream>
#include <vector>
#include "include/cef_download_handler.h"
#include "include/cef_runnable.h"
#include "cefclient/util.h"
#if defined(OS_WIN)
#include <windows.h> // NOLINT(build/include_order)
#include <shlobj.h> // NOLINT(build/include_order)
#include <shlwapi.h> // NOLINT(build/include_order)
#endif // OS_WIN
// Implementation of the CefDownloadHandler interface.
class ClientDownloadHandler : public CefDownloadHandler {
public:
ClientDownloadHandler(CefRefPtr<DownloadListener> listener,
const CefString& fileName)
: listener_(listener), filename_(fileName), file_(NULL) {
}
~ClientDownloadHandler() {
ASSERT(pending_data_.empty());
ASSERT(file_ == NULL);
if (!pending_data_.empty()) {
// Delete remaining pending data.
std::vector<std::vector<char>*>::iterator it = pending_data_.begin();
for (; it != pending_data_.end(); ++it)
delete (*it);
}
if (file_) {
// Close the dangling file pointer on the FILE thread.
CefPostTask(TID_FILE,
NewCefRunnableFunction(&ClientDownloadHandler::CloseDanglingFile,
file_));
// Notify the listener that the download failed.
listener_->NotifyDownloadError(filename_);
}
}
// --------------------------------------------------
// The following methods are called on the UI thread.
// --------------------------------------------------
void Initialize() {
// Open the file on the FILE thread.
CefPostTask(TID_FILE,
NewCefRunnableMethod(this, &ClientDownloadHandler::OnOpen));
}
// A portion of the file contents have been received. This method will be
// called multiple times until the download is complete. Return |true| to
// continue receiving data and |false| to cancel.
virtual bool ReceivedData(void* data, int data_size) {
REQUIRE_UI_THREAD();
if (data_size == 0)
return true;
// Create a new vector for the data.
std::vector<char>* buffer = new std::vector<char>(data_size);
memcpy(&(*buffer)[0], data, data_size);
// Add the new data vector to the pending data queue.
{
AutoLock lock_scope(this);
pending_data_.push_back(buffer);
}
// Write data to file on the FILE thread.
CefPostTask(TID_FILE,
NewCefRunnableMethod(this, &ClientDownloadHandler::OnReceivedData));
return true;
}
// The download is complete.
virtual void Complete() {
REQUIRE_UI_THREAD();
// Flush and close the file on the FILE thread.
CefPostTask(TID_FILE,
NewCefRunnableMethod(this, &ClientDownloadHandler::OnComplete));
}
// ----------------------------------------------------
// The following methods are called on the FILE thread.
// ----------------------------------------------------
void OnOpen() {
REQUIRE_FILE_THREAD();
if (file_)
return;
#if defined(OS_WIN)
TCHAR szFolderPath[MAX_PATH];
// Save the file in the user's "My Documents" folder.
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE,
NULL, 0, szFolderPath))) {
std::wstring fileNameStr = filename_;
LPWSTR name = PathFindFileName(fileNameStr.c_str());
LPWSTR ext = PathFindExtension(fileNameStr.c_str());
int ct = 0;
std::wstringstream ss;
if (ext) {
name[ext-name] = 0;
ext++;
}
// Make sure the file name is unique.
do {
if (ct > 0)
ss.str(L"");
ss << szFolderPath << L"\\" << name;
if (ct > 0)
ss << L" (" << ct << L")";
if (ext)
ss << L"." << ext;
ct++;
} while (PathFileExists(ss.str().c_str()));
{
AutoLock lock_scope(this);
filename_ = ss.str();
}
file_ = _wfopen(ss.str().c_str(), L"wb");
ASSERT(file_ != NULL);
}
#else
// TODO(port): Implement this.
ASSERT(false); // Not implemented
#endif
}
void OnComplete() {
REQUIRE_FILE_THREAD();
if (!file_)
return;
// Make sure any pending data is written.
OnReceivedData();
fclose(file_);
file_ = NULL;
// Notify the listener that the download completed.
listener_->NotifyDownloadComplete(filename_);
}
void OnReceivedData() {
REQUIRE_FILE_THREAD();
std::vector<std::vector<char>*> data;
// Remove all data from the pending data queue.
{
AutoLock lock_scope(this);
if (!pending_data_.empty()) {
data = pending_data_;
pending_data_.clear();
}
}
if (data.empty())
return;
// Write all pending data to file.
std::vector<std::vector<char>*>::iterator it = data.begin();
for (; it != data.end(); ++it) {
std::vector<char>* buffer = *it;
if (file_) {
size_t total = 0;
do {
size_t write =
fwrite(&(*buffer)[total], 1, buffer->size() - total, file_);
if (write == 0)
break;
total += write;
} while (total < buffer->size());
}
delete buffer;
}
data.clear();
}
static void CloseDanglingFile(FILE *file) {
fclose(file);
}
private:
CefRefPtr<DownloadListener> listener_;
CefString filename_;
FILE* file_;
std::vector<std::vector<char>*> pending_data_;
IMPLEMENT_REFCOUNTING(ClientDownloadHandler);
IMPLEMENT_LOCKING(ClientDownloadHandler);
};
CefRefPtr<CefDownloadHandler> CreateDownloadHandler(
CefRefPtr<DownloadListener> listener, const CefString& fileName) {
CefRefPtr<ClientDownloadHandler> handler =
new ClientDownloadHandler(listener, fileName);
handler->Initialize();
return handler.get();
}