OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "ios/chrome/app/startup/register_experimental_settings.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/mac/bundle_locations.h" |
| 9 #import "base/mac/scoped_nsobject.h" |
| 10 #include "base/strings/sys_string_conversions.h" |
| 11 |
| 12 namespace { |
| 13 // Key in the UserDefaults for the Experimental Keys. |
| 14 NSString* kExperimentalKeysKey = @"ExperimentalKeys"; |
| 15 |
| 16 // Returns YES if a setting value is equivalent to not having the setting at |
| 17 // all. This must always be true for default values, otherwise the experimental |
| 18 // settings will have different default behaviors in stable channel (where the |
| 19 // bundle isn't present). |
| 20 BOOL IsDefaultSettingValueValid(id value) { |
| 21 if (!value) |
| 22 return YES; |
| 23 if ([value isKindOfClass:[NSNumber class]]) |
| 24 return [value intValue] == 0; |
| 25 if ([value isKindOfClass:[NSString class]]) |
| 26 return [value length] == 0; |
| 27 // Add support for other types as necessary. |
| 28 NOTREACHED() << "Unhandled value type " |
| 29 << base::SysNSStringToUTF8(NSStringFromClass([value class])); |
| 30 return NO; |
| 31 } |
| 32 } // namespace |
| 33 |
| 34 @interface RegisterExperimentalSettings () |
| 35 // Registers all the default values for a single settings file and returns |
| 36 // all the registered keys. |
| 37 + (NSArray*)registerExperimentalSettingsForFile:(NSString*)filepath |
| 38 userDefaults:(NSUserDefaults*)userDefaults; |
| 39 @end |
| 40 |
| 41 @implementation RegisterExperimentalSettings |
| 42 |
| 43 + (void)registerExperimentalSettingsWithUserDefaults: |
| 44 (NSUserDefaults*)userDefaults |
| 45 bundle:(NSBundle*)bundle { |
| 46 // Save the current app version in user defaults. |
| 47 NSDictionary* infoDictionary = [bundle infoDictionary]; |
| 48 NSString* version = [infoDictionary objectForKey:@"CFBundleVersion"]; |
| 49 [userDefaults setObject:version forKey:@"Version"]; |
| 50 |
| 51 NSString* bundlePath = [bundle bundlePath]; |
| 52 NSString* settingsFilepath = |
| 53 [bundlePath stringByAppendingPathComponent:@"Settings.bundle"]; |
| 54 NSArray* settingsContent = |
| 55 [[NSFileManager defaultManager] contentsOfDirectoryAtPath:settingsFilepath |
| 56 error:NULL]; |
| 57 base::scoped_nsobject<NSMutableArray> currentExpKeys( |
| 58 [[NSMutableArray alloc] init]); |
| 59 |
| 60 for (NSString* filename in settingsContent) { |
| 61 // Only plist files are preferences definition. |
| 62 if ([[filename pathExtension] isEqualToString:@"plist"]) { |
| 63 NSString* filepath = |
| 64 [settingsFilepath stringByAppendingPathComponent:filename]; |
| 65 NSArray* registeredKeys = |
| 66 [self registerExperimentalSettingsForFile:filepath |
| 67 userDefaults:userDefaults]; |
| 68 [currentExpKeys addObjectsFromArray:registeredKeys]; |
| 69 } |
| 70 } |
| 71 |
| 72 // Remove all keys that are no longer used. |
| 73 NSArray* expKeys = [userDefaults arrayForKey:kExperimentalKeysKey]; |
| 74 NSMutableSet* expKeysSet = [NSMutableSet setWithArray:expKeys]; |
| 75 NSSet* currentExpKeysSet = [NSSet setWithArray:currentExpKeys]; |
| 76 [expKeysSet minusSet:currentExpKeysSet]; |
| 77 for (NSString* key in expKeysSet) { |
| 78 [userDefaults removeObjectForKey:key]; |
| 79 } |
| 80 |
| 81 if ([currentExpKeys count] > 0) { |
| 82 [userDefaults setObject:currentExpKeys forKey:kExperimentalKeysKey]; |
| 83 } else { |
| 84 [userDefaults removeObjectForKey:kExperimentalKeysKey]; |
| 85 } |
| 86 } |
| 87 |
| 88 + (NSArray*)registerExperimentalSettingsForFile:(NSString*)filepath |
| 89 userDefaults:(NSUserDefaults*)userDefaults { |
| 90 NSMutableArray* registeredKeys = [NSMutableArray array]; |
| 91 |
| 92 NSDictionary* rootDictionary = |
| 93 [NSDictionary dictionaryWithContentsOfFile:filepath]; |
| 94 // Array with all the preference specifiers. The plist is composed of many |
| 95 // Preference specifiers; one for each preference row in the settings |
| 96 // panel. |
| 97 NSArray* preferencesArray = |
| 98 [rootDictionary objectForKey:@"PreferenceSpecifiers"]; |
| 99 |
| 100 // Scan thru all the preferences in the plist file. |
| 101 for (NSDictionary* preferenceSpecifier in preferencesArray) { |
| 102 NSString* keyValue = [preferenceSpecifier objectForKey:@"Key"]; |
| 103 if (!keyValue) |
| 104 continue; |
| 105 |
| 106 id defaultValue = [preferenceSpecifier objectForKey:@"DefaultValue"]; |
| 107 // Within the app, the default for all experimental prefs is nil (matching |
| 108 // the behavior of Stable channel, where there is no settings bundle). To |
| 109 // make mistakes obvious, fail if someone tries to set any actual value as |
| 110 // the default. |
| 111 DCHECK(IsDefaultSettingValueValid(defaultValue)) |
| 112 << "'" << base::SysNSStringToUTF8([defaultValue description]) |
| 113 << "' is not a valid default value for " |
| 114 << base::SysNSStringToUTF8(keyValue); |
| 115 |
| 116 [registeredKeys addObject:keyValue]; |
| 117 |
| 118 // If a default value is set, normalize it to nil. |
| 119 id currentValue = [userDefaults objectForKey:keyValue]; |
| 120 if (currentValue && |
| 121 (!defaultValue || [currentValue isEqual:defaultValue])) { |
| 122 [userDefaults removeObjectForKey:keyValue]; |
| 123 } |
| 124 } |
| 125 return registeredKeys; |
| 126 } |
| 127 |
| 128 @end |
OLD | NEW |