Bug 1454444: Add GeckoSession.getUserAgent. r=snorp,esawin
authorAgi Sferro <agi@mozilla.com>
Thu, 11 Oct 2018 15:31:15 +0000
changeset 496496 ac0fc3f030290176619301bc08e25694a3741464
parent 496495 6e9980bbb079c59e20495ec1161eb86f0f88d706
child 496497 6f88d27fbe44c6107ca2c9f1a7ef020ccfd900f6
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, esawin
bugs1454444
milestone64.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 1454444: Add GeckoSession.getUserAgent. r=snorp,esawin This patch adds a new API to GeckoSession to get the currently used UserAgent. Differential Revision: https://phabricator.services.mozilla.com/D8022
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
mobile/android/modules/geckoview/GeckoViewSettings.jsm
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
@@ -338,35 +338,48 @@ class NavigationDelegateTest : BaseSessi
         val userAgentJs = "window.navigator.userAgent"
         val mobileSubStr = "Mobile"
         val desktopSubStr = "X11"
 
         assertThat("User agent should be set to mobile",
                    sessionRule.session.evaluateJS(userAgentJs) as String,
                    containsString(mobileSubStr))
 
+        var userAgent = sessionRule.waitForResult(sessionRule.session.getUserAgent());
+        assertThat("User agent should be reported as mobile",
+                    userAgent, containsString(mobileSubStr));
+
         sessionRule.session.settings.setInt(
             GeckoSessionSettings.USER_AGENT_MODE, GeckoSessionSettings.USER_AGENT_MODE_DESKTOP)
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         assertThat("User agent should be set to desktop",
                    sessionRule.session.evaluateJS(userAgentJs) as String,
                    containsString(desktopSubStr))
 
+        userAgent = sessionRule.waitForResult(sessionRule.session.getUserAgent());
+        assertThat("User agent should be reported as desktop",
+                    userAgent, containsString(desktopSubStr));
+
         sessionRule.session.settings.setInt(
             GeckoSessionSettings.USER_AGENT_MODE, GeckoSessionSettings.USER_AGENT_MODE_MOBILE)
 
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         assertThat("User agent should be set to mobile",
                    sessionRule.session.evaluateJS(userAgentJs) as String,
                    containsString(mobileSubStr))
+
+        userAgent = sessionRule.waitForResult(sessionRule.session.getUserAgent());
+        assertThat("User agent should be reported as mobile",
+                    userAgent, containsString(mobileSubStr));
+
     }
 
     @Test fun telemetrySnapshots() {
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
         sessionRule.waitForPageStop()
 
         val telemetry = sessionRule.runtime.telemetry
         val result = sessionRule.waitForResult(telemetry.getSnapshots(false))
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -661,16 +661,32 @@ public class GeckoSession extends LayerS
         @Override // PermissionDelegate.MediaCallback
         public void grant(final PermissionDelegate.MediaSource video, final PermissionDelegate.MediaSource audio) {
             grant(video != null ? video.id : null,
                   audio != null ? audio.id : null);
         }
     }
 
     /**
+     * Get the current user agent string for this GeckoSession.
+     *
+     * @return a {@link GeckoResult} containing the UserAgent string
+     */
+    public @NonNull GeckoResult<String> getUserAgent() {
+        final CallbackResult<String> result = new CallbackResult<String>() {
+            @Override
+            public void sendSuccess(final Object value) {
+                complete((String) value);
+            }
+        };
+        mEventDispatcher.dispatch("GeckoView:GetUserAgent", null, result);
+        return result;
+    }
+
+    /**
      * Get the current prompt delegate for this GeckoSession.
      * @return PromptDelegate instance or null if using default delegate.
      */
     public PermissionDelegate getPermissionDelegate() {
         return mPermissionHandler.getDelegate();
     }
 
     /**
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -321,16 +321,34 @@ public class GeckoViewActivity extends A
         mPendingDownloads = new LinkedList<>();
 
         for (GeckoSession.WebResponseInfo response : downloads) {
             downloadFile(response);
         }
     }
 
     private void downloadFile(GeckoSession.WebResponseInfo response) {
+        mGeckoSession
+                .getUserAgent()
+                .then(new GeckoResult.OnValueListener<String, Void>() {
+            @Override
+            public GeckoResult<Void> onValue(String userAgent) throws Throwable {
+                downloadFile(response, userAgent);
+                return null;
+            }
+        }, new GeckoResult.OnExceptionListener<Void>() {
+            @Override
+            public GeckoResult<Void> onException(Throwable exception) throws Throwable {
+                // getUserAgent() cannot fail.
+                throw new IllegalStateException("Could not get UserAgent string.");
+            }
+        });
+    }
+
+    private void downloadFile(GeckoSession.WebResponseInfo response, String userAgent) {
         if (ContextCompat.checkSelfPermission(GeckoViewActivity.this,
                 Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
             mPendingDownloads.add(response);
             ActivityCompat.requestPermissions(GeckoViewActivity.this,
                     new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                     REQUEST_WRITE_EXTERNAL_STORAGE);
             return;
         }
@@ -338,16 +356,17 @@ public class GeckoViewActivity extends A
         final Uri uri = Uri.parse(response.uri);
         final String filename = response.filename != null ? response.filename : uri.getLastPathSegment();
 
         DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
         DownloadManager.Request req = new DownloadManager.Request(uri);
         req.setMimeType(response.contentType);
         req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
         req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
+        req.addRequestHeader("User-Agent", userAgent);
         manager.enqueue(req);
     }
 
     private String mErrorTemplate;
     private String createErrorPage(final String error) {
         if (mErrorTemplate == null) {
             InputStream stream = null;
             BufferedReader reader = null;
--- a/mobile/android/modules/geckoview/GeckoViewSettings.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewSettings.jsm
@@ -10,28 +10,34 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Services: "resource://gre/modules/Services.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(
+  this, "MOBILE_USER_AGENT",
+  function() {
+    return Cc["@mozilla.org/network/protocol;1?name=http"]
+           .getService(Ci.nsIHttpProtocolHandler).userAgent;
+  });
+
+XPCOMUtils.defineLazyGetter(
   this, "DESKTOP_USER_AGENT",
   function() {
-    return Cc["@mozilla.org/network/protocol;1?name=http"]
-           .getService(Ci.nsIHttpProtocolHandler).userAgent
+    return MOBILE_USER_AGENT
            .replace(/Android \d.+?; [a-zA-Z]+/, "X11; Linux x86_64")
            .replace(/Gecko\/[0-9\.]+/, "Gecko/20100101");
   });
 
 XPCOMUtils.defineLazyGetter(
   this, "VR_USER_AGENT",
   function() {
-    return Cc["@mozilla.org/network/protocol;1?name=http"]
+    return MOBILE_USER_AGENT
            .getService(Ci.nsIHttpProtocolHandler).userAgent
            .replace(/Android \d+; [a-zA-Z]+/, "VR");
   });
 
 // This needs to match GeckoSessionSettings.java
 const USER_AGENT_MODE_MOBILE = 0;
 const USER_AGENT_MODE_DESKTOP = 1;
 const USER_AGENT_MODE_VR = 2;
@@ -47,16 +53,30 @@ class GeckoViewSettings extends GeckoVie
   }
 
   onInit() {
     debug `onInit`;
     this._useTrackingProtection = false;
     this._userAgentMode = USER_AGENT_MODE_MOBILE;
     // Required for safe browsing and tracking protection.
     SafeBrowsing.init();
+
+    this.registerListener([
+      "GeckoView:GetUserAgent",
+    ]);
+  }
+
+  onEvent(aEvent, aData, aCallback) {
+    debug `onEvent ${aEvent} ${aData}`;
+
+    switch (aEvent) {
+      case "GeckoView:GetUserAgent": {
+        aCallback.onSuccess(this.userAgent);
+      }
+    }
   }
 
   onSettingsUpdate() {
     const settings = this.settings;
     debug `onSettingsUpdate: ${settings}`;
 
     this.displayMode = settings.displayMode;
     this.userAgentMode = settings.userAgentMode;
@@ -70,21 +90,30 @@ class GeckoViewSettings extends GeckoVie
     debug `observer`;
 
     let channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
 
     if (this.browser.outerWindowID !== channel.topLevelOuterContentWindowId) {
       return;
     }
 
+    if (this.userAgentMode === USER_AGENT_MODE_DESKTOP ||
+        this.userAgentMode === USER_AGENT_MODE_VR) {
+      channel.setRequestHeader("User-Agent", this.userAgent, false);
+    }
+  }
+
+  get userAgent() {
     if (this.userAgentMode === USER_AGENT_MODE_DESKTOP) {
-      channel.setRequestHeader("User-Agent", DESKTOP_USER_AGENT, false);
-    } else if (this.userAgentMode === USER_AGENT_MODE_VR) {
-      channel.setRequestHeader("User-Agent", VR_USER_AGENT, false);
+      return DESKTOP_USER_AGENT;
     }
+    if (this.userAgentMode === USER_AGENT_MODE_VR) {
+      return VR_USER_AGENT;
+    }
+    return MOBILE_USER_AGENT;
   }
 
   get userAgentMode() {
     return this._userAgentMode;
   }
 
   set userAgentMode(aMode) {
     if (this.userAgentMode === aMode) {