Bug 1505132 - Show compatibility warning message in new aboutdebugging;r=daisuke,ladybenko
authorJulian Descottes <jdescottes@mozilla.com>
Mon, 18 Feb 2019 08:55:54 +0000
changeset 459764 4274148772f96f5450f998247dec7d9e14ffe67e
parent 459763 0e6da56d7124ec7604a332e1fa6f45b6fbc34f72
child 459765 933c04b2755f1203ba842e34b84274d2c90d49f5
push id78384
push userjdescottes@mozilla.com
push dateMon, 18 Feb 2019 10:46:45 +0000
treeherderautoland@933c04b2755f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaisuke, ladybenko
bugs1505132
milestone67.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 1505132 - Show compatibility warning message in new aboutdebugging;r=daisuke,ladybenko Depends on D18984 Differential Revision: https://phabricator.services.mozilla.com/D18985
devtools/client/aboutdebugging-new/src/actions/runtimes.js
devtools/client/aboutdebugging-new/src/components/CompatibilityWarning.js
devtools/client/aboutdebugging-new/src/components/RuntimePage.js
devtools/client/aboutdebugging-new/src/components/moz.build
devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
devtools/client/aboutdebugging-new/src/types/runtime.js
devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
devtools/client/shared/remote-debugging/version-checker.js
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -39,33 +39,20 @@ const {
   UPDATE_RUNTIME_MULTIE10S_FAILURE,
   UPDATE_RUNTIME_MULTIE10S_START,
   UPDATE_RUNTIME_MULTIE10S_SUCCESS,
   WATCH_RUNTIME_FAILURE,
   WATCH_RUNTIME_START,
   WATCH_RUNTIME_SUCCESS,
 } = require("../constants");
 
-async function getRuntimeInfo(runtime, clientWrapper) {
-  const { type } = runtime;
-  const { name, channel, deviceName, isMultiE10s, version } =
-    await clientWrapper.getDeviceDescription();
-  const icon =
-    (channel === "release" || channel === "beta" || channel === "aurora")
-      ? `chrome://devtools/skin/images/aboutdebugging-firefox-${ channel }.svg`
-      : "chrome://devtools/skin/images/aboutdebugging-firefox-nightly.svg";
-
-  return {
-    deviceName,
-    icon,
-    isMultiE10s,
-    name,
-    type,
-    version,
-  };
+async function getRuntimeIcon(channel) {
+  return (channel === "release" || channel === "beta" || channel === "aurora")
+    ? `chrome://devtools/skin/images/aboutdebugging-firefox-${ channel }.svg`
+    : "chrome://devtools/skin/images/aboutdebugging-firefox-nightly.svg";
 }
 
 function onRemoteDebuggerClientClosed() {
   window.AboutDebugging.onNetworkLocationsUpdated();
   window.AboutDebugging.onUSBRuntimesUpdated();
 }
 
 function onMultiE10sUpdated() {
@@ -73,40 +60,46 @@ function onMultiE10sUpdated() {
 }
 
 function connectRuntime(id) {
   return async (dispatch, getState) => {
     dispatch({ type: CONNECT_RUNTIME_START });
     try {
       const runtime = findRuntimeById(id, getState().runtimes);
       const clientWrapper = await createClientForRuntime(runtime);
-      const info = await getRuntimeInfo(runtime, clientWrapper);
-      const { isMultiE10s } = info;
-      delete info.isMultiE10s;
+
+      const deviceDescription = await clientWrapper.getDeviceDescription();
+      const compatibilityReport = await clientWrapper.checkVersionCompatibility();
+      const icon = await getRuntimeIcon(deviceDescription.channel);
 
       const {
         CONNECTION_PROMPT,
         PERMANENT_PRIVATE_BROWSING,
         SERVICE_WORKERS_ENABLED,
       } = RUNTIME_PREFERENCE;
-
       const connectionPromptEnabled =
         await clientWrapper.getPreference(CONNECTION_PROMPT, false);
-
       const privateBrowsing =
         await clientWrapper.getPreference(PERMANENT_PRIVATE_BROWSING, false);
       const serviceWorkersEnabled =
         await clientWrapper.getPreference(SERVICE_WORKERS_ENABLED, true);
       const serviceWorkersAvailable = serviceWorkersEnabled && !privateBrowsing;
 
       const runtimeDetails = {
         clientWrapper,
+        compatibilityReport,
         connectionPromptEnabled,
-        info,
-        isMultiE10s,
+        info: {
+          deviceName: deviceDescription.deviceName,
+          icon,
+          name: deviceDescription.name,
+          type: runtime.type,
+          version: deviceDescription.version,
+        },
+        isMultiE10s: deviceDescription.isMultiE10s,
         serviceWorkersAvailable,
       };
 
       const deviceFront = await clientWrapper.getFront("device");
       if (deviceFront) {
         deviceFront.on("multi-e10s-updated", onMultiE10sUpdated);
       }
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/CompatibilityWarning.js
@@ -0,0 +1,67 @@
+/* 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 { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+const FluentReact = require("devtools/client/shared/vendor/fluent-react");
+const Localized = createFactory(FluentReact.Localized);
+
+const Message = createFactory(require("./shared/Message"));
+
+const { MESSAGE_LEVEL } = require("../constants");
+const { COMPATIBILITY_STATUS } = require("devtools/client/shared/remote-debugging/version-checker");
+
+const TROUBLESHOOTING_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Troubleshooting";
+
+const Types = require("../types/index");
+
+class CompatibilityWarning extends PureComponent {
+  static get propTypes() {
+    return {
+      compatibilityReport: Types.compatibilityReport.isRequired,
+    };
+  }
+
+  render() {
+    const { localID, localVersion, minVersion, runtimeID, runtimeVersion, status } =
+      this.props.compatibilityReport;
+
+    if (status === COMPATIBILITY_STATUS.COMPATIBLE) {
+      return null;
+    }
+
+    const localizationId = status === COMPATIBILITY_STATUS.TOO_OLD ?
+      "about-debugging-runtime-version-too-old" :
+      "about-debugging-runtime-version-too-recent";
+
+    return Message(
+      {
+        level: MESSAGE_LEVEL.WARNING,
+      },
+      Localized(
+        {
+          id: localizationId,
+          a: dom.a({
+            href: TROUBLESHOOTING_URL,
+            target: "_blank",
+          }),
+          $localID: localID,
+          $localVersion: localVersion,
+          $minVersion: minVersion,
+          $runtimeID: runtimeID,
+          $runtimeVersion: runtimeVersion,
+        },
+        dom.p(
+          {},
+          localizationId,
+        ),
+      )
+    );
+  }
+}
+
+module.exports = CompatibilityWarning;
--- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
@@ -7,16 +7,17 @@
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 
+const CompatibilityWarning = createFactory(require("./CompatibilityWarning"));
 const ConnectionPromptSetting = createFactory(require("./ConnectionPromptSetting"));
 const DebugTargetPane = createFactory(require("./debugtarget/DebugTargetPane"));
 const ExtensionDetail = createFactory(require("./debugtarget/ExtensionDetail"));
 const InspectAction = createFactory(require("./debugtarget/InspectAction"));
 const ProfilerDialog = createFactory(require("./ProfilerDialog"));
 const RuntimeInfo = createFactory(require("./RuntimeInfo"));
 const ServiceWorkerAction = createFactory(require("./debugtarget/ServiceWorkerAction"));
 const ServiceWorkersWarning = createFactory(require("./ServiceWorkersWarning"));
@@ -140,23 +141,25 @@ class RuntimePage extends PureComponent 
 
     if (!runtimeDetails) {
       // runtimeInfo can be null when the selectPage action navigates from a runtime A
       // to a runtime B (between unwatchRuntime and watchRuntime).
       return null;
     }
 
     const { type } = runtimeDetails.info;
+    const { compatibilityReport } = runtimeDetails;
     return dom.article(
       {
         className: "page js-runtime-page",
       },
       RuntimeInfo(runtimeDetails.info),
       this.renderRemoteRuntimeActions(),
       runtimeDetails.serviceWorkersAvailable ? null : ServiceWorkersWarning(),
+      CompatibilityWarning({ compatibilityReport }),
       isSupportedDebugTargetPane(type, DEBUG_TARGET_PANE.TEMPORARY_EXTENSION)
         ? TemporaryExtensionInstaller({
             dispatch,
             temporaryInstallError,
         }) : null,
       this.renderDebugTargetPane("Temporary Extensions",
                                  temporaryExtensions,
                                  TemporaryExtensionAction,
--- a/devtools/client/aboutdebugging-new/src/components/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/moz.build
@@ -7,16 +7,17 @@ DIRS += [
     'debugtarget',
     'shared',
     'sidebar',
 ]
 
 DevToolsModules(
     'App.css',
     'App.js',
+    'CompatibilityWarning.js',
     'ConnectionPromptSetting.js',
     'ProfilerDialog.css',
     'ProfilerDialog.js',
     'RuntimeInfo.js',
     'RuntimePage.css',
     'RuntimePage.js',
     'ServiceWorkersWarning.js',
 )
--- a/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -1,14 +1,17 @@
 /* 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 { checkVersionCompatibility } =
+  require("devtools/client/shared/remote-debugging/version-checker");
+
 const { RUNTIME_PREFERENCE } = require("../constants");
 const { WorkersListener } = require("./workers-listener");
 
 const PREF_TYPES = {
   BOOL: "BOOL",
 };
 
 // Map of preference to preference type.
@@ -67,28 +70,31 @@ class ClientWrapper {
   }
 
   onFront(typeName, listener) {
     this.client.mainRoot.onFront(typeName, listener);
   }
 
   async getDeviceDescription() {
     const deviceFront = await this.getFront("device");
-    const { brandName, channel, deviceName, isMultiE10s, version } =
-      await deviceFront.getDescription();
+    const description = await deviceFront.getDescription();
     // Only expose a specific set of properties.
     return {
-      channel,
-      deviceName,
-      isMultiE10s,
-      name: brandName,
-      version,
+      channel: description.channel,
+      deviceName: description.deviceName,
+      isMultiE10s: description.isMultiE10s,
+      name: description.brandName,
+      version: description.version,
     };
   }
 
+  async checkVersionCompatibility() {
+    return checkVersionCompatibility(this.client);
+  }
+
   async setPreference(prefName, value) {
     const prefType = PREF_TO_TYPE[prefName];
     const preferenceFront = await this.client.mainRoot.getFront("preference");
     switch (prefType) {
       case PREF_TYPES.BOOL:
         return preferenceFront.setBoolPref(prefName, value);
       default:
         throw new Error("Unsupported preference" + prefName);
--- a/devtools/client/aboutdebugging-new/src/types/runtime.js
+++ b/devtools/client/aboutdebugging-new/src/types/runtime.js
@@ -1,36 +1,62 @@
 /* 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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { ClientWrapper } = require("../modules/client-wrapper");
+const { COMPATIBILITY_STATUS } = require("devtools/client/shared/remote-debugging/version-checker");
 
 const runtimeInfo = {
   // device name which is running the runtime,
   // unavailable on this-firefox runtime
   deviceName: PropTypes.string,
 
   // icon which represents the kind of runtime
   icon: PropTypes.string.isRequired,
 
   // name of runtime such as "Firefox Nightly"
   name: PropTypes.string.isRequired,
 
   // version of runtime
   version: PropTypes.string.isRequired,
 };
 
+const compatibilityReport = {
+  // build ID for the current runtime (date formatted as yyyyMMdd eg "20193101")
+  localID: PropTypes.string.isRequired,
+
+  // "platform" version for the current runtime (eg "67.0a1")
+  localVersion: PropTypes.string.isRequired,
+
+  // minimum "platform" version supported for remote debugging by the current runtime
+  minVersion: PropTypes.string.isRequired,
+
+  // build ID for the target runtime (date formatted as yyyyMMdd eg "20193101")
+  runtimeID: PropTypes.string.isRequired,
+
+  // "platform" version for the target runtime (eg "67.0a1")
+  runtimeVersion: PropTypes.string.isRequired,
+
+  // report result, either COMPATIBLE, TOO_OLD or TOO_RECENT
+  status: PropTypes.oneOf(Object.values(COMPATIBILITY_STATUS)).isRequired,
+};
+exports.compatibilityReport = PropTypes.shape(compatibilityReport);
+
 const runtimeDetails = {
   // ClientWrapper built using a DebuggerClient for the runtime
   clientWrapper: PropTypes.instanceOf(ClientWrapper).isRequired,
 
+  // compatibility report to check if the target runtime is in range of the backward
+  // compatibility policy for DevTools remote debugging.
+  compatibilityReport: PropTypes.shape(compatibilityReport).isRequired,
+
   // reflect devtools.debugger.prompt-connection preference of this runtime
   connectionPromptEnabled: PropTypes.bool.isRequired,
 
   // runtime information
   info: PropTypes.shape(runtimeInfo).isRequired,
 
   // True if this runtime supports multiple content processes
   // This might be undefined when connecting to runtimes older than Fx 66
--- a/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
+++ b/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
@@ -83,16 +83,22 @@ function createClientMock() {
     // no-op
     onFront: () => {},
     // stores the preference locally (doesn't update about:config)
     setPreference: function(prefName, value) {
       this._preferences[prefName] = value;
     },
     getPerformancePanelUrl: () => "data:text/html;charset=UTF-8,fake_profiler_page",
     loadPerformanceProfiler: () => {},
+    // Valid compatibility report
+    checkVersionCompatibility: () => {
+      const { COMPATIBILITY_STATUS } =
+        require("devtools/client/shared/remote-debugging/version-checker");
+      return { status: COMPATIBILITY_STATUS.COMPATIBLE };
+    },
   };
 }
 
 // Create a ClientWrapper mock that can be used to replace the this-firefox runtime.
 function createThisFirefoxClientMock() {
   const mockThisFirefoxDescription = {
     name: "Firefox",
     channel: "nightly",
--- a/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
+++ b/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
@@ -125,21 +125,35 @@ about-debugging-runtime-service-workers 
 about-debugging-runtime-shared-workers = Shared Workers
 # Title of the other workers category.
 about-debugging-runtime-other-workers = Other Workers
 
 # Label of the button opening the performance profiler panel in runtime pages for remote
 # runtimes.
 about-debugging-runtime-profile-button = Profile Runtime
 
-# This string is displayed in about:debugging#workers if the current configuration of the
-# browser is incompatible with service workers. Learn more points to MDN.
+# This string is displayed in the runtime page if the current configuration of the
+# target runtime is incompatible with service workers. "Learn more" points to MDN.
 # https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging#Service_workers_not_compatible
 about-debugging-runtime-service-workers-not-compatible = Your browser configuration is not compatible with Service Workers. <a>Learn more</a>
 
+# This string is displayed in the runtime page if the remote runtime version is too old.
+# "Troubleshooting" link points to https://developer.mozilla.org/docs/Tools/WebIDE/Troubleshooting
+# { $runtimeVersion } is the version of the remote runtime (for instance "67.0a1")
+# { $minVersion } is the minimum version that is compatible with the current Firefox instance (same format)
+about-debugging-runtime-version-too-old = The connected runtime has an old version ({ $runtimeVersion }). The minimum supported version is ({ $minVersion }). This is an unsupported setup and may cause DevTools to fail. Please update the connected runtime. <a>Troubleshooting</a>
+
+# This string is displayed in the runtime page if the remote runtime version is too recent.
+# "Troubleshooting" link points to https://developer.mozilla.org/en-US/docs/Tools/WebIDE/Troubleshooting
+# { $runtimeID } is the build ID of the remote runtime (for instance "20181231", format is yyyyMMdd)
+# { $localID } is the build ID of the current Firefox instance (same format)
+# { $runtimeVersion } is the version of the remote runtime (for instance "67.0a1")
+# { $localVersion } is the version of your current runtime (same format)
+about-debugging-runtime-version-too-recent = The connected runtime is more recent ({ $runtimeVersion }, buildID { $runtimeID }) than your desktop Firefox ({ $localVersion }, buildID { $localID }). This is an unsupported setup and may cause DevTools to fail. Please update Firefox. <a>Troubleshooting</a>
+
 # Displayed in the categories of "runtime" pages that don't have any debug target to
 # show. Debug targets depend on the category (extensions, tabs, workers...).
 about-debugging-debug-target-list-empty = Nothing yet.
 
 # Text of a button displayed next to debug targets of "runtime" pages. Clicking on this
 # button will open a DevTools toolbox that will allow inspecting the target.
 # A target can be an addon, a tab, a worker...
 about-debugging-debug-target-inspect-button = Inspect
--- a/devtools/client/shared/remote-debugging/version-checker.js
+++ b/devtools/client/shared/remote-debugging/version-checker.js
@@ -132,16 +132,17 @@ function _compareVersionCompatibility(lo
     // device builds, since their devices will almost always be newer than the client.
     status = COMPATIBILITY_STATUS.TOO_RECENT;
   } else {
     status = COMPATIBILITY_STATUS.COMPATIBLE;
   }
 
   return {
     localID,
+    localVersion,
     minVersion,
     runtimeID,
     runtimeVersion,
     status,
   };
 }
 // Exported for tests.
 exports._compareVersionCompatibility = _compareVersionCompatibility;