sync_file_system_service.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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 (url.is_valid())
548    local_file_service_->ClearSyncFlagForURL(url);
549
550  if (status == SYNC_STATUS_FILE_BUSY) {
551    local_file_service_->RegisterURLForWaitingSync(
552        url, base::Bind(&SyncFileSystemService::OnSyncEnabledForRemoteSync,
553                        AsWeakPtr()));
554  }
555
556  callback.Run(status);
557}
558
559void SyncFileSystemService::DidProcessLocalChange(
560    const SyncStatusCallback& callback,
561    SyncStatusCode status,
562    const FileSystemURL& url) {
563  util::Log(logging::LOG_VERBOSE, FROM_HERE,
564            "ProcessLocalChange finished with status=%d (%s) for url=%s",
565            status, SyncStatusCodeToString(status), url.DebugString().c_str());
566
567  if (url.is_valid())
568    local_file_service_->ClearSyncFlagForURL(url);
569
570  callback.Run(status);
571}
572
573void SyncFileSystemService::DidGetLocalChangeStatus(
574    const SyncFileStatusCallback& callback,
575    SyncStatusCode status,
576    bool has_pending_local_changes) {
577  callback.Run(
578      status,
579      has_pending_local_changes ?
580          SYNC_FILE_STATUS_HAS_PENDING_CHANGES : SYNC_FILE_STATUS_SYNCED);
581}
582
583void SyncFileSystemService::OnSyncEnabledForRemoteSync() {
584  remote_sync_->Schedule();
585}
586
587void SyncFileSystemService::OnLocalChangeAvailable(int64 pending_changes) {
588  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
589
590  local_sync_->OnChangesUpdated(pending_changes);
591  remote_sync_->ScheduleIfNotRunning();
592}
593
594void SyncFileSystemService::OnRemoteChangeQueueUpdated(int64 pending_changes) {
595  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
596
597  remote_sync_->OnChangesUpdated(pending_changes);
598  local_sync_->ScheduleIfNotRunning();
599}
600
601void SyncFileSystemService::OnRemoteServiceStateUpdated(
602    RemoteServiceState state,
603    const std::string& description) {
604  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
605  util::Log(logging::LOG_INFO, FROM_HERE,
606            "OnRemoteServiceStateChanged: %d %s", state, description.c_str());
607
608  remote_sync_->Schedule();
609  local_sync_->Schedule();
610
611  FOR_EACH_OBSERVER(
612      SyncEventObserver, observers_,
613      OnSyncStateUpdated(GURL(),
614                         RemoteStateToSyncServiceState(state),
615                         description));
616}
617
618void SyncFileSystemService::Observe(
619    int type,
620    const content::NotificationSource& source,
621    const content::NotificationDetails& details) {
622  // Event notification sequence.
623  //
624  // (User action)    (Notification type)
625  // Install:         INSTALLED.
626  // Update:          INSTALLED.
627  // Uninstall:       UNINSTALLED.
628  // Launch, Close:   No notification.
629  // Enable:          ENABLED.
630  // Disable:         UNLOADED(DISABLE).
631  // Reload, Restart: UNLOADED(DISABLE) -> INSTALLED -> ENABLED.
632  //
633  switch (type) {
634    case chrome::NOTIFICATION_EXTENSION_INSTALLED:
635      HandleExtensionInstalled(details);
636      break;
637    case chrome::NOTIFICATION_EXTENSION_UNLOADED:
638      HandleExtensionUnloaded(type, details);
639      break;
640    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
641      HandleExtensionUninstalled(type, details);
642      break;
643    case chrome::NOTIFICATION_EXTENSION_ENABLED:
644      HandleExtensionEnabled(type, details);
645      break;
646    default:
647      NOTREACHED() << "Unknown notification.";
648      break;
649  }
650}
651
652void SyncFileSystemService::HandleExtensionInstalled(
653    const content::NotificationDetails& details) {
654  const Extension* extension =
655      content::Details<const extensions::InstalledExtensionInfo>(details)->
656          extension;
657  GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id());
658  DVLOG(1) << "Handle extension notification for INSTALLED: " << app_origin;
659  // NOTE: When an app is uninstalled and re-installed in a sequence,
660  // |local_file_service_| may still keeps |app_origin| as disabled origin.
661  local_file_service_->SetOriginEnabled(app_origin, true);
662}
663
664void SyncFileSystemService::HandleExtensionUnloaded(
665    int type,
666    const content::NotificationDetails& details) {
667  content::Details<const extensions::UnloadedExtensionInfo> info(details);
668  if (info->reason != extension_misc::UNLOAD_REASON_DISABLE)
669    return;
670
671  std::string extension_id = info->extension->id();
672  GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id);
673
674  int reasons = ExtensionPrefs::Get(profile_)->GetDisableReasons(extension_id);
675  if (reasons & Extension::DISABLE_RELOAD) {
676    // Bypass disabling the origin since the app will be re-enabled soon.
677    // NOTE: If re-enabling the app fails, the app is disabled while it is
678    // handled as enabled origin in the SyncFS. This should be safe and will be
679    // recovered when the user re-enables the app manually or the sync service
680    // restarts.
681    DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE_RELOAD): "
682             << app_origin;
683    return;
684  }
685
686  DVLOG(1) << "Handle extension notification for UNLOAD(DISABLE): "
687           << app_origin;
688  remote_file_service_->DisableOrigin(
689      app_origin,
690      base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
691                 type, app_origin));
692  local_file_service_->SetOriginEnabled(app_origin, false);
693}
694
695void SyncFileSystemService::HandleExtensionUninstalled(
696    int type,
697    const content::NotificationDetails& details) {
698  const Extension* extension = content::Details<const Extension>(details).ptr();
699  DCHECK(extension);
700
701  RemoteFileSyncService::UninstallFlag flag =
702      RemoteFileSyncService::UNINSTALL_AND_PURGE_REMOTE;
703  // If it's loaded from an unpacked package and with key: field,
704  // the uninstall will not be sync'ed and the user might be using the
705  // same app key in other installs, so avoid purging the remote folder.
706  if (extensions::Manifest::IsUnpackedLocation(extension->location()) &&
707      extension->manifest()->HasKey(extensions::manifest_keys::kKey)) {
708    flag = RemoteFileSyncService::UNINSTALL_AND_KEEP_REMOTE;
709  }
710
711  GURL app_origin = Extension::GetBaseURLFromExtensionId(extension->id());
712  DVLOG(1) << "Handle extension notification for UNINSTALLED: "
713           << app_origin;
714  remote_file_service_->UninstallOrigin(
715      app_origin, flag,
716      base::Bind(&DidHandleOriginForExtensionUnloadedEvent,
717                 type, app_origin));
718  local_file_service_->SetOriginEnabled(app_origin, false);
719}
720
721void SyncFileSystemService::HandleExtensionEnabled(
722    int type,
723    const content::NotificationDetails& details) {
724  std::string extension_id = content::Details<const Extension>(details)->id();
725  GURL app_origin = Extension::GetBaseURLFromExtensionId(extension_id);
726  DVLOG(1) << "Handle extension notification for ENABLED: " << app_origin;
727  remote_file_service_->EnableOrigin(
728      app_origin,
729      base::Bind(&DidHandleOriginForExtensionEnabledEvent, type, app_origin));
730  local_file_service_->SetOriginEnabled(app_origin, true);
731}
732
733void SyncFileSystemService::OnStateChanged() {
734  ProfileSyncServiceBase* profile_sync_service =
735      ProfileSyncServiceFactory::GetForProfile(profile_);
736  if (profile_sync_service)
737    UpdateSyncEnabledStatus(profile_sync_service);
738}
739
740void SyncFileSystemService::OnFileStatusChanged(
741    const FileSystemURL& url,
742    SyncFileStatus sync_status,
743    SyncAction action_taken,
744    SyncDirection direction) {
745  FOR_EACH_OBSERVER(
746      SyncEventObserver, observers_,
747      OnFileSynced(url, sync_status, action_taken, direction));
748}
749
750void SyncFileSystemService::UpdateSyncEnabledStatus(
751    ProfileSyncServiceBase* profile_sync_service) {
752  if (!profile_sync_service->HasSyncSetupCompleted())
753    return;
754  bool old_sync_enabled = sync_enabled_;
755  sync_enabled_ = profile_sync_service->GetActiveDataTypes().Has(
756      syncer::APPS);
757  remote_file_service_->SetSyncEnabled(sync_enabled_);
758  if (!old_sync_enabled && sync_enabled_) {
759    local_sync_->Schedule();
760    remote_sync_->Schedule();
761  }
762}
763
764}  // namespace sync_file_system
765