Bug 1363421 - Part 2, delay the initialization of UserAgentOverrides.jsm until first nsHttpChannel is created. r=mcmanus draft
authorShih-Chiang Chien <schien@mozilla.com>
Tue, 16 May 2017 12:11:12 +0800
changeset 578235 7c3649b50ad8594dc0968961fbbd2766d0d98b0a
parent 576082 8061ed3ce6c42955e52f494166958f5b63ab940b
child 628732 0ea705cf9b02d447ac2d38cc79547df7b384775c
push id58938
push userbmo:schien@mozilla.com
push dateTue, 16 May 2017 04:14:36 +0000
reviewersmcmanus
bugs1363421
milestone55.0a1
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
@@ -29,17 +29,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
           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, SelfSupportBackend: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 = {};
 
@@ -85,17 +85,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 {
@@ -525,18 +524,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");
@@ -566,18 +563,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
@@ -354,16 +354,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
@@ -273,16 +273,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
@@ -321,16 +321,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)) {
@@ -2087,16 +2100,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
@@ -394,16 +394,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;