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