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