Cleanup ExtensionSyncService and SyncBundle.

- Merge AppSyncData into ExtensionSyncData.
- Remove duplication between AppSyncBundle and ExtensionSyncBundle by moving everything into the common SyncBundle.
- In SyncBundle, use consistent "push"/"apply" notation for changes going to/coming from Sync, rather than "process" for everything.
- Reduce the back-and-forth of ExtensionSyncService and SyncBundle calling into each other.

TBRing a straightforward change in two_client_apps_sync_test.cc
[email protected]

BUG=None

Review URL: https://codereview.chromium.org/1214923004

Cr-Commit-Position: refs/heads/master@{#337793}
diff --git a/chrome/browser/apps/ephemeral_app_browsertest.cc b/chrome/browser/apps/ephemeral_app_browsertest.cc
index 063ea4d..c99a49a 100644
--- a/chrome/browser/apps/ephemeral_app_browsertest.cc
+++ b/chrome/browser/apps/ephemeral_app_browsertest.cc
@@ -14,8 +14,8 @@
 #include "chrome/browser/apps/app_browsertest_util.h"
 #include "chrome/browser/apps/ephemeral_app_service.h"
 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
-#include "chrome/browser/extensions/app_sync_data.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
 #include "chrome/browser/extensions/extension_sync_service.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/notifications/desktop_notification_service.h"
@@ -45,13 +45,13 @@
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/notifier_settings.h"
 
-using extensions::AppSyncData;
 using extensions::Event;
 using extensions::EventRouter;
 using extensions::Extension;
 using extensions::ExtensionPrefs;
 using extensions::ExtensionRegistry;
 using extensions::ExtensionRegistryObserver;
+using extensions::ExtensionSyncData;
 using extensions::ExtensionSystem;
 using extensions::Manifest;
 using extensions::ResultCatcher;
@@ -372,7 +372,7 @@
     EXPECT_TRUE(extensions::util::IsEphemeralApp(app_id, profile()));
 
     // Ephemeral apps should not be synced.
-    scoped_ptr<AppSyncData> sync_change = GetLastSyncChangeForApp(app_id);
+    scoped_ptr<ExtensionSyncData> sync_change = GetLastSyncChangeForApp(app_id);
     EXPECT_FALSE(sync_change.get());
 
     // Ephemeral apps should not be assigned ordinals.
@@ -449,7 +449,8 @@
     ASSERT_TRUE(app);
 
     // Ephemeral apps should not be synced.
-    scoped_ptr<AppSyncData> sync_change = GetLastSyncChangeForApp(app->id());
+    scoped_ptr<ExtensionSyncData> sync_change =
+        GetLastSyncChangeForApp(app->id());
     EXPECT_FALSE(sync_change.get());
 
     // Promote the app to a regular installed app.
@@ -480,21 +481,22 @@
     int disable_reasons = enable_from_sync ? 0 : Extension::DISABLE_USER_ACTION;
     const syncer::StringOrdinal kAppLaunchOrdinal("x");
     const syncer::StringOrdinal kPageOrdinal("y");
-    AppSyncData app_sync_data(*app,
-                              enable_from_sync,
-                              disable_reasons,
-                              false /* incognito enabled */,
-                              false /* remote install */,
-                              extensions::ExtensionSyncData::BOOLEAN_UNSET,
-                              kAppLaunchOrdinal,
-                              kPageOrdinal,
-                              extensions::LAUNCH_TYPE_REGULAR);
+    ExtensionSyncData app_sync_data(
+        *app,
+        enable_from_sync,
+        disable_reasons,
+        false /* incognito enabled */,
+        false /* remote install */,
+        extensions::ExtensionSyncData::BOOLEAN_UNSET,
+        kAppLaunchOrdinal,
+        kPageOrdinal,
+        extensions::LAUNCH_TYPE_REGULAR);
 
     std::string app_id = app->id();
     app = NULL;
 
     ExtensionSyncService* sync_service = ExtensionSyncService::Get(profile());
-    sync_service->ProcessAppSyncData(app_sync_data);
+    sync_service->ApplySyncData(app_sync_data);
 
     // Verify the installation.
     VerifyPromotedApp(app_id, expected_set);
@@ -522,12 +524,13 @@
             new syncer::SyncErrorFactoryMock()));
   }
 
-  scoped_ptr<AppSyncData> GetLastSyncChangeForApp(const std::string& id) {
-    scoped_ptr<AppSyncData> sync_data;
+  scoped_ptr<ExtensionSyncData> GetLastSyncChangeForApp(const std::string& id) {
+    scoped_ptr<ExtensionSyncData> sync_data;
     for (syncer::SyncChangeList::iterator it =
              mock_sync_processor_.changes().begin();
          it != mock_sync_processor_.changes().end(); ++it) {
-      scoped_ptr<AppSyncData> data(AppSyncData::CreateFromSyncChange(*it));
+      scoped_ptr<ExtensionSyncData> data(
+          ExtensionSyncData::CreateFromSyncChange(*it));
       if (data.get() && data->id() == id)
         sync_data.reset(data.release());
     }
@@ -535,7 +538,8 @@
     return sync_data.Pass();
   }
 
-  void VerifySyncChange(const AppSyncData* sync_change, bool expect_enabled) {
+  void VerifySyncChange(const ExtensionSyncData* sync_change,
+                        bool expect_enabled) {
     if (!kEnableSync)
       return;
 
@@ -543,7 +547,7 @@
     EXPECT_TRUE(sync_change->page_ordinal().IsValid());
     EXPECT_TRUE(sync_change->app_launch_ordinal().IsValid());
     EXPECT_FALSE(sync_change->uninstalled());
-    EXPECT_EQ(expect_enabled, sync_change->extension_sync_data().enabled());
+    EXPECT_EQ(expect_enabled, sync_change->enabled());
   }
 
   void TestInstallEvent(bool close_app) {
@@ -861,7 +865,8 @@
   PromoteEphemeralAppAndVerify(app, ExtensionRegistry::BLACKLISTED);
 
   // The app should be synced, but disabled.
-  scoped_ptr<AppSyncData> sync_change = GetLastSyncChangeForApp(app->id());
+  scoped_ptr<ExtensionSyncData> sync_change =
+      GetLastSyncChangeForApp(app->id());
   VerifySyncChange(sync_change.get(), false);
 }
 
diff --git a/chrome/browser/extensions/app_sync_bundle.cc b/chrome/browser/extensions/app_sync_bundle.cc
deleted file mode 100644
index c50f2c9..0000000
--- a/chrome/browser/extensions/app_sync_bundle.cc
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/app_sync_bundle.h"
-
-#include "base/location.h"
-#include "chrome/browser/extensions/extension_sync_service.h"
-#include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/extensions/sync_helper.h"
-#include "extensions/browser/app_sorting.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_set.h"
-#include "sync/api/sync_change_processor.h"
-#include "sync/api/sync_data.h"
-#include "sync/api/sync_error_factory.h"
-
-namespace extensions {
-
-AppSyncBundle::AppSyncBundle(ExtensionSyncService* extension_sync_service)
-    : extension_sync_service_(extension_sync_service) {}
-
-AppSyncBundle::~AppSyncBundle() {}
-
-void AppSyncBundle::SetupSync(
-    syncer::SyncChangeProcessor* sync_change_processor,
-    syncer::SyncErrorFactory* sync_error_factory,
-    const syncer::SyncDataList& initial_sync_data) {
-  sync_processor_.reset(sync_change_processor);
-  sync_error_factory_.reset(sync_error_factory);
-
-  for (syncer::SyncDataList::const_iterator i = initial_sync_data.begin();
-       i != initial_sync_data.end();
-       ++i) {
-    scoped_ptr<AppSyncData> app_sync_data(AppSyncData::CreateFromSyncData(*i));
-    if (app_sync_data.get()) {
-      AddApp(app_sync_data->id());
-      extension_sync_service_->ProcessAppSyncData(*app_sync_data);
-    }
-  }
-}
-
-void AppSyncBundle::Reset() {
-  sync_processor_.reset();
-  sync_error_factory_.reset();
-  synced_apps_.clear();
-  pending_sync_data_.clear();
-}
-
-syncer::SyncChange AppSyncBundle::CreateSyncChangeToDelete(
-    const Extension* extension)
-    const {
-  AppSyncData sync_data = extension_sync_service_->GetAppSyncData(*extension);
-  return sync_data.GetSyncChange(syncer::SyncChange::ACTION_DELETE);
-}
-
-void AppSyncBundle::ProcessDeletion(const std::string& extension_id,
-                                    const syncer::SyncChange& sync_change) {
-  RemoveApp(extension_id);
-  sync_processor_->ProcessSyncChanges(FROM_HERE,
-                                      syncer::SyncChangeList(1, sync_change));
-}
-
-syncer::SyncChange AppSyncBundle::CreateSyncChange(
-    const syncer::SyncData& sync_data) {
-  const syncer::SyncDataLocal sync_data_local(sync_data);
-  if (HasExtensionId(sync_data_local.GetTag())) {
-    return syncer::SyncChange(FROM_HERE,
-                              syncer::SyncChange::ACTION_UPDATE,
-                              sync_data);
-  } else {
-    AddApp(sync_data_local.GetTag());
-    return syncer::SyncChange(FROM_HERE,
-                              syncer::SyncChange::ACTION_ADD,
-                              sync_data);
-  }
-}
-
-syncer::SyncDataList AppSyncBundle::GetAllSyncData() const {
-  std::vector<AppSyncData> app_sync_data =
-      extension_sync_service_->GetAppSyncDataList();
-  syncer::SyncDataList result(app_sync_data.size());
-  for (int i = 0; i < static_cast<int>(app_sync_data.size()); ++i) {
-    result[i] = app_sync_data[i].GetSyncData();
-  }
-  return result;
-}
-
-void AppSyncBundle::ProcessSyncChange(AppSyncData app_sync_data) {
-  if (app_sync_data.uninstalled())
-    RemoveApp(app_sync_data.id());
-  else
-    AddApp(app_sync_data.id());
-  extension_sync_service_->ProcessAppSyncData(app_sync_data);
-}
-
-void AppSyncBundle::ProcessSyncChangeList(
-    syncer::SyncChangeList sync_change_list) {
-  sync_processor_->ProcessSyncChanges(FROM_HERE, sync_change_list);
-  extension_sync_service_->extension_prefs().app_sorting()->
-      FixNTPOrdinalCollisions();
-}
-
-bool AppSyncBundle::HasExtensionId(const std::string& id) const {
-  return synced_apps_.find(id) != synced_apps_.end();
-}
-
-bool AppSyncBundle::HasPendingExtensionId(const std::string& id) const {
-  return pending_sync_data_.find(id) != pending_sync_data_.end();
-}
-
-void AppSyncBundle::AddPendingApp(const std::string& id,
-                                  const AppSyncData& app_sync_data) {
-  pending_sync_data_[id] = app_sync_data;
-}
-
-bool AppSyncBundle::IsSyncing() const {
-  return sync_processor_ != NULL;
-}
-
-void AppSyncBundle::SyncChangeIfNeeded(const Extension& extension) {
-  AppSyncData app_sync_data = extension_sync_service_->GetAppSyncData(
-      extension);
-
-  syncer::SyncChangeList sync_change_list(1, app_sync_data.GetSyncChange(
-      HasExtensionId(extension.id()) ?
-      syncer::SyncChange::ACTION_UPDATE : syncer::SyncChange::ACTION_ADD));
-  sync_processor_->ProcessSyncChanges(FROM_HERE, sync_change_list);
-  MarkPendingAppSynced(extension.id());
-}
-
-std::vector<AppSyncData> AppSyncBundle::GetPendingData() const {
-  std::vector<AppSyncData> pending_apps;
-  for (std::map<std::string, AppSyncData>::const_iterator
-           i = pending_sync_data_.begin();
-       i != pending_sync_data_.end();
-       ++i) {
-    pending_apps.push_back(i->second);
-  }
-
-  return pending_apps;
-}
-
-void AppSyncBundle::GetAppSyncDataListHelper(
-    const ExtensionSet& extensions,
-    std::vector<AppSyncData>* sync_data_list) const {
-  Profile* profile = extension_sync_service_->profile();
-
-  for (ExtensionSet::const_iterator it = extensions.begin();
-       it != extensions.end(); ++it) {
-    const Extension& extension = *it->get();
-    // If we have pending app data for this app, then this
-    // version is out of date.  We'll sync back the version we got from
-    // sync.
-    if (IsSyncing() && util::ShouldSyncApp(&extension, profile) &&
-        !HasPendingExtensionId(extension.id())) {
-      sync_data_list->push_back(extension_sync_service_->GetAppSyncData(
-          extension));
-    }
-  }
-}
-
-void AppSyncBundle::AddApp(const std::string& id) {
-  synced_apps_.insert(id);
-}
-
-void AppSyncBundle::RemoveApp(const std::string& id) {
-  synced_apps_.erase(id);
-}
-
-
-void AppSyncBundle::MarkPendingAppSynced(const std::string& id) {
-  pending_sync_data_.erase(id);
-  synced_apps_.insert(id);
-}
-
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/app_sync_bundle.h b/chrome/browser/extensions/app_sync_bundle.h
deleted file mode 100644
index b79a1ed..0000000
--- a/chrome/browser/extensions/app_sync_bundle.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_APP_SYNC_BUNDLE_H_
-#define CHROME_BROWSER_EXTENSIONS_APP_SYNC_BUNDLE_H_
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/extensions/app_sync_data.h"
-#include "chrome/browser/extensions/sync_bundle.h"
-#include "sync/api/syncable_service.h"
-
-class ExtensionSyncService;
-
-namespace syncer {
-class SyncChangeProcessor;
-class SyncErrorFactory;
-}
-
-namespace extensions {
-
-class Extension;
-class ExtensionSet;
-
-// Bundle of app specific sync stuff.
-class AppSyncBundle : public SyncBundle {
- public:
-  explicit AppSyncBundle(ExtensionSyncService* extension_sync_service);
-  ~AppSyncBundle() override;
-
-  // Setup this bundle to be sync application data.
-  void SetupSync(syncer::SyncChangeProcessor* sync_proccessor,
-                 syncer::SyncErrorFactory* sync_error_factory,
-                 const syncer::SyncDataList& initial_sync_data);
-
-  // Resets this class back to it default values, which will disable all syncing
-  // until a new sync processor is set.
-  void Reset();
-
-  // Returns a syncer::SyncChange that will delete the given application.
-  syncer::SyncChange CreateSyncChangeToDelete(const Extension* extension) const;
-
-  // Process the sync deletion of the given application.
-  void ProcessDeletion(const std::string& extension_id,
-                       const syncer::SyncChange& sync_change);
-
-  // Create a sync change based on |sync_data|.
-  syncer::SyncChange CreateSyncChange(const syncer::SyncData& sync_data);
-
-  // Get all the sync data contained in this bundle.
-  syncer::SyncDataList GetAllSyncData() const;
-
-  // Process the given sync change and apply it.
-  void ProcessSyncChange(AppSyncData app_sync_data);
-
-  // Process the list of sync changes.
-  void ProcessSyncChangeList(syncer::SyncChangeList sync_change_list);
-
-  // Check to see if the given |id| is either synced or pending to be synced.
-  bool HasExtensionId(const std::string& id) const;
-  bool HasPendingExtensionId(const std::string& id) const;
-
-  // Add a pending app to be synced.
-  void AddPendingApp(const std::string& id,
-                     const AppSyncData& app_sync_data);
-
-  // Returns a vector of all the pending sync data.
-  std::vector<AppSyncData> GetPendingData() const;
-
-  // Appends sync data objects for every app in |extensions|.
-  void GetAppSyncDataListHelper(
-      const ExtensionSet& extensions,
-      std::vector<extensions::AppSyncData>* sync_data_list) const;
-
-  // Overrides for SyncBundle.
-  // Returns true if SetupSync has been called, false otherwise.
-  bool IsSyncing() const override;
-
-  // Sync a newly-installed application or change an existing one.
-  void SyncChangeIfNeeded(const Extension& extension) override;
-
- private:
-  // Add a synced app.
-  void AddApp(const std::string& id);
-
-  // Remove a synced app.
-  void RemoveApp(const std::string& id); // make private
-
-  // Change an app from being pending to synced.
-  void MarkPendingAppSynced(const std::string& id);
-
-  ExtensionSyncService* extension_sync_service_; // Own us.
-  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
-  scoped_ptr<syncer::SyncErrorFactory> sync_error_factory_;
-
-  std::set<std::string> synced_apps_;
-  std::map<std::string, AppSyncData> pending_sync_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppSyncBundle);
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_APP_SYNC_BUNDLE_H_
diff --git a/chrome/browser/extensions/app_sync_data.cc b/chrome/browser/extensions/app_sync_data.cc
deleted file mode 100644
index 6f231ba..0000000
--- a/chrome/browser/extensions/app_sync_data.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/app_sync_data.h"
-
-#include "chrome/common/extensions/manifest_handlers/app_icon_color_info.h"
-#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
-#include "chrome/common/extensions/manifest_handlers/linked_app_icons.h"
-#include "extensions/common/extension.h"
-#include "sync/api/sync_data.h"
-#include "sync/protocol/app_specifics.pb.h"
-#include "sync/protocol/sync.pb.h"
-
-namespace extensions {
-
-AppSyncData::LinkedAppIconInfo::LinkedAppIconInfo() {
-}
-
-AppSyncData::LinkedAppIconInfo::~LinkedAppIconInfo() {
-}
-
-AppSyncData::AppSyncData() {}
-
-AppSyncData::AppSyncData(const Extension& extension,
-                         bool enabled,
-                         int disable_reasons,
-                         bool incognito_enabled,
-                         bool remote_install,
-                         ExtensionSyncData::OptionalBoolean all_urls_enabled,
-                         const syncer::StringOrdinal& app_launch_ordinal,
-                         const syncer::StringOrdinal& page_ordinal,
-                         extensions::LaunchType launch_type)
-    : extension_sync_data_(extension,
-                           enabled,
-                           disable_reasons,
-                           incognito_enabled,
-                           remote_install,
-                           all_urls_enabled),
-      app_launch_ordinal_(app_launch_ordinal),
-      page_ordinal_(page_ordinal),
-      launch_type_(launch_type) {
-  if (extension.from_bookmark()) {
-    bookmark_app_description_ = extension.description();
-    bookmark_app_url_ = AppLaunchInfo::GetLaunchWebURL(&extension).spec();
-    bookmark_app_icon_color_ = AppIconColorInfo::GetIconColorString(&extension);
-    extensions::LinkedAppIcons icons =
-        LinkedAppIcons::GetLinkedAppIcons(&extension);
-    for (const auto& icon : icons.icons) {
-      LinkedAppIconInfo linked_icon;
-      linked_icon.url = icon.url;
-      linked_icon.size = icon.size;
-      linked_icons_.push_back(linked_icon);
-    }
-  }
-}
-
-AppSyncData::~AppSyncData() {}
-
-// static
-scoped_ptr<AppSyncData> AppSyncData::CreateFromSyncData(
-    const syncer::SyncData& sync_data) {
-  scoped_ptr<AppSyncData> data(new AppSyncData);
-  if (data->PopulateFromSyncData(sync_data))
-    return data.Pass();
-  return scoped_ptr<AppSyncData>();
-}
-
-// static
-scoped_ptr<AppSyncData> AppSyncData::CreateFromSyncChange(
-    const syncer::SyncChange& sync_change) {
-  scoped_ptr<AppSyncData> data(CreateFromSyncData(sync_change.sync_data()));
-  if (!data.get())
-    return scoped_ptr<AppSyncData>();
-
-  data->extension_sync_data_.set_uninstalled(sync_change.change_type() ==
-                                             syncer::SyncChange::ACTION_DELETE);
-  return data.Pass();
-}
-
-syncer::SyncData AppSyncData::GetSyncData() const {
-  sync_pb::EntitySpecifics specifics;
-  PopulateAppSpecifics(specifics.mutable_app());
-
-  return syncer::SyncData::CreateLocalData(extension_sync_data_.id(),
-                                   extension_sync_data_.name(),
-                                   specifics);
-}
-
-syncer::SyncChange AppSyncData::GetSyncChange(
-    syncer::SyncChange::SyncChangeType change_type) const {
-  return syncer::SyncChange(FROM_HERE, change_type, GetSyncData());
-}
-
-void AppSyncData::PopulateAppSpecifics(sync_pb::AppSpecifics* specifics) const {
-  DCHECK(specifics);
-  // Only sync the ordinal values and launch type if they are valid.
-  if (app_launch_ordinal_.IsValid())
-    specifics->set_app_launch_ordinal(app_launch_ordinal_.ToInternalValue());
-  if (page_ordinal_.IsValid())
-    specifics->set_page_ordinal(page_ordinal_.ToInternalValue());
-
-  sync_pb::AppSpecifics::LaunchType sync_launch_type =
-      static_cast<sync_pb::AppSpecifics::LaunchType>(launch_type_);
-
-  // The corresponding validation of this value during processing of an
-  // AppSyncData is in ExtensionSyncService::ProcessAppSyncData.
-  if (launch_type_ >= LAUNCH_TYPE_FIRST && launch_type_ < NUM_LAUNCH_TYPES &&
-      sync_pb::AppSpecifics_LaunchType_IsValid(sync_launch_type)) {
-    specifics->set_launch_type(sync_launch_type);
-  }
-
-  if (!bookmark_app_url_.empty())
-    specifics->set_bookmark_app_url(bookmark_app_url_);
-
-  if (!bookmark_app_description_.empty())
-    specifics->set_bookmark_app_description(bookmark_app_description_);
-
-  if (!bookmark_app_icon_color_.empty())
-    specifics->set_bookmark_app_icon_color(bookmark_app_icon_color_);
-
-  for (const auto& linked_icon : linked_icons_) {
-    sync_pb::LinkedAppIconInfo* linked_app_icon_info =
-        specifics->add_linked_app_icons();
-    linked_app_icon_info->set_url(linked_icon.url.spec());
-    linked_app_icon_info->set_size(linked_icon.size);
-  }
-
-  extension_sync_data_.PopulateExtensionSpecifics(
-      specifics->mutable_extension());
-}
-
-bool AppSyncData::PopulateFromAppSpecifics(
-    const sync_pb::AppSpecifics& specifics) {
-  if (!extension_sync_data_.PopulateFromExtensionSpecifics(
-          specifics.extension()))
-    return false;
-
-  app_launch_ordinal_ = syncer::StringOrdinal(specifics.app_launch_ordinal());
-  page_ordinal_ = syncer::StringOrdinal(specifics.page_ordinal());
-
-  launch_type_ = specifics.has_launch_type()
-      ? static_cast<extensions::LaunchType>(specifics.launch_type())
-      : LAUNCH_TYPE_INVALID;
-
-  bookmark_app_url_ = specifics.bookmark_app_url();
-  bookmark_app_description_ = specifics.bookmark_app_description();
-  bookmark_app_icon_color_ = specifics.bookmark_app_icon_color();
-
-  for (int i = 0; i < specifics.linked_app_icons_size(); ++i) {
-    const sync_pb::LinkedAppIconInfo& linked_app_icon_info =
-        specifics.linked_app_icons(i);
-    if (linked_app_icon_info.has_url() && linked_app_icon_info.has_size()) {
-      LinkedAppIconInfo linked_icon;
-      linked_icon.url = GURL(linked_app_icon_info.url());
-      linked_icon.size = linked_app_icon_info.size();
-      linked_icons_.push_back(linked_icon);
-    }
-  }
-
-  return true;
-}
-
-bool AppSyncData::PopulateFromSyncData(const syncer::SyncData& sync_data) {
-  return PopulateFromAppSpecifics(sync_data.GetSpecifics().app());
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/app_sync_data.h b/chrome/browser/extensions/app_sync_data.h
deleted file mode 100644
index 4d19060a..0000000
--- a/chrome/browser/extensions/app_sync_data.h
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_APP_SYNC_DATA_H_
-#define CHROME_BROWSER_EXTENSIONS_APP_SYNC_DATA_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/extensions/extension_sync_data.h"
-#include "extensions/common/constants.h"
-#include "sync/api/string_ordinal.h"
-#include "sync/api/sync_change.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace syncer {
-class SyncData;
-}
-
-namespace sync_pb {
-class AppSpecifics;
-}
-
-namespace extensions {
-
-class Extension;
-class ExtensionSyncData;
-
-// A class that encapsulates the synced properties of an Application.
-class AppSyncData {
- public:
-  struct LinkedAppIconInfo {
-    LinkedAppIconInfo();
-    ~LinkedAppIconInfo();
-
-    GURL url;
-    int size;
-  };
-
-  AppSyncData();
-  AppSyncData(const Extension& extension,
-              bool enabled,
-              int disable_reasons,
-              bool incognito_enabled,
-              bool remote_install,
-              ExtensionSyncData::OptionalBoolean all_urls_enabled,
-              const syncer::StringOrdinal& app_launch_ordinal,
-              const syncer::StringOrdinal& page_ordinal,
-              extensions::LaunchType launch_type);
-  ~AppSyncData();
-
-  // For constructing an AppSyncData from received sync data.
-  // May return null if the sync data was invalid.
-  static scoped_ptr<AppSyncData> CreateFromSyncData(
-      const syncer::SyncData& sync_data);
-  static scoped_ptr<AppSyncData> CreateFromSyncChange(
-      const syncer::SyncChange& sync_change);
-
-  // Retrive sync data from this class.
-  syncer::SyncData GetSyncData() const;
-  syncer::SyncChange GetSyncChange(
-      syncer::SyncChange::SyncChangeType change_type) const;
-
-  const std::string& id() const { return extension_sync_data_.id(); }
-
-  bool uninstalled() const { return extension_sync_data_.uninstalled(); }
-
-  // These ordinals aren't necessarily valid. Some applications don't have
-  // valid ordinals because they don't appear on the new tab page.
-  const syncer::StringOrdinal& app_launch_ordinal() const {
-    return app_launch_ordinal_;
-  }
-  const syncer::StringOrdinal& page_ordinal() const { return page_ordinal_; }
-
-  const ExtensionSyncData& extension_sync_data() const {
-    return extension_sync_data_;
-  }
-
-  extensions::LaunchType launch_type() const {
-    return launch_type_;
-  }
-
-  const std::string& bookmark_app_url() const {
-    return bookmark_app_url_;
-  }
-
-  const std::string& bookmark_app_description() const {
-    return bookmark_app_description_;
-  }
-
-  const std::string& bookmark_app_icon_color() const {
-    return bookmark_app_icon_color_;
-  }
-
-  const std::vector<LinkedAppIconInfo>& linked_icons() const {
-    return linked_icons_;
-  }
-
- private:
-  // Convert an AppSyncData back out to a sync structure.
-  void PopulateAppSpecifics(sync_pb::AppSpecifics* specifics) const;
-
-  // Populate this class from sync inputs. Return true if the input
-  // was valid.
-  bool PopulateFromAppSpecifics(const sync_pb::AppSpecifics& specifics);
-  bool PopulateFromSyncData(const syncer::SyncData& sync_data);
-
-  ExtensionSyncData extension_sync_data_;
-  syncer::StringOrdinal app_launch_ordinal_;
-  syncer::StringOrdinal page_ordinal_;
-  extensions::LaunchType launch_type_;
-  std::string bookmark_app_url_;
-  std::string bookmark_app_description_;
-  std::string bookmark_app_icon_color_;
-  std::vector<LinkedAppIconInfo> linked_icons_;
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_APP_SYNC_DATA_H_
diff --git a/chrome/browser/extensions/app_sync_data_unittest.cc b/chrome/browser/extensions/app_sync_data_unittest.cc
deleted file mode 100644
index 0c0b28e..0000000
--- a/chrome/browser/extensions/app_sync_data_unittest.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/app_sync_data.h"
-
-#include "extensions/common/extension.h"
-#include "sync/api/string_ordinal.h"
-#include "sync/protocol/app_specifics.pb.h"
-#include "sync/protocol/sync.pb.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace extensions {
-
-const char kValidId[] = "abcdefghijklmnopabcdefghijklmnop";
-const char kName[] = "MyExtension";
-const char kValidVersion[] = "0.0.0.0";
-const char kValidUpdateUrl[] = "http://clients2.google.com/service/update2/crx";
-const int kValidDisableReasons = Extension::DISABLE_USER_ACTION;
-
-class AppSyncDataTest : public testing::Test {
- public:
-  AppSyncDataTest() {}
-  ~AppSyncDataTest() override {}
-
-  void SetRequiredExtensionValues(
-      sync_pb::ExtensionSpecifics* extension_specifics) {
-    extension_specifics->set_id(kValidId);
-    extension_specifics->set_update_url(kValidUpdateUrl);
-    extension_specifics->set_version(kValidVersion);
-    extension_specifics->set_enabled(false);
-    extension_specifics->set_disable_reasons(kValidDisableReasons);
-    extension_specifics->set_incognito_enabled(true);
-    extension_specifics->set_remote_install(false);
-    extension_specifics->set_all_urls_enabled(true);
-    extension_specifics->set_installed_by_custodian(false);
-    extension_specifics->set_name(kName);
-  }
-};
-
-TEST_F(AppSyncDataTest, SyncDataToExtensionSyncDataForApp) {
-  sync_pb::EntitySpecifics entity;
-  sync_pb::AppSpecifics* app_specifics = entity.mutable_app();
-  app_specifics->set_app_launch_ordinal(
-      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue());
-  app_specifics->set_page_ordinal(
-      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue());
-
-  SetRequiredExtensionValues(app_specifics->mutable_extension());
-
-  syncer::SyncData sync_data =
-      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
-
-  scoped_ptr<AppSyncData> app_sync_data =
-      AppSyncData::CreateFromSyncData(sync_data);
-  ASSERT_TRUE(app_sync_data.get());
-  EXPECT_EQ(app_specifics->app_launch_ordinal(),
-            app_sync_data->app_launch_ordinal().ToInternalValue());
-  EXPECT_EQ(app_specifics->page_ordinal(),
-            app_sync_data->page_ordinal().ToInternalValue());
-}
-
-
-
-TEST_F(AppSyncDataTest, ExtensionSyncDataToSyncDataForApp) {
-  sync_pb::EntitySpecifics entity;
-  sync_pb::AppSpecifics* input_specifics = entity.mutable_app();
-  input_specifics->set_app_launch_ordinal(
-      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue());
-  input_specifics->set_page_ordinal(
-      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue());
-
-  SetRequiredExtensionValues(input_specifics->mutable_extension());
-
-  syncer::SyncData sync_data =
-      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
-  scoped_ptr<AppSyncData> app_sync_data =
-      AppSyncData::CreateFromSyncData(sync_data);
-  ASSERT_TRUE(app_sync_data.get());
-
-  syncer::SyncData output_sync_data = app_sync_data->GetSyncData();
-  EXPECT_TRUE(sync_data.GetSpecifics().has_app());
-  const sync_pb::AppSpecifics& output_specifics =
-      output_sync_data.GetSpecifics().app();
-  EXPECT_EQ(input_specifics->SerializeAsString(),
-            output_specifics.SerializeAsString());
-}
-
-// Ensures that invalid StringOrdinals don't break ExtensionSyncData.
-TEST_F(AppSyncDataTest, ExtensionSyncDataInvalidOrdinal) {
-  sync_pb::EntitySpecifics entity;
-  sync_pb::AppSpecifics* app_specifics = entity.mutable_app();
-  // Set the ordinals as invalid.
-  app_specifics->set_app_launch_ordinal("");
-  app_specifics->set_page_ordinal("");
-
-  SetRequiredExtensionValues(app_specifics->mutable_extension());
-
-  syncer::SyncData sync_data =
-      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
-
-  // There should be no issue loading the sync data.
-  scoped_ptr<AppSyncData> app_sync_data =
-      AppSyncData::CreateFromSyncData(sync_data);
-  ASSERT_TRUE(app_sync_data.get());
-  app_sync_data->GetSyncData();
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
index c35d8b4..b8167cc4 100644
--- a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
+++ b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
@@ -8,6 +8,7 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
 #include "chrome/browser/extensions/extension_sync_service.h"
 #include "chrome/browser/extensions/extension_uninstall_dialog.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
@@ -32,6 +33,7 @@
 using extensions::Extension;
 using extensions::ExtensionRegistry;
 using extensions::ExtensionPrefs;
+using extensions::ExtensionSyncData;
 
 class ExtensionDisabledGlobalErrorTest : public ExtensionBrowserTest {
  protected:
@@ -185,7 +187,7 @@
   ExtensionSyncService* sync_service = ExtensionSyncService::Get(
       browser()->profile());
   extensions::ExtensionSyncData sync_data =
-      sync_service->GetExtensionSyncData(*extension);
+      sync_service->CreateSyncData(*extension);
   UninstallExtension(extension_id);
   extension = NULL;
 
@@ -209,7 +211,7 @@
   service_->updater()->set_default_check_params(params);
 
   // Sync is replacing an older version, so it pends.
-  EXPECT_FALSE(sync_service->ProcessExtensionSyncData(sync_data));
+  EXPECT_FALSE(sync_service->ApplySyncData(sync_data));
 
   WaitForExtensionInstall();
   content::RunAllBlockingPoolTasksUntilIdle();
@@ -260,7 +262,7 @@
                                          syncer::AttachmentIdList(),
                                          syncer::AttachmentServiceProxy());
   // Sync is installing a new extension, so it pends.
-  EXPECT_FALSE(sync_service->ProcessExtensionSyncData(
+  EXPECT_FALSE(sync_service->ApplySyncData(
       *extensions::ExtensionSyncData::CreateFromSyncData(sync_data)));
 
   WaitForExtensionInstall();
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 16c3a6b5..516985a 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -743,13 +743,13 @@
     return false;
   }
 
-  syncer::SyncChange sync_change;
+  syncer::SyncData sync_data;
   // Don't sync the uninstall if we're going to reinstall the extension
   // momentarily.
   if (extension_sync_service_ &&
       reason != extensions::UNINSTALL_REASON_REINSTALL) {
-     sync_change = extension_sync_service_->PrepareToSyncUninstallExtension(
-        extension.get(), is_ready());
+    sync_data = extension_sync_service_->PrepareToSyncUninstallExtension(
+        *extension);
   }
 
   InstallVerifier::Get(GetBrowserContext())->Remove(extension->id());
@@ -783,9 +783,9 @@
   ExtensionRegistry::Get(profile_)
       ->TriggerOnUninstalled(extension.get(), reason);
 
-  if (sync_change.IsValid()) {
+  if (sync_data.IsValid()) {
     extension_sync_service_->ProcessSyncUninstallExtension(extension->id(),
-                                                           sync_change);
+                                                           sync_data);
   }
 
   delayed_installs_.Remove(extension->id());
@@ -1689,11 +1689,8 @@
     }
 #endif
   }
-  if (disable_reasons != Extension::DISABLE_NONE) {
-    extension_prefs_->AddDisableReason(
-        extension->id(),
-        static_cast<Extension::DisableReason>(disable_reasons));
-  }
+  if (disable_reasons != Extension::DISABLE_NONE)
+    extension_prefs_->AddDisableReasons(extension->id(), disable_reasons);
 }
 
 void ExtensionService::UpdateActiveExtensionsInCrashReporter() {
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 0ae04bb..0a4c7fa 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -31,7 +31,6 @@
 #include "base/version.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/app_sync_data.h"
 #include "chrome/browser/extensions/blacklist.h"
 #include "chrome/browser/extensions/chrome_app_sorting.h"
 #include "chrome/browser/extensions/component_loader.h"
@@ -169,7 +168,6 @@
 using extensions::APIPermission;
 using extensions::APIPermissionSet;
 using extensions::AppSorting;
-using extensions::AppSyncData;
 using extensions::Blacklist;
 using extensions::CrxInstaller;
 using extensions::Extension;
@@ -6193,8 +6191,8 @@
         extension_sync_service()->GetAllSyncData(syncer::APPS);
     ASSERT_EQ(list.size(), 1U);
 
-    scoped_ptr<AppSyncData> app_sync_data =
-        AppSyncData::CreateFromSyncData(list[0]);
+    scoped_ptr<ExtensionSyncData> app_sync_data =
+        ExtensionSyncData::CreateFromSyncData(list[0]);
     EXPECT_TRUE(initial_ordinal.Equals(app_sync_data->app_launch_ordinal()));
     EXPECT_TRUE(initial_ordinal.Equals(app_sync_data->page_ordinal()));
   }
@@ -6206,8 +6204,8 @@
         extension_sync_service()->GetAllSyncData(syncer::APPS);
     ASSERT_EQ(list.size(), 1U);
 
-    scoped_ptr<AppSyncData> app_sync_data =
-        AppSyncData::CreateFromSyncData(list[0]);
+    scoped_ptr<ExtensionSyncData> app_sync_data =
+        ExtensionSyncData::CreateFromSyncData(list[0]);
     ASSERT_TRUE(app_sync_data.get());
     EXPECT_TRUE(initial_ordinal.LessThan(app_sync_data->app_launch_ordinal()));
     EXPECT_TRUE(initial_ordinal.Equals(app_sync_data->page_ordinal()));
@@ -6219,8 +6217,8 @@
         extension_sync_service()->GetAllSyncData(syncer::APPS);
     ASSERT_EQ(list.size(), 1U);
 
-    scoped_ptr<AppSyncData> app_sync_data =
-        AppSyncData::CreateFromSyncData(list[0]);
+    scoped_ptr<ExtensionSyncData> app_sync_data =
+        ExtensionSyncData::CreateFromSyncData(list[0]);
     ASSERT_TRUE(app_sync_data.get());
     EXPECT_TRUE(initial_ordinal.LessThan(app_sync_data->app_launch_ordinal()));
     EXPECT_TRUE(initial_ordinal.LessThan(app_sync_data->page_ordinal()));
@@ -6260,9 +6258,9 @@
         extension_sync_service()->GetAllSyncData(syncer::APPS);
     ASSERT_EQ(list.size(), 3U);
 
-    scoped_ptr<AppSyncData> data[kAppCount];
+    scoped_ptr<ExtensionSyncData> data[kAppCount];
     for (size_t i = 0; i < kAppCount; ++i) {
-      data[i] = AppSyncData::CreateFromSyncData(list[i]);
+      data[i] = ExtensionSyncData::CreateFromSyncData(list[i]);
       ASSERT_TRUE(data[i].get());
     }
 
diff --git a/chrome/browser/extensions/extension_sync_bundle.cc b/chrome/browser/extensions/extension_sync_bundle.cc
deleted file mode 100644
index 61ec117..0000000
--- a/chrome/browser/extensions/extension_sync_bundle.cc
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/extension_sync_bundle.h"
-
-#include "base/location.h"
-#include "chrome/browser/extensions/extension_sync_service.h"
-#include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/extensions/sync_helper.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_set.h"
-#include "sync/api/sync_change_processor.h"
-#include "sync/api/sync_error_factory.h"
-
-namespace extensions {
-
-ExtensionSyncBundle::ExtensionSyncBundle(
-    ExtensionSyncService* extension_sync_service)
-    : extension_sync_service_(extension_sync_service) {}
-
-ExtensionSyncBundle::~ExtensionSyncBundle() {}
-
-void ExtensionSyncBundle::SetupSync(
-    syncer::SyncChangeProcessor* sync_processor,
-    syncer::SyncErrorFactory* sync_error_factory,
-    const syncer::SyncDataList& initial_sync_data) {
-  sync_processor_.reset(sync_processor);
-  sync_error_factory_.reset(sync_error_factory);
-
-  for (syncer::SyncDataList::const_iterator i = initial_sync_data.begin();
-       i != initial_sync_data.end();
-       ++i) {
-    scoped_ptr<ExtensionSyncData> extension_sync_data(
-        ExtensionSyncData::CreateFromSyncData(*i));
-    if (extension_sync_data.get()) {
-      AddExtension(extension_sync_data->id());
-      extension_sync_service_->ProcessExtensionSyncData(*extension_sync_data);
-    }
-  }
-}
-
-void ExtensionSyncBundle::Reset() {
-  sync_processor_.reset();
-  sync_error_factory_.reset();
-  synced_extensions_.clear();
-  pending_sync_data_.clear();
-}
-
-syncer::SyncChange ExtensionSyncBundle::CreateSyncChangeToDelete(
-    const Extension* extension) const {
-  extensions::ExtensionSyncData sync_data =
-      extension_sync_service_->GetExtensionSyncData(*extension);
-  return sync_data.GetSyncChange(syncer::SyncChange::ACTION_DELETE);
-}
-
-void ExtensionSyncBundle::ProcessDeletion(
-    std::string extension_id, const syncer::SyncChange& sync_change) {
-  RemoveExtension(extension_id);
-  sync_processor_->ProcessSyncChanges(FROM_HERE,
-                                      syncer::SyncChangeList(1, sync_change));
-}
-
-syncer::SyncChange ExtensionSyncBundle::CreateSyncChange(
-    const syncer::SyncData& sync_data) {
-  const syncer::SyncDataLocal sync_data_local(sync_data);
-  if (HasExtensionId(sync_data_local.GetTag())) {
-    return syncer::SyncChange(FROM_HERE,
-                              syncer::SyncChange::ACTION_UPDATE,
-                              sync_data);
-  } else {
-    AddExtension(sync_data_local.GetTag());
-    return syncer::SyncChange(FROM_HERE,
-                              syncer::SyncChange::ACTION_ADD,
-                              sync_data);
-  }
-}
-
-syncer::SyncDataList ExtensionSyncBundle::GetAllSyncData() const {
-  std::vector<ExtensionSyncData> extension_sync_data =
-      extension_sync_service_->GetExtensionSyncDataList();
-  syncer::SyncDataList result(extension_sync_data.size());
-  for (int i = 0; i < static_cast<int>(extension_sync_data.size()); ++i) {
-    result[i] = extension_sync_data[i].GetSyncData();
-  }
-  return result;
-}
-
-void ExtensionSyncBundle::ProcessSyncChange(
-    ExtensionSyncData extension_sync_data) {
-  if (extension_sync_data.uninstalled())
-    RemoveExtension(extension_sync_data.id());
-  else
-    AddExtension(extension_sync_data.id());
-  extension_sync_service_->ProcessExtensionSyncData(extension_sync_data);
-}
-
-void ExtensionSyncBundle::ProcessSyncChangeList(
-    syncer::SyncChangeList sync_change_list) {
-  sync_processor_->ProcessSyncChanges(FROM_HERE, sync_change_list);
-}
-
-bool ExtensionSyncBundle::HasExtensionId(
-    const std::string& id) const {
-  return synced_extensions_.find(id) != synced_extensions_.end();
-}
-
-bool ExtensionSyncBundle::HasPendingExtensionId(
-    const std::string& id) const {
-  return pending_sync_data_.find(id) != pending_sync_data_.end();
-}
-
-void ExtensionSyncBundle::AddPendingExtension(
-    const std::string& id,
-    const ExtensionSyncData& extension_sync_data) {
-  pending_sync_data_[id] = extension_sync_data;
-}
-
-bool ExtensionSyncBundle::IsSyncing() const {
-  return sync_processor_ != NULL;
-}
-
-void ExtensionSyncBundle::SyncChangeIfNeeded(const Extension& extension) {
-  ExtensionSyncData extension_sync_data =
-      extension_sync_service_->GetExtensionSyncData(extension);
-
-  syncer::SyncChangeList sync_change_list(1, extension_sync_data.GetSyncChange(
-      HasExtensionId(extension.id()) ?
-      syncer::SyncChange::ACTION_UPDATE : syncer::SyncChange::ACTION_ADD));
-  sync_processor_->ProcessSyncChanges(FROM_HERE, sync_change_list);
-  MarkPendingExtensionSynced(extension.id());
-}
-
-std::vector<ExtensionSyncData> ExtensionSyncBundle::GetPendingData() const {
-  std::vector<ExtensionSyncData> pending_extensions;
-  for (std::map<std::string, ExtensionSyncData>::const_iterator
-           i = pending_sync_data_.begin();
-       i != pending_sync_data_.end();
-       ++i) {
-    pending_extensions.push_back(i->second);
-  }
-
-  return pending_extensions;
-}
-
-void ExtensionSyncBundle::GetExtensionSyncDataListHelper(
-    const ExtensionSet& extensions,
-    std::vector<ExtensionSyncData>* sync_data_list) const {
-  Profile* profile = extension_sync_service_->profile();
-
-  for (ExtensionSet::const_iterator it = extensions.begin();
-       it != extensions.end(); ++it) {
-    const Extension& extension = *it->get();
-    // If we have pending extension data for this extension, then this
-    // version is out of date.  We'll sync back the version we got from
-    // sync.
-    if (IsSyncing() && util::ShouldSyncExtension(&extension, profile) &&
-        !HasPendingExtensionId(extension.id())) {
-      sync_data_list->push_back(
-          extension_sync_service_->GetExtensionSyncData(extension));
-    }
-  }
-}
-
-void ExtensionSyncBundle::AddExtension(const std::string& id) {
-  synced_extensions_.insert(id);
-}
-
-void ExtensionSyncBundle::RemoveExtension(const std::string& id) {
-  synced_extensions_.erase(id);
-}
-
-void ExtensionSyncBundle::MarkPendingExtensionSynced(const std::string& id) {
-  pending_sync_data_.erase(id);
-  synced_extensions_.insert(id);
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_sync_bundle.h b/chrome/browser/extensions/extension_sync_bundle.h
deleted file mode 100644
index 7e8eddaf..0000000
--- a/chrome/browser/extensions/extension_sync_bundle.h
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_SYNC_BUNDLE_H_
-#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SYNC_BUNDLE_H_
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/extensions/extension_sync_data.h"
-#include "chrome/browser/extensions/sync_bundle.h"
-#include "sync/api/syncable_service.h"
-
-class ExtensionSyncService;
-
-namespace syncer {
-class SyncChangeProcessor;
-class SyncErrorFactory;
-}
-
-namespace extensions {
-
-class Extension;
-class ExtensionSet;
-
-// Bundle of extension specific sync stuff.
-class ExtensionSyncBundle : public SyncBundle {
- public:
-  explicit ExtensionSyncBundle(ExtensionSyncService* extension_sync_service);
-  ~ExtensionSyncBundle() override;
-
-  // Setup this bundle to be sync extension data.
-  void SetupSync(syncer::SyncChangeProcessor* sync_processor,
-                 syncer::SyncErrorFactory* sync_error_factory,
-                 const syncer::SyncDataList& initial_sync_data);
-
-  // Resets this class back to it default values, which will disable all syncing
-  // until a new sync processor is set.
-  void Reset();
-
-  // Returns a syncer::SyncChange that will delete the given extension.
-  syncer::SyncChange CreateSyncChangeToDelete(const Extension* extension) const;
-
-  // Process the sync deletion of the given extension.
-  void ProcessDeletion(
-      std::string extension_id, const syncer::SyncChange& sync_change);
-
-  // Create a sync change based on |sync_data|.
-  syncer::SyncChange CreateSyncChange(const syncer::SyncData& sync_data);
-
-  // Get all the sync data contained in this bundle.
-  syncer::SyncDataList GetAllSyncData() const;
-
-  // Process the given sync change and apply it.
-  void ProcessSyncChange(ExtensionSyncData extension_sync_data);
-
-  // Process the list of sync changes.
-  void ProcessSyncChangeList(syncer::SyncChangeList sync_change_list);
-
-  // Check to see if the given |id| is either synced or pending to be synced.
-  bool HasExtensionId(const std::string& id) const;
-  bool HasPendingExtensionId(const std::string& id) const;
-
-  // Add a pending extension to be synced.
-  void AddPendingExtension(const std::string& id,
-                           const ExtensionSyncData& extension_sync_data);
-
-  // Returns a vector of all the pending sync data.
-  std::vector<ExtensionSyncData> GetPendingData() const;
-
-  // Appends sync data objects for every extension in |extensions|.
-  void GetExtensionSyncDataListHelper(
-      const ExtensionSet& extensions,
-      std::vector<extensions::ExtensionSyncData>* sync_data_list) const;
-
-  // Overrides for SyncBundle.
-  // Returns true if SetupSync has been called, false otherwise.
-  bool IsSyncing() const override;
-
-  // Sync a newly-installed extension or change an existing one.
-  void SyncChangeIfNeeded(const Extension& extension) override;
-
- private:
-  // Add a synced extension.
-  void AddExtension(const std::string& id);
-
-  // Remove a synced extension.
-  void RemoveExtension(const std::string& id);
-
-  // Change an extension from being pending to synced.
-  void MarkPendingExtensionSynced(const std::string& id);
-
-  ExtensionSyncService* extension_sync_service_;  // Owns us.
-  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
-  scoped_ptr<syncer::SyncErrorFactory> sync_error_factory_;
-
-  std::set<std::string> synced_extensions_;
-  std::map<std::string, ExtensionSyncData> pending_sync_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(ExtensionSyncBundle);
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_SYNC_BUNDLE_H_
diff --git a/chrome/browser/extensions/extension_sync_data.cc b/chrome/browser/extensions/extension_sync_data.cc
index 17bb217..bacf39c 100644
--- a/chrome/browser/extensions/extension_sync_data.cc
+++ b/chrome/browser/extensions/extension_sync_data.cc
@@ -7,15 +7,20 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
-#include "chrome/browser/extensions/app_sync_data.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/common/extensions/manifest_handlers/app_icon_color_info.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/extensions/manifest_handlers/linked_app_icons.h"
 #include "components/crx_file/id_util.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_url_handlers.h"
 #include "sync/api/sync_data.h"
+#include "sync/protocol/app_specifics.pb.h"
 #include "sync/protocol/extension_specifics.pb.h"
 #include "sync/protocol/sync.pb.h"
 
+using syncer::StringOrdinal;
+
 namespace extensions {
 
 namespace {
@@ -58,15 +63,23 @@
 
 }  // namespace
 
+ExtensionSyncData::LinkedAppIconInfo::LinkedAppIconInfo() {
+}
+
+ExtensionSyncData::LinkedAppIconInfo::~LinkedAppIconInfo() {
+}
+
 ExtensionSyncData::ExtensionSyncData()
-    : uninstalled_(false),
+    : is_app_(false),
+      uninstalled_(false),
       enabled_(false),
       supports_disable_reasons_(false),
       disable_reasons_(Extension::DISABLE_NONE),
       incognito_enabled_(false),
       remote_install_(false),
       all_urls_enabled_(BOOLEAN_UNSET),
-      installed_by_custodian_(false) {
+      installed_by_custodian_(false),
+      launch_type_(LAUNCH_TYPE_INVALID) {
 }
 
 ExtensionSyncData::ExtensionSyncData(const Extension& extension,
@@ -75,7 +88,22 @@
                                      bool incognito_enabled,
                                      bool remote_install,
                                      OptionalBoolean all_urls_enabled)
-    : id_(extension.id()),
+    : ExtensionSyncData(extension, enabled, disable_reasons, incognito_enabled,
+                        remote_install, all_urls_enabled, StringOrdinal(),
+                        StringOrdinal(), LAUNCH_TYPE_INVALID) {
+}
+
+ExtensionSyncData::ExtensionSyncData(const Extension& extension,
+                                     bool enabled,
+                                     int disable_reasons,
+                                     bool incognito_enabled,
+                                     bool remote_install,
+                                     OptionalBoolean all_urls_enabled,
+                                     const StringOrdinal& app_launch_ordinal,
+                                     const StringOrdinal& page_ordinal,
+                                     extensions::LaunchType launch_type)
+    : is_app_(extension.is_app()),
+      id_(extension.id()),
       uninstalled_(false),
       enabled_(enabled),
       supports_disable_reasons_(true),
@@ -87,7 +115,23 @@
       version_(extension.from_bookmark() ? base::Version("0")
                                          : *extension.version()),
       update_url_(ManifestURL::GetUpdateURL(&extension)),
-      name_(extension.non_localized_name()) {
+      name_(extension.non_localized_name()),
+      app_launch_ordinal_(app_launch_ordinal),
+      page_ordinal_(page_ordinal),
+      launch_type_(launch_type) {
+  if (is_app_ && extension.from_bookmark()) {
+    bookmark_app_description_ = extension.description();
+    bookmark_app_url_ = AppLaunchInfo::GetLaunchWebURL(&extension).spec();
+    bookmark_app_icon_color_ = AppIconColorInfo::GetIconColorString(&extension);
+    extensions::LinkedAppIcons icons =
+        LinkedAppIcons::GetLinkedAppIcons(&extension);
+    for (const auto& icon : icons.icons) {
+      LinkedAppIconInfo linked_icon;
+      linked_icon.url = icon.url;
+      linked_icon.size = icon.size;
+      linked_icons_.push_back(linked_icon);
+    }
+  }
 }
 
 ExtensionSyncData::~ExtensionSyncData() {}
@@ -98,7 +142,7 @@
   scoped_ptr<ExtensionSyncData> data(new ExtensionSyncData);
   if (data->PopulateFromSyncData(sync_data))
     return data.Pass();
-  return scoped_ptr<ExtensionSyncData>();
+  return nullptr;
 }
 
 // static
@@ -107,7 +151,7 @@
   scoped_ptr<ExtensionSyncData> data(
       CreateFromSyncData(sync_change.sync_data()));
   if (!data.get())
-    return scoped_ptr<ExtensionSyncData>();
+    return nullptr;
 
   data->set_uninstalled(sync_change.change_type() ==
                         syncer::SyncChange::ACTION_DELETE);
@@ -116,7 +160,10 @@
 
 syncer::SyncData ExtensionSyncData::GetSyncData() const {
   sync_pb::EntitySpecifics specifics;
-  PopulateExtensionSpecifics(specifics.mutable_extension());
+  if (is_app_)
+    ToAppSpecifics(specifics.mutable_app());
+  else
+    ToExtensionSpecifics(specifics.mutable_extension());
 
   return syncer::SyncData::CreateLocalData(id_, name_, specifics);
 }
@@ -126,7 +173,7 @@
   return syncer::SyncChange(FROM_HERE, change_type, GetSyncData());
 }
 
-void ExtensionSyncData::PopulateExtensionSpecifics(
+void ExtensionSyncData::ToExtensionSpecifics(
     sync_pb::ExtensionSpecifics* specifics) const {
   DCHECK(crx_file::id_util::IdIsValid(id_));
   specifics->set_id(id_);
@@ -143,6 +190,43 @@
   specifics->set_name(name_);
 }
 
+void ExtensionSyncData::ToAppSpecifics(sync_pb::AppSpecifics* specifics) const {
+  DCHECK(specifics);
+  // Only sync the ordinal values and launch type if they are valid.
+  if (app_launch_ordinal_.IsValid())
+    specifics->set_app_launch_ordinal(app_launch_ordinal_.ToInternalValue());
+  if (page_ordinal_.IsValid())
+    specifics->set_page_ordinal(page_ordinal_.ToInternalValue());
+
+  sync_pb::AppSpecifics::LaunchType sync_launch_type =
+      static_cast<sync_pb::AppSpecifics::LaunchType>(launch_type_);
+
+  // The corresponding validation of this value during processing of an
+  // AppSyncData is in ExtensionSyncService::ProcessAppSyncData.
+  if (launch_type_ >= LAUNCH_TYPE_FIRST && launch_type_ < NUM_LAUNCH_TYPES &&
+      sync_pb::AppSpecifics_LaunchType_IsValid(sync_launch_type)) {
+    specifics->set_launch_type(sync_launch_type);
+  }
+
+  if (!bookmark_app_url_.empty())
+    specifics->set_bookmark_app_url(bookmark_app_url_);
+
+  if (!bookmark_app_description_.empty())
+    specifics->set_bookmark_app_description(bookmark_app_description_);
+
+  if (!bookmark_app_icon_color_.empty())
+    specifics->set_bookmark_app_icon_color(bookmark_app_icon_color_);
+
+  for (const auto& linked_icon : linked_icons_) {
+    sync_pb::LinkedAppIconInfo* linked_app_icon_info =
+        specifics->add_linked_app_icons();
+    linked_app_icon_info->set_url(linked_icon.url.spec());
+    linked_app_icon_info->set_size(linked_icon.size);
+  }
+
+  ToExtensionSpecifics(specifics->mutable_extension());
+}
+
 bool ExtensionSyncData::PopulateFromExtensionSpecifics(
     const sync_pb::ExtensionSpecifics& specifics) {
   if (!crx_file::id_util::IdIsValid(specifics.id())) {
@@ -201,6 +285,38 @@
   return true;
 }
 
+bool ExtensionSyncData::PopulateFromAppSpecifics(
+    const sync_pb::AppSpecifics& specifics) {
+  if (!PopulateFromExtensionSpecifics(specifics.extension()))
+    return false;
+
+  is_app_ = true;
+
+  app_launch_ordinal_ = syncer::StringOrdinal(specifics.app_launch_ordinal());
+  page_ordinal_ = syncer::StringOrdinal(specifics.page_ordinal());
+
+  launch_type_ = specifics.has_launch_type()
+      ? static_cast<extensions::LaunchType>(specifics.launch_type())
+      : LAUNCH_TYPE_INVALID;
+
+  bookmark_app_url_ = specifics.bookmark_app_url();
+  bookmark_app_description_ = specifics.bookmark_app_description();
+  bookmark_app_icon_color_ = specifics.bookmark_app_icon_color();
+
+  for (int i = 0; i < specifics.linked_app_icons_size(); ++i) {
+    const sync_pb::LinkedAppIconInfo& linked_app_icon_info =
+        specifics.linked_app_icons(i);
+    if (linked_app_icon_info.has_url() && linked_app_icon_info.has_size()) {
+      LinkedAppIconInfo linked_icon;
+      linked_icon.url = GURL(linked_app_icon_info.url());
+      linked_icon.size = linked_app_icon_info.size();
+      linked_icons_.push_back(linked_icon);
+    }
+  }
+
+  return true;
+}
+
 void ExtensionSyncData::set_uninstalled(bool uninstalled) {
   uninstalled_ = uninstalled;
 }
@@ -209,6 +325,9 @@
     const syncer::SyncData& sync_data) {
   const sync_pb::EntitySpecifics& entity_specifics = sync_data.GetSpecifics();
 
+  if (entity_specifics.has_app())
+    return PopulateFromAppSpecifics(entity_specifics.app());
+
   if (entity_specifics.has_extension())
     return PopulateFromExtensionSpecifics(entity_specifics.extension());
 
diff --git a/chrome/browser/extensions/extension_sync_data.h b/chrome/browser/extensions/extension_sync_data.h
index 03ce9bd..2c753f3 100644
--- a/chrome/browser/extensions/extension_sync_data.h
+++ b/chrome/browser/extensions/extension_sync_data.h
@@ -9,6 +9,8 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "base/version.h"
+#include "extensions/common/constants.h"
+#include "sync/api/string_ordinal.h"
 #include "sync/api/sync_change.h"
 #include "url/gurl.h"
 
@@ -17,6 +19,7 @@
 }
 
 namespace sync_pb {
+class AppSpecifics;
 class ExtensionSpecifics;
 }
 
@@ -24,7 +27,9 @@
 
 class Extension;
 
-// A class that encapsulates the synced properties of an Extension.
+// A class that encapsulates the synced properties of an App or Extension.
+// Corresponds to an ExtensionSpecifics or an AppSpecifics proto (note that an
+// AppSpecifics itself includes an ExtensionSpecifics).
 class ExtensionSyncData {
  public:
   enum OptionalBoolean {
@@ -33,13 +38,31 @@
     BOOLEAN_FALSE
   };
 
-  ExtensionSyncData();
+  struct LinkedAppIconInfo {
+    LinkedAppIconInfo();
+    ~LinkedAppIconInfo();
+
+    GURL url;
+    int size;
+  };
+
+  // Extension constructor.
   ExtensionSyncData(const Extension& extension,
                     bool enabled,
                     int disable_reasons,
                     bool incognito_enabled,
                     bool remote_install,
                     OptionalBoolean all_urls_enabled);
+  // App constructor.
+  ExtensionSyncData(const Extension& extension,
+                    bool enabled,
+                    int disable_reasons,
+                    bool incognito_enabled,
+                    bool remote_install,
+                    OptionalBoolean all_urls_enabled,
+                    const syncer::StringOrdinal& app_launch_ordinal,
+                    const syncer::StringOrdinal& page_ordinal,
+                    extensions::LaunchType launch_type);
   ~ExtensionSyncData();
 
   // For constructing an ExtensionSyncData from received sync data.
@@ -54,20 +77,14 @@
   syncer::SyncChange GetSyncChange(
       syncer::SyncChange::SyncChangeType change_type) const;
 
-  // Convert an ExtensionSyncData back out to a sync structure.
-  void PopulateExtensionSpecifics(sync_pb::ExtensionSpecifics* specifics) const;
-
-  // Populate this class from sync inputs. Returns true if the input was valid.
-  bool PopulateFromExtensionSpecifics(
-      const sync_pb::ExtensionSpecifics& specifics);
-
   void set_uninstalled(bool uninstalled);
 
+  bool is_app() const { return is_app_; }
+
   const std::string& id() const { return id_; }
 
-  // Version-independent properties (i.e., used even when the
-  // version of the currently-installed extension doesn't match
-  // |version|).
+  // Version-independent properties (i.e., used even when the version of the
+  // currently-installed extension doesn't match |version|).
   bool uninstalled() const { return uninstalled_; }
   bool enabled() const { return enabled_; }
   bool supports_disable_reasons() const { return supports_disable_reasons_; }
@@ -84,9 +101,45 @@
   // Used only for debugging.
   const std::string& name() const { return name_; }
 
+  // Everything below is App-specific - only set for Apps, not Extensions.
+
+  // These ordinals aren't necessarily valid. Some applications don't have
+  // valid ordinals because they don't appear on the new tab page.
+  const syncer::StringOrdinal& app_launch_ordinal() const {
+    return app_launch_ordinal_;
+  }
+  const syncer::StringOrdinal& page_ordinal() const { return page_ordinal_; }
+  extensions::LaunchType launch_type() const { return launch_type_; }
+  const std::string& bookmark_app_url() const { return bookmark_app_url_; }
+  const std::string& bookmark_app_description() const {
+    return bookmark_app_description_;
+  }
+  const std::string& bookmark_app_icon_color() const {
+    return bookmark_app_icon_color_;
+  }
+  const std::vector<LinkedAppIconInfo>& linked_icons() const {
+    return linked_icons_;
+  }
+
  private:
-  // Populate this class from sync inputs.
+  FRIEND_TEST_ALL_PREFIXES(ExtensionSyncDataTest,
+                           ExtensionSyncDataForExtension);
+
+  ExtensionSyncData();
+
+  // Populate this class from sync inputs. Return true if the input was valid.
   bool PopulateFromSyncData(const syncer::SyncData& sync_data);
+  bool PopulateFromExtensionSpecifics(
+      const sync_pb::ExtensionSpecifics& specifics);
+  bool PopulateFromAppSpecifics(const sync_pb::AppSpecifics& specifics);
+
+  // Convert an ExtensionSyncData back out to a sync ExtensionSpecifics.
+  void ToExtensionSpecifics(sync_pb::ExtensionSpecifics* specifics) const;
+
+  // Convert an ExtensionSyncData back out to a sync AppSpecifics.
+  void ToAppSpecifics(sync_pb::AppSpecifics* specifics) const;
+
+  bool is_app_;
 
   std::string id_;
   bool uninstalled_;
@@ -104,6 +157,15 @@
   Version version_;
   GURL update_url_;
   std::string name_;
+
+  // App-specific fields.
+  syncer::StringOrdinal app_launch_ordinal_;
+  syncer::StringOrdinal page_ordinal_;
+  extensions::LaunchType launch_type_;
+  std::string bookmark_app_url_;
+  std::string bookmark_app_description_;
+  std::string bookmark_app_icon_color_;
+  std::vector<LinkedAppIconInfo> linked_icons_;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_sync_data_unittest.cc b/chrome/browser/extensions/extension_sync_data_unittest.cc
index b61f4d1..bae256f 100644
--- a/chrome/browser/extensions/extension_sync_data_unittest.cc
+++ b/chrome/browser/extensions/extension_sync_data_unittest.cc
@@ -7,6 +7,9 @@
 #include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/version.h"
+#include "extensions/common/extension.h"
+#include "sync/api/string_ordinal.h"
+#include "sync/protocol/app_specifics.pb.h"
 #include "sync/protocol/extension_specifics.pb.h"
 #include "sync/protocol/sync.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,6 +23,7 @@
 const char kVersion[] = "1.0.0.1";
 const char kValidUpdateUrl[] =
     "https://clients2.google.com/service/update2/crx";
+const int kValidDisableReasons = Extension::DISABLE_USER_ACTION;
 const char kName[] = "MyExtension";
 
 // Serializes a protobuf structure (entity specifics) into an ExtensionSyncData
@@ -137,4 +141,90 @@
   SyncDataToProtobufEqual(extension_sync_data);
 }
 
+class AppSyncDataTest : public testing::Test {
+ public:
+  AppSyncDataTest() {}
+  ~AppSyncDataTest() override {}
+
+  void SetRequiredExtensionValues(
+      sync_pb::ExtensionSpecifics* extension_specifics) {
+    extension_specifics->set_id(kValidId);
+    extension_specifics->set_update_url(kValidUpdateUrl);
+    extension_specifics->set_version(kVersion);
+    extension_specifics->set_enabled(false);
+    extension_specifics->set_disable_reasons(kValidDisableReasons);
+    extension_specifics->set_incognito_enabled(true);
+    extension_specifics->set_remote_install(false);
+    extension_specifics->set_all_urls_enabled(true);
+    extension_specifics->set_installed_by_custodian(false);
+    extension_specifics->set_name(kName);
+  }
+};
+
+TEST_F(AppSyncDataTest, SyncDataToExtensionSyncDataForApp) {
+  sync_pb::EntitySpecifics entity;
+  sync_pb::AppSpecifics* app_specifics = entity.mutable_app();
+  app_specifics->set_app_launch_ordinal(
+      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue());
+  app_specifics->set_page_ordinal(
+      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue());
+
+  SetRequiredExtensionValues(app_specifics->mutable_extension());
+
+  syncer::SyncData sync_data =
+      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
+
+  scoped_ptr<ExtensionSyncData> app_sync_data =
+      ExtensionSyncData::CreateFromSyncData(sync_data);
+  ASSERT_TRUE(app_sync_data.get());
+  EXPECT_EQ(app_specifics->app_launch_ordinal(),
+            app_sync_data->app_launch_ordinal().ToInternalValue());
+  EXPECT_EQ(app_specifics->page_ordinal(),
+            app_sync_data->page_ordinal().ToInternalValue());
+}
+
+TEST_F(AppSyncDataTest, ExtensionSyncDataToSyncDataForApp) {
+  sync_pb::EntitySpecifics entity;
+  sync_pb::AppSpecifics* input_specifics = entity.mutable_app();
+  input_specifics->set_app_launch_ordinal(
+      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue());
+  input_specifics->set_page_ordinal(
+      syncer::StringOrdinal::CreateInitialOrdinal().ToInternalValue());
+
+  SetRequiredExtensionValues(input_specifics->mutable_extension());
+
+  syncer::SyncData sync_data =
+      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
+  scoped_ptr<ExtensionSyncData> app_sync_data =
+      ExtensionSyncData::CreateFromSyncData(sync_data);
+  ASSERT_TRUE(app_sync_data.get());
+
+  syncer::SyncData output_sync_data = app_sync_data->GetSyncData();
+  EXPECT_TRUE(sync_data.GetSpecifics().has_app());
+  const sync_pb::AppSpecifics& output_specifics =
+      output_sync_data.GetSpecifics().app();
+  EXPECT_EQ(input_specifics->SerializeAsString(),
+            output_specifics.SerializeAsString());
+}
+
+// Ensures that invalid StringOrdinals don't break ExtensionSyncData.
+TEST_F(AppSyncDataTest, ExtensionSyncDataInvalidOrdinal) {
+  sync_pb::EntitySpecifics entity;
+  sync_pb::AppSpecifics* app_specifics = entity.mutable_app();
+  // Set the ordinals as invalid.
+  app_specifics->set_app_launch_ordinal("");
+  app_specifics->set_page_ordinal("");
+
+  SetRequiredExtensionValues(app_specifics->mutable_extension());
+
+  syncer::SyncData sync_data =
+      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
+
+  // There should be no issue loading the sync data.
+  scoped_ptr<ExtensionSyncData> app_sync_data =
+      ExtensionSyncData::CreateFromSyncData(sync_data);
+  ASSERT_TRUE(app_sync_data.get());
+  app_sync_data->GetSyncData();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_sync_service.cc b/chrome/browser/extensions/extension_sync_service.cc
index 90f679f..5848247 100644
--- a/chrome/browser/extensions/extension_sync_service.cc
+++ b/chrome/browser/extensions/extension_sync_service.cc
@@ -4,13 +4,9 @@
 
 #include "chrome/browser/extensions/extension_sync_service.h"
 
-#include <iterator>
-
 #include "base/basictypes.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/threading/sequenced_worker_pool.h"
 #include "base/threading/thread_restrictions.h"
-#include "chrome/browser/extensions/app_sync_data.h"
 #include "chrome/browser/extensions/bookmark_app_helper.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_sync_data.h"
@@ -20,7 +16,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/glue/sync_start_util.h"
 #include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/extensions/sync_helper.h"
 #include "chrome/common/web_application_info.h"
 #include "components/sync_driver/sync_prefs.h"
@@ -30,21 +25,18 @@
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_icon_set.h"
-#include "extensions/common/feature_switch.h"
+#include "extensions/common/extension_set.h"
 #include "extensions/common/image_util.h"
-#include "extensions/common/manifest_constants.h"
-#include "extensions/common/manifest_handlers/icons_handler.h"
 #include "sync/api/sync_change.h"
 #include "sync/api/sync_error_factory.h"
-#include "ui/gfx/image/image_family.h"
 
-using extensions::AppSyncData;
 using extensions::Extension;
 using extensions::ExtensionPrefs;
 using extensions::ExtensionRegistry;
+using extensions::ExtensionSet;
 using extensions::ExtensionSyncData;
-using extensions::FeatureSwitch;
+using extensions::PendingEnables;
+using extensions::SyncBundle;
 
 namespace {
 
@@ -81,6 +73,28 @@
   return ExtensionSyncData::BOOLEAN_UNSET;
 }
 
+// Returns true if the sync type of |extension| matches |type|.
+bool IsCorrectSyncType(const Extension& extension, syncer::ModelType type) {
+  if (type == syncer::EXTENSIONS &&
+      extensions::sync_helper::IsSyncableExtension(&extension)) {
+    return true;
+  }
+
+  if (type == syncer::APPS &&
+      extensions::sync_helper::IsSyncableApp(&extension)) {
+    return true;
+  }
+
+  return false;
+}
+
+// Returns whether the given app or extension should be synced.
+bool ShouldSync(const Extension& extension, Profile* profile) {
+  if (extension.is_app())
+    return extensions::util::ShouldSyncApp(&extension, profile);
+  return extensions::util::ShouldSyncExtension(&extension, profile);
+}
+
 }  // namespace
 
 ExtensionSyncService::ExtensionSyncService(Profile* profile,
@@ -114,163 +128,119 @@
   return ExtensionSyncServiceFactory::GetForBrowserContext(context);
 }
 
-syncer::SyncChange ExtensionSyncService::PrepareToSyncUninstallExtension(
-    const extensions::Extension* extension, bool extensions_ready) {
+syncer::SyncData ExtensionSyncService::PrepareToSyncUninstallExtension(
+    const Extension& extension) {
   // Extract the data we need for sync now, but don't actually sync until we've
   // completed the uninstallation.
   // TODO(tim): If we get here and IsSyncing is false, this will cause
   // "back from the dead" style bugs, because sync will add-back the extension
   // that was uninstalled here when MergeDataAndStartSyncing is called.
   // See crbug.com/256795.
-  if (extensions::util::ShouldSyncApp(extension, profile_)) {
-    if (app_sync_bundle_.IsSyncing())
-      return app_sync_bundle_.CreateSyncChangeToDelete(extension);
-    else if (extensions_ready && !flare_.is_null())
-      flare_.Run(syncer::APPS);  // Tell sync to start ASAP.
-  } else if (extensions::sync_helper::IsSyncableExtension(extension)) {
-    if (extension_sync_bundle_.IsSyncing())
-      return extension_sync_bundle_.CreateSyncChangeToDelete(extension);
-    else if (extensions_ready && !flare_.is_null())
-      flare_.Run(syncer::EXTENSIONS);  // Tell sync to start ASAP.
+  syncer::ModelType type =
+      extension.is_app() ? syncer::APPS : syncer::EXTENSIONS;
+  const SyncBundle* bundle = GetSyncBundle(type);
+  if (ShouldSync(extension, profile_)) {
+    if (bundle->IsSyncing())
+      return CreateSyncData(extension).GetSyncData();
+    if (extension_service_->is_ready() && !flare_.is_null())
+      flare_.Run(type);  // Tell sync to start ASAP.
   }
 
-  return syncer::SyncChange();
+  return syncer::SyncData();
 }
 
 void ExtensionSyncService::ProcessSyncUninstallExtension(
     const std::string& extension_id,
-    const syncer::SyncChange& sync_change) {
-  if (app_sync_bundle_.HasExtensionId(extension_id) &&
-      sync_change.sync_data().GetDataType() == syncer::APPS) {
-    app_sync_bundle_.ProcessDeletion(extension_id, sync_change);
-  } else if (extension_sync_bundle_.HasExtensionId(extension_id) &&
-             sync_change.sync_data().GetDataType() == syncer::EXTENSIONS) {
-    extension_sync_bundle_.ProcessDeletion(extension_id, sync_change);
-  }
+    const syncer::SyncData& sync_data) {
+  SyncBundle* bundle = GetSyncBundle(sync_data.GetDataType());
+  if (bundle->HasExtensionId(extension_id))
+    bundle->PushSyncDeletion(extension_id, sync_data);
 }
 
-void ExtensionSyncService::SyncEnableExtension(
-    const extensions::Extension& extension) {
-
+void ExtensionSyncService::SyncEnableExtension(const Extension& extension) {
   // Syncing may not have started yet, so handle pending enables.
   if (extensions::util::ShouldSyncApp(&extension, profile_))
-    pending_app_enables_.OnExtensionEnabled(extension.id());
+    pending_app_enables_.Add(extension.id());
 
   if (extensions::util::ShouldSyncExtension(&extension, profile_))
-    pending_extension_enables_.OnExtensionEnabled(extension.id());
+    pending_extension_enables_.Add(extension.id());
 
   SyncExtensionChangeIfNeeded(extension);
 }
 
-void ExtensionSyncService::SyncDisableExtension(
-    const extensions::Extension& extension) {
-
+void ExtensionSyncService::SyncDisableExtension(const Extension& extension) {
   // Syncing may not have started yet, so handle pending enables.
   if (extensions::util::ShouldSyncApp(&extension, profile_))
-    pending_app_enables_.OnExtensionDisabled(extension.id());
+    pending_app_enables_.Remove(extension.id());
 
   if (extensions::util::ShouldSyncExtension(&extension, profile_))
-    pending_extension_enables_.OnExtensionDisabled(extension.id());
+    pending_extension_enables_.Remove(extension.id());
 
   SyncExtensionChangeIfNeeded(extension);
 }
 
+void ExtensionSyncService::SyncExtensionChangeIfNeeded(
+    const Extension& extension) {
+  if (!ShouldSync(extension, profile_))
+    return;
+
+  syncer::ModelType type =
+      extension.is_app() ? syncer::APPS : syncer::EXTENSIONS;
+  SyncBundle* bundle = GetSyncBundle(type);
+  if (bundle->IsSyncing())
+    bundle->PushSyncChangeIfNeeded(extension);
+  else if (extension_service_->is_ready() && !flare_.is_null())
+    flare_.Run(type);
+}
+
 syncer::SyncMergeResult ExtensionSyncService::MergeDataAndStartSyncing(
     syncer::ModelType type,
     const syncer::SyncDataList& initial_sync_data,
     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
     scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
   CHECK(sync_processor.get());
-  CHECK(sync_error_factory.get());
+  LOG_IF(FATAL, type != syncer::EXTENSIONS && type != syncer::APPS)
+      << "Got " << type << " ModelType";
 
-  switch (type) {
-    case syncer::EXTENSIONS:
-      extension_sync_bundle_.SetupSync(sync_processor.release(),
-                                       sync_error_factory.release(),
-                                       initial_sync_data);
-      pending_extension_enables_.OnSyncStarted(extension_service_);
-      break;
+  SyncBundle* bundle = GetSyncBundle(type);
+  bool is_apps = (type == syncer::APPS);
+  PendingEnables* pending_enables =
+      is_apps ? &pending_app_enables_ : &pending_extension_enables_;
 
-    case syncer::APPS:
-      app_sync_bundle_.SetupSync(sync_processor.release(),
-                                 sync_error_factory.release(),
-                                 initial_sync_data);
-      pending_app_enables_.OnSyncStarted(extension_service_);
-      break;
-
-    default:
-      LOG(FATAL) << "Got " << type << " ModelType";
-  }
+  bundle->MergeDataAndStartSyncing(initial_sync_data, sync_processor.Pass());
+  pending_enables->OnSyncStarted(extension_service_);
 
   // Process local extensions.
   // TODO(yoz): Determine whether pending extensions should be considered too.
   //            See crbug.com/104399.
-  syncer::SyncDataList sync_data_list = GetAllSyncData(type);
-  syncer::SyncChangeList sync_change_list;
-  for (syncer::SyncDataList::const_iterator i = sync_data_list.begin();
-       i != sync_data_list.end();
-       ++i) {
-    switch (type) {
-        case syncer::EXTENSIONS:
-          sync_change_list.push_back(
-              extension_sync_bundle_.CreateSyncChange(*i));
-          break;
-        case syncer::APPS:
-          sync_change_list.push_back(app_sync_bundle_.CreateSyncChange(*i));
-          break;
-      default:
-        LOG(FATAL) << "Got " << type << " ModelType";
-    }
-  }
+  bundle->PushSyncDataList(GetAllSyncData(type));
 
-
-  if (type == syncer::EXTENSIONS) {
-    extension_sync_bundle_.ProcessSyncChangeList(sync_change_list);
-  } else if (type == syncer::APPS) {
-    app_sync_bundle_.ProcessSyncChangeList(sync_change_list);
-  }
+  if (is_apps)
+    extension_prefs_->app_sorting()->FixNTPOrdinalCollisions();
 
   return syncer::SyncMergeResult(type);
 }
 
 void ExtensionSyncService::StopSyncing(syncer::ModelType type) {
-  if (type == syncer::APPS) {
-    app_sync_bundle_.Reset();
-  } else if (type == syncer::EXTENSIONS) {
-    extension_sync_bundle_.Reset();
-  }
+  GetSyncBundle(type)->Reset();
 }
 
 syncer::SyncDataList ExtensionSyncService::GetAllSyncData(
     syncer::ModelType type) const {
-  if (type == syncer::EXTENSIONS)
-    return extension_sync_bundle_.GetAllSyncData();
-  if (type == syncer::APPS)
-    return app_sync_bundle_.GetAllSyncData();
-
-  // We should only get sync data for extensions and apps.
-  NOTREACHED();
-
-  return syncer::SyncDataList();
+  std::vector<ExtensionSyncData> data = GetSyncDataList(type);
+  syncer::SyncDataList result;
+  result.reserve(data.size());
+  for (const ExtensionSyncData& item : data)
+    result.push_back(item.GetSyncData());
+  return result;
 }
 
 syncer::SyncError ExtensionSyncService::ProcessSyncChanges(
     const tracked_objects::Location& from_here,
     const syncer::SyncChangeList& change_list) {
-  for (syncer::SyncChangeList::const_iterator i = change_list.begin();
-      i != change_list.end();
-      ++i) {
-    syncer::ModelType type = i->sync_data().GetDataType();
-    if (type == syncer::EXTENSIONS) {
-      scoped_ptr<ExtensionSyncData> extension_data(
-          ExtensionSyncData::CreateFromSyncChange(*i));
-      if (extension_data.get())
-        extension_sync_bundle_.ProcessSyncChange(*extension_data);
-    } else if (type == syncer::APPS) {
-      scoped_ptr<AppSyncData> app_data(AppSyncData::CreateFromSyncChange(*i));
-      if (app_data.get())
-        app_sync_bundle_.ProcessSyncChange(*app_data);
-    }
+  for (const syncer::SyncChange& sync_change : change_list) {
+    syncer::ModelType type = sync_change.sync_data().GetDataType();
+    GetSyncBundle(type)->ApplySyncChange(sync_change);
   }
 
   extension_prefs_->app_sorting()->FixNTPOrdinalCollisions();
@@ -278,76 +248,62 @@
   return syncer::SyncError();
 }
 
-ExtensionSyncData ExtensionSyncService::GetExtensionSyncData(
-    const Extension& extension) const {
+ExtensionSyncData ExtensionSyncService::CreateSyncData(
+    const extensions::Extension& extension) const {
+  bool enabled = extension_service_->IsExtensionEnabled(extension.id());
+  int disable_reasons = extension_prefs_->GetDisableReasons(extension.id());
+  bool incognito_enabled = extensions::util::IsIncognitoEnabled(extension.id(),
+                                                                profile_);
+  bool remote_install =
+      extension_prefs_->HasDisableReason(extension.id(),
+                                         Extension::DISABLE_REMOTE_INSTALL);
+  ExtensionSyncData::OptionalBoolean allowed_on_all_url =
+      GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_);
+  if (extension.is_app()) {
+    return ExtensionSyncData(
+        extension, enabled, disable_reasons, incognito_enabled, remote_install,
+        allowed_on_all_url,
+        extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
+        extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()),
+        extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id()));
+  }
   return ExtensionSyncData(
-      extension,
-      extension_service_->IsExtensionEnabled(extension.id()),
-      extension_prefs_->GetDisableReasons(extension.id()),
-      extensions::util::IsIncognitoEnabled(extension.id(), profile_),
-      extension_prefs_->HasDisableReason(extension.id(),
-                                         Extension::DISABLE_REMOTE_INSTALL),
-      GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_));
+      extension, enabled, disable_reasons, incognito_enabled, remote_install,
+      allowed_on_all_url);
 }
 
-AppSyncData ExtensionSyncService::GetAppSyncData(
-    const Extension& extension) const {
-  return AppSyncData(
-      extension, extension_service_->IsExtensionEnabled(extension.id()),
-      extension_prefs_->GetDisableReasons(extension.id()),
-      extensions::util::IsIncognitoEnabled(extension.id(), profile_),
-      extension_prefs_->HasDisableReason(extension.id(),
-                                         Extension::DISABLE_REMOTE_INSTALL),
-      GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_),
-      extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
-      extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()),
-      extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id()));
-}
-
-std::vector<ExtensionSyncData>
-    ExtensionSyncService::GetExtensionSyncDataList() const {
-  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
-  std::vector<ExtensionSyncData> extension_sync_list;
-  extension_sync_bundle_.GetExtensionSyncDataListHelper(
-      registry->enabled_extensions(), &extension_sync_list);
-  extension_sync_bundle_.GetExtensionSyncDataListHelper(
-      registry->disabled_extensions(), &extension_sync_list);
-  extension_sync_bundle_.GetExtensionSyncDataListHelper(
-      registry->terminated_extensions(), &extension_sync_list);
-
-  std::vector<ExtensionSyncData> pending_extensions =
-      extension_sync_bundle_.GetPendingData();
-  extension_sync_list.insert(extension_sync_list.begin(),
-                             pending_extensions.begin(),
-                             pending_extensions.end());
-
-  return extension_sync_list;
-}
-
-std::vector<AppSyncData> ExtensionSyncService::GetAppSyncDataList() const {
-  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
-  std::vector<AppSyncData> app_sync_list;
-  app_sync_bundle_.GetAppSyncDataListHelper(
-      registry->enabled_extensions(), &app_sync_list);
-  app_sync_bundle_.GetAppSyncDataListHelper(
-      registry->disabled_extensions(), &app_sync_list);
-  app_sync_bundle_.GetAppSyncDataListHelper(
-      registry->terminated_extensions(), &app_sync_list);
-
-  std::vector<AppSyncData> pending_apps = app_sync_bundle_.GetPendingData();
-  app_sync_list.insert(app_sync_list.begin(),
-                       pending_apps.begin(),
-                       pending_apps.end());
-
-  return app_sync_list;
-}
-
-bool ExtensionSyncService::ProcessExtensionSyncData(
+bool ExtensionSyncService::ApplySyncData(
     const ExtensionSyncData& extension_sync_data) {
-  if (!ProcessExtensionSyncDataHelper(extension_sync_data,
-                                      syncer::EXTENSIONS)) {
-    extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(),
-                                               extension_sync_data);
+  const std::string& id = extension_sync_data.id();
+
+  if (extension_sync_data.is_app()) {
+    if (extension_sync_data.app_launch_ordinal().IsValid() &&
+        extension_sync_data.page_ordinal().IsValid()) {
+      extension_prefs_->app_sorting()->SetAppLaunchOrdinal(
+          id,
+          extension_sync_data.app_launch_ordinal());
+      extension_prefs_->app_sorting()->SetPageOrdinal(
+          id,
+          extension_sync_data.page_ordinal());
+    }
+
+    // The corresponding validation of this value during AppSyncData population
+    // is in AppSyncData::PopulateAppSpecifics.
+    if (extension_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
+        extension_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
+      extensions::SetLaunchType(
+          profile_, id, extension_sync_data.launch_type());
+    }
+
+    if (!extension_sync_data.bookmark_app_url().empty())
+      ApplyBookmarkAppSyncData(extension_sync_data);
+  }
+
+  syncer::ModelType type = extension_sync_data.is_app() ? syncer::APPS
+                                                        : syncer::EXTENSIONS;
+
+  if (!ApplyExtensionSyncDataHelper(extension_sync_data, type)) {
+    GetSyncBundle(type)->AddPendingExtension(id, extension_sync_data);
     extension_service_->CheckForUpdatesSoon();
     return false;
   }
@@ -355,72 +311,39 @@
   return true;
 }
 
-bool ExtensionSyncService::ProcessAppSyncData(
-    const AppSyncData& app_sync_data) {
-  const std::string& id = app_sync_data.id();
+void ExtensionSyncService::ApplyBookmarkAppSyncData(
+    const extensions::ExtensionSyncData& extension_sync_data) {
+  DCHECK(extension_sync_data.is_app());
 
-  if (app_sync_data.app_launch_ordinal().IsValid() &&
-      app_sync_data.page_ordinal().IsValid()) {
-    extension_prefs_->app_sorting()->SetAppLaunchOrdinal(
-        id,
-        app_sync_data.app_launch_ordinal());
-    extension_prefs_->app_sorting()->SetPageOrdinal(
-        id,
-        app_sync_data.page_ordinal());
-  }
-
-  // The corresponding validation of this value during AppSyncData population
-  // is in AppSyncData::PopulateAppSpecifics.
-  if (app_sync_data.launch_type() >= extensions::LAUNCH_TYPE_FIRST &&
-      app_sync_data.launch_type() < extensions::NUM_LAUNCH_TYPES) {
-    extensions::SetLaunchType(profile_, id, app_sync_data.launch_type());
-  }
-
-  if (!app_sync_data.bookmark_app_url().empty())
-    ProcessBookmarkAppSyncData(app_sync_data);
-
-  if (!ProcessExtensionSyncDataHelper(app_sync_data.extension_sync_data(),
-                                      syncer::APPS)) {
-    app_sync_bundle_.AddPendingApp(id, app_sync_data);
-    extension_service_->CheckForUpdatesSoon();
-    return false;
-  }
-
-  return true;
-}
-
-void ExtensionSyncService::ProcessBookmarkAppSyncData(
-    const AppSyncData& app_sync_data) {
   // Process bookmark app sync if necessary.
-  GURL bookmark_app_url(app_sync_data.bookmark_app_url());
+  GURL bookmark_app_url(extension_sync_data.bookmark_app_url());
   if (!bookmark_app_url.is_valid() ||
-      app_sync_data.extension_sync_data().uninstalled()) {
+      extension_sync_data.uninstalled()) {
     return;
   }
 
-  const extensions::Extension* extension =
-      extension_service_->GetInstalledExtension(
-          app_sync_data.extension_sync_data().id());
+  const Extension* extension =
+      extension_service_->GetInstalledExtension(extension_sync_data.id());
 
   // Return if there are no bookmark app details that need updating.
-  if (extension && extension->non_localized_name() ==
-                       app_sync_data.extension_sync_data().name() &&
-      extension->description() == app_sync_data.bookmark_app_description()) {
+  if (extension &&
+      extension->non_localized_name() == extension_sync_data.name() &&
+      extension->description() ==
+          extension_sync_data.bookmark_app_description()) {
     return;
   }
 
   WebApplicationInfo web_app_info;
   web_app_info.app_url = bookmark_app_url;
-  web_app_info.title =
-      base::UTF8ToUTF16(app_sync_data.extension_sync_data().name());
+  web_app_info.title = base::UTF8ToUTF16(extension_sync_data.name());
   web_app_info.description =
-      base::UTF8ToUTF16(app_sync_data.bookmark_app_description());
-  if (!app_sync_data.bookmark_app_icon_color().empty()) {
+      base::UTF8ToUTF16(extension_sync_data.bookmark_app_description());
+  if (!extension_sync_data.bookmark_app_icon_color().empty()) {
     extensions::image_util::ParseCSSColorString(
-        app_sync_data.bookmark_app_icon_color(),
+        extension_sync_data.bookmark_app_icon_color(),
         &web_app_info.generated_icon_color);
   }
-  for (const auto& icon : app_sync_data.linked_icons()) {
+  for (const auto& icon : extension_sync_data.linked_icons()) {
     WebApplicationInfo::IconInfo icon_info;
     icon_info.url = icon.url;
     icon_info.width = icon.size;
@@ -432,7 +355,6 @@
   if (!extension) {
     CreateOrUpdateBookmarkApp(extension_service_, &web_app_info);
   } else {
-    app_sync_data.extension_sync_data().name();
     GetWebApplicationInfoFromApp(profile_,
                                  extension,
                                  base::Bind(&OnWebApplicationInfoLoaded,
@@ -442,8 +364,8 @@
 }
 
 void ExtensionSyncService::SyncOrderingChange(const std::string& extension_id) {
-  const extensions::Extension* ext = extension_service_->GetInstalledExtension(
-      extension_id);
+  const Extension* ext =
+      extension_service_->GetInstalledExtension(extension_id);
 
   if (ext)
     SyncExtensionChangeIfNeeded(*ext);
@@ -454,32 +376,62 @@
   flare_ = flare;
 }
 
-bool ExtensionSyncService::IsCorrectSyncType(const Extension& extension,
-                                         syncer::ModelType type) const {
-  if (type == syncer::EXTENSIONS &&
-      extensions::sync_helper::IsSyncableExtension(&extension)) {
-    return true;
-  }
-
-  if (type == syncer::APPS &&
-      extensions::sync_helper::IsSyncableApp(&extension)) {
-    return true;
-  }
-
-  return false;
-}
-
 bool ExtensionSyncService::IsPendingEnable(
     const std::string& extension_id) const {
   return pending_app_enables_.Contains(extension_id) ||
       pending_extension_enables_.Contains(extension_id);
 }
 
-bool ExtensionSyncService::ProcessExtensionSyncDataHelper(
+SyncBundle* ExtensionSyncService::GetSyncBundle(syncer::ModelType type) {
+  return const_cast<SyncBundle*>(
+      const_cast<const ExtensionSyncService&>(*this).GetSyncBundle(type));
+}
+
+const SyncBundle* ExtensionSyncService::GetSyncBundle(
+    syncer::ModelType type) const {
+  if (type == syncer::APPS)
+    return &app_sync_bundle_;
+  return &extension_sync_bundle_;
+}
+
+std::vector<ExtensionSyncData> ExtensionSyncService::GetSyncDataList(
+    syncer::ModelType type) const {
+  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
+  std::vector<ExtensionSyncData> extension_sync_list;
+  FillSyncDataList(registry->enabled_extensions(), type, &extension_sync_list);
+  FillSyncDataList(registry->disabled_extensions(), type, &extension_sync_list);
+  FillSyncDataList(
+      registry->terminated_extensions(), type, &extension_sync_list);
+
+  std::vector<ExtensionSyncData> pending_extensions =
+      GetSyncBundle(type)->GetPendingData();
+  extension_sync_list.insert(extension_sync_list.begin(),
+                             pending_extensions.begin(),
+                             pending_extensions.end());
+
+  return extension_sync_list;
+}
+
+void ExtensionSyncService::FillSyncDataList(
+    const ExtensionSet& extensions,
+    syncer::ModelType type,
+    std::vector<ExtensionSyncData>* sync_data_list) const {
+  const SyncBundle* bundle = GetSyncBundle(type);
+  for (const scoped_refptr<const Extension>& extension : extensions) {
+    if (IsCorrectSyncType(*extension, type) &&
+        ShouldSync(*extension, profile_) &&
+        bundle->ShouldIncludeInLocalSyncDataList(*extension)) {
+      sync_data_list->push_back(CreateSyncData(*extension));
+    }
+  }
+}
+
+bool ExtensionSyncService::ApplyExtensionSyncDataHelper(
     const ExtensionSyncData& extension_sync_data,
     syncer::ModelType type) {
   const std::string& id = extension_sync_data.id();
-  const Extension* extension = extension_service_->GetInstalledExtension(id);
+  ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
+  const Extension* extension = registry->GetInstalledExtension(id);
 
   // TODO(bolms): we should really handle this better.  The particularly bad
   // case is where an app becomes an extension or vice versa, and we end up with
@@ -498,26 +450,28 @@
 
   // Extension from sync was uninstalled by the user as external extensions.
   // Honor user choice and skip installation/enabling.
-  if (extensions::ExtensionPrefs::Get(profile_)
-          ->IsExternalExtensionUninstalled(id)) {
+  if (ExtensionPrefs::Get(profile_)->IsExternalExtensionUninstalled(id)) {
     LOG(WARNING) << "Extension with id " << id
                  << " from sync was uninstalled as external extension";
     return true;
   }
 
+  int version_compare_result = extension ?
+      extension->version()->CompareTo(extension_sync_data.version()) : 0;
+
   // Set user settings.
   if (extension_sync_data.enabled()) {
+    DCHECK(!extension_sync_data.disable_reasons());
+
     // Only grant permissions if the sync data explicitly sets the disable
     // reasons to Extension::DISABLE_NONE (as opposed to the legacy (<M45) case
     // where they're not set at all), and if the version from sync matches our
     // local one. Otherwise we just enable it without granting permissions. If
     // any permissions are missing, CheckPermissionsIncrease will soon disable
     // it again.
-    DCHECK(!extension_sync_data.disable_reasons());
     bool grant_permissions =
         extension_sync_data.supports_disable_reasons() &&
-        extension &&
-        extension->version()->Equals(extension_sync_data.version());
+        extension && (version_compare_result == 0);
     if (grant_permissions)
       extension_service_->GrantPermissionsAndEnableExtension(extension);
     else
@@ -540,17 +494,14 @@
     // In the non-legacy case (>=M45), clear any existing disable reasons first.
     // Otherwise sync can't remove just some of them.
     if (extension_sync_data.supports_disable_reasons())
-      extensions::ExtensionPrefs::Get(profile_)->ClearDisableReasons(id);
+      ExtensionPrefs::Get(profile_)->ClearDisableReasons(id);
 
     extension_service_->DisableExtension(id, disable_reasons);
   }
 
-  // We need to cache some version information here because setting the
-  // incognito flag invalidates the |extension| pointer (it reloads the
-  // extension).
+  // We need to cache some information here because setting the incognito flag
+  // invalidates the |extension| pointer (it reloads the extension).
   bool extension_installed = (extension != NULL);
-  int version_compare_result = extension ?
-      extension->version()->CompareTo(extension_sync_data.version()) : 0;
 
   // If the target extension has already been installed ephemerally, it can
   // be promoted to a regular installed extension and downloading from the Web
@@ -601,18 +552,3 @@
 
   return true;
 }
-
-void ExtensionSyncService::SyncExtensionChangeIfNeeded(
-    const Extension& extension) {
-  if (extensions::util::ShouldSyncApp(&extension, profile_)) {
-    if (app_sync_bundle_.IsSyncing())
-      app_sync_bundle_.SyncChangeIfNeeded(extension);
-    else if (extension_service_->is_ready() && !flare_.is_null())
-      flare_.Run(syncer::APPS);
-  } else if (extensions::util::ShouldSyncExtension(&extension, profile_)) {
-    if (extension_sync_bundle_.IsSyncing())
-      extension_sync_bundle_.SyncChangeIfNeeded(extension);
-    else if (extension_service_->is_ready() && !flare_.is_null())
-      flare_.Run(syncer::EXTENSIONS);
-  }
-}
diff --git a/chrome/browser/extensions/extension_sync_service.h b/chrome/browser/extensions/extension_sync_service.h
index cd180e45..270aad1 100644
--- a/chrome/browser/extensions/extension_sync_service.h
+++ b/chrome/browser/extensions/extension_sync_service.h
@@ -8,29 +8,24 @@
 #include <string>
 #include <vector>
 
-#include "base/compiler_specific.h"
-#include "chrome/browser/extensions/app_sync_bundle.h"
-#include "chrome/browser/extensions/extension_sync_bundle.h"
-#include "chrome/browser/extensions/extension_sync_data.h"
 #include "chrome/browser/extensions/pending_enables.h"
+#include "chrome/browser/extensions/sync_bundle.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/common/extension.h"
-#include "sync/api/string_ordinal.h"
-#include "sync/api/sync_change.h"
 #include "sync/api/syncable_service.h"
 
 class Profile;
 
-namespace base {
-class SequencedTaskRunner;
-}
-
 namespace extensions {
-class AppSyncData;
+class Extension;
+class ExtensionSet;
+class ExtensionSyncData;
 }  // namespace extensions
 
 namespace syncer {
+class SyncChange;
+class SyncChangeProcessor;
 class SyncErrorFactory;
 }
 
@@ -46,14 +41,24 @@
   // Convenience function to get the ExtensionSyncService for a BrowserContext.
   static ExtensionSyncService* Get(content::BrowserContext* context);
 
-  const extensions::ExtensionPrefs& extension_prefs() const {
-    return *extension_prefs_;
-  }
+  // Extracts the data needed to sync the uninstall of |extension|, but doesn't
+  // actually sync anything now. Call |ProcessSyncUninstallExtension| later with
+  // the returned SyncData to actually commit the change.
+  syncer::SyncData PrepareToSyncUninstallExtension(
+      const extensions::Extension& extension);
+  // Commit a sync uninstall that was previously prepared with
+  // PrepareToSyncUninstallExtension.
+  void ProcessSyncUninstallExtension(const std::string& extension_id,
+                                     const syncer::SyncData& sync_data);
+
+  void SyncEnableExtension(const extensions::Extension& extension);
+  void SyncDisableExtension(const extensions::Extension& extension);
+
+  void SyncOrderingChange(const std::string& extension_id);
 
   // Notifies Sync (if needed) of a newly-installed extension or a change to
   // an existing extension.
-  virtual void SyncExtensionChangeIfNeeded(
-      const extensions::Extension& extension);
+  void SyncExtensionChangeIfNeeded(const extensions::Extension& extension);
 
   // syncer::SyncableService implementation.
   syncer::SyncMergeResult MergeDataAndStartSyncing(
@@ -67,67 +72,49 @@
       const tracked_objects::Location& from_here,
       const syncer::SyncChangeList& change_list) override;
 
-  // Gets the sync data for the given extension, assuming that the extension is
-  // syncable.
-  extensions::ExtensionSyncData GetExtensionSyncData(
+  // Creates the ExtensionSyncData for the given app/extension.
+  extensions::ExtensionSyncData CreateSyncData(
       const extensions::Extension& extension) const;
 
-  // Gets the sync data for the given app, assuming that the app is
-  // syncable.
-  extensions::AppSyncData GetAppSyncData(
-      const extensions::Extension& extension) const;
-
-  // Gets the ExtensionSyncData for all extensions.
-  std::vector<extensions::ExtensionSyncData> GetExtensionSyncDataList() const;
-
-  // Gets the AppSyncData for all extensions.
-  std::vector<extensions::AppSyncData> GetAppSyncDataList() const;
-
-  // Applies the change specified passed in by either ExtensionSyncData or
-  // AppSyncData to the current system.
+  // Applies the change specified passed in by either ExtensionSyncData to the
+  // current system.
   // Returns false if the changes were not completely applied and were added
   // to the pending list to be tried again.
-  bool ProcessExtensionSyncData(
-      const extensions::ExtensionSyncData& extension_sync_data);
-  bool ProcessAppSyncData(const extensions::AppSyncData& app_sync_data);
-
-  // Processes the bookmark app specific parts of an AppSyncData.
-  void ProcessBookmarkAppSyncData(const extensions::AppSyncData& app_sync_data);
-
-  syncer::SyncChange PrepareToSyncUninstallExtension(
-      const extensions::Extension* extension,
-      bool extensions_ready);
-  void ProcessSyncUninstallExtension(const std::string& extension_id,
-                                     const syncer::SyncChange& sync_change);
-
-  void SyncEnableExtension(const extensions::Extension& extension);
-  void SyncDisableExtension(const extensions::Extension& extension);
-
-  void SyncOrderingChange(const std::string& extension_id);
+  bool ApplySyncData(const extensions::ExtensionSyncData& extension_sync_data);
 
   // |flare| provides a StartSyncFlare to the SyncableService. See
-  // sync_start_util for more.
+  // sync_start_util for more. Public for testing.
   void SetSyncStartFlare(const syncer::SyncableService::StartSyncFlare& flare);
 
-  Profile* profile() { return profile_; }
-
  private:
-  // Return true if the sync type of |extension| matches |type|.
-  bool IsCorrectSyncType(const extensions::Extension& extension,
-                         syncer::ModelType type)
-      const;
-
   // Whether the given extension has been enabled before sync has started.
   bool IsPendingEnable(const std::string& extension_id) const;
 
-  // Handles setting the extension specific values in |extension_sync_data| to
+  // Gets the SyncBundle for the given |type|.
+  extensions::SyncBundle* GetSyncBundle(syncer::ModelType type);
+  const extensions::SyncBundle* GetSyncBundle(syncer::ModelType type) const;
+
+  // Gets the ExtensionSyncData for all apps or extensions.
+  std::vector<extensions::ExtensionSyncData> GetSyncDataList(
+      syncer::ModelType type) const;
+
+  void FillSyncDataList(
+      const extensions::ExtensionSet& extensions,
+      syncer::ModelType type,
+      std::vector<extensions::ExtensionSyncData>* sync_data_list) const;
+
+  // Handles applying the extension specific values in |extension_sync_data| to
   // the current system.
   // Returns false if the changes were not completely applied and need to be
   // tried again later.
-  bool ProcessExtensionSyncDataHelper(
+  bool ApplyExtensionSyncDataHelper(
       const extensions::ExtensionSyncData& extension_sync_data,
       syncer::ModelType type);
 
+  // Processes the bookmark app specific parts of an AppSyncData.
+  void ApplyBookmarkAppSyncData(
+      const extensions::ExtensionSyncData& extension_sync_data);
+
   // The normal profile associated with this ExtensionService.
   Profile* profile_;
 
@@ -136,16 +123,15 @@
 
   ExtensionService* extension_service_;
 
-  extensions::AppSyncBundle app_sync_bundle_;
-  extensions::ExtensionSyncBundle extension_sync_bundle_;
+  extensions::SyncBundle app_sync_bundle_;
+  extensions::SyncBundle extension_sync_bundle_;
 
   // Set of extensions/apps that have been enabled before sync has started.
+  // TODO(treib,kalman): This seems wrong. Why are enables special, as opposed
+  // to disables, or any other changes?
   extensions::PendingEnables pending_app_enables_;
   extensions::PendingEnables pending_extension_enables_;
 
-  // Sequenced task runner for extension related file operations.
-  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
-
   // Run()ning tells sync to try and start soon, because syncable changes
   // have started happening. It will cause sync to call us back
   // asynchronously via MergeDataAndStartSyncing as soon as possible.
diff --git a/chrome/browser/extensions/pending_enables.cc b/chrome/browser/extensions/pending_enables.cc
index 64558c5..a1de945 100644
--- a/chrome/browser/extensions/pending_enables.cc
+++ b/chrome/browser/extensions/pending_enables.cc
@@ -21,12 +21,12 @@
 PendingEnables::~PendingEnables() {
 }
 
-void PendingEnables::OnExtensionEnabled(const std::string& extension_id) {
+void PendingEnables::Add(const std::string& extension_id) {
   if (IsWaitingForSync())
     ids_.insert(extension_id);
 }
 
-void PendingEnables::OnExtensionDisabled(const std::string& extension_id) {
+void PendingEnables::Remove(const std::string& extension_id) {
   if (IsWaitingForSync())
     ids_.erase(extension_id);
 }
@@ -36,7 +36,7 @@
        it != ids_.end(); ++it) {
     const Extension* extension = service->GetExtensionById(*it, true);
     if (extension)
-      sync_bundle_->SyncChangeIfNeeded(*extension);
+      sync_bundle_->PushSyncChangeIfNeeded(*extension);
   }
   ids_.clear();
 }
diff --git a/chrome/browser/extensions/pending_enables.h b/chrome/browser/extensions/pending_enables.h
index fb1a16b..c8e59b8 100644
--- a/chrome/browser/extensions/pending_enables.h
+++ b/chrome/browser/extensions/pending_enables.h
@@ -37,8 +37,8 @@
   // Called when an extension is enabled / disabled locally.
   // These will check the sync state and figure out whether the change
   // needs to be remembered for syncing when syncing starts.
-  void OnExtensionEnabled(const std::string& extension_id);
-  void OnExtensionDisabled(const std::string& extension_id);
+  void Add(const std::string& extension_id);
+  void Remove(const std::string& extension_id);
 
   // Called when |sync_bundle_| is ready to accept sync changes.
   // Uses |service| to look up extensions from extension ids.
diff --git a/chrome/browser/extensions/sync_bundle.cc b/chrome/browser/extensions/sync_bundle.cc
new file mode 100644
index 0000000..4887b0a
--- /dev/null
+++ b/chrome/browser/extensions/sync_bundle.cc
@@ -0,0 +1,149 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/sync_bundle.h"
+
+#include "base/location.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
+#include "chrome/browser/extensions/extension_sync_service.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "extensions/common/extension.h"
+
+namespace extensions {
+
+SyncBundle::SyncBundle(ExtensionSyncService* sync_service)
+    : sync_service_(sync_service) {}
+
+SyncBundle::~SyncBundle() {}
+
+void SyncBundle::MergeDataAndStartSyncing(
+    const syncer::SyncDataList& initial_sync_data,
+    scoped_ptr<syncer::SyncChangeProcessor> sync_processor) {
+  sync_processor_.reset(sync_processor.release());
+
+  for (const syncer::SyncData& sync_data : initial_sync_data) {
+    scoped_ptr<ExtensionSyncData> extension_sync_data(
+        ExtensionSyncData::CreateFromSyncData(sync_data));
+    if (extension_sync_data.get()) {
+      AddSyncedExtension(extension_sync_data->id());
+      sync_service_->ApplySyncData(*extension_sync_data);
+    }
+  }
+}
+
+void SyncBundle::Reset() {
+  sync_processor_.reset();
+  synced_extensions_.clear();
+  pending_sync_data_.clear();
+}
+
+bool SyncBundle::IsSyncing() const {
+  return sync_processor_ != nullptr;
+}
+
+bool SyncBundle::HasExtensionId(const std::string& id) const {
+  return synced_extensions_.find(id) != synced_extensions_.end();
+}
+
+bool SyncBundle::ShouldIncludeInLocalSyncDataList(
+    const Extension& extension) const {
+  // If there is pending data for this extension, then this version is out of
+  // date. We'll sync back the version we got from sync.
+  return IsSyncing() && !HasPendingExtensionId(extension.id());
+}
+
+void SyncBundle::PushSyncDataList(
+    const syncer::SyncDataList& sync_data_list) {
+  syncer::SyncChangeList sync_change_list;
+  for (const syncer::SyncData& sync_data : sync_data_list) {
+    const syncer::SyncDataLocal sync_data_local(sync_data);
+    const std::string& extension_id = sync_data_local.GetTag();
+
+    sync_change_list.push_back(CreateSyncChange(extension_id, sync_data));
+
+    AddSyncedExtension(extension_id);
+  }
+
+  PushSyncChanges(sync_change_list);
+}
+
+void SyncBundle::PushSyncDeletion(const std::string& extension_id,
+                                  const syncer::SyncData& sync_data) {
+  RemoveSyncedExtension(extension_id);
+  PushSyncChanges(syncer::SyncChangeList(1,
+      syncer::SyncChange(FROM_HERE,
+                         syncer::SyncChange::ACTION_DELETE,
+                         sync_data)));
+}
+
+void SyncBundle::PushSyncChangeIfNeeded(const Extension& extension) {
+  syncer::SyncChangeList sync_change_list(
+      1,
+      CreateSyncChange(extension.id(),
+                       sync_service_->CreateSyncData(extension).GetSyncData()));
+  PushSyncChanges(sync_change_list);
+  MarkPendingExtensionSynced(extension.id());
+}
+
+void SyncBundle::ApplySyncChange(const syncer::SyncChange& sync_change) {
+  scoped_ptr<ExtensionSyncData> extension_sync_data(
+      ExtensionSyncData::CreateFromSyncChange(sync_change));
+  if (!extension_sync_data.get())
+    return;  // TODO(treib,kalman): Warning message?
+
+  if (extension_sync_data->uninstalled())
+    RemoveSyncedExtension(extension_sync_data->id());
+  else
+    AddSyncedExtension(extension_sync_data->id());
+  sync_service_->ApplySyncData(*extension_sync_data);
+}
+
+bool SyncBundle::HasPendingExtensionId(const std::string& id) const {
+  return pending_sync_data_.find(id) != pending_sync_data_.end();
+}
+
+void SyncBundle::AddPendingExtension(
+    const std::string& id,
+    const ExtensionSyncData& extension_sync_data) {
+  pending_sync_data_.insert(std::make_pair(id, extension_sync_data));
+}
+
+std::vector<ExtensionSyncData> SyncBundle::GetPendingData() const {
+  std::vector<ExtensionSyncData> pending_extensions;
+  for (const auto& data : pending_sync_data_)
+    pending_extensions.push_back(data.second);
+
+  return pending_extensions;
+}
+
+syncer::SyncChange SyncBundle::CreateSyncChange(
+    const std::string& extension_id,
+    const syncer::SyncData& sync_data) const {
+  return syncer::SyncChange(
+      FROM_HERE,
+      HasExtensionId(extension_id) ? syncer::SyncChange::ACTION_UPDATE
+                                   : syncer::SyncChange::ACTION_ADD,
+      sync_data);
+}
+
+void SyncBundle::PushSyncChanges(
+    const syncer::SyncChangeList& sync_change_list) {
+  sync_processor_->ProcessSyncChanges(FROM_HERE, sync_change_list);
+}
+
+void SyncBundle::AddSyncedExtension(const std::string& id) {
+  synced_extensions_.insert(id);
+}
+
+void SyncBundle::RemoveSyncedExtension(const std::string& id) {
+  synced_extensions_.erase(id);
+}
+
+void SyncBundle::MarkPendingExtensionSynced(const std::string& id) {
+  pending_sync_data_.erase(id);
+  AddSyncedExtension(id);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/sync_bundle.h b/chrome/browser/extensions/sync_bundle.h
index e37465c4..8ee58cf 100644
--- a/chrome/browser/extensions/sync_bundle.h
+++ b/chrome/browser/extensions/sync_bundle.h
@@ -5,20 +5,96 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_SYNC_BUNDLE_H_
 #define CHROME_BROWSER_EXTENSIONS_SYNC_BUNDLE_H_
 
+#include <set>
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "sync/api/sync_change.h"
+#include "sync/api/sync_change_processor.h"
+#include "sync/api/sync_data.h"
+
+class ExtensionSyncService;
+
 namespace extensions {
 
 class Extension;
+class ExtensionSyncData;
 
-// Common interface between AppSyncBundle and ExtensionSyncBundle.
 class SyncBundle {
  public:
-  virtual ~SyncBundle() {}
+  explicit SyncBundle(ExtensionSyncService* sync_service);
+  ~SyncBundle();
+
+  void MergeDataAndStartSyncing(
+      const syncer::SyncDataList& initial_sync_data,
+      scoped_ptr<syncer::SyncChangeProcessor> sync_processor);
+
+  // Resets this class back to its default values, which will disable all
+  // syncing until StartSyncing is called again.
+  void Reset();
 
   // Has this bundle started syncing yet?
-  virtual bool IsSyncing() const = 0;
+  // Returns true if MergeDataAndStartSyncing has been called, false otherwise.
+  bool IsSyncing() const;
 
-  // Syncs changes to |extension|.
-  virtual void SyncChangeIfNeeded(const Extension& extension) = 0;
+  // Checks if the extension with the given |id| is synced.
+  bool HasExtensionId(const std::string& id) const;
+
+  // Whether the given extension should be included in the SyncDataList to be
+  // sent to the server. Returns false if there is pending data that should be
+  // used instead.
+  bool ShouldIncludeInLocalSyncDataList(const Extension& extension) const;
+
+  // Handles the given list of local SyncDatas. This updates the set of synced
+  // extensions as appropriate, and then pushes the corresponding SyncChanges
+  // to the server.
+  void PushSyncDataList(const syncer::SyncDataList& sync_data_list);
+
+  // Handles the sync deletion of the given extension. This updates the set of
+  // synced extensions as appropriate, and then pushes a SyncChange to the
+  // server.
+  void PushSyncDeletion(const std::string& extension_id,
+                        const syncer::SyncData& sync_data);
+
+  // Pushes any sync changes to |extension| to the server.
+  void PushSyncChangeIfNeeded(const Extension& extension);
+
+  // Applies the given SyncChange coming from the server.
+  void ApplySyncChange(const syncer::SyncChange& sync_change);
+
+  // Checks if the extension with the given |id| is pending to be synced.
+  bool HasPendingExtensionId(const std::string& id) const;
+
+  // Adds a pending extension to be synced.
+  void AddPendingExtension(const std::string& id,
+                           const ExtensionSyncData& sync_data);
+
+  // Returns a vector of all the pending sync data.
+  std::vector<ExtensionSyncData> GetPendingData() const;
+
+ private:
+  // Creates a SyncChange to add or update an extension.
+  syncer::SyncChange CreateSyncChange(const std::string& extension_id,
+                                      const syncer::SyncData& sync_data) const;
+
+  // Pushes the given list of SyncChanges to the server.
+  void PushSyncChanges(const syncer::SyncChangeList& sync_change_list);
+
+  void AddSyncedExtension(const std::string& id);
+  void RemoveSyncedExtension(const std::string& id);
+
+  // Changes an extension from being pending to synced.
+  void MarkPendingExtensionSynced(const std::string& id);
+
+  ExtensionSyncService* sync_service_;  // Owns us.
+
+  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
+
+  std::set<std::string> synced_extensions_;
+
+  std::map<std::string, ExtensionSyncData> pending_sync_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncBundle);
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
index 48808b6..b24a7dd 100644
--- a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
@@ -5,9 +5,9 @@
 #include "base/basictypes.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/app_sync_data.h"
 #include "chrome/browser/extensions/bookmark_app_helper.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
 #include "chrome/browser/extensions/extension_sync_service.h"
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -400,23 +400,23 @@
   ExtensionSyncService* extension_sync_service =
       ExtensionSyncService::Get(GetProfile(1));
 
-  extensions::AppSyncData original_data(
-      extension_sync_service->GetAppSyncData(*extension));
+  extensions::ExtensionSyncData original_data(
+      extension_sync_service->CreateSyncData(*extension));
 
   // Create an invalid launch type and ensure it doesn't get down-synced. This
   // simulates the case of a future launch type being added which old versions
   // don't yet understand.
-  extensions::AppSyncData invalid_launch_type_data(
+  extensions::ExtensionSyncData invalid_launch_type_data(
       *extension,
-      original_data.extension_sync_data().enabled(),
-      original_data.extension_sync_data().disable_reasons(),
-      original_data.extension_sync_data().incognito_enabled(),
-      original_data.extension_sync_data().remote_install(),
-      original_data.extension_sync_data().all_urls_enabled(),
+      original_data.enabled(),
+      original_data.disable_reasons(),
+      original_data.incognito_enabled(),
+      original_data.remote_install(),
+      original_data.all_urls_enabled(),
       original_data.app_launch_ordinal(),
       original_data.page_ordinal(),
       extensions::NUM_LAUNCH_TYPES);
-  extension_sync_service->ProcessAppSyncData(invalid_launch_type_data);
+  extension_sync_service->ApplySyncData(invalid_launch_type_data);
 
   // The launch type should remain the same.
   ASSERT_TRUE(AwaitAllProfilesHaveSameApps());
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index bcf8acf..4e49c16 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -536,10 +536,6 @@
       'browser/extensions/app_icon_loader.h',
       'browser/extensions/app_icon_loader_impl.cc',
       'browser/extensions/app_icon_loader_impl.h',
-      'browser/extensions/app_sync_bundle.cc',
-      'browser/extensions/app_sync_bundle.h',
-      'browser/extensions/app_sync_data.cc',
-      'browser/extensions/app_sync_data.h',
       'browser/extensions/blacklist.cc',
       'browser/extensions/blacklist.h',
       'browser/extensions/blacklist_factory.cc',
@@ -691,8 +687,6 @@
       'browser/extensions/extension_storage_monitor.h',
       'browser/extensions/extension_storage_monitor_factory.cc',
       'browser/extensions/extension_storage_monitor_factory.h',
-      'browser/extensions/extension_sync_bundle.cc',
-      'browser/extensions/extension_sync_bundle.h',
       'browser/extensions/extension_sync_data.cc',
       'browser/extensions/extension_sync_data.h',
       'browser/extensions/extension_sync_service.cc',
@@ -819,6 +813,7 @@
       'browser/extensions/state_store_notification_observer.h',
       'browser/extensions/suspicious_extension_bubble_controller.cc',
       'browser/extensions/suspicious_extension_bubble_controller.h',
+      'browser/extensions/sync_bundle.cc',
       'browser/extensions/sync_bundle.h',
       'browser/extensions/tab_helper.cc',
       'browser/extensions/tab_helper.h',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index d638fbeb..55ee458 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -711,7 +711,6 @@
       'browser/extensions/api/web_request/web_request_api_unittest.cc',
       'browser/extensions/api/web_request/web_request_permissions_unittest.cc',
       'browser/extensions/app_data_migrator_unittest.cc',
-      'browser/extensions/app_sync_data_unittest.cc',
       'browser/extensions/blacklist_state_fetcher_unittest.cc',
       'browser/extensions/bookmark_app_helper_unittest.cc',
       'browser/extensions/chrome_app_sorting_unittest.cc',