1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_UI_APP_LIST_APP_LIST_SYNCABLE_SERVICE_H_ 6 #define CHROME_BROWSER_UI_APP_LIST_APP_LIST_SYNCABLE_SERVICE_H_ 7 8 #include <stddef.h> 9 10 #include <map> 11 #include <memory> 12 #include <string> 13 #include <vector> 14 15 #include "base/callback.h" 16 #include "base/memory/weak_ptr.h" 17 #include "base/observer_list.h" 18 #include "base/one_shot_event.h" 19 #include "build/build_config.h" 20 #include "chrome/browser/sync/glue/sync_start_util.h" 21 #include "components/keyed_service/core/keyed_service.h" 22 #include "components/sync/model/string_ordinal.h" 23 #include "components/sync/model/sync_change.h" 24 #include "components/sync/model/sync_change_processor.h" 25 #include "components/sync/model/sync_error_factory.h" 26 #include "components/sync/model/syncable_service.h" 27 #include "components/sync/protocol/app_list_specifics.pb.h" 28 29 class AppListModelUpdater; 30 class AppServiceAppModelBuilder; 31 class ChromeAppListItem; 32 class Profile; 33 34 namespace extensions { 35 class ExtensionRegistry; 36 class ExtensionSystem; 37 } 38 39 namespace sync_pb { 40 class AppListSpecifics; 41 } 42 43 namespace user_prefs { 44 class PrefRegistrySyncable; 45 } 46 47 namespace app_list { 48 49 // Keyed Service that owns, stores, and syncs an AppListModel for a profile. 50 class AppListSyncableService : public syncer::SyncableService, 51 public KeyedService { 52 public: 53 struct SyncItem { 54 SyncItem(const std::string& id, 55 sync_pb::AppListSpecifics::AppListItemType type); 56 ~SyncItem(); 57 const std::string item_id; 58 sync_pb::AppListSpecifics::AppListItemType item_type; 59 std::string item_name; 60 std::string parent_id; 61 syncer::StringOrdinal item_ordinal; 62 syncer::StringOrdinal item_pin_ordinal; 63 64 std::string ToString() const; 65 }; 66 67 class Observer { 68 public: 69 // Notifies that sync model was updated. 70 virtual void OnSyncModelUpdated() = 0; 71 72 protected: 73 virtual ~Observer() = default; 74 }; 75 76 // An app list model updater factory function used by tests. 77 using ModelUpdaterFactoryCallback = 78 base::Callback<std::unique_ptr<AppListModelUpdater>()>; 79 80 // Sets and resets an app list model updater factory function for tests. 81 class ScopedModelUpdaterFactoryForTest { 82 public: 83 explicit ScopedModelUpdaterFactoryForTest( 84 const ModelUpdaterFactoryCallback& factory); 85 ScopedModelUpdaterFactoryForTest(const ScopedModelUpdaterFactoryForTest&) = 86 delete; 87 ScopedModelUpdaterFactoryForTest& operator=( 88 const ScopedModelUpdaterFactoryForTest&) = delete; 89 ~ScopedModelUpdaterFactoryForTest(); 90 91 private: 92 ModelUpdaterFactoryCallback factory_; 93 }; 94 95 using SyncItemMap = std::map<std::string, std::unique_ptr<SyncItem>>; 96 97 // Populates the model when |profile|'s extension system is ready. 98 explicit AppListSyncableService(Profile* profile); 99 AppListSyncableService(const AppListSyncableService&) = delete; 100 AppListSyncableService& operator=(const AppListSyncableService&) = delete; 101 ~AppListSyncableService() override; 102 103 // Registers prefs to support local storage. 104 static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); 105 106 // Some sync behavior depends on whether or not an app was installed by 107 // default (as opposed to e.g. installed via explicit user action). Some 108 // tests want the AppListSyncableService to consider an app to be installed 109 // by default, without going through the heavyweight process of completely 110 // installing an app. These functions facilitate that. 111 static bool AppIsDefaultForTest(Profile* profile, const std::string& id); 112 static void SetAppIsDefaultForTest(Profile* profile, const std::string& id); 113 114 // Adds |item| to |sync_items_| and |model_|. If a sync item already exists, 115 // updates the existing sync item instead. 116 void AddItem(std::unique_ptr<ChromeAppListItem> app_item); 117 118 // Removes sync item matching |id|. 119 void RemoveItem(const std::string& id); 120 121 // Removes sync item matching |id| after item uninstall. 122 void RemoveUninstalledItem(const std::string& id); 123 124 // Called when properties of an item may have changed, e.g. default/oem state. 125 void UpdateItem(const ChromeAppListItem* app_item); 126 127 // Returns the existing sync item matching |id| or NULL. 128 const SyncItem* GetSyncItem(const std::string& id) const; 129 130 // Transfers app attributes, such as parent folder id, position in App 131 // Launcher and pin position on the shelf from one app to another app. Target 132 // app defined by |to_app_id| is not required to be present at call time. In 133 // which case attributes would be applied once the target app appears on the 134 // device. Note, pending attributes are not preserved between the user 135 // sessions. This functionality is primarily used for migrating app in case 136 // app id is changed but it is required to preserve position in App Launcher 137 // and in shelf. 138 // Returns true on success and false in case app defined by |from_app_id| 139 // does not exist. 140 bool TransferItemAttributes(const std::string& from_app_id, 141 const std::string& to_app_id); 142 143 // Sets the name of the folder for OEM apps. 144 void SetOemFolderName(const std::string& name); 145 146 // Returns optional pin position for the app specified by |app_id|. If app is 147 // not synced or does not have associated pin position then empty ordinal is 148 // returned. 149 syncer::StringOrdinal GetPinPosition(const std::string& app_id); 150 151 // Sets pin position and how it is pinned for the app specified by |app_id|. 152 // Empty |item_pin_ordinal| indicates that the app has no pin. 153 void SetPinPosition(const std::string& app_id, 154 const syncer::StringOrdinal& item_pin_ordinal); 155 156 // Gets the app list model updater. 157 AppListModelUpdater* GetModelUpdater(); 158 159 // Returns true if this service was initialized. 160 bool IsInitialized() const; 161 162 // Signalled when AppListSyncableService is Initialized. on_initialized()163 const base::OneShotEvent& on_initialized() const { return on_initialized_; } 164 165 // Returns true if sync was started. 166 bool IsSyncing() const; 167 168 // Registers new observers and makes sure that service is started. 169 void AddObserverAndStart(Observer* observer); 170 void RemoveObserver(Observer* observer); 171 profile()172 Profile* profile() { return profile_; } 173 size_t GetNumSyncItemsForTest(); GetOemFolderNameForTest()174 const std::string& GetOemFolderNameForTest() const { 175 return oem_folder_name_; 176 } 177 178 void InstallDefaultPageBreaksForTest(); 179 sync_items()180 const SyncItemMap& sync_items() const { return sync_items_; } 181 182 // syncer::SyncableService 183 void WaitUntilReadyToSync(base::OnceClosure done) override; 184 base::Optional<syncer::ModelError> MergeDataAndStartSyncing( 185 syncer::ModelType type, 186 const syncer::SyncDataList& initial_sync_data, 187 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor, 188 std::unique_ptr<syncer::SyncErrorFactory> error_handler) override; 189 void StopSyncing(syncer::ModelType type) override; 190 syncer::SyncDataList GetAllSyncDataForTesting() const; 191 base::Optional<syncer::ModelError> ProcessSyncChanges( 192 const base::Location& from_here, 193 const syncer::SyncChangeList& change_list) override; 194 195 // KeyedService 196 void Shutdown() override; 197 198 private: 199 class ModelUpdaterObserver; 200 201 // Builds the model once ExtensionService is ready. 202 void BuildModel(); 203 204 // Returns true if sync has restarted, otherwise runs |flare_|. 205 bool SyncStarted(); 206 207 // If |app_item| matches an existing sync item, returns it. Otherwise adds 208 // |app_item| to |sync_items_| and returns the new item. If |app_item| is 209 // invalid returns NULL. 210 SyncItem* FindOrAddSyncItem(const ChromeAppListItem* app_item); 211 212 // Creates a sync item for |app_item| and sends an ADD SyncChange event. 213 SyncItem* CreateSyncItemFromAppItem(const ChromeAppListItem* app_item); 214 215 // If a sync item for |app_item| already exists, update |app_item| from the 216 // sync item, otherwise create a new sync item from |app_item|. 217 void AddOrUpdateFromSyncItem(const ChromeAppListItem* app_item); 218 219 // Either uninstalling a default app or remove the REMOVE_DEFAULT sync item. 220 // Returns true if the app is removed. Otherwise deletes the existing sync 221 // item and returns false. 222 bool RemoveDefaultApp(const ChromeAppListItem* item, SyncItem* sync_item); 223 224 // Returns whether the delete-sync-item request was for a default app. If 225 // true, the |sync_item| is set to REMOVE_DEFAULT and bounced back to the 226 // sync server. The caller should abort deleting the |sync_item|. 227 bool InterceptDeleteDefaultApp(SyncItem* sync_item); 228 229 // Deletes a sync item from |sync_items_| and sends a DELETE action. 230 void DeleteSyncItem(const std::string& item_id); 231 232 // Updates existing entry in |sync_items_| from |app_item|. 233 void UpdateSyncItem(const ChromeAppListItem* app_item); 234 235 // Removes sync item matching |id|. 236 void RemoveSyncItem(const std::string& id); 237 238 // Updates folder items that may get created during initial sync. 239 void ResolveFolderPositions(); 240 241 // Removes any empty SyncItem folders and deletes them from sync. Called 242 // after a sync item is removed (which may result in an empty folder). 243 void PruneEmptySyncFolders(); 244 245 // Creates or updates a SyncItem from |specifics|. Returns true if a new item 246 // was created. 247 // TODO(crbug.com/1057577): Change return type to void. 248 bool ProcessSyncItemSpecifics(const sync_pb::AppListSpecifics& specifics); 249 250 // Handles a newly created sync item (e.g. creates a new AppItem and adds it 251 // to the model or uninstalls a deleted default item. 252 void ProcessNewSyncItem(SyncItem* sync_item); 253 254 // Handles an existing sync item. 255 void ProcessExistingSyncItem(SyncItem* sync_item); 256 257 // Sends ADD or CHANGED for sync item. 258 void SendSyncChange(SyncItem* sync_item, 259 syncer::SyncChange::SyncChangeType sync_change_type); 260 261 // Returns an existing SyncItem corresponding to |item_id| or NULL. 262 SyncItem* FindSyncItem(const std::string& item_id); 263 264 // Creates a new sync item for |item_id|. 265 SyncItem* CreateSyncItem( 266 const std::string& item_id, 267 sync_pb::AppListSpecifics::AppListItemType item_type); 268 269 // Deletes a SyncItem matching |specifics|. 270 void DeleteSyncItemSpecifics(const sync_pb::AppListSpecifics& specifics); 271 272 // Gets the preferred location for the OEM folder. It may return an invalid 273 // position and the final OEM folder position will be determined in the 274 // AppListModel. 275 syncer::StringOrdinal GetPreferredOemFolderPos(); 276 277 // Returns true if an extension matching |id| exists and was installed by 278 // an OEM (extension->was_installed_by_oem() is true). 279 bool AppIsOem(const std::string& id); 280 281 // Initializes sync items from the local storage while sync service is not 282 // enabled. 283 void InitFromLocalStorage(); 284 285 // Helper that notifies observers that sync model has been updated. 286 void NotifyObserversSyncUpdated(); 287 288 // Handles model update start/finish. 289 void HandleUpdateStarted(); 290 void HandleUpdateFinished(bool clean_up_after_init_sync); 291 292 // Cleans up the folder sync item with only one item in it. 293 // There are some edge cases with synch which will create a folder with only 294 // one item in it, which is not legitimate and the folder should be removed. 295 // We will find such folders after the initial sync and clean them up. 296 void CleanUpSingleItemSyncFolder(); 297 298 // Returns child item if |sync_item| is a user created folder with only one 299 // child item in it; otherwise, returns nullptr. 300 SyncItem* GetOnlyChildOfUserCreatedFolder(SyncItem* sync_item); 301 302 // Returns true if |sync_item| is a user created folder with only one 303 // child item in it, the child item will be removed out of the folder and 304 // place at the same location of its original folder. 305 // Otherwise, return false, no change will be made. 306 bool RemoveOnlyChildOutOfUserCreatedFolderIfNecessary(SyncItem* sync_item); 307 308 // Returns true if extension service is ready. 309 bool IsExtensionServiceReady() const; 310 311 // Returns a list of top level sync items sorted by item ordinal. 312 std::vector<SyncItem*> GetSortedTopLevelSyncItems() const; 313 314 // Remove leading, trailing and duplicate "page break" items in sorted top 315 // level item list. 316 void PruneRedundantPageBreakItems(); 317 318 // Installs the default page break items. This is only called for first time 319 // users. 320 void InstallDefaultPageBreaks(); 321 322 // Applies sync changes to the local item. 323 void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics, 324 AppListSyncableService::SyncItem* item); 325 326 // Applies changes from the local item to sync item. 327 bool UpdateSyncItemFromAppItem(const ChromeAppListItem* app_item, 328 AppListSyncableService::SyncItem* sync_item); 329 330 // Sets position, folder id and pin position for the app |app_id|. Attributes 331 // are taken from the sync item |attributes|. This generates sync update and 332 // notifies app models and Chrome shelf controller that are automatically 333 // refreshed. 334 void ApplyAppAttributes(const std::string& app_id, 335 std::unique_ptr<SyncItem> attributes); 336 337 Profile* profile_; 338 extensions::ExtensionSystem* extension_system_; 339 extensions::ExtensionRegistry* extension_registry_; 340 std::unique_ptr<AppListModelUpdater> model_updater_; 341 std::unique_ptr<ModelUpdaterObserver> model_updater_observer_; 342 343 std::unique_ptr<AppServiceAppModelBuilder> app_service_apps_builder_; 344 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor_; 345 std::unique_ptr<syncer::SyncErrorFactory> sync_error_handler_; 346 SyncItemMap sync_items_; 347 // Map that keeps pending request to transfer attributes from one app to 348 // another. 349 SyncItemMap pending_transfer_map_; 350 syncer::SyncableService::StartSyncFlare flare_; 351 bool initial_sync_data_processed_ = false; 352 bool first_app_list_sync_ = true; 353 std::string oem_folder_name_; 354 // Callback to install default page breaks. 355 // Only set for first time user for tablet form devices. 356 base::OnceClosure install_default_page_breaks_; 357 base::OnceClosure wait_until_ready_to_sync_cb_; 358 359 // List of observers. 360 base::ObserverList<Observer>::Unchecked observer_list_; 361 base::OneShotEvent on_initialized_; 362 363 base::WeakPtrFactory<AppListSyncableService> weak_ptr_factory_{this}; 364 }; 365 366 } // namespace app_list 367 368 #endif // CHROME_BROWSER_UI_APP_LIST_APP_LIST_SYNCABLE_SERVICE_H_ 369