file_system.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/drive/file_system.h"
6
7#include "base/bind.h"
8#include "base/file_util.h"
9#include "base/message_loop_proxy.h"
10#include "base/metrics/histogram.h"
11#include "base/platform_file.h"
12#include "base/prefs/pref_change_registrar.h"
13#include "base/prefs/pref_service.h"
14#include "base/stringprintf.h"
15#include "base/threading/sequenced_worker_pool.h"
16#include "chrome/browser/chromeos/drive/change_list_loader.h"
17#include "chrome/browser/chromeos/drive/change_list_processor.h"
18#include "chrome/browser/chromeos/drive/drive.pb.h"
19#include "chrome/browser/chromeos/drive/file_cache.h"
20#include "chrome/browser/chromeos/drive/file_system_observer.h"
21#include "chrome/browser/chromeos/drive/file_system_util.h"
22#include "chrome/browser/chromeos/drive/job_scheduler.h"
23#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
24#include "chrome/browser/chromeos/drive/search_metadata.h"
25#include "chrome/browser/google_apis/drive_api_parser.h"
26#include "chrome/browser/google_apis/drive_api_util.h"
27#include "chrome/browser/google_apis/drive_service_interface.h"
28#include "chrome/browser/profiles/profile.h"
29#include "chrome/common/chrome_notification_types.h"
30#include "chrome/common/pref_names.h"
31#include "content/public/browser/browser_thread.h"
32#include "content/public/browser/notification_details.h"
33
34using content::BrowserThread;
35
36namespace drive {
37namespace {
38
39const char kMimeTypeJson[] = "application/json";
40
41//================================ Helper functions ============================
42
43// Creates a temporary JSON file representing a document with |edit_url|
44// and |resource_id| under |document_dir| on blocking pool.
45FileError CreateDocumentJsonFileOnBlockingPool(
46    const base::FilePath& document_dir,
47    const GURL& edit_url,
48    const std::string& resource_id,
49    base::FilePath* temp_file_path) {
50  DCHECK(temp_file_path);
51
52  FileError error = FILE_ERROR_FAILED;
53
54  if (file_util::CreateTemporaryFileInDir(document_dir, temp_file_path)) {
55    std::string document_content = base::StringPrintf(
56        "{\"url\": \"%s\", \"resource_id\": \"%s\"}",
57        edit_url.spec().c_str(), resource_id.c_str());
58    int document_size = static_cast<int>(document_content.size());
59    if (file_util::WriteFile(*temp_file_path, document_content.data(),
60                             document_size) == document_size) {
61      error = FILE_ERROR_OK;
62    }
63  }
64
65  if (error != FILE_ERROR_OK)
66    temp_file_path->clear();
67  return error;
68}
69
70// Helper function for binding |path| to GetEntryInfoWithFilePathCallback and
71// create GetEntryInfoCallback.
72void RunGetEntryInfoWithFilePathCallback(
73    const GetEntryInfoWithFilePathCallback& callback,
74    const base::FilePath& path,
75    FileError error,
76    scoped_ptr<ResourceEntry> entry) {
77  DCHECK(!callback.is_null());
78  callback.Run(error, path, entry.Pass());
79}
80
81// Callback for ResourceMetadata::GetLargestChangestamp.
82// |callback| must not be null.
83void OnGetLargestChangestamp(
84    FileSystemMetadata metadata,  // Will be modified.
85    const GetFilesystemMetadataCallback& callback,
86    int64 largest_changestamp) {
87  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
88  DCHECK(!callback.is_null());
89
90  metadata.largest_changestamp = largest_changestamp;
91  callback.Run(metadata);
92}
93
94// Thin adapter to map GetFileCallback to FileOperationCallback.
95void GetFileCallbackToFileOperationCallbackAdapter(
96    const FileOperationCallback& callback,
97    FileError error,
98    const base::FilePath& unused_file_path,
99    const std::string& unused_mime_type,
100    DriveFileType unused_file_type) {
101  callback.Run(error);
102}
103
104// Wraps |callback| and always passes FILE_ERROR_OK when invoked.
105// This is used for adopting |callback| to wait for a non-fatal operation.
106void IgnoreError(const FileOperationCallback& callback, FileError error) {
107  callback.Run(FILE_ERROR_OK);
108}
109
110// Creates a file with unique name in |dir| and stores the path to |temp_file|.
111// Additionally, sets the permission of the file to allow read access from
112// others and group member users (i.e, "-rw-r--r--").
113// We need this wrapper because Drive cache files may be read from other
114// processes (e.g., cros_disks for mounting zip files).
115//
116// Must be called on the blocking pool.
117bool CreateTemporaryReadableFileInDir(const base::FilePath& dir,
118                                      base::FilePath* temp_file) {
119  if (!file_util::CreateTemporaryFileInDir(dir, temp_file))
120    return false;
121  return file_util::SetPosixFilePermissions(
122      *temp_file,
123      file_util::FILE_PERMISSION_READ_BY_USER |
124      file_util::FILE_PERMISSION_WRITE_BY_USER |
125      file_util::FILE_PERMISSION_READ_BY_GROUP |
126      file_util::FILE_PERMISSION_READ_BY_OTHERS);
127}
128
129}  // namespace
130
131// FileSystem::GetFileCompleteForOpenParams struct implementation.
132struct FileSystem::GetFileCompleteForOpenParams {
133  GetFileCompleteForOpenParams(const OpenFileCallback& callback,
134                               const std::string& resource_id,
135                               const std::string& md5);
136  OpenFileCallback callback;
137  std::string resource_id;
138  std::string md5;
139};
140
141FileSystem::GetFileCompleteForOpenParams::GetFileCompleteForOpenParams(
142    const OpenFileCallback& callback,
143    const std::string& resource_id,
144    const std::string& md5)
145    : callback(callback),
146      resource_id(resource_id),
147      md5(md5) {
148}
149
150// FileSystem::GetResolvedFileParams struct implementation.
151struct FileSystem::GetResolvedFileParams {
152  GetResolvedFileParams(
153      const base::FilePath& drive_file_path,
154      const DriveClientContext& context,
155      scoped_ptr<ResourceEntry> entry,
156      const GetFileContentInitializedCallback& initialized_callback,
157      const GetFileCallback& get_file_callback,
158      const google_apis::GetContentCallback& get_content_callback)
159      : drive_file_path(drive_file_path),
160        context(context),
161        entry(entry.Pass()),
162        initialized_callback(initialized_callback),
163        get_file_callback(get_file_callback),
164        get_content_callback(get_content_callback) {
165    DCHECK(!get_file_callback.is_null());
166    DCHECK(this->entry);
167  }
168
169  void OnError(FileError error) {
170    get_file_callback.Run(
171        error, base::FilePath(), std::string(), REGULAR_FILE);
172  }
173
174  void OnCacheFileFound(const base::FilePath& local_file_path) {
175    if (initialized_callback.is_null()) {
176      return;
177    }
178
179    scoped_ptr<ResourceEntry> new_entry(new ResourceEntry(*entry));
180    initialized_callback.Run(FILE_ERROR_OK,
181                             new_entry.Pass(),
182                             local_file_path,
183                             base::Closure());
184  }
185
186  void OnStartDownloading(const base::Closure& cancel_download_closure) {
187    if (initialized_callback.is_null()) {
188      return;
189    }
190
191    scoped_ptr<ResourceEntry> new_entry(new ResourceEntry(*entry));
192    initialized_callback.Run(FILE_ERROR_OK,
193                             new_entry.Pass(),
194                             base::FilePath(),
195                             cancel_download_closure);
196  }
197
198  void OnComplete(const base::FilePath& local_file_path) {
199    if (entry->file_specific_info().is_hosted_document()) {
200      get_file_callback.Run(
201          FILE_ERROR_OK, local_file_path, kMimeTypeJson, HOSTED_DOCUMENT);
202    } else {
203      get_file_callback.Run(
204          FILE_ERROR_OK, local_file_path,
205          entry->file_specific_info().content_mime_type(), REGULAR_FILE);
206    }
207  }
208
209  const base::FilePath drive_file_path;
210  const DriveClientContext context;
211  scoped_ptr<ResourceEntry> entry;
212  const GetFileContentInitializedCallback initialized_callback;
213  const GetFileCallback get_file_callback;
214  const google_apis::GetContentCallback get_content_callback;
215};
216
217// FileSystem::AddUploadedFileParams implementation.
218struct FileSystem::AddUploadedFileParams {
219  AddUploadedFileParams(const base::FilePath& file_content_path,
220                        const FileOperationCallback& callback,
221                        const std::string& resource_id,
222                        const std::string& md5)
223      : file_content_path(file_content_path),
224        callback(callback),
225        resource_id(resource_id),
226        md5(md5) {
227  }
228
229  base::FilePath file_content_path;
230  FileOperationCallback callback;
231  std::string resource_id;
232  std::string md5;
233};
234
235
236// FileSystem class implementation.
237
238FileSystem::FileSystem(
239    Profile* profile,
240    FileCache* cache,
241    google_apis::DriveServiceInterface* drive_service,
242    JobScheduler* scheduler,
243    DriveWebAppsRegistry* webapps_registry,
244    internal::ResourceMetadata* resource_metadata,
245    base::SequencedTaskRunner* blocking_task_runner)
246    : profile_(profile),
247      cache_(cache),
248      drive_service_(drive_service),
249      scheduler_(scheduler),
250      webapps_registry_(webapps_registry),
251      resource_metadata_(resource_metadata),
252      last_update_check_error_(FILE_ERROR_OK),
253      hide_hosted_docs_(false),
254      blocking_task_runner_(blocking_task_runner),
255      weak_ptr_factory_(this) {
256  // Should be created from the file browser extension API on UI thread.
257  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
258}
259
260void FileSystem::Reload() {
261  resource_metadata_->Reset(base::Bind(&FileSystem::ReloadAfterReset,
262                                       weak_ptr_factory_.GetWeakPtr()));
263}
264
265void FileSystem::Initialize() {
266  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
267
268  SetupChangeListLoader();
269
270  // Allocate the drive operation handlers.
271  drive_operations_.Init(scheduler_,
272                         this,  // FileSystemInterface
273                         cache_,
274                         resource_metadata_,
275                         blocking_task_runner_,
276                         this);  // OperationObserver
277
278  PrefService* pref_service = profile_->GetPrefs();
279  hide_hosted_docs_ = pref_service->GetBoolean(prefs::kDisableDriveHostedFiles);
280
281  InitializePreferenceObserver();
282}
283
284void FileSystem::ReloadAfterReset(FileError error) {
285  if (error != FILE_ERROR_OK) {
286    LOG(ERROR) << "Failed to reset the resource metadata: "
287               << FileErrorToString(error);
288    return;
289  }
290
291  SetupChangeListLoader();
292
293  change_list_loader_->LoadIfNeeded(
294      DirectoryFetchInfo(),
295      base::Bind(&FileSystem::OnUpdateChecked,
296                 weak_ptr_factory_.GetWeakPtr()));
297}
298
299void FileSystem::SetupChangeListLoader() {
300  change_list_loader_.reset(new ChangeListLoader(resource_metadata_,
301                                                 scheduler_,
302                                                 webapps_registry_));
303  change_list_loader_->AddObserver(this);
304}
305
306void FileSystem::CheckForUpdates() {
307  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308  DVLOG(1) << "CheckForUpdates";
309
310  if (change_list_loader_) {
311    change_list_loader_->CheckForUpdates(
312        base::Bind(&FileSystem::OnUpdateChecked,
313                   weak_ptr_factory_.GetWeakPtr()));
314  }
315}
316
317void FileSystem::OnUpdateChecked(FileError error) {
318  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
319  DVLOG(1) << "CheckForUpdates finished: " << FileErrorToString(error);
320  last_update_check_time_ = base::Time::Now();
321  last_update_check_error_ = error;
322}
323
324FileSystem::~FileSystem() {
325  // This should be called from UI thread, from DriveSystemService shutdown.
326  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
327
328  change_list_loader_->RemoveObserver(this);
329}
330
331void FileSystem::AddObserver(FileSystemObserver* observer) {
332  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
333  observers_.AddObserver(observer);
334}
335
336void FileSystem::RemoveObserver(FileSystemObserver* observer) {
337  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338  observers_.RemoveObserver(observer);
339}
340
341void FileSystem::GetEntryInfoByResourceId(
342    const std::string& resource_id,
343    const GetEntryInfoWithFilePathCallback& callback) {
344  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345  DCHECK(!resource_id.empty());
346  DCHECK(!callback.is_null());
347
348  resource_metadata_->GetEntryInfoByResourceId(
349      resource_id,
350      base::Bind(&FileSystem::GetEntryInfoByResourceIdAfterGetEntry,
351                 weak_ptr_factory_.GetWeakPtr(),
352                 callback));
353}
354
355void FileSystem::GetEntryInfoByResourceIdAfterGetEntry(
356    const GetEntryInfoWithFilePathCallback& callback,
357    FileError error,
358    const base::FilePath& file_path,
359    scoped_ptr<ResourceEntry> entry) {
360  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
361  DCHECK(!callback.is_null());
362
363  if (error != FILE_ERROR_OK) {
364    callback.Run(error, base::FilePath(), scoped_ptr<ResourceEntry>());
365    return;
366  }
367  DCHECK(entry.get());
368
369  CheckLocalModificationAndRun(
370      entry.Pass(),
371      base::Bind(&RunGetEntryInfoWithFilePathCallback,
372                 callback,
373                 file_path));
374}
375
376void FileSystem::TransferFileFromRemoteToLocal(
377    const base::FilePath& remote_src_file_path,
378    const base::FilePath& local_dest_file_path,
379    const FileOperationCallback& callback) {
380
381  drive_operations_.TransferFileFromRemoteToLocal(remote_src_file_path,
382                                                  local_dest_file_path,
383                                                  callback);
384}
385
386void FileSystem::TransferFileFromLocalToRemote(
387    const base::FilePath& local_src_file_path,
388    const base::FilePath& remote_dest_file_path,
389    const FileOperationCallback& callback) {
390
391  drive_operations_.TransferFileFromLocalToRemote(local_src_file_path,
392                                                  remote_dest_file_path,
393                                                  callback);
394}
395
396void FileSystem::Copy(const base::FilePath& src_file_path,
397                      const base::FilePath& dest_file_path,
398                      const FileOperationCallback& callback) {
399  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
400  DCHECK(!callback.is_null());
401  drive_operations_.Copy(src_file_path, dest_file_path, callback);
402}
403
404void FileSystem::Move(const base::FilePath& src_file_path,
405                      const base::FilePath& dest_file_path,
406                      const FileOperationCallback& callback) {
407  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
408  DCHECK(!callback.is_null());
409  drive_operations_.Move(src_file_path, dest_file_path, callback);
410}
411
412void FileSystem::Remove(const base::FilePath& file_path,
413                        bool is_recursive,
414                        const FileOperationCallback& callback) {
415  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
416  DCHECK(!callback.is_null());
417  drive_operations_.Remove(file_path, is_recursive, callback);
418}
419
420void FileSystem::CreateDirectory(
421    const base::FilePath& directory_path,
422    bool is_exclusive,
423    bool is_recursive,
424    const FileOperationCallback& callback) {
425  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
426  DCHECK(!callback.is_null());
427
428  change_list_loader_->LoadIfNeeded(
429      DirectoryFetchInfo(),
430      base::Bind(&FileSystem::CreateDirectoryAfterLoad,
431                 weak_ptr_factory_.GetWeakPtr(),
432                 directory_path, is_exclusive, is_recursive, callback));
433}
434
435void FileSystem::CreateDirectoryAfterLoad(
436    const base::FilePath& directory_path,
437    bool is_exclusive,
438    bool is_recursive,
439    const FileOperationCallback& callback,
440    FileError load_error) {
441  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
442  DCHECK(!callback.is_null());
443
444  if (load_error != FILE_ERROR_OK) {
445    callback.Run(load_error);
446    return;
447  }
448
449  drive_operations_.CreateDirectory(
450      directory_path, is_exclusive, is_recursive, callback);
451}
452
453void FileSystem::CreateFile(const base::FilePath& file_path,
454                            bool is_exclusive,
455                            const FileOperationCallback& callback) {
456  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
457  DCHECK(!callback.is_null());
458
459  drive_operations_.CreateFile(file_path, is_exclusive, callback);
460}
461
462void FileSystem::Pin(const base::FilePath& file_path,
463                     const FileOperationCallback& callback) {
464  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
465  DCHECK(!callback.is_null());
466
467  GetEntryInfoByPath(file_path,
468                     base::Bind(&FileSystem::PinAfterGetEntryInfoByPath,
469                                weak_ptr_factory_.GetWeakPtr(),
470                                callback));
471}
472
473void FileSystem::PinAfterGetEntryInfoByPath(
474    const FileOperationCallback& callback,
475    FileError error,
476    scoped_ptr<ResourceEntry> entry) {
477  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
478  DCHECK(!callback.is_null());
479
480  // TODO(hashimoto): Support pinning directories. crbug.com/127831
481  if (entry && entry->file_info().is_directory())
482    error = FILE_ERROR_NOT_A_FILE;
483
484  if (error != FILE_ERROR_OK) {
485    callback.Run(error);
486    return;
487  }
488  DCHECK(entry);
489
490  cache_->Pin(entry->resource_id(), entry->file_specific_info().file_md5(),
491              callback);
492}
493
494void FileSystem::Unpin(const base::FilePath& file_path,
495                       const FileOperationCallback& callback) {
496  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
497  DCHECK(!callback.is_null());
498
499  GetEntryInfoByPath(file_path,
500                     base::Bind(&FileSystem::UnpinAfterGetEntryInfoByPath,
501                                weak_ptr_factory_.GetWeakPtr(),
502                                callback));
503}
504
505void FileSystem::UnpinAfterGetEntryInfoByPath(
506    const FileOperationCallback& callback,
507    FileError error,
508    scoped_ptr<ResourceEntry> entry) {
509  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
510  DCHECK(!callback.is_null());
511
512  // TODO(hashimoto): Support pinning directories. crbug.com/127831
513  if (entry && entry->file_info().is_directory())
514    error = FILE_ERROR_NOT_A_FILE;
515
516  if (error != FILE_ERROR_OK) {
517    callback.Run(error);
518    return;
519  }
520  DCHECK(entry);
521
522  cache_->Unpin(entry->resource_id(), entry->file_specific_info().file_md5(),
523                callback);
524}
525
526void FileSystem::GetFileByPath(const base::FilePath& file_path,
527                               const GetFileCallback& callback) {
528  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
529  DCHECK(!callback.is_null());
530
531  resource_metadata_->GetEntryInfoByPath(
532      file_path,
533      base::Bind(&FileSystem::OnGetEntryInfoCompleteForGetFileByPath,
534                 weak_ptr_factory_.GetWeakPtr(),
535                 file_path,
536                 callback));
537}
538
539void FileSystem::OnGetEntryInfoCompleteForGetFileByPath(
540    const base::FilePath& file_path,
541    const GetFileCallback& callback,
542    FileError error,
543    scoped_ptr<ResourceEntry> entry) {
544  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
545  DCHECK(!callback.is_null());
546
547  if (error != FILE_ERROR_OK) {
548    callback.Run(error, base::FilePath(), std::string(), REGULAR_FILE);
549    return;
550  }
551  DCHECK(entry);
552
553  GetResolvedFileByPath(
554      make_scoped_ptr(new GetResolvedFileParams(
555          file_path,
556          DriveClientContext(USER_INITIATED),
557          entry.Pass(),
558          GetFileContentInitializedCallback(),
559          callback,
560          google_apis::GetContentCallback())));
561}
562
563void FileSystem::GetFileByResourceId(
564    const std::string& resource_id,
565    const DriveClientContext& context,
566    const GetFileCallback& get_file_callback,
567    const google_apis::GetContentCallback& get_content_callback) {
568  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
569  DCHECK(!resource_id.empty());
570  DCHECK(!get_file_callback.is_null());
571
572  resource_metadata_->GetEntryInfoByResourceId(
573      resource_id,
574      base::Bind(&FileSystem::GetFileByResourceIdAfterGetEntry,
575                 weak_ptr_factory_.GetWeakPtr(),
576                 context,
577                 get_file_callback,
578                 get_content_callback));
579}
580
581void FileSystem::GetFileByResourceIdAfterGetEntry(
582    const DriveClientContext& context,
583    const GetFileCallback& get_file_callback,
584    const google_apis::GetContentCallback& get_content_callback,
585    FileError error,
586    const base::FilePath& file_path,
587    scoped_ptr<ResourceEntry> entry) {
588  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
589  DCHECK(!get_file_callback.is_null());
590
591  if (error != FILE_ERROR_OK) {
592    get_file_callback.Run(FILE_ERROR_NOT_FOUND,
593                          base::FilePath(),
594                          std::string(),
595                          REGULAR_FILE);
596    return;
597  }
598
599  GetResolvedFileByPath(
600      make_scoped_ptr(new GetResolvedFileParams(
601          file_path,
602          context,
603          entry.Pass(),
604          GetFileContentInitializedCallback(),
605          get_file_callback,
606          get_content_callback)));
607}
608
609void FileSystem::GetFileContentByPath(
610    const base::FilePath& file_path,
611    const GetFileContentInitializedCallback& initialized_callback,
612    const google_apis::GetContentCallback& get_content_callback,
613    const FileOperationCallback& completion_callback) {
614  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
615  DCHECK(!initialized_callback.is_null());
616  DCHECK(!get_content_callback.is_null());
617  DCHECK(!completion_callback.is_null());
618
619  resource_metadata_->GetEntryInfoByPath(
620      file_path,
621      base::Bind(&FileSystem::GetFileContentByPathAfterGetEntry,
622                 weak_ptr_factory_.GetWeakPtr(),
623                 file_path,
624                 initialized_callback,
625                 get_content_callback,
626                 completion_callback));
627}
628
629void FileSystem::GetFileContentByPathAfterGetEntry(
630    const base::FilePath& file_path,
631    const GetFileContentInitializedCallback& initialized_callback,
632    const google_apis::GetContentCallback& get_content_callback,
633    const FileOperationCallback& completion_callback,
634    FileError error,
635    scoped_ptr<ResourceEntry> entry) {
636  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
637  DCHECK(!initialized_callback.is_null());
638  DCHECK(!get_content_callback.is_null());
639  DCHECK(!completion_callback.is_null());
640
641  if (error != FILE_ERROR_OK) {
642    completion_callback.Run(error);
643    return;
644  }
645
646  DCHECK(entry);
647  GetResolvedFileByPath(
648      make_scoped_ptr(new GetResolvedFileParams(
649          file_path,
650          DriveClientContext(USER_INITIATED),
651          entry.Pass(),
652          initialized_callback,
653          base::Bind(&GetFileCallbackToFileOperationCallbackAdapter,
654                     completion_callback),
655          get_content_callback)));
656}
657
658void FileSystem::GetEntryInfoByPath(const base::FilePath& file_path,
659                                    const GetEntryInfoCallback& callback) {
660  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
661  DCHECK(!callback.is_null());
662
663  // ResourceMetadata may know about the entry even if the resource
664  // metadata is not yet fully loaded. For instance, ResourceMetadata()
665  // always knows about the root directory. For "fast fetch"
666  // (crbug.com/178348) to work, it's needed to delay the resource metadata
667  // loading until the first call to ReadDirectoryByPath().
668  resource_metadata_->GetEntryInfoByPath(
669      file_path,
670      base::Bind(&FileSystem::GetEntryInfoByPathAfterGetEntry1,
671                 weak_ptr_factory_.GetWeakPtr(),
672                 file_path,
673                 callback));
674}
675
676void FileSystem::GetEntryInfoByPathAfterGetEntry1(
677    const base::FilePath& file_path,
678    const GetEntryInfoCallback& callback,
679    FileError error,
680    scoped_ptr<ResourceEntry> entry) {
681  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
682  DCHECK(!callback.is_null());
683
684  if (error == FILE_ERROR_OK) {
685    CheckLocalModificationAndRun(entry.Pass(), callback);
686    return;
687  }
688
689  // Start loading if needed. Note that directory_fetch_info is empty here,
690  // as we don't need to fetch the contents of a directory when we just need
691  // to get an entry of the directory.
692  change_list_loader_->LoadIfNeeded(
693      DirectoryFetchInfo(),
694      base::Bind(&FileSystem::GetEntryInfoByPathAfterLoad,
695                 weak_ptr_factory_.GetWeakPtr(),
696                 file_path,
697                 callback));
698}
699
700void FileSystem::GetEntryInfoByPathAfterLoad(
701    const base::FilePath& file_path,
702    const GetEntryInfoCallback& callback,
703    FileError error) {
704  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
705  DCHECK(!callback.is_null());
706
707  if (error != FILE_ERROR_OK) {
708    callback.Run(error, scoped_ptr<ResourceEntry>());
709    return;
710  }
711
712  resource_metadata_->GetEntryInfoByPath(
713      file_path,
714      base::Bind(&FileSystem::GetEntryInfoByPathAfterGetEntry2,
715                 weak_ptr_factory_.GetWeakPtr(),
716                 callback));
717}
718
719void FileSystem::GetEntryInfoByPathAfterGetEntry2(
720    const GetEntryInfoCallback& callback,
721    FileError error,
722    scoped_ptr<ResourceEntry> entry) {
723  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
724  DCHECK(!callback.is_null());
725
726  if (error != FILE_ERROR_OK) {
727    callback.Run(error, scoped_ptr<ResourceEntry>());
728    return;
729  }
730  DCHECK(entry.get());
731
732  CheckLocalModificationAndRun(entry.Pass(), callback);
733}
734
735void FileSystem::ReadDirectoryByPath(
736    const base::FilePath& directory_path,
737    const ReadDirectoryWithSettingCallback& callback) {
738  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
739  DCHECK(!callback.is_null());
740
741  // As described in GetEntryInfoByPath(), ResourceMetadata may know
742  // about the entry even if the file system is not yet fully loaded, hence we
743  // should just ask ResourceMetadata first.
744  resource_metadata_->GetEntryInfoByPath(
745      directory_path,
746      base::Bind(&FileSystem::ReadDirectoryByPathAfterGetEntry,
747                 weak_ptr_factory_.GetWeakPtr(),
748                 directory_path,
749                 callback));
750}
751
752void FileSystem::ReadDirectoryByPathAfterGetEntry(
753    const base::FilePath& directory_path,
754    const ReadDirectoryWithSettingCallback& callback,
755    FileError error,
756    scoped_ptr<ResourceEntry> entry) {
757  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
758  DCHECK(!callback.is_null());
759
760  if (error != FILE_ERROR_OK) {
761    // If we don't know about the directory, start loading.
762    change_list_loader_->LoadIfNeeded(
763        DirectoryFetchInfo(),
764        base::Bind(&FileSystem::ReadDirectoryByPathAfterLoad,
765                   weak_ptr_factory_.GetWeakPtr(),
766                   directory_path,
767                   callback));
768    return;
769  }
770
771  if (!entry->file_info().is_directory()) {
772    callback.Run(FILE_ERROR_NOT_A_DIRECTORY,
773                 hide_hosted_docs_,
774                 scoped_ptr<ResourceEntryVector>());
775    return;
776  }
777
778  // Pass the directory fetch info so we can fetch the contents of the
779  // directory before loading change lists.
780  DirectoryFetchInfo directory_fetch_info(
781      entry->resource_id(),
782      entry->directory_specific_info().changestamp());
783  change_list_loader_->LoadIfNeeded(
784      directory_fetch_info,
785      base::Bind(&FileSystem::ReadDirectoryByPathAfterLoad,
786                 weak_ptr_factory_.GetWeakPtr(),
787                 directory_path,
788                 callback));
789}
790
791void FileSystem::ReadDirectoryByPathAfterLoad(
792    const base::FilePath& directory_path,
793    const ReadDirectoryWithSettingCallback& callback,
794    FileError error) {
795  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
796  DCHECK(!callback.is_null());
797
798  if (error != FILE_ERROR_OK) {
799    callback.Run(error,
800                 hide_hosted_docs_,
801                 scoped_ptr<ResourceEntryVector>());
802    return;
803  }
804
805  resource_metadata_->ReadDirectoryByPath(
806      directory_path,
807      base::Bind(&FileSystem::ReadDirectoryByPathAfterRead,
808                 weak_ptr_factory_.GetWeakPtr(),
809                 callback));
810}
811
812void FileSystem::ReadDirectoryByPathAfterRead(
813    const ReadDirectoryWithSettingCallback& callback,
814    FileError error,
815    scoped_ptr<ResourceEntryVector> entries) {
816  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
817  DCHECK(!callback.is_null());
818
819  if (error != FILE_ERROR_OK) {
820    callback.Run(error,
821                 hide_hosted_docs_,
822                 scoped_ptr<ResourceEntryVector>());
823    return;
824  }
825  DCHECK(entries.get());  // This is valid for empty directories too.
826
827  callback.Run(FILE_ERROR_OK, hide_hosted_docs_, entries.Pass());
828}
829
830void FileSystem::GetResolvedFileByPath(
831    scoped_ptr<GetResolvedFileParams> params) {
832  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
833  DCHECK(params);
834
835  if (params->entry->file_info().is_directory()) {
836    params->OnError(FILE_ERROR_NOT_A_FILE);
837    return;
838  }
839
840  // The file's entry should have its file specific info.
841  DCHECK(params->entry->has_file_specific_info());
842
843  // For a hosted document, we create a special JSON file to represent the
844  // document instead of fetching the document content in one of the exported
845  // formats. The JSON file contains the edit URL and resource ID of the
846  // document.
847  if (params->entry->file_specific_info().is_hosted_document()) {
848    base::FilePath* temp_file_path = new base::FilePath;
849    ResourceEntry* entry_ptr = params->entry.get();
850    base::PostTaskAndReplyWithResult(
851        blocking_task_runner_,
852        FROM_HERE,
853        base::Bind(&CreateDocumentJsonFileOnBlockingPool,
854                   cache_->GetCacheDirectoryPath(
855                       FileCache::CACHE_TYPE_TMP_DOCUMENTS),
856                   GURL(entry_ptr->file_specific_info().alternate_url()),
857                   entry_ptr->resource_id(),
858                   temp_file_path),
859        base::Bind(
860            &FileSystem::GetResolvedFileByPathAfterCreateDocumentJsonFile,
861            weak_ptr_factory_.GetWeakPtr(),
862            base::Passed(&params),
863            base::Owned(temp_file_path)));
864    return;
865  }
866
867  // Returns absolute path of the file if it were cached or to be cached.
868  ResourceEntry* entry_ptr = params->entry.get();
869  cache_->GetFile(
870      entry_ptr->resource_id(),
871      entry_ptr->file_specific_info().file_md5(),
872      base::Bind(
873          &FileSystem::GetResolvedFileByPathAfterGetFileFromCache,
874          weak_ptr_factory_.GetWeakPtr(),
875          base::Passed(&params)));
876}
877
878void FileSystem::GetResolvedFileByPathAfterCreateDocumentJsonFile(
879    scoped_ptr<GetResolvedFileParams> params,
880    const base::FilePath* file_path,
881    FileError error) {
882  DCHECK(params);
883  DCHECK(file_path);
884
885  if (error != FILE_ERROR_OK) {
886    params->OnError(error);
887    return;
888  }
889
890  params->OnCacheFileFound(*file_path);
891  params->OnComplete(*file_path);
892}
893
894void FileSystem::GetResolvedFileByPathAfterGetFileFromCache(
895    scoped_ptr<GetResolvedFileParams> params,
896    FileError error,
897    const base::FilePath& cache_file_path) {
898  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
899  DCHECK(params);
900
901  // Have we found the file in cache? If so, return it back to the caller.
902  if (error == FILE_ERROR_OK) {
903    params->OnCacheFileFound(cache_file_path);
904    params->OnComplete(cache_file_path);
905    return;
906  }
907
908  // If cache file is not found, try to download the file from the server
909  // instead. This logic is rather complicated but here's how this works:
910  //
911  // Retrieve fresh file metadata from server. We will extract file size and
912  // content url from there (we want to make sure used content url is not
913  // stale).
914  //
915  // Check if we have enough space, based on the expected file size.
916  // - if we don't have enough space, try to free up the disk space
917  // - if we still don't have enough space, return "no space" error
918  // - if we have enough space, start downloading the file from the server
919  GetResolvedFileParams* params_ptr = params.get();
920  scheduler_->GetResourceEntry(
921      params_ptr->entry->resource_id(),
922      params_ptr->context,
923      base::Bind(&FileSystem::GetResolvedFileByPathAfterGetResourceEntry,
924                 weak_ptr_factory_.GetWeakPtr(),
925                 base::Passed(&params)));
926}
927
928void FileSystem::GetResolvedFileByPathAfterGetResourceEntry(
929    scoped_ptr<GetResolvedFileParams> params,
930    google_apis::GDataErrorCode status,
931    scoped_ptr<google_apis::ResourceEntry> entry) {
932  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
933  DCHECK(params);
934
935  const FileError error = util::GDataToFileError(status);
936  if (error != FILE_ERROR_OK) {
937    params->OnError(error);
938    return;
939  }
940
941  // The download URL is:
942  // 1) src attribute of content element, on GData WAPI.
943  // 2) the value of the key 'downloadUrl', on Drive API v2.
944  // In both cases, we can use ResourceEntry::download_url().
945  const GURL& download_url = entry->download_url();
946
947  // The content URL can be empty for non-downloadable files (such as files
948  // shared from others with "prevent downloading by viewers" flag set.)
949  if (download_url.is_empty()) {
950    params->OnError(FILE_ERROR_ACCESS_DENIED);
951    return;
952  }
953
954  DCHECK_EQ(params->entry->resource_id(), entry->resource_id());
955  resource_metadata_->RefreshEntry(
956      ConvertToResourceEntry(*entry),
957      base::Bind(&FileSystem::GetResolvedFileByPathAfterRefreshEntry,
958                 weak_ptr_factory_.GetWeakPtr(),
959                 base::Passed(&params),
960                 download_url));
961}
962
963void FileSystem::GetResolvedFileByPathAfterRefreshEntry(
964    scoped_ptr<GetResolvedFileParams> params,
965    const GURL& download_url,
966    FileError error,
967    const base::FilePath& drive_file_path,
968    scoped_ptr<ResourceEntry> entry) {
969  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
970  DCHECK(params);
971
972  if (error != FILE_ERROR_OK) {
973    params->OnError(error);
974    return;
975  }
976
977  int64 file_size = entry->file_info().size();
978  params->entry = entry.Pass();  // Update the entry in |params|.
979  cache_->FreeDiskSpaceIfNeededFor(
980      file_size,
981      base::Bind(&FileSystem::GetResolvedFileByPathAfterFreeDiskSpace,
982                 weak_ptr_factory_.GetWeakPtr(),
983                 base::Passed(&params),
984                 download_url));
985}
986
987void FileSystem::GetResolvedFileByPathAfterFreeDiskSpace(
988    scoped_ptr<GetResolvedFileParams> params,
989    const GURL& download_url,
990    bool has_enough_space) {
991  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
992  DCHECK(params);
993
994  if (!has_enough_space) {
995    // If no enough space, return FILE_ERROR_NO_SPACE.
996    params->OnError(FILE_ERROR_NO_SPACE);
997    return;
998  }
999
1000  // We have enough disk space. Create download destination file.
1001  const base::FilePath temp_download_directory =
1002      cache_->GetCacheDirectoryPath(FileCache::CACHE_TYPE_TMP_DOWNLOADS);
1003  base::FilePath* file_path = new base::FilePath;
1004  base::PostTaskAndReplyWithResult(
1005      blocking_task_runner_,
1006      FROM_HERE,
1007      base::Bind(&CreateTemporaryReadableFileInDir,
1008                 temp_download_directory,
1009                 file_path),
1010      base::Bind(&FileSystem::GetResolveFileByPathAfterCreateTemporaryFile,
1011                 weak_ptr_factory_.GetWeakPtr(),
1012                 base::Passed(&params),
1013                 download_url,
1014                 base::Owned(file_path)));
1015}
1016
1017void FileSystem::GetResolveFileByPathAfterCreateTemporaryFile(
1018    scoped_ptr<GetResolvedFileParams> params,
1019    const GURL& download_url,
1020    base::FilePath* temp_file,
1021    bool success) {
1022  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1023  DCHECK(params);
1024
1025  if (!success) {
1026    params->OnError(FILE_ERROR_FAILED);
1027    return;
1028  }
1029
1030  GetResolvedFileParams* params_ptr = params.get();
1031  JobID id = scheduler_->DownloadFile(
1032      params_ptr->drive_file_path,
1033      *temp_file,
1034      download_url,
1035      params_ptr->context,
1036      base::Bind(&FileSystem::GetResolvedFileByPathAfterDownloadFile,
1037                 weak_ptr_factory_.GetWeakPtr(),
1038                 base::Passed(&params)),
1039      params_ptr->get_content_callback);
1040  params_ptr->OnStartDownloading(
1041      base::Bind(&FileSystem::CancelJobInScheduler,
1042                 weak_ptr_factory_.GetWeakPtr(),
1043                 id));
1044}
1045
1046void FileSystem::GetResolvedFileByPathAfterDownloadFile(
1047    scoped_ptr<GetResolvedFileParams> params,
1048    google_apis::GDataErrorCode status,
1049    const base::FilePath& downloaded_file_path) {
1050  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1051  DCHECK(params);
1052
1053  // If user cancels download of a pinned-but-not-fetched file, mark file as
1054  // unpinned so that we do not sync the file again.
1055  if (status == google_apis::GDATA_CANCELLED) {
1056    cache_->GetCacheEntry(
1057        params->entry->resource_id(),
1058        params->entry->file_specific_info().file_md5(),
1059        base::Bind(
1060            &FileSystem::GetResolvedFileByPathAfterGetCacheEntryForCancel,
1061            weak_ptr_factory_.GetWeakPtr(),
1062            params->entry->resource_id(),
1063            params->entry->file_specific_info().file_md5()));
1064  }
1065
1066  FileError error = util::GDataToFileError(status);
1067  if (error != FILE_ERROR_OK) {
1068    params->OnError(error);
1069    return;
1070  }
1071
1072  ResourceEntry* entry = params->entry.get();
1073  cache_->Store(entry->resource_id(),
1074                entry->file_specific_info().file_md5(),
1075                downloaded_file_path,
1076                FileCache::FILE_OPERATION_MOVE,
1077                base::Bind(&FileSystem::GetResolvedFileByPathAfterStore,
1078                           weak_ptr_factory_.GetWeakPtr(),
1079                           base::Passed(&params),
1080                           downloaded_file_path));
1081}
1082
1083void FileSystem::GetResolvedFileByPathAfterGetCacheEntryForCancel(
1084    const std::string& resource_id,
1085    const std::string& md5,
1086    bool success,
1087    const FileCacheEntry& cache_entry) {
1088  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1089  // TODO(hshi): http://crbug.com/127138 notify when file properties change.
1090  // This allows file manager to clear the "Available offline" checkbox.
1091  if (success && cache_entry.is_pinned()) {
1092    cache_->Unpin(resource_id,
1093                  md5,
1094                  base::Bind(&util::EmptyFileOperationCallback));
1095  }
1096}
1097
1098void FileSystem::GetResolvedFileByPathAfterStore(
1099    scoped_ptr<GetResolvedFileParams> params,
1100    const base::FilePath& downloaded_file_path,
1101    FileError error) {
1102  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1103  DCHECK(params);
1104
1105  if (error != FILE_ERROR_OK) {
1106    blocking_task_runner_->PostTask(
1107        FROM_HERE,
1108        base::Bind(base::IgnoreResult(&file_util::Delete),
1109                   downloaded_file_path,
1110                   false /* recursive*/));
1111    params->OnError(error);
1112    return;
1113  }
1114  // Storing to cache changes the "offline available" status, hence notify.
1115  OnDirectoryChanged(params->drive_file_path.DirName());
1116
1117  ResourceEntry* entry = params->entry.get();
1118  cache_->GetFile(
1119      entry->resource_id(),
1120      entry->file_specific_info().file_md5(),
1121      base::Bind(&FileSystem::GetResolvedFileByPathAfterGetFile,
1122                 weak_ptr_factory_.GetWeakPtr(),
1123                 base::Passed(&params)));
1124}
1125
1126void FileSystem::GetResolvedFileByPathAfterGetFile(
1127    scoped_ptr<GetResolvedFileParams> params,
1128    FileError error,
1129    const base::FilePath& cache_file) {
1130  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1131  DCHECK(params);
1132
1133  if (error != FILE_ERROR_OK) {
1134    params->OnError(error);
1135    return;
1136  }
1137  params->OnComplete(cache_file);
1138}
1139
1140void FileSystem::RefreshDirectory(
1141    const base::FilePath& directory_path,
1142    const FileOperationCallback& callback) {
1143  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1144  DCHECK(!callback.is_null());
1145
1146  // Make sure the destination directory exists.
1147  resource_metadata_->GetEntryInfoByPath(
1148      directory_path,
1149      base::Bind(&FileSystem::RefreshDirectoryAfterGetEntryInfo,
1150                 weak_ptr_factory_.GetWeakPtr(),
1151                 directory_path,
1152                 callback));
1153}
1154
1155void FileSystem::RefreshDirectoryAfterGetEntryInfo(
1156    const base::FilePath& directory_path,
1157    const FileOperationCallback& callback,
1158    FileError error,
1159    scoped_ptr<ResourceEntry> entry) {
1160  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1161  DCHECK(!callback.is_null());
1162
1163  if (error != FILE_ERROR_OK) {
1164    callback.Run(error);
1165    return;
1166  }
1167  if (!entry->file_info().is_directory()) {
1168    callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
1169    return;
1170  }
1171  if (util::IsSpecialResourceId(entry->resource_id())) {
1172    // Do not load special directories. Just return.
1173    callback.Run(FILE_ERROR_OK);
1174    return;
1175  }
1176
1177  change_list_loader_->LoadDirectoryFromServer(
1178      entry->resource_id(),
1179      callback);
1180}
1181
1182void FileSystem::UpdateFileByResourceId(
1183    const std::string& resource_id,
1184    const DriveClientContext& context,
1185    const FileOperationCallback& callback) {
1186  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1187  DCHECK(!callback.is_null());
1188
1189  drive_operations_.UpdateFileByResourceId(resource_id, context, callback);
1190}
1191
1192void FileSystem::GetAvailableSpace(
1193    const GetAvailableSpaceCallback& callback) {
1194  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1195  DCHECK(!callback.is_null());
1196
1197  scheduler_->GetAboutResource(
1198      base::Bind(&FileSystem::OnGetAboutResource,
1199                 weak_ptr_factory_.GetWeakPtr(),
1200                 callback));
1201}
1202
1203void FileSystem::OnGetAboutResource(
1204    const GetAvailableSpaceCallback& callback,
1205    google_apis::GDataErrorCode status,
1206    scoped_ptr<google_apis::AboutResource> about_resource) {
1207  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1208  DCHECK(!callback.is_null());
1209
1210  FileError error = util::GDataToFileError(status);
1211  if (error != FILE_ERROR_OK) {
1212    callback.Run(error, -1, -1);
1213    return;
1214  }
1215  DCHECK(about_resource);
1216
1217  callback.Run(FILE_ERROR_OK,
1218               about_resource->quota_bytes_total(),
1219               about_resource->quota_bytes_used());
1220}
1221
1222void FileSystem::OnSearch(const SearchCallback& search_callback,
1223                          ScopedVector<ChangeList> change_lists,
1224                          FileError error) {
1225  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1226  DCHECK(!search_callback.is_null());
1227
1228  if (error != FILE_ERROR_OK) {
1229    search_callback.Run(error,
1230                        GURL(),
1231                        scoped_ptr<std::vector<SearchResultInfo> >());
1232    return;
1233  }
1234
1235  // The search results will be returned using virtual directory.
1236  // The directory is not really part of the file system, so it has no parent or
1237  // root.
1238  std::vector<SearchResultInfo>* results(new std::vector<SearchResultInfo>());
1239  scoped_ptr<std::vector<SearchResultInfo> > result_vec(results);
1240
1241  DCHECK_EQ(1u, change_lists.size());
1242  const ChangeList* change_list = change_lists[0];
1243
1244  // TODO(tbarzic): Limit total number of returned results for the query.
1245  const GURL& next_feed = change_list->next_url();
1246
1247  const base::Closure callback = base::Bind(
1248      search_callback, FILE_ERROR_OK, next_feed, base::Passed(&result_vec));
1249
1250  const std::vector<ResourceEntry>& entries = change_list->entries();
1251  if (entries.empty()) {
1252    callback.Run();
1253    return;
1254  }
1255
1256  DVLOG(1) << "OnSearch number of entries=" << entries.size();
1257  // Go through all entries generated by the feed and add them to the search
1258  // result directory.
1259  for (size_t i = 0; i < entries.size(); ++i) {
1260    // Run the callback if this is the last iteration of the loop.
1261    const bool should_run_callback = (i + 1 == entries.size());
1262    const ResourceEntry& entry = entries[i];
1263
1264    const GetEntryInfoWithFilePathCallback entry_info_callback =
1265        base::Bind(&FileSystem::AddToSearchResults,
1266                   weak_ptr_factory_.GetWeakPtr(),
1267                   results,
1268                   should_run_callback,
1269                   callback);
1270
1271    resource_metadata_->RefreshEntry(entry, entry_info_callback);
1272  }
1273}
1274
1275void FileSystem::AddToSearchResults(
1276    std::vector<SearchResultInfo>* results,
1277    bool should_run_callback,
1278    const base::Closure& callback,
1279    FileError error,
1280    const base::FilePath& drive_file_path,
1281    scoped_ptr<ResourceEntry> entry) {
1282  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1283
1284  // If a result is not present in our local file system snapshot, call
1285  // CheckForUpdates to refresh the snapshot with a delta feed. This may happen
1286  // if the entry has recently been added to the drive (and we still haven't
1287  // received its delta feed).
1288  if (error == FILE_ERROR_OK) {
1289    DCHECK(entry.get());
1290    results->push_back(SearchResultInfo(drive_file_path, *entry.get()));
1291    DVLOG(1) << "AddToSearchResults " << drive_file_path.value();
1292  } else if (error == FILE_ERROR_NOT_FOUND) {
1293    CheckForUpdates();
1294  } else {
1295    NOTREACHED();
1296  }
1297
1298  if (should_run_callback)
1299    callback.Run();
1300}
1301
1302void FileSystem::Search(const std::string& search_query,
1303                        const GURL& next_feed,
1304                        const SearchCallback& callback) {
1305  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1306  DCHECK(!callback.is_null());
1307
1308  change_list_loader_->SearchFromServer(
1309      search_query,
1310      next_feed,
1311      base::Bind(&FileSystem::OnSearch,
1312                 weak_ptr_factory_.GetWeakPtr(),
1313                 callback));
1314}
1315
1316void FileSystem::SearchMetadata(const std::string& query,
1317                                int options,
1318                                int at_most_num_matches,
1319                                const SearchMetadataCallback& callback) {
1320  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1321
1322  if (hide_hosted_docs_)
1323    options |= SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS;
1324
1325  drive::SearchMetadata(resource_metadata_,
1326                        query,
1327                        options,
1328                        at_most_num_matches,
1329                        callback);
1330}
1331
1332void FileSystem::OnDirectoryChangedByOperation(
1333    const base::FilePath& directory_path) {
1334  OnDirectoryChanged(directory_path);
1335}
1336
1337void FileSystem::OnDirectoryChanged(const base::FilePath& directory_path) {
1338  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1339
1340  FOR_EACH_OBSERVER(FileSystemObserver, observers_,
1341                    OnDirectoryChanged(directory_path));
1342}
1343
1344void FileSystem::OnFeedFromServerLoaded() {
1345  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1346
1347  FOR_EACH_OBSERVER(FileSystemObserver, observers_,
1348                    OnFeedFromServerLoaded());
1349}
1350
1351void FileSystem::OnInitialFeedLoaded() {
1352  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1353
1354  FOR_EACH_OBSERVER(FileSystemObserver,
1355                    observers_,
1356                    OnInitialLoadFinished());
1357}
1358
1359void FileSystem::AddUploadedFile(
1360    scoped_ptr<google_apis::ResourceEntry> entry,
1361    const base::FilePath& file_content_path,
1362    const FileOperationCallback& callback) {
1363  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1364  DCHECK(entry.get());
1365  DCHECK(!entry->resource_id().empty());
1366  DCHECK(!entry->file_md5().empty());
1367  DCHECK(!callback.is_null());
1368
1369  AddUploadedFileParams params(file_content_path,
1370                               callback,
1371                               entry->resource_id(),
1372                               entry->file_md5());
1373
1374  resource_metadata_->AddEntry(
1375      ConvertToResourceEntry(*entry),
1376      base::Bind(&FileSystem::AddUploadedFileToCache,
1377                 weak_ptr_factory_.GetWeakPtr(), params));
1378}
1379
1380void FileSystem::AddUploadedFileToCache(
1381    const AddUploadedFileParams& params,
1382    FileError error,
1383    const base::FilePath& file_path) {
1384  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1385  DCHECK(!params.resource_id.empty());
1386  DCHECK(!params.md5.empty());
1387  DCHECK(!params.callback.is_null());
1388
1389  // Depending on timing, a metadata may have inserted via delta feed already.
1390  // So, FILE_ERROR_EXISTS is not an error.
1391  if (error != FILE_ERROR_OK && error != FILE_ERROR_EXISTS) {
1392    params.callback.Run(error);
1393    return;
1394  }
1395
1396  OnDirectoryChanged(file_path.DirName());
1397
1398  // At this point, upload to the server is fully succeeded. Failure to store to
1399  // cache is not a fatal error, so we wrap the callback with IgnoreError, and
1400  // always return success to the caller.
1401  cache_->Store(params.resource_id,
1402                params.md5,
1403                params.file_content_path,
1404                FileCache::FILE_OPERATION_COPY,
1405                base::Bind(&IgnoreError, params.callback));
1406}
1407
1408void FileSystem::GetMetadata(
1409    const GetFilesystemMetadataCallback& callback) {
1410  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1411  DCHECK(!callback.is_null());
1412
1413  FileSystemMetadata metadata;
1414  metadata.refreshing = change_list_loader_->IsRefreshing();
1415
1416  // Metadata related to delta update.
1417  metadata.last_update_check_time = last_update_check_time_;
1418  metadata.last_update_check_error = last_update_check_error_;
1419
1420  resource_metadata_->GetLargestChangestamp(
1421      base::Bind(&OnGetLargestChangestamp, metadata, callback));
1422}
1423
1424void FileSystem::MarkCacheFileAsMounted(
1425    const base::FilePath& drive_file_path,
1426    const OpenFileCallback& callback) {
1427  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1428  DCHECK(!callback.is_null());
1429
1430  GetEntryInfoByPath(
1431      drive_file_path,
1432      base::Bind(&FileSystem::MarkCacheFileAsMountedAfterGetEntryInfo,
1433                 weak_ptr_factory_.GetWeakPtr(), callback));
1434}
1435
1436void FileSystem::MarkCacheFileAsMountedAfterGetEntryInfo(
1437    const OpenFileCallback& callback,
1438    FileError error,
1439    scoped_ptr<ResourceEntry> entry) {
1440  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1441  DCHECK(!callback.is_null());
1442
1443  if (error != FILE_ERROR_OK) {
1444    callback.Run(error, base::FilePath());
1445    return;
1446  }
1447
1448  DCHECK(entry);
1449  cache_->MarkAsMounted(entry->resource_id(),
1450                        entry->file_specific_info().file_md5(),
1451                        callback);
1452}
1453
1454void FileSystem::MarkCacheFileAsUnmounted(
1455    const base::FilePath& cache_file_path,
1456    const FileOperationCallback& callback) {
1457  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1458  DCHECK(!callback.is_null());
1459
1460  if (!cache_->IsUnderFileCacheDirectory(cache_file_path)) {
1461    callback.Run(FILE_ERROR_FAILED);
1462    return;
1463  }
1464  cache_->MarkAsUnmounted(cache_file_path, callback);
1465}
1466
1467void FileSystem::GetCacheEntryByResourceId(
1468    const std::string& resource_id,
1469    const std::string& md5,
1470    const GetCacheEntryCallback& callback) {
1471  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1472  DCHECK(!resource_id.empty());
1473  DCHECK(!callback.is_null());
1474
1475  cache_->GetCacheEntry(resource_id, md5, callback);
1476}
1477
1478void FileSystem::IterateCache(
1479    const CacheIterateCallback& iteration_callback,
1480    const base::Closure& completion_callback) {
1481  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1482  DCHECK(!iteration_callback.is_null());
1483  DCHECK(!completion_callback.is_null());
1484
1485  cache_->Iterate(iteration_callback, completion_callback);
1486}
1487
1488void FileSystem::OnDisableDriveHostedFilesChanged() {
1489  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1490  PrefService* pref_service = profile_->GetPrefs();
1491  SetHideHostedDocuments(
1492      pref_service->GetBoolean(prefs::kDisableDriveHostedFiles));
1493}
1494
1495void FileSystem::SetHideHostedDocuments(bool hide) {
1496  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1497
1498  if (hide == hide_hosted_docs_)
1499    return;
1500
1501  hide_hosted_docs_ = hide;
1502
1503  // Kick off directory refresh when this setting changes.
1504  FOR_EACH_OBSERVER(FileSystemObserver, observers_,
1505                    OnDirectoryChanged(util::GetDriveGrandRootPath()));
1506}
1507
1508//============= FileSystem: internal helper functions =====================
1509
1510void FileSystem::InitializePreferenceObserver() {
1511  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1512
1513  pref_registrar_.reset(new PrefChangeRegistrar());
1514  pref_registrar_->Init(profile_->GetPrefs());
1515  pref_registrar_->Add(
1516      prefs::kDisableDriveHostedFiles,
1517      base::Bind(&FileSystem::OnDisableDriveHostedFilesChanged,
1518                 base::Unretained(this)));
1519}
1520
1521void FileSystem::OpenFile(const base::FilePath& file_path,
1522                          const OpenFileCallback& callback) {
1523  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1524  DCHECK(!callback.is_null());
1525
1526  // If the file is already opened, it cannot be opened again before closed.
1527  // This is for avoiding simultaneous modification to the file, and moreover
1528  // to avoid an inconsistent cache state (suppose an operation sequence like
1529  // Open->Open->modify->Close->modify->Close; the second modify may not be
1530  // synchronized to the server since it is already Closed on the cache).
1531  if (open_files_.find(file_path) != open_files_.end()) {
1532    base::MessageLoopProxy::current()->PostTask(
1533        FROM_HERE,
1534        base::Bind(callback, FILE_ERROR_IN_USE, base::FilePath()));
1535    return;
1536  }
1537  open_files_.insert(file_path);
1538
1539  resource_metadata_->GetEntryInfoByPath(
1540      file_path,
1541      base::Bind(&FileSystem::OnGetEntryInfoCompleteForOpenFile,
1542                 weak_ptr_factory_.GetWeakPtr(),
1543                 file_path,
1544                 base::Bind(&FileSystem::OnOpenFileFinished,
1545                            weak_ptr_factory_.GetWeakPtr(),
1546                            file_path,
1547                            callback)));
1548}
1549
1550void FileSystem::OnGetEntryInfoCompleteForOpenFile(
1551    const base::FilePath& file_path,
1552    const OpenFileCallback& callback,
1553    FileError error,
1554    scoped_ptr<ResourceEntry> entry) {
1555  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1556  DCHECK(!callback.is_null());
1557  DCHECK(entry.get() || error != FILE_ERROR_OK);
1558
1559  if (entry.get() && !entry->has_file_specific_info())
1560    error = FILE_ERROR_NOT_FOUND;
1561
1562  if (error == FILE_ERROR_OK) {
1563    if (entry->file_specific_info().file_md5().empty() ||
1564        entry->file_specific_info().is_hosted_document()) {
1565      // No support for opening a directory or hosted document.
1566      error = FILE_ERROR_INVALID_OPERATION;
1567    }
1568  }
1569
1570  if (error != FILE_ERROR_OK) {
1571    callback.Run(error, base::FilePath());
1572    return;
1573  }
1574
1575  DCHECK(!entry->resource_id().empty());
1576  // Extract a pointer before we call Pass() so we can use it below.
1577  ResourceEntry* entry_ptr = entry.get();
1578  GetResolvedFileByPath(
1579      make_scoped_ptr(new GetResolvedFileParams(
1580          file_path,
1581          DriveClientContext(USER_INITIATED),
1582          entry.Pass(),
1583          GetFileContentInitializedCallback(),
1584          base::Bind(&FileSystem::OnGetFileCompleteForOpenFile,
1585                     weak_ptr_factory_.GetWeakPtr(),
1586                     GetFileCompleteForOpenParams(
1587                         callback,
1588                         entry_ptr->resource_id(),
1589                         entry_ptr->file_specific_info().file_md5())),
1590          google_apis::GetContentCallback())));
1591}
1592
1593void FileSystem::OnGetFileCompleteForOpenFile(
1594    const GetFileCompleteForOpenParams& params,
1595    FileError error,
1596    const base::FilePath& file_path,
1597    const std::string& mime_type,
1598    DriveFileType file_type) {
1599  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1600  DCHECK(!params.callback.is_null());
1601
1602  if (error != FILE_ERROR_OK) {
1603    params.callback.Run(error, base::FilePath());
1604    return;
1605  }
1606
1607  // OpenFile ensures that the file is a regular file.
1608  DCHECK_EQ(REGULAR_FILE, file_type);
1609
1610  cache_->MarkDirty(
1611      params.resource_id,
1612      params.md5,
1613      base::Bind(&FileSystem::OnMarkDirtyInCacheCompleteForOpenFile,
1614                 weak_ptr_factory_.GetWeakPtr(),
1615                 params));
1616}
1617
1618void FileSystem::OnMarkDirtyInCacheCompleteForOpenFile(
1619    const GetFileCompleteForOpenParams& params,
1620    FileError error) {
1621  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1622  DCHECK(!params.callback.is_null());
1623
1624  if (error != FILE_ERROR_OK) {
1625    params.callback.Run(error, base::FilePath());
1626    return;
1627  }
1628
1629  cache_->GetFile(params.resource_id, params.md5, params.callback);
1630}
1631
1632void FileSystem::OnOpenFileFinished(
1633    const base::FilePath& file_path,
1634    const OpenFileCallback& callback,
1635    FileError result,
1636    const base::FilePath& cache_file_path) {
1637  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1638  DCHECK(!callback.is_null());
1639
1640  // All the invocation of |callback| from operations initiated from OpenFile
1641  // must go through here. Removes the |file_path| from the remembered set when
1642  // the file was not successfully opened.
1643  if (result != FILE_ERROR_OK)
1644    open_files_.erase(file_path);
1645
1646  callback.Run(result, cache_file_path);
1647}
1648
1649void FileSystem::CloseFile(const base::FilePath& file_path,
1650                           const FileOperationCallback& callback) {
1651  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1652  DCHECK(!callback.is_null());
1653
1654  if (open_files_.find(file_path) == open_files_.end()) {
1655    // The file is not being opened.
1656    base::MessageLoopProxy::current()->PostTask(
1657        FROM_HERE,
1658        base::Bind(callback, FILE_ERROR_NOT_FOUND));
1659    return;
1660  }
1661
1662  // Step 1 of CloseFile: Get resource_id and md5 for |file_path|.
1663  resource_metadata_->GetEntryInfoByPath(
1664      file_path,
1665      base::Bind(&FileSystem::CloseFileAfterGetEntryInfo,
1666                 weak_ptr_factory_.GetWeakPtr(),
1667                 file_path,
1668                 base::Bind(&FileSystem::CloseFileFinalize,
1669                            weak_ptr_factory_.GetWeakPtr(),
1670                            file_path,
1671                            callback)));
1672}
1673
1674void FileSystem::CloseFileAfterGetEntryInfo(
1675    const base::FilePath& file_path,
1676    const FileOperationCallback& callback,
1677    FileError error,
1678    scoped_ptr<ResourceEntry> entry) {
1679  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1680  DCHECK(!callback.is_null());
1681
1682  if (entry.get() && !entry->has_file_specific_info())
1683    error = FILE_ERROR_NOT_FOUND;
1684
1685  if (error != FILE_ERROR_OK) {
1686    callback.Run(error);
1687    return;
1688  }
1689
1690  // Step 2 of CloseFile: Commit the modification in cache. This will trigger
1691  // background upload.
1692  // TODO(benchan,kinaba): Call ClearDirtyInCache instead of CommitDirtyInCache
1693  // if the file has not been modified. Come up with a way to detect the
1694  // intactness effectively, or provide a method for user to declare it when
1695  // calling CloseFile().
1696  cache_->CommitDirty(entry->resource_id(),
1697                      entry->file_specific_info().file_md5(),
1698                      callback);
1699}
1700
1701void FileSystem::CloseFileFinalize(const base::FilePath& file_path,
1702                                   const FileOperationCallback& callback,
1703                                   FileError result) {
1704  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1705  DCHECK(!callback.is_null());
1706
1707  // Step 3 of CloseFile.
1708  // All the invocation of |callback| from operations initiated from CloseFile
1709  // must go through here. Removes the |file_path| from the remembered set so
1710  // that subsequent operations can open the file again.
1711  open_files_.erase(file_path);
1712
1713  // Then invokes the user-supplied callback function.
1714  callback.Run(result);
1715}
1716
1717void FileSystem::CheckLocalModificationAndRun(
1718    scoped_ptr<ResourceEntry> entry,
1719    const GetEntryInfoCallback& callback) {
1720  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1721  DCHECK(entry.get());
1722  DCHECK(!callback.is_null());
1723
1724  // For entries that will never be cached, use the original entry info as is.
1725  if (!entry->has_file_specific_info() ||
1726      entry->file_specific_info().is_hosted_document()) {
1727    callback.Run(FILE_ERROR_OK, entry.Pass());
1728    return;
1729  }
1730
1731  // Checks if the file is cached and modified locally.
1732  const std::string resource_id = entry->resource_id();
1733  const std::string md5 = entry->file_specific_info().file_md5();
1734  cache_->GetCacheEntry(
1735      resource_id,
1736      md5,
1737      base::Bind(
1738          &FileSystem::CheckLocalModificationAndRunAfterGetCacheEntry,
1739          weak_ptr_factory_.GetWeakPtr(),
1740          base::Passed(&entry),
1741          callback));
1742}
1743
1744void FileSystem::CheckLocalModificationAndRunAfterGetCacheEntry(
1745    scoped_ptr<ResourceEntry> entry,
1746    const GetEntryInfoCallback& callback,
1747    bool success,
1748    const FileCacheEntry& cache_entry) {
1749  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1750  DCHECK(!callback.is_null());
1751
1752  // When no dirty cache is found, use the original entry info as is.
1753  if (!success || !cache_entry.is_dirty()) {
1754    callback.Run(FILE_ERROR_OK, entry.Pass());
1755    return;
1756  }
1757
1758  // Gets the cache file path.
1759  const std::string& resource_id = entry->resource_id();
1760  const std::string& md5 = entry->file_specific_info().file_md5();
1761  cache_->GetFile(
1762      resource_id,
1763      md5,
1764      base::Bind(
1765          &FileSystem::CheckLocalModificationAndRunAfterGetCacheFile,
1766          weak_ptr_factory_.GetWeakPtr(),
1767          base::Passed(&entry),
1768          callback));
1769}
1770
1771void FileSystem::CheckLocalModificationAndRunAfterGetCacheFile(
1772    scoped_ptr<ResourceEntry> entry,
1773    const GetEntryInfoCallback& callback,
1774    FileError error,
1775    const base::FilePath& local_cache_path) {
1776  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1777  DCHECK(!callback.is_null());
1778
1779  // When no dirty cache is found, use the original entry info as is.
1780  if (error != FILE_ERROR_OK) {
1781    callback.Run(FILE_ERROR_OK, entry.Pass());
1782    return;
1783  }
1784
1785  // If the cache is dirty, obtain the file info from the cache file itself.
1786  base::PlatformFileInfo* file_info = new base::PlatformFileInfo;
1787  base::PostTaskAndReplyWithResult(
1788      blocking_task_runner_,
1789      FROM_HERE,
1790      base::Bind(&file_util::GetFileInfo,
1791                 local_cache_path,
1792                 base::Unretained(file_info)),
1793      base::Bind(&FileSystem::CheckLocalModificationAndRunAfterGetFileInfo,
1794                 weak_ptr_factory_.GetWeakPtr(),
1795                 base::Passed(&entry),
1796                 callback,
1797                 base::Owned(file_info)));
1798}
1799
1800void FileSystem::CheckLocalModificationAndRunAfterGetFileInfo(
1801    scoped_ptr<ResourceEntry> entry,
1802    const GetEntryInfoCallback& callback,
1803    base::PlatformFileInfo* file_info,
1804    bool get_file_info_result) {
1805  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1806  DCHECK(!callback.is_null());
1807
1808  if (!get_file_info_result) {
1809    callback.Run(FILE_ERROR_NOT_FOUND, scoped_ptr<ResourceEntry>());
1810    return;
1811  }
1812
1813  PlatformFileInfoProto entry_file_info;
1814  util::ConvertPlatformFileInfoToResourceEntry(*file_info, &entry_file_info);
1815  *entry->mutable_file_info() = entry_file_info;
1816  callback.Run(FILE_ERROR_OK, entry.Pass());
1817}
1818
1819void FileSystem::CancelJobInScheduler(JobID id) {
1820  scheduler_->CancelJob(id);
1821}
1822
1823}  // namespace drive
1824