| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/drive/change_list_loader.h" | 5 #include "chrome/browser/chromeos/drive/change_list_loader.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 | 8 |
| 9 #include "base/callback.h" | 9 #include "base/callback.h" |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) { | 45 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) { |
| 46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 47 observers_.AddObserver(observer); | 47 observers_.AddObserver(observer); |
| 48 } | 48 } |
| 49 | 49 |
| 50 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) { | 50 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) { |
| 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 52 observers_.RemoveObserver(observer); | 52 observers_.RemoveObserver(observer); |
| 53 } | 53 } |
| 54 | 54 |
| 55 void ChangeListLoader::LoadIfNeeded( |
| 56 const DirectoryFetchInfo& directory_fetch_info, |
| 57 const FileOperationCallback& callback) { |
| 58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 59 DCHECK(!callback.is_null()); |
| 60 |
| 61 // If feed has already been loaded, for normal feed fetch (= empty |
| 62 // directory_fetch_info), we have nothing to do. For "fast fetch", we need to |
| 63 // schedule a fetching if a feed refresh is currently running, because we |
| 64 // don't want to wait a possibly large delta feed to arrive. |
| 65 if (loaded_ && (directory_fetch_info.empty() || !IsRefreshing())) { |
| 66 base::MessageLoopProxy::current()->PostTask( |
| 67 FROM_HERE, |
| 68 base::Bind(callback, DRIVE_FILE_OK)); |
| 69 return; |
| 70 } |
| 71 Load(directory_fetch_info, callback); |
| 72 } |
| 73 |
| 74 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) { |
| 75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 76 DCHECK(!callback.is_null()); |
| 77 |
| 78 if (loaded_ && !IsRefreshing()) |
| 79 Load(DirectoryFetchInfo(), callback); |
| 80 } |
| 81 |
| 82 void ChangeListLoader::LoadDirectoryFromServer( |
| 83 const std::string& directory_resource_id, |
| 84 const FileOperationCallback& callback) { |
| 85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 86 DCHECK(!callback.is_null()); |
| 87 |
| 88 // First fetch the latest changestamp to see if this directory needs to be |
| 89 // updated. |
| 90 scheduler_->GetAboutResource( |
| 91 base::Bind( |
| 92 &ChangeListLoader::LoadDirectoryFromServerAfterGetAbout, |
| 93 weak_ptr_factory_.GetWeakPtr(), |
| 94 directory_resource_id, |
| 95 callback)); |
| 96 } |
| 97 |
| 98 void ChangeListLoader::SearchFromServer( |
| 99 const std::string& search_query, |
| 100 const GURL& next_feed, |
| 101 const LoadFeedListCallback& callback) { |
| 102 DCHECK(!callback.is_null()); |
| 103 |
| 104 if (next_feed.is_empty()) { |
| 105 // This is first request for the |search_query|. |
| 106 scheduler_->Search( |
| 107 search_query, |
| 108 base::Bind(&ChangeListLoader::SearchFromServerAfterGetResourceList, |
| 109 weak_ptr_factory_.GetWeakPtr(), callback)); |
| 110 } else { |
| 111 // There is the remaining result so fetch it. |
| 112 scheduler_->ContinueGetResourceList( |
| 113 next_feed, |
| 114 base::Bind(&ChangeListLoader::SearchFromServerAfterGetResourceList, |
| 115 weak_ptr_factory_.GetWeakPtr(), callback)); |
| 116 } |
| 117 } |
| 118 |
| 119 void ChangeListLoader::UpdateFromFeed( |
| 120 scoped_ptr<google_apis::AboutResource> about_resource, |
| 121 ScopedVector<ChangeList> change_lists, |
| 122 bool is_delta_feed, |
| 123 const base::Closure& callback) { |
| 124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 125 DCHECK(!callback.is_null()); |
| 126 |
| 127 change_list_processor_.reset(new ChangeListProcessor(resource_metadata_)); |
| 128 // Don't send directory content change notification while performing |
| 129 // the initial content retrieval. |
| 130 const bool should_notify_changed_directories = is_delta_feed; |
| 131 |
| 132 change_list_processor_->ApplyFeeds( |
| 133 about_resource.Pass(), |
| 134 change_lists.Pass(), |
| 135 is_delta_feed, |
| 136 base::Bind(&ChangeListLoader::NotifyDirectoryChangedAfterApplyFeed, |
| 137 weak_ptr_factory_.GetWeakPtr(), |
| 138 should_notify_changed_directories, |
| 139 callback)); |
| 140 } |
| 141 |
| 142 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info, |
| 143 const FileOperationCallback& callback) { |
| 144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 145 DCHECK(!callback.is_null()); |
| 146 |
| 147 // Check if this is the first time this ChangeListLoader do loading. |
| 148 // Note: IsRefreshing() depends on pending_load_callback_ so check in advance. |
| 149 const bool is_initial_load = (!loaded_ && !IsRefreshing()); |
| 150 |
| 151 // Register the callback function to be called when it is loaded. |
| 152 const std::string& resource_id = directory_fetch_info.resource_id(); |
| 153 pending_load_callback_[resource_id].push_back(callback); |
| 154 |
| 155 // If loading task for |resource_id| is already running, do nothing. |
| 156 if (pending_load_callback_[resource_id].size() > 1) |
| 157 return; |
| 158 |
| 159 // For initial loading, even for directory fetching, we do load the full |
| 160 // feed from the server to sync up. So we register a dummy callback to |
| 161 // indicate that update for full hierarchy is running. |
| 162 if (is_initial_load && !resource_id.empty()) { |
| 163 pending_load_callback_[""].push_back( |
| 164 base::Bind(&util::EmptyFileOperationCallback)); |
| 165 } |
| 166 |
| 167 // Check the current status of local metadata, and start loading if needed. |
| 168 resource_metadata_->GetLargestChangestamp( |
| 169 base::Bind(is_initial_load ? &ChangeListLoader::DoInitialLoad |
| 170 : &ChangeListLoader::DoUpdateLoad, |
| 171 weak_ptr_factory_.GetWeakPtr(), |
| 172 directory_fetch_info)); |
| 173 } |
| 174 |
| 175 void ChangeListLoader::DoInitialLoad( |
| 176 const DirectoryFetchInfo& directory_fetch_info, |
| 177 int64 local_changestamp) { |
| 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 179 |
| 180 if (local_changestamp > 0) { |
| 181 // The local data is usable. Flush callbacks to tell loading was successful. |
| 182 OnChangeListLoadComplete(DRIVE_FILE_OK); |
| 183 |
| 184 // Continues to load from server in background. |
| 185 // Put dummy callbacks to indicate that fetching is still continuing. |
| 186 pending_load_callback_[directory_fetch_info.resource_id()].push_back( |
| 187 base::Bind(&util::EmptyFileOperationCallback)); |
| 188 if (!directory_fetch_info.empty()) { |
| 189 pending_load_callback_[""].push_back( |
| 190 base::Bind(&util::EmptyFileOperationCallback)); |
| 191 } |
| 192 } |
| 193 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); |
| 194 } |
| 195 |
| 196 void ChangeListLoader::DoUpdateLoad( |
| 197 const DirectoryFetchInfo& directory_fetch_info, |
| 198 int64 local_changestamp) { |
| 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 200 |
| 201 if (directory_fetch_info.empty()) { |
| 202 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); |
| 203 } else { |
| 204 // Note: CheckChangestampAndLoadDirectoryIfNeeded regards |
| 205 // last_know_remote_changestamp_ as the remote changestamp. To be precise, |
| 206 // we need to call GetAboutResource() here, as we do in other places like |
| 207 // LoadFromServerIfNeeded or LoadFromDirectory. However, |
| 208 // - It is costly to do GetAboutResource HTTP request every time. |
| 209 // - The chance using an old value is small; it only happens when |
| 210 // LoadIfNeeded is called during one GetAboutResource roundtrip time |
| 211 // of a feed fetching. |
| 212 // - Even if the value is old, it just marks the directory as older. It may |
| 213 // trigger one future unnecessary re-fetch, but it'll never lose data. |
| 214 CheckChangestampAndLoadDirectoryIfNeeed( |
| 215 directory_fetch_info, |
| 216 local_changestamp, |
| 217 base::Bind(&ChangeListLoader::OnDirectoryLoadComplete, |
| 218 weak_ptr_factory_.GetWeakPtr(), |
| 219 directory_fetch_info)); |
| 220 } |
| 221 } |
| 222 |
| 223 void ChangeListLoader::OnChangeListLoadComplete(DriveFileError error) { |
| 224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 225 |
| 226 if (!loaded_ && error == DRIVE_FILE_OK) { |
| 227 loaded_ = true; |
| 228 FOR_EACH_OBSERVER(ChangeListLoaderObserver, |
| 229 observers_, |
| 230 OnInitialFeedLoaded()); |
| 231 } |
| 232 |
| 233 for (LoadCallbackMap::iterator it = pending_load_callback_.begin(); |
| 234 it != pending_load_callback_.end(); ++it) { |
| 235 const std::vector<FileOperationCallback>& callbacks = it->second; |
| 236 for (size_t i = 0; i < callbacks.size(); ++i) { |
| 237 base::MessageLoopProxy::current()->PostTask( |
| 238 FROM_HERE, |
| 239 base::Bind(callbacks[i], error)); |
| 240 } |
| 241 } |
| 242 pending_load_callback_.clear(); |
| 243 } |
| 244 |
| 245 void ChangeListLoader::OnDirectoryLoadComplete( |
| 246 const DirectoryFetchInfo& directory_fetch_info, |
| 247 DriveFileError error) { |
| 248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 249 |
| 250 DVLOG_IF(1, error == DRIVE_FILE_OK) << "Fast-fetch was successful: " |
| 251 << directory_fetch_info.ToString(); |
| 252 |
| 253 const std::string& resource_id = directory_fetch_info.resource_id(); |
| 254 LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id); |
| 255 if (it != pending_load_callback_.end()) { |
| 256 DVLOG(1) << "Running callback for " << resource_id; |
| 257 const std::vector<FileOperationCallback>& callbacks = it->second; |
| 258 for (size_t i = 0; i < callbacks.size(); ++i) { |
| 259 base::MessageLoopProxy::current()->PostTask( |
| 260 FROM_HERE, |
| 261 base::Bind(callbacks[i], error)); |
| 262 } |
| 263 pending_load_callback_.erase(it); |
| 264 } |
| 265 } |
| 266 |
| 55 void ChangeListLoader::LoadFromServerIfNeeded( | 267 void ChangeListLoader::LoadFromServerIfNeeded( |
| 56 const DirectoryFetchInfo& directory_fetch_info, | 268 const DirectoryFetchInfo& directory_fetch_info, |
| 57 int64 local_changestamp) { | 269 int64 local_changestamp) { |
| 58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 59 | 271 |
| 60 // Drive v2 needs a separate application list fetch operation. | 272 // Drive v2 needs a separate application list fetch operation. |
| 61 // On GData WAPI, it is not necessary in theory, because the response | 273 // On GData WAPI, it is not necessary in theory, because the response |
| 62 // of account metadata can include both about account information (such as | 274 // of account metadata can include both about account information (such as |
| 63 // quota) and an application list at once. | 275 // quota) and an application list at once. |
| 64 // However, for Drive API v2 migration, we connect to the server twice | 276 // However, for Drive API v2 migration, we connect to the server twice |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 local_changestamp, | 345 local_changestamp, |
| 134 base::Bind( | 346 base::Bind( |
| 135 &ChangeListLoader::LoadChangeListFromServerAfterLoadDirectory, | 347 &ChangeListLoader::LoadChangeListFromServerAfterLoadDirectory, |
| 136 weak_ptr_factory_.GetWeakPtr(), | 348 weak_ptr_factory_.GetWeakPtr(), |
| 137 directory_fetch_info, | 349 directory_fetch_info, |
| 138 base::Passed(&about_resource), | 350 base::Passed(&about_resource), |
| 139 start_changestamp)); | 351 start_changestamp)); |
| 140 } | 352 } |
| 141 } | 353 } |
| 142 | 354 |
| 355 void ChangeListLoader::LoadChangeListFromServerAfterLoadDirectory( |
| 356 const DirectoryFetchInfo& directory_fetch_info, |
| 357 scoped_ptr<google_apis::AboutResource> about_resource, |
| 358 int64 start_changestamp, |
| 359 DriveFileError error) { |
| 360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 361 |
| 362 if (error == DRIVE_FILE_OK) { |
| 363 // The directory fast-fetch succeeded. Runs the callbacks waiting for the |
| 364 // directory loading. If failed, do not flush so they're run after the |
| 365 // change list loading is complete. |
| 366 OnDirectoryLoadComplete(directory_fetch_info, DRIVE_FILE_OK); |
| 367 } |
| 368 LoadChangeListFromServer(about_resource.Pass(), start_changestamp); |
| 369 } |
| 370 |
| 143 void ChangeListLoader::LoadChangeListFromServer( | 371 void ChangeListLoader::LoadChangeListFromServer( |
| 144 scoped_ptr<google_apis::AboutResource> about_resource, | 372 scoped_ptr<google_apis::AboutResource> about_resource, |
| 145 int64 start_changestamp) { | 373 int64 start_changestamp) { |
| 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 147 | 375 |
| 148 bool is_delta_feed = start_changestamp != 0; | 376 bool is_delta_feed = start_changestamp != 0; |
| 149 const LoadFeedListCallback& completion_callback = | 377 const LoadFeedListCallback& completion_callback = |
| 150 base::Bind(&ChangeListLoader::UpdateMetadataFromFeedAfterLoadFromServer, | 378 base::Bind(&ChangeListLoader::UpdateMetadataFromFeedAfterLoadFromServer, |
| 151 weak_ptr_factory_.GetWeakPtr(), | 379 weak_ptr_factory_.GetWeakPtr(), |
| 152 base::Passed(&about_resource), | 380 base::Passed(&about_resource), |
| (...skipping 11 matching lines...) Expand all Loading... |
| 164 // This is full feed fetch. | 392 // This is full feed fetch. |
| 165 scheduler_->GetAllResourceList( | 393 scheduler_->GetAllResourceList( |
| 166 base::Bind(&ChangeListLoader::OnGetResourceList, | 394 base::Bind(&ChangeListLoader::OnGetResourceList, |
| 167 weak_ptr_factory_.GetWeakPtr(), | 395 weak_ptr_factory_.GetWeakPtr(), |
| 168 base::Passed(ScopedVector<ChangeList>()), | 396 base::Passed(ScopedVector<ChangeList>()), |
| 169 completion_callback, | 397 completion_callback, |
| 170 start_time)); | 398 start_time)); |
| 171 } | 399 } |
| 172 } | 400 } |
| 173 | 401 |
| 174 void ChangeListLoader::LoadChangeListFromServerAfterLoadDirectory( | |
| 175 const DirectoryFetchInfo& directory_fetch_info, | |
| 176 scoped_ptr<google_apis::AboutResource> about_resource, | |
| 177 int64 start_changestamp, | |
| 178 DriveFileError error) { | |
| 179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 180 | |
| 181 if (error == DRIVE_FILE_OK) { | |
| 182 // The directory fast-fetch succeeded. Runs the callbacks waiting for the | |
| 183 // directory loading. If failed, do not flush so they're run after the | |
| 184 // change list loading is complete. | |
| 185 OnDirectoryLoadComplete(directory_fetch_info, DRIVE_FILE_OK); | |
| 186 } | |
| 187 LoadChangeListFromServer(about_resource.Pass(), start_changestamp); | |
| 188 } | |
| 189 | |
| 190 void ChangeListLoader::SearchFromServerAfterGetResourceList( | |
| 191 const LoadFeedListCallback& callback, | |
| 192 google_apis::GDataErrorCode status, | |
| 193 scoped_ptr<google_apis::ResourceList> resource_list) { | |
| 194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 195 DCHECK(!callback.is_null()); | |
| 196 | |
| 197 DriveFileError error = util::GDataToDriveFileError(status); | |
| 198 if (error != DRIVE_FILE_OK) { | |
| 199 callback.Run(ScopedVector<ChangeList>(), error); | |
| 200 return; | |
| 201 } | |
| 202 | |
| 203 DCHECK(resource_list); | |
| 204 | |
| 205 ScopedVector<ChangeList> change_lists; | |
| 206 change_lists.push_back(new ChangeList(*resource_list)); | |
| 207 callback.Run(change_lists.Pass(), DRIVE_FILE_OK); | |
| 208 } | |
| 209 | |
| 210 void ChangeListLoader::OnGetAppList(google_apis::GDataErrorCode status, | |
| 211 scoped_ptr<google_apis::AppList> app_list) { | |
| 212 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 213 | |
| 214 DriveFileError error = util::GDataToDriveFileError(status); | |
| 215 if (error != DRIVE_FILE_OK) | |
| 216 return; | |
| 217 | |
| 218 if (app_list.get()) { | |
| 219 webapps_registry_->UpdateFromAppList(*app_list); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 void ChangeListLoader::OnGetResourceList( | 402 void ChangeListLoader::OnGetResourceList( |
| 224 ScopedVector<ChangeList> change_lists, | 403 ScopedVector<ChangeList> change_lists, |
| 225 const LoadFeedListCallback& callback, | 404 const LoadFeedListCallback& callback, |
| 226 base::TimeTicks start_time, | 405 base::TimeTicks start_time, |
| 227 google_apis::GDataErrorCode status, | 406 google_apis::GDataErrorCode status, |
| 228 scoped_ptr<google_apis::ResourceList> resource_list) { | 407 scoped_ptr<google_apis::ResourceList> resource_list) { |
| 229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 408 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 230 DCHECK(!callback.is_null()); | 409 DCHECK(!callback.is_null()); |
| 231 | 410 |
| 232 // Looks the UMA stats we take here is useless as many methods use this | 411 // Looks the UMA stats we take here is useless as many methods use this |
| (...skipping 28 matching lines...) Expand all Loading... |
| 261 } | 440 } |
| 262 | 441 |
| 263 // This UMA stats looks also different from what we want. crbug.com/229407 | 442 // This UMA stats looks also different from what we want. crbug.com/229407 |
| 264 UMA_HISTOGRAM_TIMES("Drive.EntireFeedLoadTime", | 443 UMA_HISTOGRAM_TIMES("Drive.EntireFeedLoadTime", |
| 265 base::TimeTicks::Now() - start_time); | 444 base::TimeTicks::Now() - start_time); |
| 266 | 445 |
| 267 // Run the callback so the client can process the retrieved feeds. | 446 // Run the callback so the client can process the retrieved feeds. |
| 268 callback.Run(change_lists.Pass(), DRIVE_FILE_OK); | 447 callback.Run(change_lists.Pass(), DRIVE_FILE_OK); |
| 269 } | 448 } |
| 270 | 449 |
| 271 void ChangeListLoader::LoadDirectoryFromServer( | 450 void ChangeListLoader::UpdateMetadataFromFeedAfterLoadFromServer( |
| 272 const std::string& directory_resource_id, | 451 scoped_ptr<google_apis::AboutResource> about_resource, |
| 273 const FileOperationCallback& callback) { | 452 bool is_delta_feed, |
| 453 ScopedVector<ChangeList> change_lists, |
| 454 DriveFileError error) { |
| 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 455 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 275 DCHECK(!callback.is_null()); | |
| 276 | 456 |
| 277 // First fetch the latest changestamp to see if this directory needs to be | 457 if (error != DRIVE_FILE_OK) { |
| 278 // updated. | 458 OnChangeListLoadComplete(error); |
| 279 scheduler_->GetAboutResource( | 459 return; |
| 280 base::Bind( | 460 } |
| 281 &ChangeListLoader::LoadDirectoryFromServerAfterGetAbout, | 461 |
| 282 weak_ptr_factory_.GetWeakPtr(), | 462 UpdateFromFeed(about_resource.Pass(), |
| 283 directory_resource_id, | 463 change_lists.Pass(), |
| 284 callback)); | 464 is_delta_feed, |
| 465 base::Bind(&ChangeListLoader::OnUpdateFromFeed, |
| 466 weak_ptr_factory_.GetWeakPtr())); |
| 467 } |
| 468 |
| 469 void ChangeListLoader::OnUpdateFromFeed() { |
| 470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 471 |
| 472 OnChangeListLoadComplete(DRIVE_FILE_OK); |
| 473 |
| 474 FOR_EACH_OBSERVER(ChangeListLoaderObserver, |
| 475 observers_, |
| 476 OnFeedFromServerLoaded()); |
| 285 } | 477 } |
| 286 | 478 |
| 287 void ChangeListLoader::LoadDirectoryFromServerAfterGetAbout( | 479 void ChangeListLoader::LoadDirectoryFromServerAfterGetAbout( |
| 288 const std::string& directory_resource_id, | 480 const std::string& directory_resource_id, |
| 289 const FileOperationCallback& callback, | 481 const FileOperationCallback& callback, |
| 290 google_apis::GDataErrorCode status, | 482 google_apis::GDataErrorCode status, |
| 291 scoped_ptr<google_apis::AboutResource> about_resource) { | 483 scoped_ptr<google_apis::AboutResource> about_resource) { |
| 292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 293 DCHECK(!callback.is_null()); | 485 DCHECK(!callback.is_null()); |
| 294 | 486 |
| 295 if (util::GDataToDriveFileError(status) == DRIVE_FILE_OK) { | 487 if (util::GDataToDriveFileError(status) == DRIVE_FILE_OK) { |
| 296 DCHECK(about_resource); | 488 DCHECK(about_resource); |
| 297 last_known_remote_changestamp_ = about_resource->largest_change_id(); | 489 last_known_remote_changestamp_ = about_resource->largest_change_id(); |
| 298 } | 490 } |
| 299 | 491 |
| 300 const DirectoryFetchInfo directory_fetch_info( | 492 const DirectoryFetchInfo directory_fetch_info( |
| 301 directory_resource_id, | 493 directory_resource_id, |
| 302 last_known_remote_changestamp_); | 494 last_known_remote_changestamp_); |
| 303 DoLoadDirectoryFromServer(directory_fetch_info, callback); | 495 DoLoadDirectoryFromServer(directory_fetch_info, callback); |
| 304 } | 496 } |
| 305 | 497 |
| 498 void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeed( |
| 499 const DirectoryFetchInfo& directory_fetch_info, |
| 500 int64 local_changestamp, |
| 501 const FileOperationCallback& callback) { |
| 502 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 503 DCHECK(!directory_fetch_info.empty()); |
| 504 |
| 505 int64 directory_changestamp = std::max(directory_fetch_info.changestamp(), |
| 506 local_changestamp); |
| 507 |
| 508 // If the directory's changestamp is up-to-date, just schedule to run the |
| 509 // callback, as there is no need to fetch the directory. |
| 510 // Note that |last_known_remote_changestamp_| is 0 when it is not received |
| 511 // yet. In that case we conservatively assume that we need to fetch. |
| 512 if (last_known_remote_changestamp_ > 0 && |
| 513 directory_changestamp >= last_known_remote_changestamp_) { |
| 514 callback.Run(DRIVE_FILE_OK); |
| 515 return; |
| 516 } |
| 517 |
| 518 DVLOG(1) << "Fast-fetching directory: " << directory_fetch_info.ToString() |
| 519 << "; remote_changestamp: " << last_known_remote_changestamp_; |
| 520 |
| 521 // Start fetching the directory content, and mark it with the changestamp |
| 522 // |last_known_remote_changestamp_|. |
| 523 DirectoryFetchInfo new_directory_fetch_info( |
| 524 directory_fetch_info.resource_id(), |
| 525 std::max(directory_changestamp, last_known_remote_changestamp_)); |
| 526 DoLoadDirectoryFromServer(new_directory_fetch_info, callback); |
| 527 } |
| 528 |
| 306 void ChangeListLoader::DoLoadDirectoryFromServer( | 529 void ChangeListLoader::DoLoadDirectoryFromServer( |
| 307 const DirectoryFetchInfo& directory_fetch_info, | 530 const DirectoryFetchInfo& directory_fetch_info, |
| 308 const FileOperationCallback& callback) { | 531 const FileOperationCallback& callback) { |
| 309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 532 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 310 DCHECK(!callback.is_null()); | 533 DCHECK(!callback.is_null()); |
| 311 DCHECK(!directory_fetch_info.empty()); | 534 DCHECK(!directory_fetch_info.empty()); |
| 312 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString(); | 535 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString(); |
| 313 | 536 |
| 314 if (directory_fetch_info.resource_id() == | 537 if (directory_fetch_info.resource_id() == |
| 315 util::kDriveOtherDirSpecialResourceId) { | 538 util::kDriveOtherDirSpecialResourceId) { |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 452 | 675 |
| 453 DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString(); | 676 DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString(); |
| 454 callback.Run(error); | 677 callback.Run(error); |
| 455 // Also notify the observers. | 678 // Also notify the observers. |
| 456 if (error == DRIVE_FILE_OK) { | 679 if (error == DRIVE_FILE_OK) { |
| 457 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, | 680 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, |
| 458 OnDirectoryChanged(directory_path)); | 681 OnDirectoryChanged(directory_path)); |
| 459 } | 682 } |
| 460 } | 683 } |
| 461 | 684 |
| 462 void ChangeListLoader::SearchFromServer( | 685 void ChangeListLoader::OnGetAppList(google_apis::GDataErrorCode status, |
| 463 const std::string& search_query, | 686 scoped_ptr<google_apis::AppList> app_list) { |
| 464 const GURL& next_feed, | 687 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 465 const LoadFeedListCallback& callback) { | 688 |
| 689 DriveFileError error = util::GDataToDriveFileError(status); |
| 690 if (error != DRIVE_FILE_OK) |
| 691 return; |
| 692 |
| 693 if (app_list.get()) |
| 694 webapps_registry_->UpdateFromAppList(*app_list); |
| 695 } |
| 696 |
| 697 void ChangeListLoader::SearchFromServerAfterGetResourceList( |
| 698 const LoadFeedListCallback& callback, |
| 699 google_apis::GDataErrorCode status, |
| 700 scoped_ptr<google_apis::ResourceList> resource_list) { |
| 701 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 466 DCHECK(!callback.is_null()); | 702 DCHECK(!callback.is_null()); |
| 467 | 703 |
| 468 if (next_feed.is_empty()) { | 704 DriveFileError error = util::GDataToDriveFileError(status); |
| 469 // This is first request for the |search_query|. | |
| 470 scheduler_->Search( | |
| 471 search_query, | |
| 472 base::Bind(&ChangeListLoader::SearchFromServerAfterGetResourceList, | |
| 473 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 474 } else { | |
| 475 // There is the remaining result so fetch it. | |
| 476 scheduler_->ContinueGetResourceList( | |
| 477 next_feed, | |
| 478 base::Bind(&ChangeListLoader::SearchFromServerAfterGetResourceList, | |
| 479 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 void ChangeListLoader::UpdateMetadataFromFeedAfterLoadFromServer( | |
| 484 scoped_ptr<google_apis::AboutResource> about_resource, | |
| 485 bool is_delta_feed, | |
| 486 ScopedVector<ChangeList> change_lists, | |
| 487 DriveFileError error) { | |
| 488 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 489 | |
| 490 if (error != DRIVE_FILE_OK) { | 705 if (error != DRIVE_FILE_OK) { |
| 491 OnChangeListLoadComplete(error); | 706 callback.Run(ScopedVector<ChangeList>(), error); |
| 492 return; | 707 return; |
| 493 } | 708 } |
| 494 | 709 |
| 495 UpdateFromFeed(about_resource.Pass(), | 710 DCHECK(resource_list); |
| 496 change_lists.Pass(), | |
| 497 is_delta_feed, | |
| 498 base::Bind(&ChangeListLoader::OnUpdateFromFeed, | |
| 499 weak_ptr_factory_.GetWeakPtr())); | |
| 500 } | |
| 501 | 711 |
| 502 void ChangeListLoader::LoadIfNeeded( | 712 ScopedVector<ChangeList> change_lists; |
| 503 const DirectoryFetchInfo& directory_fetch_info, | 713 change_lists.push_back(new ChangeList(*resource_list)); |
| 504 const FileOperationCallback& callback) { | 714 callback.Run(change_lists.Pass(), DRIVE_FILE_OK); |
| 505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 506 DCHECK(!callback.is_null()); | |
| 507 | |
| 508 // If feed has already been loaded, for normal feed fetch (= empty | |
| 509 // directory_fetch_info), we have nothing to do. For "fast fetch", we need to | |
| 510 // schedule a fetching if a feed refresh is currently running, because we | |
| 511 // don't want to wait a possibly large delta feed to arrive. | |
| 512 if (loaded_ && (directory_fetch_info.empty() || !IsRefreshing())) { | |
| 513 base::MessageLoopProxy::current()->PostTask( | |
| 514 FROM_HERE, | |
| 515 base::Bind(callback, DRIVE_FILE_OK)); | |
| 516 return; | |
| 517 } | |
| 518 Load(directory_fetch_info, callback); | |
| 519 } | |
| 520 | |
| 521 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info, | |
| 522 const FileOperationCallback& callback) { | |
| 523 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 524 DCHECK(!callback.is_null()); | |
| 525 | |
| 526 // Check if this is the first time this ChangeListLoader do loading. | |
| 527 // Note: IsRefreshing() depends on pending_load_callback_ so check in advance. | |
| 528 const bool is_initial_load = (!loaded_ && !IsRefreshing()); | |
| 529 | |
| 530 // Register the callback function to be called when it is loaded. | |
| 531 const std::string& resource_id = directory_fetch_info.resource_id(); | |
| 532 pending_load_callback_[resource_id].push_back(callback); | |
| 533 | |
| 534 // If loading task for |resource_id| is already running, do nothing. | |
| 535 if (pending_load_callback_[resource_id].size() > 1) | |
| 536 return; | |
| 537 | |
| 538 // For initial loading, even for directory fetching, we do load the full | |
| 539 // feed from the server to sync up. So we register a dummy callback to | |
| 540 // indicate that update for full hierarchy is running. | |
| 541 if (is_initial_load && !resource_id.empty()) { | |
| 542 pending_load_callback_[""].push_back( | |
| 543 base::Bind(&util::EmptyFileOperationCallback)); | |
| 544 } | |
| 545 | |
| 546 // Check the current status of local metadata, and start loading if needed. | |
| 547 resource_metadata_->GetLargestChangestamp( | |
| 548 base::Bind(is_initial_load ? &ChangeListLoader::DoInitialLoad | |
| 549 : &ChangeListLoader::DoUpdateLoad, | |
| 550 weak_ptr_factory_.GetWeakPtr(), | |
| 551 directory_fetch_info)); | |
| 552 } | |
| 553 | |
| 554 void ChangeListLoader::DoInitialLoad( | |
| 555 const DirectoryFetchInfo& directory_fetch_info, | |
| 556 int64 local_changestamp) { | |
| 557 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 558 | |
| 559 if (local_changestamp > 0) { | |
| 560 // The local data is usable. Flush callbacks to tell loading was successful. | |
| 561 OnChangeListLoadComplete(DRIVE_FILE_OK); | |
| 562 | |
| 563 // Continues to load from server in background. | |
| 564 // Put dummy callbacks to indicate that fetching is still continuing. | |
| 565 pending_load_callback_[directory_fetch_info.resource_id()].push_back( | |
| 566 base::Bind(&util::EmptyFileOperationCallback)); | |
| 567 if (!directory_fetch_info.empty()) { | |
| 568 pending_load_callback_[""].push_back( | |
| 569 base::Bind(&util::EmptyFileOperationCallback)); | |
| 570 } | |
| 571 } | |
| 572 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); | |
| 573 } | |
| 574 | |
| 575 void ChangeListLoader::DoUpdateLoad( | |
| 576 const DirectoryFetchInfo& directory_fetch_info, | |
| 577 int64 local_changestamp) { | |
| 578 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 579 | |
| 580 if (directory_fetch_info.empty()) { | |
| 581 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); | |
| 582 } else { | |
| 583 // Note: CheckChangestampAndLoadDirectoryIfNeeded regards | |
| 584 // last_know_remote_changestamp_ as the remote changestamp. To be precise, | |
| 585 // we need to call GetAboutResource() here, as we do in other places like | |
| 586 // LoadFromServerIfNeeded or LoadFromDirectory. However, | |
| 587 // - It is costly to do GetAboutResource HTTP request every time. | |
| 588 // - The chance using an old value is small; it only happens when | |
| 589 // LoadIfNeeded is called during one GetAboutResource roundtrip time | |
| 590 // of a feed fetching. | |
| 591 // - Even if the value is old, it just marks the directory as older. It may | |
| 592 // trigger one future unnecessary re-fetch, but it'll never lose data. | |
| 593 CheckChangestampAndLoadDirectoryIfNeeed( | |
| 594 directory_fetch_info, | |
| 595 local_changestamp, | |
| 596 base::Bind(&ChangeListLoader::OnDirectoryLoadComplete, | |
| 597 weak_ptr_factory_.GetWeakPtr(), | |
| 598 directory_fetch_info)); | |
| 599 } | |
| 600 } | |
| 601 | |
| 602 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) { | |
| 603 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 604 DCHECK(!callback.is_null()); | |
| 605 | |
| 606 if (loaded_ && !IsRefreshing()) | |
| 607 Load(DirectoryFetchInfo(), callback); | |
| 608 } | |
| 609 | |
| 610 void ChangeListLoader::UpdateFromFeed( | |
| 611 scoped_ptr<google_apis::AboutResource> about_resource, | |
| 612 ScopedVector<ChangeList> change_lists, | |
| 613 bool is_delta_feed, | |
| 614 const base::Closure& update_finished_callback) { | |
| 615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 616 DCHECK(!update_finished_callback.is_null()); | |
| 617 | |
| 618 change_list_processor_.reset(new ChangeListProcessor(resource_metadata_)); | |
| 619 // Don't send directory content change notification while performing | |
| 620 // the initial content retrieval. | |
| 621 const bool should_notify_changed_directories = is_delta_feed; | |
| 622 | |
| 623 change_list_processor_->ApplyFeeds( | |
| 624 about_resource.Pass(), | |
| 625 change_lists.Pass(), | |
| 626 is_delta_feed, | |
| 627 base::Bind(&ChangeListLoader::NotifyDirectoryChangedAfterApplyFeed, | |
| 628 weak_ptr_factory_.GetWeakPtr(), | |
| 629 should_notify_changed_directories, | |
| 630 update_finished_callback)); | |
| 631 } | |
| 632 | |
| 633 void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeed( | |
| 634 const DirectoryFetchInfo& directory_fetch_info, | |
| 635 int64 local_changestamp, | |
| 636 const FileOperationCallback& callback) { | |
| 637 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 638 DCHECK(!directory_fetch_info.empty()); | |
| 639 | |
| 640 int64 directory_changestamp = std::max(directory_fetch_info.changestamp(), | |
| 641 local_changestamp); | |
| 642 | |
| 643 // If the directory's changestamp is up-to-date, just schedule to run the | |
| 644 // callback, as there is no need to fetch the directory. | |
| 645 // Note that |last_known_remote_changestamp_| is 0 when it is not received | |
| 646 // yet. In that case we conservatively assume that we need to fetch. | |
| 647 if (last_known_remote_changestamp_ > 0 && | |
| 648 directory_changestamp >= last_known_remote_changestamp_) { | |
| 649 callback.Run(DRIVE_FILE_OK); | |
| 650 return; | |
| 651 } | |
| 652 | |
| 653 DVLOG(1) << "Fast-fetching directory: " << directory_fetch_info.ToString() | |
| 654 << "; remote_changestamp: " << last_known_remote_changestamp_; | |
| 655 | |
| 656 // Start fetching the directory content, and mark it with the changestamp | |
| 657 // |last_known_remote_changestamp_|. | |
| 658 DirectoryFetchInfo new_directory_fetch_info( | |
| 659 directory_fetch_info.resource_id(), | |
| 660 std::max(directory_changestamp, last_known_remote_changestamp_)); | |
| 661 DoLoadDirectoryFromServer(new_directory_fetch_info, callback); | |
| 662 } | 715 } |
| 663 | 716 |
| 664 void ChangeListLoader::NotifyDirectoryChangedAfterApplyFeed( | 717 void ChangeListLoader::NotifyDirectoryChangedAfterApplyFeed( |
| 665 bool should_notify_changed_directories, | 718 bool should_notify_changed_directories, |
| 666 const base::Closure& update_finished_callback) { | 719 const base::Closure& callback) { |
| 667 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 720 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 668 DCHECK(change_list_processor_.get()); | 721 DCHECK(change_list_processor_.get()); |
| 669 DCHECK(!update_finished_callback.is_null()); | 722 DCHECK(!callback.is_null()); |
| 670 | 723 |
| 671 if (should_notify_changed_directories) { | 724 if (should_notify_changed_directories) { |
| 672 for (std::set<base::FilePath>::iterator dir_iter = | 725 for (std::set<base::FilePath>::iterator dir_iter = |
| 673 change_list_processor_->changed_dirs().begin(); | 726 change_list_processor_->changed_dirs().begin(); |
| 674 dir_iter != change_list_processor_->changed_dirs().end(); | 727 dir_iter != change_list_processor_->changed_dirs().end(); |
| 675 ++dir_iter) { | 728 ++dir_iter) { |
| 676 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, | 729 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, |
| 677 OnDirectoryChanged(*dir_iter)); | 730 OnDirectoryChanged(*dir_iter)); |
| 678 } | 731 } |
| 679 } | 732 } |
| 680 | 733 |
| 681 update_finished_callback.Run(); | 734 callback.Run(); |
| 682 | 735 |
| 683 // Cannot delete change_list_processor_ yet because we are in | 736 // Cannot delete change_list_processor_ yet because we are in |
| 684 // on_complete_callback_, which is owned by change_list_processor_. | 737 // on_complete_callback_, which is owned by change_list_processor_. |
| 685 } | 738 } |
| 686 | 739 |
| 687 void ChangeListLoader::OnUpdateFromFeed() { | |
| 688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 689 | |
| 690 OnChangeListLoadComplete(DRIVE_FILE_OK); | |
| 691 | |
| 692 FOR_EACH_OBSERVER(ChangeListLoaderObserver, | |
| 693 observers_, | |
| 694 OnFeedFromServerLoaded()); | |
| 695 } | |
| 696 | |
| 697 void ChangeListLoader::OnChangeListLoadComplete(DriveFileError error) { | |
| 698 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 699 | |
| 700 if (!loaded_ && error == DRIVE_FILE_OK) { | |
| 701 loaded_ = true; | |
| 702 FOR_EACH_OBSERVER(ChangeListLoaderObserver, | |
| 703 observers_, | |
| 704 OnInitialFeedLoaded()); | |
| 705 } | |
| 706 | |
| 707 for (LoadCallbackMap::iterator it = pending_load_callback_.begin(); | |
| 708 it != pending_load_callback_.end(); ++it) { | |
| 709 const std::vector<FileOperationCallback>& callbacks = it->second; | |
| 710 for (size_t i = 0; i < callbacks.size(); ++i) { | |
| 711 base::MessageLoopProxy::current()->PostTask( | |
| 712 FROM_HERE, | |
| 713 base::Bind(callbacks[i], error)); | |
| 714 } | |
| 715 } | |
| 716 pending_load_callback_.clear(); | |
| 717 } | |
| 718 | |
| 719 void ChangeListLoader::OnDirectoryLoadComplete( | |
| 720 const DirectoryFetchInfo& directory_fetch_info, | |
| 721 DriveFileError error) { | |
| 722 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 723 | |
| 724 DVLOG_IF(1, error == DRIVE_FILE_OK) << "Fast-fetch was successful: " | |
| 725 << directory_fetch_info.ToString(); | |
| 726 | |
| 727 const std::string& resource_id = directory_fetch_info.resource_id(); | |
| 728 LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id); | |
| 729 if (it != pending_load_callback_.end()) { | |
| 730 DVLOG(1) << "Running callback for " << resource_id; | |
| 731 const std::vector<FileOperationCallback>& callbacks = it->second; | |
| 732 for (size_t i = 0; i < callbacks.size(); ++i) { | |
| 733 base::MessageLoopProxy::current()->PostTask( | |
| 734 FROM_HERE, | |
| 735 base::Bind(callbacks[i], error)); | |
| 736 } | |
| 737 pending_load_callback_.erase(it); | |
| 738 } | |
| 739 } | |
| 740 | |
| 741 } // namespace drive | 740 } // namespace drive |
| OLD | NEW |