Commit 967bfe85 authored by kinaba@chromium.org's avatar kinaba@chromium.org

gdata: Fix "save as pdf" to work on Google Drive in ChromeOS.

BUG=137411
TEST=unit_tests --gtest_filter='*GDataFileWritingTest*'
TEST=press Ctrl+P and then the save button, choose Google Drive, and verify it is correctly saved.

Review URL: https://chromiumcodereview.appspot.com/10827068

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149799 0039d316-1c4b-4281-b951-d872f2087c98
parent 12248936
// Copyright (c) 2012 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/chromeos/gdata/file_write_helper.h"
#include "base/threading/sequenced_worker_pool.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
namespace gdata {
FileWriteHelper::FileWriteHelper(GDataFileSystemInterface* file_system)
: file_system_(file_system),
weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
// Must be created in GDataSystemService.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
FileWriteHelper::~FileWriteHelper() {
// Must be destroyed in GDataSystemService.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
void FileWriteHelper::PrepareWritableFileAndRun(
const FilePath& file_path,
const OpenFileCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
file_system_->CreateFile(
file_path,
false, // it is not an error, even if the path already exists.
base::Bind(&FileWriteHelper::PrepareWritableFileAndRunAfterCreateFile,
weak_ptr_factory_.GetWeakPtr(),
file_path,
callback));
}
void FileWriteHelper::PrepareWritableFileAndRunAfterCreateFile(
const FilePath& file_path,
const OpenFileCallback& callback,
GDataFileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (error != gdata::GDATA_FILE_OK) {
if (!callback.is_null()) {
content::BrowserThread::GetBlockingPool()->PostTask(
FROM_HERE,
base::Bind(callback, error, FilePath()));
}
return;
}
file_system_->OpenFile(
file_path,
base::Bind(&FileWriteHelper::PrepareWritableFileAndRunAfterOpenFile,
weak_ptr_factory_.GetWeakPtr(),
file_path,
callback));
}
void FileWriteHelper::PrepareWritableFileAndRunAfterOpenFile(
const FilePath& file_path,
const OpenFileCallback& callback,
GDataFileError error,
const FilePath& local_cache_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (error != gdata::GDATA_FILE_OK) {
if (!callback.is_null()) {
content::BrowserThread::GetBlockingPool()->PostTask(
FROM_HERE,
base::Bind(callback, error, FilePath()));
}
return;
}
if (!callback.is_null()) {
content::BrowserThread::GetBlockingPool()->PostTaskAndReply(
FROM_HERE,
base::Bind(callback, GDATA_FILE_OK, local_cache_path),
base::Bind(&FileWriteHelper::PrepareWritableFileAndRunAfterCallback,
weak_ptr_factory_.GetWeakPtr(),
file_path));
} else {
PrepareWritableFileAndRunAfterCallback(file_path);
}
}
void FileWriteHelper::PrepareWritableFileAndRunAfterCallback(
const FilePath& file_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
file_system_->CloseFile(file_path, FileOperationCallback());
}
} // namespace gdata
// Copyright (c) 2012 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.
#ifndef CHROME_BROWSER_CHROMEOS_GDATA_FILE_WRITE_HELPER_H_
#define CHROME_BROWSER_CHROMEOS_GDATA_FILE_WRITE_HELPER_H_
#include "base/bind.h"
#include "base/file_path.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/gdata/gdata_errorcode.h"
#include "chrome/browser/chromeos/gdata/gdata_file_system_interface.h"
namespace gdata {
// This class provides higher level operations for writing to GData files over
// GDataFileSystemInterface.
class FileWriteHelper {
public:
explicit FileWriteHelper(GDataFileSystemInterface* file_system);
~FileWriteHelper();
// Prepares a local temporary file path and passes it to |callback| on the
// blocking thread pool that allows file operations. The modification to
// the file is reflected to GData |path|. If |path| does not exist, a new
// file is created.
//
// Must be called from UI thread.
void PrepareWritableFileAndRun(const FilePath& path,
const OpenFileCallback& callback);
private:
// Part of PrepareWritableFilePathAndRun(). It tries CreateFile for the case
// file does not exist yet, does OpenFile to download and mark the file as
// dirty, runs |callback|, and finally calls CloseFile.
void PrepareWritableFileAndRunAfterCreateFile(
const FilePath& file_path,
const OpenFileCallback& callback,
GDataFileError result);
void PrepareWritableFileAndRunAfterOpenFile(
const FilePath& file_path,
const OpenFileCallback& callback,
GDataFileError result,
const FilePath& local_cache_path);
void PrepareWritableFileAndRunAfterCallback(const FilePath& file_path);
// File system owned by GDataSystemService.
GDataFileSystemInterface* file_system_;
// WeakPtrFactory bound to the UI thread.
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<FileWriteHelper> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(FileWriteHelper);
};
} // namespace gdata
#endif // CHROME_BROWSER_CHROMEOS_GDATA_FILE_WRITE_HELPER_H_
// Copyright (c) 2012 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/chromeos/gdata/file_write_helper.h"
#include "base/bind.h"
#include "base/message_loop.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/chromeos/gdata/gdata_test_util.h"
#include "chrome/browser/chromeos/gdata/mock_gdata_file_system.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::StrictMock;
using ::testing::_;
namespace gdata {
namespace {
ACTION_P(MockCreateFile, error) {
if (!arg2.is_null())
arg2.Run(error);
}
ACTION_P2(MockOpenFile, error, local_path) {
if (!arg1.is_null())
arg1.Run(error, local_path);
}
ACTION_P(MockCloseFile, error) {
if (!arg1.is_null())
arg1.Run(error);
}
void RecordOpenFileCallbackArguments(GDataFileError* error,
FilePath* path,
GDataFileError error_arg,
const FilePath& path_arg) {
base::ThreadRestrictions::AssertIOAllowed();
*error = error_arg;
*path = path_arg;
}
} // namespace
class FileWriteHelperTest : public testing::Test {
public:
FileWriteHelperTest()
: ui_thread_(content::BrowserThread::UI, &message_loop_),
mock_file_system_(new StrictMock<MockGDataFileSystem>) {
}
protected:
MessageLoopForUI message_loop_;
content::TestBrowserThread ui_thread_;
scoped_ptr< StrictMock<MockGDataFileSystem> > mock_file_system_;
};
TEST_F(FileWriteHelperTest, PrepareFileForWritingSuccess) {
const FilePath kDrivePath("/drive/file.txt");
const FilePath kLocalPath("/tmp/dummy.txt");
EXPECT_CALL(*mock_file_system_, CreateFile(kDrivePath, false, _))
.WillOnce(MockCreateFile(GDATA_FILE_OK));
EXPECT_CALL(*mock_file_system_, OpenFile(kDrivePath, _))
.WillOnce(MockOpenFile(GDATA_FILE_OK, kLocalPath));
EXPECT_CALL(*mock_file_system_, CloseFile(kDrivePath, _))
.WillOnce(MockCloseFile(GDATA_FILE_OK));
FileWriteHelper file_write_helper(mock_file_system_.get());
GDataFileError error = GDATA_FILE_ERROR_FAILED;
FilePath path;
file_write_helper.PrepareWritableFileAndRun(
kDrivePath, base::Bind(&RecordOpenFileCallbackArguments, &error, &path));
test_util::RunBlockingPoolTask();
EXPECT_EQ(GDATA_FILE_OK, error);
EXPECT_EQ(kLocalPath, path);
}
TEST_F(FileWriteHelperTest, PrepareFileForWritingCreateFail) {
const FilePath kDrivePath("/drive/file.txt");
EXPECT_CALL(*mock_file_system_, CreateFile(kDrivePath, false, _))
.WillOnce(MockCreateFile(GDATA_FILE_ERROR_ACCESS_DENIED));
EXPECT_CALL(*mock_file_system_, OpenFile(_, _)).Times(0);
EXPECT_CALL(*mock_file_system_, CloseFile(_, _)).Times(0);
FileWriteHelper file_write_helper(mock_file_system_.get());
GDataFileError error = GDATA_FILE_ERROR_FAILED;
FilePath path;
file_write_helper.PrepareWritableFileAndRun(
kDrivePath, base::Bind(&RecordOpenFileCallbackArguments, &error, &path));
test_util::RunBlockingPoolTask();
EXPECT_EQ(GDATA_FILE_ERROR_ACCESS_DENIED, error);
EXPECT_EQ(FilePath(), path);
}
TEST_F(FileWriteHelperTest, PrepareFileForWritingOpenFail) {
const FilePath kDrivePath("/drive/file.txt");
EXPECT_CALL(*mock_file_system_, CreateFile(kDrivePath, false, _))
.WillOnce(MockCreateFile(GDATA_FILE_OK));
EXPECT_CALL(*mock_file_system_, OpenFile(kDrivePath, _))
.WillOnce(MockOpenFile(GDATA_FILE_ERROR_IN_USE, FilePath()));
EXPECT_CALL(*mock_file_system_, CloseFile(_, _)).Times(0);
FileWriteHelper file_write_helper(mock_file_system_.get());
GDataFileError error = GDATA_FILE_ERROR_FAILED;
FilePath path;
file_write_helper.PrepareWritableFileAndRun(
kDrivePath, base::Bind(&RecordOpenFileCallbackArguments, &error, &path));
test_util::RunBlockingPoolTask();
EXPECT_EQ(GDATA_FILE_ERROR_IN_USE, error);
EXPECT_EQ(FilePath(), path);
}
} // namespace gdata
......@@ -8,6 +8,7 @@
#include "base/bind_helpers.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/gdata/drive_webapps_registry.h"
#include "chrome/browser/chromeos/gdata/file_write_helper.h"
#include "chrome/browser/chromeos/gdata/gdata_contacts_service.h"
#include "chrome/browser/chromeos/gdata/gdata_documents_service.h"
#include "chrome/browser/chromeos/gdata/gdata_download_observer.h"
......@@ -68,6 +69,7 @@ void GDataSystemService::Initialize(
uploader(),
webapps_registry(),
blocking_task_runner_));
file_write_helper_.reset(new FileWriteHelper(file_system()));
download_observer_.reset(new GDataDownloadObserver(uploader(),
file_system()));
sync_client_.reset(new GDataSyncClient(profile_, file_system(), cache()));
......@@ -97,6 +99,7 @@ void GDataSystemService::Shutdown() {
contacts_service_.reset();
sync_client_.reset();
download_observer_.reset();
file_write_helper_.reset();
file_system_.reset();
webapps_registry_.reset();
uploader_.reset();
......
......@@ -19,6 +19,7 @@ namespace gdata {
class DocumentsServiceInterface;
class DriveWebAppsRegistry;
class FileWriteHelper;
class GDataCache;
class GDataContactsService;
class GDataDownloadObserver;
......@@ -37,6 +38,7 @@ class GDataSystemService : public ProfileKeyedService {
DocumentsServiceInterface* docs_service() { return documents_service_.get(); }
GDataCache* cache() { return cache_; }
GDataFileSystemInterface* file_system() { return file_system_.get(); }
FileWriteHelper* file_write_helper() { return file_write_helper_.get(); }
GDataUploader* uploader() { return uploader_.get(); }
GDataContactsService* contacts_service() { return contacts_service_.get(); }
DriveWebAppsRegistry* webapps_registry() { return webapps_registry_.get(); }
......@@ -67,6 +69,7 @@ class GDataSystemService : public ProfileKeyedService {
scoped_ptr<GDataUploader> uploader_;
scoped_ptr<DriveWebAppsRegistry> webapps_registry_;
scoped_ptr<GDataFileSystemInterface> file_system_;
scoped_ptr<FileWriteHelper> file_write_helper_;
scoped_ptr<GDataDownloadObserver> download_observer_;
scoped_ptr<GDataSyncClient> sync_client_;
scoped_ptr<GDataContactsService> contacts_service_;
......
......@@ -21,6 +21,7 @@
#include "base/stringprintf.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/time.h"
#include "chrome/browser/chromeos/gdata/file_write_helper.h"
#include "chrome/browser/chromeos/gdata/gdata.pb.h"
#include "chrome/browser/chromeos/gdata/gdata_file_system_interface.h"
#include "chrome/browser/chromeos/gdata/gdata_system_service.h"
......@@ -71,6 +72,12 @@ GDataCache* GetGDataCache(Profile* profile) {
return system_service ? system_service->cache() : NULL;
}
FileWriteHelper* GetFileWriteHelper(Profile* profile) {
GDataSystemService* system_service =
GDataSystemServiceFactory::GetForProfile(profile);
return system_service ? system_service->file_write_helper() : NULL;
}
void GetHostedDocumentURLBlockingThread(const FilePath& gdata_cache_path,
GURL* url) {
std::string json;
......@@ -579,5 +586,23 @@ std::string FormatTimeAsString(const base::Time& time) {
exploded.hour, exploded.minute, exploded.second, exploded.millisecond);
}
void PrepareWritableFileAndRun(Profile* profile,
const FilePath& path,
const OpenFileCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (IsUnderGDataMountPoint(path)) {
FileWriteHelper* file_write_helper = GetFileWriteHelper(profile);
if (!file_write_helper)
return;
FilePath remote_path(ExtractGDataPath(path));
file_write_helper->PrepareWritableFileAndRun(remote_path, callback);
} else {
if (!callback.is_null()) {
content::BrowserThread::GetBlockingPool()->PostTask(
FROM_HERE, base::Bind(callback, GDATA_FILE_OK, path));
}
}
}
} // namespace util
} // namespace gdata
......@@ -110,6 +110,22 @@ bool GetTimeFromString(const base::StringPiece& raw_value, base::Time* time);
// Formats a base::Time as an RFC 3339 date/time (in UTC).
std::string FormatTimeAsString(const base::Time& time);
// Callback type for PrepareWritableFilePathAndRun.
typedef base::Callback<void (GDataFileError, const FilePath& path)>
OpenFileCallback;
// Invokes |callback| on blocking thread pool, after converting virtual |path|
// string like "/special/drive/foo.txt" to the concrete local cache file path.
// After |callback| returns, the written content is synchronized to the server.
//
// If |path| is not a GData path, it is regarded as a local path and no path
// conversion takes place.
//
// Must be called from UI thread.
void PrepareWritableFileAndRun(Profile* profile,
const FilePath& path,
const OpenFileCallback& callback);
} // namespace util
} // namespace gdata
......
......@@ -59,6 +59,12 @@
#include "printing/print_settings.h"
#include "unicode/ulocdata.h"
#ifdef OS_CHROMEOS
// TODO(kinaba): provide more non-intrusive way for handling local/remote
// distinction and remove these ugly #ifdef's. http://crbug.com/140425
#include "chrome/browser/chromeos/gdata/gdata_util.h"
#endif
#if !defined(OS_MACOSX)
#include "base/command_line.h"
#include "chrome/common/chrome_switches.h"
......@@ -122,7 +128,7 @@ void ReportPrintDestinationHistogram(enum PrintDestinationBuckets event) {
PRINT_DESTINATION_BUCKET_BOUNDARY);
}
// Name of a dictionary fielad holdong cloud print related data;
// Name of a dictionary field holding cloud print related data;
const char kCloudPrintData[] = "cloudPrintData";
// Name of a dictionary field holding the initiator tab title.
const char kInitiatorTabTitle[] = "initiatorTabTitle";
......@@ -213,6 +219,22 @@ void PrintToPdfCallback(Metafile* metafile, const FilePath& path) {
base::Bind(&base::DeletePointer<Metafile>, metafile));
}
#ifdef OS_CHROMEOS
void PrintToPdfCallbackWithCheck(Metafile* metafile,
gdata::GDataFileError error,
const FilePath& path) {
if (error != gdata::GDATA_FILE_OK) {
LOG(ERROR) << "Save to pdf failed to write: " << error;
} else {
metafile->SaveTo(path);
}
// |metafile| must be deleted on the UI thread.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&base::DeletePointer<Metafile>, metafile));
}
#endif
static base::LazyInstance<printing::StickySettings> sticky_settings =
LAZY_INSTANCE_INITIALIZER;
......@@ -911,9 +933,18 @@ void PrintPreviewHandler::PostPrintToPdfTask(base::RefCountedBytes* data) {
printing::PreviewMetafile* metafile = new printing::PreviewMetafile;
metafile->InitFromData(static_cast<const void*>(data->front()), data->size());
// PrintToPdfCallback takes ownership of |metafile|.
#ifdef OS_CHROMEOS
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
gdata::util::PrepareWritableFileAndRun(
Profile::FromBrowserContext(preview_web_contents()->GetBrowserContext()),
*print_to_pdf_path_,
base::Bind(&PrintToPdfCallbackWithCheck, metafile));
#else
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&PrintToPdfCallback, metafile,
*print_to_pdf_path_));
#endif
print_to_pdf_path_.reset();
ActivateInitiatorTabAndClosePreviewTab();
}
......
......@@ -544,6 +544,8 @@
'browser/chromeos/gdata/drive_task_executor.h',
'browser/chromeos/gdata/drive_webapps_registry.cc',
'browser/chromeos/gdata/drive_webapps_registry.h',
'browser/chromeos/gdata/file_write_helper.cc',
'browser/chromeos/gdata/file_write_helper.h',
'browser/chromeos/gdata/gdata_auth_service.cc',
'browser/chromeos/gdata/gdata_auth_service.h',
'browser/chromeos/gdata/gdata_cache.cc',
......
......@@ -1102,6 +1102,7 @@
'browser/chromeos/external_metrics_unittest.cc',
'browser/chromeos/gdata/drive_api_parser_unittest.cc',
'browser/chromeos/gdata/drive_webapps_registry_unittest.cc',
'browser/chromeos/gdata/file_write_helper_unittest.cc',
'browser/chromeos/gdata/gdata_cache_metadata_unittest.cc',
'browser/chromeos/gdata/gdata_cache_unittest.cc',
'browser/chromeos/gdata/gdata_contacts_service_stub.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment