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 @@ ...@@ -8,6 +8,7 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/gdata/drive_webapps_registry.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_contacts_service.h"
#include "chrome/browser/chromeos/gdata/gdata_documents_service.h" #include "chrome/browser/chromeos/gdata/gdata_documents_service.h"
#include "chrome/browser/chromeos/gdata/gdata_download_observer.h" #include "chrome/browser/chromeos/gdata/gdata_download_observer.h"
...@@ -68,6 +69,7 @@ void GDataSystemService::Initialize( ...@@ -68,6 +69,7 @@ void GDataSystemService::Initialize(
uploader(), uploader(),
webapps_registry(), webapps_registry(),
blocking_task_runner_)); blocking_task_runner_));
file_write_helper_.reset(new FileWriteHelper(file_system()));
download_observer_.reset(new GDataDownloadObserver(uploader(), download_observer_.reset(new GDataDownloadObserver(uploader(),
file_system())); file_system()));
sync_client_.reset(new GDataSyncClient(profile_, file_system(), cache())); sync_client_.reset(new GDataSyncClient(profile_, file_system(), cache()));
...@@ -97,6 +99,7 @@ void GDataSystemService::Shutdown() { ...@@ -97,6 +99,7 @@ void GDataSystemService::Shutdown() {
contacts_service_.reset(); contacts_service_.reset();
sync_client_.reset(); sync_client_.reset();
download_observer_.reset(); download_observer_.reset();
file_write_helper_.reset();
file_system_.reset(); file_system_.reset();
webapps_registry_.reset(); webapps_registry_.reset();
uploader_.reset(); uploader_.reset();
......
...@@ -19,6 +19,7 @@ namespace gdata { ...@@ -19,6 +19,7 @@ namespace gdata {
class DocumentsServiceInterface; class DocumentsServiceInterface;
class DriveWebAppsRegistry; class DriveWebAppsRegistry;
class FileWriteHelper;
class GDataCache; class GDataCache;
class GDataContactsService; class GDataContactsService;
class GDataDownloadObserver; class GDataDownloadObserver;
...@@ -37,6 +38,7 @@ class GDataSystemService : public ProfileKeyedService { ...@@ -37,6 +38,7 @@ class GDataSystemService : public ProfileKeyedService {
DocumentsServiceInterface* docs_service() { return documents_service_.get(); } DocumentsServiceInterface* docs_service() { return documents_service_.get(); }
GDataCache* cache() { return cache_; } GDataCache* cache() { return cache_; }
GDataFileSystemInterface* file_system() { return file_system_.get(); } GDataFileSystemInterface* file_system() { return file_system_.get(); }
FileWriteHelper* file_write_helper() { return file_write_helper_.get(); }
GDataUploader* uploader() { return uploader_.get(); } GDataUploader* uploader() { return uploader_.get(); }
GDataContactsService* contacts_service() { return contacts_service_.get(); } GDataContactsService* contacts_service() { return contacts_service_.get(); }
DriveWebAppsRegistry* webapps_registry() { return webapps_registry_.get(); } DriveWebAppsRegistry* webapps_registry() { return webapps_registry_.get(); }
...@@ -67,6 +69,7 @@ class GDataSystemService : public ProfileKeyedService { ...@@ -67,6 +69,7 @@ class GDataSystemService : public ProfileKeyedService {
scoped_ptr<GDataUploader> uploader_; scoped_ptr<GDataUploader> uploader_;
scoped_ptr<DriveWebAppsRegistry> webapps_registry_; scoped_ptr<DriveWebAppsRegistry> webapps_registry_;
scoped_ptr<GDataFileSystemInterface> file_system_; scoped_ptr<GDataFileSystemInterface> file_system_;
scoped_ptr<FileWriteHelper> file_write_helper_;
scoped_ptr<GDataDownloadObserver> download_observer_; scoped_ptr<GDataDownloadObserver> download_observer_;
scoped_ptr<GDataSyncClient> sync_client_; scoped_ptr<GDataSyncClient> sync_client_;
scoped_ptr<GDataContactsService> contacts_service_; scoped_ptr<GDataContactsService> contacts_service_;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "base/threading/sequenced_worker_pool.h" #include "base/threading/sequenced_worker_pool.h"
#include "base/time.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.pb.h"
#include "chrome/browser/chromeos/gdata/gdata_file_system_interface.h" #include "chrome/browser/chromeos/gdata/gdata_file_system_interface.h"
#include "chrome/browser/chromeos/gdata/gdata_system_service.h" #include "chrome/browser/chromeos/gdata/gdata_system_service.h"
...@@ -71,6 +72,12 @@ GDataCache* GetGDataCache(Profile* profile) { ...@@ -71,6 +72,12 @@ GDataCache* GetGDataCache(Profile* profile) {
return system_service ? system_service->cache() : NULL; 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, void GetHostedDocumentURLBlockingThread(const FilePath& gdata_cache_path,
GURL* url) { GURL* url) {
std::string json; std::string json;
...@@ -579,5 +586,23 @@ std::string FormatTimeAsString(const base::Time& time) { ...@@ -579,5 +586,23 @@ std::string FormatTimeAsString(const base::Time& time) {
exploded.hour, exploded.minute, exploded.second, exploded.millisecond); 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 util
} // namespace gdata } // namespace gdata
...@@ -110,6 +110,22 @@ bool GetTimeFromString(const base::StringPiece& raw_value, base::Time* time); ...@@ -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). // Formats a base::Time as an RFC 3339 date/time (in UTC).
std::string FormatTimeAsString(const base::Time& time); 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 util
} // namespace gdata } // namespace gdata
......
...@@ -59,6 +59,12 @@ ...@@ -59,6 +59,12 @@
#include "printing/print_settings.h" #include "printing/print_settings.h"
#include "unicode/ulocdata.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) #if !defined(OS_MACOSX)
#include "base/command_line.h" #include "base/command_line.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
...@@ -122,7 +128,7 @@ void ReportPrintDestinationHistogram(enum PrintDestinationBuckets event) { ...@@ -122,7 +128,7 @@ void ReportPrintDestinationHistogram(enum PrintDestinationBuckets event) {
PRINT_DESTINATION_BUCKET_BOUNDARY); 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"; const char kCloudPrintData[] = "cloudPrintData";
// Name of a dictionary field holding the initiator tab title. // Name of a dictionary field holding the initiator tab title.
const char kInitiatorTabTitle[] = "initiatorTabTitle"; const char kInitiatorTabTitle[] = "initiatorTabTitle";
...@@ -213,6 +219,22 @@ void PrintToPdfCallback(Metafile* metafile, const FilePath& path) { ...@@ -213,6 +219,22 @@ void PrintToPdfCallback(Metafile* metafile, const FilePath& path) {
base::Bind(&base::DeletePointer<Metafile>, metafile)); 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 = static base::LazyInstance<printing::StickySettings> sticky_settings =
LAZY_INSTANCE_INITIALIZER; LAZY_INSTANCE_INITIALIZER;
...@@ -911,9 +933,18 @@ void PrintPreviewHandler::PostPrintToPdfTask(base::RefCountedBytes* data) { ...@@ -911,9 +933,18 @@ void PrintPreviewHandler::PostPrintToPdfTask(base::RefCountedBytes* data) {
printing::PreviewMetafile* metafile = new printing::PreviewMetafile; printing::PreviewMetafile* metafile = new printing::PreviewMetafile;
metafile->InitFromData(static_cast<const void*>(data->front()), data->size()); metafile->InitFromData(static_cast<const void*>(data->front()), data->size());
// PrintToPdfCallback takes ownership of |metafile|. // 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, BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&PrintToPdfCallback, metafile, base::Bind(&PrintToPdfCallback, metafile,
*print_to_pdf_path_)); *print_to_pdf_path_));
#endif
print_to_pdf_path_.reset(); print_to_pdf_path_.reset();
ActivateInitiatorTabAndClosePreviewTab(); ActivateInitiatorTabAndClosePreviewTab();
} }
......
...@@ -544,6 +544,8 @@ ...@@ -544,6 +544,8 @@
'browser/chromeos/gdata/drive_task_executor.h', 'browser/chromeos/gdata/drive_task_executor.h',
'browser/chromeos/gdata/drive_webapps_registry.cc', 'browser/chromeos/gdata/drive_webapps_registry.cc',
'browser/chromeos/gdata/drive_webapps_registry.h', '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.cc',
'browser/chromeos/gdata/gdata_auth_service.h', 'browser/chromeos/gdata/gdata_auth_service.h',
'browser/chromeos/gdata/gdata_cache.cc', 'browser/chromeos/gdata/gdata_cache.cc',
......
...@@ -1102,6 +1102,7 @@ ...@@ -1102,6 +1102,7 @@
'browser/chromeos/external_metrics_unittest.cc', 'browser/chromeos/external_metrics_unittest.cc',
'browser/chromeos/gdata/drive_api_parser_unittest.cc', 'browser/chromeos/gdata/drive_api_parser_unittest.cc',
'browser/chromeos/gdata/drive_webapps_registry_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_metadata_unittest.cc',
'browser/chromeos/gdata/gdata_cache_unittest.cc', 'browser/chromeos/gdata/gdata_cache_unittest.cc',
'browser/chromeos/gdata/gdata_contacts_service_stub.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