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::CheckForUpdates(const FileOperationCallback& callback) { |
| 56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 57 DCHECK(!callback.is_null()); |
| 58 |
| 59 if (loaded_ && !IsRefreshing()) |
| 60 Load(DirectoryFetchInfo(), callback); |
| 61 } |
| 62 |
| 63 void ChangeListLoader::LoadIfNeeded( |
| 64 const DirectoryFetchInfo& directory_fetch_info, |
| 65 const FileOperationCallback& callback) { |
| 66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 67 DCHECK(!callback.is_null()); |
| 68 |
| 69 // If feed has already been loaded, for normal feed fetch (= empty |
| 70 // directory_fetch_info), we have nothing to do. For "fast fetch", we need to |
| 71 // schedule a fetching if a feed refresh is currently running, because we |
| 72 // don't want to wait a possibly large delta feed to arrive. |
| 73 if (loaded_ && (directory_fetch_info.empty() || !IsRefreshing())) { |
| 74 base::MessageLoopProxy::current()->PostTask( |
| 75 FROM_HERE, |
| 76 base::Bind(callback, DRIVE_FILE_OK)); |
| 77 return; |
| 78 } |
| 79 Load(directory_fetch_info, 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 |