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