Revert of Don't install OEM default apps for enterprise users (patchset #1 id:1 of...
[chromium-blink-merge.git] / chrome / browser / extensions / extension_sync_service.cc
blob12769c6781731532e5631fc6b9be6b6f1470fb43
1 // Copyright 2013 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.
5 #include "chrome/browser/extensions/extension_sync_service.h"
7 #include <iterator>
9 #include "base/basictypes.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/threading/sequenced_worker_pool.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "chrome/browser/extensions/app_sync_data.h"
14 #include "chrome/browser/extensions/bookmark_app_helper.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_sync_data.h"
17 #include "chrome/browser/extensions/extension_sync_service_factory.h"
18 #include "chrome/browser/extensions/extension_util.h"
19 #include "chrome/browser/extensions/launch_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sync/glue/sync_start_util.h"
22 #include "chrome/common/extensions/extension_constants.h"
23 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
24 #include "chrome/common/extensions/sync_helper.h"
25 #include "chrome/common/web_application_info.h"
26 #include "components/sync_driver/sync_prefs.h"
27 #include "extensions/browser/app_sorting.h"
28 #include "extensions/browser/extension_prefs.h"
29 #include "extensions/browser/extension_registry.h"
30 #include "extensions/browser/extension_util.h"
31 #include "extensions/browser/uninstall_reason.h"
32 #include "extensions/common/extension.h"
33 #include "extensions/common/extension_icon_set.h"
34 #include "extensions/common/feature_switch.h"
35 #include "extensions/common/image_util.h"
36 #include "extensions/common/manifest_constants.h"
37 #include "extensions/common/manifest_handlers/icons_handler.h"
38 #include "sync/api/sync_change.h"
39 #include "sync/api/sync_error_factory.h"
40 #include "ui/gfx/image/image_family.h"
42 using extensions::AppSyncData;
43 using extensions::Extension;
44 using extensions::ExtensionPrefs;
45 using extensions::ExtensionRegistry;
46 using extensions::ExtensionSyncData;
47 using extensions::FeatureSwitch;
49 namespace {
51 void OnWebApplicationInfoLoaded(
52 WebApplicationInfo synced_info,
53 base::WeakPtr<ExtensionService> extension_service,
54 const WebApplicationInfo& loaded_info) {
55 DCHECK_EQ(synced_info.app_url, loaded_info.app_url);
57 if (!extension_service)
58 return;
60 // Use the old icons if they exist.
61 synced_info.icons = loaded_info.icons;
62 CreateOrUpdateBookmarkApp(extension_service.get(), &synced_info);
65 // Returns the pref value for "all urls enabled" for the given extension id.
66 ExtensionSyncData::OptionalBoolean GetAllowedOnAllUrlsOptionalBoolean(
67 const std::string& extension_id,
68 content::BrowserContext* context) {
69 bool allowed_on_all_urls =
70 extensions::util::AllowedScriptingOnAllUrls(extension_id, context);
71 // If the extension is not allowed on all urls (which is not the default),
72 // then we have to sync the preference.
73 if (!allowed_on_all_urls)
74 return ExtensionSyncData::BOOLEAN_FALSE;
76 // If the user has explicitly set a value, then we sync it.
77 if (extensions::util::HasSetAllowedScriptingOnAllUrls(extension_id, context))
78 return ExtensionSyncData::BOOLEAN_TRUE;
80 // Otherwise, unset.
81 return ExtensionSyncData::BOOLEAN_UNSET;
84 } // namespace
86 ExtensionSyncService::ExtensionSyncService(Profile* profile,
87 ExtensionPrefs* extension_prefs,
88 ExtensionService* extension_service)
89 : profile_(profile),
90 extension_prefs_(extension_prefs),
91 extension_service_(extension_service),
92 app_sync_bundle_(this),
93 extension_sync_bundle_(this),
94 pending_app_enables_(make_scoped_ptr(new sync_driver::SyncPrefs(
95 extension_prefs_->pref_service())),
96 &app_sync_bundle_,
97 syncer::APPS),
98 pending_extension_enables_(make_scoped_ptr(new sync_driver::SyncPrefs(
99 extension_prefs_->pref_service())),
100 &extension_sync_bundle_,
101 syncer::EXTENSIONS) {
102 SetSyncStartFlare(sync_start_util::GetFlareForSyncableService(
103 profile_->GetPath()));
105 extension_service_->set_extension_sync_service(this);
106 extension_prefs_->app_sorting()->SetExtensionSyncService(this);
109 ExtensionSyncService::~ExtensionSyncService() {}
111 // static
112 ExtensionSyncService* ExtensionSyncService::Get(
113 content::BrowserContext* context) {
114 return ExtensionSyncServiceFactory::GetForBrowserContext(context);
117 syncer::SyncChange ExtensionSyncService::PrepareToSyncUninstallExtension(
118 const extensions::Extension* extension, bool extensions_ready) {
119 // Extract the data we need for sync now, but don't actually sync until we've
120 // completed the uninstallation.
121 // TODO(tim): If we get here and IsSyncing is false, this will cause
122 // "back from the dead" style bugs, because sync will add-back the extension
123 // that was uninstalled here when MergeDataAndStartSyncing is called.
124 // See crbug.com/256795.
125 if (extensions::util::ShouldSyncApp(extension, profile_)) {
126 if (app_sync_bundle_.IsSyncing())
127 return app_sync_bundle_.CreateSyncChangeToDelete(extension);
128 else if (extensions_ready && !flare_.is_null())
129 flare_.Run(syncer::APPS); // Tell sync to start ASAP.
130 } else if (extensions::sync_helper::IsSyncableExtension(extension)) {
131 if (extension_sync_bundle_.IsSyncing())
132 return extension_sync_bundle_.CreateSyncChangeToDelete(extension);
133 else if (extensions_ready && !flare_.is_null())
134 flare_.Run(syncer::EXTENSIONS); // Tell sync to start ASAP.
137 return syncer::SyncChange();
140 void ExtensionSyncService::ProcessSyncUninstallExtension(
141 const std::string& extension_id,
142 const syncer::SyncChange& sync_change) {
143 if (app_sync_bundle_.HasExtensionId(extension_id) &&
144 sync_change.sync_data().GetDataType() == syncer::APPS) {
145 app_sync_bundle_.ProcessDeletion(extension_id, sync_change);
146 } else if (extension_sync_bundle_.HasExtensionId(extension_id) &&
147 sync_change.sync_data().GetDataType() == syncer::EXTENSIONS) {
148 extension_sync_bundle_.ProcessDeletion(extension_id, sync_change);
152 void ExtensionSyncService::SyncEnableExtension(
153 const extensions::Extension& extension) {
155 // Syncing may not have started yet, so handle pending enables.
156 if (extensions::util::ShouldSyncApp(&extension, profile_))
157 pending_app_enables_.OnExtensionEnabled(extension.id());
159 if (extensions::util::ShouldSyncExtension(&extension, profile_))
160 pending_extension_enables_.OnExtensionEnabled(extension.id());
162 SyncExtensionChangeIfNeeded(extension);
165 void ExtensionSyncService::SyncDisableExtension(
166 const extensions::Extension& extension) {
168 // Syncing may not have started yet, so handle pending enables.
169 if (extensions::util::ShouldSyncApp(&extension, profile_))
170 pending_app_enables_.OnExtensionDisabled(extension.id());
172 if (extensions::util::ShouldSyncExtension(&extension, profile_))
173 pending_extension_enables_.OnExtensionDisabled(extension.id());
175 SyncExtensionChangeIfNeeded(extension);
178 syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
179 syncer::ModelType type,
180 const syncer::SyncDataList& initial_sync_data,
181 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
182 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
183 CHECK(sync_processor.get());
184 CHECK(sync_error_factory.get());
186 switch (type) {
187 case syncer::EXTENSIONS:
188 extension_sync_bundle_.SetupSync(sync_processor.release(),
189 sync_error_factory.release(),
190 initial_sync_data);
191 pending_extension_enables_.OnSyncStarted(extension_service_);
192 break;
194 case syncer::APPS:
195 app_sync_bundle_.SetupSync(sync_processor.release(),
196 sync_error_factory.release(),
197 initial_sync_data);
198 pending_app_enables_.OnSyncStarted(extension_service_);
199 break;
201 default:
202 LOG(FATAL) << "Got " << type << " ModelType";
205 // Process local extensions.
206 // TODO(yoz): Determine whether pending extensions should be considered too.
207 // See crbug.com/104399.
208 syncer::SyncDataList sync_data_list = GetAllSyncData(type);
209 syncer::SyncChangeList sync_change_list;
210 for (syncer::SyncDataList::const_iterator i = sync_data_list.begin();
211 i != sync_data_list.end();
212 ++i) {
213 switch (type) {
214 case syncer::EXTENSIONS:
215 sync_change_list.push_back(
216 extension_sync_bundle_.CreateSyncChange(*i));
217 break;
218 case syncer::APPS:
219 sync_change_list.push_back(app_sync_bundle_.CreateSyncChange(*i));
220 break;
221 default:
222 LOG(FATAL) << "Got " << type << " ModelType";
227 if (type == syncer::EXTENSIONS) {
228 extension_sync_bundle_.ProcessSyncChangeList(sync_change_list);
229 } else if (type == syncer::APPS) {
230 app_sync_bundle_.ProcessSyncChangeList(sync_change_list);
233 return syncer::SyncMergeResult(type);
236 void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
237 if (type == syncer::APPS) {
238 app_sync_bundle_.Reset();
239 } else if (type == syncer::EXTENSIONS) {
240 extension_sync_bundle_.Reset();
244 syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
245 syncer::ModelType type) const {
246 if (type == syncer::EXTENSIONS)
247 return extension_sync_bundle_.GetAllSyncData();
248 if (type == syncer::APPS)
249 return app_sync_bundle_.GetAllSyncData();
251 // We should only get sync data for extensions and apps.
252 NOTREACHED();
254 return syncer::SyncDataList();
257 syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
258 const tracked_objects::Location& from_here,
259 const syncer::SyncChangeList& change_list) {
260 for (syncer::SyncChangeList::const_iterator i = change_list.begin();
261 i != change_list.end();
262 ++i) {
263 syncer::ModelType type = i->sync_data().GetDataType();
264 if (type == syncer::EXTENSIONS) {
265 scoped_ptr<ExtensionSyncData> extension_data(
266 ExtensionSyncData::CreateFromSyncChange(*i));
267 if (extension_data.get())
268 extension_sync_bundle_.ProcessSyncChange(*extension_data);
269 } else if (type == syncer::APPS) {
270 scoped_ptr<AppSyncData> app_data(AppSyncData::CreateFromSyncChange(*i));
271 if (app_data.get())
272 app_sync_bundle_.ProcessSyncChange(*app_data);
276 extension_prefs_->app_sorting()->FixNTPOrdinalCollisions();
278 return syncer::SyncError();
281 ExtensionSyncData ExtensionSyncService::GetExtensionSyncData(
282 const Extension& extension) const {
283 return ExtensionSyncData(
284 extension,
285 extension_service_->IsExtensionEnabled(extension.id()),
286 extension_prefs_->GetDisableReasons(extension.id()),
287 extensions::util::IsIncognitoEnabled(extension.id(), profile_),
288 extension_prefs_->HasDisableReason(extension.id(),
289 Extension::DISABLE_REMOTE_INSTALL),
290 GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_));
293 AppSyncData ExtensionSyncService::GetAppSyncData(
294 const Extension& extension) const {
295 return AppSyncData(
296 extension, extension_service_->IsExtensionEnabled(extension.id()),
297 extension_prefs_->GetDisableReasons(extension.id()),
298 extensions::util::IsIncognitoEnabled(extension.id(), profile_),
299 extension_prefs_->HasDisableReason(extension.id(),
300 Extension::DISABLE_REMOTE_INSTALL),
301 GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_),
302 extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
303 extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()),
304 extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id()));
307 std::vector<ExtensionSyncData>
308 ExtensionSyncService::GetExtensionSyncDataList() const {
309 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
310 std::vector<ExtensionSyncData> extension_sync_list;
311 extension_sync_bundle_.GetExtensionSyncDataListHelper(
312 registry->enabled_extensions(), &extension_sync_list);
313 extension_sync_bundle_.GetExtensionSyncDataListHelper(
314 registry->disabled_extensions(), &extension_sync_list);
315 extension_sync_bundle_.GetExtensionSyncDataListHelper(
316 registry->terminated_extensions(), &extension_sync_list);
318 std::vector<ExtensionSyncData> pending_extensions =
319 extension_sync_bundle_.GetPendingData();
320 extension_sync_list.insert(extension_sync_list.begin(),
321 pending_extensions.begin(),
322 pending_extensions.end());
324 return extension_sync_list;
327 std::vector<AppSyncData> ExtensionSyncService::GetAppSyncDataList() const {
328 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
329 std::vector<AppSyncData> app_sync_list;
330 app_sync_bundle_.GetAppSyncDataListHelper(
331 registry->enabled_extensions(), &app_sync_list);
332 app_sync_bundle_.GetAppSyncDataListHelper(
333 registry->disabled_extensions(), &app_sync_list);
334 app_sync_bundle_.GetAppSyncDataListHelper(
335 registry->terminated_extensions(), &app_sync_list);
337 std::vector<AppSyncData> pending_apps = app_sync_bundle_.GetPendingData();
338 app_sync_list.insert(app_sync_list.begin(),
339 pending_apps.begin(),
340 pending_apps.end());
342 return app_sync_list;
345 bool ExtensionSyncService::ProcessExtensionSyncData(
346 const ExtensionSyncData& extension_sync_data) {
347 if (!ProcessExtensionSyncDataHelper(extension_sync_data,
348 syncer::EXTENSIONS)) {
349 extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(),
350 extension_sync_data);
351 extension_service_->CheckForUpdatesSoon();
352 return false;
355 return true;
358 bool ExtensionSyncService::ProcessAppSyncData(
359 const AppSyncData& app_sync_data) {
360 const std::string& id = app_sync_data.id();
362 if (app_sync_data.app_launch_ordinal().IsValid() &&
363 app_sync_data.page_ordinal().IsValid()) {
364 extension_prefs_->app_sorting()->SetAppLaunchOrdinal(
366 app_sync_data.app_launch_ordinal());
367 extension_prefs_->app_sorting()->SetPageOrdinal(
369 app_sync_data.page_ordinal());
372 // The corresponding validation of this value during AppSyncData population
373 // is in AppSyncData::PopulateAppSpecifics.
374 if (app_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
375 app_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
376 extensions::SetLaunchType(profile_, id, app_sync_data.launch_type());
379 if (!app_sync_data.bookmark_app_url().empty())
380 ProcessBookmarkAppSyncData(app_sync_data);
382 if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(),
383 syncer::APPS)) {
384 app_sync_bundle_.AddPendingApp(id, app_sync_data);
385 extension_service_->CheckForUpdatesSoon();
386 return false;
389 return true;
392 void ExtensionSyncService::ProcessBookmarkAppSyncData(
393 const AppSyncData& app_sync_data) {
394 // Process bookmark app sync if necessary.
395 GURL bookmark_app_url(app_sync_data.bookmark_app_url());
396 if (!bookmark_app_url.is_valid() ||
397 app_sync_data.extension_sync_data().uninstalled()) {
398 return;
401 const extensions::Extension* extension =
402 extension_service_->GetInstalledExtension(
403 app_sync_data.extension_sync_data().id());
405 // Return if there are no bookmark app details that need updating.
406 if (extension && extension->non_localized_name() ==
407 app_sync_data.extension_sync_data().name() &&
408 extension->description() == app_sync_data.bookmark_app_description()) {
409 return;
412 WebApplicationInfo web_app_info;
413 web_app_info.app_url = bookmark_app_url;
414 web_app_info.title =
415 base::UTF8ToUTF16(app_sync_data.extension_sync_data().name());
416 web_app_info.description =
417 base::UTF8ToUTF16(app_sync_data.bookmark_app_description());
418 if (!app_sync_data.bookmark_app_icon_color().empty()) {
419 extensions::image_util::ParseCSSColorString(
420 app_sync_data.bookmark_app_icon_color(),
421 &web_app_info.generated_icon_color);
423 for (const auto& icon : app_sync_data.linked_icons()) {
424 WebApplicationInfo::IconInfo icon_info;
425 icon_info.url = icon.url;
426 icon_info.width = icon.size;
427 icon_info.height = icon.size;
428 web_app_info.icons.push_back(icon_info);
431 // If the bookmark app already exists, keep the old icons.
432 if (!extension) {
433 CreateOrUpdateBookmarkApp(extension_service_, &web_app_info);
434 } else {
435 app_sync_data.extension_sync_data().name();
436 GetWebApplicationInfoFromApp(profile_,
437 extension,
438 base::Bind(&OnWebApplicationInfoLoaded,
439 web_app_info,
440 extension_service_->AsWeakPtr()));
444 void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
445 const extensions::Extension* ext = extension_service_->GetInstalledExtension(
446 extension_id);
448 if (ext)
449 SyncExtensionChangeIfNeeded(*ext);
452 void ExtensionSyncService::SetSyncStartFlare(
453 const syncer::SyncableService::StartSyncFlare& flare) {
454 flare_ = flare;
457 bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension,
458 syncer::ModelType type) const {
459 if (type == syncer::EXTENSIONS &&
460 extensions::sync_helper::IsSyncableExtension(&extension)) {
461 return true;
464 if (type == syncer::APPS &&
465 extensions::sync_helper::IsSyncableApp(&extension)) {
466 return true;
469 return false;
472 bool ExtensionSyncService::IsPendingEnable(
473 const std::string& extension_id) const {
474 return pending_app_enables_.Contains(extension_id) ||
475 pending_extension_enables_.Contains(extension_id);
478 bool ExtensionSyncService::ProcessExtensionSyncDataHelper(
479 const ExtensionSyncData& extension_sync_data,
480 syncer::ModelType type) {
481 const std::string& id = extension_sync_data.id();
482 const Extension* extension = extension_service_->GetInstalledExtension(id);
484 // TODO(bolms): we should really handle this better. The particularly bad
485 // case is where an app becomes an extension or vice versa, and we end up with
486 // a zombie extension that won't go away.
487 if (extension && !IsCorrectSyncType(*extension, type))
488 return true;
490 // Handle uninstalls first.
491 if (extension_sync_data.uninstalled()) {
492 if (!extension_service_->UninstallExtensionHelper(
493 extension_service_, id, extensions::UNINSTALL_REASON_SYNC)) {
494 LOG(WARNING) << "Could not uninstall extension " << id
495 << " for sync";
497 return true;
500 // Extension from sync was uninstalled by the user as external extensions.
501 // Honor user choice and skip installation/enabling.
502 if (extensions::ExtensionPrefs::Get(profile_)
503 ->IsExternalExtensionUninstalled(id)) {
504 LOG(WARNING) << "Extension with id " << id
505 << " from sync was uninstalled as external extension";
506 return true;
509 // Set user settings.
510 // If the extension has been disabled from sync, it may not have
511 // been installed yet, so we don't know if the disable reason was a
512 // permissions increase. That will be updated once CheckPermissionsIncrease
513 // is called for it.
514 // However if the extension is marked as a remote install in sync, we know
515 // what the disable reason is, so set it to that directly. Note that when
516 // CheckPermissionsIncrease runs, it might still add permissions increase
517 // as a disable reason for the extension.
518 if (extension_sync_data.enabled()) {
519 extension_service_->EnableExtension(id);
520 } else if (!IsPendingEnable(id)) {
521 int disable_reasons = extension_sync_data.disable_reasons();
522 if (extension_sync_data.remote_install()) {
523 // In the non-legacy case (>=M45) where disable reasons are synced at all,
524 // DISABLE_REMOTE_INSTALL should be among them already.
525 DCHECK(!disable_reasons ||
526 (disable_reasons & Extension::DISABLE_REMOTE_INSTALL));
527 disable_reasons |= Extension::DISABLE_REMOTE_INSTALL;
529 if (!disable_reasons) {
530 // Legacy case (<M45), from before we synced disable reasons (see
531 // crbug.com/484214).
532 disable_reasons = Extension::DISABLE_UNKNOWN_FROM_SYNC;
535 extension_service_->DisableExtension(
536 id, Extension::DisableReason(disable_reasons));
539 // We need to cache some version information here because setting the
540 // incognito flag invalidates the |extension| pointer (it reloads the
541 // extension).
542 bool extension_installed = (extension != NULL);
543 int version_compare_result = extension ?
544 extension->version()->CompareTo(extension_sync_data.version()) : 0;
546 // If the target extension has already been installed ephemerally, it can
547 // be promoted to a regular installed extension and downloading from the Web
548 // Store is not necessary.
549 if (extension && extensions::util::IsEphemeralApp(id, profile_))
550 extension_service_->PromoteEphemeralApp(extension, true);
552 // Update the incognito flag.
553 extensions::util::SetIsIncognitoEnabled(
554 id, profile_, extension_sync_data.incognito_enabled());
555 extension = NULL; // No longer safe to use.
557 // Update the all urls flag.
558 if (extension_sync_data.all_urls_enabled() !=
559 ExtensionSyncData::BOOLEAN_UNSET) {
560 bool allowed = extension_sync_data.all_urls_enabled() ==
561 ExtensionSyncData::BOOLEAN_TRUE;
562 extensions::util::SetAllowedScriptingOnAllUrls(id, profile_, allowed);
565 if (extension_installed) {
566 // If the extension is already installed, check if it's outdated.
567 if (version_compare_result < 0) {
568 // Extension is outdated.
569 return false;
571 } else {
572 CHECK(type == syncer::EXTENSIONS || type == syncer::APPS);
573 extensions::PendingExtensionInfo::ShouldAllowInstallPredicate filter =
574 (type == syncer::APPS) ? extensions::sync_helper::IsSyncableApp :
575 extensions::sync_helper::IsSyncableExtension;
577 if (!extension_service_->pending_extension_manager()->AddFromSync(
579 extension_sync_data.update_url(),
580 filter,
581 extension_sync_data.remote_install(),
582 extension_sync_data.installed_by_custodian())) {
583 LOG(WARNING) << "Could not add pending extension for " << id;
584 // This means that the extension is already pending installation, with a
585 // non-INTERNAL location. Add to pending_sync_data, even though it will
586 // never be removed (we'll never install a syncable version of the
587 // extension), so that GetAllSyncData() continues to send it.
589 // Track pending extensions so that we can return them in GetAllSyncData().
590 return false;
593 return true;
596 void ExtensionSyncService::SyncExtensionChangeIfNeeded(
597 const Extension& extension) {
598 if (extensions::util::ShouldSyncApp(&extension, profile_)) {
599 if (app_sync_bundle_.IsSyncing())
600 app_sync_bundle_.SyncChangeIfNeeded(extension);
601 else if (extension_service_->is_ready() && !flare_.is_null())
602 flare_.Run(syncer::APPS);
603 } else if (extensions::util::ShouldSyncExtension(&extension, profile_)) {
604 if (extension_sync_bundle_.IsSyncing())
605 extension_sync_bundle_.SyncChangeIfNeeded(extension);
606 else if (extension_service_->is_ready() && !flare_.is_null())
607 flare_.Run(syncer::EXTENSIONS);