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