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

Side by Side Diff: trunk/src/chrome/browser/sync_file_system/drive_file_sync_service.cc

Issue 14401006: Revert 195482 "Make DriveSystemService an observer of DriveNotif..." (Closed) Base URL: svn://svn.chromium.org/chrome/
Patch Set: 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/sync_file_system/drive_file_sync_service.h" 5 #include "chrome/browser/sync_file_system/drive_file_sync_service.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <string> 8 #include <string>
9 #include <utility> 9 #include <utility>
10 10
(...skipping 28 matching lines...) Expand all
39 using fileapi::FileSystemURL; 39 using fileapi::FileSystemURL;
40 40
41 namespace sync_file_system { 41 namespace sync_file_system {
42 42
43 namespace { 43 namespace {
44 44
45 const base::FilePath::CharType kTempDirName[] = FILE_PATH_LITERAL("tmp"); 45 const base::FilePath::CharType kTempDirName[] = FILE_PATH_LITERAL("tmp");
46 const base::FilePath::CharType kSyncFileSystemDir[] = 46 const base::FilePath::CharType kSyncFileSystemDir[] =
47 FILE_PATH_LITERAL("Sync FileSystem"); 47 FILE_PATH_LITERAL("Sync FileSystem");
48 48
49 // Incremental sync polling interval.
50 // TODO(calvinlo): Improve polling algorithm dependent on whether push
51 // notifications are on or off.
52 const int64 kMinimumPollingDelaySeconds = 5;
53 const int64 kMaximumPollingDelaySeconds = 10 * 60; // 10 min
54 const int64 kPollingDelaySecondsWithNotification = 4 * 60 * 60; // 4 hr
55 const double kDelayMultiplier = 1.6;
56
49 bool CreateTemporaryFile(const base::FilePath& dir_path, 57 bool CreateTemporaryFile(const base::FilePath& dir_path,
50 base::FilePath* temp_file) { 58 base::FilePath* temp_file) {
51 return file_util::CreateDirectory(dir_path) && 59 return file_util::CreateDirectory(dir_path) &&
52 file_util::CreateTemporaryFileInDir(dir_path, temp_file); 60 file_util::CreateTemporaryFileInDir(dir_path, temp_file);
53 } 61 }
54 62
55 void DeleteTemporaryFile(const base::FilePath& file_path) { 63 void DeleteTemporaryFile(const base::FilePath& file_path) {
56 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)) { 64 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)) {
57 content::BrowserThread::PostTask( 65 content::BrowserThread::PostTask(
58 content::BrowserThread::FILE, FROM_HERE, 66 content::BrowserThread::FILE, FROM_HERE,
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
260 return left.changestamp > right.changestamp; 268 return left.changestamp > right.changestamp;
261 return false; 269 return false;
262 } 270 }
263 271
264 DriveFileSyncService::DriveFileSyncService(Profile* profile) 272 DriveFileSyncService::DriveFileSyncService(Profile* profile)
265 : profile_(profile), 273 : profile_(profile),
266 last_operation_status_(SYNC_STATUS_OK), 274 last_operation_status_(SYNC_STATUS_OK),
267 state_(REMOTE_SERVICE_OK), 275 state_(REMOTE_SERVICE_OK),
268 sync_enabled_(true), 276 sync_enabled_(true),
269 largest_fetched_changestamp_(0), 277 largest_fetched_changestamp_(0),
278 polling_delay_seconds_(kMinimumPollingDelaySeconds),
270 may_have_unfetched_changes_(false), 279 may_have_unfetched_changes_(false),
271 remote_change_processor_(NULL), 280 remote_change_processor_(NULL),
272 conflict_resolution_(kDefaultPolicy), 281 conflict_resolution_(kDefaultPolicy),
273 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { 282 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
274 temporary_file_dir_ = 283 temporary_file_dir_ =
275 profile->GetPath().Append(kSyncFileSystemDir).Append(kTempDirName); 284 profile->GetPath().Append(kSyncFileSystemDir).Append(kTempDirName);
276 token_.reset(new TaskToken(AsWeakPtr())); 285 token_.reset(new TaskToken(AsWeakPtr()));
277 286
278 sync_client_.reset(new DriveFileSyncClient(profile)); 287 sync_client_.reset(new DriveFileSyncClient(profile));
279 sync_client_->AddObserver(this); 288 sync_client_->AddObserver(this);
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after
552 if (old_state == GetCurrentState()) 561 if (old_state == GetCurrentState())
553 return; 562 return;
554 563
555 if (!enabled) 564 if (!enabled)
556 last_operation_status_ = SYNC_STATUS_SYNC_DISABLED; 565 last_operation_status_ = SYNC_STATUS_SYNC_DISABLED;
557 566
558 const char* status_message = enabled ? "Sync is enabled" : "Sync is disabled"; 567 const char* status_message = enabled ? "Sync is enabled" : "Sync is disabled";
559 FOR_EACH_OBSERVER( 568 FOR_EACH_OBSERVER(
560 Observer, service_observers_, 569 Observer, service_observers_,
561 OnRemoteServiceStateUpdated(GetCurrentState(), status_message)); 570 OnRemoteServiceStateUpdated(GetCurrentState(), status_message));
571
572 if (GetCurrentState() == REMOTE_SERVICE_OK) {
573 UpdatePollingDelay(kMinimumPollingDelaySeconds);
574 SchedulePolling();
575 }
562 } 576 }
563 577
564 SyncStatusCode DriveFileSyncService::SetConflictResolutionPolicy( 578 SyncStatusCode DriveFileSyncService::SetConflictResolutionPolicy(
565 ConflictResolutionPolicy resolution) { 579 ConflictResolutionPolicy resolution) {
566 conflict_resolution_ = resolution; 580 conflict_resolution_ = resolution;
567 return SYNC_STATUS_OK; 581 return SYNC_STATUS_OK;
568 } 582 }
569 583
570 ConflictResolutionPolicy 584 ConflictResolutionPolicy
571 DriveFileSyncService::GetConflictResolutionPolicy() const { 585 DriveFileSyncService::GetConflictResolutionPolicy() const {
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
622 void DriveFileSyncService::OnAuthenticated() { 636 void DriveFileSyncService::OnAuthenticated() {
623 if (state_ == REMOTE_SERVICE_OK) 637 if (state_ == REMOTE_SERVICE_OK)
624 return; 638 return;
625 DVLOG(1) << "OnAuthenticated"; 639 DVLOG(1) << "OnAuthenticated";
626 state_ = REMOTE_SERVICE_OK; 640 state_ = REMOTE_SERVICE_OK;
627 if (GetCurrentState() != REMOTE_SERVICE_OK) 641 if (GetCurrentState() != REMOTE_SERVICE_OK)
628 return; 642 return;
629 FOR_EACH_OBSERVER( 643 FOR_EACH_OBSERVER(
630 Observer, service_observers_, 644 Observer, service_observers_,
631 OnRemoteServiceStateUpdated(GetCurrentState(), "Authenticated")); 645 OnRemoteServiceStateUpdated(GetCurrentState(), "Authenticated"));
646 UpdatePollingDelay(kMinimumPollingDelaySeconds);
632 647
633 may_have_unfetched_changes_ = true; 648 may_have_unfetched_changes_ = true;
634 MaybeStartFetchChanges(); 649 MaybeStartFetchChanges();
635 } 650 }
636 651
637 void DriveFileSyncService::OnNetworkConnected() { 652 void DriveFileSyncService::OnNetworkConnected() {
638 if (state_ == REMOTE_SERVICE_OK) 653 if (state_ == REMOTE_SERVICE_OK)
639 return; 654 return;
640 DVLOG(1) << "OnNetworkConnected"; 655 DVLOG(1) << "OnNetworkConnected";
641 state_ = REMOTE_SERVICE_OK; 656 state_ = REMOTE_SERVICE_OK;
642 if (GetCurrentState() != REMOTE_SERVICE_OK) 657 if (GetCurrentState() != REMOTE_SERVICE_OK)
643 return; 658 return;
644 FOR_EACH_OBSERVER( 659 FOR_EACH_OBSERVER(
645 Observer, service_observers_, 660 Observer, service_observers_,
646 OnRemoteServiceStateUpdated(GetCurrentState(), "Network connected")); 661 OnRemoteServiceStateUpdated(GetCurrentState(), "Network connected"));
662 UpdatePollingDelay(kMinimumPollingDelaySeconds);
647 663
648 may_have_unfetched_changes_ = true; 664 may_have_unfetched_changes_ = true;
649 MaybeStartFetchChanges(); 665 MaybeStartFetchChanges();
650 } 666 }
651 667
652 // Called by CreateForTesting. 668 // Called by CreateForTesting.
653 DriveFileSyncService::DriveFileSyncService( 669 DriveFileSyncService::DriveFileSyncService(
654 Profile* profile, 670 Profile* profile,
655 const base::FilePath& base_dir, 671 const base::FilePath& base_dir,
656 scoped_ptr<DriveFileSyncClientInterface> sync_client, 672 scoped_ptr<DriveFileSyncClientInterface> sync_client,
657 scoped_ptr<DriveMetadataStore> metadata_store) 673 scoped_ptr<DriveMetadataStore> metadata_store)
658 : profile_(profile), 674 : profile_(profile),
659 last_operation_status_(SYNC_STATUS_OK), 675 last_operation_status_(SYNC_STATUS_OK),
660 state_(REMOTE_SERVICE_OK), 676 state_(REMOTE_SERVICE_OK),
661 sync_enabled_(true), 677 sync_enabled_(true),
662 largest_fetched_changestamp_(0), 678 largest_fetched_changestamp_(0),
679 polling_delay_seconds_(-1),
663 may_have_unfetched_changes_(false), 680 may_have_unfetched_changes_(false),
664 remote_change_processor_(NULL), 681 remote_change_processor_(NULL),
665 conflict_resolution_(kDefaultPolicy), 682 conflict_resolution_(kDefaultPolicy),
666 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { 683 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
667 DCHECK(profile); 684 DCHECK(profile);
668 temporary_file_dir_ = base_dir.Append(kTempDirName); 685 temporary_file_dir_ = base_dir.Append(kTempDirName);
669 686
670 token_.reset(new TaskToken(AsWeakPtr())); 687 token_.reset(new TaskToken(AsWeakPtr()));
671 sync_client_ = sync_client.Pass(); 688 sync_client_ = sync_client.Pass();
672 metadata_store_ = metadata_store.Pass(); 689 metadata_store_ = metadata_store.Pass();
(...skipping 30 matching lines...) Expand all
703 720
704 if (token_->task_type() != TASK_TYPE_NONE) { 721 if (token_->task_type() != TASK_TYPE_NONE) {
705 DVLOG(2) << "NotifyTaskDone: " << token_->description() 722 DVLOG(2) << "NotifyTaskDone: " << token_->description()
706 << ": finished with status=" << status 723 << ": finished with status=" << status
707 << " (" << SyncStatusCodeToString(status) << ")" 724 << " (" << SyncStatusCodeToString(status) << ")"
708 << " " << token_->location().ToString(); 725 << " " << token_->location().ToString();
709 726
710 RemoteServiceState old_state = GetCurrentState(); 727 RemoteServiceState old_state = GetCurrentState();
711 UpdateServiceState(); 728 UpdateServiceState();
712 729
730 // Reset the polling delay. This will adjust the polling timer
731 // based on the current service state.
732 UpdatePollingDelay(polling_delay_seconds_);
733
713 // Notify remote sync service state if the state has been changed. 734 // Notify remote sync service state if the state has been changed.
714 if (!token_->description().empty() || old_state != GetCurrentState()) { 735 if (!token_->description().empty() || old_state != GetCurrentState()) {
715 FOR_EACH_OBSERVER( 736 FOR_EACH_OBSERVER(
716 Observer, service_observers_, 737 Observer, service_observers_,
717 OnRemoteServiceStateUpdated(GetCurrentState(), 738 OnRemoteServiceStateUpdated(GetCurrentState(),
718 token_->done_description())); 739 token_->done_description()));
719 } 740 }
720 } 741 }
721 742
722 token_->ResetTask(FROM_HERE); 743 token_->ResetTask(FROM_HERE);
723 if (!pending_tasks_.empty()) { 744 if (!pending_tasks_.empty()) {
724 base::Closure closure = pending_tasks_.front(); 745 base::Closure closure = pending_tasks_.front();
725 pending_tasks_.pop_front(); 746 pending_tasks_.pop_front();
726 closure.Run(); 747 closure.Run();
727 return; 748 return;
728 } 749 }
729 750
730 if (GetCurrentState() == REMOTE_SERVICE_DISABLED) 751 if (GetCurrentState() == REMOTE_SERVICE_DISABLED)
731 return; 752 return;
732 753
733 MaybeStartFetchChanges(); 754 MaybeStartFetchChanges();
734 755
756 SchedulePolling();
757
735 // Notify observer of the update of |pending_changes_|. 758 // Notify observer of the update of |pending_changes_|.
736 FOR_EACH_OBSERVER(Observer, service_observers_, 759 FOR_EACH_OBSERVER(Observer, service_observers_,
737 OnRemoteChangeQueueUpdated(pending_changes_.size())); 760 OnRemoteChangeQueueUpdated(pending_changes_.size()));
738 } 761 }
739 762
740 void DriveFileSyncService::UpdateServiceState() { 763 void DriveFileSyncService::UpdateServiceState() {
741 switch (last_operation_status_) { 764 switch (last_operation_status_) {
742 // Possible regular operation errors. 765 // Possible regular operation errors.
743 case SYNC_STATUS_OK: 766 case SYNC_STATUS_OK:
744 case SYNC_STATUS_FILE_BUSY: 767 case SYNC_STATUS_FILE_BUSY:
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after
994 AsWeakPtr(), base::Passed(&token), origin, largest_changestamp)); 1017 AsWeakPtr(), base::Passed(&token), origin, largest_changestamp));
995 return; 1018 return;
996 } 1019 }
997 1020
998 // Move |origin| to the incremental sync origin set if the origin has no file. 1021 // Move |origin| to the incremental sync origin set if the origin has no file.
999 if (metadata_store_->IsBatchSyncOrigin(origin) && 1022 if (metadata_store_->IsBatchSyncOrigin(origin) &&
1000 !ContainsKey(origin_to_changes_map_, origin)) { 1023 !ContainsKey(origin_to_changes_map_, origin)) {
1001 metadata_store_->MoveBatchSyncOriginToIncremental(origin); 1024 metadata_store_->MoveBatchSyncOriginToIncremental(origin);
1002 } 1025 }
1003 1026
1004 may_have_unfetched_changes_ = true; 1027 CheckForUpdates();
1005 MaybeStartFetchChanges();
1006 NotifyTaskDone(SYNC_STATUS_OK, token.Pass()); 1028 NotifyTaskDone(SYNC_STATUS_OK, token.Pass());
1007 } 1029 }
1008 1030
1009 void DriveFileSyncService::DidChangeOriginOnMetadataStore( 1031 void DriveFileSyncService::DidChangeOriginOnMetadataStore(
1010 scoped_ptr<TaskToken> token, 1032 scoped_ptr<TaskToken> token,
1011 const SyncStatusCallback& callback, 1033 const SyncStatusCallback& callback,
1012 SyncStatusCode status) { 1034 SyncStatusCode status) {
1013 NotifyTaskDone(status, token.Pass()); 1035 NotifyTaskDone(status, token.Pass());
1014 callback.Run(status); 1036 callback.Run(status);
1015 } 1037 }
(...skipping 1062 matching lines...) Expand 10 before | Expand all | Expand 10 after
2078 } 2100 }
2079 return; 2101 return;
2080 } 2102 }
2081 2103
2082 if (may_have_unfetched_changes_ && 2104 if (may_have_unfetched_changes_ &&
2083 !metadata_store_->incremental_sync_origins().empty()) { 2105 !metadata_store_->incremental_sync_origins().empty()) {
2084 FetchChangesForIncrementalSync(); 2106 FetchChangesForIncrementalSync();
2085 } 2107 }
2086 } 2108 }
2087 2109
2088 void DriveFileSyncService::OnNotificationReceived() { 2110 void DriveFileSyncService::CheckForUpdates() {
2089 // TODO(calvinlo): Try to eliminate may_have_unfetched_changes_ variable. 2111 // TODO(calvinlo): Try to eliminate may_have_unfetched_changes_ variable.
2090 may_have_unfetched_changes_ = true; 2112 may_have_unfetched_changes_ = true;
2091 MaybeStartFetchChanges(); 2113 MaybeStartFetchChanges();
2092 } 2114 }
2093 2115
2094 void DriveFileSyncService::FetchChangesForIncrementalSync() { 2116 void DriveFileSyncService::FetchChangesForIncrementalSync() {
2095 scoped_ptr<TaskToken> token(GetToken(FROM_HERE, TASK_TYPE_DRIVE, 2117 scoped_ptr<TaskToken> token(GetToken(FROM_HERE, TASK_TYPE_DRIVE,
2096 "Fetching remote change list")); 2118 "Fetching remote change list"));
2097 DCHECK(token); 2119 DCHECK(token);
2098 DCHECK(may_have_unfetched_changes_); 2120 DCHECK(may_have_unfetched_changes_);
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
2168 metadata_store_->SetOriginRootDirectory(*itr, std::string()); 2190 metadata_store_->SetOriginRootDirectory(*itr, std::string());
2169 } 2191 }
2170 2192
2171 GURL next_feed; 2193 GURL next_feed;
2172 if (changes->GetNextFeedURL(&next_feed)) 2194 if (changes->GetNextFeedURL(&next_feed))
2173 may_have_unfetched_changes_ = true; 2195 may_have_unfetched_changes_ = true;
2174 2196
2175 if (!changes->entries().empty()) 2197 if (!changes->entries().empty())
2176 largest_fetched_changestamp_ = changes->entries().back()->changestamp(); 2198 largest_fetched_changestamp_ = changes->entries().back()->changestamp();
2177 2199
2200 if (has_new_changes) {
2201 UpdatePollingDelay(kMinimumPollingDelaySeconds);
2202 } else {
2203 // If the change_queue_ was not updated, update the polling delay to wait
2204 // longer.
2205 UpdatePollingDelay(static_cast<int64>(
2206 kDelayMultiplier * polling_delay_seconds_));
2207 }
2208
2178 NotifyTaskDone(SYNC_STATUS_OK, token.Pass()); 2209 NotifyTaskDone(SYNC_STATUS_OK, token.Pass());
2179 } 2210 }
2180 2211
2181 bool DriveFileSyncService::GetOriginForEntry( 2212 bool DriveFileSyncService::GetOriginForEntry(
2182 const google_apis::ResourceEntry& entry, 2213 const google_apis::ResourceEntry& entry,
2183 GURL* origin_out) { 2214 GURL* origin_out) {
2184 typedef ScopedVector<google_apis::Link>::const_iterator iterator; 2215 typedef ScopedVector<google_apis::Link>::const_iterator iterator;
2185 for (iterator itr = entry.links().begin(); 2216 for (iterator itr = entry.links().begin();
2186 itr != entry.links().end(); ++itr) { 2217 itr != entry.links().end(); ++itr) {
2187 if ((*itr)->type() != google_apis::Link::LINK_PARENT) 2218 if ((*itr)->type() != google_apis::Link::LINK_PARENT)
(...skipping 11 matching lines...) Expand all
2199 if ((*itr)->href().GetOrigin() != resource_link.GetOrigin() || 2230 if ((*itr)->href().GetOrigin() != resource_link.GetOrigin() ||
2200 (*itr)->href().path() != resource_link.path()) 2231 (*itr)->href().path() != resource_link.path())
2201 continue; 2232 continue;
2202 2233
2203 *origin_out = origin; 2234 *origin_out = origin;
2204 return true; 2235 return true;
2205 } 2236 }
2206 return false; 2237 return false;
2207 } 2238 }
2208 2239
2240 void DriveFileSyncService::SchedulePolling() {
2241 if (polling_timer_.IsRunning() ||
2242 polling_delay_seconds_ < 0 ||
2243 GetCurrentState() == REMOTE_SERVICE_DISABLED)
2244 return;
2245
2246 DVLOG(1) << "Polling scheduled"
2247 << " (delay:" << polling_delay_seconds_ << "s)";
2248
2249 polling_timer_.Start(
2250 FROM_HERE, base::TimeDelta::FromSeconds(polling_delay_seconds_),
2251 base::Bind(&DriveFileSyncService::OnPollingTimerFired, AsWeakPtr()));
2252 }
2253
2254 void DriveFileSyncService::OnPollingTimerFired() {
2255 may_have_unfetched_changes_ = true;
2256 MaybeStartFetchChanges();
2257 }
2258
2259 void DriveFileSyncService::UpdatePollingDelay(int64 new_delay_sec) {
2260 // polling_delay_seconds_ made negative to disable polling for testing.
2261 if (polling_delay_seconds_ < 0)
2262 return;
2263
2264 if (state_ == REMOTE_SERVICE_TEMPORARY_UNAVAILABLE) {
2265 // If the service state is TEMPORARY_UNAVAILABLE, poll the service
2266 // with a modest duration (but more frequently than
2267 // kPollingDelaySecondsWithNotification) so that we have a mild chance
2268 // to recover the state.
2269 polling_delay_seconds_ = kMaximumPollingDelaySeconds;
2270 polling_timer_.Stop();
2271 return;
2272 }
2273
2274 int64 old_delay = polling_delay_seconds_;
2275
2276 // Push notifications off.
2277 polling_delay_seconds_ = std::min(new_delay_sec, kMaximumPollingDelaySeconds);
2278
2279 if (polling_delay_seconds_ < old_delay)
2280 polling_timer_.Stop();
2281 }
2282
2209 void DriveFileSyncService::NotifyObserversFileStatusChanged( 2283 void DriveFileSyncService::NotifyObserversFileStatusChanged(
2210 const FileSystemURL& url, 2284 const FileSystemURL& url,
2211 SyncFileStatus sync_status, 2285 SyncFileStatus sync_status,
2212 SyncAction action_taken, 2286 SyncAction action_taken,
2213 SyncDirection direction) { 2287 SyncDirection direction) {
2214 if (sync_status != SYNC_FILE_STATUS_SYNCED) { 2288 if (sync_status != SYNC_FILE_STATUS_SYNCED) {
2215 DCHECK_EQ(SYNC_ACTION_NONE, action_taken); 2289 DCHECK_EQ(SYNC_ACTION_NONE, action_taken);
2216 DCHECK_EQ(SYNC_DIRECTION_NONE, direction); 2290 DCHECK_EQ(SYNC_DIRECTION_NONE, direction);
2217 } 2291 }
2218 2292
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
2285 pending_batch_sync_origins_.insert(origin); 2359 pending_batch_sync_origins_.insert(origin);
2286 } 2360 }
2287 callback.Run(status, resource_id); 2361 callback.Run(status, resource_id);
2288 } 2362 }
2289 2363
2290 std::string DriveFileSyncService::sync_root_resource_id() { 2364 std::string DriveFileSyncService::sync_root_resource_id() {
2291 return metadata_store_->sync_root_directory(); 2365 return metadata_store_->sync_root_directory();
2292 } 2366 }
2293 2367
2294 } // namespace sync_file_system 2368 } // namespace sync_file_system
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698