sync_file_system_service.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/logging.h" 11#include "base/memory/ref_counted.h" 12#include "base/stl_util.h" 13#include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer.h" 14#include "chrome/browser/profiles/profile.h" 15#include "chrome/browser/profiles/profile_dependency_manager.h" 16#include "chrome/browser/sync/profile_sync_service.h" 17#include "chrome/browser/sync/profile_sync_service_factory.h" 18#include "chrome/browser/sync_file_system/drive_file_sync_service.h" 19#include "chrome/browser/sync_file_system/local_file_sync_service.h" 20#include "chrome/browser/sync_file_system/sync_event_observer.h" 21#include "chrome/common/chrome_notification_types.h" 22#include "chrome/common/extensions/extension.h" 23#include "content/public/browser/browser_thread.h" 24#include "content/public/browser/notification_details.h" 25#include "content/public/browser/notification_service.h" 26#include "googleurl/src/gurl.h" 27#include "webkit/fileapi/file_system_context.h" 28#include "webkit/fileapi/syncable/sync_direction.h" 29#include "webkit/fileapi/syncable/sync_file_metadata.h" 30#include "webkit/fileapi/syncable/sync_status_code.h" 31 32using content::BrowserThread; 33using fileapi::FileSystemURL; 34using fileapi::FileSystemURLSet; 35 36namespace sync_file_system { 37 38namespace { 39 40SyncEventObserver::SyncServiceState RemoteStateToSyncServiceState( 41 RemoteServiceState state) { 42 switch (state) { 43 case REMOTE_SERVICE_OK: 44 return SyncEventObserver::SYNC_SERVICE_RUNNING; 45 case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE: 46 return SyncEventObserver::SYNC_SERVICE_TEMPORARY_UNAVAILABLE; 47 case REMOTE_SERVICE_AUTHENTICATION_REQUIRED: 48 return SyncEventObserver::SYNC_SERVICE_AUTHENTICATION_REQUIRED; 49 case REMOTE_SERVICE_DISABLED: 50 return SyncEventObserver::SYNC_SERVICE_DISABLED; 51 } 52 NOTREACHED(); 53 return SyncEventObserver::SYNC_SERVICE_DISABLED; 54} 55 56void DidHandleOriginForExtensionUnloadedEvent( 57 int type, 58 extension_misc::UnloadedExtensionReason reason, 59 const GURL& origin, 60 SyncStatusCode code) { 61 DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED == type); 62 DCHECK(extension_misc::UNLOAD_REASON_DISABLE == reason || 63 extension_misc::UNLOAD_REASON_UNINSTALL == reason); 64 if (code != SYNC_STATUS_OK) { 65 switch (reason) { 66 case extension_misc::UNLOAD_REASON_DISABLE: 67 LOG(WARNING) << "Disabling origin for UNLOAD(DISABLE) failed: " 68 << origin.spec(); 69 break; 70 case extension_misc::UNLOAD_REASON_UNINSTALL: 71 LOG(WARNING) << "Uninstall origin for UNLOAD(UNINSTALL) failed: " 72 << origin.spec(); 73 break; 74 default: 75 break; 76 } 77 } 78} 79 80void DidHandleOriginForExtensionEnabledEvent( 81 int type, 82 const GURL& origin, 83 SyncStatusCode code) { 84 DCHECK(chrome::NOTIFICATION_EXTENSION_ENABLED == type); 85 if (code != SYNC_STATUS_OK) 86 LOG(WARNING) << "Enabling origin for ENABLED failed: " << origin.spec(); 87} 88 89} // namespace 90 91void SyncFileSystemService::Shutdown() { 92 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 93 94 local_file_service_->Shutdown(); 95 local_file_service_.reset(); 96 97 remote_file_service_.reset(); 98 99 ProfileSyncServiceBase* profile_sync_service = 100 ProfileSyncServiceFactory::GetForProfile(profile_); 101 if (profile_sync_service) 102 profile_sync_service->RemoveObserver(this); 103 104 profile_ = NULL; 105} 106 107SyncFileSystemService::~SyncFileSystemService() { 108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 109 DCHECK(!profile_); 110} 111 112void SyncFileSystemService::InitializeForApp( 113 fileapi::FileSystemContext* file_system_context, 114 const std::string& service_name, 115 const GURL& app_origin, 116 const SyncStatusCallback& callback) { 117 DCHECK(local_file_service_); 118 DCHECK(remote_file_service_); 119 DCHECK(app_origin == app_origin.GetOrigin()); 120 121 DVLOG(1) << "InitializeForApp: " << app_origin.spec(); 122 123 local_file_service_->MaybeInitializeFileSystemContext( 124 app_origin, service_name, file_system_context, 125 base::Bind(&SyncFileSystemService::DidInitializeFileSystem, 126 AsWeakPtr(), app_origin, callback)); 127} 128 129void SyncFileSystemService::GetFileSyncStatus( 130 const FileSystemURL& url, const SyncFileStatusCallback& callback) { 131 DCHECK(local_file_service_); 132 DCHECK(remote_file_service_); 133 134 // It's possible to get an invalid FileEntry. 135 if (!url.is_valid()) { 136 base::MessageLoopProxy::current()->PostTask( 137 FROM_HERE, 138 base::Bind(callback, 139 SYNC_FILE_ERROR_INVALID_URL, 140 SYNC_FILE_STATUS_UNKNOWN)); 141 return; 142 } 143 144 if (remote_file_service_->IsConflicting(url)) { 145 base::MessageLoopProxy::current()->PostTask( 146 FROM_HERE, 147 base::Bind(callback, 148 SYNC_STATUS_OK, 149 SYNC_FILE_STATUS_CONFLICTING)); 150 return; 151 } 152 153 local_file_service_->HasPendingLocalChanges( 154 url, 155 base::Bind(&SyncFileSystemService::DidGetLocalChangeStatus, 156 AsWeakPtr(), callback)); 157} 158 159void SyncFileSystemService::AddSyncEventObserver(SyncEventObserver* observer) { 160 observers_.AddObserver(observer); 161} 162 163void SyncFileSystemService::RemoveSyncEventObserver( 164 SyncEventObserver* observer) { 165 observers_.RemoveObserver(observer); 166} 167 168ConflictResolutionPolicy 169SyncFileSystemService::GetConflictResolutionPolicy() const { 170 return remote_file_service_->GetConflictResolutionPolicy(); 171} 172 173SyncStatusCode SyncFileSystemService::SetConflictResolutionPolicy( 174 ConflictResolutionPolicy policy) { 175 return remote_file_service_->SetConflictResolutionPolicy(policy); 176} 177 178SyncFileSystemService::SyncFileSystemService(Profile* profile) 179 : profile_(profile), 180 pending_local_changes_(0), 181 pending_remote_changes_(0), 182 local_sync_running_(false), 183 remote_sync_running_(false), 184 is_waiting_remote_sync_enabled_(false), 185 sync_enabled_(true) { 186} 187 188void SyncFileSystemService::Initialize( 189 scoped_ptr<LocalFileSyncService> local_file_service, 190 scoped_ptr<RemoteFileSyncService> remote_file_service) { 191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 192 DCHECK(local_file_service); 193 DCHECK(remote_file_service); 194 DCHECK(profile_); 195 196 local_file_service_ = local_file_service.Pass(); 197 remote_file_service_ = remote_file_service.Pass(); 198 199 local_file_service_->AddChangeObserver(this); 200 local_file_service_->SetLocalChangeProcessor( 201 remote_file_service_->GetLocalChangeProcessor()); 202 203 remote_file_service_->AddServiceObserver(this); 204 remote_file_service_->AddFileStatusObserver(this); 205 remote_file_service_->SetRemoteChangeProcessor(local_file_service_.get()); 206 207 ProfileSyncServiceBase* profile_sync_service = 208 ProfileSyncServiceFactory::GetForProfile(profile_); 209 if (profile_sync_service) { 210 UpdateSyncEnabledStatus(profile_sync_service); 211 profile_sync_service->AddObserver(this); 212 } 213 214 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 215 content::Source<Profile>(profile_)); 216 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED, 217 content::Source<Profile>(profile_)); 218} 219 220void SyncFileSystemService::DidInitializeFileSystem( 221 const GURL& app_origin, 222 const SyncStatusCallback& callback, 223 SyncStatusCode status) { 224 DVLOG(1) << "DidInitializeFileSystem: " 225 << app_origin.spec() << " " << status; 226 227 if (status != SYNC_STATUS_OK) { 228 callback.Run(status); 229 return; 230 } 231 232 // Local side of initialization for the app is done. 233 // Continue on initializing the remote side. 234 remote_file_service_->RegisterOriginForTrackingChanges( 235 app_origin, 236 base::Bind(&SyncFileSystemService::DidRegisterOrigin, 237 AsWeakPtr(), app_origin, callback)); 238} 239 240void SyncFileSystemService::DidRegisterOrigin( 241 const GURL& app_origin, 242 const SyncStatusCallback& callback, 243 SyncStatusCode status) { 244 DVLOG(1) << "DidRegisterOrigin: " << app_origin.spec() << " " << status; 245 246 callback.Run(status); 247} 248 249void SyncFileSystemService::SetSyncEnabledForTesting(bool enabled) { 250 sync_enabled_ = enabled; 251 remote_file_service_->SetSyncEnabled(sync_enabled_); 252} 253 254void SyncFileSystemService::MaybeStartSync() { 255 if (!profile_ || !sync_enabled_) 256 return; 257 258 DCHECK(local_file_service_); 259 DCHECK(remote_file_service_); 260 261 MaybeStartRemoteSync(); 262 MaybeStartLocalSync(); 263} 264 265void SyncFileSystemService::MaybeStartRemoteSync() { 266 if (remote_file_service_->GetCurrentState() == REMOTE_SERVICE_DISABLED) 267 return; 268 // See if we cannot / should not start a new remote sync. 269 if (remote_sync_running_ || pending_remote_changes_ == 0) 270 return; 271 // If we have registered a URL for waiting until sync is enabled on a 272 // file (and the registerred URL seems to be still valid) it won't be 273 // worth trying to start another remote sync. 274 if (is_waiting_remote_sync_enabled_) 275 return; 276 DCHECK(sync_enabled_); 277 DVLOG(1) << "Calling ProcessRemoteChange"; 278 remote_sync_running_ = true; 279 remote_file_service_->ProcessRemoteChange( 280 base::Bind(&SyncFileSystemService::DidProcessRemoteChange, 281 AsWeakPtr())); 282} 283 284void SyncFileSystemService::MaybeStartLocalSync() { 285 // If the remote service is not ready probably we should not start a 286 // local sync yet. 287 // (We should be still trying a remote sync so the state should become OK 288 // if the remote-side attempt succeeds.) 289 if (remote_file_service_->GetCurrentState() != REMOTE_SERVICE_OK) 290 return; 291 // See if we cannot / should not start a new local sync. 292 if (local_sync_running_ || pending_local_changes_ == 0) 293 return; 294 DVLOG(1) << "Calling ProcessLocalChange"; 295 local_sync_running_ = true; 296 local_file_service_->ProcessLocalChange( 297 base::Bind(&SyncFileSystemService::DidProcessLocalChange, 298 AsWeakPtr())); 299} 300 301void SyncFileSystemService::DidProcessRemoteChange( 302 SyncStatusCode status, 303 const FileSystemURL& url) { 304 DVLOG(1) << "DidProcessRemoteChange: " 305 << " status=" << status 306 << " (" << SyncStatusCodeToString(status) << ")" 307 << " url=" << url.DebugString(); 308 DCHECK(remote_sync_running_); 309 remote_sync_running_ = false; 310 311 if (status != SYNC_STATUS_NO_CHANGE_TO_SYNC && 312 remote_file_service_->GetCurrentState() != REMOTE_SERVICE_DISABLED) { 313 DCHECK(url.is_valid()); 314 local_file_service_->ClearSyncFlagForURL(url); 315 } 316 317 if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC) { 318 // We seem to have no changes to work on for now. 319 // TODO(kinuko): Might be better setting a timer to call MaybeStartSync. 320 return; 321 } 322 if (status == SYNC_STATUS_FILE_BUSY) { 323 is_waiting_remote_sync_enabled_ = true; 324 local_file_service_->RegisterURLForWaitingSync( 325 url, base::Bind(&SyncFileSystemService::OnSyncEnabledForRemoteSync, 326 AsWeakPtr())); 327 return; 328 } 329 330 base::MessageLoopProxy::current()->PostTask( 331 FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync, 332 AsWeakPtr())); 333} 334 335void SyncFileSystemService::DidProcessLocalChange( 336 SyncStatusCode status, const FileSystemURL& url) { 337 DVLOG(1) << "DidProcessLocalChange:" 338 << " status=" << status 339 << " (" << SyncStatusCodeToString(status) << ")" 340 << " url=" << url.DebugString(); 341 DCHECK(local_sync_running_); 342 local_sync_running_ = false; 343 344 if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC) { 345 // We seem to have no changes to work on for now. 346 return; 347 } 348 349 DCHECK(url.is_valid()); 350 local_file_service_->ClearSyncFlagForURL(url); 351 352 base::MessageLoopProxy::current()->PostTask( 353 FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync, 354 AsWeakPtr())); 355} 356 357void SyncFileSystemService::DidGetLocalChangeStatus( 358 const SyncFileStatusCallback& callback, 359 SyncStatusCode status, 360 bool has_pending_local_changes) { 361 callback.Run( 362 status, 363 has_pending_local_changes ? 364 SYNC_FILE_STATUS_HAS_PENDING_CHANGES : SYNC_FILE_STATUS_SYNCED); 365} 366 367void SyncFileSystemService::OnSyncEnabledForRemoteSync() { 368 is_waiting_remote_sync_enabled_ = false; 369 MaybeStartRemoteSync(); 370} 371 372void SyncFileSystemService::OnLocalChangeAvailable(int64 pending_changes) { 373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 374 DCHECK_GE(pending_changes, 0); 375 DVLOG(1) << "OnLocalChangeAvailable: " << pending_changes; 376 pending_local_changes_ = pending_changes; 377 base::MessageLoopProxy::current()->PostTask( 378 FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync, 379 AsWeakPtr())); 380} 381 382void SyncFileSystemService::OnRemoteChangeQueueUpdated(int64 pending_changes) { 383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 384 DCHECK_GE(pending_changes, 0); 385 DVLOG(1) << "OnRemoteChangeQueueUpdated: " << pending_changes; 386 pending_remote_changes_ = pending_changes; 387 if (pending_changes > 0) { 388 // The smallest change available might have changed from the previous one. 389 // Reset the is_waiting_remote_sync_enabled_ flag so that we can retry. 390 is_waiting_remote_sync_enabled_ = false; 391 } 392 base::MessageLoopProxy::current()->PostTask( 393 FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync, 394 AsWeakPtr())); 395} 396 397void SyncFileSystemService::OnRemoteServiceStateUpdated( 398 RemoteServiceState state, 399 const std::string& description) { 400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 401 DVLOG(1) << "OnRemoteServiceStateUpdated: " << state 402 << " " << description; 403 if (state == REMOTE_SERVICE_OK) { 404 base::MessageLoopProxy::current()->PostTask( 405 FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync, 406 AsWeakPtr())); 407 } 408 409 FOR_EACH_OBSERVER( 410 SyncEventObserver, observers_, 411 OnSyncStateUpdated(GURL(), 412 RemoteStateToSyncServiceState(state), 413 description)); 414} 415 416void SyncFileSystemService::Observe( 417 int type, 418 const content::NotificationSource& source, 419 const content::NotificationDetails& details) { 420 switch (type) { 421 // Delivered when an app is disabled, reloaded or restarted. 422 case chrome::NOTIFICATION_EXTENSION_UNLOADED: 423 HandleExtensionUnloaded(type, details); 424 break; 425 // Delivered when an app is enabled, reloaded or restarted. 426 case chrome::NOTIFICATION_EXTENSION_ENABLED: 427 HandleExtensionEnabled(type, details); 428 break; 429 default: 430 NOTREACHED() << "Unknown notification."; 431 break; 432 } 433} 434 435void SyncFileSystemService::HandleExtensionUnloaded( 436 int type, 437 const content::NotificationDetails& details) { 438 content::Details<const extensions::UnloadedExtensionInfo> info = 439 content::Details<const extensions::UnloadedExtensionInfo>(details); 440 std::string extension_id = info->extension->id(); 441 GURL app_origin = 442 extensions::Extension::GetBaseURLFromExtensionId(extension_id); 443 444 switch (info->reason) { 445 case extension_misc::UNLOAD_REASON_DISABLE: 446 DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): " 447 << app_origin; 448 remote_file_service_->DisableOriginForTrackingChanges( 449 app_origin, 450 base::Bind(&DidHandleOriginForExtensionUnloadedEvent, 451 type, info->reason, app_origin)); 452 local_file_service_->SetOriginEnabled(app_origin, false); 453 break; 454 case extension_misc::UNLOAD_REASON_UNINSTALL: 455 DVLOG(1) << "Handle extension notification for UNLOAD(UNINSTALL): " 456 << app_origin; 457 remote_file_service_->UninstallOrigin( 458 app_origin, 459 base::Bind(&DidHandleOriginForExtensionUnloadedEvent, 460 type, info->reason, app_origin)); 461 local_file_service_->SetOriginEnabled(app_origin, false); 462 break; 463 default: 464 // Nothing to do. 465 break; 466 } 467} 468 469void SyncFileSystemService::HandleExtensionEnabled( 470 int type, 471 const content::NotificationDetails& details) { 472 std::string extension_id = 473 content::Details<const extensions::Extension>(details)->id(); 474 GURL app_origin = 475 extensions::Extension::GetBaseURLFromExtensionId(extension_id); 476 DVLOG(1) << "Handle extension notification for ENABLED: " << app_origin; 477 remote_file_service_->EnableOriginForTrackingChanges( 478 app_origin, 479 base::Bind(&DidHandleOriginForExtensionEnabledEvent, type, app_origin)); 480 local_file_service_->SetOriginEnabled(app_origin, true); 481} 482 483void SyncFileSystemService::OnStateChanged() { 484 ProfileSyncServiceBase* profile_sync_service = 485 ProfileSyncServiceFactory::GetForProfile(profile_); 486 if (profile_sync_service) 487 UpdateSyncEnabledStatus(profile_sync_service); 488} 489 490void SyncFileSystemService::OnFileStatusChanged( 491 const FileSystemURL& url, 492 SyncFileStatus sync_status, 493 SyncAction action_taken, 494 SyncDirection direction) { 495 FOR_EACH_OBSERVER( 496 SyncEventObserver, observers_, 497 OnFileSynced(url, sync_status, action_taken, direction)); 498} 499 500void SyncFileSystemService::UpdateSyncEnabledStatus( 501 ProfileSyncServiceBase* profile_sync_service) { 502 if (!profile_sync_service->HasSyncSetupCompleted()) 503 return; 504 sync_enabled_ = profile_sync_service->GetPreferredDataTypes().Has( 505 syncer::APPS); 506 remote_file_service_->SetSyncEnabled(sync_enabled_); 507 if (sync_enabled_) { 508 base::MessageLoopProxy::current()->PostTask( 509 FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync, 510 AsWeakPtr())); 511 } 512} 513 514} // namespace sync_file_system 515