sync_file_system_service.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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 3// found in the LICENSE file. 4 5#include "chrome/browser/sync_file_system/sync_file_system_service.h" 6 7#include <string> 8 9#include "base/bind.h" 10#include "base/format_macros.h" 11#include "base/logging.h" 12#include "base/memory/ref_counted.h" 13#include "base/stl_util.h" 14#include "chrome/browser/chrome_notification_types.h" 15#include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer.h" 16#include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h" 17#include "chrome/browser/extensions/extension_prefs.h" 18#include "chrome/browser/profiles/profile.h" 19#include "chrome/browser/sync/profile_sync_service.h" 20#include "chrome/browser/sync/profile_sync_service_factory.h" 21#include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service.h" 22#include "chrome/browser/sync_file_system/local/local_file_sync_service.h" 23#include "chrome/browser/sync_file_system/logger.h" 24#include "chrome/browser/sync_file_system/sync_direction.h" 25#include "chrome/browser/sync_file_system/sync_event_observer.h" 26#include "chrome/browser/sync_file_system/sync_file_metadata.h" 27#include "chrome/browser/sync_file_system/sync_status_code.h" 28#include "chrome/browser/sync_file_system/syncable_file_system_util.h" 29#include "chrome/common/extensions/extension.h" 30#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" 31#include "content/public/browser/browser_thread.h" 32#include "content/public/browser/notification_details.h" 33#include "content/public/browser/notification_service.h" 34#include "content/public/browser/storage_partition.h" 35#include "extensions/common/manifest_constants.h" 36#include "url/gurl.h" 37#include "webkit/browser/fileapi/file_system_context.h" 38 39using content::BrowserThread; 40using extensions::Extension; 41using extensions::ExtensionPrefs; 42using fileapi::FileSystemURL; 43using fileapi::FileSystemURLSet; 44 45namespace sync_file_system { 46 47namespace { 48 49// Default delay when more changes are available. 50const int64 kSyncDelayInMilliseconds = 1 * base::Time::kMillisecondsPerSecond; 51 52// Default delay when there're more than 10 pending changes. 53const int64 kSyncDelayFastInMilliseconds = 100; 54const int kPendingChangeThresholdForFastSync = 10; 55 56// Default delay when remote service is temporarily unavailable. 57const int64 kSyncDelaySlowInMilliseconds = 58 30 * base::Time::kMillisecondsPerSecond; // Start with 30 sec + exp backoff 59 60// Default delay when there're no changes. 61const int64 kSyncDelayMaxInMilliseconds = 62 30 * 60 * base::Time::kMillisecondsPerSecond; // 30 min 63 64SyncServiceState RemoteStateToSyncServiceState( 65 RemoteServiceState state) { 66 switch (state) { 67 case REMOTE_SERVICE_OK: 68 return SYNC_SERVICE_RUNNING; 69 case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE: 70 return SYNC_SERVICE_TEMPORARY_UNAVAILABLE; 71 case REMOTE_SERVICE_AUTHENTICATION_REQUIRED: 72 return SYNC_SERVICE_AUTHENTICATION_REQUIRED; 73 case REMOTE_SERVICE_DISABLED: 74 return SYNC_SERVICE_DISABLED; 75 } 76 NOTREACHED() << "Unknown remote service state: " << state; 77 return SYNC_SERVICE_DISABLED; 78} 79 80void DidHandleOriginForExtensionUnloadedEvent( 81 int type, 82 const GURL& origin, 83 SyncStatusCode code) { 84 DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED == type || 85 chrome::NOTIFICATION_EXTENSION_UNINSTALLED == type); 86 if (code != SYNC_STATUS_OK && 87 code != SYNC_STATUS_UNKNOWN_ORIGIN) { 88 switch (type) { 89 case chrome::NOTIFICATION_EXTENSION_UNLOADED: 90 util::Log(logging::LOG_WARNING, 91 FROM_HERE, 92 "Disabling origin for UNLOADED(DISABLE) failed: %s", 93 origin.spec().c_str()); 94 break; 95 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: 96 util::Log(logging::LOG_WARNING, 97 FROM_HERE, 98 "Uninstall origin for UNINSTALLED failed: %s", 99 origin.spec().c_str()); 100 break; 101 default: 102 break; 103 } 104 } 105} 106 107void DidHandleOriginForExtensionEnabledEvent( 108 int type, 109 const GURL& origin, 110 SyncStatusCode code) { 111 DCHECK(chrome::NOTIFICATION_EXTENSION_ENABLED == type); 112 if (code != SYNC_STATUS_OK) 113 util::Log(logging::LOG_WARNING, 114 FROM_HERE, 115 "Enabling origin for ENABLED failed: %s", 116 origin.spec().c_str()); 117} 118 119std::string SyncFileStatusToString(SyncFileStatus sync_file_status) { 120 return extensions::api::sync_file_system::ToString( 121 extensions::SyncFileStatusToExtensionEnum(sync_file_status)); 122} 123 124// Gets called repeatedly until every SyncFileStatus has been mapped. 125void DidGetFileSyncStatusForDump( 126 base::ListValue* files, 127 size_t* num_results, 128 const SyncFileSystemService::DumpFilesCallback& callback, 129 base::DictionaryValue* file, 130 SyncStatusCode sync_status_code, 131 SyncFileStatus sync_file_status) { 132 DCHECK(files); 133 DCHECK(num_results); 134 135 if (file) 136 file->SetString("status", SyncFileStatusToString(sync_file_status)); 137 138 // Once all results have been received, run the callback to signal end. 139 DCHECK_LE(*num_results, files->GetSize()); 140 if (++*num_results < files->GetSize()) 141 return; 142 143 callback.Run(files); 144} 145 146} // namespace 147 148class SyncFileSystemService::SyncRunner { 149 public: 150 typedef base::Callback<void(const SyncStatusCallback&)> Task; 151 SyncRunner(const std::string& task_name, 152 const Task& sync_task, 153 RemoteFileSyncService* remote_service) 154 : task_name_(task_name), 155 sync_task_(sync_task), 156 remote_service_(remote_service), 157 current_delay_(0), 158 last_delay_(0), 159 pending_changes_(0), 160 running_(false), 161 factory_(this) {} 162 ~SyncRunner() {} 163 164 void Schedule() { 165 int64 delay = kSyncDelayInMilliseconds; 166 if (pending_changes_ == 0) { 167 ScheduleInternal(kSyncDelayMaxInMilliseconds); 168 return; 169 } 170 switch (remote_service_->GetCurrentState()) { 171 case REMOTE_SERVICE_OK: 172 if (pending_changes_ > kPendingChangeThresholdForFastSync) 173 delay = kSyncDelayFastInMilliseconds; 174 else 175 delay = kSyncDelayInMilliseconds; 176 break; 177 178 case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE: 179 delay = kSyncDelaySlowInMilliseconds; 180 if (last_delay_ >= kSyncDelaySlowInMilliseconds && 181 last_delay_ < kSyncDelayMaxInMilliseconds) 182 delay = last_delay_ * 2; 183 break; 184 185 case REMOTE_SERVICE_AUTHENTICATION_REQUIRED: 186 case REMOTE_SERVICE_DISABLED: 187 delay = kSyncDelayMaxInMilliseconds; 188 break; 189 } 190 ScheduleInternal(delay); 191 } 192 193 void ScheduleIfNotRunning() { 194 if (!timer_.IsRunning()) 195 Schedule(); 196 } 197 198 void OnChangesUpdated(int64 pending_changes) { 199 DCHECK_GE(pending_changes, 0); 200 if (pending_changes_ != pending_changes) { 201 util::Log(logging::LOG_VERBOSE, FROM_HERE, 202 "[%s] pending_changes updated: %" PRId64, 203 task_name_.c_str(), pending_changes); 204 } 205 pending_changes_ = pending_changes; 206 Schedule(); 207 } 208 209 int64 pending_changes() const { return pending_changes_; } 210 211 private: 212 void Finished(SyncStatusCode status) { 213 DCHECK(running_); 214 running_ = false; 215 util::Log(logging::LOG_VERBOSE, FROM_HERE, 216 "[%s] * Finished (elapsed: %" PRId64 " sec)", 217 task_name_.c_str(), 218 (base::Time::Now() - last_scheduled_).InSeconds()); 219 if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC || 220 status == SYNC_STATUS_FILE_BUSY) 221 ScheduleInternal(kSyncDelayMaxInMilliseconds); 222 else 223 Schedule(); 224 } 225 226 void Run() { 227 if (running_) 228 return; 229 running_ = true; 230 last_scheduled_ = base::Time::Now(); 231 last_delay_ = current_delay_; 232 233 util::Log(logging::LOG_VERBOSE, FROM_HERE, 234 "[%s] * Started", task_name_.c_str()); 235 236 sync_task_.Run(base::Bind(&SyncRunner::Finished, factory_.GetWeakPtr())); 237 } 238 239 void ScheduleInternal(int64 delay) { 240 base::TimeDelta time_to_next = base::TimeDelta::FromMilliseconds(delay); 241 242 if (timer_.IsRunning()) { 243 if (current_delay_ == delay) 244 return; 245 246 base::TimeDelta elapsed = base::Time::Now() - last_scheduled_; 247 if (elapsed < time_to_next) { 248 time_to_next = time_to_next - elapsed; 249 } else { 250 time_to_next = base::TimeDelta::FromMilliseconds( 251 kSyncDelayFastInMilliseconds); 252 } 253 timer_.Stop(); 254 } 255 256 if (current_delay_ != delay) { 257 util::Log(logging::LOG_VERBOSE, FROM_HERE, 258 "[%s] Scheduling task in %" PRId64 " secs", 259 task_name_.c_str(), time_to_next.InSeconds()); 260 } 261 current_delay_ = delay; 262 263 timer_.Start(FROM_HERE, time_to_next, this, &SyncRunner::Run); 264 } 265 266 std::string task_name_; 267 Task sync_task_; 268 RemoteFileSyncService* remote_service_; 269 base::OneShotTimer<SyncRunner> timer_; 270 base::Time last_scheduled_; 271 int64 current_delay_; 272 int64 last_delay_; 273 int64 pending_changes_; 274 bool running_; 275 base::WeakPtrFactory<SyncRunner> factory_; 276 277 DISALLOW_COPY_AND_ASSIGN(SyncRunner); 278}; 279 280//----------------------------------------------------------------------------- 281 282void SyncFileSystemService::Shutdown() { 283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 284 285 local_sync_.reset(); 286 remote_sync_.reset(); 287 288 local_file_service_->Shutdown(); 289 local_file_service_.reset(); 290 291 remote_file_service_.reset(); 292 293 ProfileSyncServiceBase* profile_sync_service = 294 ProfileSyncServiceFactory::GetForProfile(profile_); 295 if (profile_sync_service) 296 profile_sync_service->RemoveObserver(this); 297 298 profile_ = NULL; 299} 300 301SyncFileSystemService::~SyncFileSystemService() { 302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 303 DCHECK(!profile_); 304} 305 306void SyncFileSystemService::InitializeForApp( 307 fileapi::FileSystemContext* file_system_context, 308 const GURL& app_origin, 309 const SyncStatusCallback& callback) { 310 DCHECK(local_file_service_); 311 DCHECK(remote_file_service_); 312 DCHECK(app_origin == app_origin.GetOrigin()); 313 314 util::Log(logging::LOG_VERBOSE, FROM_HERE, 315 "Initializing for App: %s", app_origin.spec().c_str()); 316 317 local_file_service_->MaybeInitializeFileSystemContext( 318 app_origin, file_system_context, 319 base::Bind(&SyncFileSystemService::DidInitializeFileSystem, 320 AsWeakPtr(), app_origin, callback)); 321} 322 323SyncServiceState SyncFileSystemService::GetSyncServiceState() { 324 return RemoteStateToSyncServiceState(remote_file_service_->GetCurrentState()); 325} 326 327void SyncFileSystemService::GetExtensionStatusMap( 328 std::map<GURL, std::string>* status_map) { 329 DCHECK(status_map); 330 remote_file_service_->GetOriginStatusMap(status_map); 331} 332 333void SyncFileSystemService::DumpFiles(const GURL& origin, 334 const DumpFilesCallback& callback) { 335 DCHECK(!origin.is_empty()); 336 337 content::StoragePartition* storage_partition = 338 content::BrowserContext::GetStoragePartitionForSite(profile_, origin); 339 fileapi::FileSystemContext* file_system_context = 340 storage_partition->GetFileSystemContext(); 341 local_file_service_->MaybeInitializeFileSystemContext( 342 origin, file_system_context, 343 base::Bind(&SyncFileSystemService::DidInitializeFileSystemForDump, 344 AsWeakPtr(), origin, callback)); 345} 346 347void SyncFileSystemService::GetFileSyncStatus( 348 const FileSystemURL& url, const SyncFileStatusCallback& callback) { 349 DCHECK(local_file_service_); 350 DCHECK(remote_file_service_); 351 352 // It's possible to get an invalid FileEntry. 353 if (!url.is_valid()) { 354 base::MessageLoopProxy::current()->PostTask( 355 FROM_HERE, 356 base::Bind(callback, 357 SYNC_FILE_ERROR_INVALID_URL, 358 SYNC_FILE_STATUS_UNKNOWN)); 359 return; 360 } 361 362 if (remote_file_service_->IsConflicting(url)) { 363 base::MessageLoopProxy::current()->PostTask( 364 FROM_HERE, 365 base::Bind(callback, 366 SYNC_STATUS_OK, 367 SYNC_FILE_STATUS_CONFLICTING)); 368 return; 369 } 370 371 local_file_service_->HasPendingLocalChanges( 372 url, 373 base::Bind(&SyncFileSystemService::DidGetLocalChangeStatus, 374 AsWeakPtr(), callback)); 375} 376 377void SyncFileSystemService::AddSyncEventObserver(SyncEventObserver* observer) { 378 observers_.AddObserver(observer); 379} 380 381void SyncFileSystemService::RemoveSyncEventObserver( 382 SyncEventObserver* observer) { 383 observers_.RemoveObserver(observer); 384} 385 386ConflictResolutionPolicy 387SyncFileSystemService::GetConflictResolutionPolicy() const { 388 return remote_file_service_->GetConflictResolutionPolicy(); 389} 390 391SyncStatusCode SyncFileSystemService::SetConflictResolutionPolicy( 392 ConflictResolutionPolicy policy) { 393 return remote_file_service_->SetConflictResolutionPolicy(policy); 394} 395 396SyncFileSystemService::SyncFileSystemService(Profile* profile) 397 : profile_(profile), 398 sync_enabled_(true) { 399} 400 401void SyncFileSystemService::Initialize( 402 scoped_ptr<LocalFileSyncService> local_file_service, 403 scoped_ptr<RemoteFileSyncService> remote_file_service) { 404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 405 DCHECK(local_file_service); 406 DCHECK(remote_file_service); 407 DCHECK(profile_); 408 409 local_file_service_ = local_file_service.Pass(); 410 remote_file_service_ = remote_file_service.Pass(); 411 412 local_file_service_->AddChangeObserver(this); 413 local_file_service_->SetLocalChangeProcessor( 414 remote_file_service_->GetLocalChangeProcessor()); 415 416 remote_file_service_->AddServiceObserver(this); 417 remote_file_service_->AddFileStatusObserver(this); 418 remote_file_service_->SetRemoteChangeProcessor(local_file_service_.get()); 419 420 local_sync_.reset(new SyncRunner( 421 "Local sync", 422 base::Bind(&SyncFileSystemService::StartLocalSync, AsWeakPtr()), 423 remote_file_service_.get())); 424 remote_sync_.reset(new SyncRunner( 425 "Remote sync", 426 base::Bind(&SyncFileSystemService::StartRemoteSync, AsWeakPtr()), 427 remote_file_service_.get())); 428 429 ProfileSyncServiceBase* profile_sync_service = 430 ProfileSyncServiceFactory::GetForProfile(profile_); 431 if (profile_sync_service) { 432 UpdateSyncEnabledStatus(profile_sync_service); 433 profile_sync_service->AddObserver(this); 434 } 435 436 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, 437 content::Source<Profile>(profile_)); 438 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 439 content::Source<Profile>(profile_)); 440 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED, 441 content::Source<Profile>(profile_)); 442 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED, 443 content::Source<Profile>(profile_)); 444} 445 446void SyncFileSystemService::DidInitializeFileSystem( 447 const GURL& app_origin, 448 const SyncStatusCallback& callback, 449 SyncStatusCode status) { 450 DVLOG(1) << "DidInitializeFileSystem: " 451 << app_origin.spec() << " " << status; 452 453 if (status != SYNC_STATUS_OK) { 454 callback.Run(status); 455 return; 456 } 457 458 // Local side of initialization for the app is done. 459 // Continue on initializing the remote side. 460 remote_file_service_->RegisterOrigin( 461 app_origin, 462 base::Bind(&SyncFileSystemService::DidRegisterOrigin, 463 AsWeakPtr(), app_origin, callback)); 464} 465 466void SyncFileSystemService::DidRegisterOrigin( 467 const GURL& app_origin, 468 const SyncStatusCallback& callback, 469 SyncStatusCode status) { 470 util::Log(logging::LOG_VERBOSE, FROM_HERE, 471 "DidInitializeForApp (registered the origin): %s: %s", 472 app_origin.spec().c_str(), 473 SyncStatusCodeToString(status)); 474 475 callback.Run(status); 476} 477 478void SyncFileSystemService::DidInitializeFileSystemForDump( 479 const GURL& origin, 480 const DumpFilesCallback& callback, 481 SyncStatusCode status) { 482 DCHECK(!origin.is_empty()); 483 484 if (status != SYNC_STATUS_OK) { 485 base::ListValue empty_result; 486 callback.Run(&empty_result); 487 return; 488 } 489 490 base::ListValue* files = remote_file_service_->DumpFiles(origin).release(); 491 if (!files->GetSize()) { 492 callback.Run(files); 493 return; 494 } 495 496 base::Callback<void(base::DictionaryValue* file, 497 SyncStatusCode sync_status, 498 SyncFileStatus sync_file_status)> completion_callback = 499 base::Bind(&DidGetFileSyncStatusForDump, base::Owned(files), 500 base::Owned(new size_t(0)), callback); 501 502 // After all metadata loaded, sync status can be added to each entry. 503 for (size_t i = 0; i < files->GetSize(); ++i) { 504 base::DictionaryValue* file = NULL; 505 std::string path_string; 506 if (!files->GetDictionary(i, &file) || 507 !file->GetString("path", &path_string)) { 508 NOTREACHED(); 509 completion_callback.Run( 510 NULL, SYNC_FILE_ERROR_FAILED, SYNC_FILE_STATUS_UNKNOWN); 511 continue; 512 } 513 514 base::FilePath file_path = base::FilePath::FromUTF8Unsafe(path_string); 515 FileSystemURL url = CreateSyncableFileSystemURL(origin, file_path); 516 GetFileSyncStatus(url, base::Bind(completion_callback, file)); 517 } 518} 519 520void SyncFileSystemService::SetSyncEnabledForTesting(bool enabled) { 521 sync_enabled_ = enabled; 522 remote_file_service_->SetSyncEnabled(sync_enabled_); 523} 524 525void SyncFileSystemService::StartLocalSync( 526 const SyncStatusCallback& callback) { 527 local_file_service_->ProcessLocalChange( 528 base::Bind(&SyncFileSystemService::DidProcessLocalChange, AsWeakPtr(), 529 callback)); 530} 531 532void SyncFileSystemService::StartRemoteSync( 533 const SyncStatusCallback& callback) { 534 remote_file_service_->ProcessRemoteChange( 535 base::Bind(&SyncFileSystemService::DidProcessRemoteChange, AsWeakPtr(), 536 callback)); 537} 538 539void SyncFileSystemService::DidProcessRemoteChange( 540 const SyncStatusCallback& callback, 541 SyncStatusCode status, 542 const FileSystemURL& url) { 543 util::Log(logging::LOG_VERBOSE, FROM_HERE, 544 "ProcessRemoteChange finished with status=%d (%s) for url=%s", 545 status, SyncStatusCodeToString(status), url.DebugString().c_str()); 546 547 if (status == SYNC_STATUS_FILE_BUSY) { 548 local_file_service_->RegisterURLForWaitingSync( 549 url, base::Bind(&SyncFileSystemService::OnSyncEnabledForRemoteSync, 550 AsWeakPtr())); 551 } 552 553 callback.Run(status); 554} 555 556void SyncFileSystemService::DidProcessLocalChange( 557 const SyncStatusCallback& callback, 558 SyncStatusCode status, 559 const FileSystemURL& url) { 560 util::Log(logging::LOG_VERBOSE, FROM_HERE, 561 "ProcessLocalChange finished with status=%d (%s) for url=%s", 562 status, SyncStatusCodeToString(status), url.DebugString().c_str()); 563 564 callback.Run(status); 565} 566 567void SyncFileSystemService::DidGetLocalChangeStatus( 568 const SyncFileStatusCallback& callback, 569 SyncStatusCode status, 570 bool has_pending_local_changes) { 571 callback.Run( 572 status, 573 has_pending_local_changes ? 574 SYNC_FILE_STATUS_HAS_PENDING_CHANGES : SYNC_FILE_STATUS_SYNCED); 575} 576 577void SyncFileSystemService::OnSyncEnabledForRemoteSync() { 578 remote_sync_->Schedule(); 579} 580 581void SyncFileSystemService::OnLocalChangeAvailable(int64 pending_changes) { 582 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 583 584 local_sync_->OnChangesUpdated(pending_changes); 585 remote_sync_->ScheduleIfNotRunning(); 586} 587 588void SyncFileSystemService::OnRemoteChangeQueueUpdated(int64 pending_changes) { 589 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 590 591 remote_sync_->OnChangesUpdated(pending_changes); 592 local_sync_->ScheduleIfNotRunning(); 593} 594 595void SyncFileSystemService::OnRemoteServiceStateUpdated( 596 RemoteServiceState state, 597 const std::string& description) { 598 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 599 util::Log(logging::LOG_INFO, FROM_HERE, 600 "OnRemoteServiceStateChanged: %d %s", state, description.c_str()); 601 602 remote_sync_->Schedule(); 603 local_sync_->Schedule(); 604 605 FOR_EACH_OBSERVER( 606 SyncEventObserver, observers_, 607 OnSyncStateUpdated(GURL(), 608 RemoteStateToSyncServiceState(state), 609 description)); 610} 611 612void SyncFileSystemService::Observe( 613 int type, 614 const content::NotificationSource& source, 615 const content::NotificationDetails& details) { 616 // Event notification sequence. 617 // 618 // (User action) (Notification type) 619 // Install: INSTALLED. 620 // Update: INSTALLED. 621 // Uninstall: UNINSTALLED. 622 // Launch, Close: No notification. 623 // Enable: ENABLED. 624 // Disable: UNLOADED(DISABLE). 625 // Reload, Restart: UNLOADED(DISABLE) -> INSTALLED -> ENABLED. 626 // 627 switch (type) { 628 case chrome::NOTIFICATION_EXTENSION_INSTALLED: 629 HandleExtensionInstalled(details); 630 break; 631 case chrome::NOTIFICATION_EXTENSION_UNLOADED: 632 HandleExtensionUnloaded(type, details); 633 break; 634 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: 635 HandleExtensionUninstalled(type, details); 636 break; 637 case chrome::NOTIFICATION_EXTENSION_ENABLED: 638 HandleExtensionEnabled(type, details); 639 break; 640 default: 641 NOTREACHED() << "Unknown notification."; 642 break; 643 } 644} 645 646void SyncFileSystemService::HandleExtensionInstalled( 647 const content::NotificationDetails& details) { 648 const Extension* extension = 649 content::Details<const extensions::InstalledExtensionInfo>(details)-> 650 extension; 651 GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id()); 652 DVLOG(1) << "Handle extension notification for INSTALLED: " << app_origin; 653 // NOTE: When an app is uninstalled and re-installed in a sequence, 654 // |local_file_service_| may still keeps |app_origin| as disabled origin. 655 local_file_service_->SetOriginEnabled(app_origin, true); 656} 657 658void SyncFileSystemService::HandleExtensionUnloaded( 659 int type, 660 const content::NotificationDetails& details) { 661 content::Details<const extensions::UnloadedExtensionInfo> info(details); 662 if (info->reason != extensions::UnloadedExtensionInfo::REASON_DISABLE) 663 return; 664 665 std::string extension_id = info->extension->id(); 666 GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id); 667 668 int reasons = ExtensionPrefs::Get(profile_)->GetDisableReasons(extension_id); 669 if (reasons & Extension::DISABLE_RELOAD) { 670 // Bypass disabling the origin since the app will be re-enabled soon. 671 // NOTE: If re-enabling the app fails, the app is disabled while it is 672 // handled as enabled origin in the SyncFS. This should be safe and will be 673 // recovered when the user re-enables the app manually or the sync service 674 // restarts. 675 DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE_RELOAD): " 676 << app_origin; 677 return; 678 } 679 680 DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): " 681 << app_origin; 682 remote_file_service_->DisableOrigin( 683 app_origin, 684 base::Bind(&DidHandleOriginForExtensionUnloadedEvent, 685 type, app_origin)); 686 local_file_service_->SetOriginEnabled(app_origin, false); 687} 688 689void SyncFileSystemService::HandleExtensionUninstalled( 690 int type, 691 const content::NotificationDetails& details) { 692 const Extension* extension = content::Details<const Extension>(details).ptr(); 693 DCHECK(extension); 694 695 RemoteFileSyncService::UninstallFlag flag = 696 RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE; 697 // If it's loaded from an unpacked package and with key: field, 698 // the uninstall will not be sync'ed and the user might be using the 699 // same app key in other installs, so avoid purging the remote folder. 700 if (extensions::Manifest::IsUnpackedLocation(extension->location()) && 701 extension->manifest()->HasKey(extensions::manifest_keys::kKey)) { 702 flag = RemoteFileSyncService::UNINSTALL_AND_KEEP_REMOTE; 703 } 704 705 GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id()); 706 DVLOG(1) << "Handle extension notification for UNINSTALLED: " 707 << app_origin; 708 remote_file_service_->UninstallOrigin( 709 app_origin, flag, 710 base::Bind(&DidHandleOriginForExtensionUnloadedEvent, 711 type, app_origin)); 712 local_file_service_->SetOriginEnabled(app_origin, false); 713} 714 715void SyncFileSystemService::HandleExtensionEnabled( 716 int type, 717 const content::NotificationDetails& details) { 718 std::string extension_id = content::Details<const Extension>(details)->id(); 719 GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id); 720 DVLOG(1) << "Handle extension notification for ENABLED: " << app_origin; 721 remote_file_service_->EnableOrigin( 722 app_origin, 723 base::Bind(&DidHandleOriginForExtensionEnabledEvent, type, app_origin)); 724 local_file_service_->SetOriginEnabled(app_origin, true); 725} 726 727void SyncFileSystemService::OnStateChanged() { 728 ProfileSyncServiceBase* profile_sync_service = 729 ProfileSyncServiceFactory::GetForProfile(profile_); 730 if (profile_sync_service) 731 UpdateSyncEnabledStatus(profile_sync_service); 732} 733 734void SyncFileSystemService::OnFileStatusChanged( 735 const FileSystemURL& url, 736 SyncFileStatus sync_status, 737 SyncAction action_taken, 738 SyncDirection direction) { 739 FOR_EACH_OBSERVER( 740 SyncEventObserver, observers_, 741 OnFileSynced(url, sync_status, action_taken, direction)); 742} 743 744void SyncFileSystemService::UpdateSyncEnabledStatus( 745 ProfileSyncServiceBase* profile_sync_service) { 746 if (!profile_sync_service->HasSyncSetupCompleted()) 747 return; 748 bool old_sync_enabled = sync_enabled_; 749 sync_enabled_ = profile_sync_service->GetActiveDataTypes().Has( 750 syncer::APPS); 751 remote_file_service_->SetSyncEnabled(sync_enabled_); 752 if (!old_sync_enabled && sync_enabled_) { 753 local_sync_->Schedule(); 754 remote_sync_->Schedule(); 755 } 756} 757 758} // namespace sync_file_system 759