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