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