netwerk/protocol/http/UserAgentUpdates.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial