Bug 1405008 - Make WebIDE warn when connecting to old runtimes. r=jdescottes
MozReview-Commit-ID: KQc2b1ohksA
--- a/devtools/client/locales/en-US/webide.properties
+++ b/devtools/client/locales/en-US/webide.properties
@@ -43,16 +43,19 @@ error_operationFail=Operation failed: %1
error_cantConnectToApp=Can’t connect to app: %1$S
error_appProjectsLoadFailed=Unable to load project list. This can occur if you’ve used this profile with a newer version of Firefox.
error_folderCreationFailed=Unable to create project folder in the selected directory.
# Variable: runtime app build ID (looks like this %Y%M%D format) and firefox build ID (same format)
error_runtimeVersionTooRecent=The connected runtime has a more recent build date (%1$S) than your desktop Firefox (%2$S) does. This is an unsupported setup and may cause DevTools to fail. Please update Firefox.
+# Variable: runtime app version (looks like this 52.a3) and firefox version (same format)
+error_runtimeVersionTooOld=The connected runtime has an old version (%1$S). The minimum supported version is (%2$S). This is an unsupported setup and may cause DevTools to fail. Please update the connected runtime.
+
addons_stable=stable
addons_unstable=unstable
addons_install_button=install
addons_uninstall_button=uninstall
addons_adb_label=ADB Helper Add-on
addons_adb_warning=USB devices won’t be detected without this add-on
addons_status_unknown=?
addons_status_installed=Installed
--- a/devtools/client/webide/content/webide.js
+++ b/devtools/client/webide/content/webide.js
@@ -26,18 +26,16 @@ const {Task} = require("devtools/shared/
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
const HTML = "http://www.w3.org/1999/xhtml";
const HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Troubleshooting";
const MAX_ZOOM = 1.4;
const MIN_ZOOM = 0.6;
-const MS_PER_DAY = 86400000;
-
[["AppManager", AppManager],
["AppProjects", AppProjects],
["Connection", Connection]].forEach(([key, value]) => {
Object.defineProperty(this, key, {
value: value,
enumerable: true,
writable: false
});
@@ -747,37 +745,27 @@ var UI = {
},
resetDeck: function () {
this.resetFocus();
let deck = document.querySelector("#deck");
deck.selectedPanel = null;
},
- buildIDToDate(buildID) {
- let fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
- // Date expects 0 - 11 for months
- return new Date(fields[1], Number.parseInt(fields[2]) - 1, fields[3]);
- },
-
checkRuntimeVersion: Task.async(function* () {
- if (AppManager.connected && AppManager.deviceFront) {
- let desc = yield AppManager.deviceFront.getDescription();
- // Compare device and firefox build IDs
- // and only compare by day (strip hours/minutes) to prevent
- // warning against builds of the same day.
- let deviceID = desc.appbuildid.substr(0, 8);
- let localID = Services.appinfo.appBuildID.substr(0, 8);
- let deviceDate = this.buildIDToDate(deviceID);
- let localDate = this.buildIDToDate(localID);
- // Allow device to be newer by up to a week. This accommodates those with
- // local device builds, since their devices will almost always be newer
- // than the client.
- if (deviceDate - localDate > 7 * MS_PER_DAY) {
- this.reportError("error_runtimeVersionTooRecent", deviceID, localID);
+ if (AppManager.connected) {
+ let { client } = AppManager.connection;
+ let report = yield client.checkRuntimeVersion(AppManager.listTabsForm);
+ if (report.incompatible == "too-recent") {
+ this.reportError("error_runtimeVersionTooRecent", report.runtimeID,
+ report.localID);
+ }
+ if (report.incompatible == "too-old") {
+ this.reportError("error_runtimeVersionTooOld", report.runtimeVersion,
+ report.minVersion);
}
}
}),
/** ******** TOOLBOX **********/
/**
* There are many ways to close a toolbox:
--- a/devtools/client/webide/modules/app-manager.js
+++ b/devtools/client/webide/modules/app-manager.js
@@ -534,16 +534,20 @@ var AppManager = exports.AppManager = {
// Fx >=39 exposes a dedicated actor via getProcess request
return this.connection.client &&
this.connection.client.mainRoot &&
this.connection.client.mainRoot.traits.allowChromeProcess ||
(this._listTabsResponse &&
this._listTabsResponse.consoleActor);
},
+ get listTabsForm() {
+ return this._listTabsResponse;
+ },
+
get deviceFront() {
if (!this._listTabsResponse) {
return null;
}
return getDeviceFront(this.connection.client, this._listTabsResponse);
},
get preferenceFront() {
--- a/devtools/shared/client/debugger-client.js
+++ b/devtools/shared/client/debugger-client.js
@@ -1,39 +1,48 @@
/* 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 { Cu } = require("chrome");
+const Services = require("Services");
const promise = Cu.import("resource://devtools/shared/deprecated-sync-thenables.js", {}).Promise;
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { getStack, callFunctionWithAsyncStack } = require("devtools/shared/platform/stack");
const eventSource = require("devtools/shared/client/event-source");
const {
ThreadStateTypes,
UnsolicitedNotifications,
UnsolicitedPauses,
} = require("./constants");
loader.lazyRequireGetter(this, "Authentication", "devtools/shared/security/auth");
loader.lazyRequireGetter(this, "DebuggerSocket", "devtools/shared/security/socket", true);
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
+loader.lazyRequireGetter(this, "getDeviceFront", "devtools/shared/fronts/device", true);
+
loader.lazyRequireGetter(this, "WebConsoleClient", "devtools/shared/webconsole/client", true);
loader.lazyRequireGetter(this, "AddonClient", "devtools/shared/client/addon-client");
loader.lazyRequireGetter(this, "RootClient", "devtools/shared/client/root-client");
loader.lazyRequireGetter(this, "TabClient", "devtools/shared/client/tab-client");
loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client");
loader.lazyRequireGetter(this, "TraceClient", "devtools/shared/client/trace-client");
loader.lazyRequireGetter(this, "WorkerClient", "devtools/shared/client/worker-client");
const noop = () => {};
+// Define the minimum officially supported version of Firefox when connecting to a remote
+// runtime. (Use ".0a1" to support the very first nightly version)
+// This is usually the current ESR version.
+const MIN_SUPPORTED_PLATFORM_VERSION = "52.0a1";
+const MS_PER_DAY = 86400000;
+
/**
* Creates a client for the remote debugging protocol server. This client
* provides the means to communicate with the server and exchange the messages
* required by the protocol in a traditional JavaScript API.
*/
function DebuggerClient(transport) {
this._transport = transport;
this._transport.hooks = this;
@@ -179,16 +188,76 @@ DebuggerClient.prototype = {
deferred.resolve([applicationType, traits]);
});
this._transport.ready();
return deferred.promise;
},
/**
+ * Tells if the remote device is using a supported version of Firefox.
+ *
+ * @return Object with the following attributes:
+ * * String incompatible
+ * null if the runtime is compatible,
+ * "too-recent" if the runtime uses a too recent version,
+ * "too-old" if the runtime uses a too old version.
+ * * String minVersion
+ * The minimum supported version.
+ * * String runtimeVersion
+ * The remote runtime version.
+ * * String localID
+ * Build ID of local runtime. A date with like this: YYYYMMDD.
+ * * String deviceID
+ * Build ID of remote runtime. A date with like this: YYYYMMDD.
+ */
+ async checkRuntimeVersion(listTabsForm) {
+ let incompatible = null;
+
+ // Instead of requiring to pass `listTabsForm` here,
+ // we can call getRoot() instead, but only once Firefox ESR59 is released
+ let deviceFront = await getDeviceFront(this, listTabsForm);
+ let desc = await deviceFront.getDescription();
+
+ // 1) Check for Firefox too recent on device.
+ // Compare device and firefox build IDs
+ // and only compare by day (strip hours/minutes) to prevent
+ // warning against builds of the same day.
+ let runtimeID = desc.appbuildid.substr(0, 8);
+ let localID = Services.appinfo.appBuildID.substr(0, 8);
+ function buildIDToDate(buildID) {
+ let fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
+ // Date expects 0 - 11 for months
+ return new Date(fields[1], Number.parseInt(fields[2], 10) - 1, fields[3]);
+ }
+ let runtimeDate = buildIDToDate(runtimeID);
+ let localDate = buildIDToDate(localID);
+ // Allow device to be newer by up to a week. This accommodates those with
+ // local device builds, since their devices will almost always be newer
+ // than the client.
+ if (runtimeDate - localDate > 7 * MS_PER_DAY) {
+ incompatible = "too-recent";
+ }
+
+ // 2) Check for too old Firefox on device
+ let platformversion = desc.platformversion;
+ if (Services.vc.compare(platformversion, MIN_SUPPORTED_PLATFORM_VERSION) < 0) {
+ incompatible = "too-old";
+ }
+
+ return {
+ incompatible,
+ minVersion: MIN_SUPPORTED_PLATFORM_VERSION,
+ runtimeVersion: platformversion,
+ localID,
+ runtimeID,
+ };
+ },
+
+ /**
* Shut down communication with the debugging server.
*
* @param onClosed function
* If specified, will be called when the debugging connection
* has been closed. This parameter is deprecated - please use
* the returned Promise.
* @return Promise
* Resolves after the underlying transport is closed.