Home | History | Annotate | Download | only in drive
      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/change_list_loader.h"
      6 
      7 #include <set>
      8 
      9 #include "base/callback.h"
     10 #include "base/callback_helpers.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "chrome/browser/chromeos/drive/change_list_loader_observer.h"
     14 #include "chrome/browser/chromeos/drive/change_list_processor.h"
     15 #include "chrome/browser/chromeos/drive/file_system_util.h"
     16 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     17 #include "chrome/browser/chromeos/drive/logging.h"
     18 #include "chrome/browser/chromeos/drive/resource_metadata.h"
     19 #include "chrome/browser/drive/drive_api_util.h"
     20 #include "chrome/browser/google_apis/drive_api_parser.h"
     21 #include "content/public/browser/browser_thread.h"
     22 #include "url/gurl.h"
     23 
     24 using content::BrowserThread;
     25 
     26 namespace drive {
     27 namespace internal {
     28 
     29 ChangeListLoader::ChangeListLoader(
     30     base::SequencedTaskRunner* blocking_task_runner,
     31     ResourceMetadata* resource_metadata,
     32     JobScheduler* scheduler)
     33     : blocking_task_runner_(blocking_task_runner),
     34       resource_metadata_(resource_metadata),
     35       scheduler_(scheduler),
     36       last_known_remote_changestamp_(0),
     37       loaded_(false),
     38       weak_ptr_factory_(this) {
     39 }
     40 
     41 ChangeListLoader::~ChangeListLoader() {
     42 }
     43 
     44 bool ChangeListLoader::IsRefreshing() const {
     45   // Callback for change list loading is stored in pending_load_callback_[""].
     46   // It is non-empty if and only if there is an in-flight loading operation.
     47   return pending_load_callback_.find("") != pending_load_callback_.end();
     48 }
     49 
     50 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) {
     51   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     52   observers_.AddObserver(observer);
     53 }
     54 
     55 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
     56   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     57   observers_.RemoveObserver(observer);
     58 }
     59 
     60 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) {
     61   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     62   DCHECK(!callback.is_null());
     63 
     64   if (IsRefreshing()) {
     65     // There is in-flight loading. So keep the callback here, and check for
     66     // updates when the in-flight loading is completed.
     67     pending_update_check_callback_ = callback;
     68     return;
     69   }
     70 
     71   if (loaded_) {
     72     // We only start to check for updates iff the load is done.
     73     // I.e., we ignore checking updates if not loaded to avoid starting the
     74     // load without user's explicit interaction (such as opening Drive).
     75     util::Log(logging::LOG_INFO, "Checking for updates");
     76     Load(DirectoryFetchInfo(), callback);
     77   }
     78 }
     79 
     80 void ChangeListLoader::LoadIfNeeded(
     81     const DirectoryFetchInfo& directory_fetch_info,
     82     const FileOperationCallback& callback) {
     83   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     84   DCHECK(!callback.is_null());
     85 
     86   // If the resource metadata has been already loaded, for normal change list
     87   // fetch (= empty directory_fetch_info), we have nothing to do. For "fast
     88   // fetch", we need to schedule a fetching if a refresh is currently
     89   // running, because we don't want to wait a possibly large delta change
     90   // list to arrive.
     91   if (loaded_ && (directory_fetch_info.empty() || !IsRefreshing())) {
     92     base::MessageLoopProxy::current()->PostTask(
     93         FROM_HERE,
     94         base::Bind(callback, FILE_ERROR_OK));
     95     return;
     96   }
     97   Load(directory_fetch_info, callback);
     98 }
     99 
    100 void ChangeListLoader::LoadDirectoryFromServer(
    101     const std::string& directory_resource_id,
    102     const FileOperationCallback& callback) {
    103   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    104   DCHECK(!callback.is_null());
    105 
    106   scheduler_->GetAboutResource(
    107       base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterGetAbout,
    108                  weak_ptr_factory_.GetWeakPtr(),
    109                  directory_resource_id,
    110                  callback));
    111 }
    112 
    113 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info,
    114                             const FileOperationCallback& callback) {
    115   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    116   DCHECK(!callback.is_null());
    117 
    118   // Check if this is the first time this ChangeListLoader do loading.
    119   // Note: IsRefreshing() depends on pending_load_callback_ so check in advance.
    120   const bool is_initial_load = (!loaded_ && !IsRefreshing());
    121 
    122   // Register the callback function to be called when it is loaded.
    123   const std::string& resource_id = directory_fetch_info.resource_id();
    124   pending_load_callback_[resource_id].push_back(callback);
    125 
    126   // If loading task for |resource_id| is already running, do nothing.
    127   if (pending_load_callback_[resource_id].size() > 1)
    128     return;
    129 
    130   // For initial loading, even for directory fetching, we do load the full
    131   // resource list from the server to sync up. So we register a dummy
    132   // callback to indicate that update for full hierarchy is running.
    133   if (is_initial_load && !resource_id.empty()) {
    134     pending_load_callback_[""].push_back(
    135         base::Bind(&util::EmptyFileOperationCallback));
    136   }
    137 
    138   // Check the current status of local metadata, and start loading if needed.
    139   resource_metadata_->GetLargestChangestampOnUIThread(
    140       base::Bind(is_initial_load ? &ChangeListLoader::DoInitialLoad
    141                                  : &ChangeListLoader::DoUpdateLoad,
    142                  weak_ptr_factory_.GetWeakPtr(),
    143                  directory_fetch_info));
    144 }
    145 
    146 void ChangeListLoader::DoInitialLoad(
    147     const DirectoryFetchInfo& directory_fetch_info,
    148     int64 local_changestamp) {
    149   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    150 
    151   if (local_changestamp > 0) {
    152     // The local data is usable. Flush callbacks to tell loading was successful.
    153     OnChangeListLoadComplete(FILE_ERROR_OK);
    154 
    155     // Continues to load from server in background.
    156     // Put dummy callbacks to indicate that fetching is still continuing.
    157     pending_load_callback_[directory_fetch_info.resource_id()].push_back(
    158         base::Bind(&util::EmptyFileOperationCallback));
    159     if (!directory_fetch_info.empty()) {
    160       pending_load_callback_[""].push_back(
    161           base::Bind(&util::EmptyFileOperationCallback));
    162     }
    163   }
    164   LoadFromServerIfNeeded(directory_fetch_info, local_changestamp);
    165 }
    166 
    167 void ChangeListLoader::DoUpdateLoad(
    168     const DirectoryFetchInfo& directory_fetch_info,
    169     int64 local_changestamp) {
    170   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    171 
    172   if (directory_fetch_info.empty()) {
    173     LoadFromServerIfNeeded(directory_fetch_info, local_changestamp);
    174   } else {
    175     // Note: CheckChangestampAndLoadDirectoryIfNeeded regards
    176     // last_know_remote_changestamp_ as the remote changestamp. To be precise,
    177     // we need to call GetAboutResource() here, as we do in other places like
    178     // LoadFromServerIfNeeded or LoadFromDirectory. However,
    179     // - It is costly to do GetAboutResource HTTP request every time.
    180     // - The chance using an old value is small; it only happens when
    181     //   LoadIfNeeded is called during one GetAboutResource roundtrip time
    182     //   of a change list fetching.
    183     // - Even if the value is old, it just marks the directory as older. It may
    184     //   trigger one future unnecessary re-fetch, but it'll never lose data.
    185     CheckChangestampAndLoadDirectoryIfNeeded(
    186         directory_fetch_info,
    187         local_changestamp,
    188         base::Bind(&ChangeListLoader::OnDirectoryLoadComplete,
    189                    weak_ptr_factory_.GetWeakPtr(),
    190                    directory_fetch_info));
    191   }
    192 }
    193 
    194 void ChangeListLoader::OnChangeListLoadComplete(FileError error) {
    195   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    196 
    197   if (!loaded_ && error == FILE_ERROR_OK) {
    198     loaded_ = true;
    199     FOR_EACH_OBSERVER(ChangeListLoaderObserver,
    200                       observers_,
    201                       OnInitialLoadComplete());
    202   }
    203 
    204   for (LoadCallbackMap::iterator it = pending_load_callback_.begin();
    205        it != pending_load_callback_.end();  ++it) {
    206     const std::vector<FileOperationCallback>& callbacks = it->second;
    207     for (size_t i = 0; i < callbacks.size(); ++i) {
    208       base::MessageLoopProxy::current()->PostTask(
    209           FROM_HERE,
    210           base::Bind(callbacks[i], error));
    211     }
    212   }
    213   pending_load_callback_.clear();
    214 
    215   // If there is pending update check, try to load the change from the server
    216   // again, because there may exist an update during the completed loading.
    217   if (!pending_update_check_callback_.is_null()) {
    218     Load(DirectoryFetchInfo(),
    219          base::ResetAndReturn(&pending_update_check_callback_));
    220   }
    221 }
    222 
    223 void ChangeListLoader::OnDirectoryLoadComplete(
    224     const DirectoryFetchInfo& directory_fetch_info,
    225     FileError error) {
    226   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    227 
    228   util::Log(logging::LOG_INFO,
    229             "Fast-fetch complete: %s => %s",
    230             directory_fetch_info.ToString().c_str(),
    231             FileErrorToString(error).c_str());
    232   const std::string& resource_id = directory_fetch_info.resource_id();
    233   LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id);
    234   if (it != pending_load_callback_.end()) {
    235     DVLOG(1) << "Running callback for " << resource_id;
    236     const std::vector<FileOperationCallback>& callbacks = it->second;
    237     for (size_t i = 0; i < callbacks.size(); ++i) {
    238       base::MessageLoopProxy::current()->PostTask(
    239           FROM_HERE,
    240           base::Bind(callbacks[i], error));
    241     }
    242     pending_load_callback_.erase(it);
    243   }
    244 }
    245 
    246 void ChangeListLoader::LoadFromServerIfNeeded(
    247     const DirectoryFetchInfo& directory_fetch_info,
    248     int64 local_changestamp) {
    249   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    250 
    251   // First fetch the latest changestamp to see if there were any new changes
    252   // there at all.
    253   scheduler_->GetAboutResource(
    254       base::Bind(&ChangeListLoader::LoadFromServerIfNeededAfterGetAbout,
    255                  weak_ptr_factory_.GetWeakPtr(),
    256                  directory_fetch_info,
    257                  local_changestamp));
    258 }
    259 
    260 void ChangeListLoader::LoadFromServerIfNeededAfterGetAbout(
    261     const DirectoryFetchInfo& directory_fetch_info,
    262     int64 local_changestamp,
    263     google_apis::GDataErrorCode status,
    264     scoped_ptr<google_apis::AboutResource> about_resource) {
    265   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    266   DCHECK_EQ(GDataToFileError(status) == FILE_ERROR_OK,
    267             about_resource.get() != NULL);
    268 
    269   if (GDataToFileError(status) == FILE_ERROR_OK) {
    270     DCHECK(about_resource);
    271     last_known_remote_changestamp_ = about_resource->largest_change_id();
    272   }
    273 
    274   int64 remote_changestamp =
    275       about_resource ? about_resource->largest_change_id() : 0;
    276   if (remote_changestamp > 0 && local_changestamp >= remote_changestamp) {
    277     if (local_changestamp > remote_changestamp) {
    278       LOG(WARNING) << "Local resource metadata is fresher than server, local = "
    279                    << local_changestamp
    280                    << ", server = "
    281                    << remote_changestamp;
    282     }
    283 
    284     // No changes detected, tell the client that the loading was successful.
    285     OnChangeListLoadComplete(FILE_ERROR_OK);
    286     return;
    287   }
    288 
    289   int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0;
    290   if (start_changestamp == 0 && !about_resource.get()) {
    291     // Full update needs AboutResource. If this is a full update, we should
    292     // just give up. Note that to exit from the change list loading, we
    293     // always have to flush the pending callback tasks via
    294     // OnChangeListLoadComplete.
    295     OnChangeListLoadComplete(FILE_ERROR_FAILED);
    296     return;
    297   }
    298 
    299   if (directory_fetch_info.empty()) {
    300     // If the caller is not interested in a particular directory, just start
    301     // loading the change list.
    302     LoadChangeListFromServer(about_resource.Pass(), start_changestamp);
    303   } else {
    304     // If the caller is interested in a particular directory, start loading the
    305     // directory first.
    306     CheckChangestampAndLoadDirectoryIfNeeded(
    307         directory_fetch_info,
    308         local_changestamp,
    309         base::Bind(
    310             &ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory,
    311             weak_ptr_factory_.GetWeakPtr(),
    312             directory_fetch_info,
    313             base::Passed(&about_resource),
    314             start_changestamp));
    315   }
    316 }
    317 
    318 void ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory(
    319     const DirectoryFetchInfo& directory_fetch_info,
    320     scoped_ptr<google_apis::AboutResource> about_resource,
    321     int64 start_changestamp,
    322     FileError error) {
    323   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    324 
    325   if (error == FILE_ERROR_OK) {
    326     // The directory fast-fetch succeeded. Runs the callbacks waiting for the
    327     // directory loading. If failed, do not flush so they're run after the
    328     // change list loading is complete.
    329     OnDirectoryLoadComplete(directory_fetch_info, FILE_ERROR_OK);
    330   }
    331   LoadChangeListFromServer(about_resource.Pass(), start_changestamp);
    332 }
    333 
    334 void ChangeListLoader::LoadChangeListFromServer(
    335     scoped_ptr<google_apis::AboutResource> about_resource,
    336     int64 start_changestamp) {
    337   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    338 
    339   bool is_delta_update = start_changestamp != 0;
    340   const LoadChangeListCallback& completion_callback =
    341       base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList,
    342                  weak_ptr_factory_.GetWeakPtr(),
    343                  base::Passed(&about_resource),
    344                  is_delta_update);
    345   base::TimeTicks start_time = base::TimeTicks::Now();
    346   if (is_delta_update) {
    347     scheduler_->GetChangeList(
    348         start_changestamp,
    349         base::Bind(&ChangeListLoader::OnGetChangeList,
    350                    weak_ptr_factory_.GetWeakPtr(),
    351                    base::Passed(ScopedVector<ChangeList>()),
    352                    completion_callback,
    353                    start_time));
    354   } else {
    355     // This is full resource list fetch.
    356     scheduler_->GetAllResourceList(
    357         base::Bind(&ChangeListLoader::OnGetChangeList,
    358                    weak_ptr_factory_.GetWeakPtr(),
    359                    base::Passed(ScopedVector<ChangeList>()),
    360                    completion_callback,
    361                    start_time));
    362   }
    363 }
    364 
    365 void ChangeListLoader::OnGetChangeList(
    366     ScopedVector<ChangeList> change_lists,
    367     const LoadChangeListCallback& callback,
    368     base::TimeTicks start_time,
    369     google_apis::GDataErrorCode status,
    370     scoped_ptr<google_apis::ResourceList> resource_list) {
    371   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    372   DCHECK(!callback.is_null());
    373 
    374   // Looks the UMA stats we take here is useless as many methods use this
    375   // callback. crbug.com/229407
    376   if (change_lists.empty()) {
    377     UMA_HISTOGRAM_TIMES("Drive.InitialFeedLoadTime",
    378                         base::TimeTicks::Now() - start_time);
    379   }
    380 
    381   FileError error = GDataToFileError(status);
    382   if (error != FILE_ERROR_OK) {
    383     callback.Run(ScopedVector<ChangeList>(), error);
    384     return;
    385   }
    386 
    387   // Add the current change list to the list of collected lists.
    388   DCHECK(resource_list);
    389   change_lists.push_back(new ChangeList(*resource_list));
    390 
    391   GURL next_url;
    392   if (resource_list->GetNextFeedURL(&next_url) &&
    393       !next_url.is_empty()) {
    394     // There is the remaining result so fetch it.
    395     scheduler_->ContinueGetResourceList(
    396         next_url,
    397         base::Bind(&ChangeListLoader::OnGetChangeList,
    398                    weak_ptr_factory_.GetWeakPtr(),
    399                    base::Passed(&change_lists),
    400                    callback,
    401                    start_time));
    402     return;
    403   }
    404 
    405   // This UMA stats looks also different from what we want. crbug.com/229407
    406   UMA_HISTOGRAM_TIMES("Drive.EntireFeedLoadTime",
    407                       base::TimeTicks::Now() - start_time);
    408 
    409   // Run the callback so the client can process the retrieved change lists.
    410   callback.Run(change_lists.Pass(), FILE_ERROR_OK);
    411 }
    412 
    413 void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList(
    414     scoped_ptr<google_apis::AboutResource> about_resource,
    415     bool is_delta_update,
    416     ScopedVector<ChangeList> change_lists,
    417     FileError error) {
    418   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    419 
    420   if (error != FILE_ERROR_OK) {
    421     OnChangeListLoadComplete(error);
    422     return;
    423   }
    424 
    425   UpdateFromChangeList(
    426       about_resource.Pass(),
    427       change_lists.Pass(),
    428       is_delta_update,
    429       base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate,
    430                  weak_ptr_factory_.GetWeakPtr()));
    431 }
    432 
    433 void ChangeListLoader::LoadChangeListFromServerAfterUpdate() {
    434   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    435 
    436   OnChangeListLoadComplete(FILE_ERROR_OK);
    437 
    438   FOR_EACH_OBSERVER(ChangeListLoaderObserver,
    439                     observers_,
    440                     OnLoadFromServerComplete());
    441 }
    442 
    443 void ChangeListLoader::LoadDirectoryFromServerAfterGetAbout(
    444     const std::string& directory_resource_id,
    445     const FileOperationCallback& callback,
    446     google_apis::GDataErrorCode status,
    447     scoped_ptr<google_apis::AboutResource> about_resource) {
    448   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    449   DCHECK(!callback.is_null());
    450 
    451   if (GDataToFileError(status) == FILE_ERROR_OK)
    452     last_known_remote_changestamp_ = about_resource->largest_change_id();
    453 
    454   DoLoadDirectoryFromServer(
    455       DirectoryFetchInfo(directory_resource_id, last_known_remote_changestamp_),
    456       callback);
    457 }
    458 
    459 void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeded(
    460     const DirectoryFetchInfo& directory_fetch_info,
    461     int64 local_changestamp,
    462     const FileOperationCallback& callback) {
    463   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    464   DCHECK(!directory_fetch_info.empty());
    465 
    466   int64 directory_changestamp = std::max(directory_fetch_info.changestamp(),
    467                                          local_changestamp);
    468 
    469   // We may not fetch from the server at all if the local metadata is new
    470   // enough, but we log this message here, so "Fast-fetch start" and
    471   // "Fast-fetch complete" always match.
    472   // TODO(satorux): Distinguish the "not fetching at all" case.
    473   util::Log(logging::LOG_INFO,
    474             "Fast-fetch start: %s; Server changestamp: %s",
    475             directory_fetch_info.ToString().c_str(),
    476             base::Int64ToString(last_known_remote_changestamp_).c_str());
    477 
    478   // If the directory's changestamp is up-to-date, just schedule to run the
    479   // callback, as there is no need to fetch the directory.
    480   // Note that |last_known_remote_changestamp_| is 0 when it is not received
    481   // yet. In that case we conservatively assume that we need to fetch.
    482   if (last_known_remote_changestamp_ > 0 &&
    483       directory_changestamp >= last_known_remote_changestamp_) {
    484     callback.Run(FILE_ERROR_OK);
    485     return;
    486   }
    487 
    488   // Start fetching the directory content, and mark it with the changestamp
    489   // |last_known_remote_changestamp_|.
    490   DirectoryFetchInfo new_directory_fetch_info(
    491       directory_fetch_info.resource_id(),
    492       std::max(directory_changestamp, last_known_remote_changestamp_));
    493   DoLoadDirectoryFromServer(new_directory_fetch_info, callback);
    494 }
    495 
    496 void ChangeListLoader::DoLoadDirectoryFromServer(
    497     const DirectoryFetchInfo& directory_fetch_info,
    498     const FileOperationCallback& callback) {
    499   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    500   DCHECK(!callback.is_null());
    501   DCHECK(!directory_fetch_info.empty());
    502   DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();
    503 
    504   if (directory_fetch_info.resource_id() ==
    505           util::kDriveOtherDirSpecialResourceId) {
    506     // Load for a <other> directory is meaningless in the server.
    507     // Let it succeed and use what we have locally.
    508     callback.Run(FILE_ERROR_OK);
    509     return;
    510   }
    511 
    512   if (directory_fetch_info.resource_id() ==
    513           util::kDriveGrandRootSpecialResourceId) {
    514     // Load for a grand root directory means slightly different from other
    515     // directories. It should have two directories; <other> and mydrive root.
    516     // <other> directory should always exist, but mydrive root should be
    517     // created by root resource id retrieved from the server.
    518     // Here, we check if mydrive root exists, and if not, create it.
    519     resource_metadata_->GetResourceEntryByPathOnUIThread(
    520         base::FilePath(util::GetDriveMyDriveRootPath()),
    521         base::Bind(
    522             &ChangeListLoader
    523                 ::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath,
    524             weak_ptr_factory_.GetWeakPtr(),
    525             directory_fetch_info,
    526             callback));
    527     return;
    528   }
    529 
    530   const LoadChangeListCallback& completion_callback =
    531       base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterLoad,
    532                  weak_ptr_factory_.GetWeakPtr(),
    533                  directory_fetch_info,
    534                  callback);
    535   base::TimeTicks start_time = base::TimeTicks::Now();
    536   scheduler_->GetResourceListInDirectory(
    537       directory_fetch_info.resource_id(),
    538       base::Bind(&ChangeListLoader::OnGetChangeList,
    539                  weak_ptr_factory_.GetWeakPtr(),
    540                  base::Passed(ScopedVector<ChangeList>()),
    541                  completion_callback,
    542                  start_time));
    543 }
    544 
    545 void
    546 ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath(
    547     const DirectoryFetchInfo& directory_fetch_info,
    548     const FileOperationCallback& callback,
    549     FileError error,
    550     scoped_ptr<ResourceEntry> entry) {
    551   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    552   DCHECK(!callback.is_null());
    553   DCHECK_EQ(directory_fetch_info.resource_id(),
    554             util::kDriveGrandRootSpecialResourceId);
    555 
    556   if (error == FILE_ERROR_OK) {
    557     // MyDrive root already exists. Just return success.
    558     callback.Run(FILE_ERROR_OK);
    559     return;
    560   }
    561 
    562   // Fetch root resource id from the server.
    563   scheduler_->GetAboutResource(
    564       base::Bind(
    565           &ChangeListLoader
    566               ::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource,
    567           weak_ptr_factory_.GetWeakPtr(),
    568           directory_fetch_info,
    569           callback));
    570 }
    571 
    572 void ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource(
    573     const DirectoryFetchInfo& directory_fetch_info,
    574     const FileOperationCallback& callback,
    575     google_apis::GDataErrorCode status,
    576     scoped_ptr<google_apis::AboutResource> about_resource) {
    577   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    578   DCHECK(!callback.is_null());
    579   DCHECK_EQ(directory_fetch_info.resource_id(),
    580             util::kDriveGrandRootSpecialResourceId);
    581 
    582   FileError error = GDataToFileError(status);
    583   if (error != FILE_ERROR_OK) {
    584     callback.Run(error);
    585     return;
    586   }
    587 
    588   // Build entry map for grand root directory, which has two entries;
    589   // "/drive/root" and "/drive/other".
    590   ResourceEntryMap grand_root_entry_map;
    591   const std::string& root_resource_id = about_resource->root_folder_id();
    592   grand_root_entry_map[root_resource_id] =
    593       util::CreateMyDriveRootEntry(root_resource_id);
    594   grand_root_entry_map[util::kDriveOtherDirSpecialResourceId] =
    595       util::CreateOtherDirEntry();
    596   resource_metadata_->RefreshDirectoryOnUIThread(
    597       directory_fetch_info,
    598       grand_root_entry_map,
    599       base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh,
    600                  weak_ptr_factory_.GetWeakPtr(),
    601                  directory_fetch_info,
    602                  callback));
    603 }
    604 
    605 void ChangeListLoader::DoLoadDirectoryFromServerAfterLoad(
    606     const DirectoryFetchInfo& directory_fetch_info,
    607     const FileOperationCallback& callback,
    608     ScopedVector<ChangeList> change_lists,
    609     FileError error) {
    610   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    611   DCHECK(!callback.is_null());
    612   DCHECK(!directory_fetch_info.empty());
    613 
    614   if (error != FILE_ERROR_OK) {
    615     LOG(ERROR) << "Failed to load directory: "
    616                << directory_fetch_info.resource_id()
    617                << ": " << FileErrorToString(error);
    618     callback.Run(error);
    619     return;
    620   }
    621 
    622   ChangeListProcessor::ResourceEntryMap entry_map;
    623   ChangeListProcessor::ConvertToMap(change_lists.Pass(), &entry_map, NULL);
    624   resource_metadata_->RefreshDirectoryOnUIThread(
    625       directory_fetch_info,
    626       entry_map,
    627       base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh,
    628                  weak_ptr_factory_.GetWeakPtr(),
    629                  directory_fetch_info,
    630                  callback));
    631 }
    632 
    633 void ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh(
    634     const DirectoryFetchInfo& directory_fetch_info,
    635     const FileOperationCallback& callback,
    636     FileError error,
    637     const base::FilePath& directory_path) {
    638   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    639   DCHECK(!callback.is_null());
    640 
    641   DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
    642   callback.Run(error);
    643   // Also notify the observers.
    644   if (error == FILE_ERROR_OK) {
    645     FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
    646                       OnDirectoryChanged(directory_path));
    647   }
    648 }
    649 
    650 void ChangeListLoader::UpdateFromChangeList(
    651     scoped_ptr<google_apis::AboutResource> about_resource,
    652     ScopedVector<ChangeList> change_lists,
    653     bool is_delta_update,
    654     const base::Closure& callback) {
    655   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    656   DCHECK(!callback.is_null());
    657 
    658   ChangeListProcessor* change_list_processor =
    659       new ChangeListProcessor(resource_metadata_);
    660   // Don't send directory content change notification while performing
    661   // the initial content retrieval.
    662   const bool should_notify_changed_directories = is_delta_update;
    663 
    664   util::Log(logging::LOG_INFO,
    665             "Apply change lists (is delta: %d)",
    666             is_delta_update);
    667   blocking_task_runner_->PostTaskAndReply(
    668       FROM_HERE,
    669       base::Bind(&ChangeListProcessor::Apply,
    670                  base::Unretained(change_list_processor),
    671                  base::Passed(&about_resource),
    672                  base::Passed(&change_lists),
    673                  is_delta_update),
    674       base::Bind(&ChangeListLoader::UpdateFromChangeListAfterApply,
    675                  weak_ptr_factory_.GetWeakPtr(),
    676                  base::Owned(change_list_processor),
    677                  should_notify_changed_directories,
    678                  base::Time::Now(),
    679                  callback));
    680 }
    681 
    682 void ChangeListLoader::UpdateFromChangeListAfterApply(
    683     ChangeListProcessor* change_list_processor,
    684     bool should_notify_changed_directories,
    685     base::Time start_time,
    686     const base::Closure& callback) {
    687   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    688   DCHECK(change_list_processor);
    689   DCHECK(!callback.is_null());
    690 
    691   const base::TimeDelta elapsed = base::Time::Now() - start_time;
    692   util::Log(logging::LOG_INFO,
    693             "Change lists applied (elapsed time: %sms)",
    694             base::Int64ToString(elapsed.InMilliseconds()).c_str());
    695 
    696   if (should_notify_changed_directories) {
    697     for (std::set<base::FilePath>::iterator dir_iter =
    698             change_list_processor->changed_dirs().begin();
    699         dir_iter != change_list_processor->changed_dirs().end();
    700         ++dir_iter) {
    701       FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
    702                         OnDirectoryChanged(*dir_iter));
    703     }
    704   }
    705 
    706   callback.Run();
    707 }
    708 
    709 }  // namespace internal
    710 }  // namespace drive
    711