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/. */
4
5ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
6
7ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
8ChromeUtils.defineModuleGetter(this, "UserAgentOverrides", "resource://gre/modules/UserAgentOverrides.jsm");
9XPCOMUtils.defineLazyServiceGetter(this, "eTLDService", "@mozilla.org/network/effective-tld-service;1", "nsIEffectiveTLDService");
10
11class UAOverrider {
12  constructor(overrides) {
13    this._overrides = {};
14
15    this.initOverrides(overrides);
16  }
17
18  initOverrides(overrides) {
19    for (let override of overrides) {
20      if (!this._overrides[override.baseDomain]) {
21        this._overrides[override.baseDomain] = [];
22      }
23
24      if (!override.uriMatcher) {
25        override.uriMatcher = () => true;
26      }
27
28      this._overrides[override.baseDomain].push(override);
29    }
30  }
31
32  init() {
33    UserAgentOverrides.addComplexOverride(this.overrideCallback.bind(this));
34  }
35
36  overrideCallback(channel, defaultUA) {
37    let uaOverride = this.lookupUAOverride(channel.URI, defaultUA);
38    if (uaOverride) {
39      console.log("The user agent has been overridden for compatibility reasons.");
40      return uaOverride;
41    }
42
43    return false;
44  }
45
46  /**
47   * Try to use the eTLDService to get the base domain (will return example.com
48   * for http://foo.bar.example.com/foo/bar).
49   *
50   * However, the eTLDService is a bit picky and throws whenever we pass a
51   * blank host name or an IP into it, see bug 1337785. Since we do not plan on
52   * override UAs for such cases, we simply catch everything and return false.
53   */
54  getBaseDomainFromURI(uri) {
55    try {
56      return eTLDService.getBaseDomain(uri);
57    } catch (_) {
58      return false;
59    }
60  }
61
62  /**
63   * This function returns a User Agent based on the URI passed into. All
64   * override rules are defined in data/ua_overrides.jsm and the required format
65   * is explained there.
66   *
67   * Since it is expected and designed to have more than one override per base
68   * domain, we have to loop over this._overrides[baseDomain], which contains
69   * all available overrides.
70   *
71   * If the uriMatcher function returns true, the uaTransformer function gets
72   * called and its result will be used as the Use Agent for the current
73   * request.
74   *
75   * If there are more than one possible overrides, that is if two or more
76   * uriMatchers would return true, the first one gets applied.
77   */
78  lookupUAOverride(uri, defaultUA) {
79    let baseDomain = this.getBaseDomainFromURI(uri);
80    if (baseDomain && this._overrides[baseDomain]) {
81      for (let uaOverride of this._overrides[baseDomain]) {
82        if (uaOverride.uriMatcher(uri.specIgnoringRef)) {
83          return uaOverride.uaTransformer(defaultUA);
84        }
85      }
86    }
87
88    return false;
89  }
90}
91
92var EXPORTED_SYMBOLS = ["UAOverrider"]; /* exported UAOverrider */
93