Bug 1506549 - Return target fronts out of RootFront.getProcess and getMainProcess. r=yulia
authorAlexandre Poirot <poirot.alex@gmail.com>
Thu, 15 Nov 2018 10:22:49 +0000
changeset 503055 bd6fcbbf257de863ca6334fe6683cebb1b6811f5
parent 503054 80ebeb322e14528b47e152cd306795745b4d6b5b
child 503056 6aa455d0de4638235d95af6005e801b7368b5f96
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyulia
bugs1506549
milestone65.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 1506549 - Return target fronts out of RootFront.getProcess and getMainProcess. r=yulia MozReview-Commit-ID: EGWYEmAkbtr Depends on D11693 Differential Revision: https://phabricator.services.mozilla.com/D11622
devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
devtools/client/framework/connect/connect.js
devtools/client/framework/devtools-browser.js
devtools/client/framework/target-from-url.js
devtools/client/framework/target.js
devtools/client/framework/test/browser_target_remote.js
devtools/client/framework/test/browser_target_support.js
devtools/client/framework/test/head.js
devtools/client/framework/toolbox-process-window.js
devtools/client/scratchpad/scratchpad.js
devtools/client/webconsole/hudservice.js
devtools/client/webide/modules/app-manager.js
devtools/server/tests/mochitest/test_getProcess.html
devtools/server/tests/unit/head_dbg.js
devtools/server/tests/unit/test_xpcshell_debugging.js
devtools/shared/fronts/root.js
devtools/shared/fronts/targets/browsing-context.js
devtools/shared/fronts/targets/content-process.js
devtools/shared/webconsole/test/common.js
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
@@ -23,22 +23,16 @@ function initDebuggerClient() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   let transport = DebuggerServer.connectPipe();
   return new DebuggerClient(transport);
 }
 
-async function attachThread(client, actor) {
-  let [response, targetFront] = await client.attachTarget(actor);
-  let [response2, threadClient] = await targetFront.attachThread(null);
-  return threadClient;
-}
-
 function onNewSource(event, packet) {
   if (packet.source.url.startsWith("chrome:")) {
     ok(true, "Received a new chrome source: " + packet.source.url);
     gThreadClient.removeListener("newSource", onNewSource);
     gNewChromeSource.resolve();
   }
 }
 
@@ -58,19 +52,20 @@ registerCleanupFunction(function() {
 });
 
 add_task(async function() {
   gClient = initDebuggerClient();
 
   const [type] = await gClient.connect();
   is(type, "browser", "Root actor should identify itself as a browser.");
 
-  const response = await gClient.mainRoot.getMainProcess();
-  let actor = response.form.actor;
-  gThreadClient = await attachThread(gClient, actor);
+  const front = await gClient.mainRoot.getMainProcess();
+  await front.attach();
+  const [, threadClient] = await front.attachThread();
+  gThreadClient = threadClient;
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:mozilla");
 
   // listen for a new source and global
   gThreadClient.addListener("newSource", onNewSource);
   await gNewChromeSource.promise;
 
   await resumeAndCloseConnection();
 });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
@@ -24,18 +24,18 @@ function test() {
     requestLongerTimeout(10);
 
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
-    let chrome = yield client.mainRoot.getMainProcess();
-    let [, targetFront] = yield attachTarget(client, chrome.form);
+    let targetFront = yield client.mainRoot.getMainProcess();
+    yield targetFront.attach();
     yield targetFront.attachThread();
 
     yield testGetAllocationStack(client, chrome.form, () => {
       let p = new Promise(() => {});
       p.name = "p";
       let q = p.then();
       q.name = "q";
       let r = p.catch(() => {});
--- a/devtools/client/framework/connect/connect.js
+++ b/devtools/client/framework/connect/connect.js
@@ -124,18 +124,18 @@ var onConnectionReady = async function([
   // Build the Remote Process button
   // If Fx<39, chrome target actors were used to be exposed on RootActor
   // but in Fx>=39, chrome is debuggable via getProcess() and ParentProcessTargetActor
   if (globals.consoleActor || gClient.mainRoot.traits.allowChromeProcess) {
     const a = document.createElement("a");
     a.onclick = function() {
       if (gClient.mainRoot.traits.allowChromeProcess) {
         gClient.mainRoot.getMainProcess()
-               .then(aResponse => {
-                 openToolbox(aResponse.form, true);
+               .then(front => {
+                 openToolbox(null, true, null, front);
                });
       } else if (globals.consoleActor) {
         openToolbox(globals, true, "webconsole", false);
       }
     };
     a.title = a.textContent = L10N.getStr("mainProcess");
     a.className = "remote-process";
     a.href = "#";
@@ -217,21 +217,22 @@ function showError(type) {
 function handleConnectionTimeout() {
   showError("timeout");
 }
 
 /**
  * The user clicked on one of the buttons.
  * Opens the toolbox.
  */
-function openToolbox(form, chrome = false, tool = "webconsole") {
+function openToolbox(form, chrome = false, tool = "webconsole", activeTab = null) {
   const options = {
-    form: form,
+    form,
+    activeTab,
     client: gClient,
-    chrome: chrome,
+    chrome,
   };
   TargetFactory.forRemoteTab(options).then((target) => {
     const hostType = Toolbox.HostType.WINDOW;
     gDevTools.showToolbox(target, tool, hostType).then((toolbox) => {
       toolbox.once("destroyed", function() {
         gClient.close();
       });
     }, console.error);
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -313,20 +313,18 @@ var gDevToolsBrowser = exports.gDevTools
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
 
     const transport = DebuggerServer.connectPipe();
     const client = new DebuggerClient(transport);
 
     await client.connect();
-    const { form } = await client.mainRoot.getProcess(processId);
-    const front = await client.mainRoot.attachContentProcessTarget(form);
+    const front = await client.mainRoot.getProcess(processId);
     const options = {
-      form,
       activeTab: front,
       client,
       chrome: true,
     };
     const target = await TargetFactory.forRemoteTab(options);
     // Ensure closing the connection in order to cleanup
     // the debugger client and also the server created in the
     // content process
--- a/devtools/client/framework/target-from-url.js
+++ b/devtools/client/framework/target-from-url.js
@@ -46,17 +46,17 @@ exports.targetFromURL = async function t
   if (!type) {
     throw new Error("targetFromURL, missing type parameter");
   }
   let id = params.get("id");
   // Allows to spawn a chrome enabled target for any context
   // (handy to debug chrome stuff in a content process)
   let chrome = params.has("chrome");
 
-  let form;
+  let form, front;
   if (type === "tab") {
     // Fetch target for a remote tab
     id = parseInt(id, 10);
     if (isNaN(id)) {
       throw new Error(`targetFromURL, wrong tab id '${id}', should be a number`);
     }
     try {
       const response = await client.getTab({ outerWindowID: id });
@@ -70,18 +70,17 @@ exports.targetFromURL = async function t
   } else if (type == "process") {
     // Fetch target for a remote chrome actor
     DebuggerServer.allowChromeProcess = true;
     try {
       id = parseInt(id, 10);
       if (isNaN(id)) {
         id = 0;
       }
-      const response = await client.mainRoot.getProcess(id);
-      form = response.form;
+      front = await client.mainRoot.getProcess(id);
       chrome = true;
     } catch (ex) {
       if (ex.error == "noProcess") {
         throw new Error(`targetFromURL, process with id '${id}' doesn't exist`);
       }
       throw ex;
     }
   } else if (type == "window") {
@@ -102,17 +101,17 @@ exports.targetFromURL = async function t
         throw new Error(`targetFromURL, window with id '${id}' doesn't exist`);
       }
       throw ex;
     }
   } else {
     throw new Error(`targetFromURL, unsupported type '${type}' parameter`);
   }
 
-  return TargetFactory.forRemoteTab({ client, form, chrome });
+  return TargetFactory.forRemoteTab({ client, form, activeTab: front, chrome });
 };
 
 /**
  * Create a DebuggerClient for a given URL object having various query parameters:
  *
  * host:
  *    {String} The hostname or IP address to connect to.
  * port:
--- a/devtools/client/framework/target.js
+++ b/devtools/client/framework/target.js
@@ -520,18 +520,27 @@ Target.prototype = {
    */
   attach() {
     if (this._attach) {
       return this._attach;
     }
 
     // Attach the target actor
     const attachBrowsingContextTarget = async () => {
-      const [, targetFront] = await this._client.attachTarget(this.form.actor);
-      this.activeTab = targetFront;
+      // Some BrowsingContextTargetFront are already instantiated and passed as
+      // contructor's argument, like for ParentProcessTargetActor.
+      // For them, we only need to attach them.
+      // The call to attachTarget is to be removed once all Target are having a front
+      // passed as contructor's argument.
+      if (!this.activeTab) {
+        const [, targetFront] = await this._client.attachTarget(this.form.actor);
+        this.activeTab = targetFront;
+      } else {
+        await this.activeTab.attach();
+      }
 
       this.activeTab.on("tabNavigated", this._onTabNavigated);
       this._onFrameUpdate = packet => {
         this.emit("frame-update", packet);
       };
       this.activeTab.on("frameUpdate", this._onFrameUpdate);
     };
 
--- a/devtools/client/framework/test/browser_target_remote.js
+++ b/devtools/client/framework/test/browser_target_remote.js
@@ -2,20 +2,20 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Ensure target is closed if client is closed directly
 function test() {
   waitForExplicitFinish();
 
-  getParentProcessActors((client, response) => {
+  getParentProcessActors((client, front) => {
     const options = {
-      form: response,
-      client: client,
+      activeTab: front,
+      client,
       chrome: true,
     };
 
     TargetFactory.forRemoteTab(options).then(target => {
       target.on("close", () => {
         ok(true, "Target was closed");
         finish();
       });
--- a/devtools/client/framework/test/browser_target_support.js
+++ b/devtools/client/framework/test/browser_target_support.js
@@ -47,20 +47,20 @@ async function testTarget(client, target
 
   close(target, client);
 }
 
 // Ensure target is closed if client is closed directly
 function test() {
   waitForExplicitFinish();
 
-  getParentProcessActors((client, response) => {
+  getParentProcessActors((client, front) => {
     const options = {
-      form: response,
-      client: client,
+      activeTab: front,
+      client,
       chrome: true,
     };
 
     TargetFactory.forRemoteTab(options).then(testTarget.bind(null, client));
   });
 }
 
 function close(target, client) {
--- a/devtools/client/framework/test/head.js
+++ b/devtools/client/framework/test/head.js
@@ -30,18 +30,18 @@ function getParentProcessActors(callback
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   const client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect()
     .then(() => client.mainRoot.getMainProcess())
-    .then(response => {
-      callback(client, response.form);
+    .then(front => {
+      callback(client, front);
     });
 
   SimpleTest.registerCleanupFunction(() => {
     DebuggerServer.destroy();
   });
 }
 
 function getSourceActor(aSources, aURL) {
--- a/devtools/client/framework/toolbox-process-window.js
+++ b/devtools/client/framework/toolbox-process-window.js
@@ -88,18 +88,18 @@ var connect = async function() {
   await gClient.connect();
 
   appendStatusMessage("Get root form for toolbox");
   if (addonID) {
     const { addons } = await gClient.listAddons();
     const addonTargetActor = addons.filter(addon => addon.id === addonID).pop();
     await openToolbox({form: addonTargetActor, chrome: true});
   } else {
-    const response = await gClient.mainRoot.getMainProcess();
-    await openToolbox({form: response.form, chrome: true});
+    const front = await gClient.mainRoot.getMainProcess();
+    await openToolbox({activeTab: front, chrome: true});
   }
 };
 
 // Certain options should be toggled since we can assume chrome debugging here
 function setPrefDefaults() {
   Services.prefs.setBoolPref("devtools.inspector.showUserAgentStyles", true);
   Services.prefs.setBoolPref("devtools.performance.ui.show-platform-data", true);
   Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
@@ -135,23 +135,24 @@ window.addEventListener("load", async fu
     console.error(e);
   }
 }, { once: true });
 
 function onCloseCommand(event) {
   window.close();
 }
 
-async function openToolbox({ form, chrome }) {
+async function openToolbox({ form, activeTab, chrome }) {
   let options = {
-    form: form,
+    form,
+    activeTab,
     client: gClient,
-    chrome: chrome,
+    chrome,
   };
-  appendStatusMessage(`Create toolbox target: ${JSON.stringify(arguments, null, 2)}`);
+  appendStatusMessage(`Create toolbox target: ${JSON.stringify({form, chrome}, null, 2)}`);
   const target = await TargetFactory.forRemoteTab(options);
   const frame = document.getElementById("toolbox-iframe");
 
   // Remember the last panel that was used inside of this profile.
   // But if we are testing, then it should always open the debugger panel.
   const selectedTool =
     Services.prefs.getCharPref("devtools.browsertoolbox.panel",
       Services.prefs.getCharPref("devtools.toolbox.selectedTool",
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -2066,18 +2066,22 @@ ScratchpadWindow.prototype = extend(Scra
    */
   async _attach() {
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
 
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await client.connect();
-    const response = await client.mainRoot.getMainProcess();
-    return { form: response.form, client };
+    const front = await client.mainRoot.getMainProcess();
+    const target = await TargetFactory.forRemoteTab({
+      activeTab: front,
+      client,
+    });
+    return target;
   },
 });
 
 function ScratchpadTarget(aTarget) {
   this._target = aTarget;
 }
 
 ScratchpadTarget.consoleFor = ScratchpadTab.consoleFor;
--- a/devtools/client/webconsole/hudservice.js
+++ b/devtools/client/webconsole/hudservice.js
@@ -131,18 +131,18 @@ HUDService.prototype = {
       // (See Bug 1416105 for rationale).
       DebuggerServer.init();
       DebuggerServer.registerActors({ root: true, target: true });
 
       DebuggerServer.allowChromeProcess = true;
 
       const client = new DebuggerClient(DebuggerServer.connectPipe());
       await client.connect();
-      const response = await client.mainRoot.getMainProcess();
-      return { form: response.form, client, chrome: true };
+      const front = await client.mainRoot.getMainProcess();
+      return { activeTab: front, client, chrome: true };
     }
 
     async function openWindow(t) {
       const win = Services.ww.openWindow(null, Tools.webConsole.url,
                                        "_blank", BC_WINDOW_FEATURES, null);
 
       await new Promise(resolve => {
         win.addEventListener("DOMContentLoaded", resolve, {once: true});
--- a/devtools/client/webide/modules/app-manager.js
+++ b/devtools/client/webide/modules/app-manager.js
@@ -250,19 +250,19 @@ var AppManager = exports.AppManager = {
     }, console.error);
   },
 
   getTarget: function() {
     if (this.selectedProject.type == "mainProcess") {
       // Fx >=39 exposes a ParentProcessTargetActor to debug the main process
       if (this.connection.client.mainRoot.traits.allowChromeProcess) {
         return this.connection.client.mainRoot.getMainProcess()
-                   .then(aResponse => {
+                   .then(front => {
                      return TargetFactory.forRemoteTab({
-                       form: aResponse.form,
+                       activeTab: front,
                        client: this.connection.client,
                        chrome: true,
                      });
                    });
       }
       // Fx <39 exposes chrome target actors on the root actor
       return TargetFactory.forRemoteTab({
           form: this._listTabsResponse,
--- a/devtools/server/tests/mochitest/test_getProcess.html
+++ b/devtools/server/tests/mochitest/test_getProcess.html
@@ -80,17 +80,18 @@ function runTests() {
     client.mainRoot.listProcesses().then(response => {
       ok(response.processes.length >= 2, "Got at least the parent process and one child");
       is(response.processes.length, processCount + 1,
          "Got one additional process on the second call to listProcesses");
 
       // Connect to the first content processe available
       const content = response.processes.filter(p => (!p.parent))[0];
 
-      client.mainRoot.getProcess(content.id).then(({form: actor}) => {
+      client.mainRoot.getProcess(content.id).then(front => {
+        const actor = front.targetForm;
         ok(actor.consoleActor, "Got the console actor");
         ok(actor.chromeDebugger, "Got the thread actor");
 
         // Ensure sending at least one request to an actor...
         client.request({
           to: actor.consoleActor,
           type: "evaluateJS",
           text: "var a = 42; a",
@@ -101,18 +102,18 @@ function runTests() {
         });
       });
     });
   }
 
   // Assert that calling client.getProcess against the same process id is
   // returning the same actor.
   function getProcessAgain(firstActor, id) {
-    client.mainRoot.getProcess(id).then(response => {
-      const actor = response.form;
+    client.mainRoot.getProcess(id).then(front => {
+      const actor = front.targetForm;
       is(actor, firstActor,
          "Second call to getProcess with the same id returns the same form");
       closeClient();
     });
   }
 
   function processScript() {
     ChromeUtils.import("resource://gre/modules/Services.jsm");
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -96,19 +96,19 @@ async function createTabMemoryFront() {
 async function createFullRuntimeMemoryFront() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   const client = new DebuggerClient(DebuggerServer.connectPipe());
   await client.connect();
 
-  const { form } = await client.mainRoot.getMainProcess();
+  const front = await client.mainRoot.getMainProcess();
   const options = {
-    form,
+    activeTab: front,
     client,
     chrome: true,
   };
   const target = await TargetFactory.forRemoteTab(options);
 
   const memoryFront = target.getFront("memory");
   await memoryFront.attach();
 
@@ -400,17 +400,17 @@ async function startTestDebuggerServer(t
 async function finishClient(client) {
   await client.close();
   DebuggerServer.destroy();
   do_test_finished();
 }
 
 function getParentProcessActors(client, server = DebuggerServer) {
   server.allowChromeProcess = true;
-  return client.mainRoot.getMainProcess().then(response => response.form);
+  return client.mainRoot.getMainProcess().then(response => response.targetForm);
 }
 
 /**
  * Takes a relative file path and returns the absolute file url for it.
  */
 function getFileUrl(name, allowMissing = false) {
   const file = do_get_file(name, allowMissing);
   return Services.io.newFileURI(file).spec;
--- a/devtools/server/tests/unit/test_xpcshell_debugging.js
+++ b/devtools/server/tests/unit/test_xpcshell_debugging.js
@@ -20,21 +20,19 @@ add_task(async function() {
   const client = new DebuggerClient(transport);
   await client.connect();
 
   // Ensure that global actors are available. Just test the device actor.
   const deviceFront = await client.mainRoot.getFront("device");
   const desc = await deviceFront.getDescription();
   equal(desc.geckobuildid, Services.appinfo.platformBuildID, "device actor works");
 
-  // Even though we have no tabs, getMainProcess gives us the chromeDebugger.
-  const response = await client.mainRoot.getMainProcess();
-
-  const { chromeDebugger } = response.form;
-  const [, threadClient] = await client.attachThread(chromeDebugger);
+  // Even though we have no tabs, getMainProcess gives us the chrome debugger.
+  const front = await client.mainRoot.getMainProcess();
+  const [, threadClient] = await front.attachThread();
   const onResumed = new Promise(resolve => {
     threadClient.addOneTimeListener("paused", (event, packet) => {
       equal(packet.why.type, "breakpoint",
           "yay - hit the breakpoint at the first line in our script");
       // Resume again - next stop should be our "debugger" statement.
       threadClient.addOneTimeListener("paused", (event, packet) => {
         equal(packet.why.type, "debuggerStatement",
               "yay - hit the 'debugger' statement in our script");
--- a/devtools/shared/fronts/root.js
+++ b/devtools/shared/fronts/root.js
@@ -4,16 +4,17 @@
 "use strict";
 
 const {Ci} = require("chrome");
 const {rootSpec} = require("devtools/shared/specs/root");
 const protocol = require("devtools/shared/protocol");
 const {custom} = protocol;
 
 loader.lazyRequireGetter(this, "getFront", "devtools/shared/protocol", true);
+loader.lazyRequireGetter(this, "BrowsingContextTargetFront", "devtools/shared/fronts/targets/browsing-context", true);
 loader.lazyRequireGetter(this, "ContentProcessTargetFront", "devtools/shared/fronts/targets/content-process", true);
 
 const RootFront = protocol.FrontClassWithSpec(rootSpec, {
   initialize: function(client, form) {
     protocol.Front.prototype.initialize.call(this, client, { actor: form.from });
 
     this.applicationType = form.applicationType;
     this.traits = form.traits;
@@ -63,22 +64,18 @@ const RootFront = protocol.FrontClassWit
 
       // And then from the Child processes
       const { processes } = await this.listProcesses();
       for (const process of processes) {
         // Ignore parent process
         if (process.parent) {
           continue;
         }
-        const { form } = await this.getProcess(process.id);
-        const processActor = form.actor;
-        const response = await this._client.request({
-          to: processActor,
-          type: "listWorkers",
-        });
+        const front = await this.getProcess(process.id);
+        const response = await front.listWorkers();
         workers = workers.concat(response.workers);
       }
     } catch (e) {
       // Something went wrong, maybe our client is disconnected?
     }
 
     const result = {
       service: [],
@@ -142,16 +139,43 @@ const RootFront = protocol.FrontClassWit
    *
    * `getProcess` requests allows to fetch the target actor for any process
    * and the main process is having the process ID zero.
    */
   getMainProcess() {
     return this.getProcess(0);
   },
 
+  getProcess: custom(async function(id) {
+    // Do not use specification automatic marshalling as getProcess may return
+    // two different type: ParentProcessTargetActor or ContentProcessTargetActor.
+    // Also, we do want to memoize the fronts and return already existing ones.
+    const { form } = await this._getProcess(id);
+    let front = this.actor(form.actor);
+    if (front) {
+      return front;
+    }
+    // getProcess may return a ContentProcessTargetActor or a ParentProcessTargetActor
+    // In most cases getProcess(0) will return the main process target actor,
+    // which is a ParentProcessTargetActor, but not in xpcshell, which uses a
+    // ContentProcessTargetActor. So select the right front based on the actor ID.
+    if (form.actor.includes("contentProcessTarget")) {
+      front = new ContentProcessTargetFront(this._client, form);
+    } else {
+      // ParentProcessTargetActor doesn't have a specific front, instead it uses
+      // BrowsingContextTargetFront on the client side.
+      front = new BrowsingContextTargetFront(this._client, form);
+    }
+    this.manage(front);
+
+    return front;
+  }, {
+    impl: "_getProcess",
+  }),
+
   /**
    * Fetch the target actor for the currently selected tab, or for a specific
    * tab given as first parameter.
    *
    * @param [optional] object filter
    *        A dictionary object with following optional attributes:
    *         - outerWindowID: used to match tabs in parent process
    *         - tabId: used to match tabs in child processes
@@ -186,25 +210,16 @@ const RootFront = protocol.FrontClassWit
       }
     }
 
     return this._getTab(packet);
   }, {
     impl: "_getTab",
   }),
 
-  attachContentProcessTarget: async function(form) {
-    let front = this.actor(form.actor);
-    if (!front) {
-      front = new ContentProcessTargetFront(this._client, form);
-      this.manage(front);
-    }
-    return front;
-  },
-
   /**
    * Test request that returns the object passed as first argument.
    *
    * `echo` is special as all the property of the given object have to be passed
    * on the packet object. That's not something that can be achieve by requester helper.
    */
 
   echo(packet) {
--- a/devtools/shared/fronts/targets/browsing-context.js
+++ b/devtools/shared/fronts/targets/browsing-context.js
@@ -19,16 +19,20 @@ protocol.FrontClassWithSpec(browsingCont
     // Cache the value of some target properties that are being returned by `attach`
     // request and then keep them up-to-date in `reconfigure` request.
     this.configureOptions = {
       javascriptEnabled: null,
     };
 
     // TODO: remove once ThreadClient becomes a front
     this.client = client;
+
+    // Save the full form for Target class usage
+    // Do not use `form` name to avoid colliding with protocol.js's `form` method
+    this.targetForm = form;
   },
 
   /**
    * Attach to a thread actor.
    *
    * @param object options
    *        Configuration options.
    *        - useSourceMaps: whether to use source maps or not.
--- a/devtools/shared/fronts/targets/content-process.js
+++ b/devtools/shared/fronts/targets/content-process.js
@@ -8,16 +8,20 @@ const protocol = require("devtools/share
 
 const ContentProcessTargetFront = protocol.FrontClassWithSpec(contentProcessTargetSpec, {
   initialize: function(client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
 
     this.client = client;
     this.chromeDebugger = form.chromeDebugger;
 
+    // Save the full form for Target class usage
+    // Do not use `form` name to avoid colliding with protocol.js's `form` method
+    this.targetForm = form;
+
     this.traits = {};
   },
 
   attachThread() {
     return this.client.attachThread(this.chromeDebugger);
   },
 
   reconfigure: function() {
--- a/devtools/shared/webconsole/test/common.js
+++ b/devtools/shared/webconsole/test/common.js
@@ -73,19 +73,19 @@ var _attachConsole = async function(
   if (response.error) {
     console.error("client.connect() failed: " + response.error + " " +
                   response.message);
     callback(state, response);
     return;
   }
 
   if (!attachToTab) {
-    response = await state.dbgClient.mainRoot.getMainProcess();
-    await state.dbgClient.attachTarget(response.form.actor);
-    const consoleActor = response.form.consoleActor;
+    const front = await state.dbgClient.mainRoot.getMainProcess();
+    await front.attach();
+    const consoleActor = front.targetForm.consoleActor;
     state.actor = consoleActor;
     state.dbgClient.attachConsole(consoleActor, listeners)
       .then(_onAttachConsole.bind(null, state), _onAttachError.bind(null, state));
     return;
   }
   response = await state.dbgClient.listTabs();
   if (response.error) {
     console.error("listTabs failed: " + response.error + " " +