Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(337)

Side by Side Diff: chrome/browser/chromeos/drive/change_list_loader.cc

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

Powered by Google App Engine
This is Rietveld 408576698