netwerk/protocol/http/UserAgentUpdates.jsm

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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 "use strict";
michael@0 6
michael@0 7 this.EXPORTED_SYMBOLS = ["UserAgentUpdates"];
michael@0 8
michael@0 9 const Ci = Components.interfaces;
michael@0 10 const Cc = Components.classes;
michael@0 11 const Cu = Components.utils;
michael@0 12
michael@0 13 Cu.import("resource://gre/modules/Services.jsm");
michael@0 14 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 15
michael@0 16 XPCOMUtils.defineLazyModuleGetter(
michael@0 17 this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
michael@0 18
michael@0 19 XPCOMUtils.defineLazyModuleGetter(
michael@0 20 this, "OS", "resource://gre/modules/osfile.jsm");
michael@0 21
michael@0 22 XPCOMUtils.defineLazyModuleGetter(
michael@0 23 this, "UpdateChannel", "resource://gre/modules/UpdateChannel.jsm");
michael@0 24
michael@0 25 XPCOMUtils.defineLazyServiceGetter(
michael@0 26 this, "gUpdateTimer", "@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager");
michael@0 27
michael@0 28 XPCOMUtils.defineLazyGetter(this, "gApp",
michael@0 29 function() Cc["@mozilla.org/xre/app-info;1"]
michael@0 30 .getService(Ci.nsIXULAppInfo).QueryInterface(Ci.nsIXULRuntime)
michael@0 31 );
michael@0 32
michael@0 33 XPCOMUtils.defineLazyGetter(this, "gDecoder",
michael@0 34 function() new TextDecoder()
michael@0 35 );
michael@0 36
michael@0 37 XPCOMUtils.defineLazyGetter(this, "gEncoder",
michael@0 38 function() new TextEncoder()
michael@0 39 );
michael@0 40
michael@0 41 const TIMER_ID = "user-agent-updates-timer";
michael@0 42
michael@0 43 const PREF_UPDATES = "general.useragent.updates.";
michael@0 44 const PREF_UPDATES_ENABLED = PREF_UPDATES + "enabled";
michael@0 45 const PREF_UPDATES_URL = PREF_UPDATES + "url";
michael@0 46 const PREF_UPDATES_INTERVAL = PREF_UPDATES + "interval";
michael@0 47 const PREF_UPDATES_RETRY = PREF_UPDATES + "retry";
michael@0 48 const PREF_UPDATES_TIMEOUT = PREF_UPDATES + "timeout";
michael@0 49 const PREF_UPDATES_LASTUPDATED = PREF_UPDATES + "lastupdated";
michael@0 50
michael@0 51 const KEY_PREFDIR = "PrefD";
michael@0 52 const KEY_APPDIR = "XCurProcD";
michael@0 53 const FILE_UPDATES = "ua-update.json";
michael@0 54
michael@0 55 const PREF_APP_DISTRIBUTION = "distribution.id";
michael@0 56 const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
michael@0 57
michael@0 58 var gInitialized = false;
michael@0 59
michael@0 60 this.UserAgentUpdates = {
michael@0 61 init: function(callback) {
michael@0 62 if (gInitialized) {
michael@0 63 return;
michael@0 64 }
michael@0 65 gInitialized = true;
michael@0 66
michael@0 67 this._callback = callback;
michael@0 68 this._lastUpdated = 0;
michael@0 69 this._applySavedUpdate();
michael@0 70
michael@0 71 Services.prefs.addObserver(PREF_UPDATES, this, false);
michael@0 72 },
michael@0 73
michael@0 74 uninit: function() {
michael@0 75 if (!gInitialized) {
michael@0 76 return;
michael@0 77 }
michael@0 78 gInitialized = false;
michael@0 79 Services.prefs.removeObserver(PREF_UPDATES, this);
michael@0 80 },
michael@0 81
michael@0 82 _applyUpdate: function(update) {
michael@0 83 // Check pref again in case it has changed
michael@0 84 if (update && this._getPref(PREF_UPDATES_ENABLED, false)) {
michael@0 85 this._callback(update);
michael@0 86 } else {
michael@0 87 this._callback(null);
michael@0 88 }
michael@0 89 },
michael@0 90
michael@0 91 _applySavedUpdate: function() {
michael@0 92 if (!this._getPref(PREF_UPDATES_ENABLED, false)) {
michael@0 93 // remove previous overrides
michael@0 94 this._applyUpdate(null);
michael@0 95 return;
michael@0 96 }
michael@0 97 // try loading from profile dir, then from app dir
michael@0 98 let dirs = [KEY_PREFDIR, KEY_APPDIR];
michael@0 99 dirs.reduce((prevLoad, dir) => {
michael@0 100 let file = FileUtils.getFile(dir, [FILE_UPDATES], true).path;
michael@0 101 // tryNext returns promise to read file under dir and parse it
michael@0 102 let tryNext = () => OS.File.read(file).then(
michael@0 103 (bytes) => {
michael@0 104 let update = JSON.parse(gDecoder.decode(bytes));
michael@0 105 if (!update) {
michael@0 106 throw new Error("invalid update");
michael@0 107 }
michael@0 108 return update;
michael@0 109 }
michael@0 110 );
michael@0 111 // try to load next one if the previous load failed
michael@0 112 return prevLoad ? prevLoad.then(null, tryNext) : tryNext();
michael@0 113 }, null).then(
michael@0 114 // apply update if loading was successful
michael@0 115 (update) => this._applyUpdate(update)
michael@0 116 );
michael@0 117 this._scheduleUpdate();
michael@0 118 },
michael@0 119
michael@0 120 _saveToFile: function(update) {
michael@0 121 let file = FileUtils.getFile(KEY_PREFDIR, [FILE_UPDATES], true);
michael@0 122 let path = file.path;
michael@0 123 let bytes = gEncoder.encode(JSON.stringify(update));
michael@0 124 OS.File.writeAtomic(path, bytes, {tmpPath: path + ".tmp"}).then(
michael@0 125 () => {
michael@0 126 this._lastUpdated = Date.now();
michael@0 127 Services.prefs.setCharPref(
michael@0 128 PREF_UPDATES_LASTUPDATED, this._lastUpdated.toString());
michael@0 129 },
michael@0 130 Cu.reportError
michael@0 131 );
michael@0 132 },
michael@0 133
michael@0 134 _getPref: function(name, def) {
michael@0 135 try {
michael@0 136 switch (typeof def) {
michael@0 137 case "number": return Services.prefs.getIntPref(name);
michael@0 138 case "boolean": return Services.prefs.getBoolPref(name);
michael@0 139 }
michael@0 140 return Services.prefs.getCharPref(name);
michael@0 141 } catch (e) {
michael@0 142 return def;
michael@0 143 }
michael@0 144 },
michael@0 145
michael@0 146 _getParameters: function() ({
michael@0 147 "%DATE%": function() Date.now().toString(),
michael@0 148 "%PRODUCT%": function() gApp.name,
michael@0 149 "%APP_ID%": function() gApp.ID,
michael@0 150 "%APP_VERSION%": function() gApp.version,
michael@0 151 "%BUILD_ID%": function() gApp.appBuildID,
michael@0 152 "%OS%": function() gApp.OS,
michael@0 153 "%CHANNEL%": function() UpdateChannel.get(),
michael@0 154 "%DISTRIBUTION%": function() this._getPref(PREF_APP_DISTRIBUTION, ""),
michael@0 155 "%DISTRIBUTION_VERSION%": function() this._getPref(PREF_APP_DISTRIBUTION_VERSION, ""),
michael@0 156 }),
michael@0 157
michael@0 158 _getUpdateURL: function() {
michael@0 159 let url = this._getPref(PREF_UPDATES_URL, "");
michael@0 160 let params = this._getParameters();
michael@0 161 return url.replace(/%[A-Z_]+%/g, function(match) {
michael@0 162 let param = params[match];
michael@0 163 // preserve the %FOO% string (e.g. as an encoding) if it's not a valid parameter
michael@0 164 return param ? encodeURIComponent(param()) : match;
michael@0 165 });
michael@0 166 },
michael@0 167
michael@0 168 _fetchUpdate: function(url, success, error) {
michael@0 169 let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
michael@0 170 .createInstance(Ci.nsIXMLHttpRequest);
michael@0 171 request.mozBackgroundRequest = true;
michael@0 172 request.timeout = this._getPref(PREF_UPDATES_TIMEOUT, 60000);
michael@0 173 request.open("GET", url, true);
michael@0 174 request.overrideMimeType("application/json");
michael@0 175 request.responseType = "json";
michael@0 176
michael@0 177 request.addEventListener("load", function() {
michael@0 178 let response = request.response;
michael@0 179 response ? success(response) : error();
michael@0 180 });
michael@0 181 request.addEventListener("error", error);
michael@0 182 request.send();
michael@0 183 },
michael@0 184
michael@0 185 _update: function() {
michael@0 186 let url = this._getUpdateURL();
michael@0 187 url && this._fetchUpdate(url,
michael@0 188 (function(response) { // success
michael@0 189 // apply update and save overrides to profile
michael@0 190 this._applyUpdate(response);
michael@0 191 this._saveToFile(response);
michael@0 192 this._scheduleUpdate(); // cancel any retries
michael@0 193 }).bind(this),
michael@0 194 (function(response) { // error
michael@0 195 this._scheduleUpdate(true /* retry */);
michael@0 196 }).bind(this));
michael@0 197 },
michael@0 198
michael@0 199 _scheduleUpdate: function(retry) {
michael@0 200 // only schedule updates in the main process
michael@0 201 if (gApp.processType !== Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
michael@0 202 return;
michael@0 203 }
michael@0 204 let interval = this._getPref(PREF_UPDATES_INTERVAL, 604800 /* 1 week */);
michael@0 205 if (retry) {
michael@0 206 interval = this._getPref(PREF_UPDATES_RETRY, interval);
michael@0 207 }
michael@0 208 gUpdateTimer.registerTimer(TIMER_ID, this, Math.max(1, interval));
michael@0 209 },
michael@0 210
michael@0 211 notify: function(timer) {
michael@0 212 // timer notification
michael@0 213 if (this._getPref(PREF_UPDATES_ENABLED, false)) {
michael@0 214 this._update();
michael@0 215 }
michael@0 216 },
michael@0 217
michael@0 218 observe: function(subject, topic, data) {
michael@0 219 switch (topic) {
michael@0 220 case "nsPref:changed":
michael@0 221 if (data === PREF_UPDATES_ENABLED) {
michael@0 222 this._applySavedUpdate();
michael@0 223 } else if (data === PREF_UPDATES_INTERVAL) {
michael@0 224 this._scheduleUpdate();
michael@0 225 } else if (data === PREF_UPDATES_LASTUPDATED) {
michael@0 226 // reload from file if there has been an update
michael@0 227 let lastUpdated = parseInt(
michael@0 228 this._getPref(PREF_UPDATES_LASTUPDATED, "0"), 0);
michael@0 229 if (lastUpdated > this._lastUpdated) {
michael@0 230 this._applySavedUpdate();
michael@0 231 this._lastUpdated = lastUpdated;
michael@0 232 }
michael@0 233 }
michael@0 234 break;
michael@0 235 }
michael@0 236 },
michael@0 237
michael@0 238 QueryInterface: XPCOMUtils.generateQI([
michael@0 239 Ci.nsIObserver,
michael@0 240 Ci.nsITimerCallback,
michael@0 241 ]),
michael@0 242 };

mercurial