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