Commit message: None
CVE: None
Vulnerability description: None

Project: Chrome

FileError AddMyDriveIfNeeded(
    ResourceMetadata* resource_metadata,
    const google_apis::AboutResource& about_resource,
    base::FilePath* changed_directory_path) {
  ResourceEntry entry;
  FileError error = resource_metadata->GetResourceEntryByPath(
      util::GetDriveMyDriveRootPath(), &entry);
  if (error != FILE_ERROR_NOT_FOUND)
    return error;

  std::string local_id;
  error = resource_metadata->AddEntry(
      util::CreateMyDriveRootEntry(about_resource.root_folder_id()),
      &local_id);
  if (error != FILE_ERROR_OK)
    return error;

  *changed_directory_path = util::GetDriveGrandRootPath();
  return FILE_ERROR_OK;
}

Project: Chrome

void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  observers_.AddObserver(observer);
}

Project: Chrome

ChangeListLoader::ChangeListLoader(
    base::SequencedTaskRunner* blocking_task_runner,
    ResourceMetadata* resource_metadata,
    JobScheduler* scheduler,
    DriveServiceInterface* drive_service)
    : blocking_task_runner_(blocking_task_runner),
      resource_metadata_(resource_metadata),
      scheduler_(scheduler),
      drive_service_(drive_service),
      loaded_(false),
      weak_ptr_factory_(this) {
}

Project: Chrome

void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());

  if (IsRefreshing()) {
    pending_update_check_callback_ = callback;
    return;
  }

  if (loaded_) {
    util::Log(logging::LOG_INFO, "Checking for updates");
    Load(DirectoryFetchInfo(), callback);
  }
}

Project: Chrome

  DeltaFeedFetcher(JobScheduler* scheduler, int64 start_change_id)
      : scheduler_(scheduler),
        start_change_id_(start_change_id),
        weak_ptr_factory_(this) {
  }

Project: Chrome

  FastFetchFeedFetcher(JobScheduler* scheduler,
                       DriveServiceInterface* drive_service,
                       const std::string& directory_resource_id,
                       const std::string& root_folder_id)
      : scheduler_(scheduler),
        drive_service_(drive_service),
        directory_resource_id_(directory_resource_id),
        root_folder_id_(root_folder_id),
        weak_ptr_factory_(this) {
  }

Project: Chrome

  void FixResourceIdInChangeList(ChangeList* change_list) {
    std::vector<ResourceEntry>* entries = change_list->mutable_entries();
    std::vector<std::string>* parent_resource_ids =
        change_list->mutable_parent_resource_ids();
    for (size_t i = 0; i < entries->size(); ++i) {
      ResourceEntry* entry = &(*entries)[i];
      if (entry->has_resource_id())
        entry->set_resource_id(FixResourceId(entry->resource_id()));

      (*parent_resource_ids)[i] = FixResourceId((*parent_resource_ids)[i]);
    }
  }

Project: Chrome

  explicit FullFeedFetcher(JobScheduler* scheduler)
      : scheduler_(scheduler),
        weak_ptr_factory_(this) {
  }

Project: Chrome

bool ChangeListLoader::IsRefreshing() const {
  return pending_load_callback_.find("") != pending_load_callback_.end();
}

Project: Chrome

void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info,
                            const FileOperationCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());

  const bool is_initial_load = (!loaded_ && !IsRefreshing());

  const std::string& local_id = directory_fetch_info.local_id();
  pending_load_callback_[local_id].push_back(callback);

  if (pending_load_callback_[local_id].size() > 1)
    return;

  if (is_initial_load && !directory_fetch_info.empty()) {
    pending_load_callback_[""].push_back(
        base::Bind(&util::EmptyFileOperationCallback));
  }

  base::PostTaskAndReplyWithResult(
      blocking_task_runner_,
      FROM_HERE,
      base::Bind(&ResourceMetadata::GetLargestChangestamp,
                 base::Unretained(resource_metadata_)),
      base::Bind(&ChangeListLoader::LoadAfterGetLargestChangestamp,
                 weak_ptr_factory_.GetWeakPtr(),
                 directory_fetch_info,
                 is_initial_load));
}

Project: Chrome

void ChangeListLoader::LoadAfterLoadDirectory(
    const DirectoryFetchInfo& directory_fetch_info,
    bool is_initial_load,
    int64 start_changestamp,
    FileError error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  OnDirectoryLoadComplete(directory_fetch_info, error);

  if (is_initial_load)
    LoadChangeListFromServer(start_changestamp);
}

Project: Chrome

void ChangeListLoader::LoadChangeListFromServer(int64 start_changestamp) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!change_feed_fetcher_);
  DCHECK(cached_about_resource_);

  bool is_delta_update = start_changestamp != 0;

  if (is_delta_update) {
    change_feed_fetcher_.reset(
        new DeltaFeedFetcher(scheduler_, start_changestamp));
  } else {
    change_feed_fetcher_.reset(new FullFeedFetcher(scheduler_));
  }

  change_feed_fetcher_->Run(
      base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList,
                 weak_ptr_factory_.GetWeakPtr(),
                 base::Passed(make_scoped_ptr(
                     new google_apis::AboutResource(*cached_about_resource_))),
                 is_delta_update));
}

Project: Chrome

void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList(
    scoped_ptr<google_apis::AboutResource> about_resource,
    bool is_delta_update,
    FileError error,
    ScopedVector<ChangeList> change_lists) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(about_resource);

  change_feed_fetcher_.reset();

  if (error != FILE_ERROR_OK) {
    OnChangeListLoadComplete(error);
    return;
  }

  ChangeListProcessor* change_list_processor =
      new ChangeListProcessor(resource_metadata_);
  const bool should_notify_changed_directories = is_delta_update;

  util::Log(logging::LOG_INFO,
            "Apply change lists (is delta: %d)",
            is_delta_update);
  base::PostTaskAndReplyWithResult(
      blocking_task_runner_,
      FROM_HERE,
      base::Bind(&ChangeListProcessor::Apply,
                 base::Unretained(change_list_processor),
                 base::Passed(&about_resource),
                 base::Passed(&change_lists),
                 is_delta_update),
      base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate,
                 weak_ptr_factory_.GetWeakPtr(),
                 base::Owned(change_list_processor),
                 should_notify_changed_directories,
                 base::Time::Now()));
}

Project: Chrome

void ChangeListLoader::LoadChangeListFromServerAfterUpdate(
    ChangeListProcessor* change_list_processor,
    bool should_notify_changed_directories,
    const base::Time& start_time,
    FileError error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  const base::TimeDelta elapsed = base::Time::Now() - start_time;
  util::Log(logging::LOG_INFO,
            "Change lists applied (elapsed time: %sms)",
            base::Int64ToString(elapsed.InMilliseconds()).c_str());

  if (should_notify_changed_directories) {
    for (std::set<base::FilePath>::iterator dir_iter =
            change_list_processor->changed_dirs().begin();
        dir_iter != change_list_processor->changed_dirs().end();
        ++dir_iter) {
      FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
                        OnDirectoryChanged(*dir_iter));
    }
  }

  OnChangeListLoadComplete(error);

  FOR_EACH_OBSERVER(ChangeListLoaderObserver,
                    observers_,
                    OnLoadFromServerComplete());
}

Project: Chrome

void ChangeListLoader::LoadDirectoryFromServer(
    const DirectoryFetchInfo& directory_fetch_info,
    const FileOperationCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());
  DCHECK(!directory_fetch_info.empty());
  DCHECK(cached_about_resource_);
  DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();

  if (directory_fetch_info.local_id() == util::kDriveGrandRootLocalId) {
    base::FilePath* changed_directory_path = new base::FilePath;
    base::PostTaskAndReplyWithResult(
        blocking_task_runner_,
        FROM_HERE,
        base::Bind(&AddMyDriveIfNeeded,
                   resource_metadata_,
                   google_apis::AboutResource(*cached_about_resource_),
                   changed_directory_path),
        base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh,
                   weak_ptr_factory_.GetWeakPtr(),
                   directory_fetch_info,
                   callback,
                   base::Owned(changed_directory_path)));
    return;
  }

  FastFetchFeedFetcher* fetcher = new FastFetchFeedFetcher(
      scheduler_,
      drive_service_,
      directory_fetch_info.resource_id(),
      cached_about_resource_->root_folder_id());
  fast_fetch_feed_fetcher_set_.insert(fetcher);
  fetcher->Run(
      base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterLoad,
                 weak_ptr_factory_.GetWeakPtr(),
                 directory_fetch_info,
                 callback,
                 fetcher));
}

Project: Chrome

void ChangeListLoader::LoadDirectoryFromServerAfterLoad(
    const DirectoryFetchInfo& directory_fetch_info,
    const FileOperationCallback& callback,
    FeedFetcher* fetcher,
    FileError error,
    ScopedVector<ChangeList> change_lists) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());
  DCHECK(!directory_fetch_info.empty());

  fast_fetch_feed_fetcher_set_.erase(fetcher);
  delete fetcher;

  if (error != FILE_ERROR_OK) {
    LOG(ERROR) << "Failed to load directory: "
               << directory_fetch_info.local_id()
               << ": " << FileErrorToString(error);
    callback.Run(error);
    return;
  }

  base::FilePath* directory_path = new base::FilePath;
  base::PostTaskAndReplyWithResult(
      blocking_task_runner_,
      FROM_HERE,
      base::Bind(&ChangeListProcessor::RefreshDirectory,
                 resource_metadata_,
                 directory_fetch_info,
                 base::Passed(&change_lists),
                 directory_path),
      base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh,
                 weak_ptr_factory_.GetWeakPtr(),
                 directory_fetch_info,
                 callback,
                 base::Owned(directory_path)));
}

Project: Chrome

void ChangeListLoader::LoadDirectoryFromServerAfterRefresh(
    const DirectoryFetchInfo& directory_fetch_info,
    const FileOperationCallback& callback,
    const base::FilePath* directory_path,
    FileError error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());

  DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
  callback.Run(error);
  if (error == FILE_ERROR_OK && !directory_path->empty()) {
    FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
                      OnDirectoryChanged(*directory_path));
  }
}

Project: Chrome

void ChangeListLoader::LoadDirectoryIfNeeded(
    const base::FilePath& directory_path,
    const FileOperationCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());

  if (loaded_ && !IsRefreshing()) {
    callback.Run(FILE_ERROR_OK);
    return;
  }

  ResourceEntry* entry = new ResourceEntry;
  base::PostTaskAndReplyWithResult(
      blocking_task_runner_.get(),
      FROM_HERE,
      base::Bind(&ResourceMetadata::GetResourceEntryByPath,
                 base::Unretained(resource_metadata_),
                 directory_path,
                 entry),
      base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry,
                 weak_ptr_factory_.GetWeakPtr(),
                 directory_path,
                 callback,
                 true,  // should_try_loading_parent
                 base::Owned(entry)));
}

Project: Chrome

void ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry(
    const base::FilePath& directory_path,
    const FileOperationCallback& callback,
    bool should_try_loading_parent,
    const ResourceEntry* entry,
    FileError error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());

  if (error == FILE_ERROR_NOT_FOUND &&
      should_try_loading_parent &&
      util::GetDriveGrandRootPath().IsParent(directory_path)) {
    LoadDirectoryIfNeeded(
        directory_path.DirName(),
        base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent,
                   weak_ptr_factory_.GetWeakPtr(),
                   directory_path,
                   callback));
    return;
  }
  if (error != FILE_ERROR_OK) {
    callback.Run(error);
    return;
  }

  if (!entry->file_info().is_directory()) {
    callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
    return;
  }

  if (entry->local_id() == util::kDriveOtherDirLocalId) {
    callback.Run(FILE_ERROR_OK);
    return;
  }

  Load(DirectoryFetchInfo(entry->local_id(),
                          entry->resource_id(),
                          entry->directory_specific_info().changestamp()),
       callback);
}

Project: Chrome

void ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent(
    const base::FilePath& directory_path,
    const FileOperationCallback& callback,
    FileError error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());

  if (error != FILE_ERROR_OK) {
    callback.Run(error);
    return;
  }

  ResourceEntry* entry = new ResourceEntry;
  base::PostTaskAndReplyWithResult(
      blocking_task_runner_.get(),
      FROM_HERE,
      base::Bind(&ResourceMetadata::GetResourceEntryByPath,
                 base::Unretained(resource_metadata_),
                 directory_path,
                 entry),
      base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry,
                 weak_ptr_factory_.GetWeakPtr(),
                 directory_path,
                 callback,
                 false,  // should_try_loading_parent
                 base::Owned(entry)));
}

Project: Chrome

  void OnChangeListFetched(
      const FeedFetcherCallback& callback,
      google_apis::GDataErrorCode status,
      scoped_ptr<google_apis::ResourceList> resource_list) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    DCHECK(!callback.is_null());

    FileError error = GDataToFileError(status);
    if (error != FILE_ERROR_OK) {
      callback.Run(error, ScopedVector<ChangeList>());
      return;
    }

    DCHECK(resource_list);
    change_lists_.push_back(new ChangeList(*resource_list));

    GURL next_url;
    if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
      scheduler_->GetRemainingChangeList(
          next_url,
          base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
                     weak_ptr_factory_.GetWeakPtr(), callback));
      return;
    }

    callback.Run(FILE_ERROR_OK, change_lists_.Pass());
  }

Project: Chrome

void ChangeListLoader::OnChangeListLoadComplete(FileError error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!loaded_ && error == FILE_ERROR_OK) {
    loaded_ = true;
    FOR_EACH_OBSERVER(ChangeListLoaderObserver,
                      observers_,
                      OnInitialLoadComplete());
  }

  for (LoadCallbackMap::iterator it = pending_load_callback_.begin();
       it != pending_load_callback_.end();  ++it) {
    const std::vector<FileOperationCallback>& callbacks = it->second;
    for (size_t i = 0; i < callbacks.size(); ++i) {
      base::MessageLoopProxy::current()->PostTask(
          FROM_HERE,
          base::Bind(callbacks[i], error));
    }
  }
  pending_load_callback_.clear();

  if (!pending_update_check_callback_.is_null()) {
    Load(DirectoryFetchInfo(),
         base::ResetAndReturn(&pending_update_check_callback_));
  }
}

Project: Chrome

void ChangeListLoader::OnDirectoryLoadComplete(
    const DirectoryFetchInfo& directory_fetch_info,
    FileError error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  util::Log(logging::LOG_INFO,
            "Fast-fetch complete: %s => %s",
            directory_fetch_info.ToString().c_str(),
            FileErrorToString(error).c_str());
  const std::string& local_id = directory_fetch_info.local_id();
  LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
  if (it != pending_load_callback_.end()) {
    DVLOG(1) << "Running callback for " << local_id;
    const std::vector<FileOperationCallback>& callbacks = it->second;
    for (size_t i = 0; i < callbacks.size(); ++i) {
      base::MessageLoopProxy::current()->PostTask(
          FROM_HERE,
          base::Bind(callbacks[i], error));
    }
    pending_load_callback_.erase(it);
  }
}

Project: Chrome

  void OnFileListFetched(
      const FeedFetcherCallback& callback,
      google_apis::GDataErrorCode status,
      scoped_ptr<google_apis::ResourceList> resource_list) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    DCHECK(!callback.is_null());

    FileError error = GDataToFileError(status);
    if (error != FILE_ERROR_OK) {
      callback.Run(error, ScopedVector<ChangeList>());
      return;
    }

    DCHECK(resource_list);
    change_lists_.push_back(new ChangeList(*resource_list));

    GURL next_url;
    if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
      scheduler_->GetRemainingFileList(
          next_url,
          base::Bind(&FullFeedFetcher::OnFileListFetched,
                     weak_ptr_factory_.GetWeakPtr(), callback));
      return;
    }

    UMA_HISTOGRAM_LONG_TIMES("Drive.FullFeedLoadTime",
                             base::TimeTicks::Now() - start_time_);

    callback.Run(FILE_ERROR_OK, change_lists_.Pass());
  }

Project: Chrome

  void OnResourceListFetched(
      const FeedFetcherCallback& callback,
      google_apis::GDataErrorCode status,
      scoped_ptr<google_apis::ResourceList> resource_list) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    DCHECK(!callback.is_null());

    FileError error = GDataToFileError(status);
    if (error != FILE_ERROR_OK) {
      callback.Run(error, ScopedVector<ChangeList>());
      return;
    }

    DCHECK(resource_list);
    ChangeList* change_list = new ChangeList(*resource_list);
    if (util::IsDriveV2ApiEnabled())
      FixResourceIdInChangeList(change_list);
    change_lists_.push_back(change_list);

    GURL next_url;
    if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
      scheduler_->GetRemainingResourceList(
          next_url,
          base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
                     weak_ptr_factory_.GetWeakPtr(), callback));
      return;
    }

    UMA_HISTOGRAM_TIMES("Drive.DirectoryFeedLoadTime",
                        base::TimeTicks::Now() - start_time_);

    callback.Run(FILE_ERROR_OK, change_lists_.Pass());
  }

Project: Chrome

void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  observers_.RemoveObserver(observer);
}

Project: Chrome

void ChangeListLoader::UpdateAboutResource(
    const google_apis::AboutResourceCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());

  scheduler_->GetAboutResource(
      base::Bind(&ChangeListLoader::UpdateAboutResourceAfterGetAbout,
                 weak_ptr_factory_.GetWeakPtr(),
                 callback));
}

Project: Chrome

void ChangeListLoader::UpdateAboutResourceAfterGetAbout(
    const google_apis::AboutResourceCallback& callback,
    google_apis::GDataErrorCode status,
    scoped_ptr<google_apis::AboutResource> about_resource) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!callback.is_null());
  FileError error = GDataToFileError(status);

  if (error == FILE_ERROR_OK) {
    if (cached_about_resource_ &&
        cached_about_resource_->largest_change_id() >
        about_resource->largest_change_id()) {
      LOG(WARNING) << "Local cached about resource is fresher than server, "
                   << "local = " << cached_about_resource_->largest_change_id()
                   << ", server = " << about_resource->largest_change_id();
    }

    cached_about_resource_.reset(
        new google_apis::AboutResource(*about_resource));
  }

  callback.Run(status, about_resource.Pass());
}

Project: Chrome

ChangeListLoader::~ChangeListLoader() {
  STLDeleteElements(&fast_fetch_feed_fetcher_set_);
}

Project: Chrome

  virtual ~DeltaFeedFetcher() {
  }

Project: Chrome

  virtual ~FastFetchFeedFetcher() {
  }

Project: Chrome

  virtual ~FeedFetcher() {}

Project: Chrome

  virtual ~FullFeedFetcher() {
  }