Bug 1148544 - Figure out UA override once per LoadGroup and cache it, rather than once per channel. r=nwgh
☠☠ backed out by ee4201100b15 ☠ ☠
authorDylan Roeh <droeh@mozilla.com>
Tue, 09 Feb 2016 15:33:34 -0600
changeset 283641 64a2c41c6f204bf9b413f6976e84d9b5c01b508d
parent 283640 61e89b6101b69128dd6b76991499610b82eb95d9
child 283642 5073d841f77d266cdd0a790a31e5d38d31d8d547
push id71622
push userjwillcox@mozilla.com
push dateTue, 09 Feb 2016 22:52:05 +0000
treeherdermozilla-inbound@5073d841f77d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnwgh
bugs1148544
milestone47.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 1148544 - Figure out UA override once per LoadGroup and cache it, rather than once per channel. r=nwgh
netwerk/base/nsILoadGroup.idl
netwerk/base/nsLoadGroup.cpp
netwerk/base/nsLoadGroup.h
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/UserAgentOverrides.jsm
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpHandler.h
netwerk/protocol/http/nsIHttpProtocolHandler.idl
--- a/netwerk/base/nsILoadGroup.idl
+++ b/netwerk/base/nsILoadGroup.idl
@@ -53,17 +53,17 @@ interface nsILoadGroup : nsIRequest
 
     /**
      * Removes a request from the group.  If this is a foreground request
      * then the groupObserver's onStopRequest will be called.
      *
      * By the time this call ends, aRequest will have been removed from the
      * loadgroup, even if this function throws an exception.
      */
-    void removeRequest(in nsIRequest aRequest, 
+    void removeRequest(in nsIRequest aRequest,
                        in nsISupports aContext,
                        in nsresult aStatus);
 
     /**
      * Returns the requests contained directly in this group.
      * Enumerator element type: nsIRequest.
      */
     readonly attribute nsISimpleEnumerator requests;
@@ -92,9 +92,15 @@ interface nsILoadGroup : nsIRequest
      * to the group - typically via nsIDocShell::defaultLoadFlags on a new
      * docShell.
      * Note that these flags are *not* added to the default request for the
      * load group; it is expected the default request will already have these
      * flags (again, courtesy of setting nsIDocShell::defaultLoadFlags before
      * the docShell has created the default request.)
      */
     attribute nsLoadFlags defaultLoadFlags;
+
+    /**
+     * The cached user agent override created by UserAgentOverrides.jsm. Used
+     * for all sub-resource requests in the loadgroup.
+     */
+    attribute ACString userAgentOverrideCache;
 };
--- a/netwerk/base/nsLoadGroup.cpp
+++ b/netwerk/base/nsLoadGroup.cpp
@@ -167,35 +167,35 @@ NS_IMETHODIMP
 nsLoadGroup::GetName(nsACString &result)
 {
     // XXX is this the right "name" for a load group?
 
     if (!mDefaultLoadRequest) {
         result.Truncate();
         return NS_OK;
     }
-    
+
     return mDefaultLoadRequest->GetName(result);
 }
 
 NS_IMETHODIMP
 nsLoadGroup::IsPending(bool *aResult)
 {
     *aResult = (mForegroundCount > 0) ? true : false;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLoadGroup::GetStatus(nsresult *status)
 {
     if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest)
         return mDefaultLoadRequest->GetStatus(status);
-    
+
     *status = mStatus;
-    return NS_OK; 
+    return NS_OK;
 }
 
 static bool
 AppendRequestsToArray(PLDHashTable* aTable, nsTArray<nsIRequest*> *aArray)
 {
     for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
         auto e = static_cast<RequestMapEntry*>(iter.Get());
         nsIRequest *request = e->mKey;
@@ -475,17 +475,17 @@ nsLoadGroup::AddRequest(nsIRequest *requ
     // request is null, then the load group should inherit its load flags from
     // the request, but also we need to enforce defaultLoadFlags.
     if (mDefaultLoadRequest == request || !mDefaultLoadRequest) {
         rv = MergeDefaultLoadFlags(request, flags);
     } else {
         rv = MergeLoadFlags(request, flags);
     }
     if (NS_FAILED(rv)) return rv;
-    
+
     //
     // Add the request to the list of active requests...
     //
 
     auto entry =
         static_cast<RequestMapEntry*>(mRequests.Add(request, fallible));
     if (!entry) {
         return NS_ERROR_OUT_OF_MEMORY;
@@ -805,20 +805,34 @@ nsLoadGroup::GetDefaultLoadFlags(uint32_
 
 NS_IMETHODIMP
 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags)
 {
     mDefaultLoadFlags = aFlags;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsLoadGroup::GetUserAgentOverrideCache(nsACString & aUserAgentOverrideCache)
+{
+  aUserAgentOverrideCache = mUserAgentOverrideCache;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLoadGroup::SetUserAgentOverrideCache(const nsACString & aUserAgentOverrideCache)
+{
+  mUserAgentOverrideCache = aUserAgentOverrideCache;
+  return NS_OK;
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void 
+void
 nsLoadGroup::TelemetryReport()
 {
     if (mDefaultLoadIsTimed) {
         Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE, mTimedRequests);
         if (mTimedRequests) {
             Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE_FROM_CACHE,
                                   mCachedRequests * 100 / mTimedRequests);
         }
--- a/netwerk/base/nsLoadGroup.h
+++ b/netwerk/base/nsLoadGroup.h
@@ -24,17 +24,17 @@ class nsITimedChannel;
 class nsLoadGroup : public nsILoadGroup,
                     public nsILoadGroupChild,
                     public nsISupportsPriority,
                     public nsSupportsWeakReference,
                     public nsPILoadGroupInternal
 {
 public:
     NS_DECL_AGGREGATED
-    
+
     ////////////////////////////////////////////////////////////////////////////
     // nsIRequest methods:
     NS_DECL_NSIREQUEST
 
     ////////////////////////////////////////////////////////////////////////////
     // nsILoadGroup methods:
     NS_DECL_NSILOADGROUP
     NS_DECL_NSPILOADGROUPINTERNAL
@@ -74,24 +74,26 @@ protected:
     nsCOMPtr<nsISchedulingContext>  mSchedulingContext;
     nsCOMPtr<nsISchedulingContextService> mSchedulingContextService;
 
     nsCOMPtr<nsIRequest>            mDefaultLoadRequest;
     PLDHashTable                    mRequests;
 
     nsWeakPtr                       mObserver;
     nsWeakPtr                       mParentLoadGroup;
-    
+
     nsresult                        mStatus;
     int32_t                         mPriority;
     bool                            mIsCanceling;
 
     /* Telemetry */
     mozilla::TimeStamp              mDefaultRequestCreationTime;
     bool                            mDefaultLoadIsTimed;
     uint32_t                        mTimedRequests;
     uint32_t                        mCachedRequests;
 
     /* For nsPILoadGroupInternal */
     uint32_t                        mTimedNonCachedRequestsUntilOnEndPageLoad;
+
+    nsCString                       mUserAgentOverrideCache;
 };
 
 #endif // nsLoadGroup_h__
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -2459,16 +2459,51 @@ HttpBaseChannel::ShouldIntercept(nsIURI*
                                                         &shouldIntercept);
     if (NS_FAILED(rv)) {
       return false;
     }
   }
   return shouldIntercept;
 }
 
+void
+HttpBaseChannel::SetLoadGroupUserAgentOverride()
+{
+  nsCOMPtr<nsIURI> uri;
+  GetURI(getter_AddRefs(uri));
+  nsAutoCString uriScheme;
+  if (uri) {
+    uri->GetScheme(uriScheme);
+  }
+  nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(mLoadGroup);
+  nsCOMPtr<nsILoadGroup> rootLoadGroup;
+  if (childLoadGroup) {
+    childLoadGroup->GetRootLoadGroup(getter_AddRefs(rootLoadGroup));
+  }
+  if (rootLoadGroup && !uriScheme.EqualsLiteral("file")) {
+    nsAutoCString ua;
+    if (nsContentUtils::IsNonSubresourceRequest(this)) {
+      gHttpHandler->OnUserAgentRequest(this);
+      GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
+      rootLoadGroup->SetUserAgentOverrideCache(ua);
+    } else {
+      GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
+      // Don't overwrite the UA if it is already set (eg by an XHR with explicit UA).
+      if (ua.IsEmpty()) {
+        rootLoadGroup->GetUserAgentOverrideCache(ua);
+        SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua, false);
+      }
+    }
+  } else {
+    // If the root loadgroup doesn't exist or if the channel's URI's scheme is "file",
+    // fall back on getting the UA override per channel.
+    gHttpHandler->OnUserAgentRequest(this);
+  }
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsITraceableChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::SetNewListener(nsIStreamListener *aListener, nsIStreamListener **_retval)
 {
   if (!mTracingEnabled)
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -348,16 +348,18 @@ protected:
   nsIPrincipal *GetURIPrincipal();
 
   bool BypassServiceWorker() const;
 
   // Returns true if this channel should intercept the network request and prepare
   // for a possible synthesized response instead.
   bool ShouldIntercept(nsIURI* aURI = nullptr);
 
+  void SetLoadGroupUserAgentOverride();
+
   friend class PrivateBrowsingChannel<HttpBaseChannel>;
   friend class InterceptFailedOnStop;
 
   nsCOMPtr<nsIURI>                  mURI;
   nsCOMPtr<nsIURI>                  mOriginalURI;
   nsCOMPtr<nsIURI>                  mDocumentURI;
   nsCOMPtr<nsIStreamListener>       mListener;
   nsCOMPtr<nsISupports>             mListenerContext;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1800,19 +1800,22 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
   if (mCanceled) {
     // We may have been canceled already, either by on-modify-request
     // listeners or by load group observers; in that case, don't create IPDL
     // connection. See nsHttpChannel::AsyncOpen().
     AsyncAbort(mStatus);
     return NS_OK;
   }
 
-  // Set user agent override
+  // Set user agent override from docshell
   HttpBaseChannel::SetDocshellUserAgentOverride();
 
+  // Set user agent override from loadgroup
+  HttpBaseChannel::SetLoadGroupUserAgentOverride();
+
   MOZ_ASSERT_IF(mPostRedirectChannelShouldUpgrade,
                 mPostRedirectChannelShouldIntercept);
   bool shouldUpgrade = mPostRedirectChannelShouldUpgrade;
   if (mPostRedirectChannelShouldIntercept ||
       ShouldInterceptURI(mURI, shouldUpgrade)) {
     mResponseCouldBeSynthesized = true;
 
     nsCOMPtr<nsINetworkInterceptController> controller;
--- a/netwerk/protocol/http/UserAgentOverrides.jsm
+++ b/netwerk/protocol/http/UserAgentOverrides.jsm
@@ -41,19 +41,19 @@ this.UserAgentOverrides = {
 
     gPrefBranch = Services.prefs.getBranch("general.useragent.override.");
     gPrefBranch.addObserver("", buildOverrides, false);
 
     ppmm.addMessageListener(OVERRIDE_MESSAGE, this);
     Services.prefs.addObserver(PREF_OVERRIDES_ENABLED, buildOverrides, false);
 
     try {
-      Services.obs.addObserver(HTTP_on_modify_request, "http-on-modify-request", false);
+      Services.obs.addObserver(HTTP_on_useragent_request, "http-on-useragent-request", false);
     } catch (x) {
-      // The http-on-modify-request notification is disallowed in content processes.
+      // 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]);
         }
@@ -113,17 +113,17 @@ this.UserAgentOverrides = {
     if (!gInitialized)
       return;
     gInitialized = false;
 
     gPrefBranch.removeObserver("", buildOverrides);
 
     Services.prefs.removeObserver(PREF_OVERRIDES_ENABLED, buildOverrides);
 
-    Services.obs.removeObserver(HTTP_on_modify_request, "http-on-modify-request");
+    Services.obs.removeObserver(HTTP_on_useragent_request, "http-on-useragent-request");
   },
 
   receiveMessage: function(aMessage) {
     let name = aMessage.name;
     switch (name) {
       case OVERRIDE_MESSAGE:
         let uri = Services.io.newURI(aMessage.data.uri, null, null);
         return this.getOverrideForURI(uri);
@@ -164,17 +164,17 @@ function buildOverrides() {
     let userAgent = getUserAgentFromOverride(override);
 
     if (userAgent != DEFAULT_UA) {
       gOverrides.set(domain, userAgent);
     }
   }
 }
 
-function HTTP_on_modify_request(aSubject, aTopic, aData) {
+function HTTP_on_useragent_request(aSubject, aTopic, aData) {
   let channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
 
   for (let callback of gOverrideFunctions) {
     let modifiedUA = callback(channel, DEFAULT_UA);
     if (modifiedUA) {
       channel.setRequestHeader("User-Agent", modifiedUA, false);
       return;
     }
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5173,16 +5173,21 @@ nsHttpChannel::BeginConnect()
 
     // check to see if authorization headers should be included
     // mCustomAuthHeader is set in AsyncOpen if we find Authorization header
     mAuthProvider->AddAuthorizationHeaders(mCustomAuthHeader);
 
     // notify "http-on-modify-request" observers
     CallOnModifyRequestObservers();
 
+    // If mLoadGroup is null, e10s is enabled and this will be handled by HttpChannelChild.
+    if (mLoadGroup) {
+      HttpBaseChannel::SetLoadGroupUserAgentOverride();
+    }
+
     // Check to see if we should redirect this channel elsewhere by
     // nsIHttpChannel.redirectTo API request
     if (mAPIRedirectToURI) {
         return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
     }
     // Check to see if this principal exists on local blocklists.
     RefPtr<nsChannelClassifier> channelClassifier = new nsChannelClassifier();
     if (mLoadFlags & LOAD_CLASSIFY_URI) {
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -270,16 +270,22 @@ public:
     }
 
     // Called by the channel before writing a request
     void OnModifyRequest(nsIHttpChannel *chan)
     {
         NotifyObservers(chan, NS_HTTP_ON_MODIFY_REQUEST_TOPIC);
     }
 
+    // Called by the channel and cached in the loadGroup
+    void OnUserAgentRequest(nsIHttpChannel *chan)
+    {
+      NotifyObservers(chan, NS_HTTP_ON_USERAGENT_REQUEST_TOPIC);
+    }
+
     // Called by the channel once headers are available
     void OnExamineResponse(nsIHttpChannel *chan)
     {
         NotifyObservers(chan, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
     }
 
     // Called by the channel once headers have been merged with cached headers
     void OnExamineMergedResponse(nsIHttpChannel *chan)
--- a/netwerk/protocol/http/nsIHttpProtocolHandler.idl
+++ b/netwerk/protocol/http/nsIHttpProtocolHandler.idl
@@ -108,10 +108,19 @@ interface nsIHttpProtocolHandler : nsIPr
 
 /**
  * The observer of this topic is notified before data is read from the cache.
  * The notification is sent if and only if there is no network communication
  * at all.
  */
 #define NS_HTTP_ON_EXAMINE_CACHED_RESPONSE_TOPIC "http-on-examine-cached-response"
 
+/**
+ * Before an HTTP request corresponding to a channel with the LOAD_DOCUMENT_URI
+ * flag is sent to the server, this observer topic is notified. The observer of
+ * this topic can then choose to modify the user agent for this request before
+ * the request is actually sent to the server. Additionally, the modified user
+ * agent will be propagated to sub-resource requests from the same load group.
+ */
+#define NS_HTTP_ON_USERAGENT_REQUEST_TOPIC "http-on-useragent-request"
+
 
 %}