sync_file_system_service.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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/drive_backend_v1/drive_file_sync_service.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_status_code.h"
27#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
28#include "chrome/common/extensions/extension.h"
29#include "components/browser_context_keyed_service/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 "url/gurl.h"
35#include "webkit/browser/fileapi/file_system_context.h"
36
37using content::BrowserThread;
38using fileapi::FileSystemURL;
39using fileapi::FileSystemURLSet;
40
41namespace sync_file_system {
42
43namespace {
44
45const int64 kRetryTimerIntervalInSeconds = 20 * 60;  // 20 min.
46
47SyncServiceState RemoteStateToSyncServiceState(
48    RemoteServiceState state) {
49  switch (state) {
50    case REMOTE_SERVICE_OK:
51      return SYNC_SERVICE_RUNNING;
52    case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE:
53      return SYNC_SERVICE_TEMPORARY_UNAVAILABLE;
54    case REMOTE_SERVICE_AUTHENTICATION_REQUIRED:
55      return SYNC_SERVICE_AUTHENTICATION_REQUIRED;
56    case REMOTE_SERVICE_DISABLED:
57      return SYNC_SERVICE_DISABLED;
58  }
59  NOTREACHED() << "Unknown remote service state: " << state;
60  return SYNC_SERVICE_DISABLED;
61}
62
63void DidHandleOriginForExtensionUnloadedEvent(
64    int type,
65    const GURL& origin,
66    SyncStatusCode code) {
67  DCHECK(chrome::NOTIFICATION_EXTENSION_UNLOADED == type ||
68         chrome::NOTIFICATION_EXTENSION_UNINSTALLED == type);
69  if (code != SYNC_STATUS_OK &&
70      code != SYNC_STATUS_UNKNOWN_ORIGIN) {
71    switch (type) {
72      case chrome::NOTIFICATION_EXTENSION_UNLOADED:
73        util::Log(logging::LOG_WARNING,
74                  FROM_HERE,
75                  "Disabling origin for UNLOADED(DISABLE) failed: %s",
76                  origin.spec().c_str());
77        break;
78      case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
79        util::Log(logging::LOG_WARNING,
80                  FROM_HERE,
81                  "Uninstall origin for UNINSTALLED failed: %s",
82                  origin.spec().c_str());
83        break;
84      default:
85        break;
86    }
87  }
88}
89
90void DidHandleOriginForExtensionEnabledEvent(
91    int type,
92    const GURL& origin,
93    SyncStatusCode code) {
94  DCHECK(chrome::NOTIFICATION_EXTENSION_ENABLED == type);
95  if (code != SYNC_STATUS_OK)
96    util::Log(logging::LOG_WARNING,
97              FROM_HERE,
98              "Enabling origin for ENABLED failed: %s",
99              origin.spec().c_str());
100}
101
102std::string SyncFileStatusToString(SyncFileStatus sync_file_status) {
103  return extensions::api::sync_file_system::ToString(
104      extensions::SyncFileStatusToExtensionEnum(sync_file_status));
105}
106
107// Gets called repeatedly until every SyncFileStatus has been mapped.
108void DidGetFileSyncStatusForDump(
109    base::ListValue* files,
110    size_t* num_results,
111    const SyncFileSystemService::DumpFilesCallback& callback,
112    base::DictionaryValue* file,
113    SyncStatusCode sync_status_code,
114    SyncFileStatus sync_file_status) {
115  DCHECK(files);
116  DCHECK(num_results);
117
118  if (file)
119    file->SetString("status", SyncFileStatusToString(sync_file_status));
120
121  // Once all results have been received, run the callback to signal end.
122  DCHECK_LE(*num_results, files->GetSize());
123  if (++*num_results < files->GetSize())
124    return;
125
126  callback.Run(files);
127}
128
129}  // namespace
130
131void SyncFileSystemService::Shutdown() {
132  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133
134  local_file_service_->Shutdown();
135  local_file_service_.reset();
136
137  remote_file_service_.reset();
138
139  ProfileSyncServiceBase* profile_sync_service =
140      ProfileSyncServiceFactory::GetForProfile(profile_);
141  if (profile_sync_service)
142    profile_sync_service->RemoveObserver(this);
143
144  profile_ = NULL;
145}
146
147SyncFileSystemService::~SyncFileSystemService() {
148  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
149  DCHECK(!profile_);
150}
151
152void SyncFileSystemService::InitializeForApp(
153    fileapi::FileSystemContext* file_system_context,
154    const GURL& app_origin,
155    const SyncStatusCallback& callback) {
156  DCHECK(local_file_service_);
157  DCHECK(remote_file_service_);
158  DCHECK(app_origin == app_origin.GetOrigin());
159
160  util::Log(logging::LOG_VERBOSE, FROM_HERE,
161            "Initializing for App: %s", app_origin.spec().c_str());
162
163  local_file_service_->MaybeInitializeFileSystemContext(
164      app_origin, file_system_context,
165      base::Bind(&SyncFileSystemService::DidInitializeFileSystem,
166                 AsWeakPtr(), app_origin, callback));
167}
168
169SyncServiceState SyncFileSystemService::GetSyncServiceState() {
170  return RemoteStateToSyncServiceState(remote_file_service_->GetCurrentState());
171}
172
173void SyncFileSystemService::GetExtensionStatusMap(
174    std::map<GURL, std::string>* status_map) {
175  DCHECK(status_map);
176  remote_file_service_->GetOriginStatusMap(status_map);
177}
178
179void SyncFileSystemService::DumpFiles(const GURL& origin,
180                                      const DumpFilesCallback& callback) {
181  DCHECK(!origin.is_empty());
182
183  content::StoragePartition* storage_partition =
184      content::BrowserContext::GetStoragePartitionForSite(profile_, origin);
185  fileapi::FileSystemContext* file_system_context =
186      storage_partition->GetFileSystemContext();
187  local_file_service_->MaybeInitializeFileSystemContext(
188      origin, file_system_context,
189      base::Bind(&SyncFileSystemService::DidInitializeFileSystemForDump,
190                 AsWeakPtr(), origin, callback));
191}
192
193void SyncFileSystemService::GetFileSyncStatus(
194    const FileSystemURL& url, const SyncFileStatusCallback& callback) {
195  DCHECK(local_file_service_);
196  DCHECK(remote_file_service_);
197
198  // It's possible to get an invalid FileEntry.
199  if (!url.is_valid()) {
200    base::MessageLoopProxy::current()->PostTask(
201        FROM_HERE,
202        base::Bind(callback,
203                   SYNC_FILE_ERROR_INVALID_URL,
204                   SYNC_FILE_STATUS_UNKNOWN));
205    return;
206  }
207
208  if (remote_file_service_->IsConflicting(url)) {
209    base::MessageLoopProxy::current()->PostTask(
210        FROM_HERE,
211        base::Bind(callback,
212                   SYNC_STATUS_OK,
213                   SYNC_FILE_STATUS_CONFLICTING));
214    return;
215  }
216
217  local_file_service_->HasPendingLocalChanges(
218      url,
219      base::Bind(&SyncFileSystemService::DidGetLocalChangeStatus,
220                 AsWeakPtr(), callback));
221}
222
223void SyncFileSystemService::AddSyncEventObserver(SyncEventObserver* observer) {
224  observers_.AddObserver(observer);
225}
226
227void SyncFileSystemService::RemoveSyncEventObserver(
228    SyncEventObserver* observer) {
229  observers_.RemoveObserver(observer);
230}
231
232ConflictResolutionPolicy
233SyncFileSystemService::GetConflictResolutionPolicy() const {
234  return remote_file_service_->GetConflictResolutionPolicy();
235}
236
237SyncStatusCode SyncFileSystemService::SetConflictResolutionPolicy(
238    ConflictResolutionPolicy policy) {
239  return remote_file_service_->SetConflictResolutionPolicy(policy);
240}
241
242SyncFileSystemService::SyncFileSystemService(Profile* profile)
243    : profile_(profile),
244      pending_local_changes_(0),
245      pending_remote_changes_(0),
246      local_sync_running_(false),
247      remote_sync_running_(false),
248      is_waiting_remote_sync_enabled_(false),
249      sync_enabled_(true) {
250}
251
252void SyncFileSystemService::Initialize(
253    scoped_ptr<LocalFileSyncService> local_file_service,
254    scoped_ptr<RemoteFileSyncService> remote_file_service) {
255  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256  DCHECK(local_file_service);
257  DCHECK(remote_file_service);
258  DCHECK(profile_);
259
260  local_file_service_ = local_file_service.Pass();
261  remote_file_service_ = remote_file_service.Pass();
262
263  local_file_service_->AddChangeObserver(this);
264  local_file_service_->SetLocalChangeProcessor(
265      remote_file_service_->GetLocalChangeProcessor());
266
267  remote_file_service_->AddServiceObserver(this);
268  remote_file_service_->AddFileStatusObserver(this);
269  remote_file_service_->SetRemoteChangeProcessor(local_file_service_.get());
270
271  ProfileSyncServiceBase* profile_sync_service =
272      ProfileSyncServiceFactory::GetForProfile(profile_);
273  if (profile_sync_service) {
274    UpdateSyncEnabledStatus(profile_sync_service);
275    profile_sync_service->AddObserver(this);
276  }
277
278  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
279                 content::Source<Profile>(profile_));
280  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
281                 content::Source<Profile>(profile_));
282  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
283                 content::Source<Profile>(profile_));
284  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED,
285                 content::Source<Profile>(profile_));
286}
287
288void SyncFileSystemService::DidInitializeFileSystem(
289    const GURL& app_origin,
290    const SyncStatusCallback& callback,
291    SyncStatusCode status) {
292  DVLOG(1) << "DidInitializeFileSystem: "
293           << app_origin.spec() << " " << status;
294
295  if (status != SYNC_STATUS_OK) {
296    callback.Run(status);
297    return;
298  }
299
300  // Local side of initialization for the app is done.
301  // Continue on initializing the remote side.
302  remote_file_service_->RegisterOrigin(
303      app_origin,
304      base::Bind(&SyncFileSystemService::DidRegisterOrigin,
305                 AsWeakPtr(), app_origin, callback));
306}
307
308void SyncFileSystemService::DidRegisterOrigin(
309    const GURL& app_origin,
310    const SyncStatusCallback& callback,
311    SyncStatusCode status) {
312  util::Log(logging::LOG_VERBOSE, FROM_HERE,
313            "DidInitializeForApp (registered the origin): %s: %s",
314            app_origin.spec().c_str(),
315            SyncStatusCodeToString(status));
316
317  callback.Run(status);
318}
319
320void SyncFileSystemService::DidInitializeFileSystemForDump(
321    const GURL& origin,
322    const DumpFilesCallback& callback,
323    SyncStatusCode status) {
324  DCHECK(!origin.is_empty());
325
326  if (status != SYNC_STATUS_OK) {
327    base::ListValue empty_result;
328    callback.Run(&empty_result);
329    return;
330  }
331
332  base::ListValue* files = remote_file_service_->DumpFiles(origin).release();
333  if (!files->GetSize()) {
334    callback.Run(files);
335    return;
336  }
337
338  base::Callback<void(base::DictionaryValue* file,
339                      SyncStatusCode sync_status,
340                      SyncFileStatus sync_file_status)> completion_callback =
341      base::Bind(&DidGetFileSyncStatusForDump, base::Owned(files),
342                 base::Owned(new size_t(0)), callback);
343
344  // After all metadata loaded, sync status can be added to each entry.
345  for (size_t i = 0; i < files->GetSize(); ++i) {
346    base::DictionaryValue* file = NULL;
347    std::string path_string;
348    if (!files->GetDictionary(i, &file) ||
349        !file->GetString("path", &path_string)) {
350      NOTREACHED();
351      completion_callback.Run(
352          NULL, SYNC_FILE_ERROR_FAILED, SYNC_FILE_STATUS_UNKNOWN);
353      continue;
354    }
355
356    base::FilePath file_path = base::FilePath::FromUTF8Unsafe(path_string);
357    FileSystemURL url = CreateSyncableFileSystemURL(origin, file_path);
358    GetFileSyncStatus(url, base::Bind(completion_callback, file));
359  }
360}
361
362void SyncFileSystemService::SetSyncEnabledForTesting(bool enabled) {
363  sync_enabled_ = enabled;
364  remote_file_service_->SetSyncEnabled(sync_enabled_);
365}
366
367void SyncFileSystemService::MaybeStartSync() {
368  if (!profile_ || !sync_enabled_)
369    return;
370
371  if (pending_local_changes_ + pending_remote_changes_ == 0)
372    return;
373
374  DVLOG(2) << "MaybeStartSync() called (remote service state:"
375           << remote_file_service_->GetCurrentState() << ")";
376  switch (remote_file_service_->GetCurrentState()) {
377    case REMOTE_SERVICE_OK:
378      break;
379
380    case REMOTE_SERVICE_TEMPORARY_UNAVAILABLE:
381      if (sync_retry_timer_.IsRunning())
382        return;
383      sync_retry_timer_.Start(
384          FROM_HERE,
385          base::TimeDelta::FromSeconds(kRetryTimerIntervalInSeconds),
386          this, &SyncFileSystemService::MaybeStartSync);
387      break;
388
389    case REMOTE_SERVICE_AUTHENTICATION_REQUIRED:
390    case REMOTE_SERVICE_DISABLED:
391      // No point to run sync.
392      return;
393  }
394
395  StartRemoteSync();
396  StartLocalSync();
397}
398
399void SyncFileSystemService::StartRemoteSync() {
400  // See if we cannot / should not start a new remote sync.
401  if (remote_sync_running_ || pending_remote_changes_ == 0)
402    return;
403  // If we have registered a URL for waiting until sync is enabled on a
404  // file (and the registerred URL seems to be still valid) it won't be
405  // worth trying to start another remote sync.
406  if (is_waiting_remote_sync_enabled_)
407    return;
408  DCHECK(sync_enabled_);
409
410  util::Log(logging::LOG_VERBOSE, FROM_HERE,
411            "Calling ProcessRemoteChange for RemoteSync");
412  remote_sync_running_ = true;
413  remote_file_service_->ProcessRemoteChange(
414      base::Bind(&SyncFileSystemService::DidProcessRemoteChange,
415                 AsWeakPtr()));
416}
417
418void SyncFileSystemService::StartLocalSync() {
419  // See if we cannot / should not start a new local sync.
420  if (local_sync_running_ || pending_local_changes_ == 0)
421    return;
422  DCHECK(sync_enabled_);
423
424  util::Log(logging::LOG_VERBOSE, FROM_HERE,
425            "Calling ProcessLocalChange for LocalSync");
426  local_sync_running_ = true;
427  local_file_service_->ProcessLocalChange(
428      base::Bind(&SyncFileSystemService::DidProcessLocalChange,
429                 AsWeakPtr()));
430}
431
432void SyncFileSystemService::DidProcessRemoteChange(
433    SyncStatusCode status,
434    const FileSystemURL& url) {
435  util::Log(logging::LOG_VERBOSE, FROM_HERE,
436            "ProcessRemoteChange finished with status=%d (%s) for url=%s",
437            status, SyncStatusCodeToString(status), url.DebugString().c_str());
438  DCHECK(remote_sync_running_);
439  remote_sync_running_ = false;
440
441  if (status != SYNC_STATUS_NO_CHANGE_TO_SYNC &&
442      remote_file_service_->GetCurrentState() != REMOTE_SERVICE_DISABLED) {
443    DCHECK(url.is_valid());
444    local_file_service_->ClearSyncFlagForURL(url);
445  }
446
447  if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC) {
448    // We seem to have no changes to work on for now.
449    // TODO(kinuko): Might be better setting a timer to call MaybeStartSync.
450    return;
451  }
452  if (status == SYNC_STATUS_FILE_BUSY) {
453    is_waiting_remote_sync_enabled_ = true;
454    local_file_service_->RegisterURLForWaitingSync(
455        url, base::Bind(&SyncFileSystemService::OnSyncEnabledForRemoteSync,
456                        AsWeakPtr()));
457    return;
458  }
459
460  base::MessageLoopProxy::current()->PostTask(
461      FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
462                            AsWeakPtr()));
463}
464
465void SyncFileSystemService::DidProcessLocalChange(
466    SyncStatusCode status, const FileSystemURL& url) {
467  util::Log(logging::LOG_VERBOSE, FROM_HERE,
468            "ProcessLocalChange finished with status=%d (%s) for url=%s",
469            status, SyncStatusCodeToString(status), url.DebugString().c_str());
470  DCHECK(local_sync_running_);
471  local_sync_running_ = false;
472
473  if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC) {
474    // We seem to have no changes to work on for now.
475    return;
476  }
477
478  DCHECK(url.is_valid());
479  local_file_service_->ClearSyncFlagForURL(url);
480
481  base::MessageLoopProxy::current()->PostTask(
482      FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
483                            AsWeakPtr()));
484}
485
486void SyncFileSystemService::DidGetLocalChangeStatus(
487    const SyncFileStatusCallback& callback,
488    SyncStatusCode status,
489    bool has_pending_local_changes) {
490  callback.Run(
491      status,
492      has_pending_local_changes ?
493          SYNC_FILE_STATUS_HAS_PENDING_CHANGES : SYNC_FILE_STATUS_SYNCED);
494}
495
496void SyncFileSystemService::OnSyncEnabledForRemoteSync() {
497  is_waiting_remote_sync_enabled_ = false;
498  MaybeStartSync();
499}
500
501void SyncFileSystemService::OnLocalChangeAvailable(int64 pending_changes) {
502  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
503  DCHECK_GE(pending_changes, 0);
504  if (pending_local_changes_ != pending_changes) {
505    util::Log(logging::LOG_VERBOSE, FROM_HERE,
506              "OnLocalChangeAvailable: %" PRId64, pending_changes);
507  }
508  pending_local_changes_ = pending_changes;
509  if (pending_changes == 0)
510    return;
511
512  base::MessageLoopProxy::current()->PostTask(
513      FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
514                            AsWeakPtr()));
515}
516
517void SyncFileSystemService::OnRemoteChangeQueueUpdated(int64 pending_changes) {
518  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
519  DCHECK_GE(pending_changes, 0);
520
521  if (pending_remote_changes_ != pending_changes) {
522    util::Log(logging::LOG_VERBOSE, FROM_HERE,
523              "OnRemoteChangeAvailable: %" PRId64, pending_changes);
524  }
525  pending_remote_changes_ = pending_changes;
526  if (pending_changes == 0)
527    return;
528
529  // The smallest change available might have changed from the previous one.
530  // Reset the is_waiting_remote_sync_enabled_ flag so that we can retry.
531  is_waiting_remote_sync_enabled_ = false;
532
533  base::MessageLoopProxy::current()->PostTask(
534      FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
535                            AsWeakPtr()));
536}
537
538void SyncFileSystemService::OnRemoteServiceStateUpdated(
539    RemoteServiceState state,
540    const std::string& description) {
541  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
542  util::Log(logging::LOG_INFO, FROM_HERE,
543            "OnRemoteServiceStateChanged: %d %s", state, description.c_str());
544
545  if (state == REMOTE_SERVICE_OK) {
546    base::MessageLoopProxy::current()->PostTask(
547        FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
548                              AsWeakPtr()));
549  }
550
551  FOR_EACH_OBSERVER(
552      SyncEventObserver, observers_,
553      OnSyncStateUpdated(GURL(),
554                         RemoteStateToSyncServiceState(state),
555                         description));
556}
557
558void SyncFileSystemService::Observe(
559    int type,
560    const content::NotificationSource& source,
561    const content::NotificationDetails& details) {
562  // Event notification sequence.
563  //
564  // (User action)    (Notification type)
565  // Install:         INSTALLED.
566  // Update:          INSTALLED.
567  // Uninstall:       UNINSTALLED.
568  // Launch, Close:   No notification.
569  // Enable:          ENABLED.
570  // Disable:         UNLOADED(DISABLE).
571  // Reload, Restart: UNLOADED(DISABLE) -> INSTALLED -> ENABLED.
572  //
573  switch (type) {
574    case chrome::NOTIFICATION_EXTENSION_INSTALLED:
575      HandleExtensionInstalled(details);
576      break;
577    case chrome::NOTIFICATION_EXTENSION_UNLOADED:
578      HandleExtensionUnloaded(type, details);
579      break;
580    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
581      HandleExtensionUninstalled(type, details);
582      break;
583    case chrome::NOTIFICATION_EXTENSION_ENABLED:
584      HandleExtensionEnabled(type, details);
585      break;
586    default:
587      NOTREACHED() << "Unknown notification.";
588      break;
589  }
590}
591
592void SyncFileSystemService::HandleExtensionInstalled(
593    const content::NotificationDetails& details) {
594  const extensions::Extension* extension =
595      content::Details<const extensions::InstalledExtensionInfo>(details)->
596          extension;
597  GURL app_origin =
598      extensions::Extension::GetBaseURLFromExtensionId(extension->id());
599  DVLOG(1) << "Handle extension notification for INSTALLED: " << app_origin;
600  // NOTE: When an app is uninstalled and re-installed in a sequence,
601  // |local_file_service_| may still keeps |app_origin| as disabled origin.
602  local_file_service_->SetOriginEnabled(app_origin, true);
603}
604
605void SyncFileSystemService::HandleExtensionUnloaded(
606    int type,
607    const content::NotificationDetails& details) {
608  content::Details<const extensions::UnloadedExtensionInfo> info(details);
609  std::string extension_id = info->extension->id();
610  GURL app_origin =
611      extensions::Extension::GetBaseURLFromExtensionId(extension_id);
612  if (info->reason != extension_misc::UNLOAD_REASON_DISABLE)
613    return;
614  DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): "
615           << app_origin;
616  remote_file_service_->DisableOrigin(
617      app_origin,
618      base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
619                 type, app_origin));
620  local_file_service_->SetOriginEnabled(app_origin, false);
621}
622
623void SyncFileSystemService::HandleExtensionUninstalled(
624    int type,
625    const content::NotificationDetails& details) {
626  std::string extension_id =
627      content::Details<const extensions::Extension>(details)->id();
628  GURL app_origin =
629      extensions::Extension::GetBaseURLFromExtensionId(extension_id);
630  DVLOG(1) << "Handle extension notification for UNINSTALLED: "
631           << app_origin;
632  remote_file_service_->UninstallOrigin(
633      app_origin,
634      base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
635                 type, app_origin));
636  local_file_service_->SetOriginEnabled(app_origin, false);
637}
638
639void SyncFileSystemService::HandleExtensionEnabled(
640    int type,
641    const content::NotificationDetails& details) {
642  std::string extension_id =
643      content::Details<const extensions::Extension>(details)->id();
644  GURL app_origin =
645      extensions::Extension::GetBaseURLFromExtensionId(extension_id);
646  DVLOG(1) << "Handle extension notification for ENABLED: " << app_origin;
647  remote_file_service_->EnableOrigin(
648      app_origin,
649      base::Bind(&DidHandleOriginForExtensionEnabledEvent, type, app_origin));
650  local_file_service_->SetOriginEnabled(app_origin, true);
651}
652
653void SyncFileSystemService::OnStateChanged() {
654  ProfileSyncServiceBase* profile_sync_service =
655      ProfileSyncServiceFactory::GetForProfile(profile_);
656  if (profile_sync_service)
657    UpdateSyncEnabledStatus(profile_sync_service);
658}
659
660void SyncFileSystemService::OnFileStatusChanged(
661    const FileSystemURL& url,
662    SyncFileStatus sync_status,
663    SyncAction action_taken,
664    SyncDirection direction) {
665  FOR_EACH_OBSERVER(
666      SyncEventObserver, observers_,
667      OnFileSynced(url, sync_status, action_taken, direction));
668}
669
670void SyncFileSystemService::UpdateSyncEnabledStatus(
671    ProfileSyncServiceBase* profile_sync_service) {
672  if (!profile_sync_service->HasSyncSetupCompleted())
673    return;
674  sync_enabled_ = profile_sync_service->GetActiveDataTypes().Has(
675      syncer::APPS);
676  remote_file_service_->SetSyncEnabled(sync_enabled_);
677  if (sync_enabled_) {
678    base::MessageLoopProxy::current()->PostTask(
679        FROM_HERE, base::Bind(&SyncFileSystemService::MaybeStartSync,
680                              AsWeakPtr()));
681  }
682}
683
684}  // namespace sync_file_system
685