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