Bug 968727 - Cleans up all markupview tests for consistency and to reduce file sizes. r=bgrins, a=test-only
authorPatrick Brosset <pbrosset@mozilla.com>
Tue, 25 Mar 2014 09:55:05 +0100
changeset 192927 bbdc0136061edb72bb94f131886b65cbf01f9737
parent 192926 76622a1dc44e73f8317397ba4589ef75e2b5f72c
child 192928 329ad4441fd9d2174f9138a5ee8e796e20af3770
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins, test-only
bugs968727
milestone30.0a2
Bug 968727 - Cleans up all markupview tests for consistency and to reduce file sizes. r=bgrins, a=test-only
browser/devtools/markupview/test/browser.ini
browser/devtools/markupview/test/browser_bug896181_css_mixed_completion_new_attribute.js
browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js
browser/devtools/markupview/test/browser_inspector_markup_950732.js
browser/devtools/markupview/test/browser_inspector_markup_962647_search.js
browser/devtools/markupview/test/browser_inspector_markup_964014_copy_image_data.js
browser/devtools/markupview/test/browser_inspector_markup_968316_highlight_node_after_mouseleave_mousemove.js
browser/devtools/markupview/test/browser_inspector_markup_968316_highlit_node_on_hover_then_select.js
browser/devtools/markupview/test/browser_inspector_markup_add_attributes.js
browser/devtools/markupview/test/browser_inspector_markup_edit.js
browser/devtools/markupview/test/browser_inspector_markup_edit_2.js
browser/devtools/markupview/test/browser_inspector_markup_edit_3.js
browser/devtools/markupview/test/browser_inspector_markup_edit_4.js
browser/devtools/markupview/test/browser_inspector_markup_edit_outerhtml.js
browser/devtools/markupview/test/browser_inspector_markup_edit_outerhtml2.js
browser/devtools/markupview/test/browser_inspector_markup_mutation.js
browser/devtools/markupview/test/browser_inspector_markup_mutation_flashing.js
browser/devtools/markupview/test/browser_inspector_markup_navigation.js
browser/devtools/markupview/test/browser_inspector_markup_subset.js
browser/devtools/markupview/test/head.js
browser/devtools/styleinspector/rule-view.js
--- a/browser/devtools/markupview/test/browser.ini
+++ b/browser/devtools/markupview/test/browser.ini
@@ -7,18 +7,16 @@ support-files =
   browser_inspector_markup_navigation.html
   browser_inspector_markup_subset.html
   browser_inspector_markup_765105_tooltip.png
   browser_inspector_markup_950732.html
   browser_inspector_markup_962647_search.html
   head.js
 
 [browser_bug896181_css_mixed_completion_new_attribute.js]
-# Bug 916763 - too many intermittent failures
-skip-if = true
 [browser_inspector_markup_edit.js]
 [browser_inspector_markup_edit_2.js]
 [browser_inspector_markup_edit_3.js]
 [browser_inspector_markup_edit_4.js]
 [browser_inspector_markup_add_attributes.js]
 [browser_inspector_markup_edit_outerhtml.js]
 [browser_inspector_markup_edit_outerhtml2.js]
 [browser_inspector_markup_mutation.js]
--- a/browser/devtools/markupview/test/browser_bug896181_css_mixed_completion_new_attribute.js
+++ b/browser/devtools/markupview/test/browser_bug896181_css_mixed_completion_new_attribute.js
@@ -1,176 +1,142 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+
 // Test CSS state is correctly determined and the corresponding suggestions are
 // displayed. i.e. CSS property suggestions are shown when cursor is like:
 // ```style="di|"``` where | is the cursor; And CSS value suggestion is
 // displayed when the cursor is like: ```style="display:n|"``` properly. No
 // suggestions should ever appear when the attribute is not a style attribute.
 // The correctness and cycling of the suggestions is covered in the ruleview
 // tests.
-function test() {
-  let inspector;
-  let {
-    getInplaceEditorForSpan: inplaceEditor
-  } = devtools.require("devtools/shared/inplace-editor");
-
-  waitForExplicitFinish();
-
-  // Will hold the doc we're viewing
-  let doc;
 
-  // Holds the MarkupTool object we're testing.
-  let markup;
-  let editor;
-  let state;
-  // format :
-  //  [
-  //    what key to press,
-  //    expected input box value after keypress,
-  //    expected input.selectionStart,
-  //    expected input.selectionEnd,
-  //    is popup expected to be open ?
-  //  ]
-  let testData = [
-    ['s', 's', 1, 1, false],
-    ['t', 'st', 2, 2, false],
-    ['y', 'sty', 3, 3, false],
-    ['l', 'styl', 4, 4, false],
-    ['e', 'style', 5, 5, false],
-    ['=', 'style=', 6, 6, false],
-    ['"', 'style="', 7, 7, false],
-    ['d', 'style="direction', 8, 16, true],
-    ['VK_DOWN', 'style="display', 8, 14, true],
-    ['VK_TAB', 'style="display', 14, 14, true],
-    ['VK_TAB', 'style="dominant-baseline', 24, 24, true],
-    ['VK_TAB', 'style="direction', 16, 16, true],
-    ['click_1', 'style="display', 14, 14, false],
-    [':', 'style="display:', 15, 15, false],
-    ['n', 'style="display:none', 16, 19, false],
-    ['VK_BACK_SPACE', 'style="display:n', 16, 16, false],
-    ['VK_BACK_SPACE', 'style="display:', 15, 15, false],
-    [' ', 'style="display: ', 16, 16, false],
-    [' ', 'style="display:  ', 17, 17, false],
-    ['i', 'style="display:  inherit', 18, 24, true],
-    ['VK_RIGHT', 'style="display:  inherit', 24, 24, false],
-    [';', 'style="display:  inherit;', 25, 25, false],
-    [' ', 'style="display:  inherit; ', 26, 26, false],
-    [' ', 'style="display:  inherit;  ', 27, 27, false],
-    ['VK_LEFT', 'style="display:  inherit;  ', 26, 26, false],
-    ['c', 'style="display:  inherit; caption-side ', 27, 38, true],
-    ['o', 'style="display:  inherit; color ', 28, 31, true],
-    ['VK_RIGHT', 'style="display:  inherit; color ', 31, 31, false],
-    [' ', 'style="display:  inherit; color  ', 32, 32, false],
-    ['c', 'style="display:  inherit; color c ', 33, 33, false],
-    ['VK_BACK_SPACE', 'style="display:  inherit; color  ', 32, 32, false],
-    [':', 'style="display:  inherit; color : ', 33, 33, false],
-    ['c', 'style="display:  inherit; color :cadetblue ', 34, 42, true],
-    ['VK_DOWN', 'style="display:  inherit; color :chartreuse ', 34, 43, true],
-    ['VK_RIGHT', 'style="display:  inherit; color :chartreuse ', 43, 43, false],
-    [' ', 'style="display:  inherit; color :chartreuse  ', 44, 44, false],
-    ['!', 'style="display:  inherit; color :chartreuse !important; ', 45, 55, false],
-    ['VK_RIGHT', 'style="display:  inherit; color :chartreuse !important; ', 55, 55, false],
-    ['VK_RETURN', 'style="display:  inherit; color :chartreuse !important;"', -1, -1, false]
-  ];
-  function startTests() {
-    markup = inspector.markup;
-    markup.expandAll().then(() => {
-      let node = getContainerForRawNode(markup, doc.querySelector("#node14")).editor;
-      let attr = node.newAttr;
-      attr.focus();
-      EventUtils.sendKey("return", inspector.panelWin);
-      editor = inplaceEditor(attr);
-      checkStateAndMoveOn(0);
-    });
+const TEST_URL = TEST_URL_ROOT + "browser_inspector_markup_edit.html";
+// test data format :
+//  [
+//    what key to press,
+//    expected input box value after keypress,
+//    expected input.selectionStart,
+//    expected input.selectionEnd,
+//    is popup expected to be open ?
+//  ]
+const TEST_DATA = [
+  ['s', 's', 1, 1, false],
+  ['t', 'st', 2, 2, false],
+  ['y', 'sty', 3, 3, false],
+  ['l', 'styl', 4, 4, false],
+  ['e', 'style', 5, 5, false],
+  ['=', 'style=', 6, 6, false],
+  ['"', 'style="', 7, 7, false],
+  ['d', 'style="direction', 8, 16, true],
+  ['VK_DOWN', 'style="display', 8, 14, true],
+  ['VK_TAB', 'style="display', 14, 14, true],
+  ['VK_TAB', 'style="dominant-baseline', 24, 24, true],
+  ['VK_TAB', 'style="direction', 16, 16, true],
+  ['click_1', 'style="display', 14, 14, false],
+  [':', 'style="display:', 15, 15, false],
+  ['n', 'style="display:none', 16, 19, false],
+  ['VK_BACK_SPACE', 'style="display:n', 16, 16, false],
+  ['VK_BACK_SPACE', 'style="display:', 15, 15, false],
+  [' ', 'style="display: ', 16, 16, false],
+  [' ', 'style="display:  ', 17, 17, false],
+  ['i', 'style="display:  inherit', 18, 24, true],
+  ['VK_RIGHT', 'style="display:  inherit', 24, 24, false],
+  [';', 'style="display:  inherit;', 25, 25, false],
+  [' ', 'style="display:  inherit; ', 26, 26, false],
+  [' ', 'style="display:  inherit;  ', 27, 27, false],
+  ['VK_LEFT', 'style="display:  inherit;  ', 26, 26, false],
+  ['c', 'style="display:  inherit; caption-side ', 27, 38, true],
+  ['o', 'style="display:  inherit; color ', 28, 31, true],
+  ['VK_RIGHT', 'style="display:  inherit; color ', 31, 31, false],
+  [' ', 'style="display:  inherit; color  ', 32, 32, false],
+  ['c', 'style="display:  inherit; color c ', 33, 33, false],
+  ['VK_BACK_SPACE', 'style="display:  inherit; color  ', 32, 32, false],
+  [':', 'style="display:  inherit; color : ', 33, 33, false],
+  ['c', 'style="display:  inherit; color :cadetblue ', 34, 42, true],
+  ['VK_DOWN', 'style="display:  inherit; color :chartreuse ', 34, 43, true],
+  ['VK_RIGHT', 'style="display:  inherit; color :chartreuse ', 43, 43, false],
+  [' ', 'style="display:  inherit; color :chartreuse  ', 44, 44, false],
+  ['!', 'style="display:  inherit; color :chartreuse !important; ', 45, 55, false],
+  ['VK_RIGHT', 'style="display:  inherit; color :chartreuse !important; ', 55, 55, false],
+  ['VK_RETURN', 'style="display:  inherit; color :chartreuse !important;"', -1, -1, false]
+];
+
+let test = asyncTest(function*() {
+  info("Opening the inspector on the test URL");
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  yield inspector.markup.expandAll();
+
+  let node = getContainerForRawNode("#node14", inspector).editor;
+  let attr = node.newAttr;
+  attr.focus();
+  EventUtils.sendKey("return", inspector.panelWin);
+  let editor = inplaceEditor(attr);
+
+  for (let i = 0; i < TEST_DATA.length; i ++) {
+    yield enterData(i, editor, inspector);
+    checkData(i, editor, inspector);
   }
 
-  function checkStateAndMoveOn(index) {
-    if (index == testData.length) {
-      finishUp();
-      return;
-    }
+  while (inspector.markup.undo.canUndo()) {
+    yield undoChange(inspector);
+  }
 
-    let [key] = testData[index];
-    state = index;
+  yield inspector.once("inspector-updated");
+});
+
+function enterData(index, editor, inspector) {
+  let [key] = TEST_DATA[index];
+  info("Entering test data " + index + ": " + key + ", expecting: [" + TEST_DATA[index].slice(1) + "]");
 
-    info("pressing key " + key + " to get result: [" + testData[index].slice(1) +
-         "] for state " + state);
-    if (/(down|left|right|back_space|return)/ig.test(key)) {
-      info("added event listener for down|left|right|back_space|return keys");
-      editor.input.addEventListener("keypress", function onKeypress() {
-        if (editor.input) {
-          editor.input.removeEventListener("keypress", onKeypress);
-        }
-        info("inside event listener");
-        checkState();
-      })
-    }
-    else if (/click_[0-9]/.test(key)) {
-      editor.once("after-suggest", checkState);
-      let index = +key.split("_")[1];
-      editor.popup._list.childNodes[index].click();
-      editor.input.blur();
-      return;
-    }
-    else {
-      editor.once("after-suggest", checkState);
-    }
-    EventUtils.synthesizeKey(key, {}, inspector.panelWin);
+  let def = promise.defer();
+
+  if (/click_[0-9]/.test(key)) {
+    let nb = +key.split("_")[1];
+    info("Clicking on item " + nb + " in the list");
+    editor.once("after-suggest", () => {
+      executeSoon(def.resolve);
+    });
+    editor.popup._list.childNodes[nb].click();
+    editor.input.blur();
+    return def.promise;
   }
 
-  function checkState() {
-    executeSoon(() => {
-      info("After keypress for state " + state);
-      let [key, completion, selStart, selEnd, popupOpen] = testData[state];
-      if (selEnd != -1) {
-        is(editor.input.value, completion,
-           "Correct value is autocompleted for state " + state);
-        is(editor.input.selectionStart, selStart,
-           "Selection is starting at the right location for state " + state);
-        is(editor.input.selectionEnd, selEnd,
-           "Selection is ending at the right location for state " + state);
-        if (popupOpen) {
-          ok(editor.popup.isOpen, "Popup is open for state " + state);
-        }
-        else {
-          ok(editor.popup._panel.state != "open" &&
-             editor.popup._panel.state != "showing",
-             "Popup is closed for state " + state);
-        }
+  if (/(down|left|right|back_space|return)/ig.test(key)) {
+    info("Adding event listener for down|left|right|back_space|return keys");
+    editor.input.addEventListener("keypress", function onKeypress() {
+      if (editor.input) {
+        editor.input.removeEventListener("keypress", onKeypress);
       }
-      else {
-        let editor = getContainerForRawNode(markup, doc.querySelector("#node14")).editor;
-        let attr = editor.attrs["style"].querySelector(".editable");
-        is(attr.textContent, completion,
-           "Correct value is persisted after pressing Enter for state " + state);
-      }
-      checkStateAndMoveOn(state + 1);
+      executeSoon(def.resolve);
+    });
+  } else {
+    editor.once("after-suggest", () => {
+      executeSoon(def.resolve);
     });
   }
+  EventUtils.synthesizeKey(key, {}, inspector.panelWin);
 
-  // Create the helper tab for parsing...
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(setupTest, content);
-  }, true);
-  content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_edit.html";
+  return def.promise;
+}
+
+function checkData(index, editor, inspector) {
+  let [key, completion, selStart, selEnd, popupOpen] = TEST_DATA[index];
+  info("Test data " + index + " entered. Checking state.");
 
-  function setupTest() {
-    var target = TargetFactory.forTab(gBrowser.selectedTab);
-    gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      startTests();
-    });
-  }
-
-  function finishUp() {
-    while (markup.undo.canUndo()) {
-      markup.undo.undo();
+  if (selEnd != -1) {
+    is(editor.input.value, completion, "Completed value is correct");
+    is(editor.input.selectionStart, selStart, "Selection start position is correct");
+    is(editor.input.selectionEnd, selEnd, "Selection end position is correct");
+    if (popupOpen) {
+      ok(editor.popup.isOpen, "Popup is open");
+    } else {
+      ok(editor.popup._panel.state != "open" && editor.popup._panel.state != "showing",
+        "Popup is closed");
     }
-    doc = inspector = null;
-    gBrowser.removeCurrentTab();
-    finish();
+  } else {
+    let editor = getContainerForRawNode("#node14", inspector).editor;
+    let attr = editor.attrs["style"].querySelector(".editable");
+    is(attr.textContent, completion, "Correct value is persisted after pressing Enter");
   }
 }
--- a/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.js
@@ -1,125 +1,85 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let {PanelFactory} = devtools.require("devtools/shared/widgets/Tooltip");
-
-let contentDoc;
-let inspector;
-let markup;
+// Test that image preview tooltips are shown on img and canvas tags in the
+// markup-view and that the tooltip actually contains an image and shows the
+// right dimension label
 
 const PAGE_CONTENT = [
   '<img class="local" src="chrome://branding/content/about-logo.png" />',
   '<img class="data" src="data:image/png;base64,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" />',
-  '<img class="remote" src="http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_765105_tooltip.png" />',
+  '<img class="remote" src="' + TEST_URL_ROOT + 'browser_inspector_markup_765105_tooltip.png" />',
   '<canvas class="canvas" width="600" height="600"></canvas>'
 ].join("\n");
 
 const TEST_NODES = [
   {selector: "img.local", size: "192 x 192"},
   {selector: "img.data", size: "64 x 64"},
   {selector: "img.remote", size: "22 x 23"},
   {selector: ".canvas", size: "600 x 600"}
 ];
 
-function test() {
-  waitForExplicitFinish();
+let test = asyncTest(function*() {
+  yield addTab("data:text/html,markup view tooltip test");
+  createPage();
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
-    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
-    contentDoc = content.document;
-    waitForFocus(createDocument, content);
-  }, true);
+  let {inspector} = yield openInspector();
 
-  content.location = "data:text/html,markup view tooltip test";
-}
+  info("Selecting the first <img> tag");
+  yield selectNode("img", inspector);
 
-function createDocument() {
-  contentDoc.body.innerHTML = PAGE_CONTENT;
+  for (let testNode of TEST_NODES) {
+    let target = getImageTooltipTarget(testNode, inspector);
+    yield assertTooltipShownOn(target, inspector);
+    checkImageTooltip(testNode, inspector);
+  }
+});
 
-  var target = TargetFactory.forTab(gBrowser.selectedTab);
-  gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-    inspector = toolbox.getCurrentPanel();
-    markup = inspector.markup;
-    startTests();
-  });
-}
+function createPage() {
+  info("Fill the page with the test content");
+  content.document.body.innerHTML = PAGE_CONTENT;
 
-function startTests() {
-  // Draw something in the canvas :)
+  info("Fill the canvas");
   let doc = content.document;
   let context = doc.querySelector(".canvas").getContext("2d");
 
   context.beginPath();
   context.moveTo(300, 0);
   context.lineTo(600, 600);
   context.lineTo(0, 600);
   context.closePath();
   context.fillStyle = "#ffc821";
   context.fill();
-
-  // Actually start testing
-  inspector.selection.setNode(contentDoc.querySelector("img"));
-  inspector.once("inspector-updated", () => {
-    testImageTooltip(0);
-  });
 }
 
-function endTests() {
-  contentDoc = inspector = markup = null;
-  gBrowser.removeCurrentTab();
-  finish();
-}
-
-function testImageTooltip(index) {
-  if (index === TEST_NODES.length) {
-    return endTests();
-  }
-
-  let node = contentDoc.querySelector(TEST_NODES[index].selector);
-  ok(node, "We have the [" + TEST_NODES[index].selector + "] image node to test for tooltip");
+function getImageTooltipTarget({selector}, inspector) {
+  let node = getNode(selector);
   let isImg = node.tagName.toLowerCase() === "img";
 
-  let container = getContainerForRawNode(markup, node);
-
-  let target = container.editor.tag;
-  if (isImg) {
-    target = container.editor.getAttributeElement("src");
-  }
+  let container = getContainerForRawNode(node, inspector);
 
-  assertTooltipShownOn(target).then(() => {
-    let images = markup.tooltip.panel.getElementsByTagName("image");
-    is(images.length, 1,
-      "Tooltip for [" + TEST_NODES[index].selector + "] contains an image");
-
-    let label = markup.tooltip.panel.querySelector(".devtools-tooltip-caption");
-    is(label.textContent, TEST_NODES[index].size,
-      "Tooltip label for [" + TEST_NODES[index].selector + "] displays the right image size")
-
-    testImageTooltip(index + 1);
-  });
+   let target = container.editor.tag;
+   if (isImg) {
+     target = container.editor.getAttributeElement("src");
+   }
+  return target;
 }
 
-function compareImageData(img, imgData) {
-  let canvas = content.document.createElement("canvas");
-  canvas.width = img.naturalWidth;
-  canvas.height = img.naturalHeight;
-  let ctx = canvas.getContext("2d");
-  let data = "";
-  try {
-    ctx.drawImage(img, 0, 0);
-    data = canvas.toDataURL("image/png");
-  } catch (e) {}
-
-  is(data, imgData, "Tooltip image has the right content");
-}
-
-function assertTooltipShownOn(element, cb) {
+function assertTooltipShownOn(element, {markup}) {
   return Task.spawn(function*() {
     info("Is the element a valid hover target");
-
     let isValid = yield markup.tooltip.isValidHoverTarget(element);
     ok(isValid, "The element is a valid hover target for the image tooltip");
   });
 }
+
+function checkImageTooltip({selector, size}, {markup}) {
+  let images = markup.tooltip.panel.getElementsByTagName("image");
+  is(images.length, 1, "Tooltip for [" + selector + "] contains an image");
+
+  let label = markup.tooltip.panel.querySelector(".devtools-tooltip-caption");
+  is(label.textContent, size, "Tooltip label for [" + selector + "] displays the right image size");
+
+  markup.tooltip.hide();
+}
--- a/browser/devtools/markupview/test/browser_inspector_markup_950732.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_950732.js
@@ -1,105 +1,44 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/**
- * Tests that the markup view loads only as many nodes as specified
- * by the devtools.markup.pagesize preference.
- */
+// Tests that the markup view loads only as many nodes as specified
+// by the devtools.markup.pagesize preference and that pressing the "show all nodes"
+// actually shows the nodes
 
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-let promise = devtools.require("sdk/core/promise");
-let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
+const TEST_URL = TEST_URL_ROOT + "browser_inspector_markup_950732.html";
 
 // Make sure nodes are hidden when there are more than 5 in a row
-registerCleanupFunction(function() {
-  Services.prefs.clearUserPref("devtools.markup.pagesize");
-});
 Services.prefs.setIntPref("devtools.markup.pagesize", 5);
 
-function test() {
-  waitForExplicitFinish();
-
-  let doc;
-  let inspector;
-  let markup;
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(runTests, content);
-  }, true);
-  content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_950732.html";
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
 
-  function runTests() {
-    Task.spawn(function() {
-      yield openMarkupView();
-      yield selectUL();
-      yield reloadPage();
-      yield showAllNodes();
-
-      assertAllNodesAreVisible();
-      finishUp();
-    }).then(null, Cu.reportError);
-  }
+  info("Selecting the UL node");
+  yield clickContainer("ul", inspector);
+  info("Reloading the page with the UL node selected will expand its children");
+  yield reloadPage(inspector);
+  yield inspector.markup._waitForChildren();
 
-  function openMarkupView() {
-    let deferred = promise.defer();
+  info("Click on the 'show all nodes' button in the UL's list of children");
+  yield showAllNodes(inspector);
 
-    var target = TargetFactory.forTab(gBrowser.selectedTab);
-    let toolbox = gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      markup = inspector.markup;
-      inspector.once("inspector-updated", deferred.resolve);
-    });
-
-    return deferred.promise;
-  }
-
-  function selectUL() {
-    let deferred = promise.defer();
+  assertAllNodesAreVisible(inspector);
+});
 
-    let container = getContainerForRawNode(markup, doc.querySelector("ul"));
-    let win = container.elt.ownerDocument.defaultView;
-
-    EventUtils.sendMouseEvent({type: "mousedown"}, container.elt, win);
-    inspector.once("inspector-updated", deferred.resolve);
-
-    return deferred.promise;
-  }
-
-  function reloadPage() {
-    let deferred = promise.defer();
-
-    inspector.once("new-root", () => {
-      doc = content.document;
-      markup = inspector.markup;
-      markup._waitForChildren().then(deferred.resolve);
-    });
-    content.location.reload();
-
-    return deferred.promise;
-  }
+function showAllNodes(inspector) {
+  let container = getContainerForRawNode("ul", inspector);
+  let button = container.elt.querySelector("button");
+  ok(button, "All nodes button is here");
+  let win = button.ownerDocument.defaultView;
 
-  function showAllNodes() {
-    let container = getContainerForRawNode(markup, doc.querySelector("ul"));
-    let button = container.elt.querySelector("button");
-    let win = button.ownerDocument.defaultView;
-
-    EventUtils.sendMouseEvent({type: "click"}, button, win);
-    return markup._waitForChildren();
-  }
+  EventUtils.sendMouseEvent({type: "click"}, button, win);
+  return inspector.markup._waitForChildren();
+}
 
-  function assertAllNodesAreVisible() {
-    let ul = doc.querySelector("ul");
-    let container = getContainerForRawNode(markup, ul);
-    ok(!container.elt.querySelector("button"), "All nodes button isn't here");
-    is(container.children.childNodes.length, ul.children.length);
-  }
-
-  function finishUp() {
-    doc = inspector = markup = null;
-    gBrowser.removeCurrentTab();
-    finish();
-  }
+function assertAllNodesAreVisible(inspector) {
+  let ul = getNode("ul");
+  let container = getContainerForRawNode(ul, inspector);
+  ok(!container.elt.querySelector("button"), "All nodes button isn't here anymore");
+  is(container.children.childNodes.length, ul.children.length);
 }
--- a/browser/devtools/markupview/test/browser_inspector_markup_962647_search.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_962647_search.js
@@ -1,50 +1,39 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that searching for nodes using the selector-search input expands and
 // selects the right nodes in the markup-view, even when those nodes are deeply
 // nested (and therefore not attached yet when the markup-view is initialized).
 
-const TEST_URL = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_962647_search.html";
-
-function test() {
-  waitForExplicitFinish();
+const TEST_URL = TEST_URL_ROOT + "browser_inspector_markup_962647_search.html";
 
-  let p = content.document.querySelector("p");
-  Task.spawn(function() {
-    info("loading the test page");
-    yield addTab(TEST_URL);
+let test = asyncTest(function*() {
+  let {inspector, toolbox} = yield addTab(TEST_URL).then(openInspector);
 
-    info("opening the inspector");
-    let {inspector, toolbox} = yield openInspector();
-
-    ok(!getContainerForRawNode(inspector.markup, getNode("em")),
-      "The <em> tag isn't present yet in the markup-view");
+  ok(!getContainerForRawNode("em", inspector),
+    "The <em> tag isn't present yet in the markup-view");
 
-    // Searching for the innermost element first makes sure that the inspector
-    // back-end is able to attach the resulting node to the tree it knows at the
-    // moment. When the inspector is started, the <body> is the default selected
-    // node, and only the parents up to the ROOT are known, and its direct children
-    info("searching for the innermost child: <em>");
-    let updated = inspector.once("inspector-updated");
-    searchUsingSelectorSearch("em", inspector);
-    yield updated;
+  // Searching for the innermost element first makes sure that the inspector
+  // back-end is able to attach the resulting node to the tree it knows at the
+  // moment. When the inspector is started, the <body> is the default selected
+  // node, and only the parents up to the ROOT are known, and its direct children
+  info("searching for the innermost child: <em>");
+  let updated = inspector.once("inspector-updated");
+  searchUsingSelectorSearch("em", inspector);
+  yield updated;
 
-    ok(getContainerForRawNode(inspector.markup, getNode("em")),
-      "The <em> tag is now imported in the markup-view");
-    is(inspector.selection.node, getNode("em"),
-      "The <em> tag is the currently selected node");
+  ok(getContainerForRawNode("em", inspector),
+    "The <em> tag is now imported in the markup-view");
+  is(inspector.selection.node, getNode("em"),
+    "The <em> tag is the currently selected node");
 
-    info("searching for other nodes too");
-    for (let node of ["span", "li", "ul"]) {
-      let updated = inspector.once("inspector-updated");
-      searchUsingSelectorSearch(node, inspector);
-      yield updated;
-      is(inspector.selection.node, getNode(node),
-        "The <" + node + "> tag is the currently selected node");
-    }
-
-    gBrowser.removeCurrentTab();
-  }).then(null, ok.bind(null, false)).then(finish);
-}
+  info("searching for other nodes too");
+  for (let node of ["span", "li", "ul"]) {
+    let updated = inspector.once("inspector-updated");
+    searchUsingSelectorSearch(node, inspector);
+    yield updated;
+    is(inspector.selection.node, getNode(node),
+      "The <" + node + "> tag is the currently selected node");
+  }
+});
--- a/browser/devtools/markupview/test/browser_inspector_markup_964014_copy_image_data.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_964014_copy_image_data.js
@@ -1,131 +1,109 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that image nodes have the "copy data-uri" contextual menu item enabled
 // and that clicking it puts the image data into the clipboard
 
-let doc;
-let inspector;
-let markup;
-
 const PAGE_CONTENT = [
   '<div></div>',
   '<img class="data" src="data:image/png;base64,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" />',
   '<canvas class="canvas" width="600" height="600"></canvas>'
 ].join("\n");
 
-function test() {
-  waitForExplicitFinish();
+let test = asyncTest(function*() {
+  yield addTab("data:text/html,markup view copy image as data-uri");
+  createDocument();
+  let doc = content.document;
+
+  let {inspector} = yield openInspector();
+
+  yield selectNode("div", inspector);
+  yield assertCopyImageDataNotAvailable(inspector);
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload(evt) {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(createDocument, content);
-  }, true);
+  yield selectNode("img", inspector);
+  yield assertCopyImageDataAvailable(inspector);
+  yield triggerCopyImageUrlAndWaitForClipboard(doc.querySelector("img").src, inspector);
 
-  content.location = "data:text/html,markup view copy image as data-uri";
-}
+  yield selectNode("canvas", inspector);
+  yield assertCopyImageDataAvailable(inspector);
+  let canvas = doc.querySelector(".canvas");
+  yield triggerCopyImageUrlAndWaitForClipboard(canvas.toDataURL(), inspector);
+
+  // Check again that the menu isn't available on the DIV (to make sure our
+  // menu updating mechanism works)
+  yield selectNode("div", inspector);
+  yield assertCopyImageDataNotAvailable(inspector);
+});
 
 function createDocument() {
+  let doc = content.document;
+
   doc.body.innerHTML = PAGE_CONTENT;
   let context = doc.querySelector(".canvas").getContext("2d");
   context.beginPath();
   context.moveTo(300, 0);
   context.lineTo(600, 600);
   context.lineTo(0, 600);
   context.closePath();
   context.fillStyle = "#ffc821";
   context.fill();
-
-  openInspector().then(startTests);
 }
 
-function startTests({inspector: aInspector, toolbox: aToolbox}) {
-  inspector = aInspector;
-  markup = inspector.markup;
-
-  Task.spawn(function() {
-    yield selectNode("div", inspector);
-    yield assertCopyImageDataNotAvailable();
-
-    yield selectNode("img", inspector);
-    yield assertCopyImageDataAvailable();
-    yield triggerCopyImageUrlAndWaitForClipboard(doc.querySelector("img").src);
-
-    yield selectNode("canvas", inspector);
-    yield assertCopyImageDataAvailable();
-    let canvas = doc.querySelector(".canvas");
-    yield triggerCopyImageUrlAndWaitForClipboard(canvas.toDataURL());
-
-    // Check again that the menu isn't available on the DIV (to make sure our
-    // menu updating mechanism works)
-    yield selectNode("div", inspector);
-    yield assertCopyImageDataNotAvailable();
-  }).then(null, ok.bind(null, false)).then(endTests);
-}
-
-function endTests() {
-  doc = inspector = markup = null;
-  gBrowser.removeCurrentTab();
-  finish();
-}
-
-function assertCopyImageDataNotAvailable() {
-  return openNodeMenu().then(menu => {
+function assertCopyImageDataNotAvailable(inspector) {
+  return openNodeMenu(inspector).then(menu => {
     let item = menu.getElementsByAttribute("id", "node-menu-copyimagedatauri")[0];
     ok(item, "The menu item was found in the contextual menu");
     is(item.getAttribute("disabled"), "true", "The menu item is disabled");
-  }).then(closeNodeMenu);
+  }).then(() => closeNodeMenu(inspector));
 }
 
-function assertCopyImageDataAvailable() {
-  return openNodeMenu().then(menu => {
+function assertCopyImageDataAvailable(inspector) {
+  return openNodeMenu(inspector).then(menu => {
     let item = menu.getElementsByAttribute("id", "node-menu-copyimagedatauri")[0];
     ok(item, "The menu item was found in the contextual menu");
     is(item.getAttribute("disabled"), "", "The menu item is enabled");
-  }).then(closeNodeMenu);
+  }).then(() => closeNodeMenu(inspector));
 }
 
-function openNodeMenu() {
-  let deferred = promise.defer();
+function openNodeMenu(inspector) {
+  let def = promise.defer();
 
   inspector.nodemenu.addEventListener("popupshown", function onOpen() {
     inspector.nodemenu.removeEventListener("popupshown", onOpen, false);
-    deferred.resolve(inspector.nodemenu);
+    def.resolve(inspector.nodemenu);
   }, false);
   inspector.nodemenu.hidden = false;
   inspector.nodemenu.openPopup();
 
-  return deferred.promise;
+  return def.promise;
 }
 
-function closeNodeMenu() {
-  let deferred = promise.defer();
+function closeNodeMenu(inspector) {
+  let def = promise.defer();
 
   inspector.nodemenu.addEventListener("popuphidden", function onClose() {
     inspector.nodemenu.removeEventListener("popuphidden", onClose, false);
-    deferred.resolve(inspector.nodemenu);
+    def.resolve(inspector.nodemenu);
   }, false);
   inspector.nodemenu.hidden = true;
   inspector.nodemenu.hidePopup();
 
-  return deferred.promise;
+  return def.promise;
 }
 
-function triggerCopyImageUrlAndWaitForClipboard(expected) {
-  let deferred = promise.defer();
+function triggerCopyImageUrlAndWaitForClipboard(expected, inspector) {
+  let def = promise.defer();
 
   SimpleTest.waitForClipboard(expected, () => {
-    markup.getContainer(inspector.selection.nodeFront).copyImageDataUri();
+    inspector.markup.getContainer(inspector.selection.nodeFront).copyImageDataUri();
   }, () => {
     ok(true, "The clipboard contains the expected value " + expected.substring(0, 50) + "...");
-    deferred.resolve();
+    def.resolve();
   }, () => {
     ok(false, "The clipboard doesn't contain the expected value " + expected.substring(0, 50) + "...");
-    deferred.resolve();
+    def.resolve();
   });
 
-  return deferred.promise;
+  return def.promise;
 }
--- a/browser/devtools/markupview/test/browser_inspector_markup_968316_highlight_node_after_mouseleave_mousemove.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_968316_highlight_node_after_mouseleave_mousemove.js
@@ -1,47 +1,26 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that when after an element is selected and highlighted on hover, if the
 // mouse leaves the markup-view and comes back again on the same element, that
 // the highlighter is shown again on the node
 
-function test() {
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload(evt) {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    waitForFocus(startTests, content);
-  }, true);
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab("data:text/html,<p>Select me!</p>").then(openInspector);
 
-  content.location = "data:text/html;charset=utf-8,<p>Select me!</p>";
-}
-
-function startTests(aInspector, aToolbox) {
-  let p = content.document.querySelector("p");
-  Task.spawn(function() {
-    info("opening the inspector tool");
-    let {inspector, toolbox} = yield openInspector();
+  info("hover over the <p> line in the markup-view so that it's the currently hovered node");
+  yield hoverContainer("p", inspector);
 
-    info("hover over the <p> line in the markup-view so that it's the currently hovered node");
-    yield hoverContainer(p, inspector);
-
-    info("select the <p> markup-container line by clicking");
-    yield clickContainer(p, inspector);
-    ok(isHighlighterVisible(), "the highlighter is shown");
-
-    info("mouse-leave the markup-view");
-    yield mouseLeaveMarkupView(inspector);
-    ok(!isHighlighterVisible(), "the highlighter is hidden after mouseleave");
+  info("select the <p> markup-container line by clicking");
+  yield clickContainer("p", inspector);
+  ok(isHighlighterVisible(), "the highlighter is shown");
 
-    info("hover over the <p> line again, which is still selected");
-    yield hoverContainer(p, inspector);
-    ok(isHighlighterVisible(), "the highlighter is visible again");
-  }).then(null, ok.bind(null, false)).then(endTests);
-}
+  info("mouse-leave the markup-view");
+  yield mouseLeaveMarkupView(inspector);
+  ok(!isHighlighterVisible(), "the highlighter is hidden after mouseleave");
 
-function endTests() {
-  gBrowser.removeCurrentTab();
-  finish();
-}
+  info("hover over the <p> line again, which is still selected");
+  yield hoverContainer("p", inspector);
+  ok(isHighlighterVisible(), "the highlighter is visible again");
+});
--- a/browser/devtools/markupview/test/browser_inspector_markup_968316_highlit_node_on_hover_then_select.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_968316_highlit_node_on_hover_then_select.js
@@ -1,54 +1,36 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that when first hovering over a node and immediately after selecting it
 // by clicking on it leaves the highlighter visible for as long as the mouse is
 // over the node
 
-function test() {
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload(evt) {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    waitForFocus(startTests, content);
-  }, true);
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab("data:text/html,<p>It's going to be legen....</p>").then(openInspector);
+  let p = getNode("p");
 
-  content.location = "data:text/html;charset=utf-8,<p>It's going to be legen....</p>";
-}
-
-function startTests(aInspector, aToolbox) {
-  let p = content.document.querySelector("p");
-  Task.spawn(function() {
-    info("opening the inspector tool");
-    let {inspector, toolbox} = yield openInspector();
+  info("hovering over the <p> line in the markup-view");
+  yield hoverContainer(p, inspector);
+  ok(isHighlighterVisible(), "the highlighter is still visible");
 
-    info("hovering over the <p> line in the markup-view");
-    yield hoverContainer(p, inspector);
-    ok(isHighlighterVisible(), "the highlighter is still visible");
-
-    info("selecting the <p> line by clicking in the markup-view");
-    yield clickContainer(p, inspector);
+  info("selecting the <p> line by clicking in the markup-view");
+  yield clickContainer(p, inspector);
 
-    p.textContent = "wait for it ....";
-    info("wait and see if the highlighter stays visible even after the node was selected");
-    yield waitForTheBrieflyShowBoxModelTimeout();
+  p.textContent = "wait for it ....";
+  info("wait and see if the highlighter stays visible even after the node was selected");
+  yield waitForTheBrieflyShowBoxModelTimeout();
 
-    p.textContent = "dary!!!!";
-    ok(isHighlighterVisible(), "the highlighter is still visible");
-  }).then(null, ok.bind(null, false)).then(endTests);
-}
-
-function endTests() {
-  gBrowser.removeCurrentTab();
-  finish();
-}
+  let updated = inspector.once("inspector-updated");
+  p.textContent = "dary!!!!";
+  ok(isHighlighterVisible(), "the highlighter is still visible");
+  yield updated;
+});
 
 function waitForTheBrieflyShowBoxModelTimeout() {
   let deferred = promise.defer();
   // Note that the current timeout is 1 sec and is neither configurable nor
   // exported anywhere we can access, so hard-coding the timeout
   content.setTimeout(deferred.resolve, 1500);
   return deferred.promise;
 }
--- a/browser/devtools/markupview/test/browser_inspector_markup_add_attributes.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_add_attributes.js
@@ -8,17 +8,16 @@ http://creativecommons.org/publicdomain/
  * works as expected. Also checks that the changes are properly undoable and
  * redoable. For each step in the test, we:
  * - Create a new DIV
  * - Make the change, check that the change was made as we expect
  * - Undo the change, check that the node is back in its original state
  * - Redo the change, check that the node change was made again correctly.
  */
 
-waitForExplicitFinish();
 requestLongerTimeout(2);
 
 let TEST_URL = "data:text/html,<div>markup-view attributes addition test</div>";
 let TEST_DATA = [{
   desc: "Add an attribute value without closing \"",
   enteredText: 'style="display: block;',
   expectedAttributes: {
     style: "display: block;"
@@ -129,43 +128,38 @@ let TEST_DATA = [{
   desc: "Add event handlers",
   enteredText: "onclick=\"javascript: throw new Error('wont fire');\" onload=\"alert('here');\"",
   expectedAttributes: {
     onclick: "javascript: throw new Error('wont fire');",
     onload: "alert('here');"
   }
 }];
 
-function test() {
-  Task.spawn(function() {
-    info("Opening the inspector on the test URL");
-    let args = yield addTab(TEST_URL).then(openInspector);
-    let inspector = args.inspector;
-    let markup = inspector.markup;
+let test = asyncTest(function*() {
+  info("Opening the inspector on the test URL");
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+  let markup = inspector.markup;
 
-    info("Selecting the test node");
-    let div = getNode("div");
-    yield selectNode(div, inspector);
-    let editor = getContainerForRawNode(markup, div).editor;
+  info("Selecting the test node");
+  let div = getNode("div");
+  yield selectNode(div, inspector);
+  let editor = getContainerForRawNode(div, inspector).editor;
 
-    for (let test of TEST_DATA) {
-      info("Starting test: " + test.desc);
+  for (let test of TEST_DATA) {
+    info("Starting test: " + test.desc);
 
-      info("Enter the new attribute(s) test: " + test.enteredText);
-      let nodeMutated = inspector.once("markupmutation");
-      setEditableFieldValue(editor.newAttr, test.enteredText, inspector);
-      yield nodeMutated;
+    info("Enter the new attribute(s) test: " + test.enteredText);
+    let nodeMutated = inspector.once("markupmutation");
+    setEditableFieldValue(editor.newAttr, test.enteredText, inspector);
+    yield nodeMutated;
 
-      info("Assert that the attribute(s) has/have been applied correctly");
-      assertAttributes(div, test.expectedAttributes);
+    info("Assert that the attribute(s) has/have been applied correctly");
+    assertAttributes(div, test.expectedAttributes);
 
-      info("Undo the change");
-      yield undoChange(inspector);
+    info("Undo the change");
+    yield undoChange(inspector);
 
-      info("Assert that the attribute(s) has/have been removed correctly");
-      assertAttributes(div, {});
-    }
+    info("Assert that the attribute(s) has/have been removed correctly");
+    assertAttributes(div, {});
+  }
 
-    yield inspector.once("inspector-updated");
-
-    gBrowser.removeCurrentTab();
-  }).then(null, ok.bind(null, false)).then(finish);
-}
+  yield inspector.once("inspector-updated");
+});
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit.js
@@ -1,441 +1,409 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-/**
- * Tests that various editors work as expected. Also checks
- * that the various changes are properly undoable and redoable.
- * For each step in the test, we:
- * - Run the setup for that test (if any)
- * - Check that the node we're editing is as we expect
- * - Make the change, check that the change was made as we expect
- * - Undo the change, check that the node is back in its original state
- * - Redo the change, check that the node change was made again correctly.
- *
- * This test mostly tries to verify that the editor makes changes to the
- * underlying DOM, not that the UI updates - UI updates are based on
- * underlying DOM changes, and the mutation tests should cover those cases.
- */
+// Tests that various editors work as expected. Also checks
+// that the various changes are properly undoable and redoable.
+// For each step in the test, we:
+// - Check that the node we're editing is as we expect
+// - Make the change, check that the change was made as we expect
+// - Undo the change, check that the node is back in its original state
+// - Redo the change, check that the node change was made again correctly.
+// This test mostly tries to verify that the editor makes changes to the
+// underlying DOM, not that the UI updates - UI updates are based on
+// underlying DOM changes, and the mutation tests should cover those cases.
 
-waitForExplicitFinish();
-
-let doc, inspector, markup;
-
-let TEST_URL = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_edit.html";
+let TEST_URL = TEST_URL_ROOT + "browser_inspector_markup_edit.html";
 let LONG_ATTRIBUTE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 let LONG_ATTRIBUTE_COLLAPSED = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEF\u2026UVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 let DATA_URL_INLINE_STYLE='color: red; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC");';
 let DATA_URL_INLINE_STYLE_COLLAPSED='color: red; background: url("data:image/png;base64,iVBORw0KG\u2026NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC");';
 let DATA_URL_ATTRIBUTE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC";
 let DATA_URL_ATTRIBUTE_COLLAPSED = "data:image/png;base64,iVBORw0K\u20269/AFGGFyjOXZtQAAAAAElFTkSuQmCC";
 
 let TEST_DATA = [
   {
     desc: "Change an attribute",
-    before: function() {
-      assertAttributes(doc.querySelector("#node1"), {
+    before: function(inspector) {
+      assertAttributes("#node1", {
         id: "node1",
         class: "node1"
       });
     },
-    execute: function(after) {
+    execute: function(after, inspector) {
       inspector.once("markupmutation", after);
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node1")).editor;
+      let editor = getContainerForRawNode("#node1", inspector).editor;
       let attr = editor.attrs["class"].querySelector(".editable");
       setEditableFieldValue(attr, 'class="changednode1"', inspector);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node1"), {
+    after: function(inspector) {
+      assertAttributes("#node1", {
         id: "node1",
         class: "changednode1"
       });
     }
   },
   {
     desc: 'Try changing an attribute to a quote (") - this should result ' +
           'in it being set to an empty string',
-    before: function() {
-      assertAttributes(doc.querySelector("#node22"), {
+    before: function(inspector) {
+      assertAttributes("#node22", {
         id: "node22",
         class: "unchanged"
       });
     },
-    execute: function(after) {
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node22")).editor;
+    execute: function(after, inspector) {
+      let editor = getContainerForRawNode("#node22", inspector).editor;
       let attr = editor.attrs["class"].querySelector(".editable");
       setEditableFieldValue(attr, 'class="""', inspector);
       inspector.once("markupmutation", after);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node22"), {
+    after: function(inspector) {
+      assertAttributes("#node22", {
         id: "node22",
         class: ""
       });
     }
   },
   {
     desc: "Remove an attribute",
-    before: function() {
-      assertAttributes(doc.querySelector("#node4"), {
+    before: function(inspector) {
+      assertAttributes("#node4", {
         id: "node4",
         class: "node4"
       });
     },
-    execute: function(after) {
+    execute: function(after, inspector) {
       inspector.once("markupmutation", after);
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node4")).editor;
+      let editor = getContainerForRawNode("#node4", inspector).editor;
       let attr = editor.attrs["class"].querySelector(".editable");
       setEditableFieldValue(attr, '', inspector);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node4"), {
+    after: function(inspector) {
+      assertAttributes("#node4", {
         id: "node4",
       });
     }
   },
   {
     desc: "Add an attribute by clicking the empty space after a node",
-    before: function() {
-      assertAttributes(doc.querySelector("#node14"), {
+    before: function(inspector) {
+      assertAttributes("#node14", {
         id: "node14",
       });
     },
-    execute: function(after) {
+    execute: function(after, inspector) {
       inspector.once("markupmutation", after);
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node14")).editor;
+      let editor = getContainerForRawNode("#node14", inspector).editor;
       let attr = editor.newAttr;
       setEditableFieldValue(attr, 'class="newclass" style="color:green"', inspector);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node14"), {
+    after: function(inspector) {
+      assertAttributes("#node14", {
         id: "node14",
         class: "newclass",
         style: "color:green"
       });
     }
   },
   {
     desc: 'Try add an attribute containing a quote (") attribute by ' +
           'clicking the empty space after a node - this should result ' +
           'in it being set to an empty string',
-    before: function() {
-      assertAttributes(doc.querySelector("#node23"), {
+    before: function(inspector) {
+      assertAttributes("#node23", {
         id: "node23",
       });
     },
-    execute: function(after) {
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node23")).editor;
+    execute: function(after, inspector) {
+      let editor = getContainerForRawNode("#node23", inspector).editor;
       let attr = editor.newAttr;
       setEditableFieldValue(attr, 'class="newclass" style="""', inspector);
       inspector.once("markupmutation", after);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node23"), {
+    after: function(inspector) {
+      assertAttributes("#node23", {
         id: "node23",
         class: "newclass",
         style: ""
       });
     }
   },
   {
     desc: "Try add attributes by adding to an existing attribute's entry",
-    before: function() {
-      assertAttributes(doc.querySelector("#node24"), {
+    before: function(inspector) {
+      assertAttributes("#node24", {
         id: "node24",
       });
     },
-    execute: function(after) {
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+    execute: function(after, inspector) {
+      let editor = getContainerForRawNode("#node24", inspector).editor;
       let attr = editor.attrs["id"].querySelector(".editable");
       setEditableFieldValue(attr, attr.textContent + ' class="""', inspector);
       inspector.once("markupmutation", after);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node24"), {
+    after: function(inspector) {
+      assertAttributes("#node24", {
         id: "node24",
         class: ""
       });
     }
   },
   {
     desc: "Try to add long attribute to make sure it is collapsed in attribute editor.",
-    before: function() {
-      assertAttributes(doc.querySelector("#node24"), {
+    before: function(inspector) {
+      assertAttributes("#node24", {
         id: "node24",
         class: ""
       });
     },
-    execute: function(after) {
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+    execute: function(after, inspector) {
+      let editor = getContainerForRawNode("#node24", inspector).editor;
       let attr = editor.newAttr;
       setEditableFieldValue(attr, 'data-long="'+LONG_ATTRIBUTE+'"', inspector);
       inspector.once("markupmutation", after);
     },
-    after: function() {
-
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+    after: function(inspector) {
+      let editor = getContainerForRawNode("#node24", inspector).editor;
       let visibleAttrText = editor.attrs["data-long"].querySelector(".attr-value").textContent;
       is (visibleAttrText, LONG_ATTRIBUTE_COLLAPSED)
 
-      assertAttributes(doc.querySelector("#node24"), {
+      assertAttributes("#node24", {
         id: "node24",
         class: "",
         'data-long':LONG_ATTRIBUTE
       });
     }
   },
   {
     desc: "Try to modify the collapsed long attribute, making sure it expands.",
-    before: function() {
-      assertAttributes(doc.querySelector("#node24"), {
+    before: function(inspector) {
+      assertAttributes("#node24", {
         id: "node24",
         class: "",
         'data-long': LONG_ATTRIBUTE
       });
     },
-    execute: function(after) {
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+    execute: function(after, inspector) {
+      let editor = getContainerForRawNode("#node24", inspector).editor;
       let attr = editor.attrs["data-long"].querySelector(".editable");
 
       // Check to make sure it has expanded after focus
       attr.focus();
       EventUtils.sendKey("return", inspector.panelWin);
       let input = inplaceEditor(attr).input;
       is (input.value, 'data-long="'+LONG_ATTRIBUTE+'"');
       EventUtils.sendKey("escape", inspector.panelWin);
 
       setEditableFieldValue(attr, input.value  + ' data-short="ABC"', inspector);
       inspector.once("markupmutation", after);
     },
-    after: function() {
-
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node24")).editor;
+    after: function(inspector) {
+      let editor = getContainerForRawNode("#node24", inspector).editor;
       let visibleAttrText = editor.attrs["data-long"].querySelector(".attr-value").textContent;
       is (visibleAttrText, LONG_ATTRIBUTE_COLLAPSED)
 
-      assertAttributes(doc.querySelector("#node24"), {
+      assertAttributes("#node24", {
         id: "node24",
         class: "",
         'data-long': LONG_ATTRIBUTE,
         "data-short": "ABC"
       });
     }
   },
   {
     desc: "Try to add long data URL to make sure it is collapsed in attribute editor.",
-    before: function() {
-      assertAttributes(doc.querySelector("#node-data-url"), {
+    before: function(inspector) {
+      assertAttributes("#node-data-url", {
         id: "node-data-url"
       });
     },
-    execute: function(after) {
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node-data-url")).editor;
+    execute: function(after, inspector) {
+      let editor = getContainerForRawNode("#node-data-url", inspector).editor;
       let attr = editor.newAttr;
       setEditableFieldValue(attr, 'src="'+DATA_URL_ATTRIBUTE+'"', inspector);
       inspector.once("markupmutation", after);
     },
-    after: function() {
-
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node-data-url")).editor;
+    after: function(inspector) {
+      let editor = getContainerForRawNode("#node-data-url", inspector).editor;
       let visibleAttrText = editor.attrs["src"].querySelector(".attr-value").textContent;
       is (visibleAttrText, DATA_URL_ATTRIBUTE_COLLAPSED);
 
-      let node = doc.querySelector("#node-data-url");
+      let node = getNode("#node-data-url");
       is (node.width, 16, "Image width has been set after data url src.");
       is (node.height, 16, "Image height has been set after data url src.");
 
       assertAttributes(node, {
         id: "node-data-url",
         "src": DATA_URL_ATTRIBUTE
       });
     }
   },
   {
     desc: "Try to add long data URL to make sure it is collapsed in attribute editor.",
-    before: function() {
-      assertAttributes(doc.querySelector("#node-data-url-style"), {
+    before: function(inspector) {
+      assertAttributes("#node-data-url-style", {
         id: "node-data-url-style"
       });
     },
-    execute: function(after) {
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node-data-url-style")).editor;
+    execute: function(after, inspector) {
+      let editor = getContainerForRawNode("#node-data-url-style", inspector).editor;
       let attr = editor.newAttr;
       setEditableFieldValue(attr, "style='"+DATA_URL_INLINE_STYLE+"'", inspector);
       inspector.once("markupmutation", after);
     },
-    after: function() {
-
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node-data-url-style")).editor;
+    after: function(inspector) {
+      let editor = getContainerForRawNode("#node-data-url-style", inspector).editor;
       let visibleAttrText = editor.attrs["style"].querySelector(".attr-value").textContent;
       is (visibleAttrText, DATA_URL_INLINE_STYLE_COLLAPSED)
 
-      assertAttributes(doc.querySelector("#node-data-url-style"), {
+      assertAttributes("#node-data-url-style", {
         id: "node-data-url-style",
         'style': DATA_URL_INLINE_STYLE
       });
     }
   },
   {
     desc: "Edit text",
-    before: function() {
-      let node = doc.querySelector('.node6').firstChild;
+    before: function(inspector) {
+      let node = getNode('.node6').firstChild;
       is(node.nodeValue, "line6", "Text should be unchanged");
     },
-    execute: function(after) {
+    execute: function(after, inspector) {
       inspector.once("markupmutation", after);
-      let node = doc.querySelector('.node6').firstChild;
-      let editor = getContainerForRawNode(markup, node).editor;
+      let node = getNode('.node6').firstChild;
+      let editor = getContainerForRawNode(node, inspector).editor;
       let field = editor.elt.querySelector("pre");
       setEditableFieldValue(field, "New text", inspector);
     },
-    after: function() {
-      let node = doc.querySelector('.node6').firstChild;
+    after: function(inspector) {
+      let node = getNode('.node6').firstChild;
       is(node.nodeValue, "New text", "Text should be changed.");
     },
   },
   {
     desc: "Add an attribute value containing < > &uuml; \" & '",
-    before: function() {
-      assertAttributes(doc.querySelector("#node25"), {
+    before: function(inspector) {
+      assertAttributes("#node25", {
         id: "node25",
       });
     },
-    execute: function(after) {
+    execute: function(after, inspector) {
       inspector.once("markupmutation", after);
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node25")).editor;
+      let editor = getContainerForRawNode("#node25", inspector).editor;
       let attr = editor.newAttr;
       setEditableFieldValue(attr, 'src="somefile.html?param1=<a>&param2=&uuml;&param3=\'&quot;\'"', inspector);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node25"), {
+    after: function(inspector) {
+      assertAttributes("#node25", {
         id: "node25",
         src: "somefile.html?param1=<a>&param2=\xfc&param3='\"'"
       });
     }
   },
   {
     desc: "Modify inline style containing \"",
-    before: function() {
-      assertAttributes(doc.querySelector("#node26"), {
+    before: function(inspector) {
+      assertAttributes("#node26", {
         id: "node26",
         style: 'background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F");'
       });
     },
-    execute: function(after) {
+    execute: function(after, inspector) {
       inspector.once("markupmutation", after);
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node26")).editor;
+      let editor = getContainerForRawNode("#node26", inspector).editor;
       let attr = editor.attrs["style"].querySelector(".editable");
 
-
       attr.focus();
       EventUtils.sendKey("return", inspector.panelWin);
 
       let input = inplaceEditor(attr).input;
       let value = input.value;
 
       is (value,
         "style='background-image: url(\"moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F\");'",
         "Value contains actual double quotes"
       );
 
       value = value.replace(/mozilla\.org/, "mozilla.com");
       input.value = value;
 
       EventUtils.sendKey("return", inspector.panelWin);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node26"), {
+    after: function(inspector) {
+      assertAttributes("#node26", {
         id: "node26",
         style: 'background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.com%2F");'
       });
     }
   },
   {
     desc: "Modify inline style containing \" and \'",
-    before: function() {
-      assertAttributes(doc.querySelector("#node27"), {
+    before: function(inspector) {
+      assertAttributes("#node27", {
         id: "node27",
         class: 'Double " and single \''
       });
     },
-    execute: function(after) {
+    execute: function(after, inspector) {
       inspector.once("markupmutation", after);
-      let editor = getContainerForRawNode(markup, doc.querySelector("#node27")).editor;
+      let editor = getContainerForRawNode("#node27", inspector).editor;
       let attr = editor.attrs["class"].querySelector(".editable");
 
       attr.focus();
       EventUtils.sendKey("return", inspector.panelWin);
 
       let input = inplaceEditor(attr).input;
       let value = input.value;
 
       is (value, "class=\"Double &quot; and single '\"", "Value contains &quot;");
 
       value = value.replace(/Double/, "&quot;").replace(/single/, "'");
       input.value = value;
 
       EventUtils.sendKey("return", inspector.panelWin);
     },
-    after: function() {
-      assertAttributes(doc.querySelector("#node27"), {
+    after: function(inspector) {
+      assertAttributes("#node27", {
         id: "node27",
         class: '" " and \' \''
       });
     }
   }
 ];
 
-function test() {
-  addTab(TEST_URL).then(openInspector).then(args => {
-    inspector = args.inspector;
-    doc = content.document;
-    markup = inspector.markup;
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
 
-    markup.expandAll().then(() => {
-      // Iterate through the items in TEST_DATA
-      let cursor = 0;
+  info("Expanding all nodes in the markup-view");
+  yield inspector.markup.expandAll();
 
-      function nextEditTest() {
-        executeSoon(function() {
-          if (cursor >= TEST_DATA.length) {
-            return finishUp();
-          }
+  for (let step of TEST_DATA) {
+    yield executeStep(step, inspector);
+  }
+  yield inspector.once("inspector-updated");
+});
 
-          let step = TEST_DATA[cursor++];
-          info("Start test for: " + step.desc);
-          if (step.setup) {
-            step.setup();
-          }
-          step.before();
-          step.execute(function() {
-            step.after();
+function executeStep(step, inspector) {
+  let def = promise.defer();
 
-            undoChange(inspector).then(() => {
-              step.before();
+  info("Start test for: " + step.desc);
+  step.before(inspector);
+  step.execute(function() {
+    step.after(inspector);
 
-              redoChange(inspector).then(() => {
-                step.after();
-                info("End test for: " + step.desc);
-                nextEditTest();
-              });
-            });
-          });
-        });
-      }
+    undoChange(inspector).then(() => {
+      step.before(inspector);
 
-      nextEditTest();
+      redoChange(inspector).then(() => {
+        step.after(inspector);
+        info("End test for: " + step.desc);
+        def.resolve();
+      });
     });
-  });
-}
+  }, inspector);
 
-function finishUp() {
-  while (markup.undo.canUndo()) {
-    markup.undo.undo();
-  }
-  inspector.once("inspector-updated", () => {
-    doc = inspector = markup = null;
-    gBrowser.removeCurrentTab();
-    finish();
-  });
+  return def.promise;
 }
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit_2.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit_2.js
@@ -1,51 +1,46 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that an existing attribute can be modified
 
-waitForExplicitFinish();
-
 const TEST_URL = "data:text/html,<div id='test-div'>Test modifying my ID attribute</div>";
 
-function test() {
-  Task.spawn(function() {
-    info("Opening the inspector on the test page");
-    let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
+let test = asyncTest(function*() {
+  info("Opening the inspector on the test page");
+  let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
 
-    info("Selecting the test node");
-    let node = content.document.getElementById("test-div");
-    yield selectNode(node, inspector);
+  info("Selecting the test node");
+  let node = content.document.getElementById("test-div");
+  yield selectNode(node, inspector);
 
-    info("Verify attributes, only ID should be there for now");
-    assertAttributes(node, {
-      id: "test-div"
-    });
+  info("Verify attributes, only ID should be there for now");
+  assertAttributes(node, {
+    id: "test-div"
+  });
 
-    info("Focus the ID attribute and change its content");
-    let editor = getContainerForRawNode(inspector.markup, node).editor;
-    let attr = editor.attrs["id"].querySelector(".editable");
-    let mutated = inspector.once("markupmutation");
-    setEditableFieldValue(attr,
-      attr.textContent + ' class="newclass" style="color:green"', inspector);
-    yield mutated;
+  info("Focus the ID attribute and change its content");
+  let editor = getContainerForRawNode(node, inspector).editor;
+  let attr = editor.attrs["id"].querySelector(".editable");
+  let mutated = inspector.once("markupmutation");
+  setEditableFieldValue(attr,
+    attr.textContent + ' class="newclass" style="color:green"', inspector);
+  yield mutated;
 
-    info("Verify attributes, should have ID, class and style");
-    assertAttributes(node, {
-      id: "test-div",
-      class: "newclass",
-      style: "color:green"
-    });
+  info("Verify attributes, should have ID, class and style");
+  assertAttributes(node, {
+    id: "test-div",
+    class: "newclass",
+    style: "color:green"
+  });
 
-    info("Trying to undo the change");
-    yield undoChange(inspector);
-    assertAttributes(node, {
-      id: "test-div"
-    });
+  info("Trying to undo the change");
+  yield undoChange(inspector);
+  assertAttributes(node, {
+    id: "test-div"
+  });
 
-    yield inspector.once("inspector-updated");
-
-    gBrowser.removeCurrentTab();
-  }).then(null, ok.bind(null, false)).then(finish);
-}
+  yield inspector.once("inspector-updated");
+});
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit_3.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit_3.js
@@ -1,45 +1,39 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that a node's tagname can be edited in the markup-view
 
-waitForExplicitFinish();
-
 const TEST_URL = "data:text/html,<div id='retag-me'><div id='retag-me-2'></div></div>";
 
-function test() {
-  Task.spawn(function() {
-    info("Opening the inspector on the test page");
-    let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
+let test = asyncTest(function*() {
+  let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
 
-    yield inspector.markup.expandAll();
+  yield inspector.markup.expandAll();
 
-    info("Selecting the test node");
-    let node = content.document.getElementById("retag-me");
-    let child = content.document.querySelector("#retag-me-2");
-    yield selectNode(node, inspector);
+  info("Selecting the test node");
+  let node = content.document.getElementById("retag-me");
+  let child = content.document.querySelector("#retag-me-2");
+  yield selectNode(node, inspector);
 
-    let container = getContainerForRawNode(inspector.markup, node);
-    is(node.tagName, "DIV", "We've got #retag-me element, it's a DIV");
-    ok(container.expanded, "It is expanded");
-    is(child.parentNode, node, "Child #retag-me-2 is inside #retag-me");
+  let container = getContainerForRawNode(node, inspector);
+  is(node.tagName, "DIV", "We've got #retag-me element, it's a DIV");
+  ok(container.expanded, "It is expanded");
+  is(child.parentNode, node, "Child #retag-me-2 is inside #retag-me");
 
-    info("Changing the tagname");
-    let mutated = inspector.once("markupmutation");
-    let tagEditor = container.editor.tag;
-    setEditableFieldValue(tagEditor, "p", inspector);
-    yield mutated;
+  info("Changing the tagname");
+  let mutated = inspector.once("markupmutation");
+  let tagEditor = container.editor.tag;
+  setEditableFieldValue(tagEditor, "p", inspector);
+  yield mutated;
 
-    info("Checking that the tagname change was done");
-    let node = content.document.getElementById("retag-me");
-    let container = getContainerForRawNode(inspector.markup, node);
-    is(node.tagName, "P", "We've got #retag-me, it should now be a P");
-    ok(container.expanded, "It is still expanded");
-    ok(container.selected, "It is still selected");
-    is(child.parentNode, node, "Child #retag-me-2 is still inside #retag-me");
-
-    gBrowser.removeCurrentTab();
-  }).then(null, ok.bind(null, false)).then(finish);
-}
+  info("Checking that the tagname change was done");
+  let node = content.document.getElementById("retag-me");
+  let container = getContainerForRawNode(node, inspector);
+  is(node.tagName, "P", "We've got #retag-me, it should now be a P");
+  ok(container.expanded, "It is still expanded");
+  ok(container.selected, "It is still selected");
+  is(child.parentNode, node, "Child #retag-me-2 is still inside #retag-me");
+});
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit_4.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit_4.js
@@ -1,36 +1,30 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that a node can be deleted from the markup-view with the delete key
 
-waitForExplicitFinish();
-
 const TEST_URL = "data:text/html,<div id='delete-me'></div>";
 
-function test() {
-  Task.spawn(function() {
-    info("Opening the inspector on the test page");
-    let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
+let test = asyncTest(function*() {
+  let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
 
-    info("Selecting the test node by clicking on it to make sure it receives focus");
-    let node = content.document.getElementById("delete-me");
-    yield clickContainer(node, inspector);
+  info("Selecting the test node by clicking on it to make sure it receives focus");
+  let node = content.document.getElementById("delete-me");
+  yield clickContainer(node, inspector);
 
-    info("Deleting the element with the keyboard");
-    let mutated = inspector.once("markupmutation");
-    EventUtils.sendKey("delete", inspector.panelWin);
-    yield mutated;
-
-    info("Checking that it's gone, baby gone!");
-    ok(!content.document.getElementById("delete-me"), "The test node does not exist");
+  info("Deleting the element with the keyboard");
+  let mutated = inspector.once("markupmutation");
+  EventUtils.sendKey("delete", inspector.panelWin);
+  yield mutated;
 
-    yield undoChange(inspector);
-    ok(content.document.getElementById("delete-me"), "The test node is back!");
+  info("Checking that it's gone, baby gone!");
+  ok(!content.document.getElementById("delete-me"), "The test node does not exist");
 
-    yield inspector.once("inspector-updated");
+  yield undoChange(inspector);
+  ok(content.document.getElementById("delete-me"), "The test node is back!");
 
-    gBrowser.removeCurrentTab();
-  }).then(null, ok.bind(null, false)).then(finish);
-}
+  yield inspector.once("inspector-updated");
+});
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit_outerhtml.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit_outerhtml.js
@@ -1,232 +1,199 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
-
-
-function test() {
-  let inspector;
-  let doc;
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(setupTest, content);
-  }, true);
+// Test outerHTML edition via the markup-view
 
-  let outerHTMLs = [
-    {
-      selector: "#one",
-      oldHTML: '<div id="one">First <em>Div</em></div>',
-      newHTML: '<div id="one">First Div</div>',
-      validate: function(pageNode, selectedNode) {
-        is (pageNode.textContent, "First Div", "New div has expected text content");
-        ok (!doc.querySelector("#one em"), "No em remaining")
-      }
-    },
-    {
-      selector: "#removedChildren",
-      oldHTML: '<div id="removedChildren">removedChild <i>Italic <b>Bold <u>Underline</u></b></i> Normal</div>',
-      newHTML: '<div id="removedChildren">removedChild</div>'
-    },
-    {
-      selector: "#addedChildren",
-      oldHTML: '<div id="addedChildren">addedChildren</div>',
-      newHTML: '<div id="addedChildren">addedChildren <i>Italic <b>Bold <u>Underline</u></b></i> Normal</div>'
-    },
-    {
-      selector: "#addedAttribute",
-      oldHTML: '<div id="addedAttribute">addedAttribute</div>',
-      newHTML: '<div id="addedAttribute" class="important" disabled checked>addedAttribute</div>',
-      validate: function(pageNode, selectedNode) {
-        is (pageNode, selectedNode, "Original element is selected");
-        is (pageNode.outerHTML, '<div id="addedAttribute" class="important" disabled="" checked="">addedAttribute</div>',
-              "Attributes have been added");
-      }
-    },
-    {
-      selector: "#changedTag",
-      oldHTML: '<div id="changedTag">changedTag</div>',
-      newHTML: '<p id="changedTag" class="important">changedTag</p>'
-    },
-    {
-      selector: "#badMarkup1",
-      oldHTML: '<div id="badMarkup1">badMarkup1</div>',
-      newHTML: '<div id="badMarkup1">badMarkup1</div> hanging</div>',
-      validate: function(pageNode, selectedNode) {
-        is (pageNode, selectedNode, "Original element is selected");
-
-        let textNode = pageNode.nextSibling;
+const TEST_DATA = [
+  {
+    selector: "#one",
+    oldHTML: '<div id="one">First <em>Div</em></div>',
+    newHTML: '<div id="one">First Div</div>',
+    validate: function(pageNode, selectedNode) {
+      is(pageNode.textContent, "First Div", "New div has expected text content");
+      ok(!getNode("#one em"), "No em remaining")
+    }
+  },
+  {
+    selector: "#removedChildren",
+    oldHTML: '<div id="removedChildren">removedChild <i>Italic <b>Bold <u>Underline</u></b></i> Normal</div>',
+    newHTML: '<div id="removedChildren">removedChild</div>'
+  },
+  {
+    selector: "#addedChildren",
+    oldHTML: '<div id="addedChildren">addedChildren</div>',
+    newHTML: '<div id="addedChildren">addedChildren <i>Italic <b>Bold <u>Underline</u></b></i> Normal</div>'
+  },
+  {
+    selector: "#addedAttribute",
+    oldHTML: '<div id="addedAttribute">addedAttribute</div>',
+    newHTML: '<div id="addedAttribute" class="important" disabled checked>addedAttribute</div>',
+    validate: function(pageNode, selectedNode) {
+      is(pageNode, selectedNode, "Original element is selected");
+      is(pageNode.outerHTML, '<div id="addedAttribute" class="important" disabled="" checked="">addedAttribute</div>',
+            "Attributes have been added");
+    }
+  },
+  {
+    selector: "#changedTag",
+    oldHTML: '<div id="changedTag">changedTag</div>',
+    newHTML: '<p id="changedTag" class="important">changedTag</p>'
+  },
+  {
+    selector: "#badMarkup1",
+    oldHTML: '<div id="badMarkup1">badMarkup1</div>',
+    newHTML: '<div id="badMarkup1">badMarkup1</div> hanging</div>',
+    validate: function(pageNode, selectedNode) {
+      is(pageNode, selectedNode, "Original element is selected");
 
-        is (textNode.nodeName, "#text", "Sibling is a text element");
-        is (textNode.data, " hanging", "New text node has expected text content");
-      }
-    },
-    {
-      selector: "#badMarkup2",
-      oldHTML: '<div id="badMarkup2">badMarkup2</div>',
-      newHTML: '<div id="badMarkup2">badMarkup2</div> hanging<div></div></div></div></body>',
-      validate: function(pageNode, selectedNode) {
-        is (pageNode, selectedNode, "Original element is selected");
-
-        let textNode = pageNode.nextSibling;
+      let textNode = pageNode.nextSibling;
 
-        is (textNode.nodeName, "#text", "Sibling is a text element");
-        is (textNode.data, " hanging", "New text node has expected text content");
-      }
-    },
-    {
-      selector: "#badMarkup3",
-      oldHTML: '<div id="badMarkup3">badMarkup3</div>',
-      newHTML: '<div id="badMarkup3">badMarkup3 <em>Emphasized <strong> and strong</div>',
-      validate: function(pageNode, selectedNode) {
-        is (pageNode, selectedNode, "Original element is selected");
+      is(textNode.nodeName, "#text", "Sibling is a text element");
+      is(textNode.data, " hanging", "New text node has expected text content");
+    }
+  },
+  {
+    selector: "#badMarkup2",
+    oldHTML: '<div id="badMarkup2">badMarkup2</div>',
+    newHTML: '<div id="badMarkup2">badMarkup2</div> hanging<div></div></div></div></body>',
+    validate: function(pageNode, selectedNode) {
+      is(pageNode, selectedNode, "Original element is selected");
 
-        let em = doc.querySelector("#badMarkup3 em");
-        let strong = doc.querySelector("#badMarkup3 strong");
+      let textNode = pageNode.nextSibling;
 
-        is (em.textContent, "Emphasized  and strong", "<em> was auto created");
-        is (strong.textContent, " and strong", "<strong> was auto created");
-      }
-    },
-    {
-      selector: "#badMarkup4",
-      oldHTML: '<div id="badMarkup4">badMarkup4</div>',
-      newHTML: '<div id="badMarkup4">badMarkup4</p>',
-      validate: function(pageNode, selectedNode) {
-        is (pageNode, selectedNode, "Original element is selected");
+      is(textNode.nodeName, "#text", "Sibling is a text element");
+      is(textNode.data, " hanging", "New text node has expected text content");
+    }
+  },
+  {
+    selector: "#badMarkup3",
+    oldHTML: '<div id="badMarkup3">badMarkup3</div>',
+    newHTML: '<div id="badMarkup3">badMarkup3 <em>Emphasized <strong> and strong</div>',
+    validate: function(pageNode, selectedNode) {
+      is(pageNode, selectedNode, "Original element is selected");
+
+      let em = getNode("#badMarkup3 em");
+      let strong = getNode("#badMarkup3 strong");
 
-        let div = doc.querySelector("#badMarkup4");
-        let p = doc.querySelector("#badMarkup4 p");
+      is(em.textContent, "Emphasized  and strong", "<em> was auto created");
+      is(strong.textContent, " and strong", "<strong> was auto created");
+    }
+  },
+  {
+    selector: "#badMarkup4",
+    oldHTML: '<div id="badMarkup4">badMarkup4</div>',
+    newHTML: '<div id="badMarkup4">badMarkup4</p>',
+    validate: function(pageNode, selectedNode) {
+      is(pageNode, selectedNode, "Original element is selected");
 
-        is (div.textContent, "badMarkup4", "textContent is correct");
-        is (div.tagName, "DIV", "did not change to <p> tag");
-        is (p.textContent, "", "The <p> tag has no children");
-        is (p.tagName, "P", "Created an empty <p> tag");
-      }
-    },
-    {
-      selector: "#badMarkup5",
-      oldHTML: '<p id="badMarkup5">badMarkup5</p>',
-      newHTML: '<p id="badMarkup5">badMarkup5 <div>with a nested div</div></p>',
-      validate: function(pageNode, selectedNode) {
-        is (pageNode, selectedNode, "Original element is selected");
+      let div = getNode("#badMarkup4");
+      let p = getNode("#badMarkup4 p");
 
-        let p = doc.querySelector("#badMarkup5");
-        let nodiv = doc.querySelector("#badMarkup5 div");
-        let div = doc.querySelector("#badMarkup5 ~ div");
+      is(div.textContent, "badMarkup4", "textContent is correct");
+      is(div.tagName, "DIV", "did not change to <p> tag");
+      is(p.textContent, "", "The <p> tag has no children");
+      is(p.tagName, "P", "Created an empty <p> tag");
+    }
+  },
+  {
+    selector: "#badMarkup5",
+    oldHTML: '<p id="badMarkup5">badMarkup5</p>',
+    newHTML: '<p id="badMarkup5">badMarkup5 <div>with a nested div</div></p>',
+    validate: function(pageNode, selectedNode) {
+      is(pageNode, selectedNode, "Original element is selected");
 
-        ok (!nodiv, "The invalid markup got created as a sibling");
-        is (p.textContent, "badMarkup5 ", "The <p> tag does not take in the <div> content");
-        is (p.tagName, "P", "Did not change to a <div> tag");
-        is (div.textContent, "with a nested div", "textContent is correct");
-        is (div.tagName, "DIV", "Did not change to <p> tag");
-      }
-    },
-    {
-      selector: "#siblings",
-      oldHTML: '<div id="siblings">siblings</div>',
-      newHTML: '<div id="siblings-before-sibling">before sibling</div>' +
-               '<div id="siblings">siblings (updated)</div>' +
-               '<div id="siblings-after-sibling">after sibling</div>',
-      validate: function(pageNode, selectedNode) {
-        let beforeSiblingNode = doc.querySelector("#siblings-before-sibling");
-        let afterSiblingNode = doc.querySelector("#siblings-after-sibling");
+      let p = getNode("#badMarkup5");
+      let nodiv = getNode("#badMarkup5 div");
+      let div = getNode("#badMarkup5 ~ div");
 
-        is (beforeSiblingNode, selectedNode, "Sibling has been selected");
-        is (pageNode.textContent, "siblings (updated)", "New div has expected text content");
-        is (beforeSiblingNode.textContent, "before sibling", "Sibling has been inserted");
-        is (afterSiblingNode.textContent, "after sibling", "Sibling has been inserted");
-      }
+      ok(!nodiv, "The invalid markup got created as a sibling");
+      is(p.textContent, "badMarkup5 ", "The <p> tag does not take in the <div> content");
+      is(p.tagName, "P", "Did not change to a <div> tag");
+      is(div.textContent, "with a nested div", "textContent is correct");
+      is(div.tagName, "DIV", "Did not change to <p> tag");
     }
-  ];
-  content.location = "data:text/html," +
-    "<!DOCTYPE html>" +
-    "<head><meta charset='utf-8' /></head>" +
-    "<body>" +
-    [outer.oldHTML for (outer of outerHTMLs) ].join("\n") +
-    "</body>" +
-    "</html>";
+  },
+  {
+    selector: "#siblings",
+    oldHTML: '<div id="siblings">siblings</div>',
+    newHTML: '<div id="siblings-before-sibling">before sibling</div>' +
+             '<div id="siblings">siblings (updated)</div>' +
+             '<div id="siblings-after-sibling">after sibling</div>',
+    validate: function(pageNode, selectedNode) {
+      let beforeSiblingNode = getNode("#siblings-before-sibling");
+      let afterSiblingNode = getNode("#siblings-after-sibling");
 
-  function setupTest() {
-    var target = TargetFactory.forTab(gBrowser.selectedTab);
-    gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      inspector.once("inspector-updated", startTests);
-    });
+      is(beforeSiblingNode, selectedNode, "Sibling has been selected");
+      is(pageNode.textContent, "siblings (updated)", "New div has expected text content");
+      is(beforeSiblingNode.textContent, "before sibling", "Sibling has been inserted");
+      is(afterSiblingNode.textContent, "after sibling", "Sibling has been inserted");
+    }
   }
+];
 
-  function startTests() {
-    inspector.markup._frame.focus();
-    nextStep(0);
-  }
+const TEST_URL = "data:text/html," +
+  "<!DOCTYPE html>" +
+  "<head><meta charset='utf-8' /></head>" +
+  "<body>" +
+  [outer.oldHTML for (outer of TEST_DATA)].join("\n") +
+  "</body>" +
+  "</html>";
 
-  function nextStep(cursor) {
-    if (cursor >= outerHTMLs.length) {
-      finishUp();
-      return;
-    }
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
 
-    let currentTestData = outerHTMLs[cursor];
-    let selector = currentTestData.selector;
-    let oldHTML = currentTestData.oldHTML;
-    let newHTML = currentTestData.newHTML;
-    let rawNode = doc.querySelector(selector);
+  inspector.markup._frame.focus();
 
-    inspector.selection.once("new-node", () => {
-
-      let oldNodeFront = inspector.selection.nodeFront;
+  for (let step of TEST_DATA) {
+    yield selectNode(step.selector, inspector);
+    yield executeStep(step, inspector);
+  }
+});
 
-      // markupmutation fires once the outerHTML is set, with a target
-      // as the parent node and a type of "childList".
-      inspector.once("markupmutation", (e, aMutations) => {
+function executeStep(step, inspector) {
+  let rawNode = getNode(step.selector);
+  let oldNodeFront = inspector.selection.nodeFront;
 
-        // Check to make the sure the correct mutation has fired, and that the
-        // parent is selected (this will be reset to the child once the mutation is complete.
-        let node = inspector.selection.node;
-        let nodeFront = inspector.selection.nodeFront;
-        let mutation = aMutations[0];
-        let isFromOuterHTML = mutation.removed.some((n) => {
-          return n === oldNodeFront;
-        });
+  // markupmutation fires once the outerHTML is set, with a target
+  // as the parent node and a type of "childList".
+  return editHTML(step, inspector).then(mutations => {
+    // Check to make the sure the correct mutation has fired, and that the
+    // parent is selected (this will be reset to the child once the mutation is complete.
+    let node = inspector.selection.node;
+    let nodeFront = inspector.selection.nodeFront;
+    let mutation = mutations[0];
+    let isFromOuterHTML = mutation.removed.some(n => n === oldNodeFront);
 
-        ok (isFromOuterHTML, "The node is in the 'removed' list of the mutation");
-        is (mutation.type, "childList", "Mutation is a childList after updating outerHTML");
-        is (mutation.target, nodeFront, "Parent node is selected immediately after setting outerHTML");
-
-        // Wait for node to be reselected after outerHTML has been set
-        inspector.selection.once("new-node", () => {
+    ok(isFromOuterHTML, "The node is in the 'removed' list of the mutation");
+    is(mutation.type, "childList", "Mutation is a childList after updating outerHTML");
+    is(mutation.target, nodeFront, "Parent node is selected immediately after setting outerHTML");
 
-          // Typically selectedNode will === pageNode, but if a new element has been injected in front
-          // of it, this will not be the case.  If this happens.
-          let selectedNode = inspector.selection.node;
-          let nodeFront = inspector.selection.nodeFront;
-          let pageNode = doc.querySelector(selector);
+    // Wait for node to be reselected after outerHTML has been set
+    return inspector.selection.once("new-node").then(() => {
+      // Typically selectedNode will === pageNode, but if a new element has been injected in front
+      // of it, this will not be the case.  If this happens.
+      let selectedNode = inspector.selection.node;
+      let nodeFront = inspector.selection.nodeFront;
+      let pageNode = getNode(step.selector);
 
-          if (currentTestData.validate) {
-            currentTestData.validate(pageNode, selectedNode);
-          } else {
-            is (pageNode, selectedNode, "Original node (grabbed by selector) is selected");
-            is (pageNode.outerHTML, newHTML, "Outer HTML has been updated");
-          }
-
-          nextStep(cursor + 1);
-        });
+      if (step.validate) {
+        step.validate(pageNode, selectedNode);
+      } else {
+        is(pageNode, selectedNode, "Original node (grabbed by selector) is selected");
+        is(pageNode.outerHTML, step.newHTML, "Outer HTML has been updated");
+      }
 
-      });
-
-      is (inspector.selection.node, rawNode, "Selection is on the correct node");
-      inspector.markup.updateNodeOuterHTML(inspector.selection.nodeFront, newHTML, oldHTML);
+      // Wait for the inspector to be fully updated to avoid causing errors by
+      // abruptly closing hanging requests when the test ends
+      return inspector.once("inspector-updated");
     });
+  });
+}
 
-    inspector.selection.setNode(rawNode);
-  }
-
-
-  function finishUp() {
-    doc = inspector = null;
-    gBrowser.removeCurrentTab();
-    finish();
-  }
+function editHTML({oldHTML, newHTML}, inspector) {
+  // markupmutation fires once the outerHTML is set, with a target
+  // as the parent node and a type of "childList".
+  let mutated = inspector.once("markupmutation");
+  inspector.markup.updateNodeOuterHTML(inspector.selection.nodeFront, newHTML, oldHTML);
+  return mutated;
 }
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit_outerhtml2.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit_outerhtml2.js
@@ -1,170 +1,178 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function test() {
-  let inspector;
-  let doc;
-  let selector = "#keyboard";
-  let oldHTML = '<div id="keyboard"></div>';
-  let newHTML = '<div id="keyboard">Edited</div>';
-
-  waitForExplicitFinish();
+// Test that outerHTML editing keybindings work as expected and that *special*
+// elements like <html>, <body> and <head> can be edited correctly.
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(setupTest, content);
-  }, true);
+const TEST_URL = "data:text/html," +
+  "<!DOCTYPE html>" +
+  "<head><meta charset='utf-8' /></head>" +
+  "<body>" +
+  "<div id=\"keyboard\"></div>" +
+  "</body>" +
+  "</html>";
+const SELECTOR = "#keyboard";
+const OLD_HTML = '<div id="keyboard"></div>';
+const NEW_HTML = '<div id="keyboard">Edited</div>';
 
-  content.location = "data:text/html," +
-    "<!DOCTYPE html>" +
-    "<head><meta charset='utf-8' /></head>" +
-    "<body>" +
-    "<div id=\"keyboard\"></div>" +
-    "</body>" +
-    "</html>";
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
 
-  function setupTest() {
-    var target = TargetFactory.forTab(gBrowser.selectedTab);
-    gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      inspector.once("inspector-updated", startTests);
-    });
-  }
+  inspector.markup._frame.focus();
 
-  function startTests() {
-    inspector.markup._frame.focus();
-    testEscapeCancels();
-  }
+  info("Checking that pressing escape cancels edits");
+  yield testEscapeCancels(inspector);
 
-  function testEscapeCancels() {
-    info("Checking to make sure that pressing escape cancels edits");
-    let rawNode = doc.querySelector(selector);
+  info("Checking that pressing F2 commits edits");
+  yield testF2Commits(inspector);
 
-    inspector.selection.once("new-node", () => {
-
-      inspector.markup.htmlEditor.on("popupshown", function onPopupShown() {
-        inspector.markup.htmlEditor.off("popupshown", onPopupShown);
+  info("Checking that editing the <body> element works like other nodes");
+  yield testBody(inspector);
 
-        ok (inspector.markup.htmlEditor._visible, "HTML Editor is visible");
-        is (rawNode.outerHTML, oldHTML, "The node is starting with old HTML.");
-
-        inspector.markup.htmlEditor.on("popuphidden", function onPopupHidden() {
-          inspector.markup.htmlEditor.off("popuphidden", onPopupHidden);
-          ok (!inspector.markup.htmlEditor._visible, "HTML Editor is not visible");
+  info("Checking that editing the <head> element works like other nodes");
+  yield testHead(inspector);
 
-          let rawNode = doc.querySelector(selector);
-          is (rawNode.outerHTML, oldHTML, "Escape cancels edits");
-          testF2Commits();
-        });
-
-        inspector.markup.htmlEditor.editor.setText(newHTML);
-
-        EventUtils.sendKey("ESCAPE", inspector.markup.htmlEditor.doc.defaultView);
-      });
+  info("Checking that editing the <html> element works like other nodes");
+  yield testDocumentElement(inspector);
 
-      EventUtils.sendKey("F2", inspector.markup._frame.contentWindow);
-    });
+  info("Checking (again) that editing the <html> element works like other nodes");
+  yield testDocumentElement2(inspector);
+});
 
-    inspector.selection.setNode(rawNode);
-  }
+function testEscapeCancels(inspector) {
+  let def = promise.defer();
+  let node = getNode(SELECTOR);
 
-  function testF2Commits() {
-    info("Checking to make sure that pressing F2 commits edits");
-    let rawNode = doc.querySelector(selector);
-
+  selectNode(node, inspector).then(() => {
     inspector.markup.htmlEditor.on("popupshown", function onPopupShown() {
       inspector.markup.htmlEditor.off("popupshown", onPopupShown);
 
-      ok (inspector.markup.htmlEditor._visible, "HTML Editor is visible");
-      is (rawNode.outerHTML, oldHTML, "The node is starting with old HTML.");
+      ok(inspector.markup.htmlEditor._visible, "HTML Editor is visible");
+      is(node.outerHTML, OLD_HTML, "The node is starting with old HTML.");
 
-      inspector.once("markupmutation", (e, aMutations) => {
-        ok (!inspector.markup.htmlEditor._visible, "HTML Editor is not visible");
+      inspector.markup.htmlEditor.on("popuphidden", function onPopupHidden() {
+        inspector.markup.htmlEditor.off("popuphidden", onPopupHidden);
+        ok(!inspector.markup.htmlEditor._visible, "HTML Editor is not visible");
 
-        let rawNode = doc.querySelector(selector);
-        is (rawNode.outerHTML, newHTML, "F2 commits edits - the node has new HTML.");
-        testBody();
+        let node = getNode(SELECTOR);
+        is(node.outerHTML, OLD_HTML, "Escape cancels edits");
+        def.resolve();
       });
 
-      inspector.markup.htmlEditor.editor.setText(newHTML);
-      EventUtils.sendKey("F2", inspector.markup._frame.contentWindow);
+      inspector.markup.htmlEditor.editor.setText(NEW_HTML);
+
+      EventUtils.sendKey("ESCAPE", inspector.markup.htmlEditor.doc.defaultView);
+    });
+
+    EventUtils.sendKey("F2", inspector.markup._frame.contentWindow);
+  });
+
+  return def.promise;
+}
+
+function testF2Commits(inspector) {
+  let def = promise.defer();
+  let node = getNode(SELECTOR);
+
+  inspector.markup.htmlEditor.on("popupshown", function onPopupShown() {
+    inspector.markup.htmlEditor.off("popupshown", onPopupShown);
+
+    ok(inspector.markup.htmlEditor._visible, "HTML Editor is visible");
+    is(node.outerHTML, OLD_HTML, "The node is starting with old HTML.");
+
+    inspector.once("markupmutation", (e, aMutations) => {
+      ok(!inspector.markup.htmlEditor._visible, "HTML Editor is not visible");
+
+      let node = getNode(SELECTOR);
+      is(node.outerHTML, NEW_HTML, "F2 commits edits - the node has new HTML.");
+      def.resolve();
     });
 
-    inspector.markup._frame.contentDocument.documentElement.focus();
+    inspector.markup.htmlEditor.editor.setText(NEW_HTML);
     EventUtils.sendKey("F2", inspector.markup._frame.contentWindow);
-  }
+  });
+
+  inspector.markup._frame.contentDocument.documentElement.focus();
+  EventUtils.sendKey("F2", inspector.markup._frame.contentWindow);
+
+  return def.promise;
+}
+
+function testBody(inspector) {
+  let body = getNode("body");
+  let bodyHTML = '<body id="updated"><p></p></body>';
+  let bodyFront = inspector.markup.walker.frontForRawNode(body);
+  let doc = content.document;
+
+  let mutated = inspector.once("markupmutation");
+  inspector.markup.updateNodeOuterHTML(bodyFront, bodyHTML, body.outerHTML);
 
-  function testBody() {
-    info("Checking to make sure that editing the <body> element works like other nodes");
-    let body = doc.querySelector("body");
-    let bodyHTML = '<body id="updated"><p></p></body>';
-    let bodyFront = inspector.markup.walker.frontForRawNode(body);
-    inspector.once("markupmutation", (e, aMutations) => {
-      is (doc.querySelector("body").outerHTML, bodyHTML, "<body> HTML has been updated");
-      is (doc.querySelectorAll("head").length, 1, "no extra <head>s have been added");
-      testHead();
-    });
-    inspector.markup.updateNodeOuterHTML(bodyFront, bodyHTML, body.outerHTML);
-  }
+  return mutated.then(mutations => {
+    is(getNode("body").outerHTML, bodyHTML, "<body> HTML has been updated");
+    is(doc.querySelectorAll("head").length, 1, "no extra <head>s have been added");
+    return inspector.once("inspector-updated");
+  });
+}
 
-  function testHead() {
-    info("Checking to make sure that editing the <head> element works like other nodes");
-    let head = doc.querySelector("head");
-    let headHTML = '<head id="updated"><title>New Title</title><script>window.foo="bar";</script></head>';
-    let headFront = inspector.markup.walker.frontForRawNode(head);
-    inspector.once("markupmutation", (e, aMutations) => {
-      is (doc.title, "New Title", "New title has been added");
-      is (doc.defaultView.foo, undefined, "Script has not been executed");
-      is (doc.querySelector("head").outerHTML, headHTML, "<head> HTML has been updated");
-      is (doc.querySelectorAll("body").length, 1, "no extra <body>s have been added");
-      testDocumentElement();
-    });
-    inspector.markup.updateNodeOuterHTML(headFront, headHTML, head.outerHTML);
-  }
+function testHead(inspector) {
+  let head = getNode("head");
+  let headHTML = '<head id="updated"><title>New Title</title><script>window.foo="bar";</script></head>';
+  let headFront = inspector.markup.walker.frontForRawNode(head);
+  let doc = content.document;
+
+  let mutated = inspector.once("markupmutation");
+  inspector.markup.updateNodeOuterHTML(headFront, headHTML, head.outerHTML);
+
+  return mutated.then(mutations => {
+    is(doc.title, "New Title", "New title has been added");
+    is(doc.defaultView.foo, undefined, "Script has not been executed");
+    is(doc.querySelector("head").outerHTML, headHTML, "<head> HTML has been updated");
+    is(doc.querySelectorAll("body").length, 1, "no extra <body>s have been added");
+    return inspector.once("inspector-updated");
+  });
+}
 
-  function testDocumentElement() {
-    info("Checking to make sure that editing the <html> element works like other nodes");
-    let docElement = doc.documentElement;
-    let docElementHTML = '<html id="updated" foo="bar"><head><title>Updated from document element</title><script>window.foo="bar";</script></head><body><p>Hello</p></body></html>';
-    let docElementFront = inspector.markup.walker.frontForRawNode(docElement);
-    inspector.once("markupmutation", (e, aMutations) => {
-      is (doc.title, "Updated from document element", "New title has been added");
-      is (doc.defaultView.foo, undefined, "Script has not been executed");
-      is (doc.documentElement.id, "updated", "<html> ID has been updated");
-      is (doc.documentElement.className, "", "<html> class has been updated");
-      is (doc.documentElement.getAttribute("foo"), "bar", "<html> attribute has been updated");
-      is (doc.documentElement.outerHTML, docElementHTML, "<html> HTML has been updated");
-      is (doc.querySelectorAll("head").length, 1, "no extra <head>s have been added");
-      is (doc.querySelectorAll("body").length, 1, "no extra <body>s have been added");
-      is (doc.body.textContent, "Hello", "document.body.textContent has been updated");
-      testDocumentElement2();
-    });
-    inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML, docElement.outerHTML);
-  }
+function testDocumentElement(inspector) {
+  let doc = content.document;
+  let docElement = doc.documentElement;
+  let docElementHTML = '<html id="updated" foo="bar"><head><title>Updated from document element</title><script>window.foo="bar";</script></head><body><p>Hello</p></body></html>';
+  let docElementFront = inspector.markup.walker.frontForRawNode(docElement);
+
+  let mutated = inspector.once("markupmutation");
+  inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML, docElement.outerHTML);
+
+  return mutated.then(mutations => {
+    is(doc.title, "Updated from document element", "New title has been added");
+    is(doc.defaultView.foo, undefined, "Script has not been executed");
+    is(doc.documentElement.id, "updated", "<html> ID has been updated");
+    is(doc.documentElement.className, "", "<html> class has been updated");
+    is(doc.documentElement.getAttribute("foo"), "bar", "<html> attribute has been updated");
+    is(doc.documentElement.outerHTML, docElementHTML, "<html> HTML has been updated");
+    is(doc.querySelectorAll("head").length, 1, "no extra <head>s have been added");
+    is(doc.querySelectorAll("body").length, 1, "no extra <body>s have been added");
+    is(doc.body.textContent, "Hello", "document.body.textContent has been updated");
+  });
+}
 
-  function testDocumentElement2() {
-    info("Checking to make sure (again) that editing the <html> element works like other nodes");
-    let docElement = doc.documentElement;
-    let docElementHTML = '<html class="updated" id="somethingelse"><head><title>Updated again from document element</title><script>window.foo="bar";</script></head><body><p>Hello again</p></body></html>';
-    let docElementFront = inspector.markup.walker.frontForRawNode(docElement);
-    inspector.once("markupmutation", (e, aMutations) => {
-      is (doc.title, "Updated again from document element", "New title has been added");
-      is (doc.defaultView.foo, undefined, "Script has not been executed");
-      is (doc.documentElement.id, "somethingelse", "<html> ID has been updated");
-      is (doc.documentElement.className, "updated", "<html> class has been updated");
-      is (doc.documentElement.getAttribute("foo"), null, "<html> attribute has been removed");
-      is (doc.documentElement.outerHTML, docElementHTML, "<html> HTML has been updated");
-      is (doc.querySelectorAll("head").length, 1, "no extra <head>s have been added");
-      is (doc.querySelectorAll("body").length, 1, "no extra <body>s have been added");
-      is (doc.body.textContent, "Hello again", "document.body.textContent has been updated");
-      finishUp();
-    });
-    inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML, docElement.outerHTML);
-  }
+function testDocumentElement2(inspector) {
+  let doc = content.document;
+  let docElement = doc.documentElement;
+  let docElementHTML = '<html class="updated" id="somethingelse"><head><title>Updated again from document element</title><script>window.foo="bar";</script></head><body><p>Hello again</p></body></html>';
+  let docElementFront = inspector.markup.walker.frontForRawNode(docElement);
+
+  let mutated = inspector.once("markupmutation");
+  inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML, docElement.outerHTML);
 
-  function finishUp() {
-    doc = inspector = null;
-    gBrowser.removeCurrentTab();
-    finish();
-  }
+  return mutated.then(mutations => {
+    is(doc.title, "Updated again from document element", "New title has been added");
+    is(doc.defaultView.foo, undefined, "Script has not been executed");
+    is(doc.documentElement.id, "somethingelse", "<html> ID has been updated");
+    is(doc.documentElement.className, "updated", "<html> class has been updated");
+    is(doc.documentElement.getAttribute("foo"), null, "<html> attribute has been removed");
+    is(doc.documentElement.outerHTML, docElementHTML, "<html> HTML has been updated");
+    is(doc.querySelectorAll("head").length, 1, "no extra <head>s have been added");
+    is(doc.querySelectorAll("body").length, 1, "no extra <body>s have been added");
+    is(doc.body.textContent, "Hello again", "document.body.textContent has been updated");
+  });
 }
--- a/browser/devtools/markupview/test/browser_inspector_markup_mutation.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_mutation.js
@@ -1,190 +1,154 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that various mutations to the dom update the markup tool correctly.
- * The test for comparing the markup tool to the real dom is a bit weird:
- * - Select the text in the markup tool
+ * Tests that various mutations to the dom update the markup view correctly.
+ * The test for comparing the markup view to the real dom is a bit weird:
+ * - Select the text in the markup view
  * - Parse that as innerHTML in a document we've created for the purpose.
  * - Remove extraneous whitespace in that tree
  * - Compare it to the real dom with isEqualNode.
  */
 
-function fail(err) {
-  ok(false, err)
-}
-
-function test() {
-  waitForExplicitFinish();
-
-  // Will hold the doc we're viewing
-  let contentTab;
-  let doc;
-
-  // Holds the MarkupTool object we're testing.
-  let markup;
-
-  // Holds the document we use to help re-parse the markup tool's output.
-  let parseTab;
-  let parseDoc;
-
-  let inspector;
-
-  // Strip whitespace from a node and its children.
-  function stripWhitespace(node)
+const TEST_URL = TEST_URL_ROOT + "browser_inspector_markup_mutation.html";
+// All the mutation types we want to test.
+const TEST_DATA = [
   {
-    node.normalize();
-    let iter = node.ownerDocument.createNodeIterator(node, NodeFilter.SHOW_TEXT + NodeFilter.SHOW_COMMENT,
-      null);
-
-    while ((node = iter.nextNode())) {
-      node.nodeValue = node.nodeValue.replace(/\s+/g, '');
-      if (node.nodeType == Node.TEXT_NODE &&
-        !/[^\s]/.exec(node.nodeValue)) {
-        node.parentNode.removeChild(node);
-      }
+    desc: "Adding an attribute",
+    test: () => {
+      let node1 = getNode("#node1");
+      node1.setAttribute("newattr", "newattrval");
     }
-  }
-
-  // Verify that the markup in the tool is the same as the markup in the document.
-  function checkMarkup()
+  },
+  {
+    desc: "Removing an attribute",
+    test: () => {
+      let node1 = getNode("#node1");
+      node1.removeAttribute("newattr");
+    }
+  },
   {
-    return markup.expandAll().then(checkMarkup2);
-  }
-
-  function checkMarkup2()
+    desc: "Updating the text-content",
+    test: () => {
+      let node1 = getNode("#node1");
+      node1.textContent = "newtext";
+    }
+  },
   {
-    let contentNode = doc.querySelector("body");
-    let panelNode = getContainerForRawNode(markup, contentNode).elt;
-    let parseNode = parseDoc.querySelector("body");
-
-    // Grab the text from the markup panel...
-    let sel = panelNode.ownerDocument.defaultView.getSelection();
-    sel.selectAllChildren(panelNode);
-
-    // Parse it
-    parseNode.outerHTML = sel;
-    parseNode = parseDoc.querySelector("body");
-
-    // Pull whitespace out of text and comment nodes, there will
-    // be minor unimportant differences.
-    stripWhitespace(parseNode);
-
-    ok(contentNode.isEqualNode(parseNode), "Markup panel should match document.");
-  }
-
-  // All the mutation types we want to test.
-  let mutations = [
-    // Add an attribute
-    function() {
-      let node1 = doc.querySelector("#node1");
-      node1.setAttribute("newattr", "newattrval");
-    },
-    function() {
-      let node1 = doc.querySelector("#node1");
-      node1.removeAttribute("newattr");
-    },
-    function() {
-      let node1 = doc.querySelector("#node1");
-      node1.textContent = "newtext";
-    },
-    function() {
-      let node2 = doc.querySelector("#node2");
+    desc: "Updating the innerHTML",
+    test: () => {
+      let node2 = getNode("#node2");
       node2.innerHTML = "<div><span>foo</span></div>";
-    },
-
-    function() {
-      let node4 = doc.querySelector("#node4");
+    }
+  },
+  {
+    desc: "Removing child nodes",
+    test: () => {
+      let node4 = getNode("#node4");
       while (node4.firstChild) {
         node4.removeChild(node4.firstChild);
       }
-    },
-    function() {
-      // Move a child to a new parent.
-      let node17 = doc.querySelector("#node17");
-      let node1 = doc.querySelector("#node2");
+    }
+  },
+  {
+    desc: "Appending a child to a different parent",
+    test: () => {
+      let node17 = getNode("#node17");
+      let node1 = getNode("#node2");
       node1.appendChild(node17);
-    },
+    }
+  },
+  {
+    desc: "Swapping a parent and child element, putting them in the same tree",
+    // body
+    //  node1
+    //  node18
+    //    node19
+    //      node20
+    //        node21
+    // will become:
+    // body
+    //   node1
+    //     node20
+    //      node21
+    //      node18
+    //        node19
+    test: () => {
+      let node18 = getNode("#node18");
+      let node20 = getNode("#node20");
 
-    function() {
-      // Swap a parent and child element, putting them in the same tree.
-      // body
-      //  node1
-      //  node18
-      //    node19
-      //      node20
-      //        node21
-      // will become:
-      // body
-      //   node1
-      //     node20
-      //      node21
-      //      node18
-      //        node19
-      let node18 = doc.querySelector("#node18");
-      let node20 = doc.querySelector("#node20");
-
-      let node1 = doc.querySelector("#node1");
+      let node1 = getNode("#node1");
 
       node1.appendChild(node20);
       node20.appendChild(node18);
-    },
-  ];
+    }
+  }
+];
 
-  // Create the helper tab for parsing...
-  parseTab = gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    parseDoc = content.document;
+let test = asyncTest(function*() {
+  info("Creating the helper tab for parsing");
+  let parseTab = yield addTab("data:text/html,<html></html>");
+  let parseDoc = content.document;
 
-    // Then create the actual dom we're inspecting...
-    contentTab = gBrowser.selectedTab = gBrowser.addTab();
-    gBrowser.selectedBrowser.addEventListener("load", function onload2() {
-      gBrowser.selectedBrowser.removeEventListener("load", onload2, true);
-      doc = content.document;
-      // Strip whitespace from the doc for easier comparison.
-      stripWhitespace(doc.documentElement);
-      waitForFocus(setupTest, content);
-    }, true);
-    content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_mutation.html";
-  }, true);
+  info("Creating the test tab");
+  let contentTab = yield addTab(TEST_URL);
+  let doc = content.document;
+  // Strip whitespace in the document for easier comparison
+  stripWhitespace(doc.documentElement);
 
-  content.location = "data:text/html,<html></html>";
+  let {inspector} = yield openInspector();
+  let markup = inspector.markup;
+
+  info("Expanding all markup-view nodes");
+  yield markup.expandAll();
+
+  for (let step of TEST_DATA) {
+    info("Starting test: " + step.desc);
 
-  function setupTest() {
-    var target = TargetFactory.forTab(gBrowser.selectedTab);
-    gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      startTests();
-    });
-  }
+    info("Executing the test markup mutation, listening for inspector-updated before moving on");
+    let updated = inspector.once("inspector-updated");
+    step.test();
+    yield updated;
 
-  function startTests() {
-    markup = inspector.markup;
-    checkMarkup().then(() => {
-      nextStep(0);
-    }).then(null, fail);
-  }
+    info("Expanding all markup-view nodes to make sure new nodes are imported");
+    yield markup.expandAll();
 
-  function nextStep(cursor) {
-    if (cursor >= mutations.length) {
-      finishUp();
-      return;
+    info("Comparing the markup-view markup with the content document");
+    compareMarkup(parseDoc, inspector);
+  }
+});
+
+function stripWhitespace(node) {
+  node.normalize();
+  let iter = node.ownerDocument.createNodeIterator(node,
+    NodeFilter.SHOW_TEXT + NodeFilter.SHOW_COMMENT, null);
+
+  while ((node = iter.nextNode())) {
+    node.nodeValue = node.nodeValue.replace(/\s+/g, '');
+    if (node.nodeType == Node.TEXT_NODE &&
+      !/[^\s]/.exec(node.nodeValue)) {
+      node.parentNode.removeChild(node);
     }
-    mutations[cursor]();
-    inspector.once("markupmutation", function() {
-      executeSoon(function() {
-        checkMarkup().then(() => {
-          nextStep(cursor + 1);
-        }).then(null, fail);
-      });
-    });
-  }
-
-  function finishUp() {
-    doc = inspector = null;
-    gBrowser.removeTab(contentTab);
-    gBrowser.removeTab(parseTab);
-    finish();
   }
 }
+
+function compareMarkup(parseDoc, inspector) {
+  // Grab the text from the markup panel...
+  let markupContainerEl = getContainerForRawNode("body", inspector).elt;
+  let sel = markupContainerEl.ownerDocument.defaultView.getSelection();
+  sel.selectAllChildren(markupContainerEl);
+
+  // Parse it
+  let parseNode = parseDoc.querySelector("body");
+  parseNode.outerHTML = sel;
+  parseNode = parseDoc.querySelector("body");
+
+  // Pull whitespace out of text and comment nodes, there will
+  // be minor unimportant differences.
+  stripWhitespace(parseNode);
+
+  // console.log(contentNode.innerHTML, parseNode.innerHTML);
+  ok(getNode("body").isEqualNode(parseNode),
+    "Markup panel matches what's in the content document.");
+}
--- a/browser/devtools/markupview/test/browser_inspector_markup_mutation_flashing.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_mutation_flashing.js
@@ -1,130 +1,89 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
-
-function test() {
-  waitForExplicitFinish();
-
-  // Will hold the doc we're viewing
-  let contentTab;
-  let doc;
-  let listElement;
-
-  // Holds the MarkupTool object we're testing.
-  let markup;
-  let inspector;
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
-  // Then create the actual dom we're inspecting...
-  contentTab = gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(setupTest, content);
-  }, true);
-  content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_mutation_flashing.html";
-
-  function setupTest() {
-    var target = TargetFactory.forTab(gBrowser.selectedTab);
-    gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      startTests();
-    });
-  }
-
-  function startTests() {
-    markup = inspector.markup;
-
-    // Get the content UL element
-    listElement = doc.querySelector(".list");
+// Test that markup-containers in the markup-view do flash when their
+// corresponding DOM nodes mutate
 
-    // Making sure children are expanded
-    inspector.selection.setNode(listElement.lastElementChild);
-    inspector.once("inspector-updated", () => {
-      // testData contains a list of mutations to test
-      // Each array item is an object with:
-      // - mutate: a function that should make changes to the content DOM
-      // - assert: a function that should test flashing background
-      let testData = [{
-        // Adding a new node should flash the new node
-        mutate: () => {
-          let newLi = doc.createElement("LI");
-          newLi.textContent = "new list item";
-          listElement.appendChild(newLi);
-        },
-        assert: () => {
-          assertNodeFlashing(listElement.lastElementChild);
-        }
-      }, {
-        // Removing a node should flash its parent
-        mutate: () => {
-          listElement.removeChild(listElement.lastElementChild);
-        },
-        assert: () => {
-          assertNodeFlashing(listElement);
-        }
-      }, {
-        // Re-appending an existing node should only flash this node
-        mutate: () => {
-          listElement.appendChild(listElement.firstElementChild);
-        },
-        assert: () => {
-          assertNodeFlashing(listElement.lastElementChild);
-        }
-      }, {
-        // Adding an attribute should flash the node
-        mutate: () => {
-          listElement.setAttribute("name-" + Date.now(), "value-" + Date.now());
-        },
-        assert: () => {
-          assertNodeFlashing(listElement);
-        }
-      }, {
-        // Editing an attribute should flash the node
-        mutate: () => {
-          listElement.setAttribute("class", "list value-" + Date.now());
-        },
-        assert: () => {
-          assertNodeFlashing(listElement);
-        }
-      }, {
-        // Removing an attribute should flash the node
-        mutate: () => {
-          listElement.removeAttribute("class");
-        },
-        assert: () => {
-          assertNodeFlashing(listElement);
-        }
-      }];
-      testMutation(testData, 0);
-    });
+const TEST_URL = TEST_URL_ROOT + "browser_inspector_markup_mutation_flashing.html";
+// The test data contains a list of mutations to test.
+// Each item is an object:
+// - desc: a description of the test step, for better logging
+// - mutate: a function that should make changes to the content DOM
+// - shouldFlash: a function that returns the element that should be the one flashing
+const TEST_DATA = [{
+  desc: "Adding a new node should flash the new node",
+  mutate: (doc, rootNode) => {
+    let newLi = doc.createElement("LI");
+    newLi.textContent = "new list item";
+    rootNode.appendChild(newLi);
+  },
+  shouldFlash: rootNode => rootNode.lastElementChild
+}, {
+  desc: "Removing a node should flash its parent",
+  mutate: (doc, rootNode) => {
+    rootNode.removeChild(rootNode.lastElementChild);
+  },
+  shouldFlash: rootNode => rootNode
+}, {
+  desc: "Re-appending an existing node should only flash this node",
+  mutate: (doc, rootNode) => {
+    rootNode.appendChild(rootNode.firstElementChild);
+  },
+  shouldFlash: rootNode => rootNode.lastElementChild
+}, {
+  desc: "Adding an attribute should flash the node",
+  mutate: (doc, rootNode) => {
+    rootNode.setAttribute("name-" + Date.now(), "value-" + Date.now());
+  },
+  shouldFlash: rootNode => rootNode
+}, {
+  desc: "Editing an attribute should flash the node",
+  mutate: (doc, rootNode) => {
+    rootNode.setAttribute("class", "list value-" + Date.now());
+  },
+  shouldFlash: rootNode => rootNode
+}, {
+  desc: "Removing an attribute should flash the node",
+  mutate: (doc, rootNode) => {
+    rootNode.removeAttribute("class");
+  },
+  shouldFlash: rootNode => rootNode
+}];
+
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  info("Getting the <ul.list> root node to test mutations on");
+  let rootNode = getNode(".list");
+
+  info("Selecting the last element of the root node before starting");
+  yield selectNode(rootNode.lastElementChild, inspector);
+
+  for (let {mutate, shouldFlash, desc} of TEST_DATA) {
+    info("Starting test: " + desc);
+
+    info("Mutating the DOM and listening for markupmutation event");
+    let mutated = inspector.once("markupmutation");
+    let updated = inspector.once("inspector-updated");
+    mutate(content.document, rootNode);
+    yield mutated;
+
+    info("Asserting that the correct markup-container is flashing");
+    assertNodeFlashing(shouldFlash(rootNode), inspector);
+
+    // Making sure the inspector has finished updating before moving on
+    yield updated;
   }
+});
 
-  function testMutation(testData, cursor) {
-    if (cursor < testData.length) {
-      let {mutate, assert} = testData[cursor];
-      mutate();
-      inspector.once("markupmutation", () => {
-        assert();
-        testMutation(testData, cursor + 1);
-      });
-    } else {
-      endTests();
-    }
-  }
+function assertNodeFlashing(node, inspector) {
+  let container = getContainerForRawNode(node, inspector);
 
-  function endTests() {
-    gBrowser.removeTab(contentTab);
-    doc = inspector = contentTab = markup = listElement = null;
-    finish();
-  }
-
-  function assertNodeFlashing(rawNode) {
-    let container = getContainerForRawNode(markup, rawNode);
-
-    if(!container) {
-      ok(false, "Node not found");
-    } else {
-      ok(container.tagState.classList.contains("theme-bg-contrast"),
-        "Node is flashing");
-    }
+  if (!container) {
+    ok(false, "Node not found");
+  } else {
+    ok(container.tagState.classList.contains("theme-bg-contrast"),
+      "Node is flashing");
   }
 }
--- a/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
@@ -1,150 +1,134 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
-
-
-function test() {
-  let inspector;
-
-  waitForExplicitFinish();
-
-  let doc;
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
-  let keySequences = [
-    ["pageup", "*doctype*"],
-    ["down", "html"],
-    ["down", "head"],
-    ["down", "body"],
-    ["down", "node0"],
-    ["right", "node0"],
-    ["down", "node1"],
-    ["down", "node2"],
-    ["down", "node3"],
-    ["down", "*comment*"],
-    ["down", "node4"],
-    ["right", "node4"],
-    ["down", "*text*"],
-    ["down", "node5"],
-    ["down", "node6"],
-    ["down", "*comment*"],
-    ["down" , "node7"],
-    ["right", "node7"],
-    ["down", "*text*"],
-    ["down", "node8"],
-    ["left", "node7"],
-    ["left", "node7"],
-    ["right", "node7"],
-    ["right", "*text*"],
-    ["down", "node8"],
-    ["right", "node8"],
-    ["left", "node8"],
-    ["down", "node9"],
-    ["down", "node10"],
-    ["down", "node11"],
-    ["down", "node12"],
-    ["right", "node12"],
-    ["down", "*text*"],
-    ["down", "node13"],
-    ["down", "node14"],
-    ["down", "node15"],
-    ["down", "node15"],
-    ["down", "node15"],
-    ["up", "node14"],
-    ["up", "node13"],
-    ["up", "*text*"],
-    ["up", "node12"],
-    ["left", "node12"],
-    ["down", "node14"],
-    ["home", "*doctype*"],
-    ["pagedown", "*text*"],
-    ["down", "node5"],
-    ["down", "node6"],
-    ["down", "*comment*"],
-    ["down", "node7"],
-    ["left", "node7"],
-    ["down", "node9"],
-    ["down", "node10"],
-    ["pageup", "node2"],
-    ["pageup", "*doctype*"],
-    ["down", "html"],
-    ["left", "html"],
-    ["down", "html"]
-  ];
+// Test that the markup-view nodes can be navigated to with the keyboard
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(setupTest, content);
-  }, true);
-
-  content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_navigation.html";
-
-  function setupTest() {
-    var target = TargetFactory.forTab(gBrowser.selectedTab);
-    gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      inspector.once("inspector-updated", startNavigation);
-    });
-  }
-
-  function startNavigation() {
-    nextStep(0);
-  }
-
-  function nextStep(cursor) {
-    if (cursor >= keySequences.length) {
-      finishUp();
-      return;
-    }
-
-    let key = keySequences[cursor][0];
-    let className = keySequences[cursor][1];
-    inspector.markup._frame.focus();
+const TEST_URL = TEST_URL_ROOT + "browser_inspector_markup_navigation.html";
+const TEST_DATA = [
+  ["pageup", "*doctype*"],
+  ["down", "html"],
+  ["down", "head"],
+  ["down", "body"],
+  ["down", "node0"],
+  ["right", "node0"],
+  ["down", "node1"],
+  ["down", "node2"],
+  ["down", "node3"],
+  ["down", "*comment*"],
+  ["down", "node4"],
+  ["right", "node4"],
+  ["down", "*text*"],
+  ["down", "node5"],
+  ["down", "node6"],
+  ["down", "*comment*"],
+  ["down" , "node7"],
+  ["right", "node7"],
+  ["down", "*text*"],
+  ["down", "node8"],
+  ["left", "node7"],
+  ["left", "node7"],
+  ["right", "node7"],
+  ["right", "*text*"],
+  ["down", "node8"],
+  ["right", "node8"],
+  ["left", "node8"],
+  ["down", "node9"],
+  ["down", "node10"],
+  ["down", "node11"],
+  ["down", "node12"],
+  ["right", "node12"],
+  ["down", "*text*"],
+  ["down", "node13"],
+  ["down", "node14"],
+  ["down", "node15"],
+  ["down", "node15"],
+  ["down", "node15"],
+  ["up", "node14"],
+  ["up", "node13"],
+  ["up", "*text*"],
+  ["up", "node12"],
+  ["left", "node12"],
+  ["down", "node14"],
+  ["home", "*doctype*"],
+  ["pagedown", "*text*"],
+  ["down", "node5"],
+  ["down", "node6"],
+  ["down", "*comment*"],
+  ["down", "node7"],
+  ["left", "node7"],
+  ["down", "node9"],
+  ["down", "node10"],
+  ["pageup", "node2"],
+  ["pageup", "*doctype*"],
+  ["down", "html"],
+  ["left", "html"],
+  ["down", "html"]
+];
 
-    switch(key) {
-      case "right":
-        EventUtils.synthesizeKey("VK_RIGHT", {});
-        break;
-      case "down":
-        EventUtils.synthesizeKey("VK_DOWN", {});
-        break;
-      case "left":
-        EventUtils.synthesizeKey("VK_LEFT", {});
-        break;
-      case "up":
-        EventUtils.synthesizeKey("VK_UP", {});
-        break;
-      case "pageup":
-        EventUtils.synthesizeKey("VK_PAGE_UP", {});
-        break;
-      case "pagedown":
-        EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
-        break;
-      case "home":
-        EventUtils.synthesizeKey("VK_HOME", {});
-        break;
-    }
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  info("Making sure the markup-view frame is focused");
+  inspector.markup._frame.focus();
+
+  info("Starting to iterate through the test data");
+  for (let [key, className] of TEST_DATA) {
+    info("Testing step: " + key + " to navigate to " + className);
+    pressKey(key);
+
+    info("Making sure markup-view children get updated");
+    yield waitForChildrenUpdated(inspector);
+
+    info("Checking the right node is selected");
+    checkSelectedNode(key, className, inspector);
+  }
+});
 
-    inspector.markup._waitForChildren().then(() => executeSoon(() => {
-      let node = inspector.selection.node;
-
-      if (className == "*comment*") {
-        is(node.nodeType, Node.COMMENT_NODE, "[" + cursor + "] should be a comment after moving " + key);
-      } else if (className == "*text*") {
-        is(node.nodeType, Node.TEXT_NODE, "[" + cursor + "] should be text after moving " + key);
-      } else if (className == "*doctype*") {
-        is(node.nodeType, Node.DOCUMENT_TYPE_NODE, "[" + cursor + "] should be doctype after moving " + key);
-      } else {
-        is(node.className, className, "[" + cursor + "] right node selected: " + className + " after moving " + key);
-      }
-
-      nextStep(cursor + 1);
-    }));
-  }
-
-  function finishUp() {
-    doc = inspector = null;
-    gBrowser.removeCurrentTab();
-    finish();
+function pressKey(key) {
+  switch(key) {
+    case "right":
+      EventUtils.synthesizeKey("VK_RIGHT", {});
+      break;
+    case "down":
+      EventUtils.synthesizeKey("VK_DOWN", {});
+      break;
+    case "left":
+      EventUtils.synthesizeKey("VK_LEFT", {});
+      break;
+    case "up":
+      EventUtils.synthesizeKey("VK_UP", {});
+      break;
+    case "pageup":
+      EventUtils.synthesizeKey("VK_PAGE_UP", {});
+      break;
+    case "pagedown":
+      EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
+      break;
+    case "home":
+      EventUtils.synthesizeKey("VK_HOME", {});
+      break;
   }
 }
+
+function waitForChildrenUpdated(inspector) {
+  let def = promise.defer();
+  inspector.markup._waitForChildren().then(() => {
+    executeSoon(def.resolve);
+  });
+  return def.promise;
+}
+
+function checkSelectedNode(key, className, inspector) {
+  let node = inspector.selection.node;
+
+  if (className == "*comment*") {
+    is(node.nodeType, Node.COMMENT_NODE, "Found a comment after pressing " + key);
+  } else if (className == "*text*") {
+    is(node.nodeType, Node.TEXT_NODE, "Found text after pressing " + key);
+  } else if (className == "*doctype*") {
+    is(node.nodeType, Node.DOCUMENT_TYPE_NODE, "Found the doctype after pressing " + key);
+  } else {
+    is(node.className, className, "Found node: " + className + " after pressing " + key);
+  }
+}
--- a/browser/devtools/markupview/test/browser_inspector_markup_subset.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_subset.js
@@ -1,150 +1,84 @@
-/* Any copyright", " is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/ */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/**
- * Tests that the markup view loads only as many nodes as specified
- * by the devtools.markup.pagesize preference.
- */
+// Tests that the markup view loads only as many nodes as specified by the
+// devtools.markup.pagesize preference.
 
-registerCleanupFunction(function() {
-  Services.prefs.clearUserPref("devtools.markup.pagesize");
-});
 Services.prefs.setIntPref("devtools.markup.pagesize", 5);
 
-
-function test() {
-  waitForExplicitFinish();
-
-  // Will hold the doc we're viewing
-  let doc;
-
-  let inspector;
-
-  // Holds the MarkupTool object we're testing.
-  let markup;
+const TEST_URL = TEST_URL_ROOT + "browser_inspector_markup_subset.html";
+const TEST_DATA = [{
+  desc: "Select the last item",
+  selector: "#z",
+  expected: "*more*vwxyz"
+}, {
+  desc: "Select the first item",
+  selector: "#a",
+  expected: "abcde*more*"
+}, {
+  desc: "Select the last item",
+  selector: "#z",
+  expected: "*more*vwxyz"
+}, {
+  desc: "Select an already-visible item",
+  selector: "#v",
+  // Because "v" was already visible, we shouldn't have loaded
+  // a different page.
+  expected: "*more*vwxyz"
+}, {
+  desc: "Verify childrenDirty reloads the page",
+  selector: "#w",
+  forceReload: true,
+  // But now that we don't already have a loaded page, selecting
+  // w should center around w.
+  expected: "*more*uvwxy*more*"
+}];
 
-  function assertChildren(expected)
-  {
-    let container = getContainerForRawNode(markup, doc.querySelector("body"));
-    let found = [];
-    for (let child of container.children.children) {
-      if (child.classList.contains("more-nodes")) {
-        found += "*more*";
-      } else {
-        found += child.container.node.getAttribute("id");
-      }
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  info("Start iterating through the test data");
+  for (let step of TEST_DATA) {
+    info("Start test: " + step.desc);
+
+    if (step.forceReload) {
+      forceReload(inspector);
     }
-    is(found, expected, "Got the expected children.");
-  }
+    info("Selecting the node that corresponds to " + step.selector);
+    yield selectNode(step.selector, inspector);
 
-  function forceReload()
-  {
-    let container = getContainerForRawNode(markup, doc.querySelector("body"));
-    container.childrenDirty = true;
+    info("Checking that the right nodes are shwon");
+    assertChildren(step.expected, inspector);
   }
 
-  let selections = [
-    {
-      desc: "Select the last item",
-      selector: "#z",
-      before: function() {},
-      after: function() {
-        assertChildren("*more*vwxyz");
-      }
-    },
-    {
-      desc: "Select the first item",
-      selector: "#a",
-      before: function() {
-      },
-      after: function() {
-        assertChildren("abcde*more*");
-      }
-    },
-    {
-      desc: "Select the last item",
-      selector: "#z",
-      before: function() {},
-      after: function() {
-        assertChildren("*more*vwxyz");
-      }
-    },
-    {
-      desc: "Select an already-visible item",
-      selector: "#v",
-      before: function() {},
-      after: function() {
-        // Because "v" was already visible, we shouldn't have loaded
-        // a different page.
-        assertChildren("*more*vwxyz");
-      },
-    },
-    {
-      desc: "Verify childrenDirty reloads the page",
-      selector: "#w",
-      before: function() {
-        forceReload();
-      },
-      after: function() {
-        // But now that we don't already have a loaded page, selecting
-        // w should center around w.
-        assertChildren("*more*uvwxy*more*");
-      },
-    },
-  ];
+  info("Checking that clicking the more button loads everything");
+  clickShowMoreNodes(inspector);
+  yield inspector.markup._waitForChildren();
+  assertChildren("abcdefghijklmnopqrstuvwxyz", inspector);
+});
 
-  // Create the helper tab for parsing...
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function onload() {
-    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    doc = content.document;
-    waitForFocus(setupTest, content);
-  }, true);
-  content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_subset.html";
-
-  function setupTest() {
-    var target = TargetFactory.forTab(gBrowser.selectedTab);
-    let toolbox = gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-      inspector = toolbox.getCurrentPanel();
-      markup = inspector.markup;
-      inspector.once("inspector-updated", runNextSelection);
-    });
+function assertChildren(expected, inspector) {
+  let container = getContainerForRawNode("body", inspector);
+  let found = "";
+  for (let child of container.children.children) {
+    if (child.classList.contains("more-nodes")) {
+      found += "*more*";
+    } else {
+      found += child.container.node.getAttribute("id");
+    }
   }
-
-  function runNextSelection() {
-    let selection = selections.shift();
-    if (!selection) {
-      clickMore();
-      return;
-    }
+  is(found, expected, "Got the expected children.");
+}
 
-    info(selection.desc);
-    selection.before();
-    inspector.once("inspector-updated", function() {
-      selection.after();
-      runNextSelection();
-    });
-    inspector.selection.setNode(doc.querySelector(selection.selector));
-  }
+function forceReload(inspector) {
+  let container = getContainerForRawNode("body", inspector);
+  container.childrenDirty = true;
+}
 
-  function clickMore() {
-    info("Check that clicking more loads the whole thing.");
-    // Make sure that clicking the "more" button loads all the nodes.
-    let container = getContainerForRawNode(markup, doc.querySelector("body"));
-    let button = container.elt.querySelector("button");
-    let win = button.ownerDocument.defaultView;
-
-    EventUtils.sendMouseEvent({type: "click"}, button, win);
-
-    markup._waitForChildren().then(() => {
-      assertChildren("abcdefghijklmnopqrstuvwxyz");
-      finishUp();
-    });
-  }
-
-  function finishUp() {
-    doc = inspector = markup = null;
-    gBrowser.removeCurrentTab();
-    finish();
-  }
+function clickShowMoreNodes(inspector) {
+  let container = getContainerForRawNode("body", inspector);
+  let button = container.elt.querySelector("button");
+  let win = button.ownerDocument.defaultView;
+  EventUtils.sendMouseEvent({type: "click"}, button, win);
 }
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -1,151 +1,188 @@
 /* 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/. */
 
 const Cu = Components.utils;
-
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let TargetFactory = devtools.TargetFactory;
 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 let promise = devtools.require("sdk/core/promise");
 let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
 
+// All test are asynchronous
+waitForExplicitFinish();
+
 //Services.prefs.setBoolPref("devtools.dump.emit", true);
 
+// Set the testing flag on gDevTools and reset it when the test ends
 gDevTools.testing = true;
-SimpleTest.registerCleanupFunction(() => {
-  gDevTools.testing = false;
-});
+registerCleanupFunction(() => gDevTools.testing = false);
 
 // Clear preferences that may be set during the course of tests.
-function clearUserPrefs() {
+registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
   Services.prefs.clearUserPref("devtools.dump.emit");
-}
+  Services.prefs.clearUserPref("devtools.markup.pagesize");
+});
 
-registerCleanupFunction(clearUserPrefs);
+// Auto close the toolbox and close the test tabs when the test ends
+registerCleanupFunction(() => {
+  try {
+    let target = TargetFactory.forTab(gBrowser.selectedTab);
+    gDevTools.closeToolbox(target);
+  } catch (ex) {
+    dump(ex);
+  }
+  while (gBrowser.tabs.length > 1) {
+    gBrowser.removeCurrentTab();
+  }
+});
+
+const TEST_URL_ROOT = "http://mochi.test:8888/browser/browser/devtools/markupview/test/";
+
+/**
+ * Define an async test based on a generator function
+ */
+function asyncTest(generator) {
+  return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
+}
 
 /**
  * Add a new test tab in the browser and load the given url.
  * @param {String} url The url to be loaded in the new tab
- * @return a promise that resolves when the url is loaded
+ * @return a promise that resolves to the tab object when the url is loaded
  */
 function addTab(url) {
+  info("Adding a new tab with URL: '" + url + "'");
   let def = promise.defer();
 
-  gBrowser.selectedTab = gBrowser.addTab();
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
-    info("URL " + url + " loading complete into new test tab");
-    waitForFocus(def.resolve, content);
+    info("URL '" + url + "' loading complete");
+    waitForFocus(() => {
+      def.resolve(tab);
+    }, content);
   }, true);
   content.location = url;
 
   return def.promise;
 }
 
 /**
+ * Reload the current page
+ * @return a promise that resolves when the inspector has emitted the event
+ * new-root
+ */
+function reloadPage(inspector) {
+  info("Reloading the page");
+  let newRoot = inspector.once("new-root");
+  content.location.reload();
+  return newRoot;
+}
+
+/**
  * Open the toolbox, with the inspector tool visible.
  * @return a promise that resolves when the inspector is ready
  */
 function openInspector() {
+  info("Opening the inspector panel");
   let def = promise.defer();
 
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-    info("Toolbox open");
+    info("The toolbox is open");
     let inspector = toolbox.getCurrentPanel();
     inspector.once("inspector-updated", () => {
-      info("Inspector panel active and ready");
+      info("The inspector panel is active and ready");
       def.resolve({toolbox: toolbox, inspector: inspector});
     });
   }).then(null, console.error);
 
   return def.promise;
 }
 
 /**
- * Get the MarkupContainer object instance that corresponds to the given
- * HTML node
- * @param {MarkupView} markupView The instance of MarkupView currently loaded into the inspector panel
- * @param {DOMNode} rawNode The DOM node for which the container is required
- * @return {MarkupContainer}
- */
-function getContainerForRawNode(markupView, rawNode) {
-  let front = markupView.walker.frontForRawNode(rawNode);
-  let container = markupView.getContainer(front);
-  return container;
-}
-
-/**
  * Simple DOM node accesor function that takes either a node or a string css
  * selector as argument and returns the corresponding node
  * @param {String|DOMNode} nodeOrSelector
  * @return {DOMNode}
  */
 function getNode(nodeOrSelector) {
-  let node = nodeOrSelector;
-
-  if (typeof nodeOrSelector === "string") {
-    node = content.document.querySelector(nodeOrSelector);
-    ok(node, "A node was found for selector " + nodeOrSelector);
-  }
-
-  return node;
+  info("Getting the node for '" + nodeOrSelector + "'");
+  return typeof nodeOrSelector === "string" ?
+    content.document.querySelector(nodeOrSelector) :
+    nodeOrSelector;
 }
 
 /**
  * Set the inspector's current selection to a node or to the first match of the
  * given css selector
  * @param {String|DOMNode} nodeOrSelector
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
  * @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
  * @return a promise that resolves when the inspector is updated with the new
  * node
  */
 function selectNode(nodeOrSelector, inspector, reason="test") {
-  info("Selecting the node " + nodeOrSelector);
+  info("Selecting the node for '" + nodeOrSelector + "'");
   let node = getNode(nodeOrSelector);
   let updated = inspector.once("inspector-updated");
   inspector.selection.setNode(node, reason);
   return updated;
 }
 
 /**
+ * Get the MarkupContainer object instance that corresponds to the given
+ * HTML node
+ * @param {DOMNode|String} nodeOrSelector The DOM node for which the
+ * container is required
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {MarkupContainer}
+ */
+function getContainerForRawNode(nodeOrSelector, {markup}) {
+  let front = markup.walker.frontForRawNode(getNode(nodeOrSelector));
+  let container = markup.getContainer(front);
+  info("Markup-container object for " + nodeOrSelector + " " + container);
+  return container;
+}
+
+/**
  * Simulate a mouse-over on the markup-container (a line in the markup-view)
  * that corresponds to the node or selector passed.
  * @param {String|DOMNode} nodeOrSelector
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
  * @return a promise that resolves when the container is hovered and the higlighter
  * is shown on the corresponding node
  */
 function hoverContainer(nodeOrSelector, inspector) {
   info("Hovering over the markup-container for node " + nodeOrSelector);
   let highlit = inspector.toolbox.once("node-highlight");
-  let container = getContainerForRawNode(inspector.markup, getNode(nodeOrSelector));
+  let container = getContainerForRawNode(getNode(nodeOrSelector), inspector);
   EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousemove"},
     inspector.markup.doc.defaultView);
   return highlit;
 }
 
 /**
  * Simulate a click on the markup-container (a line in the markup-view)
  * that corresponds to the node or selector passed.
  * @param {String|DOMNode} nodeOrSelector
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
  * @return a promise that resolves when the node has been selected.
  */
 function clickContainer(nodeOrSelector, inspector) {
   info("Clicking on the markup-container for node " + nodeOrSelector);
   let updated = inspector.once("inspector-updated");
-  let container = getContainerForRawNode(inspector.markup, getNode(nodeOrSelector));
+  let container = getContainerForRawNode(getNode(nodeOrSelector), inspector);
   EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousedown"},
     inspector.markup.doc.defaultView);
   EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mouseup"},
     inspector.markup.doc.defaultView);
   return updated;
 }
 
 /**
@@ -174,52 +211,57 @@ function mouseLeaveMarkupView(inspector)
     inspector.toolbox.doc.defaultView);
   executeSoon(def.resolve);
 
   return def.promise;
 }
 
 /**
  * Focus a given editable element, enter edit mode, set value, and commit
- * @param {DOMNode} field The element that gets editable after receiving focus and <ENTER> keypress
+ * @param {DOMNode} field The element that gets editable after receiving focus
+ * and <ENTER> keypress
  * @param {String} value The string value to be set into the edited field
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
  */
 function setEditableFieldValue(field, value, inspector) {
   field.focus();
   EventUtils.sendKey("return", inspector.panelWin);
   let input = inplaceEditor(field).input;
   ok(input, "Found editable field for setting value: " + value);
   input.value = value;
   EventUtils.sendKey("return", inspector.panelWin);
 }
 
 /**
  * Checks that a node has the given attributes
  *
- * @param {HTMLNode} element The node to check.
+ * @param {DOMNode|String} nodeOrSelector The node or node selector to check.
  * @param {Object} attrs An object containing the attributes to check.
  *        e.g. {id: "id1", class: "someclass"}
  *
  * Note that node.getAttribute() returns attribute values provided by the HTML
  * parser. The parser only provides unescaped entities so &amp; will return &.
  */
-function assertAttributes(element, attrs) {
-  is(element.attributes.length, Object.keys(attrs).length,
+function assertAttributes(nodeOrSelector, attrs) {
+  let node = getNode(nodeOrSelector);
+
+  is(node.attributes.length, Object.keys(attrs).length,
     "Node has the correct number of attributes.");
   for (let attr in attrs) {
-    is(element.getAttribute(attr), attrs[attr],
+    is(node.getAttribute(attr), attrs[attr],
       "Node has the correct " + attr + " attribute.");
   }
 }
 
 /**
  * Undo the last markup-view action and wait for the corresponding mutation to
  * occur
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
  * @return a promise that resolves when the markup-mutation has been treated or
  * rejects if no undo action is possible
  */
 function undoChange(inspector) {
   let canUndo = inspector.markup.undo.canUndo();
   ok(canUndo, "The last change in the markup-view can be undone");
   if (!canUndo) {
     return promise.reject();
@@ -228,17 +270,18 @@ function undoChange(inspector) {
   let mutated = inspector.once("markupmutation");
   inspector.markup.undo.undo();
   return mutated;
 }
 
 /**
  * Redo the last markup-view action and wait for the corresponding mutation to
  * occur
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
  * @return a promise that resolves when the markup-mutation has been treated or
  * rejects if no redo action is possible
  */
 function redoChange(inspector) {
   let canRedo = inspector.markup.undo.canRedo();
   ok(canRedo, "The last change in the markup-view can be redone");
   if (!canRedo) {
     return promise.reject();
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -1379,19 +1379,23 @@ CssRuleView.prototype = {
     this._viewedElement = aElement;
     if (!this._viewedElement) {
       this._showEmpty();
       return promise.resolve(undefined);
     }
 
     this._elementStyle = new ElementStyle(aElement, this.store, this.pageStyle);
     return this._populate().then(() => {
-      this._elementStyle.onChanged = () => {
-        this._changed();
-      };
+      // A new node may already be selected, in which this._elementStyle will
+      // be null.
+      if (this._elementStyle) {
+        this._elementStyle.onChanged = () => {
+          this._changed();
+        };
+      }
     }).then(null, console.error);
   },
 
   /**
    * Update the rules for the currently highlighted element.
    */
   nodeChanged: function() {
     // Ignore refreshes during editing or when no element is selected.