Bug 1363421 - Part 2, delay the initialization of UserAgentOverrides.jsm until first nsHttpChannel is created. r=mcmanus
authorShih-Chiang Chien <schien@mozilla.com>
Tue, 16 May 2017 12:11:12 +0800
changeset 359747 bc372bd6dff30050a1c92c7fe64f9b92bf05ceba
parent 359746 06ab5407311a1dc96ada9a49e05edb88859c6c64
child 359748 df9c4ed9ec081bb01719e3c5f9dd1fab701abe2a
push id31857
push userphilringnalda@gmail.com
push dateSun, 21 May 2017 20:00:29 +0000
treeherdermozilla-central@cf9f9525e4d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1363421
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1363421 - Part 2, delay the initialization of UserAgentOverrides.jsm until first nsHttpChannel is created. r=mcmanus UAOverridesBootstrapper.js is introduced to delay the initialization of UserAgentOverrides.jsm until the creation of the first nsHttpChannel. Uninit will be triggered at profile-change-net-teardown because no network traffice after this point. MozReview-Commit-ID: F8Lpn6RyZEm
browser/components/nsBrowserGlue.js
browser/installer/package-manifest.in
mobile/android/chrome/content/browser.js
mobile/android/installer/package-manifest.in
netwerk/protocol/http/UAOverridesBootstrapper.js
netwerk/protocol/http/UAOverridesBootstrapper.manifest
netwerk/protocol/http/UserAgentOverrides.jsm
netwerk/protocol/http/moz.build
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -32,17 +32,17 @@ XPCOMUtils.defineLazyGetter(this, "Weave
           LightweightThemeManager:false, LoginHelper:false, LoginManagerParent:false,
           NetUtil:false, NewTabUtils:false, OS:false,
           PageThumbs:false, PdfJs:false, PermissionUI:false, PlacesBackups:false,
           PlacesUtils:false, PluralForm:false, PrivateBrowsingUtils:false,
           ProcessHangMonitor:false, ReaderParent:false, RecentWindow:false,
           RemotePrompt:false, SessionStore:false,
           ShellService:false, SimpleServiceDiscovery:false, TabCrashHandler:false,
           Task:false, UITour:false, WebChannel:false,
-          WindowsRegistry:false, webrtcUI:false, UserAgentOverrides: false */
+          WindowsRegistry:false, webrtcUI:false */
 
 /**
  * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
  * XXX Bug 1325373 is for making eslint detect these automatically.
  */
 
 let initializedModules = {};
 
@@ -87,17 +87,16 @@ let initializedModules = {};
   ["ShellService", "resource:///modules/ShellService.jsm"],
   ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
   ["Task", "resource://gre/modules/Task.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
   ["WebChannel", "resource://gre/modules/WebChannel.jsm"],
   ["WindowsRegistry", "resource://gre/modules/WindowsRegistry.jsm"],
   ["webrtcUI", "resource:///modules/webrtcUI.jsm", "init"],
-  ["UserAgentOverrides", "resource://gre/modules/UserAgentOverrides.jsm"],
 ].forEach(([name, resource, init]) => {
   if (init) {
     XPCOMUtils.defineLazyGetter(this, name, () => {
       Cu.import(resource, initializedModules);
       initializedModules[name][init]();
       return initializedModules[name];
     });
   } else {
@@ -511,18 +510,16 @@ BrowserGlue.prototype = {
     this._flashHangCount = 0;
     this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
 
     if (AppConstants.platform == "macosx") {
       // Handles prompting to inform about incompatibilites when accessibility
       // and e10s are active together.
       E10SAccessibilityCheck.init();
     }
-
-    UserAgentOverrides.init();
   },
 
   // cleanup (called on application shutdown)
   _dispose: function BG__dispose() {
     let os = Services.obs;
     os.removeObserver(this, "notifications-open-settings");
     os.removeObserver(this, "prefservice:after-app-defaults");
     os.removeObserver(this, "final-ui-startup");
@@ -556,18 +553,16 @@ BrowserGlue.prototype = {
     os.removeObserver(this, "handle-xul-text-link");
     os.removeObserver(this, "profile-before-change");
     if (AppConstants.MOZ_TELEMETRY_REPORTING) {
       os.removeObserver(this, "keyword-search");
     }
     os.removeObserver(this, "browser-search-engine-modified");
     os.removeObserver(this, "flash-plugin-hang");
     os.removeObserver(this, "xpi-signature-changed");
-
-    UserAgentOverrides.uninit();
   },
 
   _onAppDefaults: function BG__onAppDefaults() {
     // apply distribution customizations (prefs)
     // other customizations are applied in _finalUIStartup()
     this._distributionCustomizer.applyPrefDefaults();
   },
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -356,16 +356,18 @@
 
 ; JavaScript components
 @RESPATH@/components/ConsoleAPI.manifest
 @RESPATH@/components/ConsoleAPIStorage.js
 @RESPATH@/components/BrowserElementParent.manifest
 @RESPATH@/components/BrowserElementParent.js
 @RESPATH@/components/FeedProcessor.manifest
 @RESPATH@/components/FeedProcessor.js
+@RESPATH@/components/UAOverridesBootstrapper.js
+@RESPATH@/components/UAOverridesBootstrapper.manifest
 @RESPATH@/components/WellKnownOpportunisticUtils.js
 @RESPATH@/components/WellKnownOpportunisticUtils.manifest
 #ifndef XP_MACOSX
 ; OSX uses native platform impl.  Windows, Linux, and Android uses fallback JS impl.
 @BINPATH@/components/nsDNSServiceDiscovery.manifest
 @BINPATH@/components/nsDNSServiceDiscovery.js
 #endif
 @RESPATH@/browser/components/BrowserFeeds.manifest
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -453,17 +453,16 @@ var BrowserApp = {
 
     NativeWindow.init();
     FormAssistant.init();
     IndexedDB.init();
     XPInstallObserver.init();
     CharacterEncoding.init();
     ActivityObserver.init();
     RemoteDebugger.init();
-    UserAgentOverrides.init();
     DesktopUserAgent.init();
     Distribution.init();
     Tabs.init();
     SearchEngines.init();
     Experiments.init();
 
     // XXX maybe we don't do this if the launch was kicked off from external
     Services.io.offline = false;
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -272,16 +272,18 @@
 @BINPATH@/components/Push.js
 @BINPATH@/components/Push.manifest
 @BINPATH@/components/PushComponents.js
 #endif
 @BINPATH@/components/BrowserElementParent.manifest
 @BINPATH@/components/BrowserElementParent.js
 @BINPATH@/components/FeedProcessor.manifest
 @BINPATH@/components/FeedProcessor.js
+@BINPATH@/components/UAOverridesBootstrapper.js
+@BINPATH@/components/UAOverridesBootstrapper.manifest
 @BINPATH@/components/WellKnownOpportunisticUtils.js
 @BINPATH@/components/WellKnownOpportunisticUtils.manifest
 @BINPATH@/components/mozProtocolHandler.js
 @BINPATH@/components/mozProtocolHandler.manifest
 @BINPATH@/components/nsDNSServiceDiscovery.manifest
 @BINPATH@/components/nsDNSServiceDiscovery.js
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsSearchService.js
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/UAOverridesBootstrapper.js
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/UserAgentOverrides.jsm");
+
+function UAOverridesBootstrapper() {
+  this.init();
+}
+
+UAOverridesBootstrapper.prototype = {
+  init: function uaob_init() {
+    Services.obs.addObserver(this, "profile-change-net-teardown", false);
+    UserAgentOverrides.init();
+  },
+
+  observe: function uaob_observe(aSubject, aTopic, aData) {
+    if (aTopic == "profile-change-net-teardown") {
+      Services.obs.removeObserver(this, "profile-change-net-teardown");
+      UserAgentOverrides.uninit();
+    }
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+  classID: Components.ID("{965b0ca8-155b-11e7-93ae-92361f002671}")
+};
+
+const components = [UAOverridesBootstrapper];
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/UAOverridesBootstrapper.manifest
@@ -0,0 +1,3 @@
+# UAOverridesBootstrapper.js
+component {965b0ca8-155b-11e7-93ae-92361f002671} UAOverridesBootstrapper.js process=main
+contract @mozilla.org/network/ua-overrides-bootstrapper;1 {965b0ca8-155b-11e7-93ae-92361f002671} process=main
--- a/netwerk/protocol/http/UserAgentOverrides.jsm
+++ b/netwerk/protocol/http/UserAgentOverrides.jsm
@@ -44,28 +44,34 @@ this.UserAgentOverrides = {
     Services.prefs.addObserver(PREF_OVERRIDES_ENABLED, buildOverrides);
 
     try {
       Services.obs.addObserver(HTTP_on_useragent_request, "http-on-useragent-request");
     } catch (x) {
       // The http-on-useragent-request notification is disallowed in content processes.
     }
 
-    UserAgentUpdates.init(function(overrides) {
-      gOverrideForHostCache.clear();
-      if (overrides) {
-        for (let domain in overrides) {
-          overrides[domain] = getUserAgentFromOverride(overrides[domain]);
+    try {
+      UserAgentUpdates.init(function(overrides) {
+        gOverrideForHostCache.clear();
+        if (overrides) {
+          for (let domain in overrides) {
+            overrides[domain] = getUserAgentFromOverride(overrides[domain]);
+          }
+          overrides.get = function(key) { return this[key]; };
         }
-        overrides.get = function(key) { return this[key]; };
-      }
-      gUpdatedOverrides = overrides;
-    });
+        gUpdatedOverrides = overrides;
+      });
 
-    buildOverrides();
+      buildOverrides();
+    } catch (e) {
+      // UserAgentOverrides is initialized before profile is ready.
+      // UA override might not work correctly.
+    }
+
     gInitialized = true;
   },
 
   addComplexOverride: function uao_addComplexOverride(callback) {
     // Add to front of array so complex overrides have precedence
     gOverrideFunctions.unshift(callback);
   },
 
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -114,16 +114,18 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/netwerk/base',
 ]
 
 EXTRA_COMPONENTS += [
+    'UAOverridesBootstrapper.js',
+    'UAOverridesBootstrapper.manifest',
     'WellKnownOpportunisticUtils.js',
     'WellKnownOpportunisticUtils.manifest',
 ]
 
 if CONFIG['OS_TARGET'] == 'Darwin':
     if CONFIG['HOST_MAJOR_VERSION'] == '15':
         DEFINES.update(
             HAS_CONNECTX=True,
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -328,16 +328,29 @@ nsHttpHandler::SetFastOpenOSSupport()
         }
     }
 #endif
 
     LOG(("nsHttpHandler::SetFastOpenOSSupport %s supported.\n",
          mFastOpenSupported ? "" : "not"));
 }
 
+void
+nsHttpHandler::EnsureUAOverridesInit()
+{
+    MOZ_ASSERT(XRE_IsParentProcess());
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsresult rv;
+    nsCOMPtr<nsISupports> bootstrapper
+        = do_GetService("@mozilla.org/network/ua-overrides-bootstrapper;1", &rv);
+    MOZ_ASSERT(bootstrapper);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+}
+
 nsHttpHandler::~nsHttpHandler()
 {
     LOG(("Deleting nsHttpHandler [this=%p]\n", this));
 
     // make sure the connection manager is shutdown
     if (mConnMgr) {
         nsresult rv = mConnMgr->Shutdown();
         if (NS_FAILED(rv)) {
@@ -2098,16 +2111,21 @@ nsHttpHandler::NewProxiedChannel2(nsIURI
 
     uint32_t caps = mCapabilities;
 
     if (!IsNeckoChild()) {
         // HACK: make sure PSM gets initialized on the main thread.
         net_EnsurePSMInit();
     }
 
+    if (XRE_IsParentProcess()) {
+        // Load UserAgentOverrides.jsm before any HTTP request is issued.
+        EnsureUAOverridesInit();
+    }
+
     uint64_t channelId;
     rv = NewChannelId(channelId);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = httpChannel->Init(uri, caps, proxyInfo, proxyResolveFlags, proxyURI, channelId);
     if (NS_FAILED(rv))
         return rv;
 
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -398,16 +398,18 @@ private:
     MOZ_MUST_USE nsresult SetAcceptLanguages();
     MOZ_MUST_USE nsresult SetAcceptEncodings(const char *, bool mIsSecure);
 
     MOZ_MUST_USE nsresult InitConnectionMgr();
 
     void     NotifyObservers(nsIHttpChannel *chan, const char *event);
 
     void SetFastOpenOSSupport();
+
+    void EnsureUAOverridesInit();
 private:
 
     // cached services
     nsMainThreadPtrHandle<nsIIOService>              mIOService;
     nsMainThreadPtrHandle<nsIStreamConverterService> mStreamConvSvc;
     nsMainThreadPtrHandle<nsICookieService>          mCookieService;
     nsMainThreadPtrHandle<nsISiteSecurityService>    mSSService;
     nsMainThreadPtrHandle<nsIThrottlingService>      mThrottlingService;