Bug 1081093 - Auto-install Tools Adapter add-on in WebIDE. r=jryans
authorPaul Rouget <paul@mozilla.com>
Fri, 17 Oct 2014 16:02:38 -0700
changeset 211212 369cc491a650d3417f2c45a9f077e958a5a60a41
parent 211211 c499f128708d76e59c5a1258231fd1bacc392862
child 211213 9acb83f0af3b3cec51075bd5521f9cf76fd5ba99
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjryans
bugs1081093
milestone36.0a1
Bug 1081093 - Auto-install Tools Adapter add-on in WebIDE. r=jryans
browser/devtools/webide/content/addons.js
browser/devtools/webide/content/webide.js
browser/devtools/webide/modules/addons.js
browser/devtools/webide/test/addons/fxdt-adapters-linux32.xpi
browser/devtools/webide/test/addons/fxdt-adapters-linux64.xpi
browser/devtools/webide/test/addons/fxdt-adapters-mac64.xpi
browser/devtools/webide/test/addons/fxdt-adapters-win32.xpi
browser/devtools/webide/test/chrome.ini
browser/devtools/webide/test/head.js
browser/devtools/webide/test/test_addons.html
browser/devtools/webide/webide-prefs.js
browser/locales/en-US/chrome/browser/devtools/webide.properties
--- a/browser/devtools/webide/content/addons.js
+++ b/browser/devtools/webide/content/addons.js
@@ -25,23 +25,24 @@ window.addEventListener("unload", functi
   ForgetAddonsList();
 }, true);
 
 function CloseUI() {
   window.parent.UI.openProject();
 }
 
 function BuildUI(addons) {
-  BuildItem(addons.adb, true /* is adb */);
+  BuildItem(addons.adb, "adb");
+  BuildItem(addons.adapters, "adapters");
   for (let addon of addons.simulators) {
-    BuildItem(addon, false /* is adb */);
+    BuildItem(addon, "simulator");
   }
 }
 
-function BuildItem(addon, isADB) {
+function BuildItem(addon, type) {
 
   function onAddonUpdate(event, arg) {
     switch (event) {
       case "update":
         progress.removeAttribute("value");
         li.setAttribute("status", addon.status);
         status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
         break;
@@ -68,30 +69,39 @@ function BuildItem(addon, isADB) {
     for (let e of events) {
       addon.off(e, onAddonUpdate);
     }
   });
 
   let li = document.createElement("li");
   li.setAttribute("status", addon.status);
 
-  // Used in tests
-  if (isADB) {
-    li.setAttribute("addon", "adb");
-  } else {
-    li.setAttribute("addon", "simulator-" + addon.version);
-  }
-
   let name = document.createElement("span");
   name.className = "name";
-  if (isADB) {
-    name.textContent = Strings.GetStringFromName("addons_adb_label");
-  } else {
-    let stability = Strings.GetStringFromName("addons_" + addon.stability);
-    name.textContent = Strings.formatStringFromName("addons_simulator_label", [addon.version, stability], 2);
+
+  switch (type) {
+    case "adb":
+      li.setAttribute("addon", type);
+      name.textContent = Strings.GetStringFromName("addons_adb_label");
+      break;
+    case "adapters":
+      li.setAttribute("addon", type);
+      try {
+        name.textContent = Strings.GetStringFromName("addons_adapters_label");
+      } catch(e) {
+        // This code (bug 1081093) will be backported to Aurora, which doesn't
+        // contain this string.
+        name.textContent = "Tools Adapters Add-on";
+      }
+      break;
+    case "simulator":
+      li.setAttribute("addon", "simulator-" + addon.version);
+      let stability = Strings.GetStringFromName("addons_" + addon.stability);
+      name.textContent = Strings.formatStringFromName("addons_simulator_label", [addon.version, stability], 2);
+      break;
   }
 
   li.appendChild(name);
 
   let status = document.createElement("span");
   status.className = "status";
   status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
   li.appendChild(status);
@@ -106,17 +116,17 @@ function BuildItem(addon, isADB) {
   uninstallButton.className = "uninstall-button";
   uninstallButton.onclick = () => addon.uninstall();
   uninstallButton.textContent = Strings.GetStringFromName("addons_uninstall_button");
   li.appendChild(uninstallButton);
 
   let progress = document.createElement("progress");
   li.appendChild(progress);
 
-  if (isADB) {
+  if (type == "adb") {
     let warning = document.createElement("p");
     warning.textContent = Strings.GetStringFromName("addons_adb_warning");
     warning.className = "warning";
     li.appendChild(warning);
   }
 
   document.querySelector("ul").appendChild(li);
 }
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -64,25 +64,32 @@ let UI = {
 
     this.onfocus = this.onfocus.bind(this);
     window.addEventListener("focus", this.onfocus, true);
 
     AppProjects.load().then(() => {
       this.autoSelectProject();
     });
 
-    // Auto install the ADB Addon Helper. Only once.
-    // If the user decides to uninstall the addon, we won't install it again.
-    let autoInstallADBHelper = Services.prefs.getBoolPref("devtools.webide.autoinstallADBHelper");
-    if (autoInstallADBHelper && !Devices.helperAddonInstalled) {
+    // Auto install the ADB Addon Helper and Tools Adapters. Only once.
+    // If the user decides to uninstall any of this addon, we won't install it again.
+    let autoinstallADBHelper = Services.prefs.getBoolPref("devtools.webide.autoinstallADBHelper");
+    let autoinstallFxdtAdapters = Services.prefs.getBoolPref("devtools.webide.autoinstallFxdtAdapters");
+    if (autoinstallADBHelper) {
       GetAvailableAddons().then(addons => {
         addons.adb.install();
       }, console.error);
     }
+    if (autoinstallFxdtAdapters) {
+      GetAvailableAddons().then(addons => {
+        addons.adapters.install();
+      }, console.error);
+    }
     Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
+    Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", false);
 
     this.lastConnectedRuntime = Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
 
     this.setupDeck();
   },
 
   uninit: function() {
     window.removeEventListener("focus", this.onfocus, true);
--- a/browser/devtools/webide/modules/addons.js
+++ b/browser/devtools/webide/modules/addons.js
@@ -8,59 +8,49 @@ const {AddonManager} = Cu.import("resour
 const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
 const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
 const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {GetAddonsJSON} = require("devtools/webide/remote-resources");
 
 let SIMULATOR_LINK = Services.prefs.getCharPref("devtools.webide.simulatorAddonsURL");
 let ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL");
+let ADAPTERS_LINK = Services.prefs.getCharPref("devtools.webide.adaptersAddonURL");
 let SIMULATOR_ADDON_ID = Services.prefs.getCharPref("devtools.webide.simulatorAddonID");
 let ADB_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adbAddonID");
+let ADAPTERS_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adaptersAddonID");
 
 let platform = Services.appShell.hiddenDOMWindow.navigator.platform;
 let OS = "";
 if (platform.indexOf("Win") != -1) {
   OS = "win32";
 } else if (platform.indexOf("Mac") != -1) {
   OS = "mac64";
 } else if (platform.indexOf("Linux") != -1) {
   if (platform.indexOf("x86_64") != -1) {
     OS = "linux64";
   } else {
-    OS = "linux";
+    OS = "linux32";
   }
 }
 
-Simulator.on("unregister", updateSimulatorAddons);
-Simulator.on("register", updateSimulatorAddons);
-Devices.on("addon-status-updated", updateAdbAddon);
-
-function updateSimulatorAddons(event, version) {
+let addonsListener = {};
+addonsListener.onEnabled =
+addonsListener.onDisabled =
+addonsListener.onInstalled =
+addonsListener.onUninstalled = (updatedAddon) => {
   GetAvailableAddons().then(addons => {
-    let foundAddon = null;
-    for (let addon of addons.simulators) {
-      if (addon.version == version) {
-        foundAddon = addon;
-        break;
+    for (let a of [...addons.simulators, addons.adb, addons.adapters]) {
+      if (a.addonID == updatedAddon.id) {
+        a.updateInstallStatus();
       }
     }
-    if (!foundAddon) {
-      console.warn("An unknown simulator (un)registered", version);
-      return;
-    }
-    foundAddon.updateInstallStatus();
   });
 }
-
-function updateAdbAddon() {
-  GetAvailableAddons().then(addons => {
-    addons.adb.updateInstallStatus();
-  });
-}
+AddonManager.addAddonListener(addonsListener);
 
 let GetAvailableAddons_promise = null;
 let GetAvailableAddons = exports.GetAvailableAddons = function() {
   if (!GetAvailableAddons_promise) {
     let deferred = promise.defer();
     GetAvailableAddons_promise = deferred.promise;
     let addons = {
       simulators: [],
@@ -68,16 +58,17 @@ let GetAvailableAddons = exports.GetAvai
     }
     GetAddonsJSON(true).then(json => {
       for (let stability in json) {
         for (let version of json[stability]) {
           addons.simulators.push(new SimulatorAddon(stability, version));
         }
       }
       addons.adb = new ADBAddon();
+      addons.adapters = new AdaptersAddon();
       deferred.resolve(addons);
     }, e => {
       GetAvailableAddons_promise = null;
       deferred.reject(e);
     });
   }
   return GetAvailableAddons_promise;
 }
@@ -94,33 +85,42 @@ Addon.prototype = {
       this._status = value;
       this.emit("update");
     }
   },
   get status() {
     return this._status;
   },
 
+  updateInstallStatus: function() {
+    AddonManager.getAddonByID(this.addonID, (addon) => {
+      if (addon && !addon.userDisabled) {
+        this.status = "installed";
+      } else {
+        this.status = "uninstalled";
+      }
+    });
+  },
+
   install: function() {
-    if (this.status != "uninstalled") {
-      throw new Error("Not uninstalled");
-    }
-    this.status = "preparing";
-
     AddonManager.getAddonByID(this.addonID, (addon) => {
+      if (addon && !addon.userDisabled) {
+        this.status = "installed";
+        return;
+      }
+      this.status = "preparing";
       if (addon && addon.userDisabled) {
         addon.userDisabled = false;
       } else {
         AddonManager.getInstallForURL(this.xpiLink, (install) => {
           install.addListener(this);
           install.install();
         }, "application/x-xpinstall");
       }
     });
-
   },
 
   uninstall: function() {
     AddonManager.getAddonByID(this.addonID, (addon) => {
       addon.uninstall();
     });
   },
 
@@ -162,49 +162,35 @@ Addon.prototype = {
     this.installFailureHandler(install, "Install failed");
   },
 }
 
 function SimulatorAddon(stability, version) {
   EventEmitter.decorate(this);
   this.stability = stability;
   this.version = version;
-  this.xpiLink = SIMULATOR_LINK.replace(/#OS#/g, OS)
+  // This addon uses the string "linux" for "linux32"
+  let fixedOS = OS == "linux32" ? "linux" : OS;
+  this.xpiLink = SIMULATOR_LINK.replace(/#OS#/g, fixedOS)
                                .replace(/#VERSION#/g, version)
                                .replace(/#SLASHED_VERSION#/g, version.replace(/\./g, "_"));
   this.addonID = SIMULATOR_ADDON_ID.replace(/#SLASHED_VERSION#/g, version.replace(/\./g, "_"));
   this.updateInstallStatus();
 }
-
-SimulatorAddon.prototype = Object.create(Addon.prototype, {
-  updateInstallStatus: {
-    enumerable: true,
-    value: function() {
-      let sim = Simulator.getByVersion(this.version);
-      if (sim) {
-        this.status = "installed";
-      } else {
-        this.status = "uninstalled";
-      }
-    }
-  },
-});
+SimulatorAddon.prototype = Object.create(Addon.prototype);
 
 function ADBAddon() {
   EventEmitter.decorate(this);
-  this.xpiLink = ADB_LINK.replace(/#OS#/g, OS);
+  // This addon uses the string "linux" for "linux32"
+  let fixedOS = OS == "linux32" ? "linux" : OS;
+  this.xpiLink = ADB_LINK.replace(/#OS#/g, fixedOS);
   this.addonID = ADB_ADDON_ID;
   this.updateInstallStatus();
 }
+ADBAddon.prototype = Object.create(Addon.prototype);
 
-ADBAddon.prototype = Object.create(Addon.prototype, {
-  updateInstallStatus: {
-    enumerable: true,
-    value: function() {
-      if (Devices.helperAddonInstalled) {
-        this.status = "installed";
-      } else {
-        this.status = "uninstalled";
-      }
-    }
-  },
-});
-
+function AdaptersAddon() {
+  EventEmitter.decorate(this);
+  this.xpiLink = ADAPTERS_LINK.replace(/#OS#/g, OS);
+  this.addonID = ADAPTERS_ADDON_ID;
+  this.updateInstallStatus();
+}
+AdaptersAddon.prototype = Object.create(Addon.prototype);
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a512ae3d199746350c650e527b0178411b0440e
GIT binary patch
literal 1156
zc$^FHW@Zs#U|`^2SQYE%`bJKz+?|nuA%=;8frmkcAt^t<q`0Igu|O}YI5dQlfqBCR
zn~+UFTw1}+z{v6qs1&TbcY?1sv!OuS``_AZ{<)gcFE?JAti-{^y0_v?TKe6IIcX<-
zqPG6HKetY!Dqv5_r;mHyTML^v^YVH-vQE9ql(IXXFS<suHtVXnLGjet2c7+yzQ+_f
z?06#faMovrGWDgE#}e3hZYgU#b8Ogs_+j6D4LAPRmt43+&pepMq;}9UCoAI0bJIH|
z4-`NA__Z|h{-JB!Dq$I(Cc(iX(KjZ2W1H|M*Q)E;#}lkg{s(%iHcb-P<FwvNZ?1IT
zP1EP<>GRHAirFi_jBAd|!$}FLlX(`OoaE(uGU|Pe!_FTnr~DUbxK^)ORD1m$m$_Nj
zp7#@jCObx|r%OKl;5zO9l#5jv-Y$i@c~dOAtXIBDmpr#j+<kfJvqqNbk2n51|53$C
z;%wsgytL5ppnyj#DR+NMsILvTZ|{k-@a6s0CNW`aPrXv@#cA7L>MvUozuAr<03I3B
zfsygsZq}Tiz!1L9%)r16jEu~@;*!Li9KE8HG;mz>0^^|<GcHntV*6(s2-G|e&)Ioh
zilb9c-fKsguX5<F$$q(^EBm>1I-@sC+}jef`rr3o3J;q;wb)v3-fUg*w_uf*#iWfv
zt9$J^mE=8At)^$~|0u0@<WRlGp0C!54}LpF{a7YFS+M4k<w3vMTrz31zlf*utZC$D
z@maQMLHD~)Oa7nO<WiA$DJV^jfumxIq^9@#hY}7lE{rvr42qY(o?cK@*CM8?adv|D
zX}4v(!F+3<G%I`DHrGmdx$Dmr-jo#%%X==>iZB|<%y=Ct5w^E}!CAYKSm)>BYzi)q
zDqpX^ExY6E)Vn{@%WiLTw0?VH^3x(ow?c{Ihn}=<(C_-FGG*`Q>a$&|Cce3)(YGkl
z<Lc@U=lt)id98c%%!ygCry5q~-Du6e@=7PlGgb0*P{@iD$7$9RCkJkwR2ej<$ZFHI
zrJ^&E-+tOEzwXVoM1N_O=l5rxd9<!%CtqpZO3jzqcMCO^L~Z7le%<2hEc=jGDx!Cl
z;qSR35x3?Qv+bGp@mEOMrGSP0vFDn#XW4!{zN_p#L#0H9%sE!+vj5&Um1emejY%nN
zNh-6~K4R4C;j_%*sH*Ps%SYeb*J$eAHR;vy%CA4{ItngrTzA1x+Ml^v&c*gS@9r~F
z_jqnUuh@MjRLV`e?YvgIdeR-G#~Ko?qN>Z+`IgIY{hS&afByKhduJV9U$A|3<TrnS
zHzSiAGp@`m!2kk4F2j;W5DPVHvqG{qT2ewb5i><1o7fAaP|_zWBz<BS$jS!N!~}$k
Kf%Iu+5Dx$(df(Lm
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a512ae3d199746350c650e527b0178411b0440e
GIT binary patch
literal 1156
zc$^FHW@Zs#U|`^2SQYE%`bJKz+?|nuA%=;8frmkcAt^t<q`0Igu|O}YI5dQlfqBCR
zn~+UFTw1}+z{v6qs1&TbcY?1sv!OuS``_AZ{<)gcFE?JAti-{^y0_v?TKe6IIcX<-
zqPG6HKetY!Dqv5_r;mHyTML^v^YVH-vQE9ql(IXXFS<suHtVXnLGjet2c7+yzQ+_f
z?06#faMovrGWDgE#}e3hZYgU#b8Ogs_+j6D4LAPRmt43+&pepMq;}9UCoAI0bJIH|
z4-`NA__Z|h{-JB!Dq$I(Cc(iX(KjZ2W1H|M*Q)E;#}lkg{s(%iHcb-P<FwvNZ?1IT
zP1EP<>GRHAirFi_jBAd|!$}FLlX(`OoaE(uGU|Pe!_FTnr~DUbxK^)ORD1m$m$_Nj
zp7#@jCObx|r%OKl;5zO9l#5jv-Y$i@c~dOAtXIBDmpr#j+<kfJvqqNbk2n51|53$C
z;%wsgytL5ppnyj#DR+NMsILvTZ|{k-@a6s0CNW`aPrXv@#cA7L>MvUozuAr<03I3B
zfsygsZq}Tiz!1L9%)r16jEu~@;*!Li9KE8HG;mz>0^^|<GcHntV*6(s2-G|e&)Ioh
zilb9c-fKsguX5<F$$q(^EBm>1I-@sC+}jef`rr3o3J;q;wb)v3-fUg*w_uf*#iWfv
zt9$J^mE=8At)^$~|0u0@<WRlGp0C!54}LpF{a7YFS+M4k<w3vMTrz31zlf*utZC$D
z@maQMLHD~)Oa7nO<WiA$DJV^jfumxIq^9@#hY}7lE{rvr42qY(o?cK@*CM8?adv|D
zX}4v(!F+3<G%I`DHrGmdx$Dmr-jo#%%X==>iZB|<%y=Ct5w^E}!CAYKSm)>BYzi)q
zDqpX^ExY6E)Vn{@%WiLTw0?VH^3x(ow?c{Ihn}=<(C_-FGG*`Q>a$&|Cce3)(YGkl
z<Lc@U=lt)id98c%%!ygCry5q~-Du6e@=7PlGgb0*P{@iD$7$9RCkJkwR2ej<$ZFHI
zrJ^&E-+tOEzwXVoM1N_O=l5rxd9<!%CtqpZO3jzqcMCO^L~Z7le%<2hEc=jGDx!Cl
z;qSR35x3?Qv+bGp@mEOMrGSP0vFDn#XW4!{zN_p#L#0H9%sE!+vj5&Um1emejY%nN
zNh-6~K4R4C;j_%*sH*Ps%SYeb*J$eAHR;vy%CA4{ItngrTzA1x+Ml^v&c*gS@9r~F
z_jqnUuh@MjRLV`e?YvgIdeR-G#~Ko?qN>Z+`IgIY{hS&afByKhduJV9U$A|3<TrnS
zHzSiAGp@`m!2kk4F2j;W5DPVHvqG{qT2ewb5i><1o7fAaP|_zWBz<BS$jS!N!~}$k
Kf%Iu+5Dx$(df(Lm
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a512ae3d199746350c650e527b0178411b0440e
GIT binary patch
literal 1156
zc$^FHW@Zs#U|`^2SQYE%`bJKz+?|nuA%=;8frmkcAt^t<q`0Igu|O}YI5dQlfqBCR
zn~+UFTw1}+z{v6qs1&TbcY?1sv!OuS``_AZ{<)gcFE?JAti-{^y0_v?TKe6IIcX<-
zqPG6HKetY!Dqv5_r;mHyTML^v^YVH-vQE9ql(IXXFS<suHtVXnLGjet2c7+yzQ+_f
z?06#faMovrGWDgE#}e3hZYgU#b8Ogs_+j6D4LAPRmt43+&pepMq;}9UCoAI0bJIH|
z4-`NA__Z|h{-JB!Dq$I(Cc(iX(KjZ2W1H|M*Q)E;#}lkg{s(%iHcb-P<FwvNZ?1IT
zP1EP<>GRHAirFi_jBAd|!$}FLlX(`OoaE(uGU|Pe!_FTnr~DUbxK^)ORD1m$m$_Nj
zp7#@jCObx|r%OKl;5zO9l#5jv-Y$i@c~dOAtXIBDmpr#j+<kfJvqqNbk2n51|53$C
z;%wsgytL5ppnyj#DR+NMsILvTZ|{k-@a6s0CNW`aPrXv@#cA7L>MvUozuAr<03I3B
zfsygsZq}Tiz!1L9%)r16jEu~@;*!Li9KE8HG;mz>0^^|<GcHntV*6(s2-G|e&)Ioh
zilb9c-fKsguX5<F$$q(^EBm>1I-@sC+}jef`rr3o3J;q;wb)v3-fUg*w_uf*#iWfv
zt9$J^mE=8At)^$~|0u0@<WRlGp0C!54}LpF{a7YFS+M4k<w3vMTrz31zlf*utZC$D
z@maQMLHD~)Oa7nO<WiA$DJV^jfumxIq^9@#hY}7lE{rvr42qY(o?cK@*CM8?adv|D
zX}4v(!F+3<G%I`DHrGmdx$Dmr-jo#%%X==>iZB|<%y=Ct5w^E}!CAYKSm)>BYzi)q
zDqpX^ExY6E)Vn{@%WiLTw0?VH^3x(ow?c{Ihn}=<(C_-FGG*`Q>a$&|Cce3)(YGkl
z<Lc@U=lt)id98c%%!ygCry5q~-Du6e@=7PlGgb0*P{@iD$7$9RCkJkwR2ej<$ZFHI
zrJ^&E-+tOEzwXVoM1N_O=l5rxd9<!%CtqpZO3jzqcMCO^L~Z7le%<2hEc=jGDx!Cl
z;qSR35x3?Qv+bGp@mEOMrGSP0vFDn#XW4!{zN_p#L#0H9%sE!+vj5&Um1emejY%nN
zNh-6~K4R4C;j_%*sH*Ps%SYeb*J$eAHR;vy%CA4{ItngrTzA1x+Ml^v&c*gS@9r~F
z_jqnUuh@MjRLV`e?YvgIdeR-G#~Ko?qN>Z+`IgIY{hS&afByKhduJV9U$A|3<TrnS
zHzSiAGp@`m!2kk4F2j;W5DPVHvqG{qT2ewb5i><1o7fAaP|_zWBz<BS$jS!N!~}$k
Kf%Iu+5Dx$(df(Lm
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a512ae3d199746350c650e527b0178411b0440e
GIT binary patch
literal 1156
zc$^FHW@Zs#U|`^2SQYE%`bJKz+?|nuA%=;8frmkcAt^t<q`0Igu|O}YI5dQlfqBCR
zn~+UFTw1}+z{v6qs1&TbcY?1sv!OuS``_AZ{<)gcFE?JAti-{^y0_v?TKe6IIcX<-
zqPG6HKetY!Dqv5_r;mHyTML^v^YVH-vQE9ql(IXXFS<suHtVXnLGjet2c7+yzQ+_f
z?06#faMovrGWDgE#}e3hZYgU#b8Ogs_+j6D4LAPRmt43+&pepMq;}9UCoAI0bJIH|
z4-`NA__Z|h{-JB!Dq$I(Cc(iX(KjZ2W1H|M*Q)E;#}lkg{s(%iHcb-P<FwvNZ?1IT
zP1EP<>GRHAirFi_jBAd|!$}FLlX(`OoaE(uGU|Pe!_FTnr~DUbxK^)ORD1m$m$_Nj
zp7#@jCObx|r%OKl;5zO9l#5jv-Y$i@c~dOAtXIBDmpr#j+<kfJvqqNbk2n51|53$C
z;%wsgytL5ppnyj#DR+NMsILvTZ|{k-@a6s0CNW`aPrXv@#cA7L>MvUozuAr<03I3B
zfsygsZq}Tiz!1L9%)r16jEu~@;*!Li9KE8HG;mz>0^^|<GcHntV*6(s2-G|e&)Ioh
zilb9c-fKsguX5<F$$q(^EBm>1I-@sC+}jef`rr3o3J;q;wb)v3-fUg*w_uf*#iWfv
zt9$J^mE=8At)^$~|0u0@<WRlGp0C!54}LpF{a7YFS+M4k<w3vMTrz31zlf*utZC$D
z@maQMLHD~)Oa7nO<WiA$DJV^jfumxIq^9@#hY}7lE{rvr42qY(o?cK@*CM8?adv|D
zX}4v(!F+3<G%I`DHrGmdx$Dmr-jo#%%X==>iZB|<%y=Ct5w^E}!CAYKSm)>BYzi)q
zDqpX^ExY6E)Vn{@%WiLTw0?VH^3x(ow?c{Ihn}=<(C_-FGG*`Q>a$&|Cce3)(YGkl
z<Lc@U=lt)id98c%%!ygCry5q~-Du6e@=7PlGgb0*P{@iD$7$9RCkJkwR2ej<$ZFHI
zrJ^&E-+tOEzwXVoM1N_O=l5rxd9<!%CtqpZO3jzqcMCO^L~Z7le%<2hEc=jGDx!Cl
z;qSR35x3?Qv+bGp@mEOMrGSP0vFDn#XW4!{zN_p#L#0H9%sE!+vj5&Um1emejY%nN
zNh-6~K4R4C;j_%*sH*Ps%SYeb*J$eAHR;vy%CA4{ItngrTzA1x+Ml^v&c*gS@9r~F
z_jqnUuh@MjRLV`e?YvgIdeR-G#~Ko?qN>Z+`IgIY{hS&afByKhduJV9U$A|3<TrnS
zHzSiAGp@`m!2kk4F2j;W5DPVHvqG{qT2ewb5i><1o7fAaP|_zWBz<BS$jS!N!~}$k
Kf%Iu+5Dx$(df(Lm
--- a/browser/devtools/webide/test/chrome.ini
+++ b/browser/devtools/webide/test/chrome.ini
@@ -15,16 +15,20 @@ support-files =
   addons/fxos_3_0_simulator-linux.xpi
   addons/fxos_3_0_simulator-linux64.xpi
   addons/fxos_3_0_simulator-win32.xpi
   addons/fxos_3_0_simulator-mac64.xpi
   addons/adbhelper-linux.xpi
   addons/adbhelper-linux64.xpi
   addons/adbhelper-win32.xpi
   addons/adbhelper-mac64.xpi
+  addons/fxdt-adapters-linux32.xpi
+  addons/fxdt-adapters-linux64.xpi
+  addons/fxdt-adapters-win32.xpi
+  addons/fxdt-adapters-mac64.xpi
   head.js
   hosted_app.manifest
   templates.json
 
 [test_basic.html]
 [test_newapp.html]
 [test_import.html]
 [test_duplicate_import.html]
--- a/browser/devtools/webide/test/head.js
+++ b/browser/devtools/webide/test/head.js
@@ -22,35 +22,32 @@ if (window.location === "chrome://browse
 }
 
 Services.prefs.setBoolPref("devtools.webide.enabled", true);
 Services.prefs.setBoolPref("devtools.webide.enableLocalRuntime", true);
 
 Services.prefs.setCharPref("devtools.webide.addonsURL", TEST_BASE + "addons/simulators.json");
 Services.prefs.setCharPref("devtools.webide.simulatorAddonsURL", TEST_BASE + "addons/fxos_#SLASHED_VERSION#_simulator-#OS#.xpi");
 Services.prefs.setCharPref("devtools.webide.adbAddonURL", TEST_BASE + "addons/adbhelper-#OS#.xpi");
+Services.prefs.setCharPref("devtools.webide.adaptersAddonURL", TEST_BASE + "addons/fxdt-adapters-#OS#.xpi");
 Services.prefs.setCharPref("devtools.webide.templatesURL", TEST_BASE + "templates.json");
 
 
 SimpleTest.registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("devtools.webide.templatesURL");
   Services.prefs.clearUserPref("devtools.webide.enabled");
   Services.prefs.clearUserPref("devtools.webide.enableLocalRuntime");
-  Services.prefs.clearUserPref("devtools.webide.addonsURL");
-  Services.prefs.clearUserPref("devtools.webide.simulatorAddonsURL");
-  Services.prefs.clearUserPref("devtools.webide.adbAddonURL");
-  Services.prefs.clearUserPref("devtools.webide.autoInstallADBHelper", false);
+  Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper");
+  Services.prefs.clearUserPref("devtools.webide.autoinstallFxdtAdapters");
 });
 
-function openWebIDE(autoInstallADBHelper) {
+function openWebIDE(autoInstallAddons) {
   info("opening WebIDE");
 
-  if (!autoInstallADBHelper) {
-    Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
-  }
+  Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", !!autoInstallAddons);
+  Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", !!autoInstallAddons);
 
   let deferred = promise.defer();
 
   let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
   let win = ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
 
   win.addEventListener("load", function onLoad() {
     win.removeEventListener("load", onLoad);
--- a/browser/devtools/webide/test/test_addons.html
+++ b/browser/devtools/webide/test/test_addons.html
@@ -104,20 +104,20 @@
 
           win.Cmds.showAddons();
 
           let frame = win.document.querySelector("#deck-panel-addons");
           let addonDoc = frame.contentWindow.document;
           let lis;
 
           lis = addonDoc.querySelectorAll("li");
-          is(lis.length, 4, "4 addons listed");
+          is(lis.length, 5, "5 addons listed");
 
           lis = addonDoc.querySelectorAll('li[status="installed"]');
-          is(lis.length, 2, "2 addons installed");
+          is(lis.length, 3, "3 addons installed");
 
           lis = addonDoc.querySelectorAll('li[status="uninstalled"]');
           is(lis.length, 2, "2 addons uninstalled");
 
           info("Uninstalling Simulator 2.0");
 
           yield installSimulatorFromUI(addonDoc, "2.0");
 
--- a/browser/devtools/webide/webide-prefs.js
+++ b/browser/devtools/webide/webide-prefs.js
@@ -1,18 +1,21 @@
 # -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 # 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/.
 
 pref("devtools.webide.showProjectEditor", true);
 pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json");
 pref("devtools.webide.autoinstallADBHelper", true);
+pref("devtools.webide.autoinstallFxdtAdapters", false);
 pref("devtools.webide.restoreLastProject", true);
 pref("devtools.webide.enableLocalRuntime", true);
 pref("devtools.webide.addonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/index.json");
 pref("devtools.webide.simulatorAddonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/#VERSION#/#OS#/fxos_#SLASHED_VERSION#_simulator-#OS#-latest.xpi");
 pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozilla.org");
 pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
 pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
+pref("devtools.webide.adaptersAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxdt-adapters/#OS#/fxdt-adapters-#OS#-latest.xpi");
+pref("devtools.webide.adaptersAddonID", "fxdevtools-adapters@mozilla.org");
 pref("devtools.webide.monitorWebSocketURL", "ws://localhost:9000");
 pref("devtools.webide.lastConnectedRuntime", "");
 pref("devtools.webide.lastSelectedProject", "");
--- a/browser/locales/en-US/chrome/browser/devtools/webide.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webide.properties
@@ -47,16 +47,17 @@ addons_unstable=unstable
 # LOCALIZATION NOTE (addons_simulator_label): This label is shown as the name of
 # a given simulator version in the "Manage Simulators" pane.  %1$S: Firefox OS
 # version in the simulator, ex. 1.3.  %2$S: Simulator stability label, ex.
 # "stable" or "unstable".
 addons_simulator_label=Firefox OS %1$S Simulator (%2$S)
 addons_install_button=install
 addons_uninstall_button=uninstall
 addons_adb_label=ADB Helper Add-on
+addons_adapters_label=Tools Adapters Add-on
 addons_adb_warning=USB devices won't be detected without this add-on
 addons_status_unknown=?
 addons_status_installed=Installed
 addons_status_uninstalled=Not Installed
 addons_status_preparing=preparing
 addons_status_downloading=downloading
 addons_status_installing=installing