Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | this.EXPORTED_SYMBOLS = [ "DistributionCustomizer" ]; |
michael@0 | 6 | |
michael@0 | 7 | const Ci = Components.interfaces; |
michael@0 | 8 | const Cc = Components.classes; |
michael@0 | 9 | const Cr = Components.results; |
michael@0 | 10 | const Cu = Components.utils; |
michael@0 | 11 | |
michael@0 | 12 | const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC = |
michael@0 | 13 | "distribution-customization-complete"; |
michael@0 | 14 | |
michael@0 | 15 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 16 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 17 | XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", |
michael@0 | 18 | "resource://gre/modules/PlacesUtils.jsm"); |
michael@0 | 19 | |
michael@0 | 20 | this.DistributionCustomizer = function DistributionCustomizer() { |
michael@0 | 21 | // For parallel xpcshell testing purposes allow loading the distribution.ini |
michael@0 | 22 | // file from the profile folder through an hidden pref. |
michael@0 | 23 | let loadFromProfile = false; |
michael@0 | 24 | try { |
michael@0 | 25 | loadFromProfile = Services.prefs.getBoolPref("distribution.testing.loadFromProfile"); |
michael@0 | 26 | } catch(ex) {} |
michael@0 | 27 | let dirSvc = Cc["@mozilla.org/file/directory_service;1"]. |
michael@0 | 28 | getService(Ci.nsIProperties); |
michael@0 | 29 | let iniFile = loadFromProfile ? dirSvc.get("ProfD", Ci.nsIFile) |
michael@0 | 30 | : dirSvc.get("XREExeF", Ci.nsIFile); |
michael@0 | 31 | iniFile.leafName = "distribution"; |
michael@0 | 32 | iniFile.append("distribution.ini"); |
michael@0 | 33 | if (iniFile.exists()) |
michael@0 | 34 | this._iniFile = iniFile; |
michael@0 | 35 | } |
michael@0 | 36 | |
michael@0 | 37 | DistributionCustomizer.prototype = { |
michael@0 | 38 | _iniFile: null, |
michael@0 | 39 | |
michael@0 | 40 | get _ini() { |
michael@0 | 41 | let ini = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. |
michael@0 | 42 | getService(Ci.nsIINIParserFactory). |
michael@0 | 43 | createINIParser(this._iniFile); |
michael@0 | 44 | this.__defineGetter__("_ini", function() ini); |
michael@0 | 45 | return this._ini; |
michael@0 | 46 | }, |
michael@0 | 47 | |
michael@0 | 48 | get _locale() { |
michael@0 | 49 | let locale; |
michael@0 | 50 | try { |
michael@0 | 51 | locale = this._prefs.getCharPref("general.useragent.locale"); |
michael@0 | 52 | } |
michael@0 | 53 | catch (e) { |
michael@0 | 54 | locale = "en-US"; |
michael@0 | 55 | } |
michael@0 | 56 | this.__defineGetter__("_locale", function() locale); |
michael@0 | 57 | return this._locale; |
michael@0 | 58 | }, |
michael@0 | 59 | |
michael@0 | 60 | get _prefSvc() { |
michael@0 | 61 | let svc = Cc["@mozilla.org/preferences-service;1"]. |
michael@0 | 62 | getService(Ci.nsIPrefService); |
michael@0 | 63 | this.__defineGetter__("_prefSvc", function() svc); |
michael@0 | 64 | return this._prefSvc; |
michael@0 | 65 | }, |
michael@0 | 66 | |
michael@0 | 67 | get _prefs() { |
michael@0 | 68 | let branch = this._prefSvc.getBranch(null); |
michael@0 | 69 | this.__defineGetter__("_prefs", function() branch); |
michael@0 | 70 | return this._prefs; |
michael@0 | 71 | }, |
michael@0 | 72 | |
michael@0 | 73 | get _ioSvc() { |
michael@0 | 74 | let svc = Cc["@mozilla.org/network/io-service;1"]. |
michael@0 | 75 | getService(Ci.nsIIOService); |
michael@0 | 76 | this.__defineGetter__("_ioSvc", function() svc); |
michael@0 | 77 | return this._ioSvc; |
michael@0 | 78 | }, |
michael@0 | 79 | |
michael@0 | 80 | _makeURI: function DIST__makeURI(spec) { |
michael@0 | 81 | return this._ioSvc.newURI(spec, null, null); |
michael@0 | 82 | }, |
michael@0 | 83 | |
michael@0 | 84 | _parseBookmarksSection: |
michael@0 | 85 | function DIST_parseBookmarksSection(parentId, section) { |
michael@0 | 86 | let keys = []; |
michael@0 | 87 | for (let i in enumerate(this._ini.getKeys(section))) |
michael@0 | 88 | keys.push(i); |
michael@0 | 89 | keys.sort(); |
michael@0 | 90 | |
michael@0 | 91 | let items = {}; |
michael@0 | 92 | let defaultItemId = -1; |
michael@0 | 93 | let maxItemId = -1; |
michael@0 | 94 | |
michael@0 | 95 | for (let i = 0; i < keys.length; i++) { |
michael@0 | 96 | let m = /^item\.(\d+)\.(\w+)\.?(\w*)/.exec(keys[i]); |
michael@0 | 97 | if (m) { |
michael@0 | 98 | let [foo, iid, iprop, ilocale] = m; |
michael@0 | 99 | iid = parseInt(iid); |
michael@0 | 100 | |
michael@0 | 101 | if (ilocale) |
michael@0 | 102 | continue; |
michael@0 | 103 | |
michael@0 | 104 | if (!items[iid]) |
michael@0 | 105 | items[iid] = {}; |
michael@0 | 106 | if (keys.indexOf(keys[i] + "." + this._locale) >= 0) { |
michael@0 | 107 | items[iid][iprop] = this._ini.getString(section, keys[i] + "." + |
michael@0 | 108 | this._locale); |
michael@0 | 109 | } else { |
michael@0 | 110 | items[iid][iprop] = this._ini.getString(section, keys[i]); |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | if (iprop == "type" && items[iid]["type"] == "default") |
michael@0 | 114 | defaultItemId = iid; |
michael@0 | 115 | |
michael@0 | 116 | if (maxItemId < iid) |
michael@0 | 117 | maxItemId = iid; |
michael@0 | 118 | } else { |
michael@0 | 119 | dump("Key did not match: " + keys[i] + "\n"); |
michael@0 | 120 | } |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | let prependIndex = 0; |
michael@0 | 124 | for (let iid = 0; iid <= maxItemId; iid++) { |
michael@0 | 125 | if (!items[iid]) |
michael@0 | 126 | continue; |
michael@0 | 127 | |
michael@0 | 128 | let index = PlacesUtils.bookmarks.DEFAULT_INDEX; |
michael@0 | 129 | let newId; |
michael@0 | 130 | |
michael@0 | 131 | switch (items[iid]["type"]) { |
michael@0 | 132 | case "default": |
michael@0 | 133 | break; |
michael@0 | 134 | |
michael@0 | 135 | case "folder": |
michael@0 | 136 | if (iid < defaultItemId) |
michael@0 | 137 | index = prependIndex++; |
michael@0 | 138 | |
michael@0 | 139 | newId = PlacesUtils.bookmarks.createFolder(parentId, |
michael@0 | 140 | items[iid]["title"], |
michael@0 | 141 | index); |
michael@0 | 142 | |
michael@0 | 143 | this._parseBookmarksSection(newId, "BookmarksFolder-" + |
michael@0 | 144 | items[iid]["folderId"]); |
michael@0 | 145 | |
michael@0 | 146 | if (items[iid]["description"]) |
michael@0 | 147 | PlacesUtils.annotations.setItemAnnotation(newId, |
michael@0 | 148 | "bookmarkProperties/description", |
michael@0 | 149 | items[iid]["description"], 0, |
michael@0 | 150 | PlacesUtils.annotations.EXPIRE_NEVER); |
michael@0 | 151 | |
michael@0 | 152 | break; |
michael@0 | 153 | |
michael@0 | 154 | case "separator": |
michael@0 | 155 | if (iid < defaultItemId) |
michael@0 | 156 | index = prependIndex++; |
michael@0 | 157 | PlacesUtils.bookmarks.insertSeparator(parentId, index); |
michael@0 | 158 | break; |
michael@0 | 159 | |
michael@0 | 160 | case "livemark": |
michael@0 | 161 | if (iid < defaultItemId) |
michael@0 | 162 | index = prependIndex++; |
michael@0 | 163 | |
michael@0 | 164 | // Don't bother updating the livemark contents on creation. |
michael@0 | 165 | PlacesUtils.livemarks.addLivemark({ title: items[iid]["title"] |
michael@0 | 166 | , parentId: parentId |
michael@0 | 167 | , index: index |
michael@0 | 168 | , feedURI: this._makeURI(items[iid]["feedLink"]) |
michael@0 | 169 | , siteURI: this._makeURI(items[iid]["siteLink"]) |
michael@0 | 170 | }).then(null, Cu.reportError); |
michael@0 | 171 | break; |
michael@0 | 172 | |
michael@0 | 173 | case "bookmark": |
michael@0 | 174 | default: |
michael@0 | 175 | if (iid < defaultItemId) |
michael@0 | 176 | index = prependIndex++; |
michael@0 | 177 | |
michael@0 | 178 | newId = PlacesUtils.bookmarks.insertBookmark(parentId, |
michael@0 | 179 | this._makeURI(items[iid]["link"]), |
michael@0 | 180 | index, items[iid]["title"]); |
michael@0 | 181 | |
michael@0 | 182 | if (items[iid]["description"]) |
michael@0 | 183 | PlacesUtils.annotations.setItemAnnotation(newId, |
michael@0 | 184 | "bookmarkProperties/description", |
michael@0 | 185 | items[iid]["description"], 0, |
michael@0 | 186 | PlacesUtils.annotations.EXPIRE_NEVER); |
michael@0 | 187 | |
michael@0 | 188 | break; |
michael@0 | 189 | } |
michael@0 | 190 | } |
michael@0 | 191 | }, |
michael@0 | 192 | |
michael@0 | 193 | _customizationsApplied: false, |
michael@0 | 194 | applyCustomizations: function DIST_applyCustomizations() { |
michael@0 | 195 | this._customizationsApplied = true; |
michael@0 | 196 | if (!this._iniFile) |
michael@0 | 197 | return this._checkCustomizationComplete(); |
michael@0 | 198 | |
michael@0 | 199 | // nsPrefService loads very early. Reload prefs so we can set |
michael@0 | 200 | // distribution defaults during the prefservice:after-app-defaults |
michael@0 | 201 | // notification (see applyPrefDefaults below) |
michael@0 | 202 | this._prefSvc.QueryInterface(Ci.nsIObserver); |
michael@0 | 203 | this._prefSvc.observe(null, "reload-default-prefs", null); |
michael@0 | 204 | }, |
michael@0 | 205 | |
michael@0 | 206 | _bookmarksApplied: false, |
michael@0 | 207 | applyBookmarks: function DIST_applyBookmarks() { |
michael@0 | 208 | this._bookmarksApplied = true; |
michael@0 | 209 | if (!this._iniFile) |
michael@0 | 210 | return this._checkCustomizationComplete(); |
michael@0 | 211 | |
michael@0 | 212 | let sections = enumToObject(this._ini.getSections()); |
michael@0 | 213 | |
michael@0 | 214 | // The global section, and several of its fields, is required |
michael@0 | 215 | // (we also check here to be consistent with applyPrefDefaults below) |
michael@0 | 216 | if (!sections["Global"]) |
michael@0 | 217 | return this._checkCustomizationComplete(); |
michael@0 | 218 | let globalPrefs = enumToObject(this._ini.getKeys("Global")); |
michael@0 | 219 | if (!(globalPrefs["id"] && globalPrefs["version"] && globalPrefs["about"])) |
michael@0 | 220 | return this._checkCustomizationComplete(); |
michael@0 | 221 | |
michael@0 | 222 | let bmProcessedPref; |
michael@0 | 223 | try { |
michael@0 | 224 | bmProcessedPref = this._ini.getString("Global", |
michael@0 | 225 | "bookmarks.initialized.pref"); |
michael@0 | 226 | } |
michael@0 | 227 | catch (e) { |
michael@0 | 228 | bmProcessedPref = "distribution." + |
michael@0 | 229 | this._ini.getString("Global", "id") + ".bookmarksProcessed"; |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | let bmProcessed = false; |
michael@0 | 233 | try { |
michael@0 | 234 | bmProcessed = this._prefs.getBoolPref(bmProcessedPref); |
michael@0 | 235 | } |
michael@0 | 236 | catch (e) {} |
michael@0 | 237 | |
michael@0 | 238 | if (!bmProcessed) { |
michael@0 | 239 | if (sections["BookmarksMenu"]) |
michael@0 | 240 | this._parseBookmarksSection(PlacesUtils.bookmarksMenuFolderId, |
michael@0 | 241 | "BookmarksMenu"); |
michael@0 | 242 | if (sections["BookmarksToolbar"]) |
michael@0 | 243 | this._parseBookmarksSection(PlacesUtils.toolbarFolderId, |
michael@0 | 244 | "BookmarksToolbar"); |
michael@0 | 245 | this._prefs.setBoolPref(bmProcessedPref, true); |
michael@0 | 246 | } |
michael@0 | 247 | return this._checkCustomizationComplete(); |
michael@0 | 248 | }, |
michael@0 | 249 | |
michael@0 | 250 | _prefDefaultsApplied: false, |
michael@0 | 251 | applyPrefDefaults: function DIST_applyPrefDefaults() { |
michael@0 | 252 | this._prefDefaultsApplied = true; |
michael@0 | 253 | if (!this._iniFile) |
michael@0 | 254 | return this._checkCustomizationComplete(); |
michael@0 | 255 | |
michael@0 | 256 | let sections = enumToObject(this._ini.getSections()); |
michael@0 | 257 | |
michael@0 | 258 | // The global section, and several of its fields, is required |
michael@0 | 259 | if (!sections["Global"]) |
michael@0 | 260 | return this._checkCustomizationComplete(); |
michael@0 | 261 | let globalPrefs = enumToObject(this._ini.getKeys("Global")); |
michael@0 | 262 | if (!(globalPrefs["id"] && globalPrefs["version"] && globalPrefs["about"])) |
michael@0 | 263 | return this._checkCustomizationComplete(); |
michael@0 | 264 | |
michael@0 | 265 | let defaults = this._prefSvc.getDefaultBranch(null); |
michael@0 | 266 | |
michael@0 | 267 | // Global really contains info we set as prefs. They're only |
michael@0 | 268 | // separate because they are "special" (read: required) |
michael@0 | 269 | |
michael@0 | 270 | defaults.setCharPref("distribution.id", this._ini.getString("Global", "id")); |
michael@0 | 271 | defaults.setCharPref("distribution.version", |
michael@0 | 272 | this._ini.getString("Global", "version")); |
michael@0 | 273 | |
michael@0 | 274 | let partnerAbout = Cc["@mozilla.org/supports-string;1"]. |
michael@0 | 275 | createInstance(Ci.nsISupportsString); |
michael@0 | 276 | try { |
michael@0 | 277 | if (globalPrefs["about." + this._locale]) { |
michael@0 | 278 | partnerAbout.data = this._ini.getString("Global", "about." + this._locale); |
michael@0 | 279 | } else { |
michael@0 | 280 | partnerAbout.data = this._ini.getString("Global", "about"); |
michael@0 | 281 | } |
michael@0 | 282 | defaults.setComplexValue("distribution.about", |
michael@0 | 283 | Ci.nsISupportsString, partnerAbout); |
michael@0 | 284 | } catch (e) { |
michael@0 | 285 | /* ignore bad prefs due to bug 895473 and move on */ |
michael@0 | 286 | Cu.reportError(e); |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | if (sections["Preferences"]) { |
michael@0 | 290 | for (let key in enumerate(this._ini.getKeys("Preferences"))) { |
michael@0 | 291 | try { |
michael@0 | 292 | let value = eval(this._ini.getString("Preferences", key)); |
michael@0 | 293 | switch (typeof value) { |
michael@0 | 294 | case "boolean": |
michael@0 | 295 | defaults.setBoolPref(key, value); |
michael@0 | 296 | break; |
michael@0 | 297 | case "number": |
michael@0 | 298 | defaults.setIntPref(key, value); |
michael@0 | 299 | break; |
michael@0 | 300 | case "string": |
michael@0 | 301 | defaults.setCharPref(key, value); |
michael@0 | 302 | break; |
michael@0 | 303 | case "undefined": |
michael@0 | 304 | defaults.setCharPref(key, value); |
michael@0 | 305 | break; |
michael@0 | 306 | } |
michael@0 | 307 | } catch (e) { /* ignore bad prefs and move on */ } |
michael@0 | 308 | } |
michael@0 | 309 | } |
michael@0 | 310 | |
michael@0 | 311 | // We eval() the localizable prefs as well (even though they'll |
michael@0 | 312 | // always get set as a string) to keep the INI format consistent: |
michael@0 | 313 | // string prefs always need to be in quotes |
michael@0 | 314 | |
michael@0 | 315 | let localizedStr = Cc["@mozilla.org/pref-localizedstring;1"]. |
michael@0 | 316 | createInstance(Ci.nsIPrefLocalizedString); |
michael@0 | 317 | |
michael@0 | 318 | if (sections["LocalizablePreferences"]) { |
michael@0 | 319 | for (let key in enumerate(this._ini.getKeys("LocalizablePreferences"))) { |
michael@0 | 320 | try { |
michael@0 | 321 | let value = eval(this._ini.getString("LocalizablePreferences", key)); |
michael@0 | 322 | value = value.replace("%LOCALE%", this._locale, "g"); |
michael@0 | 323 | localizedStr.data = "data:text/plain," + key + "=" + value; |
michael@0 | 324 | defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); |
michael@0 | 325 | } catch (e) { /* ignore bad prefs and move on */ } |
michael@0 | 326 | } |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | if (sections["LocalizablePreferences-" + this._locale]) { |
michael@0 | 330 | for (let key in enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) { |
michael@0 | 331 | try { |
michael@0 | 332 | let value = eval(this._ini.getString("LocalizablePreferences-" + this._locale, key)); |
michael@0 | 333 | localizedStr.data = "data:text/plain," + key + "=" + value; |
michael@0 | 334 | defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); |
michael@0 | 335 | } catch (e) { /* ignore bad prefs and move on */ } |
michael@0 | 336 | } |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | return this._checkCustomizationComplete(); |
michael@0 | 340 | }, |
michael@0 | 341 | |
michael@0 | 342 | _checkCustomizationComplete: function DIST__checkCustomizationComplete() { |
michael@0 | 343 | let prefDefaultsApplied = this._prefDefaultsApplied || !this._iniFile; |
michael@0 | 344 | if (this._customizationsApplied && this._bookmarksApplied && |
michael@0 | 345 | prefDefaultsApplied) { |
michael@0 | 346 | let os = Cc["@mozilla.org/observer-service;1"]. |
michael@0 | 347 | getService(Ci.nsIObserverService); |
michael@0 | 348 | os.notifyObservers(null, DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC, null); |
michael@0 | 349 | } |
michael@0 | 350 | } |
michael@0 | 351 | }; |
michael@0 | 352 | |
michael@0 | 353 | function enumerate(UTF8Enumerator) { |
michael@0 | 354 | while (UTF8Enumerator.hasMore()) |
michael@0 | 355 | yield UTF8Enumerator.getNext(); |
michael@0 | 356 | } |
michael@0 | 357 | |
michael@0 | 358 | function enumToObject(UTF8Enumerator) { |
michael@0 | 359 | let ret = {}; |
michael@0 | 360 | for (let i in enumerate(UTF8Enumerator)) |
michael@0 | 361 | ret[i] = 1; |
michael@0 | 362 | return ret; |
michael@0 | 363 | } |