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 10 matching lines...) Expand all Loading... |
21 using content::BrowserThread; | 21 using content::BrowserThread; |
22 | 22 |
23 namespace drive { | 23 namespace drive { |
24 | 24 |
25 ChangeListLoader::ChangeListLoader(DriveResourceMetadata* resource_metadata, | 25 ChangeListLoader::ChangeListLoader(DriveResourceMetadata* resource_metadata, |
26 DriveScheduler* scheduler, | 26 DriveScheduler* scheduler, |
27 DriveWebAppsRegistry* webapps_registry) | 27 DriveWebAppsRegistry* webapps_registry) |
28 : resource_metadata_(resource_metadata), | 28 : resource_metadata_(resource_metadata), |
29 scheduler_(scheduler), | 29 scheduler_(scheduler), |
30 webapps_registry_(webapps_registry), | 30 webapps_registry_(webapps_registry), |
31 refreshing_(false), | |
32 last_known_remote_changestamp_(0), | 31 last_known_remote_changestamp_(0), |
33 loaded_(false), | 32 loaded_(false), |
34 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | 33 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
35 } | 34 } |
36 | 35 |
37 ChangeListLoader::~ChangeListLoader() { | 36 ChangeListLoader::~ChangeListLoader() { |
38 } | 37 } |
39 | 38 |
| 39 bool ChangeListLoader::IsRefreshing() const { |
| 40 // Callback for change list loading is stored in pending_load_callback_[""]. |
| 41 // It is non-empty if and only if there is an in-flight loading operation. |
| 42 return pending_load_callback_.find("") != pending_load_callback_.end(); |
| 43 } |
| 44 |
40 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) { | 45 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) { |
41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
42 observers_.AddObserver(observer); | 47 observers_.AddObserver(observer); |
43 } | 48 } |
44 | 49 |
45 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) { | 50 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) { |
46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
47 observers_.RemoveObserver(observer); | 52 observers_.RemoveObserver(observer); |
48 } | 53 } |
49 | 54 |
50 void ChangeListLoader::LoadFromServerIfNeeded( | 55 void ChangeListLoader::LoadFromServerIfNeeded( |
51 const DirectoryFetchInfo& directory_fetch_info, | 56 const DirectoryFetchInfo& directory_fetch_info, |
52 const FileOperationCallback& callback) { | 57 int64 local_changestamp) { |
53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
54 DCHECK(!callback.is_null()); | |
55 | |
56 // Sets the refreshing flag, so that the caller does not send refresh requests | |
57 // in parallel (see DriveFileSystem::CheckForUpdates). Corresponding | |
58 // "refresh_ = false" is in OnGetAboutResource when the cached feed is up to | |
59 // date, or in OnFeedFromServerLoaded called back from LoadFromServer(). | |
60 refreshing_ = true; | |
61 | 59 |
62 // Drive v2 needs a separate application list fetch operation. | 60 // Drive v2 needs a separate application list fetch operation. |
63 // On GData WAPI, it is not necessary in theory, because the response | 61 // On GData WAPI, it is not necessary in theory, because the response |
64 // of account metadata can include both about account information (such as | 62 // of account metadata can include both about account information (such as |
65 // quota) and an application list at once. | 63 // quota) and an application list at once. |
66 // However, for Drive API v2 migration, we connect to the server twice | 64 // However, for Drive API v2 migration, we connect to the server twice |
67 // (one for about account information and another for an application list) | 65 // (one for about account information and another for an application list) |
68 // regardless of underlying API, so that we can simplify the code. | 66 // regardless of underlying API, so that we can simplify the code. |
69 // Note that the size of account metadata on GData WAPI seems small enough | 67 // Note that the size of account metadata on GData WAPI seems small enough |
70 // and (by controlling the query parameter) the response for GetAboutResource | 68 // and (by controlling the query parameter) the response for GetAboutResource |
71 // operation doesn't contain application list. Thus, the effect should be | 69 // operation doesn't contain application list. Thus, the effect should be |
72 // small cost. | 70 // small cost. |
73 // TODO(haruki): Application list rarely changes and is not necessarily | 71 // TODO(haruki): Application list rarely changes and is not necessarily |
74 // refreshed as often as files. | 72 // refreshed as often as files. |
75 scheduler_->GetAppList( | 73 scheduler_->GetAppList( |
76 base::Bind(&ChangeListLoader::OnGetAppList, | 74 base::Bind(&ChangeListLoader::OnGetAppList, |
77 weak_ptr_factory_.GetWeakPtr())); | 75 weak_ptr_factory_.GetWeakPtr())); |
78 | 76 |
79 // First fetch the latest changestamp to see if there were any new changes | 77 // First fetch the latest changestamp to see if there were any new changes |
80 // there at all. | 78 // there at all. |
81 scheduler_->GetAboutResource( | 79 scheduler_->GetAboutResource( |
82 base::Bind(&ChangeListLoader::LoadFromServerIfNeededAfterGetAbout, | 80 base::Bind(&ChangeListLoader::LoadFromServerIfNeededAfterGetAbout, |
83 weak_ptr_factory_.GetWeakPtr(), | 81 weak_ptr_factory_.GetWeakPtr(), |
84 directory_fetch_info, | 82 directory_fetch_info, |
85 callback)); | 83 local_changestamp)); |
86 } | 84 } |
87 | 85 |
88 void ChangeListLoader::LoadFromServerIfNeededAfterGetAbout( | 86 void ChangeListLoader::LoadFromServerIfNeededAfterGetAbout( |
89 const DirectoryFetchInfo& directory_fetch_info, | 87 const DirectoryFetchInfo& directory_fetch_info, |
90 const FileOperationCallback& callback, | 88 int64 local_changestamp, |
91 google_apis::GDataErrorCode status, | 89 google_apis::GDataErrorCode status, |
92 scoped_ptr<google_apis::AboutResource> about_resource) { | 90 scoped_ptr<google_apis::AboutResource> about_resource) { |
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
94 DCHECK(!callback.is_null()); | |
95 DCHECK(refreshing_); | |
96 DCHECK_EQ(util::GDataToDriveFileError(status) == DRIVE_FILE_OK, | 92 DCHECK_EQ(util::GDataToDriveFileError(status) == DRIVE_FILE_OK, |
97 about_resource.get() != NULL); | 93 about_resource.get() != NULL); |
98 | 94 |
99 if (util::GDataToDriveFileError(status) == DRIVE_FILE_OK) { | 95 if (util::GDataToDriveFileError(status) == DRIVE_FILE_OK) { |
100 DCHECK(about_resource); | 96 DCHECK(about_resource); |
101 last_known_remote_changestamp_ = about_resource->largest_change_id(); | 97 last_known_remote_changestamp_ = about_resource->largest_change_id(); |
102 } | 98 } |
103 | 99 |
104 resource_metadata_->GetLargestChangestamp( | |
105 base::Bind(&ChangeListLoader::CompareChangestampsAndLoadIfNeeded, | |
106 weak_ptr_factory_.GetWeakPtr(), | |
107 directory_fetch_info, | |
108 callback, | |
109 base::Passed(&about_resource))); | |
110 } | |
111 | |
112 void ChangeListLoader::CompareChangestampsAndLoadIfNeeded( | |
113 const DirectoryFetchInfo& directory_fetch_info, | |
114 const FileOperationCallback& callback, | |
115 scoped_ptr<google_apis::AboutResource> about_resource, | |
116 int64 local_changestamp) { | |
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
118 DCHECK(!callback.is_null()); | |
119 DCHECK(refreshing_); | |
120 | |
121 int64 remote_changestamp = | 100 int64 remote_changestamp = |
122 about_resource ? about_resource->largest_change_id() : 0; | 101 about_resource ? about_resource->largest_change_id() : 0; |
123 if (remote_changestamp > 0 && local_changestamp >= remote_changestamp) { | 102 if (remote_changestamp > 0 && local_changestamp >= remote_changestamp) { |
124 if (local_changestamp > remote_changestamp) { | 103 if (local_changestamp > remote_changestamp) { |
125 LOG(WARNING) << "Cached client feed is fresher than server, client = " | 104 LOG(WARNING) << "Cached client feed is fresher than server, client = " |
126 << local_changestamp | 105 << local_changestamp |
127 << ", server = " | 106 << ", server = " |
128 << remote_changestamp; | 107 << remote_changestamp; |
129 } | 108 } |
130 | 109 |
131 // No changes detected, tell the client that the loading was successful. | 110 // No changes detected, tell the client that the loading was successful. |
132 OnChangeListLoadComplete(callback, DRIVE_FILE_OK); | 111 OnChangeListLoadComplete(DRIVE_FILE_OK); |
133 return; | 112 return; |
134 } | 113 } |
135 | 114 |
136 int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0; | 115 int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0; |
137 if (start_changestamp == 0 && !about_resource.get()) { | 116 if (start_changestamp == 0 && !about_resource.get()) { |
138 // Full update needs AboutResource. If this is a full update, we should just | 117 // Full update needs AboutResource. If this is a full update, we should just |
139 // give up. Note that to exit from the feed loading, we always have to flush | 118 // give up. Note that to exit from the feed loading, we always have to flush |
140 // the pending callback tasks via OnChangeListLoadComplete. | 119 // the pending callback tasks via OnChangeListLoadComplete. |
141 OnChangeListLoadComplete(callback, DRIVE_FILE_ERROR_FAILED); | 120 OnChangeListLoadComplete(DRIVE_FILE_ERROR_FAILED); |
142 return; | 121 return; |
143 } | 122 } |
144 | 123 |
145 if (directory_fetch_info.empty()) { | 124 if (directory_fetch_info.empty()) { |
146 // If the caller is not interested in a particular directory, just start | 125 // If the caller is not interested in a particular directory, just start |
147 // loading the change list. | 126 // loading the change list. |
148 LoadChangeListFromServer(about_resource.Pass(), | 127 LoadChangeListFromServer(about_resource.Pass(), start_changestamp); |
149 start_changestamp, | |
150 callback); | |
151 } else if (directory_fetch_info.changestamp() < remote_changestamp) { | |
152 // If the caller is interested in a particular directory, and the | |
153 // directory changestamp is older than server's, start loading the | |
154 // directory first. Skip special entries as they are not meaningful in the | |
155 // server. | |
156 DVLOG(1) << "Fast-fetching directory: " << directory_fetch_info.ToString() | |
157 << "; remote_changestamp: " << remote_changestamp; | |
158 const DirectoryFetchInfo new_directory_fetch_info( | |
159 directory_fetch_info.resource_id(), remote_changestamp); | |
160 DoLoadDirectoryFromServer( | |
161 new_directory_fetch_info, | |
162 base::Bind(&ChangeListLoader::StartLoadChangeListFromServer, | |
163 weak_ptr_factory_.GetWeakPtr(), | |
164 directory_fetch_info, | |
165 base::Passed(&about_resource), | |
166 start_changestamp, | |
167 callback)); | |
168 } else { | 128 } else { |
169 // The directory is up-to-date, but not the case for other parts. | 129 // If the caller is interested in a particular directory, start loading the |
170 // Proceed to change list loading. StartLoadChangeListFromServer will | 130 // directory first. |
171 // run |callback| for notifying the directory is ready before feed load. | 131 CheckChangestampAndLoadDirectoryIfNeeed( |
172 StartLoadChangeListFromServer(directory_fetch_info, | 132 directory_fetch_info, |
173 about_resource.Pass(), | 133 local_changestamp, |
174 start_changestamp, | 134 base::Bind( |
175 callback, | 135 &ChangeListLoader::LoadChangeListFromServerAfterLoadDirectory, |
176 DRIVE_FILE_OK); | 136 weak_ptr_factory_.GetWeakPtr(), |
| 137 directory_fetch_info, |
| 138 base::Passed(&about_resource), |
| 139 start_changestamp)); |
177 } | 140 } |
178 } | 141 } |
179 | 142 |
180 void ChangeListLoader::LoadChangeListFromServer( | 143 void ChangeListLoader::LoadChangeListFromServer( |
181 scoped_ptr<google_apis::AboutResource> about_resource, | 144 scoped_ptr<google_apis::AboutResource> about_resource, |
182 int64 start_changestamp, | 145 int64 start_changestamp) { |
183 const FileOperationCallback& callback) { | |
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
185 DCHECK(!callback.is_null()); | |
186 DCHECK(refreshing_); | |
187 | 147 |
188 bool is_delta_feed = start_changestamp != 0; | 148 bool is_delta_feed = start_changestamp != 0; |
189 const LoadFeedListCallback& completion_callback = | 149 const LoadFeedListCallback& completion_callback = |
190 base::Bind(&ChangeListLoader::UpdateMetadataFromFeedAfterLoadFromServer, | 150 base::Bind(&ChangeListLoader::UpdateMetadataFromFeedAfterLoadFromServer, |
191 weak_ptr_factory_.GetWeakPtr(), | 151 weak_ptr_factory_.GetWeakPtr(), |
192 base::Passed(&about_resource), | 152 base::Passed(&about_resource), |
193 is_delta_feed, | 153 is_delta_feed); |
194 callback); | |
195 base::TimeTicks start_time = base::TimeTicks::Now(); | 154 base::TimeTicks start_time = base::TimeTicks::Now(); |
196 if (is_delta_feed) { | 155 if (is_delta_feed) { |
197 scheduler_->GetChangeList( | 156 scheduler_->GetChangeList( |
198 start_changestamp, | 157 start_changestamp, |
199 base::Bind(&ChangeListLoader::OnGetResourceList, | 158 base::Bind(&ChangeListLoader::OnGetResourceList, |
200 weak_ptr_factory_.GetWeakPtr(), | 159 weak_ptr_factory_.GetWeakPtr(), |
201 base::Passed(ScopedVector<ChangeList>()), | 160 base::Passed(ScopedVector<ChangeList>()), |
202 completion_callback, | 161 completion_callback, |
203 start_time)); | 162 start_time)); |
204 } else { | 163 } else { |
205 // This is full feed fetch. | 164 // This is full feed fetch. |
206 scheduler_->GetAllResourceList( | 165 scheduler_->GetAllResourceList( |
207 base::Bind(&ChangeListLoader::OnGetResourceList, | 166 base::Bind(&ChangeListLoader::OnGetResourceList, |
208 weak_ptr_factory_.GetWeakPtr(), | 167 weak_ptr_factory_.GetWeakPtr(), |
209 base::Passed(ScopedVector<ChangeList>()), | 168 base::Passed(ScopedVector<ChangeList>()), |
210 completion_callback, | 169 completion_callback, |
211 start_time)); | 170 start_time)); |
212 } | 171 } |
213 } | 172 } |
214 | 173 |
215 void ChangeListLoader::StartLoadChangeListFromServer( | 174 void ChangeListLoader::LoadChangeListFromServerAfterLoadDirectory( |
216 const DirectoryFetchInfo& directory_fetch_info, | 175 const DirectoryFetchInfo& directory_fetch_info, |
217 scoped_ptr<google_apis::AboutResource> about_resource, | 176 scoped_ptr<google_apis::AboutResource> about_resource, |
218 int64 start_changestamp, | 177 int64 start_changestamp, |
219 const FileOperationCallback& callback, | |
220 DriveFileError error) { | 178 DriveFileError error) { |
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
222 DCHECK(!callback.is_null()); | |
223 DCHECK(refreshing_); | |
224 | 180 |
225 if (error == DRIVE_FILE_OK) { | 181 if (error == DRIVE_FILE_OK) { |
226 OnDirectoryLoadComplete(directory_fetch_info, callback, DRIVE_FILE_OK); | 182 // The directory fast-fetch succeeded. Runs the callbacks waiting for the |
227 DVLOG(1) << "Fast-fetch was successful: " << directory_fetch_info.ToString() | 183 // directory loading. If failed, do not flush so they're run after the |
228 << "; Start loading the change list"; | 184 // change list loading is complete. |
229 // Stop passing |callback| as it's just consumed. | 185 OnDirectoryLoadComplete(directory_fetch_info, DRIVE_FILE_OK); |
230 LoadChangeListFromServer( | |
231 about_resource.Pass(), | |
232 start_changestamp, | |
233 base::Bind(&util::EmptyFileOperationCallback)); | |
234 } else { | |
235 // The directory fast-fetch failed, but the change list loading may | |
236 // succeed. Keep passing |callback| so it's run after the change list | |
237 // loading is complete. | |
238 LoadChangeListFromServer( | |
239 about_resource.Pass(), start_changestamp, callback); | |
240 } | 186 } |
| 187 LoadChangeListFromServer(about_resource.Pass(), start_changestamp); |
241 } | 188 } |
242 | 189 |
243 void ChangeListLoader::SearchFromServerAfterGetResourceList( | 190 void ChangeListLoader::SearchFromServerAfterGetResourceList( |
244 const LoadFeedListCallback& callback, | 191 const LoadFeedListCallback& callback, |
245 google_apis::GDataErrorCode status, | 192 google_apis::GDataErrorCode status, |
246 scoped_ptr<google_apis::ResourceList> resource_list) { | 193 scoped_ptr<google_apis::ResourceList> resource_list) { |
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
248 DCHECK(!callback.is_null()); | 195 DCHECK(!callback.is_null()); |
249 | 196 |
250 DriveFileError error = util::GDataToDriveFileError(status); | 197 DriveFileError error = util::GDataToDriveFileError(status); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
338 } | 285 } |
339 | 286 |
340 void ChangeListLoader::LoadDirectoryFromServerAfterGetAbout( | 287 void ChangeListLoader::LoadDirectoryFromServerAfterGetAbout( |
341 const std::string& directory_resource_id, | 288 const std::string& directory_resource_id, |
342 const FileOperationCallback& callback, | 289 const FileOperationCallback& callback, |
343 google_apis::GDataErrorCode status, | 290 google_apis::GDataErrorCode status, |
344 scoped_ptr<google_apis::AboutResource> about_resource) { | 291 scoped_ptr<google_apis::AboutResource> about_resource) { |
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
346 DCHECK(!callback.is_null()); | 293 DCHECK(!callback.is_null()); |
347 | 294 |
348 int64 remote_changestamp = 0; | |
349 if (util::GDataToDriveFileError(status) == DRIVE_FILE_OK) { | 295 if (util::GDataToDriveFileError(status) == DRIVE_FILE_OK) { |
350 DCHECK(about_resource); | 296 DCHECK(about_resource); |
351 remote_changestamp = about_resource->largest_change_id(); | 297 last_known_remote_changestamp_ = about_resource->largest_change_id(); |
352 last_known_remote_changestamp_ = remote_changestamp; | |
353 } | 298 } |
354 | 299 |
355 const DirectoryFetchInfo directory_fetch_info(directory_resource_id, | 300 const DirectoryFetchInfo directory_fetch_info( |
356 remote_changestamp); | 301 directory_resource_id, |
| 302 last_known_remote_changestamp_); |
357 DoLoadDirectoryFromServer(directory_fetch_info, callback); | 303 DoLoadDirectoryFromServer(directory_fetch_info, callback); |
358 } | 304 } |
359 | 305 |
360 void ChangeListLoader::DoLoadDirectoryFromServer( | 306 void ChangeListLoader::DoLoadDirectoryFromServer( |
361 const DirectoryFetchInfo& directory_fetch_info, | 307 const DirectoryFetchInfo& directory_fetch_info, |
362 const FileOperationCallback& callback) { | 308 const FileOperationCallback& callback) { |
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
364 DCHECK(!callback.is_null()); | 310 DCHECK(!callback.is_null()); |
365 DCHECK(!directory_fetch_info.empty()); | 311 DCHECK(!directory_fetch_info.empty()); |
366 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString(); | 312 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString(); |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
530 scheduler_->ContinueGetResourceList( | 476 scheduler_->ContinueGetResourceList( |
531 next_feed, | 477 next_feed, |
532 base::Bind(&ChangeListLoader::SearchFromServerAfterGetResourceList, | 478 base::Bind(&ChangeListLoader::SearchFromServerAfterGetResourceList, |
533 weak_ptr_factory_.GetWeakPtr(), callback)); | 479 weak_ptr_factory_.GetWeakPtr(), callback)); |
534 } | 480 } |
535 } | 481 } |
536 | 482 |
537 void ChangeListLoader::UpdateMetadataFromFeedAfterLoadFromServer( | 483 void ChangeListLoader::UpdateMetadataFromFeedAfterLoadFromServer( |
538 scoped_ptr<google_apis::AboutResource> about_resource, | 484 scoped_ptr<google_apis::AboutResource> about_resource, |
539 bool is_delta_feed, | 485 bool is_delta_feed, |
540 const FileOperationCallback& callback, | |
541 ScopedVector<ChangeList> change_lists, | 486 ScopedVector<ChangeList> change_lists, |
542 DriveFileError error) { | 487 DriveFileError error) { |
543 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 488 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
544 DCHECK(!callback.is_null()); | |
545 DCHECK(refreshing_); | |
546 | 489 |
547 if (error != DRIVE_FILE_OK) { | 490 if (error != DRIVE_FILE_OK) { |
548 OnChangeListLoadComplete(callback, error); | 491 OnChangeListLoadComplete(error); |
549 return; | 492 return; |
550 } | 493 } |
551 | 494 |
552 UpdateFromFeed(about_resource.Pass(), | 495 UpdateFromFeed(about_resource.Pass(), |
553 change_lists.Pass(), | 496 change_lists.Pass(), |
554 is_delta_feed, | 497 is_delta_feed, |
555 base::Bind(&ChangeListLoader::OnUpdateFromFeed, | 498 base::Bind(&ChangeListLoader::OnUpdateFromFeed, |
556 weak_ptr_factory_.GetWeakPtr(), | 499 weak_ptr_factory_.GetWeakPtr())); |
557 !loaded(), // is_initial_load | |
558 callback)); | |
559 } | 500 } |
560 | 501 |
561 void ChangeListLoader::LoadIfNeeded( | 502 void ChangeListLoader::LoadIfNeeded( |
562 const DirectoryFetchInfo& directory_fetch_info, | 503 const DirectoryFetchInfo& directory_fetch_info, |
563 const FileOperationCallback& callback) { | 504 const FileOperationCallback& callback) { |
564 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
565 DCHECK(!callback.is_null()); | 506 DCHECK(!callback.is_null()); |
566 | 507 |
567 // If feed has already been loaded, for normal feed fetch (= empty | 508 // If feed has already been loaded, for normal feed fetch (= empty |
568 // directory_fetch_info), we have nothing to do. For "fast fetch", we need to | 509 // directory_fetch_info), we have nothing to do. For "fast fetch", we need to |
569 // schedule a fetching if a feed refresh is currently running, because we | 510 // schedule a fetching if a feed refresh is currently running, because we |
570 // don't want to wait a possibly large delta feed to arrive. | 511 // don't want to wait a possibly large delta feed to arrive. |
571 if (loaded() && (directory_fetch_info.empty() || !refreshing())) { | 512 if (loaded() && (directory_fetch_info.empty() || !IsRefreshing())) { |
572 base::MessageLoopProxy::current()->PostTask( | 513 base::MessageLoopProxy::current()->PostTask( |
573 FROM_HERE, | 514 FROM_HERE, |
574 base::Bind(callback, DRIVE_FILE_OK)); | 515 base::Bind(callback, DRIVE_FILE_OK)); |
575 return; | 516 return; |
576 } | 517 } |
| 518 Load(directory_fetch_info, callback); |
| 519 } |
577 | 520 |
578 // At this point, it is either !loaded() or refreshing(). | 521 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info, |
579 // If the change list loading is in progress, schedule the callback to | 522 const FileOperationCallback& callback) { |
580 // run when it's ready (i.e. when the entire resource list is loaded, or | 523 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
581 // the directory contents are available per "fast fetch"). | 524 DCHECK(!callback.is_null()); |
582 if (refreshing()) { | 525 |
583 ScheduleRun(directory_fetch_info, callback); | 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) |
584 return; | 536 return; |
585 } | |
586 | 537 |
587 if (!directory_fetch_info.empty()) { | 538 // For initial loading, even for directory fetching, we do load the full |
588 // Add a dummy task to so ScheduleRun() can check that the directory is | 539 // feed from the server to sync up. So we register a dummy callback to |
589 // being fetched. This will be cleared either in | 540 // indicate that update for full hierarchy is running. |
590 // ProcessPendingLoadCallbackForDirectory() or FlushPendingLoadCallback(). | 541 if (is_initial_load && !resource_id.empty()) { |
591 pending_load_callback_[directory_fetch_info.resource_id()].push_back( | 542 pending_load_callback_[""].push_back( |
592 base::Bind(&util::EmptyFileOperationCallback)); | 543 base::Bind(&util::EmptyFileOperationCallback)); |
593 } | 544 } |
594 | 545 |
595 // First check if the local data is usable or not. | 546 // Check the current status of local metadata, and start loading if needed. |
596 CheckLocalChangestamp(base::Bind( | 547 resource_metadata_->GetLargestChangestamp( |
597 &ChangeListLoader::LoadAfterCheckLocalChangestamp, | 548 base::Bind(is_initial_load ? &ChangeListLoader::DoInitialLoad |
598 weak_ptr_factory_.GetWeakPtr(), | 549 : &ChangeListLoader::DoUpdateLoad, |
599 directory_fetch_info, | 550 weak_ptr_factory_.GetWeakPtr(), |
600 callback)); | 551 directory_fetch_info)); |
601 } | 552 } |
602 | 553 |
603 void ChangeListLoader::LoadAfterCheckLocalChangestamp( | 554 void ChangeListLoader::DoInitialLoad( |
604 const DirectoryFetchInfo& directory_fetch_info, | 555 const DirectoryFetchInfo& directory_fetch_info, |
605 const FileOperationCallback& callback, | |
606 int64 local_changestamp) { | 556 int64 local_changestamp) { |
607 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 557 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
608 DCHECK(!callback.is_null()); | |
609 | 558 |
610 if (local_changestamp > 0) { | 559 if (local_changestamp > 0) { |
611 loaded_ = true; | 560 // The local data is usable. Flush callbacks to tell loading was successful. |
| 561 OnChangeListLoadComplete(DRIVE_FILE_OK); |
612 | 562 |
613 // The local data is usable. Change the refreshing state and tell the | 563 // Continues to load from server in background. |
614 // callback that the loading was successful. | 564 // Put dummy callbacks to indicate that fetching is still continuing. |
615 OnChangeListLoadComplete(callback, DRIVE_FILE_OK); | 565 pending_load_callback_[directory_fetch_info.resource_id()].push_back( |
616 FOR_EACH_OBSERVER(ChangeListLoaderObserver, | 566 base::Bind(&util::EmptyFileOperationCallback)); |
617 observers_, | 567 if (!directory_fetch_info.empty()) { |
618 OnInitialFeedLoaded()); | 568 pending_load_callback_[""].push_back( |
| 569 base::Bind(&util::EmptyFileOperationCallback)); |
| 570 } |
| 571 } |
| 572 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); |
| 573 } |
619 | 574 |
620 // Load from server if needed (i.e. the cache is old). Note that we | 575 void ChangeListLoader::DoUpdateLoad( |
621 // should still propagate |directory_fetch_info| though the directory is | 576 const DirectoryFetchInfo& directory_fetch_info, |
622 // loaded first. This way, the UI can get notified via a directory change | 577 int64 local_changestamp) { |
623 // event as soon as the current directory contents are fetched. | 578 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
624 LoadFromServerIfNeeded(directory_fetch_info, | 579 |
625 base::Bind(&util::EmptyFileOperationCallback)); | 580 if (directory_fetch_info.empty()) { |
| 581 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); |
626 } else { | 582 } else { |
627 // The local data is not usable. Start loading from the server. Though the | 583 // Note: CheckChangestampAndLoadDirectoryIfNeeded regards |
628 // function name ends with "IfNeeded", this function should always start | 584 // last_know_remote_changestamp_ as the remote changestamp. To be precise, |
629 // loading as the local changestamp is zero now. | 585 // we need to call GetAboutResource() here, as we do in other places like |
630 LoadFromServerIfNeeded(directory_fetch_info, callback); | 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)); |
631 } | 599 } |
632 } | 600 } |
633 | 601 |
634 void ChangeListLoader::CheckLocalChangestamp( | |
635 const GetChangestampCallback& callback) { | |
636 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
637 DCHECK(!callback.is_null()); | |
638 DCHECK(!loaded_); | |
639 | |
640 // Sets the refreshing flag, so that the caller does not send refresh requests | |
641 // in parallel (see DriveFileSystem::LoadFeedIfNeeded). | |
642 // | |
643 // The flag will be unset when loading completes. | |
644 refreshing_ = true; | |
645 | |
646 resource_metadata_->GetLargestChangestamp(callback); | |
647 } | |
648 | |
649 | |
650 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) { | 602 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) { |
651 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 603 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
652 DCHECK(!callback.is_null()); | 604 DCHECK(!callback.is_null()); |
653 | 605 |
654 if (loaded() && !refreshing()) | 606 if (loaded() && !IsRefreshing()) |
655 LoadFromServerIfNeeded(DirectoryFetchInfo(), callback); | 607 Load(DirectoryFetchInfo(), callback); |
656 } | 608 } |
657 | 609 |
658 void ChangeListLoader::UpdateFromFeed( | 610 void ChangeListLoader::UpdateFromFeed( |
659 scoped_ptr<google_apis::AboutResource> about_resource, | 611 scoped_ptr<google_apis::AboutResource> about_resource, |
660 ScopedVector<ChangeList> change_lists, | 612 ScopedVector<ChangeList> change_lists, |
661 bool is_delta_feed, | 613 bool is_delta_feed, |
662 const base::Closure& update_finished_callback) { | 614 const base::Closure& update_finished_callback) { |
663 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
664 DCHECK(!update_finished_callback.is_null()); | 616 DCHECK(!update_finished_callback.is_null()); |
665 DVLOG(1) << "Updating directory with a feed"; | |
666 | 617 |
667 change_list_processor_.reset(new ChangeListProcessor(resource_metadata_)); | 618 change_list_processor_.reset(new ChangeListProcessor(resource_metadata_)); |
668 // Don't send directory content change notification while performing | 619 // Don't send directory content change notification while performing |
669 // the initial content retrieval. | 620 // the initial content retrieval. |
670 const bool should_notify_changed_directories = is_delta_feed; | 621 const bool should_notify_changed_directories = is_delta_feed; |
671 | 622 |
672 change_list_processor_->ApplyFeeds( | 623 change_list_processor_->ApplyFeeds( |
673 about_resource.Pass(), | 624 about_resource.Pass(), |
674 change_lists.Pass(), | 625 change_lists.Pass(), |
675 is_delta_feed, | 626 is_delta_feed, |
676 base::Bind(&ChangeListLoader::NotifyDirectoryChangedAfterApplyFeed, | 627 base::Bind(&ChangeListLoader::NotifyDirectoryChangedAfterApplyFeed, |
677 weak_ptr_factory_.GetWeakPtr(), | 628 weak_ptr_factory_.GetWeakPtr(), |
678 should_notify_changed_directories, | 629 should_notify_changed_directories, |
679 update_finished_callback)); | 630 update_finished_callback)); |
680 } | 631 } |
681 | 632 |
682 void ChangeListLoader::ScheduleRun( | 633 void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeed( |
683 const DirectoryFetchInfo& directory_fetch_info, | 634 const DirectoryFetchInfo& directory_fetch_info, |
| 635 int64 local_changestamp, |
684 const FileOperationCallback& callback) { | 636 const FileOperationCallback& callback) { |
685 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 637 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
686 DCHECK(!callback.is_null()); | 638 DCHECK(!directory_fetch_info.empty()); |
687 DCHECK(refreshing_); | |
688 | 639 |
689 if (directory_fetch_info.empty()) { | 640 int64 directory_changestamp = std::max(directory_fetch_info.changestamp(), |
690 // If the caller is not interested in a particular directory, just add the | 641 local_changestamp); |
691 // callback to the pending list and return. | |
692 pending_load_callback_[""].push_back(callback); | |
693 return; | |
694 } | |
695 | |
696 const std::string& resource_id = directory_fetch_info.resource_id(); | |
697 | |
698 // If the directory of interest is already scheduled to be fetched, add the | |
699 // callback to the pending list and return. | |
700 LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id); | |
701 if (it != pending_load_callback_.end()) { | |
702 it->second.push_back(callback); | |
703 return; | |
704 } | |
705 | 642 |
706 // If the directory's changestamp is up-to-date, just schedule to run the | 643 // If the directory's changestamp is up-to-date, just schedule to run the |
707 // callback, as there is no need to fetch the directory. | 644 // callback, as there is no need to fetch the directory. |
708 // Note that |last_known_remote_changestamp_| is 0 when it is not received | 645 // Note that |last_known_remote_changestamp_| is 0 when it is not received |
709 // yet. In that case we conservatively assume that we need to fetch. | 646 // yet. In that case we conservatively assume that we need to fetch. |
710 if (last_known_remote_changestamp_ > 0 && | 647 if (last_known_remote_changestamp_ > 0 && |
711 directory_fetch_info.changestamp() >= last_known_remote_changestamp_) { | 648 directory_changestamp >= last_known_remote_changestamp_) { |
712 base::MessageLoopProxy::current()->PostTask( | 649 callback.Run(DRIVE_FILE_OK); |
713 FROM_HERE, | |
714 base::Bind(callback, DRIVE_FILE_OK)); | |
715 return; | 650 return; |
716 } | 651 } |
717 | 652 |
718 // The directory should be fetched. Add a dummy task to so ScheduleRun() | 653 DVLOG(1) << "Fast-fetching directory: " << directory_fetch_info.ToString() |
719 // can check that the directory is being fetched. | 654 << "; remote_changestamp: " << last_known_remote_changestamp_; |
720 pending_load_callback_[resource_id].push_back( | |
721 base::Bind(&util::EmptyFileOperationCallback)); | |
722 | 655 |
723 // Start fetching the directory content, and mark it with the changestamp | 656 // Start fetching the directory content, and mark it with the changestamp |
724 // |last_known_remote_changestamp_|. To be precise, instead we need to call | 657 // |last_known_remote_changestamp_|. |
725 // GetAboutResource() to get the latest changestamp. However, | |
726 // - It is costly to do GetAboutResource HTTP request every time. | |
727 // - The chance using an old value is small; it only happens when LoadIfNeeded | |
728 // is called during one GetAboutResource roundtrip time of a feed fetching. | |
729 // - Even if the value is old, it just marks the directory as older. It may | |
730 // trigger one future unnecessary re-fetch, but it'll never lose data, etc. | |
731 DirectoryFetchInfo new_directory_fetch_info( | 658 DirectoryFetchInfo new_directory_fetch_info( |
732 directory_fetch_info.resource_id(), | 659 directory_fetch_info.resource_id(), |
733 std::max(directory_fetch_info.changestamp(), | 660 std::max(directory_changestamp, last_known_remote_changestamp_)); |
734 last_known_remote_changestamp_)); | 661 DoLoadDirectoryFromServer(new_directory_fetch_info, callback); |
735 DoLoadDirectoryFromServer( | |
736 new_directory_fetch_info, | |
737 base::Bind(&ChangeListLoader::OnDirectoryLoadComplete, | |
738 weak_ptr_factory_.GetWeakPtr(), | |
739 directory_fetch_info, | |
740 callback)); | |
741 } | 662 } |
742 | 663 |
743 void ChangeListLoader::NotifyDirectoryChangedAfterApplyFeed( | 664 void ChangeListLoader::NotifyDirectoryChangedAfterApplyFeed( |
744 bool should_notify_changed_directories, | 665 bool should_notify_changed_directories, |
745 const base::Closure& update_finished_callback) { | 666 const base::Closure& update_finished_callback) { |
746 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 667 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
747 DCHECK(change_list_processor_.get()); | 668 DCHECK(change_list_processor_.get()); |
748 DCHECK(!update_finished_callback.is_null()); | 669 DCHECK(!update_finished_callback.is_null()); |
749 | 670 |
750 loaded_ = true; | |
751 | |
752 if (should_notify_changed_directories) { | 671 if (should_notify_changed_directories) { |
753 for (std::set<base::FilePath>::iterator dir_iter = | 672 for (std::set<base::FilePath>::iterator dir_iter = |
754 change_list_processor_->changed_dirs().begin(); | 673 change_list_processor_->changed_dirs().begin(); |
755 dir_iter != change_list_processor_->changed_dirs().end(); | 674 dir_iter != change_list_processor_->changed_dirs().end(); |
756 ++dir_iter) { | 675 ++dir_iter) { |
757 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, | 676 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, |
758 OnDirectoryChanged(*dir_iter)); | 677 OnDirectoryChanged(*dir_iter)); |
759 } | 678 } |
760 } | 679 } |
761 | 680 |
762 update_finished_callback.Run(); | 681 update_finished_callback.Run(); |
763 | 682 |
764 // Cannot delete change_list_processor_ yet because we are in | 683 // Cannot delete change_list_processor_ yet because we are in |
765 // on_complete_callback_, which is owned by change_list_processor_. | 684 // on_complete_callback_, which is owned by change_list_processor_. |
766 } | 685 } |
767 | 686 |
768 void ChangeListLoader::OnUpdateFromFeed( | 687 void ChangeListLoader::OnUpdateFromFeed() { |
769 bool is_inital_load, | |
770 const FileOperationCallback& callback) { | |
771 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
772 DCHECK(!callback.is_null()); | |
773 | 689 |
774 OnChangeListLoadComplete(callback, DRIVE_FILE_OK); | 690 OnChangeListLoadComplete(DRIVE_FILE_OK); |
775 if (is_inital_load) { | |
776 FOR_EACH_OBSERVER(ChangeListLoaderObserver, | |
777 observers_, | |
778 OnInitialFeedLoaded()); | |
779 } | |
780 | 691 |
781 FOR_EACH_OBSERVER(ChangeListLoaderObserver, | 692 FOR_EACH_OBSERVER(ChangeListLoaderObserver, |
782 observers_, | 693 observers_, |
783 OnFeedFromServerLoaded()); | 694 OnFeedFromServerLoaded()); |
784 } | 695 } |
785 | 696 |
786 void ChangeListLoader::OnChangeListLoadComplete( | 697 void ChangeListLoader::OnChangeListLoadComplete(DriveFileError error) { |
787 const FileOperationCallback& callback, | |
788 DriveFileError error) { | |
789 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 698 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
790 DCHECK(!callback.is_null()); | |
791 | 699 |
792 refreshing_ = false; | 700 if (!loaded_ && error == DRIVE_FILE_OK) { |
793 callback.Run(error); | 701 loaded_ = true; |
794 FlushPendingLoadCallback(error); | 702 FOR_EACH_OBSERVER(ChangeListLoaderObserver, |
795 } | 703 observers_, |
796 | 704 OnInitialFeedLoaded()); |
797 void ChangeListLoader::OnDirectoryLoadComplete( | 705 } |
798 const DirectoryFetchInfo& directory_fetch_info, | |
799 const FileOperationCallback& callback, | |
800 DriveFileError error) { | |
801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
802 DCHECK(!callback.is_null()); | |
803 | |
804 callback.Run(error); | |
805 ProcessPendingLoadCallbackForDirectory(directory_fetch_info.resource_id(), | |
806 error); | |
807 } | |
808 | |
809 void ChangeListLoader::FlushPendingLoadCallback(DriveFileError error) { | |
810 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
811 DCHECK(!refreshing_); | |
812 | 706 |
813 for (LoadCallbackMap::iterator it = pending_load_callback_.begin(); | 707 for (LoadCallbackMap::iterator it = pending_load_callback_.begin(); |
814 it != pending_load_callback_.end(); ++it) { | 708 it != pending_load_callback_.end(); ++it) { |
815 const std::vector<FileOperationCallback>& callbacks = it->second; | 709 const std::vector<FileOperationCallback>& callbacks = it->second; |
816 for (size_t i = 0; i < callbacks.size(); ++i) { | 710 for (size_t i = 0; i < callbacks.size(); ++i) { |
817 base::MessageLoopProxy::current()->PostTask( | 711 base::MessageLoopProxy::current()->PostTask( |
818 FROM_HERE, | 712 FROM_HERE, |
819 base::Bind(callbacks[i], error)); | 713 base::Bind(callbacks[i], error)); |
820 } | 714 } |
821 } | 715 } |
822 pending_load_callback_.clear(); | 716 pending_load_callback_.clear(); |
823 } | 717 } |
824 | 718 |
825 void ChangeListLoader::ProcessPendingLoadCallbackForDirectory( | 719 void ChangeListLoader::OnDirectoryLoadComplete( |
826 const std::string& resource_id, | 720 const DirectoryFetchInfo& directory_fetch_info, |
827 DriveFileError error) { | 721 DriveFileError error) { |
828 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 722 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
829 | 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(); |
830 LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id); | 728 LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id); |
831 if (it != pending_load_callback_.end()) { | 729 if (it != pending_load_callback_.end()) { |
832 DVLOG(1) << "Running callback for " << resource_id; | 730 DVLOG(1) << "Running callback for " << resource_id; |
833 const std::vector<FileOperationCallback>& callbacks = it->second; | 731 const std::vector<FileOperationCallback>& callbacks = it->second; |
834 for (size_t i = 0; i < callbacks.size(); ++i) { | 732 for (size_t i = 0; i < callbacks.size(); ++i) { |
835 base::MessageLoopProxy::current()->PostTask( | 733 base::MessageLoopProxy::current()->PostTask( |
836 FROM_HERE, | 734 FROM_HERE, |
837 base::Bind(callbacks[i], error)); | 735 base::Bind(callbacks[i], error)); |
838 } | 736 } |
839 pending_load_callback_.erase(it); | 737 pending_load_callback_.erase(it); |
840 } | 738 } |
841 } | 739 } |
842 | 740 |
843 } // namespace drive | 741 } // namespace drive |
OLD | NEW |