// 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 #include #include #include "include/cef_download_handler.h" #include "include/cef_runnable.h" #include "cefclient/util.h" #if defined(OS_WIN) #include // NOLINT(build/include_order) #include // NOLINT(build/include_order) #include // NOLINT(build/include_order) #endif // OS_WIN // Implementation of the CefDownloadHandler interface. class ClientDownloadHandler : public CefDownloadHandler { public: ClientDownloadHandler(CefRefPtr 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*>::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* buffer = new std::vector(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*> 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*>::iterator it = data.begin(); for (; it != data.end(); ++it) { std::vector* 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 listener_; CefString filename_; FILE* file_; std::vector*> pending_data_; IMPLEMENT_REFCOUNTING(ClientDownloadHandler); IMPLEMENT_LOCKING(ClientDownloadHandler); }; CefRefPtr CreateDownloadHandler( CefRefPtr listener, const CefString& fileName) { CefRefPtr handler = new ClientDownloadHandler(listener, fileName); handler->Initialize(); return handler.get(); }