Bug 1507852 - Add GeckoView API to toggle desktop viewport support r=geckoview-reviewers,snorp,agi
authorRandall Barker <rbarker@mozilla.com>
Tue, 08 Jan 2019 00:58:16 +0000
changeset 512788 953e31ae0e5d51f97dcd7070bea05c0ffd7f1c2c
parent 512787 275e1a2e1d91468d53642a12fbc41b2cb456cab7
child 512789 62e3131970c01474e08e9ddd69c0d06fe966ce37
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgeckoview-reviewers, snorp, agi
bugs1507852
milestone66.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 1507852 - Add GeckoView API to toggle desktop viewport support r=geckoview-reviewers,snorp,agi Differential Revision: https://phabricator.services.mozilla.com/D15598
mobile/android/chrome/geckoview/GeckoViewSettingsChild.js
mobile/android/geckoview/api.txt
mobile/android/geckoview/src/androidTest/assets/www/viewport.html
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
--- a/mobile/android/chrome/geckoview/GeckoViewSettingsChild.js
+++ b/mobile/android/chrome/geckoview/GeckoViewSettingsChild.js
@@ -31,33 +31,39 @@ XPCOMUtils.defineLazyGetter(
     return MOBILE_USER_AGENT.replace(/Mobile/, "Mobile 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;
 
+// This needs to match GeckoSessionSettings.java
+const VIEWPORT_MODE_MOBILE = 0;
+const VIEWPORT_MODE_DESKTOP = 1;
+
 // Handles GeckoView content settings including:
 // * tracking protection
 // * user agent mode
 class GeckoViewSettingsChild extends GeckoViewChildModule {
   onInit() {
     debug `onInit`;
     this._userAgentMode = USER_AGENT_MODE_MOBILE;
     this._userAgentOverride = null;
+    this._viewportMode = VIEWPORT_MODE_MOBILE;
   }
 
   onSettingsUpdate() {
     debug `onSettingsUpdate ${this.settings}`;
 
     this.displayMode = this.settings.displayMode;
     this.useTrackingProtection = !!this.settings.useTrackingProtection;
     this.userAgentMode = this.settings.userAgentMode;
     this.userAgentOverride = this.settings.userAgentOverride;
+    this.viewportMode = this.settings.viewportMode;
     this.allowJavascript = this.settings.allowJavascript;
   }
 
   get useTrackingProtection() {
     return docShell.useTrackingProtection;
   }
 
   set useTrackingProtection(aUse) {
@@ -87,21 +93,16 @@ class GeckoViewSettingsChild extends Gec
     }
     this._userAgentMode = aMode;
     const docShell = content && GeckoViewUtils.getRootDocShell(content);
     if (docShell) {
       docShell.customUserAgent = this.userAgent;
     } else {
       warn `Failed to set custom user agent. Doc shell not found`;
     }
-    if (this._userAgentOverride !== null) {
-      return;
-    }
-    const utils = content.windowUtils;
-    utils.setDesktopModeViewport(aMode === USER_AGENT_MODE_DESKTOP);
   }
 
   get userAgentOverride() {
     return this._userAgentOverride;
   }
 
   set userAgentOverride(aUserAgent) {
     if (aUserAgent === this._userAgentOverride) {
@@ -109,37 +110,44 @@ class GeckoViewSettingsChild extends Gec
     }
     this._userAgentOverride = aUserAgent;
     const docShell = content && GeckoViewUtils.getRootDocShell(content);
     if (docShell) {
       docShell.customUserAgent = this.userAgent;
     } else {
       warn `Failed to set custom user agent. Doc shell not found`;
     }
-    const utils = content.windowUtils;
-    if (aUserAgent === null) {
-      utils.setDesktopModeViewport(this._userAgentMode === USER_AGENT_MODE_DESKTOP);
-      return;
-    }
-    utils.setDesktopModeViewport(false);
   }
 
   get displayMode() {
     const docShell = content && GeckoViewUtils.getRootDocShell(content);
     return docShell ? docShell.displayMode
                     : Ci.nsIDocShell.DISPLAY_MODE_BROWSER;
   }
 
   set displayMode(aMode) {
     const docShell = content && GeckoViewUtils.getRootDocShell(content);
     if (docShell) {
       docShell.displayMode = aMode;
     }
   }
 
+  get viewportMode() {
+    return this._viewportMode;
+  }
+
+  set viewportMode(aMode) {
+    if (aMode === this._viewportMode) {
+      return;
+    }
+    this._viewportMode = aMode;
+    const utils = content.windowUtils;
+    utils.setDesktopModeViewport(aMode === VIEWPORT_MODE_DESKTOP);
+  }
+
   get allowJavascript() {
     return docShell.allowJavascript;
   }
 
   set allowJavascript(aAllowJavascript) {
     docShell.allowJavascript = aAllowJavascript;
   }
 }
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -665,16 +665,19 @@ package org.mozilla.geckoview {
     field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Integer> USER_AGENT_MODE;
     field public static final int USER_AGENT_MODE_DESKTOP = 1;
     field public static final int USER_AGENT_MODE_MOBILE = 0;
     field public static final int USER_AGENT_MODE_VR = 2;
     field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.String> USER_AGENT_OVERRIDE;
     field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> USE_MULTIPROCESS;
     field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> USE_PRIVATE_MODE;
     field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Boolean> USE_TRACKING_PROTECTION;
+    field public static final org.mozilla.geckoview.GeckoSessionSettings.Key<java.lang.Integer> VIEWPORT_MODE;
+    field public static final int VIEWPORT_MODE_DESKTOP = 1;
+    field public static final int VIEWPORT_MODE_MOBILE = 0;
   }
 
   public static class GeckoSessionSettings.Key<T> {
   }
 
   @android.support.annotation.UiThread public class GeckoView extends android.widget.FrameLayout {
     ctor public GeckoView(android.content.Context);
     ctor public GeckoView(android.content.Context, android.util.AttributeSet);
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/www/viewport.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<style type="text/css">
+#wide {
+   background-color: rgb(200, 0, 0);
+   width: 100%;
+   height: 40px;
+}
+</style>
+</head>
+<body>
+<div id="wide"></div>
+</body>
+</html>
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
@@ -43,16 +43,17 @@ open class BaseSessionTest(noErrorCollec
         const val TITLE_CHANGE_HTML_PATH = "/assets/www/titleChange.html"
         const val TRACKERS_PATH = "/assets/www/trackers.html"
         const val VIDEO_OGG_PATH = "/assets/www/ogg.html"
         const val VIDEO_MP4_PATH = "/assets/www/mp4.html"
         const val VIDEO_WEBM_PATH = "/assets/www/webm.html"
         const val VIDEO_BAD_PATH = "/assets/www/badVideoPath.html"
         const val UNKNOWN_HOST_URI = "http://www.test.invalid/"
         const val FULLSCREEN_PATH = "/assets/www/fullscreen.html"
+        const val VEIWPORT_PATH = "/assets/www/viewport.html"
     }
 
     @get:Rule val sessionRule = GeckoSessionTestRule()
 
     @get:Rule val errors = ErrorCollector()
 
     val mainSession get() = sessionRule.session
 
--- 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
@@ -12,16 +12,17 @@ import org.mozilla.geckoview.GeckoSessio
 import org.mozilla.geckoview.GeckoSessionSettings
 import org.mozilla.geckoview.WebRequestError
 
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.NullDelegate
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ReuseSession
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDevToolsAPI
+import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
 import org.mozilla.geckoview.test.util.Callbacks
 
 import android.support.test.filters.MediumTest
 import android.support.test.runner.AndroidJUnit4
 import org.hamcrest.Matchers.*
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -486,16 +487,53 @@ class NavigationDelegateTest : BaseSessi
         sessionRule.session.reload()
         sessionRule.session.waitForPageStop()
 
         userAgent = sessionRule.session.evaluateJS(userAgentJs) as String
         assertThat("User agent should again be reported as VR after disabling override in onLoadRequest",
                 userAgent, containsString(vrSubStr))
     }
 
+    @WithDevToolsAPI
+    @WithDisplay(width = 600, height = 200)
+    @Test fun viewportMode() {
+        sessionRule.session.loadTestPath(VEIWPORT_PATH)
+        sessionRule.waitForPageStop()
+
+        val desktopInnerWidth = 980.0
+        val physicalWidth = 600.0
+        val pixelRatio = sessionRule.session.evaluateJS("window.devicePixelRatio") as Double
+        val mobileInnerWidth = physicalWidth / pixelRatio
+        val innerWidthJs = "window.innerWidth"
+
+        var innerWidth = sessionRule.session.evaluateJS(innerWidthJs) as Double
+        assertThat("innerWidth should be equal to $mobileInnerWidth",
+                innerWidth, closeTo(mobileInnerWidth, 0.1))
+
+        sessionRule.session.settings.setInt(
+                GeckoSessionSettings.VIEWPORT_MODE, GeckoSessionSettings.VIEWPORT_MODE_DESKTOP)
+
+        sessionRule.session.reload()
+        sessionRule.session.waitForPageStop()
+
+        innerWidth = sessionRule.session.evaluateJS(innerWidthJs) as Double
+        assertThat("innerWidth should be equal to $desktopInnerWidth", innerWidth,
+                closeTo(desktopInnerWidth, 0.1))
+
+        sessionRule.session.settings.setInt(
+                GeckoSessionSettings.VIEWPORT_MODE, GeckoSessionSettings.VIEWPORT_MODE_MOBILE)
+
+        sessionRule.session.reload()
+        sessionRule.session.waitForPageStop()
+
+        innerWidth = sessionRule.session.evaluateJS(innerWidthJs) as Double
+        assertThat("innerWidth should be equal to $mobileInnerWidth again",
+                innerWidth, closeTo(mobileInnerWidth, 0.1))
+    }
+
     @Test fun telemetrySnapshots() {
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
         sessionRule.waitForPageStop()
 
         val telemetry = sessionRule.runtime.telemetry
         val result = sessionRule.waitForResult(telemetry.getSnapshots(false))
 
         assertThat("Snapshots should not be null",
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSessionSettings.java
@@ -29,16 +29,20 @@ public final class GeckoSessionSettings 
     public static final int DISPLAY_MODE_STANDALONE = 2;
     public static final int DISPLAY_MODE_FULLSCREEN = 3;
 
     // This needs to match GeckoViewSettingsChild.js and GeckoViewSettings.jsm
     public static final int USER_AGENT_MODE_MOBILE = 0;
     public static final int USER_AGENT_MODE_DESKTOP = 1;
     public static final int USER_AGENT_MODE_VR = 2;
 
+    // This needs to match GeckoViewSettingsChild.js
+    public static final int VIEWPORT_MODE_MOBILE = 0;
+    public static final int VIEWPORT_MODE_DESKTOP = 1;
+
     public static class Key<T> {
         /* package */ final String name;
         /* package */ final boolean initOnly;
         /* package */ final Collection<T> values;
 
         /* package */ Key(final String name) {
             this(name, /* initOnly */ false, /* values */ null);
         }
@@ -93,16 +97,23 @@ public final class GeckoSessionSettings 
     /**
      * Key to specify the user agent override string.
      * Set value to null to use the user agent specified by USER_AGENT_MODE.
      */
     public static final Key<String> USER_AGENT_OVERRIDE =
         new Key<String>("userAgentOverride", /* initOnly */ false, /* values */ null);
 
     /**
+     * Key to specify which viewport mode we should use.
+     */
+    public static final Key<Integer> VIEWPORT_MODE =
+        new Key<Integer>("viewportMode", /* initOnly */ false,
+                         Arrays.asList(VIEWPORT_MODE_MOBILE, VIEWPORT_MODE_DESKTOP));
+
+    /**
      * Key to specify which display-mode we should use.
      */
     public static final Key<Integer> DISPLAY_MODE =
         new Key<Integer>("displayMode", /* initOnly */ false,
                          Arrays.asList(DISPLAY_MODE_BROWSER, DISPLAY_MODE_MINIMAL_UI,
                                        DISPLAY_MODE_STANDALONE, DISPLAY_MODE_FULLSCREEN));
 
     /**
@@ -148,16 +159,17 @@ public final class GeckoSessionSettings 
         mBundle.putBoolean(USE_TRACKING_PROTECTION.name, false);
         mBundle.putBoolean(USE_PRIVATE_MODE.name, false);
         mBundle.putBoolean(USE_MULTIPROCESS.name, true);
         mBundle.putBoolean(SUSPEND_MEDIA_WHEN_INACTIVE.name, false);
         mBundle.putBoolean(ALLOW_JAVASCRIPT.name, true);
         mBundle.putBoolean(FULL_ACCESSIBILITY_TREE.name, false);
         mBundle.putInt(USER_AGENT_MODE.name, USER_AGENT_MODE_MOBILE);
         mBundle.putString(USER_AGENT_OVERRIDE.name, null);
+        mBundle.putInt(VIEWPORT_MODE.name, VIEWPORT_MODE_MOBILE);
         mBundle.putInt(DISPLAY_MODE.name, DISPLAY_MODE_BROWSER);
     }
 
     public void setBoolean(final @NonNull Key<Boolean> key, final boolean value) {
         synchronized (mBundle) {
             if (valueChangedLocked(key, value)) {
                 mBundle.putBoolean(key.name, value);
                 dispatchUpdate();
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
@@ -5,16 +5,19 @@ layout: geckoview
 <h1> GeckoView API Changelog. </h1>
 
 ## v66
 - Added [`@NonNull`][66.1] or [`@Nullable`][66.2] to all APIs.
 
 [66.1]: https://developer.android.com/reference/android/support/annotation/NonNull
 [66.2]: https://developer.android.com/reference/android/support/annotation/Nullable
 
+- Added GeckoRuntimeSetting for enabling desktop viewport. Desktop viewport is
+  no longer set by `USER_AGENT_MODE_DESKTOP` and must be set separately.
+
 ## v65
 - Moved [`CompositorController`][65.1], [`DynamicToolbarAnimator`][65.2],
   [`OverscrollEdgeEffect`][65.3], [`PanZoomController`][65.4] from
   `org.mozilla.gecko.gfx` to [`org.mozilla.geckoview`][65.5]
 
 [65.1]: ../CompositorController.html
 [65.2]: ../DynamicToolbarAnimator.html
 [65.3]: ../OverscrollEdgeEffect.html
@@ -93,9 +96,9 @@ layout: geckoview
 [65.23]: ../GeckoSession.FinderResult.html
 
 - Update [`CrashReporter#sendCrashReport`][65.24] to return the crash ID as a
   [`GeckoResult<String>`][65.25].
 
 [65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
 [65.25]: ../GeckoResult.html
 
-[api-version]: 8827d6c8c7c2aa54cfad31c21aca5dc7d6d42311
+[api-version]: a0f14e2c654491c9f12b3e1ce46e5e59705ef917