netwerk/protocol/http/UserAgentOverrides.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/protocol/http/UserAgentOverrides.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,182 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +this.EXPORTED_SYMBOLS = [ "UserAgentOverrides" ];
    1.11 +
    1.12 +const Ci = Components.interfaces;
    1.13 +const Cc = Components.classes;
    1.14 +
    1.15 +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
    1.16 +Components.utils.import("resource://gre/modules/Services.jsm");
    1.17 +Components.utils.import("resource://gre/modules/UserAgentUpdates.jsm");
    1.18 +
    1.19 +const OVERRIDE_MESSAGE = "Useragent:GetOverride";
    1.20 +const PREF_OVERRIDES_ENABLED = "general.useragent.site_specific_overrides";
    1.21 +const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
    1.22 +                     .getService(Ci.nsIHttpProtocolHandler)
    1.23 +                     .userAgent;
    1.24 +const MAX_OVERRIDE_FOR_HOST_CACHE_SIZE = 250;
    1.25 +
    1.26 +XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
    1.27 +                                  "@mozilla.org/parentprocessmessagemanager;1",
    1.28 +                                  "nsIMessageListenerManager");  // Might have to make this broadcast?
    1.29 +
    1.30 +var gPrefBranch;
    1.31 +var gOverrides = new Map;
    1.32 +var gUpdatedOverrides;
    1.33 +var gOverrideForHostCache = new Map;
    1.34 +var gInitialized = false;
    1.35 +var gOverrideFunctions = [
    1.36 +  function (aHttpChannel) UserAgentOverrides.getOverrideForURI(aHttpChannel.URI)
    1.37 +];
    1.38 +var gBuiltUAs = new Map;
    1.39 +
    1.40 +this.UserAgentOverrides = {
    1.41 +  init: function uao_init() {
    1.42 +    if (gInitialized)
    1.43 +      return;
    1.44 +
    1.45 +    gPrefBranch = Services.prefs.getBranch("general.useragent.override.");
    1.46 +    gPrefBranch.addObserver("", buildOverrides, false);
    1.47 +
    1.48 +    ppmm.addMessageListener(OVERRIDE_MESSAGE, this);
    1.49 +    Services.prefs.addObserver(PREF_OVERRIDES_ENABLED, buildOverrides, false);
    1.50 +
    1.51 +    try {
    1.52 +      Services.obs.addObserver(HTTP_on_modify_request, "http-on-modify-request", false);
    1.53 +    } catch (x) {
    1.54 +      // The http-on-modify-request notification is disallowed in content processes.
    1.55 +    }
    1.56 +
    1.57 +    UserAgentUpdates.init(function(overrides) {
    1.58 +      gOverrideForHostCache.clear();
    1.59 +      if (overrides) {
    1.60 +        for (let domain in overrides) {
    1.61 +          overrides[domain] = getUserAgentFromOverride(overrides[domain]);
    1.62 +        }
    1.63 +        overrides.get = function(key) this[key];
    1.64 +      }
    1.65 +      gUpdatedOverrides = overrides;
    1.66 +    });
    1.67 +
    1.68 +    buildOverrides();
    1.69 +    gInitialized = true;
    1.70 +  },
    1.71 +
    1.72 +  addComplexOverride: function uao_addComplexOverride(callback) {
    1.73 +    // Add to front of array so complex overrides have precedence
    1.74 +    gOverrideFunctions.unshift(callback);
    1.75 +  },
    1.76 +
    1.77 +  getOverrideForURI: function uao_getOverrideForURI(aURI) {
    1.78 +    let host = aURI.asciiHost;
    1.79 +    if (!gInitialized ||
    1.80 +        (!gOverrides.size && !gUpdatedOverrides) ||
    1.81 +        !(host)) {
    1.82 +      return null;
    1.83 +    }
    1.84 +
    1.85 +    let override = gOverrideForHostCache.get(host);
    1.86 +    if (override !== undefined)
    1.87 +      return override;
    1.88 +
    1.89 +    function findOverride(overrides) {
    1.90 +      let searchHost = host;
    1.91 +      let userAgent = overrides.get(searchHost);
    1.92 +
    1.93 +      while (!userAgent) {
    1.94 +        let dot = searchHost.indexOf('.');
    1.95 +        if (dot === -1) {
    1.96 +          return null;
    1.97 +        }
    1.98 +        searchHost = searchHost.slice(dot + 1);
    1.99 +        userAgent = overrides.get(searchHost);
   1.100 +      }
   1.101 +      return userAgent;
   1.102 +    }
   1.103 +
   1.104 +    override = (gOverrides.size && findOverride(gOverrides))
   1.105 +            || (gUpdatedOverrides && findOverride(gUpdatedOverrides));
   1.106 +
   1.107 +    if (gOverrideForHostCache.size >= MAX_OVERRIDE_FOR_HOST_CACHE_SIZE) {
   1.108 +      gOverrideForHostCache.clear();
   1.109 +    }
   1.110 +    gOverrideForHostCache.set(host, override);
   1.111 +
   1.112 +    return override;
   1.113 +  },
   1.114 +
   1.115 +  uninit: function uao_uninit() {
   1.116 +    if (!gInitialized)
   1.117 +      return;
   1.118 +    gInitialized = false;
   1.119 +
   1.120 +    gPrefBranch.removeObserver("", buildOverrides);
   1.121 +
   1.122 +    Services.prefs.removeObserver(PREF_OVERRIDES_ENABLED, buildOverrides);
   1.123 +
   1.124 +    Services.obs.removeObserver(HTTP_on_modify_request, "http-on-modify-request");
   1.125 +  },
   1.126 +
   1.127 +  receiveMessage: function(aMessage) {
   1.128 +    let name = aMessage.name;
   1.129 +    switch (name) {
   1.130 +      case OVERRIDE_MESSAGE:
   1.131 +        let uri = aMessage.data.uri;
   1.132 +        return this.getOverrideForURI(uri);
   1.133 +      default:
   1.134 +        throw("Wrong Message in UserAgentOverride: " + name);
   1.135 +    }
   1.136 +  }
   1.137 +};
   1.138 +
   1.139 +function getUserAgentFromOverride(override)
   1.140 +{
   1.141 +  let userAgent = gBuiltUAs.get(override);
   1.142 +  if (userAgent !== undefined) {
   1.143 +    return userAgent;
   1.144 +  }
   1.145 +  let [search, replace] = override.split("#", 2);
   1.146 +  if (search && replace) {
   1.147 +    userAgent = DEFAULT_UA.replace(new RegExp(search, "g"), replace);
   1.148 +  } else {
   1.149 +    userAgent = override;
   1.150 +  }
   1.151 +  gBuiltUAs.set(override, userAgent);
   1.152 +  return userAgent;
   1.153 +}
   1.154 +
   1.155 +function buildOverrides() {
   1.156 +  gOverrides.clear();
   1.157 +  gOverrideForHostCache.clear();
   1.158 +
   1.159 +  if (!Services.prefs.getBoolPref(PREF_OVERRIDES_ENABLED))
   1.160 +    return;
   1.161 +
   1.162 +  let builtUAs = new Map;
   1.163 +  let domains = gPrefBranch.getChildList("");
   1.164 +
   1.165 +  for (let domain of domains) {
   1.166 +    let override = gPrefBranch.getCharPref(domain);
   1.167 +    let userAgent = getUserAgentFromOverride(override);
   1.168 +
   1.169 +    if (userAgent != DEFAULT_UA) {
   1.170 +      gOverrides.set(domain, userAgent);
   1.171 +    }
   1.172 +  }
   1.173 +}
   1.174 +
   1.175 +function HTTP_on_modify_request(aSubject, aTopic, aData) {
   1.176 +  let channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
   1.177 +
   1.178 +  for (let callback of gOverrideFunctions) {
   1.179 +    let modifiedUA = callback(channel, DEFAULT_UA);
   1.180 +    if (modifiedUA) {
   1.181 +      channel.setRequestHeader("User-Agent", modifiedUA, false);
   1.182 +      return;
   1.183 +    }
   1.184 +  }
   1.185 +}

mercurial