Merge fx-team to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 05 Mar 2016 19:22:45 -0500
changeset 323278 46210f3ae07855edfe1383210d78433e38a6b564
parent 323222 fcd55efa0672b393db88ab6bfd2d4b190072c161 (current diff)
parent 323277 b29add2c129122e4af8582cac68259c0cc96baec (diff)
child 323300 03330eb525e1ec8fed65071864b4f4f3be30c75d
child 323310 54abddb1c44c6224ba7e6810b9b11b5d24d9bffc
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone47.0a1
first release with
nightly linux32
46210f3ae078 / 47.0a1 / 20160306030215 / files
nightly linux64
46210f3ae078 / 47.0a1 / 20160306030215 / files
nightly mac
46210f3ae078 / 47.0a1 / 20160306030215 / files
nightly win32
46210f3ae078 / 47.0a1 / 20160306030215 / files
nightly win64
46210f3ae078 / 47.0a1 / 20160306030215 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c. a=merge
dom/base/test/browser_bug902350.js
dom/push/PushService.jsm
mobile/android/base/resources/drawable-hdpi-v11/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-hdpi/bookmark_folder_closed.png
mobile/android/base/resources/drawable-hdpi/folder_up.png
mobile/android/base/resources/drawable-hdpi/home_group_expanded.png
mobile/android/base/resources/drawable-nodpi/bookmarked_star.png
mobile/android/base/resources/drawable-xhdpi-v11/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-xhdpi/bookmark_folder_closed.png
mobile/android/base/resources/drawable-xhdpi/folder_up.png
mobile/android/base/resources/drawable-xhdpi/home_group_expanded.png
mobile/android/base/resources/drawable-xlarge-hdpi-v11/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-xlarge-mdpi-v11/ic_menu_bookmark_add.png
mobile/android/base/resources/drawable-xlarge-mdpi-v11/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-xlarge-xhdpi-v11/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-xlarge-xxhdpi-v11/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-xxhdpi-v11/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-xxhdpi/home_group_expanded.png
mobile/android/base/resources/drawable/ic_menu_bookmark_remove.xml
mobile/android/chrome/content/browser.js
widget/tests/plugin_scroll_invalidation.html
widget/tests/test_plugin_scroll_invalidation.html
--- a/.eslintignore
+++ b/.eslintignore
@@ -83,17 +83,16 @@ devtools/client/canvasdebugger/**
 devtools/client/commandline/**
 devtools/client/debugger/**
 devtools/client/eyedropper/**
 devtools/client/framework/**
 # devtools/client/inspector/shared/*.js files are eslint-clean, so they aren't
 # included in the ignore list.
 devtools/client/inspector/computed/**
 devtools/client/inspector/fonts/**
-devtools/client/inspector/markup/test/**
 devtools/client/inspector/shared/test/**
 devtools/client/inspector/test/**
 devtools/client/inspector/*.js
 devtools/client/jsonview/**
 devtools/client/memory/**
 devtools/client/netmonitor/**
 devtools/client/performance/**
 devtools/client/projecteditor/**
@@ -124,17 +123,17 @@ devtools/shared/sourcemap/*
 devtools/shared/qrcode/decoder/*
 devtools/shared/qrcode/encoder/*
 devtools/client/shared/vendor/*
 devtools/client/shared/d3.js
 devtools/client/webaudioeditor/lib/dagre-d3.js
 devtools/client/sourceeditor/codemirror/*.js
 devtools/client/sourceeditor/codemirror/**/*.js
 devtools/client/sourceeditor/test/codemirror/*
-devtools/client/markupview/test/lib_*
+devtools/client/inspector/markup/test/lib_*
 
 # mobile/android/ exclusions
 mobile/android/chrome/content
 mobile/android/tests/
 
 # Uses `#filter substitution`
 mobile/android/b2gdroid/app/b2gdroid.js
 mobile/android/app/mobile.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1588,19 +1588,21 @@ pref("browser.tabs.remote.autostart.2", 
 #endif
 
 // For the about:tabcrashed page
 pref("browser.tabs.crashReporting.sendReport", true);
 pref("browser.tabs.crashReporting.includeURL", false);
 pref("browser.tabs.crashReporting.emailMe", false);
 pref("browser.tabs.crashReporting.email", "");
 
+#ifndef RELEASE_BUILD
 #ifndef MOZ_MULET
 pref("layers.async-pan-zoom.enabled", true);
 #endif
+#endif
 
 // Enable e10s add-on interposition by default.
 pref("extensions.interposition.enabled", true);
 pref("extensions.interposition.prefetching", true);
 
 // Enable blocking of e10s for add-on users on beta/release.
 #ifdef RELEASE_BUILD
 pref("extensions.e10sBlocksEnabling", true);
--- a/browser/base/content/abouthealthreport/abouthealth.js
+++ b/browser/base/content/abouthealthreport/abouthealth.js
@@ -105,16 +105,25 @@ var healthReportWrapper = {
       content: content
     }
 
     let iframe = document.getElementById("remote-report");
     iframe.contentWindow.postMessage(data, reportUrl);
   },
 
   handleRemoteCommand: function (evt) {
+    // Do an origin check to harden against the frame content being loaded from unexpected locations.
+    let allowedPrincipal = Services.scriptSecurityManager.getCodebasePrincipal(this._getReportURI());
+    let targetPrincipal = evt.target.nodePrincipal;
+    if (!allowedPrincipal.equals(targetPrincipal)) {
+      Cu.reportError(`Origin check failed for message "${evt.detail.command}": ` +
+                     `target origin is "${targetPrincipal.origin}", expected "${allowedPrincipal.origin}"`);
+      return;
+    }
+
     switch (evt.detail.command) {
       case "DisableDataSubmission":
         this.setDataSubmission(false);
         break;
       case "EnableDataSubmission":
         this.setDataSubmission(true);
         break;
       case "RequestCurrentPrefs":
--- a/browser/base/content/test/alerts/browser_notification_replace.js
+++ b/browser/base/content/test/alerts/browser_notification_replace.js
@@ -11,28 +11,28 @@ add_task(function* test_notificationRepl
     url: notificationURL
   }, function* dummyTabTask(aBrowser) {
     yield ContentTask.spawn(aBrowser, {}, function* () {
       let win = content.window.wrappedJSObject;
       let notification = win.showNotification1();
       let promiseCloseEvent = ContentTaskUtils.waitForEvent(notification, "close");
 
       let showEvent = yield ContentTaskUtils.waitForEvent(notification, "show");
-      is(showEvent.target.body, "Test body 1", "Showed tagged notification");
+      Assert.equal(showEvent.target.body, "Test body 1", "Showed tagged notification");
 
       let newNotification = win.showNotification2();
       let newShowEvent = yield ContentTaskUtils.waitForEvent(newNotification, "show");
-      is(newShowEvent.target.body, "Test body 2", "Showed new notification with same tag");
+      Assert.equal(newShowEvent.target.body, "Test body 2", "Showed new notification with same tag");
 
       let closeEvent = yield promiseCloseEvent;
-      is(closeEvent.target.body, "Test body 1", "Closed previous tagged notification");
+      Assert.equal(closeEvent.target.body, "Test body 1", "Closed previous tagged notification");
 
       let promiseNewCloseEvent = ContentTaskUtils.waitForEvent(newNotification, "close");
       newNotification.close();
       let newCloseEvent = yield promiseNewCloseEvent;
-      is(newCloseEvent.target.body, "Test body 2", "Closed new notification");
+      Assert.equal(newCloseEvent.target.body, "Test body 2", "Closed new notification");
     });
   });
 });
 
 add_task(function* cleanup() {
   Services.perms.remove(makeURI(notificationURL), "desktop-notification");
 });
--- a/browser/base/content/test/chat/browser_focus.js
+++ b/browser/base/content/test/chat/browser_focus.js
@@ -247,12 +247,12 @@ add_chat_task(function* testFocusedEleme
   Services.focus.moveFocus(tabb.contentWindow, null, Services.focus.MOVEFOCUS_ROOT, 0);
   yield promise;
 
   promise = promiseOneMessage(chat.content, "Social:FocusEnsured");
   chatbar.focus();
   yield promise;
 
   yield ContentTask.spawn(chat.content, null, function* () {
-    is(content.document.activeElement.getAttribute("id"), "input2",
+    Assert.equal(content.document.activeElement.getAttribute("id"), "input2",
       "correct input field still has focus");
   });
 });
--- a/browser/base/content/test/chat/browser_tearoff.js
+++ b/browser/base/content/test/chat/browser_tearoff.js
@@ -61,17 +61,17 @@ add_chat_task(function* testTearoffChat(
   Assert.equal(numChatsInWindow(window), 0, "should be no chats in the chat bar");
 
   // get the chatbox from the new window.
   chatbox = domwindow.document.getElementById("chatter")
   Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
 
   yield ContentTask.spawn(chatbox.content, null, function* () {
     let div = content.document.getElementById("testdiv");
-    is(div.getAttribute("test"), "1", "docshell should have been swapped");
+    Assert.equal(div.getAttribute("test"), "1", "docshell should have been swapped");
     div.setAttribute("test", "2");
   });
 
   // swap the window back to the chatbar
   promise = promiseOneEvent(domwindow, "unload");
   swap = domwindow.document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
   swap.click();
 
@@ -79,17 +79,17 @@ add_chat_task(function* testTearoffChat(
 
   Assert.equal(numChatsInWindow(window), 1, "chat should be docked back in the window");
   chatbox = chatbar.selectedChat;
   Assert.equal(chatbox.getAttribute("label"), chatTitle,
                "the new chatbox should show the title of the chat window again");
 
   yield ContentTask.spawn(chatbox.content, null, function* () {
     let div = content.document.getElementById("testdiv");
-    is(div.getAttribute("test"), "2", "docshell should have been swapped");
+    Assert.equal(div.getAttribute("test"), "2", "docshell should have been swapped");
   });
 });
 
 // Similar test but with 2 chats.
 add_chat_task(function* testReattachTwice() {
   let chatbox1 = yield promiseOpenChat("http://example.com#1");
   let chatbox2 = yield promiseOpenChat("http://example.com#2");
   Assert.equal(numChatsInWindow(window), 2, "both chats should be docked in the window");
--- a/browser/base/content/test/general/browser_PageMetaData_pushstate.js
+++ b/browser/base/content/test/general/browser_PageMetaData_pushstate.js
@@ -1,32 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 add_task(function* () {
   let rooturi = "https://example.com/browser/toolkit/modules/tests/browser/";
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, rooturi + "metadata_simple.html");
-  let result = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    return PageMetadata.getData(content.document);
-  });
-  // result should have description
-  is(result.url, rooturi + "metadata_simple.html", "metadata url is correct");
-  is(result.title, "Test Title", "metadata title is correct");
-  is(result.description, "A very simple test page", "description is correct");
+  yield ContentTask.spawn(gBrowser.selectedBrowser, { rooturi }, function* (args) {
+    let result = PageMetadata.getData(content.document);
+    // Result should have description.
+    Assert.equal(result.url, args.rooturi + "metadata_simple.html", "metadata url is correct");
+    Assert.equal(result.title, "Test Title", "metadata title is correct");
+    Assert.equal(result.description, "A very simple test page", "description is correct");
 
-  result = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
     content.history.pushState({}, "2", "2.html");
-    return PageMetadata.getData(content.document);
+    result = PageMetadata.getData(content.document);
+    // Result should not have description.
+    Assert.equal(result.url, args.rooturi + "2.html", "metadata url is correct");
+    Assert.equal(result.title, "Test Title", "metadata title is correct");
+    Assert.ok(!result.description, "description is undefined");
+
+    Assert.equal(content.document.documentURI, args.rooturi + "2.html",
+      "content.document has correct url");
   });
-  // result should not have description
-  is(result.url, rooturi + "2.html", "metadata url is correct");
-  is(result.title, "Test Title", "metadata title is correct");
-  ok(!result.description, "description is undefined");
 
-  let documentURI = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    return content.document.documentURI;
-  });
   is(gBrowser.currentURI.spec, rooturi + "2.html", "gBrowser has correct url");
-  is(documentURI, rooturi + "2.html", "content.document has correct url");
 
   gBrowser.removeTab(gBrowser.selectedTab);
 });
--- a/browser/base/content/test/general/browser_aboutHome_wrapsCorrectly.js
+++ b/browser/base/content/test/general/browser_aboutHome_wrapsCorrectly.js
@@ -3,26 +3,26 @@ add_task(function* () {
 
   let resizedPromise = BrowserTestUtils.waitForEvent(newWindow, "resize");
   newWindow.resizeTo(300, 300);
   yield resizedPromise;
 
   yield BrowserTestUtils.openNewForegroundTab(newWindow.gBrowser, "about:home");
 
   yield ContentTask.spawn(newWindow.gBrowser.selectedBrowser, {}, function* () {
-    is(content.document.body.getAttribute("narrow"), "true", "narrow mode");
+    Assert.equal(content.document.body.getAttribute("narrow"), "true", "narrow mode");
   });
 
   resizedPromise = BrowserTestUtils.waitForContentEvent(newWindow.gBrowser.selectedBrowser, "resize");
 
 
   yield ContentTask.spawn(newWindow.gBrowser.selectedBrowser, {}, function* () {
     content.window.resizeTo(800, 800);
   });
 
   yield resizedPromise;
 
   yield ContentTask.spawn(newWindow.gBrowser.selectedBrowser, {}, function* () {
-    is(content.document.body.hasAttribute("narrow"), false, "non-narrow mode");
+    Assert.equal(content.document.body.hasAttribute("narrow"), false, "non-narrow mode");
   });
 
   yield BrowserTestUtils.closeWindow(newWindow);
 });
--- a/browser/base/content/test/general/browser_aboutTabCrashed_withoutDump.js
+++ b/browser/base/content/test/general/browser_aboutTabCrashed_withoutDump.js
@@ -24,21 +24,21 @@ add_task(function* test_without_dump() {
   }, function*(browser) {
     let tab = gBrowser.getTabForBrowser(browser);
     yield BrowserTestUtils.crashBrowser(browser);
 
     let tabRemovedPromise = BrowserTestUtils.removeTab(tab, { dontRemove: true });
 
     yield ContentTask.spawn(browser, null, function*() {
       let doc = content.document;
-      ok(!doc.documentElement.classList.contains("crashDumpAvailable"),
+      Assert.ok(!doc.documentElement.classList.contains("crashDumpAvailable"),
          "doesn't have crash dump");
 
       let container = doc.getElementById("crash-reporter-container");
-      ok(container, "has crash-reporter-container");
-      ok(container.hidden, "crash-reporter-container is hidden");
+      Assert.ok(container, "has crash-reporter-container");
+      Assert.ok(container.hidden, "crash-reporter-container is hidden");
 
       doc.getElementById("closeTab").click();
     });
 
     yield tabRemovedPromise;
   });
 });
--- a/browser/base/content/test/general/browser_bug431826.js
+++ b/browser/base/content/test/general/browser_bug431826.js
@@ -8,56 +8,45 @@ add_task(function* () {
   let promise = remote(function () {
     return ContentTaskUtils.waitForEvent(this, "DOMContentLoaded", true, event => {
       return content.document.documentURI != "about:blank";
     }).then(() => 0); // don't want to send the event to the chrome process
   });
   gBrowser.loadURI("https://nocert.example.com/");
   yield promise;
 
-  let uri = yield remote(() => {
-    return content.document.documentURI;
+  yield remote(() => {
+    // Confirm that we are displaying the contributed error page, not the default
+    let uri = content.document.documentURI;
+    Assert.ok(uri.startsWith("about:certerror"), "Broken page should go to about:certerror, not about:neterror");
   });
 
-  // Confirm that we are displaying the contributed error page, not the default
-  ok(uri.startsWith("about:certerror"), "Broken page should go to about:certerror, not about:neterror");
-
   let advancedDiv, advancedDivVisibility, technicalDivCollapsed;
 
-  [advancedDiv, advancedDivVisibility] = yield remote(() => {
+  yield remote(() => {
     let div = content.document.getElementById("advancedPanel");
-    if (div) {
-      return [true, div.ownerDocument.defaultView.getComputedStyle(div, "").visibility];
-    } else {
-      return [null, null];
-    }
+    // Confirm that the expert section is collapsed
+    Assert.ok(div, "Advanced content div should exist");
+    Assert.equal(div.ownerDocument.defaultView.getComputedStyle(div, "").visibility,
+      "hidden", "Advanced content should not be visible by default");
   });
 
-  // Confirm that the expert section is collapsed
-  ok(advancedDiv, "Advanced content div should exist");
-  is(advancedDivVisibility, "hidden", "Advanced content should not be visible by default");
-
   // Tweak the expert mode pref
   gPrefService.setBoolPref("browser.xul.error_pages.expert_bad_cert", true);
 
   promise = remote(function () {
     return ContentTaskUtils.waitForEvent(this, "DOMContentLoaded", true);
   });
   gBrowser.reload();
   yield promise;
 
-  [advancedDiv, advancedDivVisibility] = yield remote(() => {
+  yield remote(() => {
     let div = content.document.getElementById("advancedPanel");
-    if (div) {
-      return [true, div.ownerDocument.defaultView.getComputedStyle(div, "").visibility];
-    } else {
-      return [null, null];
-    }
+    Assert.ok(div, "Advanced content div should exist");
+    Assert.equal(div.ownerDocument.defaultView.getComputedStyle(div, "").visibility,
+      "visible", "Advanced content should be visible by default");
   });
 
-  ok(advancedDiv, "Advanced content div should exist");
-  is(advancedDivVisibility, "visible", "Advanced content should be visible by default");
-
   // Clean up
   gBrowser.removeCurrentTab();
   if (gPrefService.prefHasUserValue("browser.xul.error_pages.expert_bad_cert"))
     gPrefService.clearUserPref("browser.xul.error_pages.expert_bad_cert");
 });
--- a/browser/base/content/test/general/browser_bug561636.js
+++ b/browser/base/content/test/general/browser_bug561636.js
@@ -68,30 +68,28 @@ function* blurChildElement(browser)
 {
   yield ContentTask.spawn(browser, {}, function* () {
     content.document.getElementById('i').blur();
   });
 }
 
 function* checkChildFocus(browser, message)
 {
-  let [activeElement, validMsg] =
-    yield ContentTask.spawn(browser, message, function* (msg) {
-      var focused = content.document.activeElement == content.document.getElementById('i');
+  yield ContentTask.spawn(browser, [message, testId], function* (args) {
+    let [msg, testId] = args;
+    var focused = content.document.activeElement == content.document.getElementById('i');
 
-      var validMsg = true;
-      if (msg) {
-        validMsg = (msg == content.document.getElementById('i').validationMessage);
-      }
+    var validMsg = true;
+    if (msg) {
+      validMsg = (msg == content.document.getElementById('i').validationMessage);
+    }
 
-      return [focused, validMsg];
+    Assert.equal(focused, true, "Test " + testId + " First invalid element should be focused");
+    Assert.equal(validMsg, true, "Test " + testId + " The panel should show the message from validationMessage");
   });
-
-  is(activeElement, true, "Test " + testId + " First invalid element should be focused");
-  is(validMsg, true, "Test " + testId + " The panel should show the message from validationMessage");
 }
 
 /**
  * In this test, we check that no popup appears if the form is valid.
  */
 add_task(function* ()
 {
   incrementTest();
--- a/browser/base/content/test/general/browser_bug595507.js
+++ b/browser/base/content/test/general/browser_bug595507.js
@@ -17,20 +17,20 @@ add_task(function* () {
   let popupShownPromise = promiseWaitForEvent(gInvalidFormPopup, "popupshown");
 
   yield ContentTask.spawn(browser, {}, function* () {
     content.document.getElementsByTagName('iframe')[0]
            .contentDocument.getElementById('s').click();
   });
   yield popupShownPromise;
 
-  let activeElement = yield ContentTask.spawn(browser, {}, function* () {
+  yield ContentTask.spawn(browser, {}, function* () {
     let childdoc = content.document.getElementsByTagName('iframe')[0].contentDocument;
-    return childdoc.activeElement == childdoc.getElementById('i');
+    Assert.equal(childdoc.activeElement, childdoc.getElementById("i"),
+      "First invalid element should be focused");
   });
-  is(activeElement, true, "First invalid element should be focused");
 
   ok(gInvalidFormPopup.state == 'showing' || gInvalidFormPopup.state == 'open',
      "The invalid form popup should be shown");
 
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/base/content/test/general/browser_bug676619.js
+++ b/browser/base/content/test/general/browser_bug676619.js
@@ -15,19 +15,19 @@ function test () {
       ContentTask.spawn(gBrowser.selectedBrowser, link, link => {
         content.document.getElementById(link).click();
       });
     }
 
     function testLink(link, name, next) {
       addWindowListener("chrome://mozapps/content/downloads/unknownContentType.xul", function (win) {
         ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
-          return content.document.getElementById("unload-flag").textContent;
-        }).then(unloadFlag => {
-          is(unloadFlag, "Okay", "beforeunload shouldn't have fired");
+          Assert.equal(content.document.getElementById("unload-flag").textContent,
+            "Okay", "beforeunload shouldn't have fired");
+        }).then(() => {
           is(win.document.getElementById("location").value, name, "file name should match");
           win.close();
           next();
         });
       });
 
       ContentTask.spawn(gBrowser.selectedBrowser, link, link => {
         content.document.getElementById(link).click();
--- a/browser/base/content/test/general/browser_bug734076.js
+++ b/browser/base/content/test/general/browser_bug734076.js
@@ -19,18 +19,19 @@ add_task(function* ()
         return ContentTask.spawn(gBrowser.selectedBrowser, { writeDomainURL: writeDomainURL }, function* (arg) {
           let contentBody = content.document.body;
           contentBody.style.backgroundImage = "url('" + arg.writeDomainURL + "')";
 
           return "context-viewbgimage";
         });
       },
       verify: function () {
-        return ContentTask.spawn(gBrowser.selectedBrowser, { }, function* (arg) {
-          return [content.document.body.textContent, "no domain was inherited for view background image"];
+        return ContentTask.spawn(gBrowser.selectedBrowser, null, function* (arg) {
+          Assert.ok(!content.document.body.textContent,
+            "no domain was inherited for view background image");
         });
       }
     },
     {
       name: "view image",
       url: "http://mochi.test:8888/",
       element: "img",
       go: function () {
@@ -39,18 +40,19 @@ add_task(function* ()
           let img = doc.createElement("img");
           img.setAttribute("src", arg.writeDomainURL);
           doc.body.insertBefore(img, doc.body.firstChild);
 
           return "context-viewimage";
         });
       },
       verify: function () {
-        return ContentTask.spawn(gBrowser.selectedBrowser, { }, function* (arg) {
-          return [content.document.body.textContent, "no domain was inherited for view image"];
+        return ContentTask.spawn(gBrowser.selectedBrowser, null, function* (arg) {
+          Assert.ok(!content.document.body.textContent,
+            "no domain was inherited for view image");
         });
       }
     },
     {
       name: "show only this frame",
       url: "http://mochi.test:8888/",
       element: "iframe",
       go: function () {
@@ -65,18 +67,19 @@ add_task(function* ()
             iframe.addEventListener("load", function onload() {
               iframe.removeEventListener("load", onload, true);
               resolve("context-showonlythisframe");
             }, true);
           });
         });
       },
       verify: function () {
-        return ContentTask.spawn(gBrowser.selectedBrowser, { writeDomainURL: writeDomainURL }, function* (arg) {
-          return [content.document.body.textContent, "no domain was inherited for 'show only this frame'"];
+        return ContentTask.spawn(gBrowser.selectedBrowser, null, function* (arg) {
+          Assert.ok(!content.document.body.textContent,
+            "no domain was inherited for 'show only this frame'");
         });
       }
     }
   ];
 
   let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
 
   for (let test of tests) {
@@ -91,18 +94,17 @@ add_task(function* ()
     yield BrowserTestUtils.synthesizeMouse(test.element, 3, 3,
           { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser);
     yield popupShownPromise;
 
     let loadedAfterCommandPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
     document.getElementById(commandToRun).click();
     yield loadedAfterCommandPromise;
 
-    let result = yield test.verify();
-    ok(!result[0], result[1]);
+    yield test.verify();
 
     let popupHiddenPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popuphidden");
     contentAreaContextMenu.hidePopup();
     yield popupHiddenPromise;
   }
 
   gBrowser.removeCurrentTab();
 });
--- a/browser/base/content/test/general/browser_clipboard.js
+++ b/browser/base/content/test/general/browser_clipboard.js
@@ -1,12 +1,13 @@
 // This test is used to check copy and paste in editable areas to ensure that non-text
 // types (html and images) are copied to and pasted from the clipboard properly.
 
-var testPage = "<body style='margin: 0'><img id='img' tabindex='1' src='http://example.org/browser/browser/base/content/test/general/moz.png'>" +
+var testPage = "<body style='margin: 0'>" +
+               "  <img id='img' tabindex='1' src='http://example.org/browser/browser/base/content/test/general/moz.png'>" +
                "  <div id='main' contenteditable='true'>Test <b>Bold</b> After Text</div>" +
                "</body>";
 
 add_task(function*() {
   let tab = gBrowser.addTab();
   let browser = gBrowser.getBrowserForTab(tab);
 
   gBrowser.selectedTab = tab;
@@ -18,77 +19,71 @@ add_task(function*() {
                    Components.interfaces.nsIDOMWindowUtils.MODIFIER_META :
                    Components.interfaces.nsIDOMWindowUtils.MODIFIER_CONTROL;
 
   // On windows, HTML clipboard includes extra data.
   // The values are from widget/windows/nsDataObj.cpp.
   const htmlPrefix = (navigator.platform.indexOf("Win") >= 0) ? "<html><body>\n<!--StartFragment-->" : "";
   const htmlPostfix = (navigator.platform.indexOf("Win") >= 0) ? "<!--EndFragment-->\n</body>\n</html>" : "";
 
-  let results = yield ContentTask.spawn(browser, { modifier: modifier, htmlPrefix: htmlPrefix, htmlPostfix: htmlPostfix },
-                                        function* (arg) {
+  yield ContentTask.spawn(browser, { modifier, htmlPrefix, htmlPostfix }, function* (arg) {
     var doc = content.document;
     var main = doc.getElementById("main");
     main.focus();
 
     const utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                          .getInterface(Components.interfaces.nsIDOMWindowUtils);
 
     const modifier = arg.modifier;
-    function sendKey(key)
-    {
-     if (utils.sendKeyEvent("keydown", key, 0, modifier)) {
-       utils.sendKeyEvent("keypress", key, key.charCodeAt(0), modifier);
-     }
-     utils.sendKeyEvent("keyup", key, 0, modifier);
-    }
-
-    let results = [];
-    function is(l, r, v) {
-      results.push(((l === r) ? "PASSED" : "FAILED") + " got: " + l + " expected: " + r + " - " + v);
+    function sendKey(key) {
+      if (utils.sendKeyEvent("keydown", key, 0, modifier)) {
+        utils.sendKeyEvent("keypress", key, key.charCodeAt(0), modifier);
+      }
+      utils.sendKeyEvent("keyup", key, 0, modifier);
     }
 
     // Select an area of the text.
     let selection = doc.getSelection();
     selection.modify("move", "left", "line");
     selection.modify("move", "right", "character");
     selection.modify("move", "right", "character");
     selection.modify("move", "right", "character");
     selection.modify("extend", "right", "word");
     selection.modify("extend", "right", "word");
 
     yield new Promise((resolve, reject) => {
       addEventListener("copy", function copyEvent(event) {
         removeEventListener("copy", copyEvent, true);
         // The data is empty as the selection is copied during the event default phase.
-        is(event.clipboardData.mozItemCount, 0, "Zero items on clipboard");
+        Assert.equal(event.clipboardData.mozItemCount, 0, "Zero items on clipboard");
         resolve();
       }, true)
 
       sendKey("c");
     });
 
     selection.modify("move", "right", "line");
 
     yield new Promise((resolve, reject) => {
       addEventListener("paste", function copyEvent(event) {
         removeEventListener("paste", copyEvent, true);
         let clipboardData = event.clipboardData; 
-        is(clipboardData.mozItemCount, 1, "One item on clipboard");
-        is(clipboardData.types.length, 2, "Two types on clipboard");
-        is(clipboardData.types[0], "text/html", "text/html on clipboard");
-        is(clipboardData.types[1], "text/plain", "text/plain on clipboard");
-        is(clipboardData.getData("text/html"), arg.htmlPrefix + "t <b>Bold</b>" + arg.htmlPostfix, "text/html value");
-        is(clipboardData.getData("text/plain"), "t Bold", "text/plain value");
+        Assert.equal(clipboardData.mozItemCount, 1, "One item on clipboard");
+        Assert.equal(clipboardData.types.length, 2, "Two types on clipboard");
+        Assert.equal(clipboardData.types[0], "text/html", "text/html on clipboard");
+        Assert.equal(clipboardData.types[1], "text/plain", "text/plain on clipboard");
+        Assert.equal(clipboardData.getData("text/html"), arg.htmlPrefix +
+          "t <b>Bold</b>" + arg.htmlPostfix, "text/html value");
+        Assert.equal(clipboardData.getData("text/plain"), "t Bold", "text/plain value");
         resolve();
       }, true)
       sendKey("v");
     });
 
-    is(main.innerHTML, "Test <b>Bold</b> After Textt <b>Bold</b>", "Copy and paste html");
+    Assert.equal(main.innerHTML, "Test <b>Bold</b> After Textt <b>Bold</b>", "Copy and paste html");
 
     selection.modify("extend", "left", "word");
     selection.modify("extend", "left", "word");
     selection.modify("extend", "left", "character");
 
     yield new Promise((resolve, reject) => {
       addEventListener("cut", function copyEvent(event) {
         removeEventListener("cut", copyEvent, true);
@@ -102,37 +97,32 @@ add_task(function*() {
     });
 
     selection.modify("move", "left", "line");
 
     yield new Promise((resolve, reject) => {
       addEventListener("paste", function copyEvent(event) {
         removeEventListener("paste", copyEvent, true);
         let clipboardData = event.clipboardData; 
-        is(clipboardData.mozItemCount, 1, "One item on clipboard 2");
-        is(clipboardData.types.length, 2, "Two types on clipboard 2");
-        is(clipboardData.types[0], "text/html", "text/html on clipboard 2");
-        is(clipboardData.types[1], "text/plain", "text/plain on clipboard 2");
-        is(clipboardData.getData("text/html"), arg.htmlPrefix + "<i>Italic</i> " + arg.htmlPostfix, "text/html value 2");
-        is(clipboardData.getData("text/plain"), "Some text", "text/plain value 2");
+        Assert.equal(clipboardData.mozItemCount, 1, "One item on clipboard 2");
+        Assert.equal(clipboardData.types.length, 2, "Two types on clipboard 2");
+        Assert.equal(clipboardData.types[0], "text/html", "text/html on clipboard 2");
+        Assert.equal(clipboardData.types[1], "text/plain", "text/plain on clipboard 2");
+        Assert.equal(clipboardData.getData("text/html"), arg.htmlPrefix +
+          "<i>Italic</i> " + arg.htmlPostfix, "text/html value 2");
+        Assert.equal(clipboardData.getData("text/plain"), "Some text", "text/plain value 2");
         resolve();
       }, true)
       sendKey("v");
     });
 
-    is(main.innerHTML, "<i>Italic</i> Test <b>Bold</b> After<b></b>", "Copy and paste html 2");
-
-    return results;
+    Assert.equal(main.innerHTML, "<i>Italic</i> Test <b>Bold</b> After<b></b>",
+      "Copy and paste html 2");
   });
 
-  is(results.length, 15, "Correct number of results");
-  for (var t = 0; t < results.length; t++) {
-    ok(results[t].startsWith("PASSED"), results[t]);
-  }
-
   // Next, check that the Copy Image command works.
 
   // The context menu needs to be opened to properly initialize for the copy
   // image command to run.
   let contextMenu = document.getElementById("contentAreaContextMenu");
   let contextMenuShown = promisePopupShown(contextMenu);
   BrowserTestUtils.synthesizeMouseAtCenter("#img", { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser);
   yield contextMenuShown;
@@ -140,18 +130,17 @@ add_task(function*() {
   document.getElementById("context-copyimage-contents").doCommand();
 
   contextMenu.hidePopup();
   yield promisePopupHidden(contextMenu);
 
   // Focus the content again
   yield SimpleTest.promiseFocus(browser.contentWindowAsCPOW);
 
-  let expectedContent = yield ContentTask.spawn(browser, { modifier: modifier, htmlPrefix: htmlPrefix, htmlPostfix: htmlPostfix },
-                                                function* (arg) {
+  yield ContentTask.spawn(browser, { modifier, htmlPrefix, htmlPostfix }, function* (arg) {
     var doc = content.document;
     var main = doc.getElementById("main");
     main.focus();
 
     yield new Promise((resolve, reject) => {
       addEventListener("paste", function copyEvent(event) {
         removeEventListener("paste", copyEvent, true);
         let clipboardData = event.clipboardData;
@@ -171,19 +160,17 @@ add_task(function*() {
 
       const modifier = arg.modifier;
       if (utils.sendKeyEvent("keydown", "v", 0, modifier)) {
         utils.sendKeyEvent("keypress", "v", "v".charCodeAt(0), modifier);
       }
       utils.sendKeyEvent("keyup", "v", 0, modifier);
     });
 
-    // Return the new content which should now include an image.
-    return main.innerHTML;
+    // The new content should now include an image.
+    Assert.equal(main.innerHTML, '<i>Italic</i> <img id="img" tabindex="1" ' +
+      'src="http://example.org/browser/browser/base/content/test/general/moz.png">' +
+      'Test <b>Bold</b> After<b></b>', "Paste after copy image");
   });
 
-  is(expectedContent, '<i>Italic</i> <img id="img" tabindex="1" ' +
-                      'src="http://example.org/browser/browser/base/content/test/general/moz.png">' +
-                      'Test <b>Bold</b> After<b></b>', "Paste after copy image");
-
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/base/content/test/general/browser_contextmenu.js
+++ b/browser/base/content/test/general/browser_contextmenu.js
@@ -420,17 +420,17 @@ add_task(function* test_copylinkcommand(
         input.value = "";
       });
       document.commandDispatcher
               .getControllerForCommand("cmd_paste")
               .doCommand("cmd_paste");
       yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
         let doc = content.document;
         let input = doc.getElementById("test-input");
-        is(input.value, "http://mozilla.com/", "paste for command cmd_paste");
+        Assert.equal(input.value, "http://mozilla.com/", "paste for command cmd_paste");
       });
     }
   });
 });
 
 add_task(function* test_pagemenu() {
   yield test_contextmenu("#test-pagemenu",
     ["context-navigation",   null,
@@ -468,17 +468,17 @@ add_task(function* test_pagemenu() {
      "context-viewinfo",     true
     ],
     {postCheckContextMenuFn: function*() {
       let item = contextMenu.getElementsByAttribute("generateditemid", "1")[0];
       ok(item, "Got generated XUL menu item");
       item.doCommand();
       yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
         let pagemenu = content.document.getElementById("test-pagemenu");
-        ok(!pagemenu.hasAttribute("hopeless"), "attribute got removed");
+        Assert.ok(!pagemenu.hasAttribute("hopeless"), "attribute got removed");
       });
     }
   });
 });
 
 add_task(function* test_dom_full_screen() {
   yield test_contextmenu("#test-dom-full-screen",
     ["context-navigation",           null,
@@ -895,14 +895,14 @@ function* test_contextmenu(selector, men
 function* selectText(selector) {
   yield ContentTask.spawn(gBrowser.selectedBrowser, selector, function*(selector) {
     info(`Selecting text of ${selector}`);
     let doc = content.document;
     let win = doc.defaultView;
     win.getSelection().removeAllRanges();
     let div = doc.createRange();
     let element = doc.querySelector(selector);
-    ok(element, "Found element to select text from");
+    Assert.ok(element, "Found element to select text from");
     div.setStartBefore(element);
     div.setEndAfter(element);
     win.getSelection().addRange(div);
   });
 }
--- a/browser/base/content/test/general/browser_documentnavigation.js
+++ b/browser/base/content/test/general/browser_documentnavigation.js
@@ -112,20 +112,20 @@ add_task(function* ()
   yield* expectFocusOnF6(false, "main-window", gURLBar.inputField,
                                 false, "basic focus content page with button focused urlbar");
 
   // The document root should be focused, not the button
   yield* expectFocusOnF6(false, "html1", "html1",
                                 true, "basic focus again content page with button focused");
 
   // Check to ensure that the root element is focused
-  let match = yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* () {
-    return content.document.activeElement == content.document.documentElement;
+  yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* () {
+    Assert.ok(content.document.activeElement == content.document.documentElement,
+      "basic focus again content page with button focused child root is focused");
   });
-  ok(match, "basic focus again content page with button focused child root is focused");
 });
 
 // Open a second tab. Document focus should skip the background tab.
 add_task(function* ()
 {
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, testPage2);
 
   yield* expectFocusOnF6(false, "main-window", gURLBar.inputField,
--- a/browser/base/content/test/general/browser_locationBarCommand.js
+++ b/browser/base/content/test/general/browser_locationBarCommand.js
@@ -40,18 +40,17 @@ add_task(function* shift_left_click_test
   let win = yield newWindowPromise;
 
   // Wait for the initial browser to load.
   let browser = win.gBrowser.selectedBrowser;
   yield BrowserTestUtils.browserLoaded(browser);
 
   info("URL should be loaded in a new window");
   is(gURLBar.value, "", "Urlbar reverted to original value");
-  let childFocus = yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
-  ok(childFocus, "There should be no focused element");
+  yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
   is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused");
   is(win.gURLBar.textValue, TEST_VALUE, "New URL is loaded in new window");
 
   // Cleanup.
   yield promiseWindowClosed(win);
 });
 
 add_task(function* right_click_test() {
@@ -110,18 +109,17 @@ add_task(function* load_in_current_tab_t
 
     // Trigger a load and check it occurs in the current tab.
     let loadStartedPromise = promiseLoadStarted();
     triggerCommand(test.click || false, test.event || {});
     yield loadStartedPromise;
 
     info("URL should be loaded in the current tab");
     is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
-    let childFocus = yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
-    ok(childFocus, "There should be no focused element");
+    yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
     is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused");
     is(gBrowser.selectedTab, tab, "New URL was loaded in the current tab");
 
     // Cleanup.
     gBrowser.removeCurrentTab();
   }
 });
 
@@ -140,18 +138,17 @@ add_task(function* load_in_new_tab_test(
     // Trigger a load and check it occurs in the current tab.
     let tabSwitchedPromise = promiseNewTabSwitched();
     triggerCommand(test.click || false, test.event || {});
     yield tabSwitchedPromise;
 
     // Check the load occurred in a new tab.
     info("URL should be loaded in a new focused tab");
     is(gURLBar.inputField.value, TEST_VALUE, "Urlbar still has the value we entered");
-    let childFocus = yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
-    ok(childFocus, "There should be no focused element");
+    yield promiseCheckChildNoFocusedElement(gBrowser.selectedBrowser);
     is(document.activeElement, gBrowser.selectedBrowser, "Content window should be focused");
     isnot(gBrowser.selectedTab, tab, "New URL was loaded in a new tab");
 
     // Cleanup.
     gBrowser.removeCurrentTab();
     gBrowser.removeCurrentTab();
   }
 });
@@ -219,17 +216,18 @@ function promiseWaitForNewWindow() {
 
     Services.wm.addListener(listener);
   });
 }
 
 function promiseCheckChildNoFocusedElement(browser)
 {
   if (!gMultiProcessBrowser) {
-    return Services.focus.focusedElement == null;
+    Assert.equal(Services.focus.focusedElement, null, "There should be no focused element");
+    return;
   }
 
   return ContentTask.spawn(browser, { }, function* () {
     const fm = Components.classes["@mozilla.org/focus-manager;1"].
                           getService(Components.interfaces.nsIFocusManager);
-    return fm.focusedElement == null;
+    Assert.equal(fm.focusedElement, null, "There should be no focused element");
   });
 }
--- a/browser/base/content/test/general/browser_locationBarExternalLoad.js
+++ b/browser/base/content/test/general/browser_locationBarExternalLoad.js
@@ -40,32 +40,26 @@ function testURL(url, loadFunc, endFunc)
   yield tabSwitchedPromise;
   yield pageshowPromise;
 
   let pagePrincipal = gBrowser.contentPrincipal;
   loadFunc(url);
 
   yield promiseWaitForEvent(browser, "pageshow");
 
-  let focused = yield ContentTask.spawn(browser, { isRemote: gMultiProcessBrowser },
+  yield ContentTask.spawn(browser, { isRemote: gMultiProcessBrowser },
     function* (arg) {
       const fm = Components.classes["@mozilla.org/focus-manager;1"].
                             getService(Components.interfaces.nsIFocusManager);
-      if (fm.focusedElement != null) {
-        return "FAIL - focusedElement not null";
-      }
+      Assert.equal(fm.focusedElement, null, "focusedElement not null");
 
       if (arg.isRemote) {
-        return fm.activeWindow == content ? "PASS" :
-                                            "FAIL - activeWindow not correct";
+        Assert.equal(fm.activeWindow, content, "activeWindow not correct");
       }
-
-      return "PASS";
   });
 
-  is(focused, "PASS", "should be no focused element");
   is(document.activeElement, browser, "content window should be focused");
 
   ok(!gBrowser.contentPrincipal.equals(pagePrincipal),
      "load of " + url + " by " + loadFunc.name + " should produce a page with a different principal");
 
   gBrowser.removeTab(tab);
 }
--- a/browser/base/content/test/general/browser_purgehistory_clears_sh.js
+++ b/browser/base/content/test/general/browser_purgehistory_clears_sh.js
@@ -13,30 +13,26 @@ add_task(function* purgeHistoryTest() {
 
     ok(!browser.webNavigation.canGoBack,
        "Initial value for webNavigation.canGoBack");
     ok(!browser.webNavigation.canGoForward,
        "Initial value for webNavigation.canGoBack");
     ok(backButton.hasAttribute("disabled"), "Back button is disabled");
     ok(forwardButton.hasAttribute("disabled"), "Forward button is disabled");
 
-    let pushState = ContentTask.spawn(browser, null, function*() {
+    yield ContentTask.spawn(browser, null, function*() {
       let startHistory = content.history.length;
       content.history.pushState({}, "");
       content.history.pushState({}, "");
       content.history.back();
       let newHistory = content.history.length;
-      return [startHistory, newHistory];
+      Assert.equal(startHistory, 1, "Initial SHistory size");
+      Assert.equal(newHistory, 3, "New SHistory size");
     });
 
-    let [startHistory, newHistory] = yield pushState;
-
-    is(startHistory, 1, "Initial SHistory size");
-    is(newHistory, 3, "New SHistory size");
-
     ok(browser.webNavigation.canGoBack, true,
        "New value for webNavigation.canGoBack");
     ok(browser.webNavigation.canGoForward, true,
        "New value for webNavigation.canGoForward");
     ok(!backButton.hasAttribute("disabled"), "Back button was enabled");
     ok(!forwardButton.hasAttribute("disabled"), "Forward button was enabled");
 
 
@@ -45,22 +41,20 @@ add_task(function* purgeHistoryTest() {
       .getService(Ci.mozIJSSubScriptLoader)
       .loadSubScript("chrome://browser/content/sanitize.js", tmp);
 
     let {Sanitizer} = tmp;
     let sanitizer = new Sanitizer();
 
     yield sanitizer.sanitize(["history"]);
 
-    let historyAfterPurge = yield ContentTask.spawn(browser, null, function*() {
-      return content.history.length;
+    yield ContentTask.spawn(browser, null, function*() {
+      Assert.equal(content.history.length, 1, "SHistory correctly cleared");
     });
 
-    is(historyAfterPurge, 1, "SHistory correctly cleared");
-
     ok(!browser.webNavigation.canGoBack,
        "webNavigation.canGoBack correctly cleared");
     ok(!browser.webNavigation.canGoForward,
        "webNavigation.canGoForward correctly cleared");
     ok(backButton.hasAttribute("disabled"), "Back button was disabled");
     ok(forwardButton.hasAttribute("disabled"), "Forward button was disabled");
   });
 });
--- a/browser/base/content/test/general/browser_refreshBlocker.js
+++ b/browser/base/content/test/general/browser_refreshBlocker.js
@@ -17,18 +17,18 @@ const PREF = "accessibility.blockautoref
  * @returns Promise
  */
 function* attemptFakeRefresh(browser, expectRefresh) {
   yield ContentTask.spawn(browser, expectRefresh, function*(expectRefresh) {
     let URI = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
     let refresher = docShell.QueryInterface(Ci.nsIRefreshURI);
     refresher.refreshURI(URI, 0, false, true);
 
-    is(refresher.refreshPending, expectRefresh,
-       "Got the right refreshPending state");
+    Assert.equal(refresher.refreshPending, expectRefresh,
+      "Got the right refreshPending state");
 
     if (refresher.refreshPending) {
       // Cancel the pending refresh
       refresher.cancelRefreshURITimers();
     }
 
     // The RefreshBlocker will wait until onLocationChange has
     // been fired before it will show any notifications (see bug
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -104,20 +104,20 @@ function doSelectTests(contentType, dtd)
 
   EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
   is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(3), "Select item 3 again");
   is(menulist.selectedIndex, isWindows ? 3 : 1, "Select item 3 selectedIndex");
 
   is((yield getChangeEvents()), 0, "Before closed - number of change events");
 
   EventUtils.synthesizeKey("a", { accelKey: true });
-  let selection = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
-    return String(content.getSelection());
+  yield ContentTask.spawn(gBrowser.selectedBrowser, { isWindows }, function(args) {
+    Assert.equal(String(content.getSelection()), args.isWindows ? "Text" : "",
+      "Select all while popup is open");
   });
-  is(selection, isWindows ? "Text" : "", "Select all while popup is open");
 
   yield hideSelectPopup(selectPopup);
 
   is(menulist.selectedIndex, 3, "Item 3 still selected");
   is((yield getChangeEvents()), 1, "After closed - number of change events");
 
   // Opening and closing the popup without changing the value should not fire a change event.
   yield openSelectPopup(selectPopup, true);
--- a/browser/base/content/test/general/browser_ssl_error_reports.js
+++ b/browser/base/content/test/general/browser_ssl_error_reports.js
@@ -132,21 +132,21 @@ function* testSendReportDisabled(testURL
   // Load the page.
   browser.loadURI(testURL);
   yield promiseErrorPageLoaded(browser);
 
   // Check that we loaded the right error page.
   yield checkErrorPage(browser, errorURISuffix);
 
   // Check that the error reporting section is hidden.
-  let hidden = yield ContentTask.spawn(browser, null, function* () {
+  yield ContentTask.spawn(browser, null, function* () {
     let section = content.document.getElementById("certificateErrorReporting");
-    return content.getComputedStyle(section).display == "none";
+    Assert.equal(content.getComputedStyle(section).display, "none",
+      "error reporting section should be hidden");
   });
-  ok(hidden, "error reporting section should be hidden");
 
   // Cleanup.
   gBrowser.removeTab(tab);
 }
 
 function isErrorStatus(status) {
   return status < 200 || status >= 300;
 }
@@ -162,14 +162,13 @@ function createReportResponseStatusPromi
         resolve(subject.responseStatus);
       }
     };
     Services.obs.addObserver(observer, "http-on-examine-response", false);
   });
 }
 
 function checkErrorPage(browser, suffix) {
-  return ContentTask.spawn(browser, null, function* () {
-    return content.document.documentURI;
-  }).then(uri => {
-    ok(uri.startsWith(`about:${suffix}`), "correct error page loaded");
+  return ContentTask.spawn(browser, { suffix }, function* (args) {
+    let uri = content.document.documentURI;
+    Assert.ok(uri.startsWith(`about:${args.suffix}`), "correct error page loaded");
   });
 }
--- a/browser/base/content/test/general/browser_trackingUI_6.js
+++ b/browser/base/content/test/general/browser_trackingUI_6.js
@@ -22,18 +22,18 @@ add_task(function* test_fetch() {
     SpecialPowers.pushPrefEnv({ set: [['privacy.trackingprotection.enabled', true]] },
                               resolve);
   });
 
   yield BrowserTestUtils.withNewTab({ gBrowser, url: URL }, function* (newTabBrowser) {
     let securityChange = waitForSecurityChange();
     yield ContentTask.spawn(newTabBrowser, null, function* () {
       yield content.wrappedJSObject.test_fetch()
-                   .then((response) => { ok(false, "should have denied the request"); })
-                   .catch((e) => { ok(true, `Caught exception: ${e}`); });
+                   .then(response => Assert.ok(false, "should have denied the request"))
+                   .catch(e => Assert.ok(true, `Caught exception: ${e}`));
     });
     yield securityChange;
 
     var TrackingProtection = newTabBrowser.ownerGlobal.TrackingProtection;
     ok(TrackingProtection, "got TP object");
     ok(TrackingProtection.enabled, "TP is enabled");
 
     is(TrackingProtection.content.getAttribute("state"), "blocked-tracking-content",
--- a/browser/base/content/test/general/browser_visibleFindSelection.js
+++ b/browser/base/content/test/general/browser_visibleFindSelection.js
@@ -21,22 +21,21 @@ add_task(function*() {
   EventUtils.synthesizeKey("v", {});
   yield scrollPromise;
 
   // Finds the div in the red box.
   scrollPromise = promiseWaitForEvent(gBrowser, "scroll");
   EventUtils.synthesizeKey("g", { accelKey: true });
   yield scrollPromise;
 
-  let scrollLeftPos = yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* (arg) {
-    return content.document.getElementById("s").getBoundingClientRect().left;
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+    Assert.ok(content.document.getElementById("s").getBoundingClientRect().left >= 0,
+      "scroll should include find result");
   });
 
-  ok(scrollLeftPos >= 0, "scroll should include find result");
-
   // clear the find bar
   EventUtils.synthesizeKey("a", { accelKey: true });
   EventUtils.synthesizeKey("VK_DELETE", { });
 
   gFindBar.close();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/base/content/test/newtab/browser_newtab_bug721442.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug721442.js
@@ -12,17 +12,17 @@ add_task(function* () {
   yield* addNewTabPageTab();
   yield* checkGrid("7p,8p,9p,0,1,2,3,4,5");
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
     function checkTooltip(aIndex, aExpected, aMessage) {
       let cell = content.gGrid.cells[aIndex];
 
       let link = cell.node.querySelector(".newtab-link");
-      is(link.getAttribute("title"), aExpected, aMessage);
+      Assert.equal(link.getAttribute("title"), aExpected, aMessage);
     }
 
     checkTooltip(0, "http://example7.com/", "1st tooltip is correct");
     checkTooltip(1, "title\nhttp://example8.com/", "2nd tooltip is correct");
     checkTooltip(2, "http://example9.com/", "3rd tooltip is correct");
   });
 });
 
--- a/browser/base/content/test/newtab/browser_newtab_bug723102.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug723102.js
@@ -10,16 +10,15 @@ add_task(function* () {
   let firstTab = gBrowser.selectedTab;
 
   yield* addNewTabPageTab();
   yield BrowserTestUtils.removeTab(firstTab);
 
   ok(NewTabUtils.allPages.enabled, "page is enabled");
   NewTabUtils.allPages.enabled = false;
 
-  let disabled = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    return content.gGrid.node.hasAttribute("page-disabled");
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+    Assert.ok(content.gGrid.node.hasAttribute("page-disabled"), "page is disabled");
   });
-  ok(disabled, "page is disabled");
 
   NewTabUtils.allPages.enabled = true;
 });
 
--- a/browser/base/content/test/newtab/browser_newtab_bug723121.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug723121.js
@@ -9,17 +9,17 @@ add_task(function* () {
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
     let grid = content.gGrid;
     let cell = grid.cells[0];
     let site = cell.site.node;
     let link = site.querySelector(".newtab-link");
 
     function checkGridLocked(aLocked, aMessage) {
-      is(grid.node.hasAttribute("locked"), aLocked, aMessage);
+      Assert.equal(grid.node.hasAttribute("locked"), aLocked, aMessage);
     }
 
     function sendDragEvent(aEventType, aTarget) {
       let dataTransfer = new content.DataTransfer(aEventType, false);
       let event = content.document.createEvent("DragEvents");
       event.initDragEvent(aEventType, true, true, content, 0, 0, 0, 0, 0,
                           false, false, false, false, 0, null, dataTransfer);
       aTarget.dispatchEvent(event);
--- a/browser/base/content/test/newtab/browser_newtab_bug991210.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug991210.js
@@ -16,20 +16,20 @@ add_task(function* () {
 
   // wait until about:newtab loads before calling provider callback
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab");
 
   afterLoadProvider.callback([]);
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
     let {_cellMargin, _cellHeight, _cellWidth, node} = content.gGrid;
-    isnot(_cellMargin, null, "grid has a computed cell margin");
-    isnot(_cellHeight, null, "grid has a computed cell height");
-    isnot(_cellWidth, null, "grid has a computed cell width");
+    Assert.notEqual(_cellMargin, null, "grid has a computed cell margin");
+    Assert.notEqual(_cellHeight, null, "grid has a computed cell height");
+    Assert.notEqual(_cellWidth, null, "grid has a computed cell width");
     let {height, maxHeight, maxWidth} = node.style;
-    isnot(height, "", "grid has a computed grid height");
-    isnot(maxHeight, "", "grid has a computed grid max-height");
-    isnot(maxWidth, "", "grid has a computed grid max-width");
+    Assert.notEqual(height, "", "grid has a computed grid height");
+    Assert.notEqual(maxHeight, "", "grid has a computed grid max-height");
+    Assert.notEqual(maxWidth, "", "grid has a computed grid max-width");
   });
 
   // restore original state
   NewTabUtils.links.removeProvider(afterLoadProvider);
 });
--- a/browser/base/content/test/newtab/browser_newtab_disable.js
+++ b/browser/base/content/test/newtab/browser_newtab_disable.js
@@ -29,20 +29,20 @@ add_task(function* () {
 
   // create a second new tab page and make sure it's disabled. enable it
   // again and check if the former page gets enabled as well.
   yield* addNewTabPageTab();
   isDisabled = yield isGridDisabled(firstTab.linkedBrowser);
   ok(isDisabled, "page is disabled");
 
   // check that no sites have been rendered
-  let sitesLength = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
-    return content.document.querySelectorAll(".site").length;
+  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
+    Assert.equal(content.document.querySelectorAll(".site").length, 0,
+      "no sites have been rendered");
   });
-  is(0, sitesLength, "no sites have been rendered");
 
   NewTabUtils.allPages.enabled = true;
 
   isDisabled = yield isGridDisabled();
   ok(!isDisabled, "page is not disabled");
 
   isDisabled = yield isGridDisabled(firstTab.linkedBrowser);
   ok(!isDisabled, "old page is not disabled");
--- a/browser/base/content/test/newtab/browser_newtab_drop_preview.js
+++ b/browser/base/content/test/newtab/browser_newtab_drop_preview.js
@@ -23,17 +23,17 @@ add_task(function* () {
 
     sites = sites.slice(0, 9);
     return sites.map(function (aSite) {
       if (!aSite)
         return "";
 
       let pinned = aSite.isPinned();
       if (pinned != aSite.node.hasAttribute("pinned")) {
-        ok(false, "invalid state (site.isPinned() != site[pinned])");
+        Assert.ok(false, "invalid state (site.isPinned() != site[pinned])");
       }
 
       return aSite.url.replace(/^http:\/\/example(\d+)\.com\/$/, "$1") + (pinned ? "p" : "");
     });
   });
 
   let expectedSites = "3,1p,2p,4,0p,5p,6,7,8"
   is(foundSites, expectedSites, "grid status = " + expectedSites);
--- a/browser/base/content/test/newtab/browser_newtab_search.js
+++ b/browser/base/content/test/newtab/browser_newtab_search.js
@@ -145,17 +145,17 @@ add_task(function* () {
 
     let input = content.document.getElementById("newtab-search-text");
     input.focus();
 
     info("Waiting for suggestions table to open");
     let observer = new content.MutationObserver(() => {
       if (input.getAttribute("aria-expanded") == "true") {
         observer.disconnect();
-        ok(!table.hidden, "Search suggestion table unhidden");
+        Assert.ok(!table.hidden, "Search suggestion table unhidden");
         sendAsyncMessage("test:newtab-suggestions-open", {});
       }
     });
     observer.observe(input, {
       attributes: true,
       attributeFilter: ["aria-expanded"],
     });
   });
@@ -168,39 +168,39 @@ add_task(function* () {
   // message.
   yield suggestionsOpenPromise;
   yield suggestionsPromise;
 
   // Empty the search input, causing the suggestions to be hidden.
   EventUtils.synthesizeKey("a", { accelKey: true });
   EventUtils.synthesizeKey("VK_DELETE", {});
 
-  let tableHidden = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    return content.document.getElementById("searchSuggestionTable").hidden;
+  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+    Assert.ok(content.document.getElementById("searchSuggestionTable").hidden,
+      "Search suggestion table hidden");
   });
-  ok(tableHidden, "Search suggestion table hidden");
 
   // Remove the search bar from toolbar
   CustomizableUI.removeWidgetFromArea("search-container");
   // Focus a different element than the search input from the page.
   yield BrowserTestUtils.synthesizeMouseAtCenter("#newtab-customize-button", { }, gBrowser.selectedBrowser);
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* () {
     let input = content.document.getElementById("newtab-search-text");
-    isnot(input, content.document.activeElement, "Search input should not be focused");
+    Assert.notEqual(input, content.document.activeElement, "Search input should not be focused");
   });
 
   // Test that Ctrl/Cmd + K will focus the input field from the page.
   let focusPromise = promiseSearchEvents(["FocusInput"]);
   EventUtils.synthesizeKey("k", { accelKey: true });
   yield focusPromise;
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* () {
     let input = content.document.getElementById("newtab-search-text");
-    is(input, content.document.activeElement, "Search input should be focused");
+    Assert.equal(input, content.document.activeElement, "Search input should be focused");
   });
 
   // Reset changes made to toolbar
   CustomizableUI.reset();
 
   // Test that Ctrl/Cmd + K will focus the search bar from toolbar.
   EventUtils.synthesizeKey("k", { accelKey: true });
   let searchBar = document.getElementById("searchbar");
@@ -221,19 +221,21 @@ add_task(function* () {
       resolve();
     }, true, true);
   });
 
   tab.linkedBrowser.loadURI("about:home");
   yield aboutHomeLoaded;
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* () {
-    is(content.document.documentURI.toLowerCase(), "about:home", "New tab's uri should be about:home");
+    Assert.equal(content.document.documentURI.toLowerCase(), "about:home",
+      "New tab's uri should be about:home");
     let searchInput = content.document.getElementById("searchText");
-    is(searchInput, content.document.activeElement, "Search input must be the selected element");
+    Assert.equal(searchInput, content.document.activeElement,
+      "Search input must be the selected element");
   });
 
   NewTabUtils.allPages.enabled = true;
   CustomizableUI.reset();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // Done.  Revert the current engine and remove the new engines.
   searchEventsPromise = promiseSearchEvents(["CurrentEngine"]);
@@ -292,13 +294,13 @@ function promiseNewSearchEngine({name: b
 
 function* checkCurrentEngine(engineInfo)
 {
   let engine = Services.search.currentEngine;
   ok(engine.name.includes(engineInfo.name),
      "Sanity check: current engine: engine.name=" + engine.name +
      " basename=" + engineInfo.name);
 
-  let engineName = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    return content.gSearch._contentSearchController.defaultEngine.name;
+  yield ContentTask.spawn(gBrowser.selectedBrowser, { name: engine.name }, function* (args) {
+    Assert.equal(content.gSearch._contentSearchController.defaultEngine.name,
+      args.name, "currentEngineName: " + args.name);
   });
-  is(engineName, engine.name, "currentEngineName: " + engine.name);
 }
--- a/browser/base/content/test/newtab/browser_newtab_sponsored_icon_click.js
+++ b/browser/base/content/test/newtab/browser_newtab_sponsored_icon_click.js
@@ -17,31 +17,37 @@ add_task(function* () {
 
     let site = cell.node.querySelector(".newtab-site");
     site.setAttribute("type", "sponsored");
 
     // test explain text appearing upon a click
     let sponsoredButton = site.querySelector(".newtab-sponsored");
     EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, content);
     let explain = site.querySelector(".sponsored-explain");
-    isnot(explain, null, "Sponsored explanation shown");
-    ok(explain.querySelector("input").classList.contains("newtab-control-block"), "sponsored tiles show blocked image");
-    ok(sponsoredButton.hasAttribute("active"), "Sponsored button has active attribute");
+    Assert.notEqual(explain, null, "Sponsored explanation shown");
+    Assert.ok(explain.querySelector("input").classList.contains("newtab-control-block"),
+      "sponsored tiles show blocked image");
+    Assert.ok(sponsoredButton.hasAttribute("active"), "Sponsored button has active attribute");
 
     // test dismissing sponsored explain
     EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, content);
-    is(site.querySelector(".sponsored-explain"), null, "Sponsored explanation no longer shown");
-    ok(!sponsoredButton.hasAttribute("active"), "Sponsored button does not have active attribute");
+    Assert.equal(site.querySelector(".sponsored-explain"), null,
+      "Sponsored explanation no longer shown");
+    Assert.ok(!sponsoredButton.hasAttribute("active"),
+      "Sponsored button does not have active attribute");
 
     // test with enhanced tile
     site.setAttribute("type", "enhanced");
     EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, content);
     explain = site.querySelector(".sponsored-explain");
-    isnot(explain, null, "Sponsored explanation shown");
-    ok(explain.querySelector("input").classList.contains("newtab-customize"), "enhanced tiles show customize image");
-    ok(sponsoredButton.hasAttribute("active"), "Sponsored button has active attribute");
+    Assert.notEqual(explain, null, "Sponsored explanation shown");
+    Assert.ok(explain.querySelector("input").classList.contains("newtab-customize"),
+      "enhanced tiles show customize image");
+    Assert.ok(sponsoredButton.hasAttribute("active"), "Sponsored button has active attribute");
 
     // test dismissing enhanced explain
     EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, content);
-    is(site.querySelector(".sponsored-explain"), null, "Sponsored explanation no longer shown");
-    ok(!sponsoredButton.hasAttribute("active"), "Sponsored button does not have active attribute");
+    Assert.equal(site.querySelector(".sponsored-explain"), null,
+      "Sponsored explanation no longer shown");
+    Assert.ok(!sponsoredButton.hasAttribute("active"),
+      "Sponsored button does not have active attribute");
   });
 });
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -328,40 +328,39 @@ function* addNewTabPageTab() {
  * Compares the current grid arrangement with the given pattern.
  * @param the pattern (see below)
  *
  * Example: checkGrid("3p,2,,1p")
  * Result: We expect the first cell to contain the pinned site 'http://example3.com/'.
  *         The second cell contains 'http://example2.com/'. The third cell is empty.
  *         The fourth cell contains the pinned site 'http://example4.com/'.
  */
-function* checkGrid(aSitesPattern) {
-  let length = aSitesPattern.split(",").length;
+function* checkGrid(pattern) {
+  let length = pattern.split(",").length;
 
-  let foundPattern = 
-    yield ContentTask.spawn(gWindow.gBrowser.selectedBrowser,
-                            { length: length }, function* (args) {
-      let grid = content.wrappedJSObject.gGrid;
+  yield ContentTask.spawn(gWindow.gBrowser.selectedBrowser,
+                          { length, pattern }, function* (args) {
+    let grid = content.wrappedJSObject.gGrid;
+
+    let sites = grid.sites.slice(0, args.length);
+    let foundPattern = sites.map(function (aSite) {
+      if (!aSite)
+        return "";
 
-      let sites = grid.sites.slice(0, args.length);
-      return sites.map(function (aSite) {
-        if (!aSite)
-          return "";
+      let pinned = aSite.isPinned();
+      let hasPinnedAttr = aSite.node.hasAttribute("pinned");
 
-        let pinned = aSite.isPinned();
-        let hasPinnedAttr = aSite.node.hasAttribute("pinned");
+      if (pinned != hasPinnedAttr)
+        ok(false, "invalid state (site.isPinned() != site[pinned])");
 
-        if (pinned != hasPinnedAttr)
-          ok(false, "invalid state (site.isPinned() != site[pinned])");
+      return aSite.url.replace(/^http:\/\/example(\d+)\.com\/$/, "$1") + (pinned ? "p" : "");
+    });
 
-        return aSite.url.replace(/^http:\/\/example(\d+)\.com\/$/, "$1") + (pinned ? "p" : "");
-      });
+    Assert.equal(foundPattern, args.pattern, "grid status = " + args.pattern);
   });
-
-  is(foundPattern, aSitesPattern, "grid status = " + aSitesPattern);
 }
 
 /**
  * Blocks a site from the grid.
  * @param aIndex The cell index.
  */
 function blockCell(aIndex) {
   return new Promise(resolve => {
--- a/browser/base/content/test/plugins/browser_CTP_hide_overlay.js
+++ b/browser/base/content/test/plugins/browser_CTP_hide_overlay.js
@@ -25,46 +25,44 @@ add_task(function* () {
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
 
   // Tests that the overlay can be hidden for plugins using the close icon.
-  let overlayIsVisible = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
     let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon")
     let bounds = closeIcon.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
 
-    return overlay.classList.contains("visible");
+    Assert.ok(!overlay.classList.contains("visible"), "overlay should be hidden.");
   });
-
-  ok(!overlayIsVisible, "overlay should be hidden.");
 });
 
 // Test that the overlay cannot be interacted with after the user closes the overlay
 add_task(function* () {
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gBrowser.selectedBrowser);
 
-  let overlayHidden = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
     let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon")
     let closeIconBounds = closeIcon.getBoundingClientRect();
     let overlayBounds = overlay.getBoundingClientRect();
     let overlayLeft = (overlayBounds.left + overlayBounds.right) / 2;
     let overlayTop = (overlayBounds.left + overlayBounds.right) / 2 ;
@@ -75,16 +73,16 @@ add_task(function* () {
     // Simulate clicking on the close icon.
     utils.sendMouseEvent("mousedown", closeIconLeft, closeIconTop, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", closeIconLeft, closeIconTop, 0, 1, 0, false, 0, 0);
 
     // Simulate clicking on the overlay.
     utils.sendMouseEvent("mousedown", overlayLeft, overlayTop, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", overlayLeft, overlayTop, 0, 1, 0, false, 0, 0);
 
-    return overlay.hasAttribute("dismissed") && !overlay.classList.contains("visible");
+    Assert.ok(overlay.hasAttribute("dismissed") && !overlay.classList.contains("visible"),
+      "Overlay should be hidden");
   });
 
   let notification = PopupNotifications.getNotification("click-to-play-plugins");
 
   ok(notification.dismissed, "No notification should be shown");
-  ok(overlayHidden, "Overlay should be hidden");
 });
--- a/browser/base/content/test/plugins/browser_CTP_iframe.js
+++ b/browser/base/content/test/plugins/browser_CTP_iframe.js
@@ -20,35 +20,29 @@ add_task(function* () {
 
   Services.prefs.setBoolPref("plugins.click_to_play", true);
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_iframe.html");
 
   // Tests that the overlays are visible and actionable if the plugin is in an iframe.
 
-  let result = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
     let frame = content.document.getElementById("frame");
     let doc = frame.contentDocument;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return plugin && overlay.classList.contains("visible");
-  });
-  ok(result, "Test 1, Plugin overlay should exist, not be hidden");
+    Assert.ok(plugin && overlay.classList.contains("visible"),
+      "Test 1, Plugin overlay should exist, not be hidden");
 
-  result = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    let frame = content.document.getElementById("frame");
-    let doc = frame.contentDocument;
-    let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
     let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon");
     let bounds = closeIcon.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = doc.defaultView.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
-    return overlay.classList.contains("visible");
+    Assert.ok(!overlay.classList.contains("visible"),
+      "Test 1, Plugin overlay should exist, be hidden");
   });
-  ok(!result, "Test 1, Plugin overlay should exist, be hidden");
 });
 
--- a/browser/base/content/test/plugins/browser_CTP_notificationBar.js
+++ b/browser/base/content/test/plugins/browser_CTP_notificationBar.js
@@ -61,85 +61,82 @@ add_task(function* () {
 });
 
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_overlayed.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return plugin.pluginFallbackType;
+    Assert.equal(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
+      "Test 3b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
   });
-  is(result, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-     "Test 3b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
 
   let pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 1a, plugin should not be activated");
 
-  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(!(overlay && overlay.classList.contains("visible")),
+      "Test 3b, overlay should be hidden.");
   });
-  ok(!result, "Test 3b, overlay should be hidden.");
 });
 
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_positioned.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   // Expecting a plugin notification bar when plugins are overlaid offscreen.
   yield promisePopupNotification("click-to-play-plugins");
   yield promiseForNotificationBar("plugin-hidden", gTestBrowser);
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return plugin.pluginFallbackType;
+    Assert.equal(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
+      "Test 4b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
   });
-  is(result, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-     "Test 4b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
 
-  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(!(overlay && overlay.classList.contains("visible")),
+      "Test 4b, overlay should be hidden.");
   });
-  ok(!result, "Test 4b, overlay should be hidden.");
 });
 
 // Test that the notification bar is getting dismissed when directly activating plugins
 // via the doorhanger.
 
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_small.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   // Expecting a plugin notification bar when plugins are overlaid offscreen.
   yield promisePopupNotification("click-to-play-plugins");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return plugin.pluginFallbackType;
+    Assert.equal(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
+      "Test 6, Plugin should be click-to-play");
   });
-  is(result, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-     "Test 6, Plugin should be click-to-play");
 
   yield promisePopupNotification("click-to-play-plugins");
 
   let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 6, Should have a click-to-play notification");
 
   // simulate "always allow"
   yield promiseForNotificationShown(notification);
--- a/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
+++ b/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
@@ -48,23 +48,23 @@ add_task(function* () {
     doc.getElementById('container').appendChild(p);
   });
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   yield promisePopupNotification("click-to-play-plugins");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementById("test");
     let doc = content.document;
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(overlay && overlay.classList.contains("visible"),
+      "Test 2, overlay should be visible.");
   });
-  ok(result, "Test 2, overlay should be visible.");
 });
 
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_outsideScrollArea.html");
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let doc = content.document;
     let p = doc.createElement('embed');
@@ -77,23 +77,23 @@ add_task(function* () {
     doc.getElementById('container').appendChild(p);
   });
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   yield promisePopupNotification("click-to-play-plugins");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let plugin = content.document.getElementById("test");
     let doc = content.document;
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(overlay && overlay.classList.contains("visible"),
+      "Test 3, overlay should be visible.");
   });
-  ok(result, "Test 3, overlay should be visible.");
 });
 
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_outsideScrollArea.html");
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let doc = content.document;
     let p = doc.createElement('embed');
@@ -105,16 +105,16 @@ add_task(function* () {
 
     doc.getElementById('container').appendChild(p);
   });
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   yield promisePopupNotification("click-to-play-plugins");
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let plugin = content.document.getElementById("test");
     let doc = content.document;
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(!(overlay && overlay.classList.contains("visible")),
+      "Test 4, overlay should be hidden.");
   });
-  ok(!result, "Test 4, overlay should be hidden.");
 });
--- a/browser/base/content/test/plugins/browser_CTP_resize.js
+++ b/browser/base/content/test/plugins/browser_CTP_resize.js
@@ -37,94 +37,94 @@ add_task(function* () {
 });
 
 // Test that the overlay is hidden for "small" plugin elements and is shown
 // once they are resized to a size that can hold the overlay
 add_task(function* () {
   let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(popupNotification, "Test 2, Should have a click-to-play notification");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(!(overlay && overlay.classList.contains("visible")),
+      "Test 2, overlay should be hidden.");
   });
-  ok(!result, "Test 2, overlay should be hidden.");
 });
 
 add_task(function* () {
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementById("test");
     plugin.style.width = "300px";
   });
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(!(overlay && overlay.classList.contains("visible")),
+      "Test 3, overlay should be hidden.");
   });
-  ok(!result, "Test 3, overlay should be hidden.");
 });
 
 
 add_task(function* () {
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementById("test");
     plugin.style.height = "300px";
   });
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     content.document.getElementById("test").clientTop;
   });
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(overlay && overlay.classList.contains("visible"),
+      "Test 4, overlay should be visible.");
   });
-  ok(result, "Test 4, overlay should be visible.");
 });
 
 add_task(function* () {
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementById("test");
     plugin.style.width = "10px";
     plugin.style.height = "10px";
   });
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     content.document.getElementById("test").clientTop;
   });
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(!(overlay && overlay.classList.contains("visible")),
+      "Test 5, overlay should be hidden.");
   });
-  ok(!result, "Test 5, overlay should be hidden.");
 });
 
 add_task(function* () {
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementById("test");
     plugin.style.height = "300px";
     plugin.style.width = "300px";
   });
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     content.document.getElementById("test").clientTop;
   });
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(overlay && overlay.classList.contains("visible"),
+      "Test 6, overlay should be visible.");
   });
-  ok(result, "Test 6, overlay should be visible.");
 });
--- a/browser/base/content/test/plugins/browser_CTP_zoom.js
+++ b/browser/base/content/test/plugins/browser_CTP_zoom.js
@@ -44,19 +44,19 @@ add_task(function* () {
 add_task(function* () {
   for (let count = 0; count < 4; count++) {
 
     FullZoom.enlarge();
 
     // Reload the page
     yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_zoom.html");
     yield promiseUpdatePluginBindings(gTestBrowser);
-    let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+    yield ContentTask.spawn(gTestBrowser, { count }, function* (args) {
       let doc = content.document;
       let plugin = doc.getElementById("test");
       let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-      return overlay && overlay.classList.contains("visible");
+      Assert.ok(overlay && overlay.classList.contains("visible"),
+        "Overlay should be visible for zoom change count " + args.count);
     });
-    ok(result, "Overlay should be visible for zoom change count " + count);
   }
 });
 
 
--- a/browser/base/content/test/plugins/browser_blocking.js
+++ b/browser/base/content/test/plugins/browser_blocking.js
@@ -78,31 +78,27 @@ add_task(function* () {
 
   yield promisePopupNotification("click-to-play-plugins");
 
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE,
      "Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
   ok(!pluginInfo.activated, "Test 18a, Plugin should not be activated");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
-    let plugin = content.document.getElementById("test");
-    let doc = content.document;
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
-  });
-  ok(result, "Test 18a, Plugin overlay should exist, not be hidden");
-
-  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
+    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    Assert.ok(overlay && overlay.classList.contains("visible"),
+      "Test 18a, Plugin overlay should exist, not be hidden");
+
     let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
-    return updateLink.style.visibility != "hidden";
+    Assert.ok(updateLink.style.visibility != "hidden",
+      "Test 18a, Plugin should have an update link");
   });
-  ok(result, "Test 18a, Plugin should have an update link");
 
   let promise = waitForEvent(gBrowser.tabContainer, "TabOpen", null, true);
   let pluginUpdateURL = Services.urlFormatter.formatURLPref("plugins.update.url");
   info(pluginUpdateURL);
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
@@ -124,23 +120,23 @@ add_task(function* () {
 
 add_task(function* () {
   // clicking the update link should not activate the plugin
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE,
      "Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
   ok(!pluginInfo.activated, "Test 18b, Plugin should not be activated");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
+    Assert.ok(overlay && overlay.classList.contains("visible"),
+      "Test 18b, Plugin overlay should exist, not be hidden");
   });
-  ok(result, "Test 18b, Plugin overlay should exist, not be hidden");
 });
 
 // Tests a vulnerable plugin with no update
 add_task(function* () {
   updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
 
   yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginVulnerableNoUpdate.xml", gTestBrowser);
 
@@ -152,31 +148,27 @@ add_task(function* () {
   let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 18c, Should have a click-to-play notification");
 
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE,
      "Test 18c, plugin fallback type should be PLUGIN_VULNERABLE_NO_UPDATE");
   ok(!pluginInfo.activated, "Test 18c, Plugin should not be activated");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return overlay && overlay.classList.contains("visible");
-  });
-  ok(result, "Test 18c, Plugin overlay should exist, not be hidden");
+    Assert.ok(overlay && overlay.classList.contains("visible"),
+      "Test 18c, Plugin overlay should exist, not be hidden");
 
-  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
-    let doc = content.document;
-    let plugin = doc.getElementById("test");
     let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
-    return updateLink && updateLink.style.display != "block";
+    Assert.ok(updateLink && updateLink.style.display != "block",
+      "Test 18c, Plugin should not have an update link");
   });
-  ok(result, "Test 18c, Plugin should not have an update link");
 
   // check that click "Always allow" works with blocked plugins
   yield promiseForNotificationShown(notification);
 
   PopupNotifications.panel.firstChild._primaryButton.click();
 
   pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE,
@@ -337,22 +329,21 @@ add_task(function* () {
 
   // Since the plugin notification is dismissed by default, reshow it.
   yield promiseForNotificationShown(notification);
 
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED,
      "Test 26, plugin fallback type should be PLUGIN_BLOCKLISTED");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let plugin = content.document.getElementById("test");
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return objLoadingContent.activated;
+    Assert.ok(!objLoadingContent.activated, "Plugin should not be activated.");
   });
-  ok(!result, "Plugin should not be activated.");
 
   const testUrl = "http://test.url.com/";
 
   let firstPanelChild = PopupNotifications.panel.firstChild;
   let infoLink = document.getAnonymousElementByAttribute(firstPanelChild, "anonid",
     "click-to-play-plugins-notification-link");
   is(infoLink.href, testUrl,
     "Test 26, the notification URL needs to match the infoURL from the blocklist file.");
--- a/browser/base/content/test/plugins/browser_blocklist_content.js
+++ b/browser/base/content/test/plugins/browser_blocklist_content.js
@@ -36,37 +36,35 @@ add_task(function* () {
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let test = content.document.getElementById("test");
-    return test.activated;
+    Assert.ok(test.activated, "task 1a: test plugin should be activated!");
   });
-  ok(result, "task 1a: test plugin should be activated!");
 });
 
 // Load a fresh page, load a new plugin blocklist, then load the same page again.
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>GO!</html>");
   yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginHard.xml", gTestBrowser);
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let test = content.document.getElementById("test");
-    return test.activated;
+    ok(!test.activated, "task 2a: test plugin shouldn't activate!");
   });
-  ok(!result, "task 2a: test plugin shouldn't activate!");
 });
 
 // Unload the block list and lets do this again, only this time lets
 // hack around in the content blocklist service maliciously.
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>GO!</html>");
 
   yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
@@ -77,32 +75,30 @@ add_task(function* () {
   info("test 3a: loading " + gChromeRoot + "blocklist_proxy.js" + "\n");
   mm.loadFrameScript(gChromeRoot + "blocklist_proxy.js", true);
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let test = content.document.getElementById("test");
-    return test.activated;
+    Assert.ok(test.activated, "task 3a: test plugin should be activated!");
   });
-  ok(result, "task 3a: test plugin should be activated!");
 });
 
 // Load a fresh page, load a new plugin blocklist, then load the same page again.
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>GO!</html>");
   yield asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginHard.xml", gTestBrowser);
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let test = content.document.getElementById("test");
-    return test.activated;
+    Assert.ok(!test.activated, "task 4a: test plugin shouldn't activate!");
   });
-  ok(!result, "task 4a: test plugin shouldn't activate!");
 
   yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
 });
--- a/browser/base/content/test/plugins/browser_bug743421.js
+++ b/browser/base/content/test/plugins/browser_bug743421.js
@@ -45,83 +45,75 @@ add_task(function* () {
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
     new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin());
   });
 
   yield promisePopupNotification("click-to-play-plugins");
 });
 
 add_task(function* () {
-  let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementsByTagName("embed")[0];
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return objLoadingContent.activated;
+    Assert.ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated");
   });
-  ok(!isActivated, "Test 1b, Plugin should not be activated");
 
   // Click the activate button on doorhanger to make sure it works
   let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
 
   yield promiseForNotificationShown(notification);
 
   PopupNotifications.panel.firstChild._primaryButton.click();
 
-  isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementsByTagName("embed")[0];
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return objLoadingContent.activated;
+    Assert.ok(objLoadingContent.activated, "Test 1b, Plugin should be activated");
   });
-  ok(isActivated, "Test 1b, Plugin should be activated");
 });
 
 add_task(function* () {
   let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 1c, Should still have a click-to-play notification");
 
-  let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin());
     let plugin = content.document.getElementsByTagName("embed")[1];
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return objLoadingContent.activated;
+    Assert.ok(objLoadingContent.activated,
+      "Test 1c, Newly inserted plugin in activated page should be activated");
   });
-  ok(isActivated, "Test 1c, Newly inserted plugin in activated page should be activated");
 });
 
 add_task(function* () {
-  let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementsByTagName("embed")[1];
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return objLoadingContent.activated;
+    Assert.ok(objLoadingContent.activated, "Test 1d, Plugin should be activated");
   });
-  ok(isActivated, "Test 1d, Plugin should be activated");
 
   let promise = waitForEvent(gTestBrowser.contentWindow, "hashchange", null);
   gTestBrowser.contentWindow.location += "#anchorNavigation";
   yield promise;
 });
 
 add_task(function* () {
-  let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin());
     let plugin = content.document.getElementsByTagName("embed")[2];
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return objLoadingContent.activated;
+    Assert.ok(objLoadingContent.activated, "Test 1e, Plugin should be activated");
   });
-  ok(isActivated, "Test 1e, Plugin should be activated");
 });
 
 add_task(function* () {
-  let isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementsByTagName("embed")[2];
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return objLoadingContent.activated;
-  });
-  ok(isActivated, "Test 1f, Plugin should be activated");
+    Assert.ok(objLoadingContent.activated, "Test 1f, Plugin should be activated");
 
-  isActivated = yield ContentTask.spawn(gTestBrowser, {}, function* () {
     content.history.replaceState({}, "", "replacedState");
     new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin());
-    let plugin = content.document.getElementsByTagName("embed")[3];
-    let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    return objLoadingContent.activated;
+    plugin = content.document.getElementsByTagName("embed")[3];
+    objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+    Assert.ok(objLoadingContent.activated, "Test 1g, Plugin should be activated");
   });
-  ok(isActivated, "Test 1f, Plugin should not be activated");
 });
--- a/browser/base/content/test/plugins/browser_bug744745.js
+++ b/browser/base/content/test/plugins/browser_bug744745.js
@@ -32,20 +32,19 @@ add_task(function* () {
 
   gTestBrowser.addEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
 
   let testRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
   yield promiseTabLoadEvent(gBrowser.selectedTab, testRoot + "plugin_bug744745.html");
 
   yield promiseForCondition(function () { return gNumPluginBindingsAttached == 1; });
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, {}, function* () {
     let plugin = content.document.getElementById("test");
     if (!plugin) {
+      Assert.ok(false, "plugin element not available.");
       return false;
     }
     // We can't use MochiKit's routine
     let style = content.getComputedStyle(plugin);
-    return 'opacity' in style && style.opacity == 1;
+    Assert.ok(("opacity" in style) && style.opacity == 1, "plugin style properly configured.");
   });
-
-  ok(result, true, "plugin style properly configured.");
 });
--- a/browser/base/content/test/plugins/browser_bug812562.js
+++ b/browser/base/content/test/plugins/browser_bug812562.js
@@ -36,31 +36,31 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(popupNotification, "test part 1: Should have a click-to-play notification");
 
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "plugin should be marked as VULNERABLE");
 
-  let found = yield ContentTask.spawn(gTestBrowser, {}, function* () {
-    return !!content.document.getElementById("test");
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
+    Assert.ok(!!content.document.getElementById("test"),
+      "test part 1: plugin should not be activated");
   });
-  ok(found, "test part 1: plugin should not be activated");
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html></html>");
 });
 
 add_task(function* () {
   let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(!popupNotification, "test part 2: Should not have a click-to-play notification");
-  let found = yield ContentTask.spawn(gTestBrowser, {}, function* () {
-    return !!content.document.getElementById("test");
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
+    Assert.ok(!content.document.getElementById("test"),
+      "test part 2: plugin should not be activated");
   });
-  ok(!found, "test part 2: plugin should not be activated");
 
   let obsPromise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
   let overlayPromise = promisePopupNotification("click-to-play-plugins");
   gTestBrowser.contentWindow.history.back();
   yield obsPromise;
   yield overlayPromise;
 });
 
@@ -68,13 +68,13 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(popupNotification, "test part 3: Should have a click-to-play notification");
 
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "plugin should be marked as VULNERABLE");
 
-  let found = yield ContentTask.spawn(gTestBrowser, {}, function* () {
-    return !!content.document.getElementById("test");
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
+    Assert.ok(!!content.document.getElementById("test"),
+      "test part 3: plugin should not be activated");
   });
-  ok(found, "test part 3: plugin should not be activated");
 });
--- a/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js
+++ b/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js
@@ -54,30 +54,29 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   // Wait for the plugin to crash
   yield pluginCrashed;
 
   let crashReportStatus = TestUtils.topicObserved("crash-report-status", onSubmitStatus);
 
   // Test that the crash submission UI is actually visible and submit the crash report.
-  let crashUiVisible = yield ContentTask.spawn(gTestBrowser, config, function* (aConfig) {
+  yield ContentTask.spawn(gTestBrowser, config, function* (aConfig) {
     let doc = content.document;
     let plugin = doc.getElementById("plugin");
     let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
     let submitButton = doc.getAnonymousElementByAttribute(plugin, "anonid", "submitButton");
     // Test that we don't send the URL when urlOptIn is false.
     doc.getAnonymousElementByAttribute(plugin, "anonid", "submitURLOptIn").checked = aConfig.urlOptIn;
     submitButton.click();
-    return content.getComputedStyle(pleaseSubmit).display == "block";
+    Assert.equal(content.getComputedStyle(pleaseSubmit).display == "block",
+      aConfig.shouldSubmissionUIBeVisible, "The crash UI should be visible");
   });
 
   yield crashReportStatus;
-
-  is(crashUiVisible, config.shouldSubmissionUIBeVisible, "The crash UI should be visible");
 });
 
 add_task(function* () {
   config = {
     shouldSubmissionUIBeVisible: true,
     comment: "a test comment",
     urlOptIn: true
   };
@@ -92,31 +91,30 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   // Wait for the plugin to crash
   yield pluginCrashed;
 
   let crashReportStatus = TestUtils.topicObserved("crash-report-status", onSubmitStatus);
 
   // Test that the crash submission UI is actually visible and submit the crash report.
-  let crashUiVisible = yield ContentTask.spawn(gTestBrowser, config, function* (aConfig) {
+  yield ContentTask.spawn(gTestBrowser, config, function* (aConfig) {
     let doc = content.document;
     let plugin = doc.getElementById("plugin");
     let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
     let submitButton = doc.getAnonymousElementByAttribute(plugin, "anonid", "submitButton");
     // Test that we send the URL when urlOptIn is true.
     doc.getAnonymousElementByAttribute(plugin, "anonid", "submitURLOptIn").checked = aConfig.urlOptIn;
     doc.getAnonymousElementByAttribute(plugin, "anonid", "submitComment").value = aConfig.comment;
     submitButton.click();
-    return content.getComputedStyle(pleaseSubmit).display == "block";
+    Assert.equal(content.getComputedStyle(pleaseSubmit).display == "block",
+      aConfig.shouldSubmissionUIBeVisible, "The crash UI should be visible");
   });
 
   yield crashReportStatus;
-
-  is(crashUiVisible, config.shouldSubmissionUIBeVisible, "The crash UI should be visible");
 });
 
 add_task(function* () {
   config = {
     shouldSubmissionUIBeVisible: false,
     comment: "",
     urlOptIn: true
   };
@@ -131,24 +129,23 @@ add_task(function* () {
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   // Wait for the plugin to crash
   yield pluginCrashed;
 
   // Test that the crash submission UI is not visible and do not submit a crash report.
-  let crashUiVisible = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, config, function* (aConfig) {
     let doc = content.document;
     let plugin = doc.getElementById("plugin");
     let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
-    return !!pleaseSubmit && content.getComputedStyle(pleaseSubmit).display == "block";
+    Assert.equal(!!pleaseSubmit && content.getComputedStyle(pleaseSubmit).display == "block",
+      aConfig.shouldSubmissionUIBeVisible, "Plugin crash UI should not be visible");
   });
-
-  is(crashUiVisible, config.shouldSubmissionUIBeVisible, "Plugin crash UI should not be visible");
 });
 
 function promisePluginCrashed() {
   return new ContentTask.spawn(gTestBrowser, {}, function* () {
     yield new Promise((resolve) => {
       addEventListener("PluginCrashed", function onPluginCrashed() {
         removeEventListener("PluginCrashed", onPluginCrashed);
         resolve();
--- a/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
+++ b/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
@@ -138,28 +138,29 @@ add_task(function* testChromeHearsPlugin
                                   Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE);
 
   // Send the message down to PluginContent.jsm saying that the plugin has
   // crashed, and that we have a crash report.
   let mm = browser.messageManager;
   mm.sendAsyncMessage(CRASHED_MESSAGE,
                       { pluginName: "", runID, state: "please" });
 
-  let [gotExpected, msg] = yield ContentTask.spawn(browser, {}, function* () {
+  yield ContentTask.spawn(browser, null, function* () {
     // At this point, the content process should have heard the
     // plugin crash message from the parent, and we are OK to emit
     // the PluginCrashed event.
     let plugin = content.document.getElementById("plugin");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     let statusDiv = plugin.ownerDocument
                           .getAnonymousElementByAttribute(plugin, "anonid",
                                                           "submitStatus");
 
     if (statusDiv.getAttribute("status") == "please") {
-      return [false, "Did not expect plugin to be in crash report mode yet."];
+      Assert.ok(false, "Did not expect plugin to be in crash report mode yet.");
+      return;
     }
 
     // Now we need the plugin to seem crashed to PluginContent.jsm, without
     // actually crashing the plugin again. We hack around this by overriding
     // the pluginFallbackType again.
     Object.defineProperty(plugin, "pluginFallbackType", {
       get: function() {
         return Ci.nsIObjectLoadingContent.PLUGIN_CRASHED;
@@ -171,21 +172,19 @@ add_task(function* testChromeHearsPlugin
       pluginDumpID: "",
       browserDumpID: "",
       submittedCrashReport: false,
       bubbles: true,
       cancelable: true,
     });
 
     plugin.dispatchEvent(event);
-    return [statusDiv.getAttribute("status") == "please",
-            "Should have been showing crash report UI"];
+    Assert.equal(statusDiv.getAttribute("status"), "please",
+      "Should have been showing crash report UI");
   });
-
-  ok(gotExpected, msg);
   yield BrowserTestUtils.closeWindow(win);
 });
 
 /**
  * In this case, the content process hears about the crash first.
  */
 add_task(function* testContentHearsCrashFirst() {
   // Open a remote window so that we can run this test even if e10s is not
@@ -197,63 +196,59 @@ add_task(function* testContentHearsCrash
   yield BrowserTestUtils.browserLoaded(browser);
 
   // In this case, we want the <object> to match the -moz-handler-crashed
   // pseudoselector, and we want the plugin to seem crashed, since the
   // content process in this case has heard about the crash first.
   let runID = yield preparePlugin(browser,
                                   Ci.nsIObjectLoadingContent.PLUGIN_CRASHED);
 
-  let [gotExpected, msg] = yield ContentTask.spawn(browser, null, function* () {
+  yield ContentTask.spawn(browser, null, function* () {
     // At this point, the content process has not yet heard from the
     // parent about the crash report. Let's ensure that by making sure
     // we're not showing the plugin crash report UI.
     let plugin = content.document.getElementById("plugin");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     let statusDiv = plugin.ownerDocument
                           .getAnonymousElementByAttribute(plugin, "anonid",
                                                           "submitStatus");
 
     if (statusDiv.getAttribute("status") == "please") {
-      return [false, "Did not expect plugin to be in crash report mode yet."];
+      Assert.ok(false, "Did not expect plugin to be in crash report mode yet.");
     }
 
     let event = new content.PluginCrashedEvent("PluginCrashed", {
       pluginName: "",
       pluginDumpID: "",
       browserDumpID: "",
       submittedCrashReport: false,
       bubbles: true,
       cancelable: true,
     });
 
     plugin.dispatchEvent(event);
 
-    return [statusDiv.getAttribute("status") != "please",
-            "Should not yet be showing crash report UI"];
+    Assert.notEqual(statusDiv.getAttribute("status"), "please",
+      "Should not yet be showing crash report UI");
   });
 
-  ok(gotExpected, msg);
-
   // Now send the message down to PluginContent.jsm that the plugin has
   // crashed...
   let mm = browser.messageManager;
   mm.sendAsyncMessage(CRASHED_MESSAGE,
                       { pluginName: "", runID, state: "please"});
 
-  [gotExpected, msg] = yield ContentTask.spawn(browser, null, function* () {
+  yield ContentTask.spawn(browser, null, function* () {
     // At this point, the content process will have heard the message
     // from the parent and reacted to it. We should be showing the plugin
     // crash report UI now.
     let plugin = content.document.getElementById("plugin");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     let statusDiv = plugin.ownerDocument
                           .getAnonymousElementByAttribute(plugin, "anonid",
                                                           "submitStatus");
 
-    return [statusDiv.getAttribute("status") == "please",
-            "Should have been showing crash report UI"];
+    Assert.equal(statusDiv.getAttribute("status"), "please",
+      "Should have been showing crash report UI");
   });
 
-  ok(gotExpected, msg);
-
   yield BrowserTestUtils.closeWindow(win);
 });
--- a/browser/base/content/test/plugins/browser_plugin_infolink.js
+++ b/browser/base/content/test/plugins/browser_plugin_infolink.js
@@ -36,29 +36,29 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED,
      "Test 1a, plugin fallback type should be PLUGIN_DISABLED");
 
   // This test opens a new tab to about:addons
   let promise = waitForEvent(gBrowser.tabContainer, "TabOpen", null, true);
-  let success = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let pluginNode = content.document.getElementById("test");
     let manageLink = content.document.getAnonymousElementByAttribute(pluginNode, "anonid", "managePluginsLink");
     let bounds = manageLink.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
-    return true;
+    Assert.ok(true, "click on manage link");
   });
-  ok(success, "click on manage link");
+
   yield promise;
 
   promise = waitForEvent(gBrowser.tabContainer, "TabClose", null, true);
 
   // in-process page, no cpows here
   let condition = function() {
     let win = gBrowser.selectedBrowser.contentWindow;
     if (!!win && !!win.wrappedJSObject && !!win.wrappedJSObject.gViewController) {
--- a/browser/base/content/test/plugins/browser_plugin_reloading.js
+++ b/browser/base/content/test/plugins/browser_plugin_reloading.js
@@ -60,26 +60,26 @@ add_task(function* () {
   yield promisePlayObject("test");
 
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.displayedType, Ci.nsIObjectLoadingContent.TYPE_PLUGIN, "Test 3, plugin should have started");
   ok(pluginInfo.activated, "Test 4, plugin node should not be activated");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let plugin = content.document.getElementById("test");
     let npobj1 = Components.utils.waiveXrays(plugin).getObjectValue();
     plugin.src = plugin.src;
     let pluginsDiffer = false;
-     try {
-       Components.utils.waiveXrays(plugin).checkObjectValue(npobj1);
-     } catch (e) {
-       pluginsDiffer = true;
-     }
-     return pluginsDiffer;
+    try {
+      Components.utils.waiveXrays(plugin).checkObjectValue(npobj1);
+    } catch (e) {
+      pluginsDiffer = true;
+    }
+
+     Assert.ok(pluginsDiffer, "Test 5, plugins differ.");
   });
-  ok(result, "Test 5, plugins differ.");
 
   pluginInfo = yield promiseForPluginInfo("test");
   ok(pluginInfo.activated, "Test 6, Plugin should have retained activated state.");
   is(pluginInfo.displayedType, Ci.nsIObjectLoadingContent.TYPE_PLUGIN, "Test 7, plugin should have started");
 });
--- a/browser/base/content/test/plugins/browser_pluginnotification.js
+++ b/browser/base/content/test/plugins/browser_pluginnotification.js
@@ -79,17 +79,17 @@ add_task(function* () {
   yield promiseForNotificationShown(notification);
 
   PopupNotifications.panel.firstChild._primaryButton.click();
 
   pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Plugin should not be activated");
 
   // Simulate clicking the overlay
-  yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let bounds = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
@@ -153,23 +153,23 @@ add_task(function* () {
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_two_types.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   yield promisePopupNotification("click-to-play-plugins");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let test = content.document.getElementById("test");
     let secondtestA = content.document.getElementById("secondtestA");
     let secondtestB = content.document.getElementById("secondtestB");
-    return test.activated && !secondtestA.activated && !secondtestB.activated;
+    Assert.ok(test.activated && !secondtestA.activated && !secondtestB.activated,
+      "Content plugins are set up");
   });
-  ok(result, "Content plugins are set up");
 
   clearAllPluginPermissions();
 });
 
 // Tests that the plugin's "activated" property is true for working plugins
 // with click-to-play disabled.
 add_task(function* () {
   updateAllTestPlugins(Ci.nsIPluginTag.STATE_ENABLED);
@@ -185,23 +185,22 @@ add_task(function* () {
 
 // Tests that the overlay is shown instead of alternate content when
 // plugins are click to play.
 add_task(function* () {
   updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_alternate_content.html");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return !!mainBox;
+    Assert.ok(!!mainBox, "Test 15, Plugin overlay should exist");
   });
-  ok(result, "Test 15, Plugin overlay should exist");
 });
 
 // Tests that mContentType is used for click-to-play plugins, and not the
 // inspected type.
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_bug749455.html");
 
   // Work around for delayed PluginBindingAttached
@@ -221,17 +220,17 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated");
 
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed,
      "Test 19a, Doorhanger should start out dismissed");
 
-  yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let icon = doc.getAnonymousElementByAttribute(plugin, "class", "icon");
     let bounds = icon.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
@@ -251,17 +250,17 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated");
 
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed,
      "Test 19c, Doorhanger should start out dismissed");
 
-  yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let text = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgClickToPlay");
     let bounds = text.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
@@ -282,17 +281,17 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated");
 
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed,
      "Test 19e, Doorhanger should start out dismissed");
 
-  yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
     utils.sendMouseEvent("mousedown", 50, 50, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", 50, 50, 0, 1, 0, false, 0, 0);
   });
 
@@ -315,64 +314,63 @@ add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_hidden_to_visible.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 20a, Should have a click-to-play notification");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    return !!overlay;
+    Assert.ok(!!overlay, "Test 20a, Plugin overlay should exist");
   });
-  ok(result, "Test 20a, Plugin overlay should exist");
 
-  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
     let overlayRect = mainBox.getBoundingClientRect();
-    return overlayRect.width == 0 && overlayRect.height == 0;
+    Assert.ok(overlayRect.width == 0 && overlayRect.height == 0,
+      "Test 20a, plugin should have an overlay with 0px width and height");
   });
-  ok(result, "Test 20a, plugin should have an overlay with 0px width and height");
 
   let pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 20b, plugin should not be activated");
 
-  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let div = doc.getElementById("container");
-    return div.style.display == "none";
+    Assert.equal(div.style.display, "none",
+      "Test 20b, container div should be display: none");
   });
-  ok(result, "Test 20b, container div should be display: none");
 
-  yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let div = doc.getElementById("container");
     div.style.display = "block";
   });
 
-  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
     let overlayRect = mainBox.getBoundingClientRect();
-    return overlayRect.width == 200 && overlayRect.height == 200;
+    Assert.ok(overlayRect.width == 200 && overlayRect.height == 200,
+      "Test 20c, plugin should have overlay dims of 200px");
   });
-  ok(result, "Test 20c, plugin should have overlay dims of 200px");
 
   pluginInfo = yield promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 20b, plugin should not be activated");
 
   ok(notification.dismissed, "Test 20c, Doorhanger should start out dismissed");
 
-  yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let bounds = plugin.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
@@ -381,23 +379,23 @@ add_task(function* () {
 
   let condition = () => !notification.dismissed && !!PopupNotifications.panel.firstChild;
   yield promiseForCondition(condition);
   PopupNotifications.panel.firstChild._primaryButton.click();
 
   pluginInfo = yield promiseForPluginInfo("test");
   ok(pluginInfo.activated, "Test 20c, plugin should be activated");
 
-  result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlayRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
-    return overlayRect.width == 0 && overlayRect.height == 0;
+    Assert.ok(overlayRect.width == 0 && overlayRect.height == 0,
+      "Test 20c, plugin should have overlay dims of 0px");
   });
-  ok(result, "Test 20c, plugin should have overlay dims of 0px");
 
   clearAllPluginPermissions();
 });
 
 // Test having multiple different types of plugin on one page
 add_task(function* () {
   // contains three plugins, application/x-test, application/x-second-test x 2
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_two_types.html");
@@ -406,23 +404,23 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 21a, Should have a click-to-play notification");
 
   // confirm all three are blocked by ctp at this point
   let ids = ["test", "secondtestA", "secondtestB"];
   for (let id of ids) {
-    let result = yield ContentTask.spawn(gTestBrowser, {aId: id}, function* () {
+    yield ContentTask.spawn(gTestBrowser, { id }, function* (args) {
       let doc = content.document;
-      let plugin = doc.getElementById(arguments[0].aId);
+      let plugin = doc.getElementById(args.id);
       let overlayRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
-      return overlayRect.width == 200 && overlayRect.height == 200;
+      Assert.ok(overlayRect.width == 200 && overlayRect.height == 200,
+        "Test 21a, plugin " + args.id + " should have click-to-play overlay with dims");
     });
-    ok(result, "Test 21a, plugin " + id + " should have click-to-play overlay with dims");
 
     let pluginInfo = yield promiseForPluginInfo(id);
     ok(!pluginInfo.activated, "Test 21a, Plugin with id=" + id + " should not be activated");
   }
 
   notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 21a, Should have a click-to-play notification");
 
@@ -461,33 +459,34 @@ add_task(function* () {
 
   notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 21b, Should have a click-to-play notification");
 
   yield promiseForNotificationShown(notification);
 
   ok(notification.options.pluginData.size == 2, "Test 21c, Should have one type of plugin in the notification");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     let overlayRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
-    return overlayRect.width == 0 && overlayRect.height == 0;
+    Assert.ok(overlayRect.width == 0 && overlayRect.height == 0,
+      "Test 21c, plugin should have overlay dims of 0px");
   });
-  ok(result, "Test 21c, plugin should have overlay dims of 0px");
 
   ids = ["secondtestA", "secondtestB"];
   for (let id of ids) {
-    let result = yield ContentTask.spawn(gTestBrowser, {aId: id}, function* () {
+    yield ContentTask.spawn(gTestBrowser, { id }, function* (args) {
       let doc = content.document;
-      let plugin = doc.getElementById(arguments[0].aId);
+      let plugin = doc.getElementById(args.id);
       let overlayRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
-      return overlayRect.width == 200 && overlayRect.height == 200;
+      Assert.ok(overlayRect.width == 200 && overlayRect.height == 200,
+        "Test 21c, plugin " + args.id + " should have click-to-play overlay with zero dims");
     });
-    ok(result, "Test 21c, plugin " + id + " should have click-to-play overlay with zero dims");
+    
 
     let pluginInfo = yield promiseForPluginInfo(id);
     ok(!pluginInfo.activated, "Test 21c, Plugin with id=" + id + " should not be activated");
   }
 
   centerAction = null;
   for (let action of notification.options.pluginData.values()) {
     if (action.pluginName == "Second Test") {
@@ -516,23 +515,23 @@ add_task(function* () {
   // "click" the button to activate the Second Test plugins
   PopupNotifications.panel.firstChild._primaryButton.click();
 
   notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 21d, Should have a click-to-play notification");
 
   ids = ["test", "secondtestA", "secondtestB"];
   for (let id of ids) {
-    let result = yield ContentTask.spawn(gTestBrowser, {aId: id}, function* () {
+    yield ContentTask.spawn(gTestBrowser, { id }, function* (args) {
       let doc = content.document;
-      let plugin = doc.getElementById(arguments[0].aId);
+      let plugin = doc.getElementById(args.id);
       let overlayRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
-      return overlayRect.width == 0 && overlayRect.height == 0;
+      Assert.ok(overlayRect.width == 0 && overlayRect.height == 0,
+        "Test 21d, plugin " + args.id + " should have click-to-play overlay with zero dims");
     });
-    ok(result, "Test 21d, plugin " + id + " should have click-to-play overlay with zero dims");
 
     let pluginInfo = yield promiseForPluginInfo(id);
     ok(pluginInfo.activated, "Test 21d, Plugin with id=" + id + " should not be activated");
   }
 });
 
 // Tests that a click-to-play plugin resets its activated state when changing types
 add_task(function* () {
@@ -548,29 +547,29 @@ add_task(function* () {
   let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 22, Should have a click-to-play notification");
 
   // Plugin should start as CTP
   let pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
      "Test 23, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
 
-  yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     plugin.type = null;
     // We currently don't properly change state just on type change,
     // so rebind the plugin to tree. bug 767631
     plugin.parentNode.appendChild(plugin);
   });
 
   pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.displayedType, Ci.nsIObjectLoadingContent.TYPE_NULL, "Test 23, plugin should be TYPE_NULL");
 
-  let result = yield ContentTask.spawn(gTestBrowser, {}, function* () {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
     let doc = content.document;
     let plugin = doc.getElementById("test");
     plugin.type = "application/x-test";
     plugin.parentNode.appendChild(plugin);
   });
 
   pluginInfo = yield promiseForPluginInfo("test");
   is(pluginInfo.displayedType, Ci.nsIObjectLoadingContent.TYPE_NULL, "Test 23, plugin should be TYPE_NULL");
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -440,17 +440,17 @@ extensions.registerSchemaAPI("tabs", nul
             if (createProperties.pinned) {
               window.gBrowser.pinTab(tab);
             }
 
             resolve(TabManager.convert(extension, tab));
           }
 
           let window = createProperties.windowId !== null ?
-            WindowManager.getWindow(createProperties.windowId) :
+            WindowManager.getWindow(createProperties.windowId, context) :
             WindowManager.topWindow;
           if (!window.gBrowser) {
             let obs = (finishedWindow, topic, data) => {
               if (finishedWindow != window) {
                 return;
               }
               Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
               createInWindow(window);
@@ -624,17 +624,17 @@ extensions.registerSchemaAPI("tabs", nul
 
       captureVisibleTab: function(windowId, options) {
         if (!extension.hasPermission("<all_urls>")) {
           return Promise.reject({message: "The <all_urls> permission is required to use the captureVisibleTab API"});
         }
 
         let window = windowId == null ?
           WindowManager.topWindow :
-          WindowManager.getWindow(windowId);
+          WindowManager.getWindow(windowId, context);
 
         let browser = window.gBrowser.selectedBrowser;
         let recipient = {
           innerWindowID: browser.innerWindowID,
         };
 
         if (!options) {
           options = {};
@@ -769,17 +769,17 @@ extensions.registerSchemaAPI("tabs", nul
         let index = moveProperties.index;
         let tabsMoved = [];
         if (!Array.isArray(tabIds)) {
           tabIds = [tabIds];
         }
 
         let destinationWindow = null;
         if (moveProperties.windowId !== null) {
-          destinationWindow = WindowManager.getWindow(moveProperties.windowId);
+          destinationWindow = WindowManager.getWindow(moveProperties.windowId, context);
           // Ignore invalid window.
           if (!destinationWindow) {
             return;
           }
         }
 
         /*
           Indexes are maintained on a per window basis so that a call to
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -620,50 +620,111 @@ global.WindowManager = {
   WINDOW_ID_CURRENT: -2,
 
   get topWindow() {
     return Services.wm.getMostRecentWindow("navigator:browser");
   },
 
   windowType(window) {
     // TODO: Make this work.
+
+    let {chromeFlags} = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIDocShell)
+                              .treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIXULWindow);
+
+    if (chromeFlags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG) {
+      return "popup";
+    }
+
     return "normal";
   },
 
   getId(window) {
     if (this._windows.has(window)) {
       return this._windows.get(window);
     }
     let id = this._nextId++;
     this._windows.set(window, id);
     return id;
   },
 
-  getWindow(id) {
+  getWindow(id, context) {
+    if (id == this.WINDOW_ID_CURRENT) {
+      return currentWindow(context);
+    }
+
     for (let window of WindowListManager.browserWindows(true)) {
       if (this.getId(window) == id) {
         return window;
       }
     }
     return null;
   },
 
+  setState(window, state) {
+    if (state != "fullscreen" && window.fullScreen) {
+      window.fullScreen = false;
+    }
+
+    switch (state) {
+      case "maximized":
+        window.maximize();
+        break;
+
+      case "minimized":
+      case "docked":
+        window.minimize();
+        break;
+
+      case "normal":
+        // Restore sometimes returns the window to its previous state, rather
+        // than to the "normal" state, so it may need to be called anywhere from
+        // zero to two times.
+        window.restore();
+        if (window.windowState != window.STATE_NORMAL) {
+          window.restore();
+        }
+        if (window.windowState != window.STATE_NORMAL) {
+          // And on OS-X, where normal vs. maximized is basically a heuristic,
+          // we need to cheat.
+          window.sizeToContent();
+        }
+        break;
+
+      case "fullscreen":
+        window.fullScreen = true;
+        break;
+
+      default:
+        throw new Error(`Unexpected window state: ${state}`);
+    }
+  },
+
   convert(extension, window, getInfo) {
+    const STATES = {
+      [window.STATE_MAXIMIZED]: "maximized",
+      [window.STATE_MINIMIZED]: "minimized",
+      [window.STATE_NORMAL]: "normal",
+    };
+    let state = STATES[window.windowState];
+    if (window.fullScreen) {
+      state = "fullscreen";
+    }
+
     let result = {
       id: this.getId(window),
       focused: window.document.hasFocus(),
       top: window.screenY,
       left: window.screenX,
       width: window.outerWidth,
       height: window.outerHeight,
       incognito: PrivateBrowsingUtils.isWindowPrivate(window),
-
-      // We fudge on these next two.
       type: this.windowType(window),
-      state: window.fullScreen ? "fullscreen" : "normal",
+      state,
     };
 
     if (getInfo && getInfo.populate) {
       result.tabs = TabManager.for(extension).getTabs(window);
     }
 
     return result;
   },
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -1,15 +1,17 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
 } = ExtensionUtils;
 
@@ -37,17 +39,17 @@ extensions.registerSchemaAPI("windows", 
         AllWindowEvents.addListener("blur", listener);
         return () => {
           AllWindowEvents.removeListener("focus", listener);
           AllWindowEvents.removeListener("blur", listener);
         };
       }).api(),
 
       get: function(windowId, getInfo) {
-        let window = WindowManager.getWindow(windowId);
+        let window = WindowManager.getWindow(windowId, context);
         return Promise.resolve(WindowManager.convert(extension, window, getInfo));
       },
 
       getCurrent: function(getInfo) {
         let window = currentWindow(context);
         return Promise.resolve(WindowManager.convert(extension, window, getInfo));
       },
 
@@ -58,16 +60,23 @@ extensions.registerSchemaAPI("windows", 
 
       getAll: function(getInfo) {
         let windows = Array.from(WindowListManager.browserWindows(),
                                  window => WindowManager.convert(extension, window, getInfo));
         return Promise.resolve(windows);
       },
 
       create: function(createData) {
+        if (createData.state !== null && createData.state != "normal") {
+          if (createData.left !== null || createData.top !== null ||
+              createData.width !== null || createData.height !== null) {
+            return Promise.reject({message: `"state": "${createData.state}" may not be combined with "left", "top", "width", or "height"`});
+          }
+        }
+
         function mkstr(s) {
           let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
           result.data = s;
           return result;
         }
 
         let args = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
 
@@ -99,61 +108,103 @@ extensions.registerSchemaAPI("windows", 
             args.AppendElement(array);
           } else {
             args.AppendElement(mkstr(createData.url));
           }
         } else {
           args.AppendElement(mkstr(aboutNewTabService.newTabURL));
         }
 
-        let extraFeatures = "";
+        let features = ["chrome"];
+
+        if (createData.type === null || createData.type == "normal") {
+          features.push("dialog=no", "all");
+        } else {
+          // All other types create "popup"-type windows by default.
+          features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close");
+        }
+
         if (createData.incognito !== null) {
           if (createData.incognito) {
-            extraFeatures += ",private";
+            features.push("private");
           } else {
-            extraFeatures += ",non-private";
+            features.push("non-private");
           }
         }
 
         let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank",
-                                            "chrome,dialog=no,all" + extraFeatures, args);
+                                            features.join(","), args);
 
         if (createData.left !== null || createData.top !== null) {
           let left = createData.left !== null ? createData.left : window.screenX;
           let top = createData.top !== null ? createData.top : window.screenY;
           window.moveTo(left, top);
         }
         if (createData.width !== null || createData.height !== null) {
           let width = createData.width !== null ? createData.width : window.outerWidth;
           let height = createData.height !== null ? createData.height : window.outerHeight;
           window.resizeTo(width, height);
         }
 
-        // TODO: focused, type, state
+        // TODO: focused, type
 
         return new Promise(resolve => {
           window.addEventListener("load", function listener() {
             window.removeEventListener("load", listener);
-            resolve(WindowManager.convert(extension, window));
+
+            if (createData.state == "maximized" || createData.state == "normal" ||
+                (createData.state == "fullscreen" && AppConstants.platform != "macosx")) {
+              window.document.documentElement.setAttribute("sizemode", createData.state);
+            } else if (createData.state !== null) {
+              // window.minimize() has no useful effect until the window has
+              // been shown.
+
+              let obs = doc => {
+                if (doc === window.document) {
+                  Services.obs.removeObserver(obs, "document-shown");
+                  WindowManager.setState(window, createData.state);
+                  resolve();
+                }
+              };
+              Services.obs.addObserver(obs, "document-shown", false);
+              return;
+            }
+
+            resolve();
           });
+        }).then(() => {
+          return WindowManager.convert(extension, window);
         });
       },
 
       update: function(windowId, updateInfo) {
-        let window = WindowManager.getWindow(windowId);
+        // TODO: When we support size/position updates:
+        // if (updateInfo.state !== null && updateInfo.state != "normal") {
+        //   if (updateInfo.left !== null || updateInfo.top !== null ||
+        //       updateInfo.width !== null || updateInfo.height !== null) {
+        //     return Promise.reject({message: `"state": "${updateInfo.state}" may not be combined with "left", "top", "width", or "height"`});
+        //   }
+        // }
+
+        let window = WindowManager.getWindow(windowId, context);
         if (updateInfo.focused) {
           Services.focus.activeWindow = window;
         }
-        // TODO: All the other properties...
+
+        if (updateInfo.state !== null) {
+          WindowManager.setState(window, updateInfo.state);
+        }
+
+        // TODO: All the other properties, focused=false...
 
         return Promise.resolve(WindowManager.convert(extension, window));
       },
 
       remove: function(windowId) {
-        let window = WindowManager.getWindow(windowId);
+        let window = WindowManager.getWindow(windowId, context);
         window.close();
 
         return new Promise(resolve => {
           let listener = () => {
             AllWindowEvents.removeListener("domwindowclosed", listener);
             resolve();
           };
           AllWindowEvents.addListener("domwindowclosed", listener);
--- a/browser/components/extensions/schemas/windows.json
+++ b/browser/components/extensions/schemas/windows.json
@@ -330,23 +330,21 @@
                 "description": "If true, opens an active window. If false, opens an inactive window."
               },
               "incognito": {
                 "type": "boolean",
                 "optional": true,
                 "description": "Whether the new window should be an incognito window."
               },
               "type": {
-                "unsupported": true,
                 "$ref": "CreateType",
                 "optional": true,
                 "description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set."
               },
               "state": {
-                "unsupported": true,
                 "$ref": "WindowState",
                 "optional": true,
                 "description": "The initial state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
               }
             },
             "optional": true
           },
           {
@@ -412,17 +410,16 @@
               },
               "drawAttention": {
                 "unsupported": true,
                 "type": "boolean",
                 "optional": true,
                 "description": "If true, causes the window to be displayed in a manner that draws the user's attention to the window, without changing the focused window. The effect lasts until the user changes focus to the window. This option has no effect if the window already has focus. Set to false to cancel a previous draw attention request."
               },
               "state": {
-                "unsupported": true,
                 "$ref": "WindowState",
                 "optional": true,
                 "description": "The new state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
               }
             }
           },
           {
             "type": "function",
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -44,14 +44,15 @@ support-files =
 [browser_ext_tabs_duplicate.js]
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_update_url.js]
 [browser_ext_tabs_onUpdated.js]
 [browser_ext_tabs_sendMessage.js]
 [browser_ext_tabs_move.js]
 [browser_ext_tabs_move_window.js]
 [browser_ext_tabs_onHighlighted.js]
+[browser_ext_windows_create.js]
 [browser_ext_windows_create_tabId.js]
 [browser_ext_windows_update.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_tab_runtimeConnect.js]
 [browser_ext_topwindowid.js]
 [browser_ext_webNavigation_getFrames.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_windows_create.js
@@ -0,0 +1,167 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* testWindowCreate() {
+  let extension = ExtensionTestUtils.loadExtension({
+    background() {
+      let _checkWindowPromise;
+      browser.test.onMessage.addListener(msg => {
+        if (msg == "checked-window") {
+          _checkWindowPromise.resolve();
+          _checkWindowPromise = null;
+        }
+      });
+
+      let os;
+
+      function checkWindow(expected) {
+        return new Promise(resolve => {
+          _checkWindowPromise = {resolve};
+          browser.test.sendMessage("check-window", expected);
+        });
+      }
+
+      function createWindow(params, expected, keep = false) {
+        return browser.windows.create(params).then(window => {
+          for (let key of Object.keys(params)) {
+            if (key == "state" && os == "mac" && params.state == "normal") {
+              // OS-X doesn't have a hard distinction between "normal" and
+              // "maximized" states.
+              browser.test.assertTrue(window.state == "normal" || window.state == "maximized",
+                                      `Expected window.state (currently ${window.state}) to be "normal" but will accept "maximized"`);
+            } else {
+              browser.test.assertEq(params[key], window[key], `Got expected value for window.${key}`);
+            }
+          }
+
+          return checkWindow(expected).then(() => {
+            if (keep) {
+              return window;
+            }
+            if (params.state == "fullscreen" && os == "win") {
+              // FIXME: Closing a fullscreen window causes a window leak in
+              // Windows tests.
+              return browser.windows.update(window.id, {state: "normal"}).then(() => {
+                return browser.windows.remove(window.id);
+              });
+            }
+            return browser.windows.remove(window.id);
+          });
+        });
+      }
+
+      browser.runtime.getPlatformInfo().then(info => { os = info.os; })
+      .then(() => createWindow({state: "maximized"}, {state: "STATE_MAXIMIZED"}))
+      .then(() => createWindow({state: "minimized"}, {state: "STATE_MINIMIZED"}))
+      .then(() => createWindow({state: "normal"}, {state: "STATE_NORMAL", hiddenChrome: []}))
+      .then(() => createWindow({state: "fullscreen"}, {state: "STATE_FULLSCREEN"}))
+      .then(() => {
+        return createWindow({type: "popup"},
+                            {hiddenChrome: ["menubar", "toolbar", "location", "directories", "status", "extrachrome"],
+                             chromeFlags: ["CHROME_OPENAS_DIALOG"]},
+                            true);
+      }).then(window => {
+        return browser.tabs.query({windowType: "popup", active: true}).then(tabs => {
+          browser.test.assertEq(1, tabs.length, "Expected only one popup");
+          browser.test.assertEq(window.id, tabs[0].windowId, "Expected new window to be returned in query");
+
+          return browser.windows.remove(window.id);
+        });
+      }).then(() => {
+        browser.test.notifyPass("window-create");
+      }).catch(e => {
+        browser.test.fail(`${e} :: ${e.stack}`);
+        browser.test.notifyFail("window-create");
+      });
+    },
+  });
+
+  let latestWindow;
+  let windowListener = (window, topic) => {
+    if (topic == "domwindowopened") {
+      latestWindow = window;
+    }
+  };
+  Services.ww.registerNotification(windowListener);
+
+  extension.onMessage("check-window", expected => {
+    if (expected.state != null) {
+      let {windowState} = latestWindow;
+      if (latestWindow.fullScreen) {
+        windowState = latestWindow.STATE_FULLSCREEN;
+      }
+
+      if (expected.state == "STATE_NORMAL" && AppConstants.platform == "macosx") {
+        ok(windowState == window.STATE_NORMAL || windowState == window.STATE_MAXIMIZED,
+           `Expected windowState (currently ${windowState}) to be STATE_NORMAL but will accept STATE_MAXIMIZED`);
+      } else {
+        is(windowState, window[expected.state],
+           `Expected window state to be ${expected.state}`);
+      }
+    }
+    if (expected.hiddenChrome) {
+      let chromeHidden = latestWindow.document.documentElement.getAttribute("chromehidden");
+      is(chromeHidden.trim().split(/\s+/).sort().join(" "),
+         expected.hiddenChrome.sort().join(" "),
+         "Got expected hidden chrome");
+    }
+    if (expected.chromeFlags) {
+      let {chromeFlags} = latestWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                      .getInterface(Ci.nsIDocShell)
+                                      .treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
+                                      .getInterface(Ci.nsIXULWindow);
+      for (let flag of expected.chromeFlags) {
+        ok(chromeFlags & Ci.nsIWebBrowserChrome[flag],
+           `Expected window to have the ${flag} flag`);
+      }
+    }
+
+    extension.sendMessage("checked-window");
+  });
+
+  yield extension.startup();
+  yield extension.awaitFinish("window-create");
+  yield extension.unload();
+
+  Services.ww.unregisterNotification(windowListener);
+  latestWindow = null;
+});
+
+
+// Tests that incompatible parameters can't be used together.
+add_task(function* testWindowCreateParams() {
+  let extension = ExtensionTestUtils.loadExtension({
+    background() {
+      function* getCalls() {
+        for (let state of ["minimized", "maximized", "fullscreen"]) {
+          for (let param of ["left", "top", "width", "height"]) {
+            let expected = `"state": "${state}" may not be combined with "left", "top", "width", or "height"`;
+
+            yield browser.windows.create({state, [param]: 100}).then(
+              val => {
+                browser.test.fail(`Expected error but got "${val}" instead`);
+              },
+              error => {
+                browser.test.assertTrue(
+                  error.message.includes(expected),
+                  `Got expected error (got: '${error.message}', expected: '${expected}'`);
+              });
+          }
+        }
+      }
+
+      Promise.all(getCalls()).then(() => {
+        browser.test.notifyPass("window-create-params");
+      }).catch(e => {
+        browser.test.fail(`${e} :: ${e.stack}`);
+        browser.test.notifyFail("window-create-params");
+      });
+    },
+  });
+
+  yield extension.startup();
+  yield extension.awaitFinish("window-create-params");
+  yield extension.unload();
+});
+
--- a/browser/components/extensions/test/browser/browser_ext_windows_update.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_update.js
@@ -14,20 +14,16 @@ add_task(function* () {
 
   let window1 = window;
   let window2 = yield BrowserTestUtils.openNewBrowserWindow();
 
   Services.focus.activeWindow = window2;
   yield promiseWaitForFocus(window2);
 
   let extension = ExtensionTestUtils.loadExtension({
-    manifest: {
-      "permissions": ["windows"],
-    },
-
     background: function() {
       browser.windows.getAll(undefined, function(wins) {
         browser.test.assertEq(wins.length, 2, "should have two windows");
 
         // Sort the unfocused window to the lower index.
         wins.sort(function(win1, win2) {
           if (win1.focused === win2.focused) {
             return 0;
@@ -46,8 +42,86 @@ add_task(function* () {
   yield Promise.all([extension.startup(), extension.awaitMessage("check")]);
 
   yield promiseWaitForFocus(window1);
 
   yield extension.unload();
 
   yield BrowserTestUtils.closeWindow(window2);
 });
+
+
+add_task(function* testWindowUpdate() {
+  let extension = ExtensionTestUtils.loadExtension({
+    background() {
+      let _checkWindowPromise;
+      browser.test.onMessage.addListener(msg => {
+        if (msg == "checked-window") {
+          _checkWindowPromise.resolve();
+          _checkWindowPromise = null;
+        }
+      });
+
+      let os;
+      function checkWindow(expected) {
+        return new Promise(resolve => {
+          _checkWindowPromise = {resolve};
+          browser.test.sendMessage("check-window", expected);
+        });
+      }
+
+      function updateWindow(windowId, params, expected) {
+        return browser.windows.update(windowId, params).then(window => {
+          for (let key of Object.keys(params)) {
+            if (key == "state" && os == "mac" && params.state == "normal") {
+              // OS-X doesn't have a hard distinction between "normal" and
+              // "maximized" states.
+              browser.test.assertTrue(window.state == "normal" || window.state == "maximized",
+                                      `Expected window.state (currently ${window.state}) to be "normal" but will accept "maximized"`);
+            } else {
+              browser.test.assertEq(params[key], window[key], `Got expected value for window.${key}`);
+            }
+          }
+
+          return checkWindow(expected);
+        });
+      }
+
+      let windowId = browser.windows.WINDOW_ID_CURRENT;
+
+      browser.runtime.getPlatformInfo().then(info => { os = info.os; })
+      .then(() => updateWindow(windowId, {state: "maximized"}, {state: "STATE_MAXIMIZED"}))
+      .then(() => updateWindow(windowId, {state: "minimized"}, {state: "STATE_MINIMIZED"}))
+      .then(() => updateWindow(windowId, {state: "normal"}, {state: "STATE_NORMAL"}))
+      .then(() => updateWindow(windowId, {state: "fullscreen"}, {state: "STATE_FULLSCREEN"}))
+      .then(() => updateWindow(windowId, {state: "normal"}, {state: "STATE_NORMAL"}))
+      .then(() => {
+        browser.test.notifyPass("window-update");
+      }).catch(e => {
+        browser.test.fail(`${e} :: ${e.stack}`);
+        browser.test.notifyFail("window-update");
+      });
+    },
+  });
+
+  extension.onMessage("check-window", expected => {
+    if (expected.state != null) {
+      let {windowState} = window;
+      if (window.fullScreen) {
+        windowState = window.STATE_FULLSCREEN;
+      }
+
+      if (expected.state == "STATE_NORMAL" && AppConstants.platform == "macosx") {
+        ok(windowState == window.STATE_NORMAL || windowState == window.STATE_MAXIMIZED,
+           `Expected windowState (currently ${windowState}) to be STATE_NORMAL but will accept STATE_MAXIMIZED`);
+      } else {
+        is(windowState, window[expected.state],
+           `Expected window state to be ${expected.state}`);
+      }
+    }
+
+    extension.sendMessage("checked-window");
+  });
+
+  yield extension.startup();
+  yield extension.awaitFinish("window-update");
+  yield extension.unload();
+});
--- a/browser/components/newtab/tests/browser/browser_newtab_overrides.js
+++ b/browser/components/newtab/tests/browser/browser_newtab_overrides.js
@@ -53,21 +53,21 @@ add_task(function* redirector_ignores_ov
      * Simulate typing "about:newtab" in the url bar.
      *
      * Bug 1240169 - We expect the redirector to lead the user to "about:newtab", the default URL,
      * due to invoking AboutRedirector. A user interacting with the chrome otherwise would lead
      * to the overriding URLs.
      */
     yield BrowserTestUtils.withNewTab(tabOptions, function*(browser) {
       yield ContentTask.spawn(browser, {}, function*() {
-        is(content.location.href, "about:newtab", "Got right URL");
-        is(content.document.location.href, "about:newtab", "Got right URL");
-        is(content.document.nodePrincipal,
-           Services.scriptSecurityManager.getSystemPrincipal(),
-           "nodePrincipal should match systemPrincipal");
+        Assert.equal(content.location.href, "about:newtab", "Got right URL");
+        Assert.equal(content.document.location.href, "about:newtab", "Got right URL");
+        Assert.equal(content.document.nodePrincipal,
+          Services.scriptSecurityManager.getSystemPrincipal(),
+          "nodePrincipal should match systemPrincipal");
       });
     });  // jshint ignore:line
   }
 });
 
 /*
  * Tests loading an overridden newtab page by simulating opening a newtab page from chrome
  */
@@ -87,18 +87,18 @@ add_task(function* override_loads_in_bro
 
     // simulate a newtab open as a user would
     BrowserOpenTab();  // jshint ignore:line
 
     let browser = gBrowser.selectedBrowser;
     yield BrowserTestUtils.browserLoaded(browser);
 
     yield ContentTask.spawn(browser, {url: overrideURL}, function*(args) {
-      is(content.location.href, args.url.trim(), "Got right URL");
-      is(content.document.location.href, args.url.trim(), "Got right URL");
+      Assert.equal(content.location.href, args.url.trim(), "Got right URL");
+      Assert.equal(content.document.location.href, args.url.trim(), "Got right URL");
     });  // jshint ignore:line
     yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
 
 /*
  * Tests edge cases when someone overrides the newtabpage with whitespace
  */
@@ -119,18 +119,18 @@ add_task(function* override_blank_loads_
 
     // simulate a newtab open as a user would
     BrowserOpenTab();  // jshint ignore:line
 
     let browser = gBrowser.selectedBrowser;
     yield BrowserTestUtils.browserLoaded(browser);
 
     yield ContentTask.spawn(browser, {}, function*() {
-      is(content.location.href, "about:blank", "Got right URL");
-      is(content.document.location.href, "about:blank", "Got right URL");
+      Assert.equal(content.location.href, "about:blank", "Got right URL");
+      Assert.equal(content.document.location.href, "about:blank", "Got right URL");
     });  // jshint ignore:line
     yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
 
 function nextChangeNotificationPromise(aNewURL, testMessage) {
   return TestUtils.topicObserved("newtab-url-changed", function observer(aSubject, aData) {  // jshint unused:false
       Assert.equal(aData, aNewURL, testMessage);
--- a/browser/components/newtab/tests/browser/browser_remotenewtab_pageloads.js
+++ b/browser/components/newtab/tests/browser/browser_remotenewtab_pageloads.js
@@ -29,19 +29,22 @@ add_task(function* open_newtab() {
    * bar, or by using the new tab shortcut key.
    */
   BrowserOpenTab();  // jshint ignore:line
 
   let browser = gBrowser.selectedBrowser;
   yield BrowserTestUtils.browserLoaded(browser);
 
   yield ContentTask.spawn(browser, {url: TEST_URL}, function*(args) {
-    is(content.document.location.href, args.url, "document.location should match the external resource");
-    is(content.document.documentURI, args.url, "document.documentURI should match the external resource");
-    is(content.document.nodePrincipal.URI.spec, args.url, "nodePrincipal should match the external resource");
+    Assert.equal(content.document.location.href, args.url,
+      "document.location should match the external resource");
+    Assert.equal(content.document.documentURI, args.url,
+      "document.documentURI should match the external resource");
+    Assert.equal(content.document.nodePrincipal.URI.spec, args.url,
+      "nodePrincipal should match the external resource");
   });
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 function nextChangeNotificationPromise(aNewURL, testMessage) {
   return TestUtils.topicObserved("newtab-url-changed", function observer(aSubject, aData) {  // jshint unused:false
       Assert.equal(aData, aNewURL, testMessage);
       return true;
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -25,17 +25,16 @@ support-files =
   pageopeningwindow.html
 [browser_bookmarkProperties_addFolderDefaultButton.js]
 [browser_bookmarkProperties_addKeywordForThisSearch.js]
 [browser_bookmarkProperties_addLivemark.js]
 [browser_bookmarkProperties_editTagContainer.js]
 [browser_bookmarkProperties_readOnlyRoot.js]
 [browser_bookmarksProperties.js]
 [browser_drag_bookmarks_on_toolbar.js]
-skip-if = e10s # Bug ?????? - test fails - "Number of dragged items should be the same. - Got 0, expected 1"
 [browser_forgetthissite_single.js]
 [browser_history_sidebar_search.js]
 [browser_library_batch_delete.js]
 [browser_library_commands.js]
 [browser_library_downloads.js]
 [browser_library_infoBox.js]
 [browser_library_left_pane_fixnames.js]
 [browser_library_left_pane_select_hierarchy.js]
--- a/browser/components/preferences/in-content/tests/browser_defaultbrowser_alwayscheck.js
+++ b/browser/components/preferences/in-content/tests/browser_defaultbrowser_alwayscheck.js
@@ -2,74 +2,74 @@
 
 const CHECK_DEFAULT_INITIAL = Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser");
 
 add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() {
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
 
   yield test_with_mock_shellservice({isDefault: false}, function*() {
     let setDefaultPane = content.document.getElementById("setDefaultPane");
-    is(setDefaultPane.selectedIndex, "0",
-       "The 'make default' pane should be visible when not default");
+    Assert.equal(setDefaultPane.selectedIndex, "0",
+      "The 'make default' pane should be visible when not default");
     let alwaysCheck = content.document.getElementById("alwaysCheckDefault");
-    is(alwaysCheck.checked, false, "Always Check is unchecked by default");
-    is(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), false,
-       "alwaysCheck pref should be false by default in test runs");
+    Assert.ok(!alwaysCheck.checked, "Always Check is unchecked by default");
+    Assert.ok(!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "alwaysCheck pref should be false by default in test runs");
 
     let setDefaultButton = content.document.getElementById("setDefaultButton");
     setDefaultButton.click();
     content.window.gMainPane.updateSetDefaultBrowser();
 
     yield ContentTaskUtils.waitForCondition(() => alwaysCheck.checked,
       "'Always Check' checkbox should get checked after clicking the 'Set Default' button");
 
-    is(alwaysCheck.checked, true,
-       "Clicking 'Make Default' checks the 'Always Check' checkbox");
-    is(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), true,
-       "Checking the checkbox should set the pref to true");
-    is(alwaysCheck.disabled, true,
-       "'Always Check' checkbox is locked with default browser and alwaysCheck=true");
-    is(setDefaultPane.selectedIndex, "1",
-       "The 'make default' pane should not be visible when default");
-    is(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), true,
-       "checkDefaultBrowser pref is now enabled");
+    Assert.ok(alwaysCheck.checked,
+      "Clicking 'Make Default' checks the 'Always Check' checkbox");
+    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "Checking the checkbox should set the pref to true");
+    Assert.ok(alwaysCheck.disabled,
+      "'Always Check' checkbox is locked with default browser and alwaysCheck=true");
+    Assert.equal(setDefaultPane.selectedIndex, "1",
+      "The 'make default' pane should not be visible when default");
+    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "checkDefaultBrowser pref is now enabled");
   });
 
   gBrowser.removeCurrentTab();
   Services.prefs.clearUserPref("browser.shell.checkDefaultBrowser");
 });
 
 add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() {
   Services.prefs.lockPref("browser.shell.checkDefaultBrowser");
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
 
   yield test_with_mock_shellservice({isDefault: false}, function*() {
     let setDefaultPane = content.document.getElementById("setDefaultPane");
-    is(setDefaultPane.selectedIndex, "0",
-       "The 'make default' pane should be visible when not default");
+    Assert.equal(setDefaultPane.selectedIndex, "0",
+      "The 'make default' pane should be visible when not default");
     let alwaysCheck = content.document.getElementById("alwaysCheckDefault");
-    is(alwaysCheck.disabled, true, "Always Check is disabled when locked");
-    is(alwaysCheck.checked, true,
-       "Always Check is checked because defaultPref is true and pref is locked");
-    is(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), true,
-       "alwaysCheck pref should ship with 'true' by default");
+    Assert.ok(alwaysCheck.disabled, "Always Check is disabled when locked");
+    Assert.ok(alwaysCheck.checked,
+      "Always Check is checked because defaultPref is true and pref is locked");
+    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "alwaysCheck pref should ship with 'true' by default");
 
     let setDefaultButton = content.document.getElementById("setDefaultButton");
     setDefaultButton.click();
     content.window.gMainPane.updateSetDefaultBrowser();
 
     yield ContentTaskUtils.waitForCondition(() => setDefaultPane.selectedIndex == "1",
       "Browser is now default");
 
-    is(alwaysCheck.checked, true,
-       "'Always Check' is still checked because it's locked");
-    is(alwaysCheck.disabled, true,
-       "'Always Check is disabled because it's locked");
-    is(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"), true,
-       "The pref is locked and so doesn't get changed");
+    Assert.ok(alwaysCheck.checked,
+      "'Always Check' is still checked because it's locked");
+    Assert.ok(alwaysCheck.disabled,
+      "'Always Check is disabled because it's locked");
+    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "The pref is locked and so doesn't get changed");
   });
 
   Services.prefs.unlockPref("browser.shell.checkDefaultBrowser");
   gBrowser.removeCurrentTab();
 });
 
 registerCleanupFunction(function() {
   Services.prefs.unlockPref("browser.shell.checkDefaultBrowser");
--- a/browser/components/preferences/in-content/tests/browser_subdialogs.js
+++ b/browser/components/preferences/in-content/tests/browser_subdialogs.js
@@ -34,22 +34,22 @@ function* open_subdialog_and_test_generi
     let expectedStyleSheetURLs = subdialog._injectedStyleSheets.slice(0);
     for (let styleSheet of subdialog._frame.contentDocument.styleSheets) {
       let index = expectedStyleSheetURLs.indexOf(styleSheet.href);
       if (index >= 0) {
         expectedStyleSheetURLs.splice(index, 1);
       }
     }
 
-    ok(!!subdialog._frame.contentWindow, "The dialog should be non-null");
-    isnot(subdialog._frame.contentWindow.location.toString(), "about:blank",
+    Assert.ok(!!subdialog._frame.contentWindow, "The dialog should be non-null");
+    Assert.notEqual(subdialog._frame.contentWindow.location.toString(), "about:blank",
       "Subdialog URL should not be about:blank");
-    is(win.getComputedStyle(subdialog._overlay, "").visibility, "visible",
+    Assert.equal(win.getComputedStyle(subdialog._overlay, "").visibility, "visible",
       "Overlay should be visible");
-    is(expectedStyleSheetURLs.length, 0,
+    Assert.equal(expectedStyleSheetURLs.length, 0,
       "No stylesheets that were expected are missing");
     return result;
   });
 }
 
 function* close_subdialog_and_test_generic_end_state(browser, closingFn, closingButton, acceptCount, options) {
   let dialogclosingPromise = ContentTask.spawn(browser, {closingButton, acceptCount}, function*(expectations) {
     let win = content.window;
@@ -59,24 +59,24 @@ function* close_subdialog_and_test_gener
     let closingEvent =
       yield ContentTaskUtils.waitForEvent(frame.contentWindow, "dialogclosing");
     let actualAcceptCount = frame.contentWindow.arguments &&
                             frame.contentWindow.arguments[0].acceptCount;
 
     info("waiting for about:blank load");
     yield ContentTaskUtils.waitForEvent(frame, "load");
 
-    isnot(win.getComputedStyle(subdialog._overlay, "").visibility, "visible",
+    Assert.notEqual(win.getComputedStyle(subdialog._overlay, "").visibility, "visible",
       "overlay is not visible");
-    is(frame.getAttribute("style"), "", "inline styles should be cleared");
-    is(frame.contentWindow.location.href.toString(), "about:blank",
+    Assert.equal(frame.getAttribute("style"), "", "inline styles should be cleared");
+    Assert.equal(frame.contentWindow.location.href.toString(), "about:blank",
       "sub-dialog should be unloaded");
-    is(closingEvent.detail.button, expectations.closingButton,
+    Assert.equal(closingEvent.detail.button, expectations.closingButton,
       "closing event should indicate button was '" + expectations.closingButton + "'");
-    is(actualAcceptCount, expectations.acceptCount,
+    Assert.equal(actualAcceptCount, expectations.acceptCount,
       "should be 1 if accepted, 0 if canceled, undefined if closed w/out button");
   });
 
   if (options && options.runClosingFnOutsideOfContentTask) {
     yield closingFn();
   } else {
     ContentTask.spawn(browser, null, closingFn);
   }
@@ -92,29 +92,30 @@ add_task(function* test_initialize() {
 
 add_task(function* check_titlebar_focus_returnval_titlechanges_accepting() {
   yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
 
   let domtitlechangedPromise = BrowserTestUtils.waitForEvent(tab.linkedBrowser, "DOMTitleChanged");
   yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
     let dialog = content.window.gSubDialog._frame.contentWindow;
     let dialogTitleElement = content.document.getElementById("dialogTitle");
-    is(dialogTitleElement.textContent, "Sample sub-dialog",
+    Assert.equal(dialogTitleElement.textContent, "Sample sub-dialog",
        "Title should be correct initially");
-    is(dialog.document.activeElement.value, "Default text",
+    Assert.equal(dialog.document.activeElement.value, "Default text",
        "Textbox with correct text is focused");
     dialog.document.title = "Updated title";
   });
 
   info("waiting for DOMTitleChanged event");
   yield domtitlechangedPromise;
 
   ContentTask.spawn(tab.linkedBrowser, null, function*() {
     let dialogTitleElement = content.document.getElementById("dialogTitle");
-    is(dialogTitleElement.textContent, "Updated title", "subdialog should have updated title");
+    Assert.equal(dialogTitleElement.textContent, "Updated title",
+      "subdialog should have updated title");
   });
 
   // Accept the dialog
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { content.window.gSubDialog._frame.contentDocument.documentElement.acceptDialog(); },
     "accept", 1);
 });
 
@@ -177,18 +178,20 @@ add_task(function* escape_should_close_d
     null, undefined, {runClosingFnOutsideOfContentTask: true});
 });
 
 add_task(function* correct_width_and_height_should_be_used_for_dialog() {
   yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
 
   yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
     let frameStyle = content.window.gSubDialog._frame.style;
-    is(frameStyle.width, "32em", "Width should be set on the frame from the dialog");
-    is(frameStyle.height, "5em", "Height should be set on the frame from the dialog");
+    Assert.equal(frameStyle.width, "32em",
+      "Width should be set on the frame from the dialog");
+    Assert.equal(frameStyle.height, "5em",
+      "Height should be set on the frame from the dialog");
   });
 
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
     null, 0);
 });
 
 add_task(function* wrapped_text_in_dialog_should_have_expected_scrollHeight() {
@@ -210,39 +213,39 @@ add_task(function* wrapped_text_in_dialo
       sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione
       voluptatem sequi nesciunt.`
     return oldHeight;
   });
 
   yield ContentTask.spawn(tab.linkedBrowser, oldHeight, function*(oldHeight) {
     let frame = content.window.gSubDialog._frame;
     let docEl = frame.contentDocument.documentElement;
-    is(frame.style.width, "32em",
+    Assert.equal(frame.style.width, "32em",
       "Width should be set on the frame from the dialog");
-    ok(docEl.scrollHeight > oldHeight,
+    Assert.ok(docEl.scrollHeight > oldHeight,
       "Content height increased (from " + oldHeight + " to " + docEl.scrollHeight + ").");
-    is(frame.style.height, docEl.scrollHeight + "px",
+    Assert.equal(frame.style.height, docEl.scrollHeight + "px",
       "Height on the frame should be higher now");
   });
 
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
     null, 0);
 });
 
 add_task(function* dialog_too_tall_should_get_reduced_in_height() {
   yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser, function domcontentloadedFn() {
     let frame = content.window.gSubDialog._frame;
     frame.contentDocument.documentElement.style.height = '100000px';
   });
 
   yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
     let frame = content.window.gSubDialog._frame;
-    is(frame.style.width, "32em", "Width should be set on the frame from the dialog");
-    ok(parseInt(frame.style.height) < content.window.innerHeight,
+    Assert.equal(frame.style.width, "32em", "Width should be set on the frame from the dialog");
+    Assert.ok(parseInt(frame.style.height, 10) < content.window.innerHeight,
        "Height on the frame should be smaller than window's innerHeight");
   });
 
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
     null, 0);
 });
 
@@ -250,19 +253,19 @@ add_task(function* scrollWidth_and_scrol
   yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser, function domcontentloadedFn() {
     let frame = content.window.gSubDialog._frame;
     frame.contentDocument.documentElement.style.removeProperty("height");
     frame.contentDocument.documentElement.style.removeProperty("width");
   });
 
   yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
     let frame = content.window.gSubDialog._frame;
-    ok(frame.style.width.endsWith("px"),
+    Assert.ok(frame.style.width.endsWith("px"),
        "Width (" + frame.style.width + ") should be set to a px value of the scrollWidth from the dialog");
-    ok(frame.style.height.endsWith("px"),
+    Assert.ok(frame.style.height.endsWith("px"),
        "Height (" + frame.style.height + ") should be set to a px value of the scrollHeight from the dialog");
   });
 
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { content.window.gSubDialog._frame.contentWindow.window.close(); },
     null, 0);
 });
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutHomeButtonAfterWindowClose.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutHomeButtonAfterWindowClose.js
@@ -9,18 +9,16 @@ add_task(function* test_no_sessionrestor
   (yield BrowserTestUtils.openNewBrowserWindow({private: true})).close();
 
   let win = yield BrowserTestUtils.openNewBrowserWindow({private: true});
   let tab = win.gBrowser.addTab("about:home");
   let browser = tab.linkedBrowser;
 
   yield BrowserTestUtils.browserLoaded(browser);
 
-  let display = yield ContentTask.spawn(browser, {}, function* (){
+  yield ContentTask.spawn(browser, null, function* () {
     let button = content.document.getElementById("restorePreviousSession");
-    return content.getComputedStyle(button).display;
+    Assert.equal(content.getComputedStyle(button).display, "none",
+      "The Session Restore about:home button should be disabled");
   });
 
-  is(display, "none",
-    "The Session Restore about:home button should be disabled");
-
   win.close();
 });
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js
@@ -9,16 +9,15 @@ add_task(function* testNoSessionRestoreB
   (yield BrowserTestUtils.openNewBrowserWindow({private: true})).close();
 
   let win = yield BrowserTestUtils.openNewBrowserWindow({private: true});
   let tab = win.gBrowser.addTab("about:sessionrestore");
   let browser = tab.linkedBrowser;
 
   yield BrowserTestUtils.browserLoaded(browser);
 
-  let disabled = yield ContentTask.spawn(browser, {}, function* (){
-    return content.document.getElementById("errorTryAgain").disabled;
+  yield ContentTask.spawn(browser, null, function* () {
+    Assert.ok(content.document.getElementById("errorTryAgain").disabled,
+      "The Restore about:sessionrestore button should be disabled");
   });
 
-  ok(disabled, "The Restore about:sessionrestore button should be disabled");
-
   win.close();
 });
--- a/browser/components/sessionstore/test/browser_463206.js
+++ b/browser/components/sessionstore/test/browser_463206.js
@@ -27,31 +27,27 @@ add_task(function* () {
     typeText(content.frames[0].frames[1].document.getElementById("in1"), new Date());
   });
 
   // Duplicate the tab.
   let tab2 = gBrowser.duplicateTab(tab);
   yield promiseTabRestored(tab2);
 
   // Query a few values from the top and its child frames.
-  let query = ContentTask.spawn(tab2.linkedBrowser, null, function* () {
-    return [
-      content.document.getElementById("out1").value,
+  yield ContentTask.spawn(tab2.linkedBrowser, null, function* () {
+    Assert.notEqual(content.document.getElementById("out1").value,
       content.frames[1].document.getElementById("out1").value,
-      content.document.getElementsByName("1|#out2")[0].value,
-      content.frames[1].document.getElementById("out2").value,
-      content.frames[0].frames[1].document.getElementById("in1").value,
-      content.frames[1].frames[0].document.getElementById("in1").value
-    ];
+      "text isn't reused for frames");
+    Assert.notEqual(content.document.getElementsByName("1|#out2")[0].value,
+      "", "text containing | and # is correctly restored");
+    Assert.equal(content.frames[1].document.getElementById("out2").value,
+      "", "id prefixes can't be faked");
+    // Disabled for now, Bug 588077
+    //Assert.equal(content.frames[0].frames[1].document.getElementById("in1").value,
+    //  "", "id prefixes aren't mixed up");
+    Assert.equal(content.frames[1].frames[0].document.getElementById("in1").value,
+      "", "id prefixes aren't mixed up");
   });
 
-  let [v1, v2, v3, v4, v5, v6] = yield query;
-  isnot(v1, v2, "text isn't reused for frames");
-  isnot(v3, "", "text containing | and # is correctly restored");
-  is(v4, "", "id prefixes can't be faked");
-  // Disabled for now, Bug 588077
-  //is(v5, "", "id prefixes aren't mixed up");
-  is(v6, "", "id prefixes aren't mixed up");
-
   // Cleanup.
   gBrowser.removeTab(tab2);
   gBrowser.removeTab(tab);
 });
--- a/browser/components/sessionstore/test/browser_491168.js
+++ b/browser/components/sessionstore/test/browser_491168.js
@@ -1,14 +1,20 @@
 "use strict";
 
 const REFERRER1 = "http://example.org/?" + Date.now();
 const REFERRER2 = "http://example.org/?" + Math.random();
 
 add_task(function* () {
+  function* checkDocumentReferrer(referrer, msg) {
+    yield ContentTask.spawn(gBrowser.selectedBrowser, { referrer, msg }, function* (args) {
+      Assert.equal(content.document.referrer, args.referrer, args.msg);
+    });
+  }
+
   // Add a new tab.
   let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
 
   // Load a new URI with a specific referrer.
   let referrerURI = Services.io.newURI(REFERRER1, null, null);
   browser.loadURI("http://example.org", referrerURI, null);
@@ -17,26 +23,20 @@ add_task(function* () {
   yield TabStateFlusher.flush(browser);
   let tabState = JSON.parse(ss.getTabState(tab));
   is(tabState.entries[0].referrer,  REFERRER1,
      "Referrer retrieved via getTabState matches referrer set via loadURI.");
 
   tabState.entries[0].referrer = REFERRER2;
   yield promiseTabState(tab, tabState);
 
-  is((yield promiseDocumentReferrer()), REFERRER2,
-     "document.referrer matches referrer set via setTabState.");
+  yield checkDocumentReferrer(REFERRER2,
+    "document.referrer matches referrer set via setTabState.");
   gBrowser.removeCurrentTab();
 
   // Restore the closed tab.
   tab = ss.undoCloseTab(window, 0);
   yield promiseTabRestored(tab);
 
-  is((yield promiseDocumentReferrer()), REFERRER2,
-     "document.referrer is still correct after closing and reopening the tab.");
+  yield checkDocumentReferrer(REFERRER2,
+    "document.referrer is still correct after closing and reopening the tab.");
   gBrowser.removeCurrentTab();
 });
-
-function promiseDocumentReferrer() {
-  return ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
-    return content.document.referrer;
-  });
-}
--- a/browser/components/sessionstore/test/browser_500328.js
+++ b/browser/components/sessionstore/test/browser_500328.js
@@ -38,20 +38,19 @@ function checkState(tab) {
       popStateCount++;
       // When content fires a PopStateEvent and we observe it from a chrome event
       // listener (as we do here, and, thankfully, nowhere else in the tree), the
       // state object will be a cross-compartment wrapper to an object that was
       // deserialized in the content scope. And in this case, since RegExps are
       // not currently Xrayable (see bug 1014991), trying to pull |obj3| (a RegExp)
       // off of an Xrayed Object won't work. So we need to waive.
       ContentTask.spawn(tab.linkedBrowser, aEvent.state, function(state) {
-        return Cu.waiveXrays(state).obj3.toString();
-      }).then(function(stateStr) {
-        is(stateStr, '/^a$/', "second popstate object.");
-
+        Assert.equal(Cu.waiveXrays(state).obj3.toString(),
+          "/^a$/", "second popstate object.");
+      }).then(function() {
         // Make sure that the new-elem node is present in the document.  If it's
         // not, then this history entry has a different doc identifier than the
         // previous entry, which is bad.
         let doc = contentWindow.document;
         let newElem = doc.getElementById("new-elem");
         ok(newElem, "doc should contain new-elem.");
         newElem.parentNode.removeChild(newElem);
         ok(!doc.getElementById("new-elem"), "new-elem should be removed.");
--- a/browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js
+++ b/browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js
@@ -23,14 +23,14 @@ add_task(function*() {
   let state = JSON.parse(ss.getTabState(tab));
   let entries = state.entries;
   is(entries.length, 1, "There should only be one entry");
   is(entries[0].url, PAGE_2, "Should have PAGE_2 as the sole history entry");
   is(browser.currentURI.spec, PAGE_2, "Should have PAGE_2 as the browser currentURI");
 
   yield ContentTask.spawn(browser, PAGE_2, function*(PAGE_2) {
     docShell.QueryInterface(Ci.nsIWebNavigation);
-    is(docShell.currentURI.spec, PAGE_2,
+    Assert.equal(docShell.currentURI.spec, PAGE_2,
        "Content should have PAGE_2 as the browser currentURI");
   });
 
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/components/sessionstore/test/browser_purge_shistory.js
+++ b/browser/components/sessionstore/test/browser_purge_shistory.js
@@ -11,17 +11,18 @@ const TAB_STATE = {
   index: 1,
 };
 
 function checkTabContents(browser) {
   return ContentTask.spawn(browser, null, function* () {
     let Ci = Components.interfaces;
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory.QueryInterface(Ci.nsISHistoryInternal);
-    return history && history.count == 1 && content.document.documentURI == "about:mozilla";
+    Assert.ok(history && history.count == 1 && content.document.documentURI == "about:mozilla",
+      "expected tab contents found");
   });
 }
 
 add_task(function* () {
   // Create a new tab.
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
@@ -38,21 +39,21 @@ add_task(function* () {
   // Prepare the tab state.
   let promise = promiseTabRestoring(tab2);
   ss.setTabState(tab2, JSON.stringify(TAB_STATE));
   ok(tab2.hasAttribute("pending"), "tab is pending");
   yield promise;
 
   // Purge session history.
   Services.obs.notifyObservers(null, "browser:purge-session-history", "");
-  ok((yield checkTabContents(browser)), "expected tab contents found");
+  yield checkTabContents(browser);
   ok(tab2.hasAttribute("pending"), "tab is still pending");
 
   // Kick off tab restoration.
   gBrowser.selectedTab = tab2;
   yield promiseTabRestored(tab2);
-  ok((yield checkTabContents(browser2)), "expected tab contents found");
+  yield checkTabContents(browser2);
   ok(!tab2.hasAttribute("pending"), "tab is not pending anymore");
 
   // Cleanup.
   gBrowser.removeTab(tab2);
   gBrowser.removeTab(tab);
 });
--- a/browser/components/sessionstore/test/browser_replace_load.js
+++ b/browser/components/sessionstore/test/browser_replace_load.js
@@ -35,19 +35,18 @@ var testSwitchToTab = Task.async(functio
   // Switch-to-tab with a similar URI.
   switchToTabHavingURI(url, false, options);
 
   // Tab should now restore
   yield promiseTabRestored(tab);
   is(browser.currentURI.spec, url, "correct URL loaded");
 
   // Check that we didn't lose any history entries.
-  let count = yield ContentTask.spawn(browser, null, function* () {
+  yield ContentTask.spawn(browser, null, function* () {
     let Ci = Components.interfaces;
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory.QueryInterface(Ci.nsISHistoryInternal);
-    return history && history.count;
+    Assert.equal(history && history.count, 3, "three history entries");
   });
-  is(count, 3, "three history entries");
 
   // Cleanup.
   gBrowser.removeTab(tab);
 });
--- a/browser/components/sessionstore/test/browser_sessionStoreContainer.js
+++ b/browser/components/sessionstore/test/browser_sessionStoreContainer.js
@@ -1,30 +1,26 @@
 /* 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/. */
 
-function retrieveUserContextId(browser) {
-  return ContentTask.spawn(browser, null, function* () {
-    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
-    return loadContext.originAttributes.userContextId;
-  });
-}
-
-add_task(function() {
+add_task(function* () {
   for (let i = 0; i < 3; ++i) {
     let tab = gBrowser.addTab("http://example.com/", {userContextId: i});
     let browser = tab.linkedBrowser;
 
     yield promiseBrowserLoaded(browser);
 
     let tab2 = gBrowser.duplicateTab(tab);
     let browser2 = tab2.linkedBrowser;
     yield promiseTabRestored(tab2)
 
-    let userContextId = yield retrieveUserContextId(browser2);
-    is(userContextId, i, "The docShell has the correct userContextId");
+    yield ContentTask.spawn(browser2, { expectedId: i }, function* (args) {
+      let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
+      Assert.equal(loadContext.originAttributes.userContextId,
+        args.expectedId, "The docShell has the correct userContextId");
+    });
 
     yield promiseRemoveTab(tab);
     yield promiseRemoveTab(tab2);
   }
 });
 
--- a/browser/components/sessionstore/test/browser_switch_remoteness.js
+++ b/browser/components/sessionstore/test/browser_switch_remoteness.js
@@ -1,18 +1,19 @@
 "use strict";
 
 const URL = "http://example.com/browser_switch_remoteness_";
 
-function countHistoryEntries(browser) {
-  return ContentTask.spawn(browser, null, function* () {
+function countHistoryEntries(browser, expected) {
+  return ContentTask.spawn(browser, { expected }, function* (args) {
     let Ci = Components.interfaces;
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory.QueryInterface(Ci.nsISHistoryInternal);
-    return history && history.count;
+    Assert.equal(history && history.count, args.expected,
+      "correct number of shistory entries");
   });
 }
 
 add_task(function* () {
   // Add a new tab.
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
@@ -25,23 +26,21 @@ add_task(function* () {
   // Load more pages than we would save to disk on a clean shutdown.
   for (let i = 0; i < MAX_BACK + 2; i++) {
     browser.loadURI(URL + i);
     yield promiseBrowserLoaded(browser);
     ok(browser.isRemoteBrowser, "browser is still remote");
   }
 
   // Check we have the right number of shistory entries.
-  let count = yield countHistoryEntries(browser);
-  is(count, MAX_BACK + 2, "correct number of shistory entries");
+  yield countHistoryEntries(browser, MAX_BACK + 2);
 
   // Load a non-remote page.
   browser.loadURI("about:robots");
   yield promiseTabRestored(tab);
   ok(!browser.isRemoteBrowser, "browser is not remote anymore");
 
   // Check that we didn't lose any shistory entries.
-  count = yield countHistoryEntries(browser);
-  is(count, MAX_BACK + 3, "correct number of shistory entries");
+  yield countHistoryEntries(browser, MAX_BACK + 3);
 
   // Cleanup.
   gBrowser.removeTab(tab);
 });
--- a/browser/components/translation/test/browser_translation_bing.js
+++ b/browser/components/translation/test/browser_translation_bing.js
@@ -43,17 +43,17 @@ add_task(function* test_bing_translation
     Cu.import("resource:///modules/translation/BingTranslator.jsm");
     Cu.import("resource:///modules/translation/TranslationDocument.jsm");
 
     let client = new BingTranslator(
       new TranslationDocument(content.document), "fr", "en");
     let result = yield client.translate();
 
     // XXXmikedeboer; here you would continue the test/ content inspection.
-    ok(result, "There should be a result");
+    Assert.ok(result, "There should be a result");
   });
 
   gBrowser.removeTab(tab);
 });
 
 /**
  * Ensures that the BingTranslator handles out-of-valid-key response
  * correctly. Sometimes Bing Translate replies with
@@ -84,17 +84,17 @@ add_task(function* test_handling_out_of_
     try {
       yield client.translate();
     } catch (ex) {
       // It is alright that the translation fails.
     }
     client._resetToken();
 
     // Checking if the client detected service and unavailable.
-    ok(client._serviceUnavailable, "Service should be detected unavailable.");
+    Assert.ok(client._serviceUnavailable, "Service should be detected unavailable.");
   });
 
   // Cleaning up.
   Services.prefs.setCharPref(kClientIdPref, "testClient");
   gBrowser.removeTab(tab);
 });
 
 /**
--- a/browser/components/translation/test/browser_translation_yandex.js
+++ b/browser/components/translation/test/browser_translation_yandex.js
@@ -42,17 +42,17 @@ add_task(function* test_yandex_translati
   yield ContentTask.spawn(browser, null, function*() {
     Cu.import("resource:///modules/translation/TranslationDocument.jsm");
     Cu.import("resource:///modules/translation/YandexTranslator.jsm");
 
     let client = new YandexTranslator(
       new TranslationDocument(content.document), "fr", "en");
     let result = yield client.translate();
 
-    ok(result, "There should be a result.");
+    Assert.ok(result, "There should be a result.");
   });
 
   gBrowser.removeTab(tab);
 });
 
 /**
  * Ensure that Yandex.Translate is propertly attributed.
  */
--- a/browser/components/uitour/UITour-lib.js
+++ b/browser/components/uitour/UITour-lib.js
@@ -235,20 +235,19 @@ if (typeof Mozilla == 'undefined') {
   };
 
   /**
    * Request the browser open the Firefox Accounts page.
    *
    * @param {Object} extraURLCampaignParams - An object containing additional
    * paramaters for the URL opened by the browser for reasons of promotional
    * campaign tracking. Each attribute of the object must have a name that
-   * begins with "utm_" and a value that is a string. Both the name and value
-   * must contain only alphanumeric characters, dashes or underscores (meaning
-   * that you are limited to values that don't need encoding, as any such
-   * characters in the name or value will be rejected.)
+   * begins with "utm_" and a value that is a string that contains only only
+   * alphanumeric characters, dashes or underscores. The values may be any
+   * string and will automatically be encoded.
    */
   Mozilla.UITour.showFirefoxAccounts = function(extraURLCampaignParams) {
     _sendEvent('showFirefoxAccounts', {
       extraURLCampaignParams: JSON.stringify(extraURLCampaignParams),
     });
   };
 
   Mozilla.UITour.resetFirefox = function() {
--- a/browser/components/uitour/test/browser.ini
+++ b/browser/components/uitour/test/browser.ini
@@ -34,17 +34,17 @@ skip-if = os == "linux" || e10s # Linux:
 skip-if = e10s # Bug 1240747 - UITour.jsm not e10s friendly
 [browser_UITour_forceReaderMode.js]
 [browser_UITour_heartbeat.js]
 skip-if = e10s # Bug 1240747 - UITour.jsm not e10s friendly.
 [browser_UITour_loop.js]
 skip-if = true # Bug 1225832 - New Loop architecture is not compatible with test.
 [browser_UITour_loop_panel.js]
 [browser_UITour_modalDialog.js]
-skip-if = os != "mac" || e10s # modal dialog disabling only working on OS X. Bug 1240747 - UITour.jsm not e10s friendly
+skip-if = os != "mac" # modal dialog disabling only working on OS X.
 [browser_UITour_observe.js]
 skip-if = e10s # Bug 1240747 - UITour.jsm not e10s friendly.
 [browser_UITour_panel_close_annotation.js]
 skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
 [browser_UITour_pocket.js]
 skip-if = os == "linux" || e10s || debug # Bug 1240747 - UITour.jsm not e10s friendly.
 [browser_UITour_registerPageID.js]
 [browser_UITour_resetProfile.js]
--- a/browser/extensions/loop/bootstrap.js
+++ b/browser/extensions/loop/bootstrap.js
@@ -596,28 +596,35 @@ var WindowListener = {
         if (this._browserSharePaused || !this._listeningToTabSelect) {
           return;
         }
 
         let browser = gBrowser.selectedBrowser;
 
         let cursor = document.getElementById("loop-remote-cursor");
         if (!cursor) {
-          cursor = document.createElement("image");
+          // Create a container to keep the pointer inside.
+          // This allows us to hide the overflow when out of bounds.
+          let cursorContainer = document.createElement("div");
+          cursorContainer.setAttribute("id", "loop-remote-cursor-container");
+
+          cursor = document.createElement("img");
           cursor.setAttribute("id", "loop-remote-cursor");
+
+          cursorContainer.appendChild(cursor);
+          // Note that browser.parent is a xul:stack so container will use
+          // 100% of space if no other constrains added.
+          browser.parentNode.appendChild(cursorContainer);
         }
 
-        // Update the cursor's position.
-        cursor.setAttribute("left",
-                            cursorData.ratioX * browser.boxObject.width);
-        cursor.setAttribute("top",
-                            cursorData.ratioY * browser.boxObject.height);
-
-        // browser's parent is a xul:stack, so positioning with left/top works.
-        browser.parentNode.appendChild(cursor);
+        // Update the cursor's position with CSS.
+        cursor.style.left =
+          Math.abs(cursorData.ratioX * browser.boxObject.width) + "px";
+        cursor.style.top =
+          Math.abs(cursorData.ratioY * browser.boxObject.height) + "px";
       },
 
       /**
        *  Removes the remote cursor from the screen
        *
        *  @param browser OPT browser where the cursor should be removed from.
        */
       removeRemoteCursor: function() {
--- a/browser/extensions/loop/chrome/content/modules/MozLoopAPI.jsm
+++ b/browser/extensions/loop/chrome/content/modules/MozLoopAPI.jsm
@@ -139,16 +139,24 @@ const kPushMessageName = "Loop:Message:P
 const kPushSubscription = "pushSubscription";
 const kRoomsPushPrefix = "Rooms:";
 const kMauPrefMap = new Map(
   Object.getOwnPropertyNames(LOOP_MAU_TYPE).map(name => {
     let parts = name.toLowerCase().split("_");
     return [LOOP_MAU_TYPE[name], parts[0] + parts[1].charAt(0).toUpperCase() + parts[1].substr(1)];
   })
 );
+
+/**
+ * WARNING: Every function in kMessageHandlers must call the reply() function,
+ * as otherwise the content requesters can be left hanging.
+ *
+ * Ideally, we should rewrite them to handle failure/long times better, at which
+ * point this could be relaxed slightly.
+ */
 const kMessageHandlers = {
   /**
    * Start browser sharing, which basically means to start listening for tab
    * switches and passing the new window ID to the sender whenever that happens.
    *
    * @param {Object}   message Message meant for the handler function, containing
    *                           the following parameters in its `data` property:
    *                           [
--- a/browser/extensions/loop/chrome/content/panels/vendor/simpleSlideshow.js
+++ b/browser/extensions/loop/chrome/content/panels/vendor/simpleSlideshow.js
@@ -94,20 +94,18 @@ loop.SimpleSlideshow = function () {
 
     propTypes: {
       data: React.PropTypes.array.isRequired
     },
     render: function () {
       var slidesNodes = this.props.data.map(function (slideNode, index) {
         var isActive = state.currentSlide === index;
         return React.createElement(Slide, { active: isActive,
-          imageAlt: slideNode.imageAlt,
           imageClass: slideNode.imageClass,
           indexClass: slideNode.id,
-          key: slideNode.id,
           text: slideNode.text,
           title: slideNode.title });
       });
       return React.createElement(
         "div",
         { className: "slides" },
         slidesNodes
       );
--- a/browser/extensions/loop/chrome/content/shared/css/conversation.css
+++ b/browser/extensions/loop/chrome/content/shared/css/conversation.css
@@ -371,16 +371,54 @@ html[dir="rtl"] .settings-menu.dropdown-
 }
 
 @keyframes rotate-spinner {
   to {
     transform: rotate(360deg);
   }
 }
 
+/* Stream paused */
+.focus-stream > .room-inner-info-area > .remote-stream-paused {
+  background-color: #fff;
+  border: solid 1px #a6a6a6;
+  box-shadow: 0 2px 4px rgba(0,0,0,.25);
+  display: inline-block;
+  margin: auto;
+  padding: 10px 15px;
+}
+
+.focus-stream > .room-inner-info-area > .remote-stream-paused > h1 {
+  font-size: 1.6rem;
+  line-height: 3rem;
+  padding-left: 42px;
+  position: relative;
+  text-align: left;
+}
+
+.focus-stream > .room-inner-info-area > .remote-stream-paused > h1:before {
+  background: url("../img/paused-hello.svg") center center no-repeat;
+  content: "";
+  display: block;
+  height: 30px;
+  left: 0;
+  position: absolute;
+  width: 32px;
+}
+
+html[dir="rtl"] .focus-stream > .room-inner-info-area > .remote-stream-paused > h1 {
+  padding-left: 0;
+  padding-right: 42px;
+}
+
+html[dir="rtl"] .focus-stream > .room-inner-info-area > .remote-stream-paused > h1:before {
+  right: 0;
+  transform: scaleX(-1);
+}
+
 .remote > .avatar {
   /* make visually distinct from local avatar */
   opacity: 0.25;
 }
 
 /* Force full height on all parents up to the video elements
  * this way we can ensure the aspect ratio and use height 100%
  * on the video element
@@ -590,16 +628,20 @@ body[platform="win"] .share-service-drop
      chat and video displays. */
   width: calc(100% - 200px);
   /* 100% height to fill up media-layout, thus forcing other elements into the
      second column that's 200px wide */
   height: 100%;
   background-color: #d8d8d8;
 }
 
+.media-wrapper > .focus-stream.screen-sharing-paused > .remote-video-box {
+  display: none;
+}
+
 .media-wrapper > .local {
   flex: 0 1 auto;
   width: 200px;
   height: 150px;
 }
 
 .media-wrapper > .local .local-video,
 .media-wrapper > .focus-stream > .local .local-video {
--- a/browser/extensions/loop/chrome/content/shared/img/firefox-hello_logo.svg
+++ b/browser/extensions/loop/chrome/content/shared/img/firefox-hello_logo.svg
@@ -1,1 +1,1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 225 50"><path fill="#1E92CE" d="M146.3,20.2c-0.1-0.1-0.2-0.2-0.4-0.3c-0.2-0.1-0.3-0.1-0.5-0.1c-0.2,0-0.3,0-0.5,0.1 c-0.2,0.1-0.3,0.2-0.4,0.3c-0.1,0.1-0.2,0.3-0.3,0.4c-0.1,0.2-0.1,0.3-0.1,0.5c0,0.2,0,0.4,0.1,0.5c0.1,0.2,0.2,0.3,0.3,0.4 c0.1,0.1,0.2,0.2,0.4,0.3c0.2,0.1,0.3,0.1,0.5,0.1c0.2,0,0.3,0,0.5-0.1c0.2-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4 c0.1-0.2,0.1-0.3,0.1-0.5c0-0.2,0-0.3-0.1-0.5C146.5,20.4,146.4,20.3,146.3,20.2z M146.3,21.5c-0.1,0.1-0.1,0.2-0.2,0.3 c-0.1,0.1-0.2,0.2-0.3,0.2c-0.1,0.1-0.3,0.1-0.4,0.1c-0.1,0-0.3,0-0.4-0.1c-0.1-0.1-0.2-0.1-0.3-0.2c-0.1-0.1-0.2-0.2-0.2-0.3 c-0.1-0.1-0.1-0.3-0.1-0.4c0-0.2,0-0.3,0.1-0.4c0.1-0.1,0.1-0.2,0.2-0.3c0.1-0.1,0.2-0.2,0.3-0.2c0.1-0.1,0.3-0.1,0.4-0.1 c0.1,0,0.3,0,0.4,0.1c0.1,0.1,0.2,0.1,0.3,0.2c0.1,0.1,0.2,0.2,0.2,0.3c0.1,0.1,0.1,0.3,0.1,0.4C146.4,21.2,146.4,21.4,146.3,21.5 z M145.7,21.4C145.7,21.3,145.6,21.3,145.7,21.4c-0.1-0.1-0.1-0.1-0.1-0.2c0,0,0,0,0,0c0.1,0,0.2,0,0.3-0.1 c0.1-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.1,0-0.2c0,0,0-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1c-0.1,0-0.1,0-0.2,0H145v1.4h0.2v-0.6 c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0.1,0.1,0.1,0.1c0,0.1,0.1,0.1,0.1,0.2l0.1,0.2h0.3L145.7,21.4C145.7,21.4,145.7,21.4,145.7,21.4z M145.3,21h-0.1v-0.5h0.1c0.1,0,0.2,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0-0.1,0.1 C145.4,21,145.4,21,145.3,21z M54.4,37.8h4.1V26.5h6.8v-3.3h-6.8v-6.7h8.4l0.5-3.3h-13V37.8z M71.6,11.9c-1.5,0-2.6,1.2-2.6,2.6 c0,1.4,1.1,2.6,2.5,2.6c1.5,0,2.6-1.2,2.6-2.6C74.1,13.1,73,11.9,71.6,11.9z M69.5,37.8h3.9V19.4l-3.9,0.7V37.8z M85.4,19.3 c-1.7,0-3.2,0.9-4.6,2.9c0-1-0.2-2-0.7-2.8l-3.6,0.9c0.4,1.1,0.6,2.6,0.6,4.8v12.7h3.9V25.6c0.4-1.5,1.7-2.7,3.4-2.7 c0.4,0,0.7,0.1,1.1,0.2l1.2-3.6C86.3,19.4,86,19.3,85.4,19.3z M94,19.4c-2.3,0-4,0.7-5.5,2.4c-1.6,1.8-2.2,3.9-2.2,7 c0,5.7,3.2,9.4,8.3,9.4c2.4,0,4.6-0.8,6.4-2.4l-1.5-2.4c-1.3,1.2-2.8,1.8-4.5,1.8c-3.5,0-4.4-2.6-4.4-5.1v-0.3h10.7V29 c0-4.2-0.8-6.4-2.3-7.8C97.4,19.9,95.7,19.4,94,19.4z M90.6,27c0-2.9,1.2-4.6,3.4-4.6c1.9,0,3.2,1.7,3.2,4.6H90.6z M108,19.8v-2.7 c0-1.6,0.9-2.5,2.2-2.5c0.7,0,1.3,0.2,2.2,0.6l1.3-2.5c-1.2-0.7-2.5-1-4-1c-3.2,0-5.5,1.7-5.5,5.5c0,1.7,0.1,2.7,0.1,2.7h-1.7v2.7 h1.7v15.3h3.9V22.5h3.7l1-2.7H108z M119.9,19.4c-4.7,0-7.8,3.7-7.8,9.4c0,5.7,3,9.4,7.9,9.4c4.9,0,7.9-3.6,7.9-9.4 C127.9,23.2,125,19.4,119.9,19.4z M120.1,35.3c-2.3,0-3.6-1.5-3.6-6.7c0-4.4,1.1-6.2,3.5-6.2c2.3,0,3.7,1.5,3.7,6.6 C123.6,33.4,122.4,35.3,120.1,35.3z M143.2,19.8h-4.5c-0.5,0.7-2.3,4.3-2.8,5.5c-0.9-1.7-2.4-4.5-3.3-5.8l-4.2,0.9l5.2,7.7 l-6.7,9.7h4.9c0.7-1,3.2-5.4,3.9-6.7c0.4,0.6,3.3,5.7,3.9,6.7h4.9L138,28L143.2,19.8z M165.8,23.6h-10.3V13.3h-2.9v24.6h2.9V26 h10.3v11.9h2.9V13.3h-2.9V23.6z M179,19.5c-2.1,0-3.9,0.8-5.3,2.5c-1.5,1.8-2.1,3.7-2.1,6.7c0,5.9,3,9.5,7.9,9.5 c2.3,0,4.4-0.8,6-2.2l-1.1-1.8c-1.3,1.1-2.6,1.7-4.4,1.7c-1.8,0-3.4-0.6-4.4-2.2c-0.6-0.9-0.8-2.2-0.8-3.9v-0.4h11.1V29 c-0.1-4.3-0.5-5.9-2-7.5C182.6,20.2,181,19.5,179,19.5z M174.8,27.3c0.1-3.7,1.6-5.6,4.1-5.6c1.4,0,2.6,0.6,3.2,1.6 c0.5,0.9,0.8,2,0.8,4H174.8z M193.1,36.1c-0.8,0-1-0.4-1-1.8V16c0-2.7-0.5-4.2-0.5-4.2l-2.8,0.5c0,0,0.4,1.3,0.4,3.7v18.9 c0,1.3,0.3,2,0.9,2.5c0.5,0.5,1.3,0.8,2.1,0.8c0.8,0,1.1-0.1,1.8-0.4l-0.6-1.8C193.4,36,193.2,36.1,193.1,36.1z M200.3,36.1 c-0.8,0-1-0.4-1-1.8V16c0-2.7-0.5-4.2-0.5-4.2l-2.8,0.5c0,0,0.4,1.3,0.4,3.7v18.9c0,1.3,0.3,2,0.9,2.5c0.5,0.5,1.2,0.8,2.1,0.8 c0.7,0,1.1-0.1,1.8-0.4l-0.6-1.8C200.6,36,200.4,36.1,200.3,36.1z M216.3,22.6c-1.2-1.8-3.1-3.1-6.1-3.1c-4.7,0-7.6,3.5-7.6,9.4 c0,5.9,2.8,9.5,7.7,9.5c4.5,0,7.7-3.2,7.7-9.1C218,26.3,217.5,24.2,216.3,22.6z M214.3,33.3c-0.6,1.7-2,2.7-3.9,2.7 c-1.5,0-3-0.7-3.6-1.8c-0.7-1.1-1.1-3.3-1.1-5.8c0-2.1,0.3-3.5,0.9-4.7c0.6-1.2,2.1-1.9,3.6-1.9c1.5,0,3,0.7,3.8,2.3 c0.6,1.2,0.8,2.9,0.8,5.4C214.8,31.2,214.7,32.2,214.3,33.3z M26.1,7.7C16.1,7.7,8,14.9,8,23.6c0,4.4,2,8.3,5.2,11.2 c-0.6,2-1.7,4.7-3.9,7.3c0.4,0.7,6.6-1.7,11-3.4c1.8,0.5,3.7,0.8,5.7,0.8c10,0,18.1-7.1,18.1-15.9C44.1,14.9,36,7.7,26.1,7.7z M31.5,17.3c1.3,0,2.4,1.1,2.4,2.4c0,1.3-1.1,2.4-2.4,2.4c-1.3,0-2.4-1.1-2.4-2.4C29.1,18.3,30.2,17.3,31.5,17.3z M20.6,17.3 c1.3,0,2.4,1.1,2.4,2.4c0,1.3-1.1,2.4-2.4,2.4c-1.3,0-2.4-1.1-2.4-2.4C18.2,18.3,19.3,17.3,20.6,17.3z M26.1,34.7 C26.1,34.7,26,34.7,26.1,34.7c-0.1,0-0.1,0-0.1,0c-4.8,0-10.2-3.1-11.5-8.4c3.3,1.5,7.8,2.2,11.5,2.2c3.7,0,8.3-0.7,11.5-2.2 C36.3,31.6,30.9,34.7,26.1,34.7z"/></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 225 50"><path fill="#06A6E0" d="M146.3,20.2c-0.1-0.1-0.2-0.2-0.4-0.3c-0.2-0.1-0.3-0.1-0.5-0.1c-0.2,0-0.3,0-0.5,0.1 c-0.2,0.1-0.3,0.2-0.4,0.3c-0.1,0.1-0.2,0.3-0.3,0.4c-0.1,0.2-0.1,0.3-0.1,0.5c0,0.2,0,0.4,0.1,0.5c0.1,0.2,0.2,0.3,0.3,0.4 c0.1,0.1,0.2,0.2,0.4,0.3c0.2,0.1,0.3,0.1,0.5,0.1c0.2,0,0.3,0,0.5-0.1c0.2-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4 c0.1-0.2,0.1-0.3,0.1-0.5c0-0.2,0-0.3-0.1-0.5C146.5,20.4,146.4,20.3,146.3,20.2z M146.3,21.5c-0.1,0.1-0.1,0.2-0.2,0.3 c-0.1,0.1-0.2,0.2-0.3,0.2c-0.1,0.1-0.3,0.1-0.4,0.1c-0.1,0-0.3,0-0.4-0.1c-0.1-0.1-0.2-0.1-0.3-0.2c-0.1-0.1-0.2-0.2-0.2-0.3 c-0.1-0.1-0.1-0.3-0.1-0.4c0-0.2,0-0.3,0.1-0.4c0.1-0.1,0.1-0.2,0.2-0.3c0.1-0.1,0.2-0.2,0.3-0.2c0.1-0.1,0.3-0.1,0.4-0.1 c0.1,0,0.3,0,0.4,0.1c0.1,0.1,0.2,0.1,0.3,0.2c0.1,0.1,0.2,0.2,0.2,0.3c0.1,0.1,0.1,0.3,0.1,0.4C146.4,21.2,146.4,21.4,146.3,21.5 z M145.7,21.4C145.7,21.3,145.6,21.3,145.7,21.4c-0.1-0.1-0.1-0.1-0.1-0.2c0,0,0,0,0,0c0.1,0,0.2,0,0.3-0.1 c0.1-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.1,0-0.2c0,0,0-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1c-0.1,0-0.1,0-0.2,0H145v1.4h0.2v-0.6 c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0.1,0.1,0.1,0.1c0,0.1,0.1,0.1,0.1,0.2l0.1,0.2h0.3L145.7,21.4C145.7,21.4,145.7,21.4,145.7,21.4z M145.3,21h-0.1v-0.5h0.1c0.1,0,0.2,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0-0.1,0.1 C145.4,21,145.4,21,145.3,21z M54.4,37.8h4.1V26.5h6.8v-3.3h-6.8v-6.7h8.4l0.5-3.3h-13V37.8z M71.6,11.9c-1.5,0-2.6,1.2-2.6,2.6 c0,1.4,1.1,2.6,2.5,2.6c1.5,0,2.6-1.2,2.6-2.6C74.1,13.1,73,11.9,71.6,11.9z M69.5,37.8h3.9V19.4l-3.9,0.7V37.8z M85.4,19.3 c-1.7,0-3.2,0.9-4.6,2.9c0-1-0.2-2-0.7-2.8l-3.6,0.9c0.4,1.1,0.6,2.6,0.6,4.8v12.7h3.9V25.6c0.4-1.5,1.7-2.7,3.4-2.7 c0.4,0,0.7,0.1,1.1,0.2l1.2-3.6C86.3,19.4,86,19.3,85.4,19.3z M94,19.4c-2.3,0-4,0.7-5.5,2.4c-1.6,1.8-2.2,3.9-2.2,7 c0,5.7,3.2,9.4,8.3,9.4c2.4,0,4.6-0.8,6.4-2.4l-1.5-2.4c-1.3,1.2-2.8,1.8-4.5,1.8c-3.5,0-4.4-2.6-4.4-5.1v-0.3h10.7V29 c0-4.2-0.8-6.4-2.3-7.8C97.4,19.9,95.7,19.4,94,19.4z M90.6,27c0-2.9,1.2-4.6,3.4-4.6c1.9,0,3.2,1.7,3.2,4.6H90.6z M108,19.8v-2.7 c0-1.6,0.9-2.5,2.2-2.5c0.7,0,1.3,0.2,2.2,0.6l1.3-2.5c-1.2-0.7-2.5-1-4-1c-3.2,0-5.5,1.7-5.5,5.5c0,1.7,0.1,2.7,0.1,2.7h-1.7v2.7 h1.7v15.3h3.9V22.5h3.7l1-2.7H108z M119.9,19.4c-4.7,0-7.8,3.7-7.8,9.4c0,5.7,3,9.4,7.9,9.4c4.9,0,7.9-3.6,7.9-9.4 C127.9,23.2,125,19.4,119.9,19.4z M120.1,35.3c-2.3,0-3.6-1.5-3.6-6.7c0-4.4,1.1-6.2,3.5-6.2c2.3,0,3.7,1.5,3.7,6.6 C123.6,33.4,122.4,35.3,120.1,35.3z M143.2,19.8h-4.5c-0.5,0.7-2.3,4.3-2.8,5.5c-0.9-1.7-2.4-4.5-3.3-5.8l-4.2,0.9l5.2,7.7 l-6.7,9.7h4.9c0.7-1,3.2-5.4,3.9-6.7c0.4,0.6,3.3,5.7,3.9,6.7h4.9L138,28L143.2,19.8z M165.8,23.6h-10.3V13.3h-2.9v24.6h2.9V26 h10.3v11.9h2.9V13.3h-2.9V23.6z M179,19.5c-2.1,0-3.9,0.8-5.3,2.5c-1.5,1.8-2.1,3.7-2.1,6.7c0,5.9,3,9.5,7.9,9.5 c2.3,0,4.4-0.8,6-2.2l-1.1-1.8c-1.3,1.1-2.6,1.7-4.4,1.7c-1.8,0-3.4-0.6-4.4-2.2c-0.6-0.9-0.8-2.2-0.8-3.9v-0.4h11.1V29 c-0.1-4.3-0.5-5.9-2-7.5C182.6,20.2,181,19.5,179,19.5z M174.8,27.3c0.1-3.7,1.6-5.6,4.1-5.6c1.4,0,2.6,0.6,3.2,1.6 c0.5,0.9,0.8,2,0.8,4H174.8z M193.1,36.1c-0.8,0-1-0.4-1-1.8V16c0-2.7-0.5-4.2-0.5-4.2l-2.8,0.5c0,0,0.4,1.3,0.4,3.7v18.9 c0,1.3,0.3,2,0.9,2.5c0.5,0.5,1.3,0.8,2.1,0.8c0.8,0,1.1-0.1,1.8-0.4l-0.6-1.8C193.4,36,193.2,36.1,193.1,36.1z M200.3,36.1 c-0.8,0-1-0.4-1-1.8V16c0-2.7-0.5-4.2-0.5-4.2l-2.8,0.5c0,0,0.4,1.3,0.4,3.7v18.9c0,1.3,0.3,2,0.9,2.5c0.5,0.5,1.2,0.8,2.1,0.8 c0.7,0,1.1-0.1,1.8-0.4l-0.6-1.8C200.6,36,200.4,36.1,200.3,36.1z M216.3,22.6c-1.2-1.8-3.1-3.1-6.1-3.1c-4.7,0-7.6,3.5-7.6,9.4 c0,5.9,2.8,9.5,7.7,9.5c4.5,0,7.7-3.2,7.7-9.1C218,26.3,217.5,24.2,216.3,22.6z M214.3,33.3c-0.6,1.7-2,2.7-3.9,2.7 c-1.5,0-3-0.7-3.6-1.8c-0.7-1.1-1.1-3.3-1.1-5.8c0-2.1,0.3-3.5,0.9-4.7c0.6-1.2,2.1-1.9,3.6-1.9c1.5,0,3,0.7,3.8,2.3 c0.6,1.2,0.8,2.9,0.8,5.4C214.8,31.2,214.7,32.2,214.3,33.3z M26.1,7.7C16.1,7.7,8,14.9,8,23.6c0,4.4,2,8.3,5.2,11.2 c-0.6,2-1.7,4.7-3.9,7.3c0.4,0.7,6.6-1.7,11-3.4c1.8,0.5,3.7,0.8,5.7,0.8c10,0,18.1-7.1,18.1-15.9C44.1,14.9,36,7.7,26.1,7.7z M31.5,17.3c1.3,0,2.4,1.1,2.4,2.4c0,1.3-1.1,2.4-2.4,2.4c-1.3,0-2.4-1.1-2.4-2.4C29.1,18.3,30.2,17.3,31.5,17.3z M20.6,17.3 c1.3,0,2.4,1.1,2.4,2.4c0,1.3-1.1,2.4-2.4,2.4c-1.3,0-2.4-1.1-2.4-2.4C18.2,18.3,19.3,17.3,20.6,17.3z M26.1,34.7 C26.1,34.7,26,34.7,26.1,34.7c-0.1,0-0.1,0-0.1,0c-4.8,0-10.2-3.1-11.5-8.4c3.3,1.5,7.8,2.2,11.5,2.2c3.7,0,8.3-0.7,11.5-2.2 C36.3,31.6,30.9,34.7,26.1,34.7z"/></svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/chrome/content/shared/img/paused-hello.svg
@@ -0,0 +1,1 @@
+<svg width="32" height="30" viewBox="0 0 32 30" xmlns="http://www.w3.org/2000/svg"><path d="M15.999 0c-8.834 0-15.999 6.135-15.999 13.7 0 3.767 1.776 7.179 4.648 9.656-.499 1.714-1.487 4.04-3.438 6.311.334.575 5.83-1.454 9.704-2.973 1.599.456 3.306.706 5.084.706 8.837 0 16.001-6.134 16.001-13.701 0-7.565-7.164-13.7-16.001-13.7zm4.742 8.186v9.823c0 .121-.035.226-.106.314-.07.088-.154.133-.25.133h-2.844c-.096 0-.18-.044-.25-.133-.07-.088-.106-.193-.106-.314v-9.823c0-.121.035-.226.106-.314.07-.088.154-.133.25-.133h2.844c.096 0 .18.044.25.133.07.088.106.193.106.314zm-5.333 0v9.823c0 .121-.035.226-.106.314-.07.088-.154.133-.25.133h-2.844c-.096 0-.18-.044-.25-.133-.07-.088-.106-.193-.106-.314v-9.823c0-.121.035-.226.106-.314.07-.088.154-.133.25-.133h2.844c.096 0 .18.044.25.133.07.088.106.193.106.314z" fill="#757575"/></svg>
--- a/browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js
+++ b/browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js
@@ -107,16 +107,17 @@ loop.store.ActiveRoomStore = (function(m
       "chatMessageExchanged",
       "localSrcMediaElement",
       "localVideoDimensions",
       "mediaConnected",
       "receivingScreenShare",
       "remoteSrcMediaElement",
       "remoteVideoDimensions",
       "remoteVideoEnabled",
+      "streamPaused",
       "screenSharingState",
       "screenShareMediaElement",
       "videoMuted"
     ],
 
     /**
      * Returns initial state data for this active room.
      *
@@ -146,16 +147,18 @@ loop.store.ActiveRoomStore = (function(m
         // Any urls (aka context) associated with the room.
         roomContextUrls: null,
         // The description for a room as stored in the context data.
         roomDescription: null,
         // Room information failed to be obtained for a reason. See ROOM_INFO_FAILURES.
         roomInfoFailure: null,
         // The name of the room.
         roomName: null,
+        // True when sharing screen has been paused.
+        streamPaused: false,
         // Social API state.
         socialShareProviders: null,
         // True if media has been connected both-ways.
         mediaConnected: false,
         // True if a chat message was sent or received during a session.
         // Read more at https://wiki.mozilla.org/Loop/Session.
         chatMessageExchanged: false
       };
@@ -263,17 +266,18 @@ loop.store.ActiveRoomStore = (function(m
         "mediaStreamDestroyed",
         "remoteVideoStatus",
         "videoDimensionsChanged",
         "startBrowserShare",
         "endScreenShare",
         "toggleBrowserSharing",
         "updateSocialShareInfo",
         "connectionStatus",
-        "mediaConnected"
+        "mediaConnected",
+        "videoScreenStreamChanged"
       ];
       // Register actions that are only used on Desktop.
       if (this._isDesktop) {
         // 'receivedTextChatMessage' and  'sendTextChatMessage' actions are only
         // registered for Telemetry. Once measured, they're unregistered.
         actions.push("receivedTextChatMessage", "sendTextChatMessage");
       }
       this.dispatcher.register(this, actions);
@@ -1203,16 +1207,28 @@ loop.store.ActiveRoomStore = (function(m
       var storeProp = (actionData.isLocal ? "local" : "remote") + "VideoDimensions";
       var nextState = {};
       nextState[storeProp] = this.getStoreState()[storeProp];
       nextState[storeProp][actionData.videoType] = actionData.dimensions;
       this.setStoreState(nextState);
     },
 
     /**
+     * Listen to screen stream changes in order to check if sharing screen
+     * has been paused.
+     *
+     * @param {sharedActions.VideoScreenStreamChanged} actionData
+     */
+    videoScreenStreamChanged: function(actionData) {
+      this.setStoreState({
+        streamPaused: !actionData.hasVideo
+      });
+    },
+
+    /**
      * Handles chat messages received and/ or about to send. If this is the first
      * chat message for the current session, register a count with telemetry.
      * It will unhook the listeners when the telemetry criteria have been
      * fulfilled to make sure we remain lean.
      * Note: the 'receivedTextChatMessage' and 'sendTextChatMessage' actions are
      *       only registered on Desktop.
      *
      * @param  {sharedActions.ReceivedTextChatMessage|SendTextChatMessage} actionData
--- a/browser/extensions/loop/chrome/content/shared/js/loopapi-client.js
+++ b/browser/extensions/loop/chrome/content/shared/js/loopapi-client.js
@@ -6,17 +6,16 @@ var loop = loop || {};
 (function() {
   "use strict";
 
   var _slice = Array.prototype.slice;
 
   var kMessageName = "Loop:Message";
   var kPushMessageName = "Loop:Message:Push";
   var kBatchMessage = "Batch";
-  var kReplyTimeoutMs = 5000;
   var gListeningForMessages = false;
   var gListenersMap = {};
   var gListeningForPushMessages = false;
   var gSubscriptionsMap = {};
   var gRootObj = window;
 
   loop._lastMessageID = 0;
 
@@ -70,30 +69,20 @@ var loop = loop || {};
         };
 
         gRootObj.addMessageListener(kMessageName, gListeningForMessages);
       }
 
       gListenersMap[seq] = resolve;
 
       gRootObj.sendAsyncMessage(kMessageName, payload);
-
-      gRootObj.setTimeout(function() {
-        // Check if the promise was already resolved before by the message handler.
-        if (!gListenersMap[seq]) {
-          return;
-        }
-        resolve();
-        delete gListenersMap[seq];
-      }, kReplyTimeoutMs);
     });
   };
 
   // These functions should only be used in unit tests.
-  loop.request.getReplyTimeoutMs = function() { return kReplyTimeoutMs; };
   loop.request.inspect = function() { return _.extend({}, gListenersMap); };
   loop.request.reset = function() {
     gListeningForMessages = false;
     gListenersMap = {};
   };
 
   loop.storedRequests = {};
 
--- a/browser/extensions/loop/chrome/content/shared/js/utils.js
+++ b/browser/extensions/loop/chrome/content/shared/js/utils.js
@@ -391,33 +391,35 @@ if (inChrome) {
    * Formats a url for display purposes. This includes converting the
    * domain to punycode, and then decoding the url.
    * Intended to be used for both display and (uglier in confusing cases) clickthrough,
    * as described by dveditz in comment 12 of the bug 1196143,
    * as well as testing the behavior case in the browser.
    *
    * @param {String}  url                   The url to format.
    * @param {String}  suppressConsoleError  For testing, call with a boolean which is true to squash the default console error.
-   * @return {Object}                       An object containing the hostname and full location.
+   * @return {Object}                       An object containing the hostname,
+   *                                        full location and protocol.
    */
   function formatURL(url, suppressConsoleError) {
     // We're using new URL to pass this through the browser's ACE/punycode
     // processing system. If the browser considers a url to need to be
     // punycode encoded for it to be displayed, then new URL will do that for
     // us. This saves us needing our own punycode library.
     // Note that URL does canonicalize hostname-only URLs,
     // adding a slash to them, but this is ok for at least HTTP(S)
     // because GET always has to specify a path, which will (by default) be
     var urlObject;
     try {
       urlObject = new URL(url);
       // Finally, ensure we look good.
       return {
         hostname: urlObject.hostname,
-        location: decodeURI(urlObject.href)
+        location: decodeURI(urlObject.href),
+        protocol: urlObject.protocol
       };
     } catch (ex) {
       if (suppressConsoleError ? !suppressConsoleError : true) {
         console.log("Error occurred whilst parsing URL: ", ex);
         console.trace();
       }
       return null;
     }
--- a/browser/extensions/loop/chrome/content/shared/js/views.js
+++ b/browser/extensions/loop/chrome/content/shared/js/views.js
@@ -601,21 +601,26 @@ loop.shared.views = function (_, mozL10n
         linkInfo: "Shared URL"
       }));
     },
 
     render: function () {
       // Bug 1196143 - formatURL sanitizes(decodes) the URL from IDN homographic attacks.
       // Try catch to not produce output if invalid url
       try {
-        var sanitizeURL = loop.shared.utils.formatURL(this.props.url, true).hostname;
+        var sanitizedURL = loop.shared.utils.formatURL(this.props.url, true);
       } catch (ex) {
         return null;
       }
 
+      // Only allow specific types of URLs.
+      if (!sanitizedURL || sanitizedURL.protocol !== "http:" && sanitizedURL.protocol !== "https:" && sanitizedURL.protocol !== "ftp:") {
+        return null;
+      }
+
       var thumbnail = this.props.thumbnail;
 
       if (!thumbnail) {
         thumbnail = this.props.useDesktopPaths ? "shared/img/icons-16x16.svg#globe" : "shared/img/icons-16x16.svg#globe";
       }
 
       var wrapperClasses = classNames({
         "context-wrapper": true,
@@ -635,17 +640,17 @@ loop.shared.views = function (_, mozL10n
           React.createElement("img", { className: "context-preview", src: thumbnail }),
           React.createElement(
             "span",
             { className: "context-info" },
             this.props.description,
             React.createElement(
               "span",
               { className: "context-url" },
-              sanitizeURL
+              sanitizedURL.hostname
             )
           )
         )
       );
     }
   });
 
   /**
@@ -661,16 +666,17 @@ loop.shared.views = function (_, mozL10n
 
     propTypes: {
       cursorStore: React.PropTypes.instanceOf(loop.store.RemoteCursorStore),
       dispatcher: React.PropTypes.object,
       displayAvatar: React.PropTypes.bool.isRequired,
       isLoading: React.PropTypes.bool.isRequired,
       mediaType: React.PropTypes.string.isRequired,
       posterUrl: React.PropTypes.string,
+      screenSharingPaused: React.PropTypes.bool,
       shareCursor: React.PropTypes.bool,
       // Expecting "local" or "remote".
       srcMediaElement: React.PropTypes.object
     },
 
     getInitialState: function () {
       return {
         videoElementSize: null
@@ -776,17 +782,17 @@ loop.shared.views = function (_, mozL10n
       }
 
       var videoElement = this.getDOMNode().querySelector("video");
       if (!videoElement || videoElement.tagName.toLowerCase() !== "video") {
         // Must be displaying the avatar view, so don't try and attach video.
         return;
       }
 
-      if (this.props.shareCursor) {
+      if (this.props.shareCursor && !this.props.screenSharingPaused) {
         videoElement.addEventListener("loadeddata", this.handleVideoDimensions);
         videoElement.addEventListener("mousemove", this.handleMouseMove);
       }
 
       // Set the src of our video element
       var attrName = "";
       if ("srcObject" in videoElement) {
         // srcObject is according to the standard.
@@ -867,16 +873,17 @@ loop.shared.views = function (_, mozL10n
       // Passing in matchMedia, allows it to be overriden for ui-showcase's
       // benefit. We expect either the override or window.matchMedia.
       matchMedia: React.PropTypes.func.isRequired,
       remotePosterUrl: React.PropTypes.string,
       remoteSrcMediaElement: React.PropTypes.object,
       renderRemoteVideo: React.PropTypes.bool.isRequired,
       screenShareMediaElement: React.PropTypes.object,
       screenSharePosterUrl: React.PropTypes.string,
+      screenSharingPaused: React.PropTypes.bool,
       showInitialContext: React.PropTypes.bool.isRequired,
       useDesktopPaths: React.PropTypes.bool.isRequired
     },
 
     isLocalMediaAbsolutelyPositioned: function (matchMedia) {
       if (!matchMedia) {
         matchMedia = this.props.matchMedia;
       }
@@ -933,17 +940,18 @@ loop.shared.views = function (_, mozL10n
     render: function () {
       var remoteStreamClasses = classNames({
         "remote": true,
         "focus-stream": !this.props.displayScreenShare
       });
 
       var screenShareStreamClasses = classNames({
         "screen": true,
-        "focus-stream": this.props.displayScreenShare
+        "focus-stream": this.props.displayScreenShare,
+        "screen-sharing-paused": this.props.screenSharingPaused
       });
 
       var mediaWrapperClasses = classNames({
         "media-wrapper": true,
         "receiving-screen-share": this.props.displayScreenShare,
         "showing-local-streams": this.props.localSrcMediaElement || this.props.localPosterUrl,
         "showing-remote-streams": this.props.remoteSrcMediaElement || this.props.remotePosterUrl || this.props.isRemoteLoading
       });
@@ -976,16 +984,17 @@ loop.shared.views = function (_, mozL10n
             { className: screenShareStreamClasses },
             React.createElement(MediaView, {
               cursorStore: this.props.cursorStore,
               dispatcher: this.props.dispatcher,
               displayAvatar: false,
               isLoading: this.props.isScreenShareLoading,
               mediaType: "screen-share",
               posterUrl: this.props.screenSharePosterUrl,
+              screenSharingPaused: this.props.screenSharingPaused,
               shareCursor: true,
               srcMediaElement: this.props.screenShareMediaElement }),
             this.props.displayScreenShare ? this.props.children : null
           ),
           React.createElement(loop.shared.views.chat.TextChatView, {
             dispatcher: this.props.dispatcher,
             showInitialContext: this.props.showInitialContext,
             useDesktopPaths: this.props.useDesktopPaths }),
--- a/browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js
+++ b/browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js
@@ -743,16 +743,27 @@ describe("loop.store.ActiveRoomStore", f
       actionData.isLocal = false;
       store.videoDimensionsChanged(new sharedActions.VideoDimensionsChanged(actionData));
 
       expect(store.getStoreState().remoteVideoDimensions)
         .to.have.property(actionData.videoType, actionData.dimensions);
     });
   });
 
+  describe("#videoScreenStreamChanged", function() {
+    it("should set streamPaused if screen stream has no video", function() {
+      var actionData = {
+        hasVideo: false
+      };
+
+      store.videoScreenStreamChanged(new sharedActions.VideoScreenStreamChanged(actionData));
+      expect(store.getStoreState().streamPaused).eql(true);
+    });
+  });
+
   describe("#updateRoomInfo", function() {
     var fakeRoomInfo;
 
     beforeEach(function() {
       fakeRoomInfo = {
         roomContextUrls: [{
           description: "fake site",
           location: "http://invalid.com",
--- a/browser/extensions/loop/chrome/content/shared/test/loopapi-client_test.js
+++ b/browser/extensions/loop/chrome/content/shared/test/loopapi-client_test.js
@@ -1,24 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 describe("loopapi-client", function() {
   "use strict";
 
   var expect = chai.expect;
-  var sandbox, clock, replyTimeoutMs;
+  var sandbox;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     window.addMessageListener = sinon.stub();
     window.removeMessageListener = sinon.stub();
     window.sendAsyncMessage = sinon.stub();
-    clock = sandbox.useFakeTimers();
-    replyTimeoutMs = loop.request.getReplyTimeoutMs();
   });
 
   afterEach(function() {
     loop.request.reset();
     loop.subscribe.reset();
     sandbox.restore();
 
     delete window.addMessageListener;
@@ -31,37 +29,52 @@ describe("loopapi-client", function() {
       var promise = loop.request("GetLoopPref", "enabled");
 
       expect(promise).to.be.an.instanceof(Promise);
       sinon.assert.calledOnce(window.sendAsyncMessage);
       sinon.assert.calledWithExactly(window.sendAsyncMessage, "Loop:Message",
         [loop._lastMessageID, "GetLoopPref", "enabled"]);
       sinon.assert.calledOnce(window.addMessageListener);
 
-      clock.tick(replyTimeoutMs);
+      // Call the added listener, so that the promise resolves.
+      window.addMessageListener.args[0][1]({
+        name: "Loop:Message",
+        data: [loop._lastMessageID, "true"]
+      });
+
       return promise;
     });
 
     it("should correct the command name", function() {
       var promise = loop.request("getLoopPref", "enabled");
 
       sinon.assert.calledWithExactly(window.sendAsyncMessage, "Loop:Message",
         [loop._lastMessageID, "GetLoopPref", "enabled"]);
 
-      clock.tick(replyTimeoutMs);
+      // Call the added listener, so that the promise resolves.
+      window.addMessageListener.args[0][1]({
+        name: "Loop:Message",
+        data: [loop._lastMessageID, "true"]
+      });
+
       return promise;
     });
 
     it("should pass all arguments in-order", function() {
       var promise = loop.request("SetLoopPref", "enabled", false, 1, 2, 3);
 
       sinon.assert.calledWithExactly(window.sendAsyncMessage, "Loop:Message",
         [loop._lastMessageID, "SetLoopPref", "enabled", false, 1, 2, 3]);
 
-      clock.tick(replyTimeoutMs);
+      // Call the added listener, so that the promise resolves.
+      window.addMessageListener.args[0][1]({
+        name: "Loop:Message",
+        data: [loop._lastMessageID, "true"]
+      });
+
       return promise;
     });
 
     it("should resolve the promise when a response is received", function() {
       var listener;
       window.addMessageListener = function(name, callback) {
         listener = callback;
       };
@@ -72,42 +85,39 @@ describe("loopapi-client", function() {
 
       listener({
         data: [loop._lastMessageID, "result"]
       });
 
       return promise;
     });
 
-    it("should cancel the message listener when no reply is received in time", function() {
-      var promise = loop.request("GetLoopPref", "enabled");
-
-      promise.then(function(result) {
-        expect(result).to.eql(undefined);
-      });
-
-      clock.tick(replyTimeoutMs);
-      return promise;
-    });
-
     it("should not start listening for messages more than once", function() {
       return new Promise(function(resolve) {
         loop.request("GetLoopPref", "enabled").then(function() {
           sinon.assert.calledOnce(window.addMessageListener);
 
           loop.request("GetLoopPref", "enabled").then(function() {
             sinon.assert.calledOnce(window.addMessageListener);
 
             resolve();
           });
 
-          clock.tick(replyTimeoutMs);
+          // Call the added listener, so that the promise resolves.
+          window.addMessageListener.args[0][1]({
+            name: "Loop:Message",
+            data: [loop._lastMessageID, "true"]
+          });
         });
 
-        clock.tick(replyTimeoutMs);
+        // Call the added listener, so that the promise resolves.
+        window.addMessageListener.args[0][1]({
+          name: "Loop:Message",
+          data: [loop._lastMessageID, "true"]
+        });
       });
     });
   });
 
   describe("loop.storeRequest", function() {
     afterEach(function() {
       loop.storedRequests = {};
     });
@@ -161,34 +171,44 @@ describe("loopapi-client", function() {
       expect(promise).to.be.an.instanceof(Promise);
       sinon.assert.calledOnce(window.sendAsyncMessage);
       sinon.assert.calledWithExactly(window.sendAsyncMessage, "Loop:Message",
         [loop._lastMessageID, "Batch", [
           [loop._lastMessageID - 2, "GetLoopPref", "enabled"],
           [loop._lastMessageID - 1, "GetLoopPref", "e10s.enabled"]]
         ]);
 
-      clock.tick(replyTimeoutMs);
+      // Call the added listener, so that the promise resolves.
+      window.addMessageListener.args[0][1]({
+        name: "Loop:Message",
+        data: [loop._lastMessageID, "true"]
+      });
+
       return promise;
     });
 
     it("should correct command names", function() {
       var promise = loop.requestMulti(
         ["GetLoopPref", "enabled"],
         // Use lowercase 'g' on purpose, it should get corrected:
         ["getLoopPref", "e10s.enabled"]
       );
 
       sinon.assert.calledWithExactly(window.sendAsyncMessage, "Loop:Message",
         [loop._lastMessageID, "Batch", [
           [loop._lastMessageID - 2, "GetLoopPref", "enabled"],
           [loop._lastMessageID - 1, "GetLoopPref", "e10s.enabled"]]
         ]);
 
-      clock.tick(replyTimeoutMs);
+      // Call the added listener, so that the promise resolves.
+      window.addMessageListener.args[0][1]({
+        name: "Loop:Message",
+        data: [loop._lastMessageID, "true"]
+      });
+
       return promise;
     });
 
     it("should resolve the promise when a response is received", function() {
       var listener;
       window.addMessageListener = function(name, callback) {
         listener = callback;
       };
--- a/browser/extensions/loop/chrome/content/shared/test/utils_test.js
+++ b/browser/extensions/loop/chrome/content/shared/test/utils_test.js
@@ -298,28 +298,30 @@ describe("loop.shared.utils", function()
       // Stub to prevent console messages.
       sandbox.stub(window.console, "log");
     });
 
     it("should decode encoded URIs", function() {
       expect(sharedUtils.formatURL("http://invalid.com/?a=Foo%20Bar"))
         .eql({
           location: "http://invalid.com/?a=Foo Bar",
-          hostname: "invalid.com"
+          hostname: "invalid.com",
+          protocol: "http:"
         });
     });
 
     it("should change some idn urls to ascii encoded", function() {
       // Note, this is based on the browser's list of what does/doesn't get
       // altered for punycode, so if the list changes this could change in the
       // future.
       expect(sharedUtils.formatURL("http://\u0261oogle.com/"))
         .eql({
           location: "http://xn--oogle-qmc.com/",
-          hostname: "xn--oogle-qmc.com"
+          hostname: "xn--oogle-qmc.com",
+          protocol: "http:"
         });
     });
 
     it("should return null if suppressConsoleError is true and the url is not valid", function() {
       expect(sharedUtils.formatURL("hinvalid//url", true)).eql(null);
     });
 
     it("should return null if suppressConsoleError is true and is a malformed URI sequence", function() {
--- a/browser/extensions/loop/chrome/content/shared/test/views_test.js
+++ b/browser/extensions/loop/chrome/content/shared/test/views_test.js
@@ -827,16 +827,26 @@ describe("VideoMuteButton", function() {
       });
 
       var node = view.getDOMNode();
 
       expect(node.querySelector(".remote").classList.contains("focus-stream")).eql(false);
       expect(node.querySelector(".screen").classList.contains("focus-stream")).eql(true);
     });
 
+    it("should mark the screen share stream as paused when screen shared has been paused", function() {
+      view = mountTestComponent({
+        screenSharingPaused: true
+      });
+
+      var node = view.getDOMNode();
+
+      expect(node.querySelector(".screen").classList.contains("screen-sharing-paused")).eql(true);
+    });
+
     it("should not mark the wrapper as receiving screen share when not displaying a screen share", function() {
       view = mountTestComponent({
         displayScreenShare: false
       });
 
       expect(view.getDOMNode().querySelector(".media-wrapper")
         .classList.contains("receiving-screen-share")).eql(false);
     });
--- a/browser/extensions/loop/chrome/locale/af/loop.properties
+++ b/browser/extensions/loop/chrome/locale/af/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ar/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ar/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/as/loop.properties
+++ b/browser/extensions/loop/chrome/locale/as/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ast/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ast/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/az/loop.properties
+++ b/browser/extensions/loop/chrome/locale/az/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Söhbət başlat…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label={{clientShortname}} haqqında ətraflı öyrənin »
 rooms_room_full_call_to_action_nonFx_label=Öz söhbətinizi başlatmaq üçün {{brandShortname}} endirin
 rooms_room_full_label=Bu söhbətdə artıq 2 nəfər var.
 rooms_room_join_label=Söhbətə qoşul
 rooms_room_joined_owner_connected_label2=Yoldaşınız artıq qoşuludur və vərəqlərinizi görə biləcəklər.
 rooms_room_joined_owner_not_connected_label=Yoldaşınız {{roomURLHostname}} ünvanını sizində gəzmək üçün gözləyir.
 
 self_view_hidden_message=Özünü göstərmə gizlədilib amma hələ də göndərilir. göstərmək üçün pəncərə ölçülərini dəyişin
 
+peer_left_session=Yoldaşınız çıxdı.
+peer_unexpected_quit=Yoldaşınız gözlənilmədən ayrıldı.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} sizin ölkədə icazə verilmir.
 
 display_name_guest=Qonaq
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/be/loop.properties
+++ b/browser/extensions/loop/chrome/locale/be/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/bg/loop.properties
+++ b/browser/extensions/loop/chrome/locale/bg/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
 ## These are displayed together at the top of the panel when a user is needed to
 ## sign-in again. The emphesis is on the first line to get the user to sign-in again,
@@ -156,8 +156,12 @@ rooms_room_joined_owner_not_connected_label=Ваш приятел ви чака да посетите {{roomURLHostname}} заедно.
 
 self_view_hidden_message=Какво виждат другите е скрито, но се изпраща; преоразмерете прозореца, за да го видите
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} е недостъпен във вашата държава.
 
 display_name_guest=Гост
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/bn-BD/loop.properties
+++ b/browser/extensions/loop/chrome/locale/bn-BD/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=আলাপ শুরু করুন
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -257,8 +257,12 @@ rooms_room_joined_owner_not_connected_label=আপনার বন্ধু আপনার সাথে {{roomURLHostname}} ব্রাউজ করতে অপেক্ষা করছেন।
 
 self_view_hidden_message=সেলফ-ভিউ লুকানো কিন্তু এখনো ছবি পাঠাবে; দেখাতে উইন্ডোর আকার পরিবর্তন করুন
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} আপনার দেশের বিদ্যমান নয়।
 
 display_name_guest=অতিথি
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/bn-IN/loop.properties
+++ b/browser/extensions/loop/chrome/locale/bn-IN/loop.properties
@@ -142,8 +142,12 @@ rooms_room_full_call_to_action_nonFx_label=আপনার নিজের একটি শুরু করতে হলে {{brandShortname}} ডাউনলোড করে নিন
 rooms_room_full_label=এই কথোপকথনটিতে আগে থেকেই দুজন বর্তমান রয়েছেন।
 rooms_room_join_label=কথোপকথনটিতে যোগ দিন
 rooms_room_joined_label=কেউ একজন কথোপকথনটিতে যোগ দিয়েছে!
 
 self_view_hidden_message=স্বয়ং-ভিউ লোকানো কিন্তু এখন পাঠানো হয়েছে; রিসাইজ উইন্ডো দেখানো হবে
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/bs/loop.properties
+++ b/browser/extensions/loop/chrome/locale/bs/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=Preuzmite {{brandShortname}} da započnete svoj
 rooms_room_full_label=Već ima dvoje ljudi u ovom razgovoru
 rooms_room_join_label=Pridruži se razgovoru
 rooms_room_joined_label=Neko se pridružio razgovoru!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ca/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ca/loop.properties
@@ -146,8 +146,12 @@ rooms_room_full_label=Ja hi ha dues pers
 rooms_room_join_label=Uniu-vos a la conversa
 rooms_room_joined_label=Algú s'ha unit a la conversa.
 
 self_view_hidden_message=La vista pròpia està amagada, però encara s'està enviant; redimensioneu la finestra per veure-la
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message=El {{clientShortname}} no està disponible al vostre país.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/cs/loop.properties
+++ b/browser/extensions/loop/chrome/locale/cs/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Zahájit konverzaci…
 loopMenuItem_accesskey=Z
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -258,8 +258,12 @@ rooms_room_joined_owner_not_connected_label=Váš přítel čeká na prohlížení s vámi v místnosti {{roomURLHostname}}.
 
 self_view_hidden_message=Váš obraz byl skryt, ale je nadále odesílán; pro jeho zobrazení změňte velikost okna
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} není ve vaší zemi dostupný.
 
 display_name_guest=Host
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/cy/loop.properties
+++ b/browser/extensions/loop/chrome/locale/cy/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Cychwyn sgwrs…
 loopMenuItem_accesskey=C
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -248,8 +248,12 @@ rooms_room_full_label=Mae eisioes dau be
 rooms_room_join_label=Ymuno â'r sgwrs
 rooms_room_joined_label=Mae rhywun wedi ymuno â'r sgwrs!
 
 self_view_hidden_message=Golwg o'ch hun yn cael ei guddio ond yn dal i gael ei anfon; newid maint ffenestr i'w dangos
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message=Nid yw {{clientShortname}} ar gael yn eich gwlad.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/da/loop.properties
+++ b/browser/extensions/loop/chrome/locale/da/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Start en samtale…
 loopMenuItem_accesskey=a
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,19 @@ rooms_room_full_call_to_action_label=Læs mere om {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Hent {{brandShortname}} for at starte din egen
 rooms_room_full_label=Der er allerede to personer i denne samtale.
 rooms_room_join_label=Vær med i samtalen
 rooms_room_joined_owner_connected_label2=Din ven har nu oprettet forbindelse og vil være i stand til at se dine faneblade.
 rooms_room_joined_owner_not_connected_label=Din ven venter på at besøge {{roomURLHostname}} sammen med dig.
 
 self_view_hidden_message=Billedet fra eget kamera er skjult, men sendes stadig til andre. Gør vinduet større for at få vist billedet fra dit kamera
 
+peer_unexpected_quit=Din ven har uventet afbrudt forbindelsen.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} er ikke tilgængelig i dit land.
 
 display_name_guest=Gæst
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/de/loop.properties
+++ b/browser/extensions/loop/chrome/locale/de/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Gespräch beginnen…
 loopMenuItem_accesskey=G
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -253,13 +253,20 @@ rooms_room_full_call_to_action_label=Erfahren Sie mehr über {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Laden Sie {{brandShortname}} herunter, um Ihr Eigenes zu beginnen.
 rooms_room_full_label=In diesem Gespräch befinden sich bereits zwei Personen.
 rooms_room_join_label=Gespräch betreten
 rooms_room_joined_owner_connected_label2=Ihr Freund ist jetzt verbunden und kann Ihre Tabs sehen.
 rooms_room_joined_owner_not_connected_label=Ihr Freund wartet darauf, mit Ihnen {{roomURLHostname}} anzusehen.
 
 self_view_hidden_message=Eigenes Kamerabild ist ausgeblendet, wird aber gesendet. Passen Sie die Fenstergröße an, um es anzuzeigen.
 
+peer_left_session=Ihr Freund hat das Gespräch verlassen.
+peer_unexpected_quit=Die Verbindung zu Ihrem Freund wurde unerwartet getrennt.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} ist in Ihrem Land nicht verfügbar.
 
 display_name_guest=Gast
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/dsb/loop.properties
+++ b/browser/extensions/loop/chrome/locale/dsb/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Rozgrono zachopiś…
 loopMenuItem_accesskey=z
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=Zgóńśo wěcej wó {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Ześěgniśo {{brandShortname}}, aby swójsku zachopił
 rooms_room_full_label=Stej južo dwě wósobje w toś tom rozgronje.
 rooms_room_join_label=Rozgronoju se pśizamknuś
 rooms_room_joined_owner_connected_label2=Waš pśijaśel jo něnto zwězany a móžo waše rejtarki wiźeś.
 rooms_room_joined_owner_not_connected_label=Waš pśijaśel caka, aby {{roomURLHostname}} z wami pśeglědował.
 
 self_view_hidden_message=Samonaglěd schowany, ale sćelo se hyšći; změńśo wjelikosć wokna, kótarež ma se pokazaś
 
+peer_left_session=Waš pśijaśel jo wótešeł.
+peer_unexpected_quit=Zwisk z wašym pśijaśelom jo se źělił.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} njestoj we wašom kraju k dispoziciji.
 
 display_name_guest=Gósć
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/el/loop.properties
+++ b/browser/extensions/loop/chrome/locale/el/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=Κάντε λήψη του {{brandShortname}} για να ξεκινήσετε την δική σας
 rooms_room_full_label=Υπάρχουν ήδη δύο άτομα σε αυτήν τη συνομιλία.
 rooms_room_join_label=Συμμετέχετε στη συνομιλία
 rooms_room_joined_label=Κάποιος εισήλθε στην συνομιλία!
 
 self_view_hidden_message=Το βίντεο σας εμφανίζεται μόνο στους υπόλοιπους συμμετέχοντες: μεγαλώστε το παράθυρό σας για να εμφανιστεί και σε εσάς
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/en-GB/loop.properties
+++ b/browser/extensions/loop/chrome/locale/en-GB/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Start a conversation…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -257,8 +257,12 @@ rooms_room_joined_owner_not_connected_la
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window \\\n to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} is not available in your country.
 
 display_name_guest=Guest
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/eo/loop.properties
+++ b/browser/extensions/loop/chrome/locale/eo/loop.properties
@@ -146,8 +146,12 @@ rooms_room_full_label=Jam estas du personoj en tiu ĉi konversacio.
 rooms_room_join_label=Aliĝu al la konversacio
 rooms_room_joined_label=Iu aliĝis al la konversacio!
 
 self_view_hidden_message=Propra vido kaŝita, sed tamen sendata. Ŝanĝu la grandecon de la fenestro por montri ĝin.
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} ne disponeblas en via lando.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/es-CL/loop.properties
+++ b/browser/extensions/loop/chrome/locale/es-CL/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Iniciar una conversación…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -258,8 +258,12 @@ rooms_room_joined_owner_not_connected_label=Tu amigo está esperando para navegar {{roomURLHostname}} contigo.
 
 self_view_hidden_message=La vista local está oculta pero continúa siendo enviada; redimensione la ventana para mostrarla
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} no está disponible en tu país.
 
 display_name_guest=Invitado
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/es-ES/loop.properties
+++ b/browser/extensions/loop/chrome/locale/es-ES/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Iniciar una conversación…
 loopMenuItem_accesskey=I
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=Obtén más información sobre {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Descarga {{brandShortname}} para iniciar la tuya
 rooms_room_full_label=Ya hay dos personas en esta conversación.
 rooms_room_join_label=Únete a la conversación
 rooms_room_joined_owner_connected_label2=Tu amigo ya está conectado y podrá ver tus pestañas.
 rooms_room_joined_owner_not_connected_label=Tu amigo está esperando para navegar contigo por {{roomURLHostname}}.
 
 self_view_hidden_message=Se está enviando la vista propia aunque esté oculta; ajusta la ventana para verla
 
+peer_left_session=Tu amigo se ha ido.
+peer_unexpected_quit=Tu amigo se ha desconectado de forma inesperada.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} no está disponible en tu país.
 
 display_name_guest=Invitado
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/es-MX/loop.properties
+++ b/browser/extensions/loop/chrome/locale/es-MX/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Iniciar una conversación…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=Saber más acerca de {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Descarga {{brandShortname}} para iniciar la tuya
 rooms_room_full_label=Ya hay dos personas en esta conversación.
 rooms_room_join_label=Unirse a la conversación
 rooms_room_joined_owner_connected_label2=Tu amigo está ahora conectado y podrá ver tus pestañas.
 rooms_room_joined_owner_not_connected_label=Tu amigo está esperando para navegar {{roomURLHostname}} contigo.
 
 self_view_hidden_message=Auto-vista oculta pero enviándose; redimensiona ventana para mostrar
 
+peer_left_session=Tu amigo se ha ido.
+peer_unexpected_quit=Tu amigo se ha desconectado inesperadamente.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} no está disponible en tu país.
 
 display_name_guest=Invitado
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/et/loop.properties
+++ b/browser/extensions/loop/chrome/locale/et/loop.properties
@@ -1,16 +1,14 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Tere
-
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Vestluse alustamine…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
 ## These are displayed together at the top of the panel when a user is needed to
@@ -258,8 +256,12 @@ rooms_room_joined_owner_not_connected_label=Sinu sõber ootab, et sinuga {{roomURLHostname}} sirvida.
 
 self_view_hidden_message=Sinu pilti edastatakse, kuid see on praegu peidetud. Pildi kuvamiseks muuda akna suurust.
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} pole sinu riigis saadaval.
 
 display_name_guest=Külaline
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/eu/loop.properties
+++ b/browser/extensions/loop/chrome/locale/eu/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=Dagoeneko badaude bi pertsona hizketaldi honetan.
 rooms_room_join_label=Egin bat hizketaldiarekin
 rooms_room_joined_label=Norbaitek bat egin du zure hizketaldiarekin!
 
 self_view_hidden_message=Norbere ikuspegia ezkutatuta baina oraindik bidaltzen; erakusteko, aldatu leihoaren tamaina
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/fa/loop.properties
+++ b/browser/extensions/loop/chrome/locale/fa/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=شروع یک گفت‌وگو…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=درباره {{clientShortname}} بیشتر اطلاع پیدا کنید »
 rooms_room_full_call_to_action_nonFx_label=برای شروع گفت‌وگو خودتان {{brandShortname}} را بارگیری کنید
 rooms_room_full_label=در حال حاضر در نفر در این گفت‌وگو حضور دارند.
 rooms_room_join_label=پیوستن به گفت‌وگو
 rooms_room_joined_owner_connected_label2=هم‌اکنون دوست شما متصل شده است و می‌تواند زبانه‌های شما را ببیند.
 rooms_room_joined_owner_not_connected_label=دوست شما منتظر است تا {{roomURLHostname}} را همراه با شما ببیند.
 
 self_view_hidden_message=نمای فردی پنهان شده است ولی ارسال می‌شود؛ برای نمایش اندازه پنجره را تغییر دهید
 
+peer_left_session=دوست شما گفت‌وگو را ترک کرد.
+peer_unexpected_quit=دوست شما بطور غیرمنتظره‌ای قطع شد.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} در کشور شما در دسترس نیست.
 
 display_name_guest=مهمان
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ff/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ff/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=Aawto {{brandShortname}} ngam fuɗɗaade nde maa
 rooms_room_full_label=Yimɓe ɗiɗo ena e yeewtere ndee tawo.
 rooms_room_join_label=Naat e yeewtere ndee
 rooms_room_joined_label=Won naatɗo e yeewtere ndee!
 
 self_view_hidden_message=Jiytol maa koko suuɗii kono ena yaha haa jooni; ɓeydu njaajeendi ngam hollude
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/fi/loop.properties
+++ b/browser/extensions/loop/chrome/locale/fi/loop.properties
@@ -141,8 +141,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=Keskustelussa on jo kaksi henkilöä paikalla.
 rooms_room_join_label=Liity keskusteluun
 rooms_room_joined_label=Joku on liittynyt keskusteluun!
 
 self_view_hidden_message=Omanäkymä piilotettu, mutta lähetetään edelleen. Muuta ikkunan kokoa nähdäksesi.
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/fr/loop.properties
+++ b/browser/extensions/loop/chrome/locale/fr/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Lancer une conversation…
 loopMenuItem_accesskey=L
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -18,17 +18,17 @@ loopMenuItem_accesskey=L
 ## and this is displayed in slightly larger font. Please arrange as necessary for
 ## your locale.
 ## {{clientShortname2}} will be replaced by the brand name for either string.
 sign_in_again_title_line_one=Veuillez vous connecter à nouveau
 sign_in_again_title_line_two2=pour continuer à utiliser {{clientShortname2}}
 sign_in_again_button=Se connecter
 ## LOCALIZATION_NOTE(sign_in_again_use_as_guest_button2): {{clientSuperShortname}}
 ## will be replaced by the super short brandname.
-sign_in_again_use_as_guest_button2=Utiliser {{clientSuperShortname}} en tant qu'invité
+sign_in_again_use_as_guest_button2=Utiliser {{clientSuperShortname}} en tant qu’invité
 
 panel_browse_with_friend_button=Consulter cette page avec une autre personne
 panel_disconnect_button=Déconnexion
 
 ## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
 ## user to create his or her first conversation.
 first_time_experience_subheading2=Cliquez sur le bouton Hello pour consulter des pages web avec une autre personne.
 first_time_experience_subheading_button_above=Cliquez sur le bouton ci-dessus pour naviguer sur le Web avec une autre personne.
@@ -38,32 +38,32 @@ first_time_experience_subheading_button_
 first_time_experience_content=Utilisez-le pour vous organiser, travailler et rire ensemble.
 first_time_experience_content2=Utilisez vous pour réaliser vos projets : vous organiser, travailler et rire ensemble.
 first_time_experience_button_label2=Principe de fonctionnement
 
 ## First Time Experience Slides
 fte_slide_1_title=Naviguez sur le Web avec une autre personne
 ## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
 ## will be replaced by the short name 2.
-fte_slide_1_copy=Que ce soit pour planifier un voyage ou l'achat d'un cadeau, {{clientShortname2}} vous permet de prendre des décisions plus rapidement.
+fte_slide_1_copy=Que ce soit pour planifier un voyage ou l’achat d’un cadeau, {{clientShortname2}} vous permet de prendre des décisions plus rapidement.
 fte_slide_2_title=Sur la même page au même moment
-fte_slide_2_copy=Utilisez la conversation texte ou vidéo pour partager vos idées, comparer vos choix et vous mettre d'accord.
+fte_slide_2_copy=Utilisez la conversation texte ou vidéo pour partager vos idées, comparer vos choix et vous mettre d’accord.
 fte_slide_3_title=Invitez votre interlocuteur en lui envoyant un lien
 ## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
 ## will be replaced by the super short brand name.
-fte_slide_3_copy={{clientSuperShortname}} fonctionne avec la plupart des navigateurs de bureau. Aucun compte n'est nécessaire et tout le monde peut l'utiliser gratuitement.
+fte_slide_3_copy={{clientSuperShortname}} fonctionne avec la plupart des navigateurs de bureau. Aucun compte n’est nécessaire et tout le monde peut l’utiliser gratuitement.
 ## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
 ## will be replaced by the super short brand name.
-fte_slide_4_title=Cherchez l'icône {{clientSuperShortname}} pour commencer
+fte_slide_4_title=Cherchez l’icône {{clientSuperShortname}} pour commencer
 ## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
 ## will be replaced by the brand short name.
-fte_slide_4_copy=Une fois sur la page à propos de laquelle vous souhaitez discuter, cliquez sur l'icône dans {{brandShortname}} pour créer un lien. Vous pouvez alors l'envoyer à votre interlocuteur de la manière que vous voulez.
+fte_slide_4_copy=Une fois sur la page à propos de laquelle vous souhaitez discuter, cliquez sur l’icône dans {{brandShortname}} pour créer un lien. Vous pouvez alors l’envoyer à votre interlocuteur de la manière que vous voulez.
 
-invite_header_text_bold=Invitez quelqu'un à consulter cette page avec vous !
-invite_header_text_bold2=Invitez quelqu'un à vous rejoindre !
+invite_header_text_bold=Invitez quelqu’un à consulter cette page avec vous !
+invite_header_text_bold2=Invitez quelqu’un à vous rejoindre !
 invite_header_text3=Utiliser Firefox Hello est très simple, envoyez simplement un lien à votre interlocuteur et vous pourrez naviguer sur le Web ensemble !
 invite_header_text4=Partagez ce lien pour surfer sur le Web ensemble.
 ## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
 ## invite_email_link_button, invite_facebook_button2): These labels appear under
 ## an iconic button for the invite view.
 invite_copy_link_button=Copier le lien
 invite_copied_link_button=Copié
 invite_email_link_button=Envoyer le lien
@@ -76,17 +76,17 @@ invite_your_link=Votre lien :
 session_expired_error_description=Session expirée. Toutes les URL précédemment créées et partagées ne fonctionneront plus.
 could_not_authenticate=Authentification impossible
 password_changed_question=Avez-vous modifié votre mot de passe ?
 try_again_later=Veuillez réessayer plus tard
 could_not_connect=Connexion au serveur impossible
 check_internet_connection=Veuillez vérifier votre connexion Internet
 login_expired=Votre identifiant a expiré
 service_not_available=Service actuellement indisponible
-problem_accessing_account=Une erreur s'est produite lors de l'accès à votre compte
+problem_accessing_account=Une erreur s’est produite lors de l’accès à votre compte
 
 ## LOCALIZATION NOTE(retry_button): Displayed when there is an error to retry
 ## the appropriate action.
 retry_button=Réessayer
 
 share_email_subject7=Votre invitation à naviguer sur le Web avec une autre personne
 ## LOCALIZATION NOTE (share_email_body7): In this item, don't translate the
 ## part between {{..}} and leave the \n\n part alone
@@ -104,17 +104,17 @@ share_tweet=Rejoignez-moi pour une conversation vidéo sur {{clientShortname2}} !
 share_add_service_button=Ajouter un service
 
 ## LOCALIZATION NOTE (copy_link_menuitem, email_link_menuitem, delete_conversation_menuitem):
 ## These menu items are displayed from a panel's context menu for a conversation.
 copy_link_menuitem=Copier le lien
 email_link_menuitem=Envoyer le lien
 delete_conversation_menuitem2=Supprimer
 
-panel_footer_signin_or_signup_link=S'inscrire ou se connecter
+panel_footer_signin_or_signup_link=S’inscrire ou se connecter
 
 settings_menu_item_account=Compte
 settings_menu_item_settings=Paramètres
 settings_menu_item_signout=Se déconnecter
 settings_menu_item_signin=Se connecter
 settings_menu_item_turnnotificationson=Activer les notifications
 settings_menu_item_turnnotificationsoff=Désactiver les notifications
 settings_menu_item_feedback=Donner son avis
@@ -137,76 +137,75 @@ call_with_contact_title=Conversation ave
 
 # Outgoing conversation
 
 outgoing_call_title=Lancer une conversation ?
 initiate_audio_video_call_button2=Lancer
 initiate_audio_video_call_tooltip2=Lancer une conversation vidéo
 initiate_audio_call_button2=Conversation audio
 
-peer_ended_conversation2=Votre correspondant a mis fin à la conversation.
+peer_ended_conversation2=Votre interlocuteur a mis fin à la conversation.
 restart_call=Rappeler
 
 ## LOCALIZATION NOTE (contact_offline_title): Title which is displayed when the
 ## contact is offline.
-contact_offline_title=Cette personne n'est pas en ligne
+contact_offline_title=Cette personne n’est pas en ligne
 ## LOCALIZATION NOTE (call_timeout_notification_text): Title which is displayed
 ## when the call didn't go through.
 call_timeout_notification_text=Votre appel n'a pas abouti.
 
 ## LOCALIZATION NOTE (cancel_button):
 ## This button is displayed when a call has failed.
 cancel_button=Annuler
 rejoin_button=Rejoindre la conversation
 
-cannot_start_call_session_not_ready=Impossible de passer l'appel, la session n'est pas prête.
+cannot_start_call_session_not_ready=Impossible de passer l’appel, la session n’est pas prête.
 network_disconnected=La connexion réseau a été brusquement interrompue.
-connection_error_see_console_notification=Échec de l'appel ; consultez la console pour plus de détails.
+connection_error_see_console_notification=Échec de l’appel ; consultez la console pour plus de détails.
 no_media_failure_message=Aucune caméra ni aucun microphone trouvés.
 ice_failure_message=Échec de connexion. Votre pare-feu bloque peut-être les appels.
 
 ## LOCALIZATION NOTE (legal_text_and_links3): In this item, don't translate the
 ## parts between {{..}} because these will be replaced with links with the labels
 ## from legal_text_tos and legal_text_privacy. clientShortname will be replaced
 ## by the brand name.
-legal_text_and_links3=En utilisant {{clientShortname}} vous acceptez les {{terms_of_use}} \
-  et la {{privacy_notice}}.
-legal_text_tos=conditions d'utilisation
+legal_text_and_links3=En utilisant {{clientShortname}} vous acceptez les {{terms_of_use}} et la {{privacy_notice}}.
+legal_text_tos=conditions d’utilisation
 legal_text_privacy=politique de confidentialité
 
 ## LOCALIZATION NOTE (powered_by_beforeLogo, powered_by_afterLogo):
 ## These 2 strings are displayed before and after a 'Telefonica'
 ## logo.
-powered_by_beforeLogo=Avec l'appui de
+powered_by_beforeLogo=Avec l’appui de
 powered_by_afterLogo=
 
 ## LOCALIZATION_NOTE (feedback_rejoin_button): Displayed on the feedback form after
 ## a signed-in to signed-in user call.
 feedback_rejoin_button=Rappeler
 ## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
 ## an abusive user.
-feedback_report_user_button=Signaler l'utilisateur
-feedback_window_heading=Comment s'est passée votre conversation ?
+feedback_report_user_button=Signaler l’utilisateur
+feedback_window_heading=Comment s’est passée votre conversation ?
 feedback_request_button=Donner mon avis
 
 tour_label=Visite guidée
 
 rooms_list_recently_browsed2=Navigation récente
 rooms_list_currently_browsing2=Navigation en cours
 rooms_signout_alert=Les conversations ouvertes vont être fermées
 room_name_untitled_page=Page sans titre
 
 ## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
-door_hanger_return=À bientôt ! Vous pouvez accéder à cette session partagée à n'importe quel moment depuis le panneau Hello.
+door_hanger_return=À bientôt ! Vous pouvez accéder à cette session partagée à n’importe quel moment depuis le panneau Hello.
 door_hanger_prompt_name=Voulez-vous lui donner un nom pour la mémoriser plus facilement ? Nom actuel :
 door_hanger_button=OK
 
 # Infobar strings
 
-infobar_screenshare_no_guest_message=Dès que l'autre personne suivra le lien, elle pourra voir tous les onglets sur lesquels vous cliquerez.
+infobar_screenshare_no_guest_message=Dès que l’autre personne suivra le lien, elle pourra voir tous les onglets sur lesquels vous cliquerez.
 infobar_screenshare_browser_message2=Vous partagez vos onglets. Vos amis pourront voir tous les onglets sur lesquels vous cliquez.
 infobar_screenshare_browser_message3=Vous partagez à présent vos onglets. Votre contact verra tous les onglets sur lesquels vous cliquerez.
 infobar_screenshare_stop_sharing_message=Vous ne partagez plus vos onglets
 infobar_button_restart_label2=Recommencer à partager
 infobar_button_restart_accesskey=e
 infobar_button_stop_label2=Arrêter le partage
 infobar_button_stop_accesskey=A
 infobar_button_disconnect_label=Déconnexion
@@ -251,15 +250,22 @@ rooms_panel_title=Sélectionnez une conversation ou démarrez-en une nouvelle
 
 rooms_room_full_call_to_action_label=En savoir plus sur {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Téléchargez {{brandShortname}} pour lancer la vôtre
 rooms_room_full_label=Il y a deux personnes dans cette conversation.
 rooms_room_join_label=Rejoindre la conversation
 rooms_room_joined_owner_connected_label2=Votre interlocuteur est connecté et peut à présent voir vos onglets.
 rooms_room_joined_owner_not_connected_label=Votre interlocuteur attend pour parcourir {{roomURLHostname}} avec vous.
 
-self_view_hidden_message=Retour vidéo masqué, mais la vidéo est toujours transmise. Redimensionnez la fenêtre pour l'afficher.
+self_view_hidden_message=Retour vidéo masqué, mais la vidéo est toujours transmise. Redimensionnez la fenêtre pour l’afficher.
+
+peer_left_session=L’autre personne a quitté la conversation.
+peer_unexpected_quit=L’autre personne a été brusquement déconnectée.
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
-tos_failure_message={{clientShortname}} n'est pas disponible dans votre pays.
+tos_failure_message={{clientShortname}} n’est pas disponible dans votre pays.
 
 display_name_guest=Invité
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/fy-NL/loop.properties
+++ b/browser/extensions/loop/chrome/locale/fy-NL/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Begjin in petear…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -257,8 +257,16 @@ rooms_room_joined_owner_not_connected_la
 
 self_view_hidden_message=Eigen werjefte ferburgen, mar wurdt noch hieltyd ferstjoerd; wizigje finsterformaat om te toanen
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} is net beskikber yn jo lân.
 
 display_name_guest=Gast
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/fy/loop.properties
+++ b/browser/extensions/loop/chrome/locale/fy/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Begjin in petear…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -257,8 +257,16 @@ rooms_room_joined_owner_not_connected_la
 
 self_view_hidden_message=Eigen werjefte ferburgen, mar wurdt noch hieltyd ferstjoerd; wizigje finsterformaat om te toanen
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} is net beskikber yn jo lân.
 
 display_name_guest=Gast
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ga/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ga/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/gd/loop.properties
+++ b/browser/extensions/loop/chrome/locale/gd/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Tòisich air còmhradh…
 loopMenuItem_accesskey=s
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -246,8 +246,12 @@ rooms_room_joined_owner_not_connected_label=Tha do charaid a’ feitheamh ort airson {{roomURLHostname}} a rùrachadh còmhla riut.
 
 self_view_hidden_message=Tha do dhealbh fhèin am falach ach 'ga chur fhathast; atharraich meud na h-uinneige gus fhaicinn
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message=Chan eil {{clientShortname}} ri fhaighinn ’nad dhùthaich.
 
 display_name_guest=Aoigh
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/gl/loop.properties
+++ b/browser/extensions/loop/chrome/locale/gl/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/gu-IN/loop.properties
+++ b/browser/extensions/loop/chrome/locale/gu-IN/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/he/loop.properties
+++ b/browser/extensions/loop/chrome/locale/he/loop.properties
@@ -134,8 +134,12 @@ retry_call_button=ניסיון חוזר
 rooms_leave_button_label=עזיבה
 
 
 rooms_room_join_label=הצטרפות לדיון
 
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/hi-IN/loop.properties
+++ b/browser/extensions/loop/chrome/locale/hi-IN/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=खुद का शुरू करने के लिए {{brandShortname}} को डाउनलोड करें
 rooms_room_full_label=इस बातचीत मैं पहले से ही दो लोग शामिल हैं.
 rooms_room_join_label=बातचीत मैं शामिल हो जाइये
 rooms_room_joined_label=कोई बातचीत मैं शामिल हुआ है!
 
 self_view_hidden_message=निजी-दृश्य छुपा हुआ है पर अभी भी भेजा जा रहा है; देखने के लिए विंडो का आकर बदलें
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/hr/loop.properties
+++ b/browser/extensions/loop/chrome/locale/hr/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/hsb/loop.properties
+++ b/browser/extensions/loop/chrome/locale/hsb/loop.properties
@@ -1,16 +1,14 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Witajće
-
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Rozmołwu započeć…
 loopMenuItem_accesskey=m
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
 ## These are displayed together at the top of the panel when a user is needed to
@@ -252,13 +250,20 @@ rooms_room_full_call_to_action_label=Zhońće wjace wo {{clientShortname}}»
 rooms_room_full_call_to_action_nonFx_label=Sćehńće {{brandShortname}}, zo byšće swójsku započał
 rooms_room_full_label=Stej hižo dwě wosobje w tutej rozmołwje.
 rooms_room_join_label=Rozmołwje so přidružić
 rooms_room_joined_owner_connected_label2=Waš přećel je nětko zwjazany a móže waše rajtarki widźeć.
 rooms_room_joined_owner_not_connected_label=Waš přećel čaka, zo by {{roomURLHostname}} z wami přehladował.
 
 self_view_hidden_message=Samonapohlad schowany, ale sćele so hišće; změńće wulkosć wokna, kotrež ma so pokazać
 
+peer_left_session=Waš přećel je wotešoł.
+peer_unexpected_quit=Zwisk z wašim přećelo je so njenadźicy dzělił.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} we wašim kraju k dispoziciji njesteji.
 
 display_name_guest=Hósć
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ht/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ht/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/hu/loop.properties
+++ b/browser/extensions/loop/chrome/locale/hu/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Kezdjen beszélgetni…
 loopMenuItem_accesskey=k
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=Tudjon meg többet a {{clientShortname}}ról »
 rooms_room_full_call_to_action_nonFx_label=A {{brandShortname}} letöltésével sajátot indíthat
 rooms_room_full_label=Már vannak ketten ebben a csevegésben.
 rooms_room_join_label=Csatlakozás a csevegéshez
 rooms_room_joined_owner_connected_label2=Ismerőse csatlakozott, és láthatja az Ön böngészőlapjait.
 rooms_room_joined_owner_not_connected_label=Ismerőse arra vár, hogy közösen böngészhessék a következőt: {{roomURLHostname}}.
 
 self_view_hidden_message=A saját kamera képe elrejtve, de elküldésre kerül. Méretezze át az ablakot a megjelenítéshez
 
+peer_left_session=Ismerőse távozott.
+peer_unexpected_quit=Ismerőse váratlanul szétkapcsolódott.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message=A {{clientShortname}} nem érhető el ebben az országban.
 
 display_name_guest=Vendég
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/hy-AM/loop.properties
+++ b/browser/extensions/loop/chrome/locale/hy-AM/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Սկսել զրույց...
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -257,8 +257,12 @@ rooms_room_joined_owner_not_connected_label=Ձեր ընկերը սպասում է՝ {{roomURLHostname}}-ը ձեզ հետ դիտարկելու համար:
 
 self_view_hidden_message=Ինքնադիտումը թաքցված է, բայց դեռ ուղարկվում է. չափափոխել պատուհանը՝ ցուցադրելու համար
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}}-ը հասանելի չէ ձեր երկրում:
 
 display_name_guest=Հյուր
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/id/loop.properties
+++ b/browser/extensions/loop/chrome/locale/id/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Mulai sebuah percakapan …
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -257,8 +257,12 @@ rooms_room_joined_owner_not_connected_la
 
 self_view_hidden_message=Tampilan diri sedang tersembunyi tetapi tetap dikirim, ubah ukuran jendela untuk menampilkannya
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} tidak tersedia di negara Anda.
 
 display_name_guest=Tamu
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/it/loop.properties
+++ b/browser/extensions/loop/chrome/locale/it/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Avvia una conversazione…
 loopMenuItem_accesskey=u
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=Ulteriori informazioni su {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Scarica {{brandShortname}} per avviare una nuova conversazione
 rooms_room_full_label=Questa conversazione ha già due partecipanti.
 rooms_room_join_label=Partecipa alla conversazione
 rooms_room_joined_owner_connected_label2=L’utente invitato si è collegato e da questo momento può vedere le schede aperte del tuo browser.
 rooms_room_joined_owner_not_connected_label=Un utente ti sta aspettando su {{roomURLHostname}}.
 
 self_view_hidden_message=L’anteprima della fotocamera è nascosta, ma l’interlocutore può ugualmente vederti. Ridimensiona la finestra per visualizzarla nuovamente.
 
+peer_left_session=L'interlocutore si è disconnesso.
+peer_unexpected_quit=L'interlocutore si è inaspettatamente disconnesso.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} non è disponibile per questa nazione.
 
 display_name_guest=Ospite
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ja/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ja/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=通話を開始...
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label={{clientShortname}} の詳細 »
 rooms_room_full_call_to_action_nonFx_label={{brandShortname}} をダウンロードして会話を始めましょう
 rooms_room_full_label=この会話には既に 2 名が参加しています。
 rooms_room_join_label=会話に参加
 rooms_room_joined_owner_connected_label2=あなたの友人が接続し、あなたのタブを見られるようになりました。
 rooms_room_joined_owner_not_connected_label=友人があなたと {{roomURLHostname}} を見るために待っています。
 
 self_view_hidden_message=セルフビューは隠れていますが送信されています。表示するにはウィンドウの大きさを変更してください
 
+peer_left_session=友人が退出しました。
+peer_unexpected_quit=友人の接続が予期せず終了しました。
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} はあなたがお住まいの国では利用できません。
 
 display_name_guest=ゲスト
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/kk/loop.properties
+++ b/browser/extensions/loop/chrome/locale/kk/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Сөйлесуді бастау…
 loopMenuItem_accesskey=т
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -246,8 +246,12 @@ rooms_room_joined_owner_not_connected_label=Сіздің досыңыз {{roomURLHostname}} сайтын бірге қарауға сізді күтіп тұр.
 
 self_view_hidden_message=Өздік көрініс жасырылған, бірақ, жіберілуде; көрсету үшін терезе өлшемін өзгертіңіз
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} сіздің еліңізде қолжетерсіз.
 
 display_name_guest=Қонақ
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/km/loop.properties
+++ b/browser/extensions/loop/chrome/locale/km/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/kn/loop.properties
+++ b/browser/extensions/loop/chrome/locale/kn/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=ನೀವೇ ಖುದ್ದಾಗಿ ಪ್ರಾರಂಭಿಸಲು {{brandShortname}} ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ
 rooms_room_full_label=ಈಗಾಗಲೇ ಈ ಸಂವಾದಲ್ಲಿ ಇಬ್ಬರಿದ್ದಾರೆ
 rooms_room_join_label=ಸಂವಾದ ಸೇರಿ
 rooms_room_joined_label=ಮತ್ತೊಬ್ಬರು ಸಂವಾದವನ್ನು ಸೇರಿದ್ದಾರೆ!
 
 self_view_hidden_message=ಸ್ವ-ನೋಟ ಮರೆಯಾಗಿಸಲಾಗಿದ್ದರೂ ಕಳಿಸಲಾಗುತ್ತಿದೆ; ತೋರಿಸಲು ಕಿಟಕಿಯನ್ನು ಹಿರಿದಾಗಿಸಿ
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ko/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ko/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=대화 시작…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -248,8 +248,12 @@ rooms_room_full_label=이 대화에는 이미 두명의 사람이 참가하고 있습니다.
 rooms_room_join_label=대화 참여
 rooms_room_joined_label=누군가 대화에 참여했습니다!
 
 self_view_hidden_message=자기 보기 모드는 숨겨졌으나 전달됩니다. 보기하시려면 창 크기를 조절
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}}는 현재 지역에서 사용할 수 없습니다.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ku/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ku/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/lij/loop.properties
+++ b/browser/extensions/loop/chrome/locale/lij/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Inìçia 'na convesaçion…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -248,8 +248,12 @@ rooms_room_full_label=Gh'é za doe personn-e in sta converaçion.
 rooms_room_join_label=Uniscite a-a converaçion
 rooms_room_joined_label=Quarchedun o s'é unio a-a converaçion!
 
 self_view_hidden_message=A vista de ti a l'é ascoza ma a l'é ancon trasmissa; dimenscionn-a o barcon pe veddila
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} o no gh'é into teu paize.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/lt/loop.properties
+++ b/browser/extensions/loop/chrome/locale/lt/loop.properties
@@ -146,8 +146,12 @@ rooms_room_full_label=Šiame pokalbyje jau dalyvauja du žmonės.
 rooms_room_join_label=Prisijungti prie pokalbio
 rooms_room_joined_label=Kažkas prisijungė prie pokalbio!
 
 self_view_hidden_message=Jūs savo atvaizdo nematote, bet jis yra siunčiamas pašnekovui; norėdami matyti, pakeiskite lango dydį
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message=„{{clientShortname}}“ paslauga jūsų šalyje neteikiama.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/lv/loop.properties
+++ b/browser/extensions/loop/chrome/locale/lv/loop.properties
@@ -142,8 +142,12 @@ rooms_room_full_call_to_action_nonFx_label=Lejupielādējiet {{brandShortname}} lai sāktu jūsu
 rooms_room_full_label=Jau ir divi cilvēki šajā sarunā.
 rooms_room_join_label=Pievienoties sarunai
 rooms_room_joined_label=Kāds pievienojās sarunai!
 
 self_view_hidden_message=Jūsu paša skats ir paslēpts, bet joprojām tiek sūtīts. Izmainiet loga izmēru, lai aplūkotu to
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/mk/loop.properties
+++ b/browser/extensions/loop/chrome/locale/mk/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ml/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ml/loop.properties
@@ -146,8 +146,12 @@ rooms_room_full_label=ഈ സംഭാഷണത്തില്‍ രണ്ടുപേര്‍ ഇപ്പോളേ ഉണ്ട്.
 rooms_room_join_label=സംഭാഷണത്തില്‍ ചേരൂ
 rooms_room_joined_label=ആരോ സംഭാഷണത്തില്‍ ചേര്‍ന്നു!
 
 self_view_hidden_message=സ്വയം-കാഴ്ച അദൃശ്യമാണു് പക്ഷേ എന്നിരുന്നാലും അയയ്ക്കപ്പെടുന്നു; കാണുന്നതിനായി ജാലകത്തിന്റെ വലുപ്പം മാറ്റുക
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} താങ്കളുടെ രാജ്യത്തു് ലഭ്യമല്ല.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/mn/loop.properties
+++ b/browser/extensions/loop/chrome/locale/mn/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ms/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ms/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/my/loop.properties
+++ b/browser/extensions/loop/chrome/locale/my/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/nb-NO/loop.properties
+++ b/browser/extensions/loop/chrome/locale/nb-NO/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=Last ned {{brandShortname}} for å starte din egen
 rooms_room_full_label=Det er allerede to personer i denne samtalen.
 rooms_room_join_label=Vær med i samtalen
 rooms_room_joined_label=Noen har sluttet seg til samtalen!
 
 self_view_hidden_message=Bildet fra eget kamera er skjult, men blir fortsatt sendt; endre størrelsen på vinduet for å se
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ne-NP/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ne-NP/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/nl/loop.properties
+++ b/browser/extensions/loop/chrome/locale/nl/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Een gesprek beginnen…
 loopMenuItem_accesskey=s
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=Meer info over {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Download {{brandShortname}} om zelf een gesprek te beginnen
 rooms_room_full_label=Er zijn al twee personen in dit gesprek.
 rooms_room_join_label=Deelnemen aan het gesprek
 rooms_room_joined_owner_connected_label2=Uw vriend is nu verbonden en kan uw tabbladen zien.
 rooms_room_joined_owner_not_connected_label=Uw vriend wacht en wil samen met u {{roomURLHostname}} bekijken.
 
 self_view_hidden_message=Eigen weergave verborgen maar wordt nog steeds verzonden; wijzig vensterformaat om te tonen
 
+peer_left_session=Uw vriend heeft het gesprek verlaten.
+peer_unexpected_quit=Uw vriend heeft onverwacht de verbinding verbroken.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} is niet in uw land beschikbaar.
 
 display_name_guest=Gast
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/or/loop.properties
+++ b/browser/extensions/loop/chrome/locale/or/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/pa-IN/loop.properties
+++ b/browser/extensions/loop/chrome/locale/pa-IN/loop.properties
@@ -1,16 +1,14 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=ਹੈਲੋ
-
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=ਗੱਲਬਾਤ ਸ਼ੁਰੂ ਕਰੋ...
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
 ## These are displayed together at the top of the panel when a user is needed to
@@ -156,8 +154,16 @@ rooms_room_full_label=ਇਹ ਗੱਲਬਾਤ ਵਿੱਚ ਪਹਿਲਾਂ ਹੀ ਦੋ ਵਿਅਕਤੀ ਹਨ।
 rooms_room_join_label=ਗੱਲਬਾਤ ਵਿੱਚ ਜੁੜੋ
 rooms_room_joined_label=ਕੋਈ ਗੱਲਬਾਤ ਵਿੱਚ ਜੁੜਿਆ ਹੈ
 
 self_view_hidden_message=ਸਵੈ-ਦ੍ਰਿਸ਼ ਓਹਲੇ ਹੈ, ਪਰ ਹਾਲੇ ਵੀ ਭੇਜਿਆ ਜਾ ਰਿਹਾ ਹੈ, ਵੇਖਣ ਲਈ ਵਿੰਡੋ ਦਾ ਆਕਾਰ ਬਦਲੋ
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} ਤੁਹਾਡੇ ਦੇ ਵਿੱਚ ਉਪਲੱਬਧ ਨਹੀਂ ਹੈ।
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/pa/loop.properties
+++ b/browser/extensions/loop/chrome/locale/pa/loop.properties
@@ -1,16 +1,14 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=ਹੈਲੋ
-
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=ਗੱਲਬਾਤ ਸ਼ੁਰੂ ਕਰੋ...
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
 ## These are displayed together at the top of the panel when a user is needed to
@@ -156,8 +154,16 @@ rooms_room_full_label=ਇਹ ਗੱਲਬਾਤ ਵਿੱਚ ਪਹਿਲਾਂ ਹੀ ਦੋ ਵਿਅਕਤੀ ਹਨ।
 rooms_room_join_label=ਗੱਲਬਾਤ ਵਿੱਚ ਜੁੜੋ
 rooms_room_joined_label=ਕੋਈ ਗੱਲਬਾਤ ਵਿੱਚ ਜੁੜਿਆ ਹੈ
 
 self_view_hidden_message=ਸਵੈ-ਦ੍ਰਿਸ਼ ਓਹਲੇ ਹੈ, ਪਰ ਹਾਲੇ ਵੀ ਭੇਜਿਆ ਜਾ ਰਿਹਾ ਹੈ, ਵੇਖਣ ਲਈ ਵਿੰਡੋ ਦਾ ਆਕਾਰ ਬਦਲੋ
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} ਤੁਹਾਡੇ ਦੇ ਵਿੱਚ ਉਪਲੱਬਧ ਨਹੀਂ ਹੈ।
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/pl/loop.properties
+++ b/browser/extensions/loop/chrome/locale/pl/loop.properties
@@ -1,14 +1,12 @@
 # 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/.
 
-clientSuperShortname=Firefox Hello
-
 loopMenuItem_label=Rozpocznij rozmowę…
 loopMenuItem_accesskey=R
 
 sign_in_again_title_line_one=Zaloguj się ponownie,
 sign_in_again_title_line_two2=aby dalej używać {{clientShortname2}}
 sign_in_again_button=Zaloguj się
 
 sign_in_again_use_as_guest_button2=Używaj {{clientSuperShortname}} jako gość
@@ -165,11 +163,18 @@ rooms_room_full_call_to_action_label=Więcej informacji o {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Pobierz program {{brandShortname}}, aby rozpocząć własną
 rooms_room_full_label=Dwoje innych uczestników bierze już udział w tej rozmowie
 rooms_room_join_label=Dołącz do rozmowy
 rooms_room_joined_owner_connected_label2=Zestawiono połączenie, druga osoba będzie od teraz mogła widzieć Twoje karty.
 rooms_room_joined_owner_not_connected_label=Ktoś czeka, aby wspólnie przeglądać {{roomURLHostname}}.
 
 self_view_hidden_message=Obraz z kamery jest ukryty ale nadal wysyłany (powiększenie okna ukaże go)
 
+peer_left_session=Rozmówca się rozłączył.
+peer_unexpected_quit=Rozmówca nieoczekiwanie się rozłączył.
+
 tos_failure_message=Usługa {{clientShortname}} nie jest dostępna w tym kraju.
 
 display_name_guest=Gość
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Firefox Hello
--- a/browser/extensions/loop/chrome/locale/pt-BR/loop.properties
+++ b/browser/extensions/loop/chrome/locale/pt-BR/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Iniciar uma conversa…
 loopMenuItem_accesskey=c
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -38,17 +38,17 @@ first_time_experience_subheading_button_above=Clique no botão acima para navegar na web com um amigo.
 first_time_experience_content=Use para planejar junto, trabalhar junto, rir junto.
 first_time_experience_content2=Use para fazer coisas: planejar juntos, rir juntos, trabalhar juntos.
 first_time_experience_button_label2=Veja como funciona
 
 ## First Time Experience Slides
 fte_slide_1_title=Navegue na web com um amigo
 ## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
 ## will be replaced by the short name 2.
-fte_slide_1_copy=Se está planejando uma viagem ou comprar um presente, {{clientShortname2}} te deixa tomar decisões mais rápidas em tempo real.
+fte_slide_1_copy=Se está planejando uma viagem ou comprando um presente, {{clientShortname2}} te deixa tomar decisões mais rápidas em tempo real.
 fte_slide_2_title=Use a mesma página
 fte_slide_2_copy=Use o chat embutido de texto ou vídeo para compartilhar ideias, comparar opções e chegar a um acordo.
 fte_slide_3_title=Convide um amigo enviando um link
 ## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
 ## will be replaced by the super short brand name.
 fte_slide_3_copy=O {{clientSuperShortname}} funciona com a maioria dos navegadores desktop. Não são necessárias contas e todos se conectam de graça.
 ## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
 ## will be replaced by the super short brand name.
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=Saiba mais sobre o {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Baixe o {{brandShortname}} para iniciar a sua
 rooms_room_full_label=Já existem duas pessoas nesta conversa.
 rooms_room_join_label=Entre na conversa
 rooms_room_joined_owner_connected_label2=Seu amigo agora está conectado e será capaz de ver suas abas.
 rooms_room_joined_owner_not_connected_label=Seu amigo está esperando para navegar em {{roomURLHostname}} com você.
 
 self_view_hidden_message=A visualização da sua câmera está oculta, mas ainda é transmitida; redimensione a janela para visualizá-la novamente
 
+peer_left_session=Seu amigo saiu.
+peer_unexpected_quit=Seu amigo foi desconectado inesperadamente.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} não está disponível no seu país.
 
 display_name_guest=Convidado
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/pt-PT/loop.properties
+++ b/browser/extensions/loop/chrome/locale/pt-PT/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Iniciar uma conversa…
 loopMenuItem_accesskey=c
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=Saiba mais sobre o {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Descarregue o {{brandShortname}} para começar a sua
 rooms_room_full_label=Já existem duas pessoas nesta conversa.
 rooms_room_join_label=Junte-se à conversa
 rooms_room_joined_owner_connected_label2=O seu amigo está agora ligado e poderá ver os seus separadores.
 rooms_room_joined_owner_not_connected_label=O seu amigo está à espera para navegar em {{roomURLHostname}} consigo.
 
 self_view_hidden_message=Vista própria oculta mas ainda a ser enviada; redimensione a janela para mostrar
 
+peer_left_session=O seu amigo saiu.
+peer_unexpected_quit=O seu amigo foi desligado de forma inesperada.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} não está disponível no seu país.
 
 display_name_guest=Visitante
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/pt/loop.properties
+++ b/browser/extensions/loop/chrome/locale/pt/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=Descarregue o {{brandShortname}} para começar a sua
 rooms_room_full_label=Já existem duas pessoas nesta conversa.
 rooms_room_join_label=Junte-se à conversa
 rooms_room_joined_label=Alguém se juntou à conversa!
 
 self_view_hidden_message=Vista própria oculta mas ainda a ser enviada; redimensione a janela para mostrar
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/rm/loop.properties
+++ b/browser/extensions/loop/chrome/locale/rm/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Cumenzar ina conversaziun…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -235,8 +235,12 @@ rooms_room_join_label=Entrar en la conve
 rooms_room_joined_owner_connected_label2=Tia amia u tes ami è ussa connectà e po vesair tes tabs.
 rooms_room_joined_owner_not_connected_label=Tia amia u tes ami spetga per navigar cun tai sin {{roomURLHostname}}.
 
 self_view_hidden_message=Tes video è zuppentà, ma vegn anc adina transmess; engrondir la fanestra per al mussar
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} n'è betg disponibel en tes pajais.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ro/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ro/loop.properties
@@ -146,8 +146,12 @@ rooms_room_full_label=Conversația are deja doi participanți.
 rooms_room_join_label=Alăturați-vă conversației
 rooms_room_joined_label=Cineva s-a alăturat conversației!
 
 self_view_hidden_message=Auto-vedere ascunsă dar e transmisă în continuare; redimensionați fereastra pentru a o afișa
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} nu este disponibil în țara ta.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ru/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ru/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Сделать звонок…
 loopMenuItem_accesskey=е
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -253,13 +253,18 @@ rooms_room_full_call_to_action_label=Узнайте больше о {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Загрузите {{brandShortname}}, чтобы сделать звонок
 rooms_room_full_label=В этом звонке уже участвуют два человека.
 rooms_room_join_label=Присоединиться к звонку
 rooms_room_joined_owner_connected_label2=Ваш друг сейчас подключен и сможет увидеть ваши вкладки.
 rooms_room_joined_owner_not_connected_label=Ваш друг ожидает просмотра {{roomURLHostname}} с вами.
 
 self_view_hidden_message=Вид самого себя скрыт, но всё же отправляется; измените размер окна для показа
 
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} не доступен в вашей стране.
 
 display_name_guest=Гость
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/si/loop.properties
+++ b/browser/extensions/loop/chrome/locale/si/loop.properties
@@ -142,8 +142,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/sk/loop.properties
+++ b/browser/extensions/loop/chrome/locale/sk/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Začať konverzáciu…
 loopMenuItem_accesskey=Z
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -253,13 +253,20 @@ rooms_room_full_call_to_action_label=Zistiť viac o {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Prevezmite si {{brandShortname}} a začnite svoju vlastnú
 rooms_room_full_label=V tejto konverzácii sú už dve osoby.
 rooms_room_join_label=Pripojiť ku konverzácii
 rooms_room_joined_owner_connected_label2=Váš priateľ je teraz pripojený a môže vidieť vaše karty.
 rooms_room_joined_owner_not_connected_label=Váš priateľ čaká na prehľadávanie {{roomURLHostname}} s vami.
 
 self_view_hidden_message=Záber z kamery je skrytý, napriek tomu sa stále odosiela; pre zobrazenie upravte veľkosť okna
 
+peer_left_session=Váš priateľ odišiel.
+peer_unexpected_quit=Váš priateľ neočakávane ukončil spojenie.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} nie je vo vašej krajine dostupný.
 
 display_name_guest=Hosť
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/sl/loop.properties
+++ b/browser/extensions/loop/chrome/locale/sl/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Začni pogovor …
 loopMenuItem_accesskey=Z
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -249,8 +249,12 @@ rooms_room_full_label=V tem pogovoru sta že dve drugi osebi.
 rooms_room_join_label=Pridruži se pogovoru
 rooms_room_joined_label=Nekdo se je pridružil pogovoru!
 
 self_view_hidden_message=Lasten pogled skrit, vendar se še vedno pošilja. Razširite okno za prikaz
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} v vaši državi ni na voljo.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/son/loop.properties
+++ b/browser/extensions/loop/chrome/locale/son/loop.properties
@@ -146,8 +146,12 @@ rooms_room_full_label=There are already 
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} is not available in your country.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/sq/loop.properties
+++ b/browser/extensions/loop/chrome/locale/sq/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Filloni një bisedë…
 loopMenuItem_accesskey=F
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -253,13 +253,20 @@ rooms_room_full_call_to_action_label=Mësoni më tepër rreth {{clientShortname}}-së »
 rooms_room_full_call_to_action_nonFx_label=Shkarkoni {{brandShortname}}-in që të filloni tuajën
 rooms_room_full_label=Ka tashmë dy vetë në këtë bisedë.
 rooms_room_join_label=Hyni në bisedë
 rooms_room_joined_owner_connected_label2=Shoku juaj tani është i lidhur dhe do të jetë në gjendje të shohë skedat tuaja.
 rooms_room_joined_owner_not_connected_label=Shoku juaj po pret të shfletojë {{roomURLHostname}} me ju.
 
 self_view_hidden_message=I vetëfshehur, por prapë i dukshëm; ripërmasoni dritaren që të shfaqet
 
+peer_left_session=Shoku juaj u largua.
+peer_unexpected_quit=Shoku juaj u shkëput papritmas.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} s’është i passhëm në vendin tuaj.
 
 display_name_guest=Mysafir
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/sr/loop.properties
+++ b/browser/extensions/loop/chrome/locale/sr/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Започни разговор…
 loopMenuItem_accesskey=З
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -21,41 +21,48 @@ loopMenuItem_accesskey=З
 sign_in_again_title_line_one=Пријавите се опет
 sign_in_again_title_line_two2=да бисте наставили користити {{clientShortname2}}
 sign_in_again_button=Пријави се
 ## LOCALIZATION_NOTE(sign_in_again_use_as_guest_button2): {{clientSuperShortname}}
 ## will be replaced by the super short brandname.
 sign_in_again_use_as_guest_button2=Користи {{clientSuperShortname}} као гост
 
 panel_browse_with_friend_button=Гледај ову страницу са пријатељем
-panel_stop_sharing_tabs_button=Престани делити језичке
+panel_disconnect_button=Прекини везу
 
-## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
+## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
 ## user to create his or her first conversation.
 first_time_experience_subheading2=Кликните на Hello дугме да прегледавате веб странице са пријатељем.
 
-## LOCALIZATION_NOTE(first_time_experience_content): Message describing
+## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
 ## ways to use Hello project.
 first_time_experience_content=Корисите га да планирате, радите и да се смејете заједно.
 first_time_experience_button_label2=Погледајте како ради
 
+## First Time Experience Slides
+## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
+## will be replaced by the short name 2.
+## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
+## will be replaced by the super short brand name.
+## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
+## will be replaced by the super short brand name.
+## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
+## will be replaced by the brand short name.
+
 invite_header_text_bold=Позовите некога да прегледава ову страницу са вама!
 invite_header_text3=Потребно је двоје за коришћење Firefox Hello, пошаљите пријатељу везу да заједно претражујете веб!
 ## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
 ## invite_email_link_button, invite_facebook_button2): These labels appear under
 ## an iconic button for the invite view.
 invite_copy_link_button=Копирај везу
 invite_copied_link_button=Копирано!
 invite_email_link_button=Пошаљи везу е-поштом
 invite_facebook_button3=Facebook
 invite_your_link=Ваша веза:
 
-# Status text
-display_name_guest=Гост
-
 # Error bars
 ## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
 ## These may be displayed at the top of the panel.
 session_expired_error_description=Сесија је истекла. Сви URL које сте раније направили и делили више неће радити.
 could_not_authenticate=Нисам могао потврдити
 password_changed_question=Да ли сте променили лозинку?
 try_again_later=Покушајте поново касније
 could_not_connect=Нисам могао да се повежем на сервер
@@ -178,38 +185,18 @@ room_name_untitled_page=Безимена страница
 ## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
 door_hanger_return=Видимо се после! Можете се вратити овој подељеној сесији било када користећи Hello панел.
 door_hanger_prompt_name=Да ли желите да јој дате име ради лакшег памћења? Тренутно име:
 door_hanger_button=У реду
 
 # Infobar strings
 
 infobar_screenshare_browser_message2=Делите ваше језичке. Ваши пријатељи могу видети било који језичак на који кликнете
-infobar_screenshare_paused_browser_message=Дељење језичака је паузирано
-infobar_button_gotit_label=Разумем!
-infobar_button_gotit_accesskey=Р
-infobar_button_pause_label=Паузирај
-infobar_button_pause_accesskey=П
-infobar_button_restart_label=Поново покрени
 infobar_button_restart_accesskey=е
-infobar_button_resume_label=Настави
-infobar_button_resume_accesskey=Н
-infobar_button_stop_label=Заустави
 infobar_button_stop_accesskey=З
-infobar_menuitem_dontshowagain_label=Не показуј ово поново
-infobar_menuitem_dontshowagain_accesskey=Н
-
-# Context in conversation strings
-
-## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
-## has no conversations available.
-no_conversations_message_heading2=Нема разговора.
-## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
-## user to start a new conversation.
-no_conversations_start_message2=Започните нови!
 
 # E10s not supported strings
 
 e10s_not_supported_button_label=Покрени нови прозор
 e10s_not_supported_subheading={{brandShortname}} не ради у мулти-процес прозору.
 # 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/.
@@ -249,8 +236,12 @@ rooms_room_full_label=Већ има двоје људи у овом разговору.
 rooms_room_join_label=Придружи се разговору
 rooms_room_joined_label=Неко се придружио разговору!
 
 self_view_hidden_message=Приказ вашег екрана је сакривен али је и даље послат; промените величину прозора да би се приказао
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} није доступан у вашој држави.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/sv-SE/loop.properties
+++ b/browser/extensions/loop/chrome/locale/sv-SE/loop.properties
@@ -1,16 +1,14 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hej
-
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Starta en konversation…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
 ## These are displayed together at the top of the panel when a user is needed to
@@ -252,13 +250,20 @@ rooms_room_full_call_to_action_label=Läs mer om {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Hämta {{brandShortname}} för att börja ett eget
 rooms_room_full_label=Det är redan två person i denna konversation.
 rooms_room_join_label=Delta i konversationen
 rooms_room_joined_owner_connected_label2=Din vän är nu ansluten och kommer att kunna se dina flikar.
 rooms_room_joined_owner_not_connected_label=Din vän väntar på att surfa {{roomURLHostname}} tillsammans med dig.
 
 self_view_hidden_message=Själv-vy dold, men skickas ändå; ändra fönsterstorlek för att visa
 
+peer_left_session=Din vän har lämnat konversationen.
+peer_unexpected_quit=Din vän har oväntat kopplats bort.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} är inte tillgänglig i ditt land.
 
 display_name_guest=Gäst
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ta/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ta/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=உங்களுடையதை தொடங்க {{brandShortname}} யை பதிவிறக்கவும்
 rooms_room_full_label=இந்த உரையாடலில் ஏற்கனவே இரண்டு பேர் உள்ளனர்.
 rooms_room_join_label=உரையாடலில் சேர்
 rooms_room_joined_label=உரையாடலில் யாரோ ஒருவர் சேர்ந்துள்ளார்!
 
 self_view_hidden_message=நீங்கள் மறைக்கப்பட்டுள்ளீர்கள் ஆனால் அனுப்பப்படுகிறது, உங்களை காட்ட சாளரத்தை மறுஅளவு செய்க
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/te/loop.properties
+++ b/browser/extensions/loop/chrome/locale/te/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/th/loop.properties
+++ b/browser/extensions/loop/chrome/locale/th/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_label=ดาวน์โหลด {{brandShortname}} เพื่อเริ่มการสนทนาของคุณเอง
 rooms_room_full_label=มีสองบุคคลในการสนทนานี้อยู่แล้ว
 rooms_room_join_label=เข้าร่วมการสนทนา
 rooms_room_joined_label=มีใครบางคนได้เข้าร่วมการสนทนา!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/tr/loop.properties
+++ b/browser/extensions/loop/chrome/locale/tr/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Görüşme başlat…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -257,8 +257,12 @@ rooms_room_joined_owner_not_connected_label=Arkadaşınız sizinle birlikte {{roomURLHostname}} sitesinde gezmek için bekliyor.
 
 self_view_hidden_message=Kendi görünümünüz gizlendi ama hâlâ gönderiliyor. Görmek için pencereyi boyutlandırın
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} ülkenizde kullanılamıyor.
 
 display_name_guest=Misafir
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/uk/loop.properties
+++ b/browser/extensions/loop/chrome/locale/uk/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=Почати розмову…
 loopMenuItem_accesskey=П
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -253,13 +253,20 @@ rooms_room_full_call_to_action_label=Дізнатися більше про {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=Завантажити {{brandShortname}}, щоб почати власну
 rooms_room_full_label=В цій розмові вже беруть участь двоє людей.
 rooms_room_join_label=Долучитися до розмови
 rooms_room_joined_owner_connected_label2=Ваш друг тепер під'єднаний і зможе бачити ваші вкладки.
 rooms_room_joined_owner_not_connected_label=Ваш друг чекає на спільний перегляд {{roomURLHostname}}.
 
 self_view_hidden_message=Вигляд самого себе прихований, але все ще відправляється; змініть розмір вікна для показу
 
+peer_left_session=Ваш друг пішов.
+peer_unexpected_quit=Ваш друг неочікувано від'єднався.
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} недоступний у вашій країні.
 
 display_name_guest=Гість
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/ur/loop.properties
+++ b/browser/extensions/loop/chrome/locale/ur/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/vi/loop.properties
+++ b/browser/extensions/loop/chrome/locale/vi/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/xh/loop.properties
+++ b/browser/extensions/loop/chrome/locale/xh/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/zh-CN/loop.properties
+++ b/browser/extensions/loop/chrome/locale/zh-CN/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=开始一个对话…
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=详细了解有关 {{clientShortname}} »
 rooms_room_full_call_to_action_nonFx_label=下载 {{brandShortname}} 开始您自己的通话
 rooms_room_full_label=这个通话中已经有两个人了。
 rooms_room_join_label=加入通话
 rooms_room_joined_owner_connected_label2=您的朋友目前已连接,并将可以看到您的标签页。
 rooms_room_joined_owner_not_connected_label=您的朋友正在等待与您浏览 {{roomURLHostname}}。
 
 self_view_hidden_message=已隐藏但仍在发送中;调整窗口以显示
 
+peer_left_session=您的朋友已离开。
+peer_unexpected_quit=您的朋友已意外断开。
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message={{clientShortname}} 在您的国家不可用。
 
 display_name_guest=访客
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/zh-TW/loop.properties
+++ b/browser/extensions/loop/chrome/locale/zh-TW/loop.properties
@@ -1,15 +1,15 @@
 # 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/.
 
 # Panel Strings
 
-clientSuperShortname=Hello
+
 
 ## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
 ## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
 ## use "..." if \u2026 doesn't suit traditions in your locale.
 loopMenuItem_label=開始一段通話⋯
 loopMenuItem_accesskey=t
 
 ## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
@@ -252,13 +252,20 @@ rooms_room_full_call_to_action_label=了解 {{clientShortname}} 的更多資訊 »
 rooms_room_full_call_to_action_nonFx_label=下載 {{brandShortname}},開始您自己的通話
 rooms_room_full_label=這場通話中已經有兩個人。
 rooms_room_join_label=加入通話
 rooms_room_joined_owner_connected_label2=您的朋友已上線,將能夠看到您的分頁。
 rooms_room_joined_owner_not_connected_label=您的朋友正等待與您一起瀏覽 {{roomURLHostname}}。
 
 self_view_hidden_message=已隱藏您自己的畫面,但還是會送出。請調整視窗大小以顯示自己的畫面。
 
+peer_left_session=您的朋友已離開對話。
+peer_unexpected_quit=您的朋友網路連線中斷。
+
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
 tos_failure_message=無法在您所在的國家使用 {{clientShortname}}。
 
 display_name_guest=訪客
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/locale/zu/loop.properties
+++ b/browser/extensions/loop/chrome/locale/zu/loop.properties
@@ -156,8 +156,12 @@ rooms_room_full_call_to_action_nonFx_lab
 rooms_room_full_label=There are already two people in this conversation.
 rooms_room_join_label=Join the conversation
 rooms_room_joined_label=Someone has joined the conversation!
 
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 ## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
 ## as this will be replaced by clientShortname2.
+
+## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
+## should remain "Hello" for all locales.
+clientSuperShortname=Hello
--- a/browser/extensions/loop/chrome/skin/shared/loop.css
+++ b/browser/extensions/loop/chrome/skin/shared/loop.css
@@ -314,9 +314,18 @@
   #loop-slideshow-browser {
     width: 620px;
     height:450px;
     margin-top: 10%;
 
     /* XXX derived from width, so should be 50% - (620px / 2)? */
     -moz-margin-start: calc(50% - 310px);
   }
+
+  #loop-remote-cursor-container {
+    position: absolute;
+    pointer-events: none;
+    /* Hide overflow so that when the tail of the pointer gets to the edge
+     * of shared area, it doesn't widen the viewport by adding a scrollbar.
+     */
+    overflow: hidden;
+  }
 }
--- a/browser/extensions/loop/install.rdf.in
+++ b/browser/extensions/loop/install.rdf.in
@@ -4,17 +4,17 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>loop@mozilla.org</em:id>
     <em:bootstrap>true</em:bootstrap>
-    <em:version>1.1.9</em:version>
+    <em:version>1.1.11</em:version>
     <em:type>2</em:type>
 
     <!-- Target Application this extension can install into,
          with minimum and maximum supported versions. -->
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>46.0a1</em:minVersion>
--- a/browser/extensions/loop/test/functional/test_1_browser_call.py
+++ b/browser/extensions/loop/test/functional/test_1_browser_call.py
@@ -120,20 +120,21 @@ class Test1BrowserCall(MarionetteTestCas
                       "room URL returned by server: '" + room_url +
                       "' has invalid scheme")
         return room_url
 
     def standalone_load_and_join_room(self, url):
         self.switch_to_standalone()
         self.marionette.navigate(url)
 
-        # Join the room
-        join_button = self.wait_for_element_displayed(By.CLASS_NAME,
-                                                      "btn-join")
-        join_button.click()
+        # Join the room - the first time around, the tour will be displayed
+        # so we look for its close button.
+        tour_close_button = self.wait_for_element_displayed(By.CLASS_NAME,
+                                                            "button-close")
+        tour_close_button.click()
 
     # Assumes the standalone or the conversation window is selected first.
     def check_video(self, selector):
         video = self.wait_for_element_displayed(By.CSS_SELECTOR,
                                                 selector, 30)
         self.wait_for_element_attribute_to_be_false(video, "paused")
         self.assertEqual(video.get_attribute("ended"), "false")
 
--- a/browser/extensions/pdfjs/test/browser_pdfjs_main.js
+++ b/browser/extensions/pdfjs/test/browser_pdfjs_main.js
@@ -20,48 +20,48 @@ add_task(function* test() {
       yield waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf");
 
       ok(gBrowser.isFindBarInitialized(), "Browser FindBar initialized!");
 
       yield ContentTask.spawn(newTabBrowser, null, function* () {
         //
         // Overall sanity tests
         //
-        ok(content.document.querySelector('div#viewer'), "document content has viewer UI");
-        ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object");
+        Assert.ok(content.document.querySelector('div#viewer'), "document content has viewer UI");
+        Assert.ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object");
 
         //
         // Sidebar: open
         //
         var sidebar = content.document.querySelector('button#sidebarToggle'),
             outerContainer = content.document.querySelector('div#outerContainer');
 
         sidebar.click();
-        ok(outerContainer.classList.contains('sidebarOpen'), "sidebar opens on click");
+        Assert.ok(outerContainer.classList.contains('sidebarOpen'), "sidebar opens on click");
 
         //
         // Sidebar: close
         //
         sidebar.click();
-        ok(!outerContainer.classList.contains('sidebarOpen'), "sidebar closes on click");
+        Assert.ok(!outerContainer.classList.contains('sidebarOpen'), "sidebar closes on click");
 
         //
         // Page change from prev/next buttons
         //
         var prevPage = content.document.querySelector('button#previous'),
             nextPage = content.document.querySelector('button#next');
 
         var pgNumber = content.document.querySelector('input#pageNumber').value;
-        is(parseInt(pgNumber, 10), 1, 'initial page is 1');
+        Assert.equal(parseInt(pgNumber, 10), 1, "initial page is 1");
 
         //
         // Bookmark button
         //
         var viewBookmark = content.document.querySelector('a#viewBookmark');
         viewBookmark.click();
 
-        ok(viewBookmark.href.length > 0, "viewBookmark button has href");
+        Assert.ok(viewBookmark.href.length > 0, "viewBookmark button has href");
 
         var viewer = content.wrappedJSObject.PDFViewerApplication;
         yield viewer.close();
       });
     });
 });
--- a/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js
+++ b/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js
@@ -152,27 +152,27 @@ add_task(function* test() {
   info('Pref action: ' + handlerInfo.preferredAction);
 
   yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" },
     function* (newTabBrowser) {
       yield waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf");
 
       yield ContentTask.spawn(newTabBrowser, null, function* () {
         // Check if PDF is opened with internal viewer
-        ok(content.document.querySelector('div#viewer'), "document content has viewer UI");
-        ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object");
+        Assert.ok(content.document.querySelector("div#viewer"), "document content has viewer UI");
+        Assert.ok("PDFJS" in content.wrappedJSObject, "window content has PDFJS object");
       });
 
       yield ContentTask.spawn(newTabBrowser, null, contentSetUp);
 
       yield Task.spawn(runTests(newTabBrowser));
 
       yield ContentTask.spawn(newTabBrowser, null, function*() {
         let pageNumber = content.document.querySelector('input#pageNumber');
-        is(pageNumber.value, pageNumber.max, "Document is left on the last page");
+        Assert.equal(pageNumber.value, pageNumber.max, "Document is left on the last page");
       });
     });
 });
 
 function* contentSetUp() {
   /**
    * Outline Items gets appended to the document later on we have to
    * wait for them before we start to navigate though document
@@ -250,17 +250,17 @@ function* runTests(browser) {
           window.removeEventListener('pagechange', pageChange);
           window.clearTimeout(timeout);
           deferred.resolve(+pageNumber.value);
         }
       });
 
       // Get the element and trigger the action for changing the page
       var el = document.querySelector(test.action.selector);
-      ok(el, "Element '" + test.action.selector + "' has been found");
+      Assert.ok(el, "Element '" + test.action.selector + "' has been found");
 
       // The value option is for input case
       if (test.action.value)
         el.value = test.action.value;
 
       // Dispatch the event for changing the page
       if (test.action.event == "keydown") {
         var ev = document.createEvent("KeyboardEvent");
@@ -269,15 +269,15 @@ function* runTests(browser) {
         el.dispatchEvent(ev);
       }
       else {
         var ev = new Event(test.action.event);
       }
       el.dispatchEvent(ev);
 
       let pgNumber = yield deferred.promise;
-      is(pgNumber, test.expectedPage, test.message);
+      Assert.equal(pgNumber, test.expectedPage, test.message);
     }
 
     var viewer = content.wrappedJSObject.PDFViewerApplication;
     yield viewer.close();
   });
 }
--- a/browser/extensions/pdfjs/test/browser_pdfjs_views.js
+++ b/browser/extensions/pdfjs/test/browser_pdfjs_views.js
@@ -16,46 +16,52 @@ add_task(function* test() {
   info('Pref action: ' + handlerInfo.preferredAction);
 
   yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" },
     function* (browser) {
       // check that PDF is opened with internal viewer
       yield waitForPdfJS(browser, TESTROOT + "file_pdfjs_test.pdf");
 
       yield ContentTask.spawn(browser, null, function* () {
-        ok(content.document.querySelector('div#viewer'), "document content has viewer UI");
-        ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object");
+        Assert.ok(content.document.querySelector("div#viewer"), "document content has viewer UI");
+        Assert.ok("PDFJS" in content.wrappedJSObject, "window content has PDFJS object");
 
         //open sidebar
         var sidebar = content.document.querySelector('button#sidebarToggle');
         var outerContainer = content.document.querySelector('div#outerContainer');
 
         sidebar.click();
-        ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click');
+        Assert.ok(outerContainer.classList.contains("sidebarOpen"), "sidebar opens on click");
 
         // check that thumbnail view is open
         var thumbnailView = content.document.querySelector('div#thumbnailView');
         var outlineView = content.document.querySelector('div#outlineView');
 
-        is(thumbnailView.getAttribute('class'), null, 'Initial view is thumbnail view');
-        is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden initially');
+        Assert.equal(thumbnailView.getAttribute("class"), null,
+          "Initial view is thumbnail view");
+        Assert.equal(outlineView.getAttribute("class"), "hidden",
+          "Outline view is hidden initially");
 
         //switch to outline view
         var viewOutlineButton = content.document.querySelector('button#viewOutline');
         viewOutlineButton.click();
 
-        is(thumbnailView.getAttribute('class'), 'hidden', 'Thumbnail view is hidden when outline is selected');
-        is(outlineView.getAttribute('class'), '', 'Outline view is visible when selected');
+        Assert.equal(thumbnailView.getAttribute("class"), "hidden",
+          "Thumbnail view is hidden when outline is selected");
+        Assert.equal(outlineView.getAttribute("class"), "",
+          "Outline view is visible when selected");
 
         //switch back to thumbnail view
         var viewThumbnailButton = content.document.querySelector('button#viewThumbnail');
         viewThumbnailButton.click();
 
-        is(thumbnailView.getAttribute('class'), '', 'Thumbnail view is visible when selected');
-        is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden when thumbnail is selected');
+        Assert.equal(thumbnailView.getAttribute("class"), "",
+          "Thumbnail view is visible when selected");
+        Assert.equal(outlineView.getAttribute("class"), "hidden",
+          "Outline view is hidden when thumbnail is selected");
 
         sidebar.click();
 
         var viewer = content.wrappedJSObject.PDFViewerApplication;
         yield viewer.close();
       });
     });
 });
--- a/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js
+++ b/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js
@@ -88,30 +88,30 @@ add_task(function* test() {
 
               document.removeEventListener("pagerendered", onPageRendered, true);
               resolve();
             }, true);
           });
         }
 
         // check that PDF is opened with internal viewer
-        ok(content.document.querySelector('div#viewer'), "document content has viewer UI");
-        ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object");
+        Assert.ok(content.document.querySelector("div#viewer"), "document content has viewer UI");
+        Assert.ok("PDFJS" in content.wrappedJSObject, "window content has PDFJS object");
 
         let initialWidth, previousWidth;
         initialWidth = previousWidth =
           parseInt(content.document.querySelector("div#pageContainer1").style.width);
 
         for (let test of TESTS) {
           // We zoom using an UI element
           var ev;
           if (test.action.selector) {
             // Get the element and trigger the action for changing the zoom
             var el = document.querySelector(test.action.selector);
-            ok(el, "Element '" + test.action.selector + "' has been found");
+            Assert.ok(el, "Element '" + test.action.selector + "' has been found");
 
             if (test.action.index){
               el.selectedIndex = test.action.index;
             }
 
             // Dispatch the event for changing the zoom
             ev = new Event(test.action.event);
           }
@@ -133,21 +133,21 @@ add_task(function* test() {
           // The zoom value displayed in the zoom select
           var zoomValue = pageZoomScale.options[pageZoomScale.selectedIndex].innerHTML;
 
           let pageContainer = content.document.querySelector('div#pageContainer1');
           let actualWidth = parseInt(pageContainer.style.width);
 
           // the actual zoom of the PDF document
           let computedZoomValue = parseInt(((actualWidth/initialWidth).toFixed(2))*100) + "%";
-          is(computedZoomValue, zoomValue, "Content has correct zoom");
+          Assert.equal(computedZoomValue, zoomValue, "Content has correct zoom");
 
           // Check that document zooms in the expected way (in/out)
           let zoom = (actualWidth - previousWidth) * test.expectedZoom;
-          ok(zoom > 0, test.message);
+          Assert.ok(zoom > 0, test.message);
 
           previousWidth = actualWidth;
         }
 
         var viewer = content.wrappedJSObject.PDFViewerApplication;
         yield viewer.close();
       });
     });
--- a/browser/extensions/pocket/bootstrap.js
+++ b/browser/extensions/pocket/bootstrap.js
@@ -280,36 +280,54 @@ var PocketContextMenu = {
     }
     menu.hidden = !showSaveLinkToPocket;
   }
 }
 
 // PocketReader
 // Listen for reader mode setup and add our button to the reader toolbar
 var PocketReader = {
+  _hidden: true,
+  get hidden() {
+    return this._hidden;
+  },
+  set hidden(hide) {
+    hide = !!hide;
+    if (hide === this._hidden)
+      return;
+    this._hidden = hide;
+    this.update();
+  },
   startup: function() {
+    // Setup the listeners, update will be called when the widget is added,
+    // no need to do that now.
     let mm = Services.mm;
     mm.addMessageListener("Reader:OnSetup", this);
     mm.addMessageListener("Reader:Clicked-pocket-button", this);
-    mm.broadcastAsyncMessage("Reader:AddButton",
-                             { id: "pocket-button",
-                               title: gPocketBundle.GetStringFromName("pocket-button.tooltiptext"),
-                               image: "chrome://pocket/content/panels/img/pocket.svg#pocket-mark" });
   },
   shutdown: function() {
     let mm = Services.mm;
     mm.removeMessageListener("Reader:OnSetup", this);
     mm.removeMessageListener("Reader:Clicked-pocket-button", this);
-    mm.broadcastAsyncMessage("Reader:RemoveButton", { id: "pocket-button" });
+    this.hidden = true;
+  },
+  update: function() {
+    if (this.hidden) {
+      Services.mm.broadcastAsyncMessage("Reader:RemoveButton", { id: "pocket-button" });
+    } else {
+      Services.mm.broadcastAsyncMessage("Reader:AddButton",
+                               { id: "pocket-button",
+                                 title: gPocketBundle.GetStringFromName("pocket-button.tooltiptext"),
+                                 image: "chrome://pocket/content/panels/img/pocket.svg#pocket-mark" });
+    }
   },
   receiveMessage: function(message) {
     switch (message.name) {
       case "Reader:OnSetup": {
-        // tell the reader about our button.  A chrome url here doesn't work, but
-        // we can use the resoure url.
+        // Tell the reader about our button.
         message.target.messageManager.
           sendAsyncMessage("Reader:AddButton", { id: "pocket-button",
                                                  title: gPocketBundle.GetStringFromName("pocket-button.tooltiptext"),
                                                  image: "chrome://pocket/content/panels/img/pocket.svg#pocket-mark"});
         break;
       }
       case "Reader:Clicked-pocket-button": {
         let doc = message.target.ownerDocument;
@@ -351,20 +369,20 @@ var PocketOverlay = {
   startup: function(reason) {
     let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
                               .getService(Ci.nsIStyleSheetService);
     this._sheetType = styleSheetService.AUTHOR_SHEET;
     this._cachedSheet = styleSheetService.preloadSheet(gPocketStyleURI,
                                                        this._sheetType);
     AboutSaved.register();
     AboutSignup.register();
-    CreatePocketWidget(reason);
+    PocketReader.startup();
     CustomizableUI.addListener(this);
+    CreatePocketWidget(reason);
     PocketContextMenu.init();
-    PocketReader.startup();
 
     if (reason != APP_STARTUP) {
       for (let win of allBrowserWindows()) {
         this.setWindowScripts(win);
         this.addStyles(win);
         this.updateWindow(win);
       }
     }
@@ -454,44 +472,32 @@ var PocketOverlay = {
         "id": "panelMenu_pocketSeparator"
       });
       // nextSibling is no-id toolbarseparator
       // insert separator first then button
       sib = sib.nextSibling;
       sib.parentNode.insertBefore(sep, sib);
       sib.parentNode.insertBefore(menu, sib);
     }
-
-    this.updatePocketItemVisibility(document);
   },
-  onWidgetAdded: function(aWidgetId, aArea, aPosition) {
-    for (let win of allBrowserWindows()) {
-      this.updatePocketItemVisibility(win.document);
+  onWidgetAfterDOMChange: function(aWidgetNode) {
+    if (aWidgetNode.id != "pocket-button") {
+      return;
     }
-  },
-  onWidgetRemoved: function(aWidgetId, aArea, aPosition) {
-    for (let win of allBrowserWindows()) {
-      this.updatePocketItemVisibility(win.document);
-    }
-  },
-  updatePocketItemVisibility: function(doc) {
+    let doc = aWidgetNode.ownerDocument;
     let hidden = !CustomizableUI.getPlacementOfWidget("pocket-button");
     for (let prefix of ["panelMenu_", "menu_", "BMB_"]) {
       let element = doc.getElementById(prefix + "pocket");
       if (element) {
         element.hidden = hidden;
         doc.getElementById(prefix + "pocketSeparator").hidden = hidden;
       }
     }
     // enable or disable reader button
-    if (hidden) {
-      PocketReader.shutdown();
-    } else {
-      PocketReader.startup();
-    }
+    PocketReader.hidden = hidden;
   },
 
   addStyles: function(win) {
     let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
     utils.addSheet(this._cachedSheet, this._sheetType);
   },
 
   removeStyles: function(win) {
--- a/browser/modules/test/browser_SelfSupportBackend.js
+++ b/browser/modules/test/browser_SelfSupportBackend.js
@@ -150,18 +150,18 @@ add_task(function* test_selfSupport() {
 
   let observePromise = ContentTask.spawn(selfSupportBrowser, null, function* checkObserve() {
     yield new Promise(resolve => {
       let win = Cu.waiveXrays(content);
       win.Mozilla.UITour.observe((event, data) => {
         if (event != "Heartbeat:Engaged") {
           return;
         }
-        is(data.flowId, "myFlowID", "Check flowId");
-        ok(!!data.timestamp, "Check timestamp");
+        Assert.equal(data.flowId, "myFlowID", "Check flowId");
+        Assert.ok(!!data.timestamp, "Check timestamp");
         resolve(data);
       }, () => {});
     });
   });
 
   info("Notifying Heartbeat:Engaged");
   UITour.notify("Heartbeat:Engaged", {
     flowId: "myFlowID",
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -14,18 +14,18 @@
 %include linuxShared.inc
 %filter substitution
 
 %define forwardTransitionLength 150ms
 %define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-wrapper
 
 :root {
   --backbutton-urlbar-overlap: 6px;
-  /* icon width + border + horizontal padding (includes the overlap from backbutton-urlbar-overlap) */
-  --forwardbutton-width: 31px;
+  /* icon width + border + horizontal padding (without the overlap from backbutton-urlbar-overlap) */
+  --forwardbutton-width: 25px;
 
   --toolbarbutton-hover-background: rgba(255,255,255,.5) linear-gradient(rgba(255,255,255,.5), transparent);
   --toolbarbutton-hover-bordercolor: rgba(0,0,0,.25);
   --toolbarbutton-hover-boxshadow: none;
 
   --toolbarbutton-active-background: rgba(154,154,154,.5) linear-gradient(rgba(255,255,255,.7), rgba(255,255,255,.4));
   --toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
   --toolbarbutton-active-boxshadow: 0 1px 1px rgba(0,0,0,.1) inset, 0 0 1px rgba(0,0,0,.3) inset;
@@ -737,35 +737,35 @@ menuitem:not([type]):not(.menuitem-toolt
   padding: 0;
 }
 
 #forward-button > .toolbarbutton-icon {
   padding-left: calc(var(--backbutton-urlbar-overlap) + 3px);
   padding-right: 3px;
   border-left-style: none;
   border-radius: 0;
-  max-width: var(--forwardbutton-width);
+  max-width: calc(var(--forwardbutton-width) + var(--backbutton-urlbar-overlap));
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
   transition: margin-left @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@ > #forward-button[disabled] {
-  margin-left: calc(0px - var(--forwardbutton-width));
+  margin-left: calc(0px - var(--forwardbutton-width) - var(--backbutton-urlbar-overlap));
 }
 
 @conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] {
   /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
   transition-delay: 100s;
 }
 
 @conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
   /* when not hovered anymore, trigger a new transition to hide the forward button immediately */
-  margin-left: calc(-0.01px - var(--forwardbutton-width));
+  margin-left: calc(-0.01px - var(--forwardbutton-width) - var(--backbutton-urlbar-overlap));
 }
 
 /* undo close tab menu item */
 #alltabs_undoCloseTab {
   list-style-image: url(chrome://browser/skin/undoCloseTab.png);
 }
 
 .unified-nav-back[_moz-menuactive] {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -15,18 +15,18 @@
 @namespace html url("http://www.w3.org/1999/xhtml");
 @namespace svg url("http://www.w3.org/2000/svg");
 
 :root {
   --space-above-tabbar: 9px;
   --tabs-toolbar-color: #333;
 
   --backbutton-urlbar-overlap: 6px;
-  /* icon width + border + horizontal padding (includes the overlap from backbutton-urlbar-overlap) */
-  --forwardbutton-width: 32px;
+  /* icon width + border + horizontal padding (without the overlap from backbutton-urlbar-overlap) */
+  --forwardbutton-width: 26px;
 
   --toolbarbutton-hover-background: hsla(0,0%,100%,.1) linear-gradient(hsla(0,0%,100%,.3), hsla(0,0%,100%,.1)) padding-box;
   --toolbarbutton-hover-bordercolor: hsla(0,0%,0%,.2);
   --toolbarbutton-hover-boxshadow: 0 1px 0 hsla(0,0%,100%,.5),
                                    0 1px 0 hsla(0,0%,100%,.5) inset;
 
   --toolbarbutton-active-background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), transparent) border-box;
   --toolbarbutton-active-bordercolor: hsla(0,0%,0%,.3);
@@ -1349,27 +1349,27 @@ toolbar .toolbarbutton-1 > .toolbarbutto
   }
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
   transition: margin-left @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@ > #forward-button[disabled] {
-  margin-left: calc(0px - var(--forwardbutton-width));
+  margin-left: calc(0px - var(--forwardbutton-width) - var(--backbutton-urlbar-overlap));
 }
 
 @conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] {
   /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
   transition-delay: 100s;
 }
 
 @conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
   /* when not hovered anymore, trigger a new transition to hide the forward button immediately */
-  margin-left: calc(-0.01px - var(--forwardbutton-width));
+  margin-left: calc(-0.01px - var(--forwardbutton-width) - var(--backbutton-urlbar-overlap));
 }
 
 .unified-nav-back[_moz-menuactive]:-moz-locale-dir(ltr),
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/menu-back.png") !important;
 }
 
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(ltr),
--- a/browser/themes/osx/devedition.css
+++ b/browser/themes/osx/devedition.css
@@ -1,14 +1,18 @@
 % 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/.
 
 %include ../shared/devedition.inc.css
 
+:root {
+  --forwardbutton-width: 32px;
+}
+
 /* Use only 1px separator between nav toolbox and page content */
 #navigator-toolbox::after {
   background: linear-gradient(to top, var(--chrome-navigator-toolbox-separator-color), var(--chrome-navigator-toolbox-separator-color) 1px, transparent 1px);
 }
 
 /* Include extra space on left/right for dragging since there is no space above
    the tabs */
 #main-window[tabsintitlebar] #TabsToolbar {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -15,18 +15,18 @@
 %define forwardTransitionLength 150ms
 %define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-wrapper
 
 :root {
   --space-above-tabbar: 15px;
 
   --backbutton-urlbar-overlap: 6px;
 
-  /* icon width + border + horizontal padding (includes the overlap from backbutton-urlbar-overlap) */
-  --forwardbutton-width: 31px;
+  /* icon width + border + horizontal padding (without the overlap from backbutton-urlbar-overlap) */
+  --forwardbutton-width: 25px;
 
   --toolbarbutton-vertical-inner-padding: 2px;
   --toolbarbutton-vertical-outer-padding: 8px;
 
   --toolbarbutton-hover-background: rgba(0,0,0,.1);
   --toolbarbutton-hover-bordercolor: rgba(0,0,0,.2);
   --toolbarbutton-hover-boxshadow: none;
 
@@ -933,35 +933,35 @@ toolbar[brighttext] .toolbarbutton-1 > .
   margin-top: 1px !important;
 }
 
 #forward-button > .toolbarbutton-icon {
   border-left-style: none !important;
   border-radius: 0 !important;
   padding-left: calc(var(--backbutton-urlbar-overlap) + 3px) !important;
   padding-right: 3px !important;
-  max-width: var(--forwardbutton-width) !important;
+  max-width: calc(var(--forwardbutton-width) + var(--backbutton-urlbar-overlap)) !important;
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
   transition: margin-left @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@ > #forward-button[disabled] {
-  margin-left: calc(0px - var(--forwardbutton-width));
+  margin-left: calc(0px - var(--forwardbutton-width) - var(--backbutton-urlbar-overlap));
 }
 
 @conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] {
   /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
   transition-delay: 100s;
 }
 
 @conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
   /* when not hovered anymore, trigger a new transition to hide the forward button immediately */
-  margin-left: calc(-0.01px - var(--forwardbutton-width));
+  margin-left: calc(-0.01px - var(--forwardbutton-width) - var(--backbutton-urlbar-overlap));
 }
 
 #back-button {
   padding-top: 3px !important;
   padding-bottom: 3px !important;
   -moz-padding-start: 5px !important;
   -moz-padding-end: 0 !important;
   position: relative !important;
--- a/devtools/.eslintrc
+++ b/devtools/.eslintrc
@@ -35,17 +35,17 @@
 
     // Rules from the React plugin
     "react/display-name": 1,
     "react/no-danger": 1,
     "react/no-did-mount-set-state": 1,
     "react/no-did-update-set-state": 1,
     "react/no-direct-mutation-state": 1,
     "react/no-unknown-property": 1,
-    "react/prefer-es6-class": 1,
+    "react/prefer-es6-class": [1, "never"],
     "react/prop-types": 1,
     "react/sort-comp": [1, {
       order: [
         "propTypes",
         "everything-else",
         "render"
       ]
     }],
--- a/devtools/client/commandline/test/browser_cmd_screenshot.js
+++ b/devtools/client/commandline/test/browser_cmd_screenshot.js
@@ -126,76 +126,66 @@ function* addTabWithToolbarRunTests(win)
           chrome: { value: false },
         },
       },
       exec: {
         output: new RegExp("^Copied to clipboard.$"),
       },
       post: Task.async(function*() {
         let imgSize = yield getImageSizeFromClipboard();
-        let winSize = yield ContentTask.spawn(browser, {}, function*() {
-          return {
-            width: content.innerWidth,
-            height: content.innerHeight,
-          };
+        yield ContentTask.spawn(browser, imgSize, function*(imgSize) {
+          Assert.equal(imgSize.width, content.innerWidth, "Image width matches window size");
+          Assert.equal(imgSize.height, content.innerHeight, "Image height matches window size");
         });
-        is(imgSize.width, winSize.width, "Image width matches window size");
-        is(imgSize.height, winSize.height, "Image height matches window size");
       })
     },
     {
       setup: "screenshot --fullpage --clipboard",
       check: {
         args: {
           fullpage: { value: true },
           clipboard: { value: true },
           chrome: { value: false },
         },
       },
       exec: {
         output: new RegExp("^Copied to clipboard.$"),
       },
       post: Task.async(function*() {
         let imgSize = yield getImageSizeFromClipboard();
-        let pageSize = yield ContentTask.spawn(browser, {}, function*() {
-          return {
-            width: content.innerWidth +
-                   content.scrollMaxX - content.scrollMinX,
-            height: content.innerHeight +
-                    content.scrollMaxY - content.scrollMinY,
-          };
+        yield ContentTask.spawn(browser, imgSize, function*(imgSize) {
+          Assert.equal(imgSize.width,
+            content.innerWidth + content.scrollMaxX - content.scrollMinX,
+            "Image width matches page size");
+          Assert.equal(imgSize.height,
+            content.innerHeight + content.scrollMaxY - content.scrollMinY,
+            "Image height matches page size");
         });
-        is(imgSize.width, pageSize.width, "Image width matches page size");
-        is(imgSize.height, pageSize.height, "Image height matches page size");
       })
     },
     {
       setup: "screenshot --selector img#testImage --clipboard",
       check: {
         args: {
           clipboard: { value: true },
           chrome: { value: false },
         },
       },
       exec: {
         output: new RegExp("^Copied to clipboard.$"),
       },
       post: Task.async(function*() {
         let imgSize = yield getImageSizeFromClipboard();
-        let elemSize = yield ContentTask.spawn(browser, {}, function*() {
+        yield ContentTask.spawn(browser, imgSize, function*(imgSize) {
           let img = content.document.querySelector("img#testImage");
-          return {
-            width: img.clientWidth,
-            height: img.clientHeight,
-          };
+          Assert.equal(imgSize.width, img.clientWidth,
+             "Image width matches element size");
+          Assert.equal(imgSize.height, img.clientHeight,
+             "Image height matches element size");
         });
-        is(imgSize.width, elemSize.width,
-           "Image width matches element size");
-        is(imgSize.height, elemSize.height,
-           "Image height matches element size");
       })
     },
   ]);
 
   // Trigger scrollbars by forcing document to overflow
   // This only affects results on OSes with scrollbars that reduce document size
   // (non-floating scrollbars).  With default OS settings, this means Windows
   // and Linux are affected, but Mac is not.  For Mac to exhibit this behavior,
@@ -228,80 +218,72 @@ function* addTabWithToolbarRunTests(win)
           chrome: { value: false },
         },
       },
       exec: {
         output: new RegExp("^Copied to clipboard.$"),
       },
       post: Task.async(function*() {
         let imgSize = yield getImageSizeFromClipboard();
-        let winSize = yield ContentTask.spawn(browser, {}, function*() {
-          return {
-            width: content.innerWidth,
-            height: content.innerHeight,
-          };
+        imgSize.scrollbarWidth = scrollbarSize.width;
+        imgSize.scrollbarHeight = scrollbarSize.height;
+        yield ContentTask.spawn(browser, imgSize, function*(imgSize) {
+          Assert.equal(imgSize.width, content.innerWidth - imgSize.scrollbarWidth,
+             "Image width matches window size minus scrollbar size");
+          Assert.equal(imgSize.height, content.innerHeight - imgSize.scrollbarHeight,
+             "Image height matches window size minus scrollbar size");
         });
-        is(imgSize.width, winSize.width - scrollbarSize.width,
-           "Image width matches window size minus scrollbar size");
-        is(imgSize.height, winSize.height - scrollbarSize.height,
-           "Image height matches window size minus scrollbar size");
       })
     },
     {
       setup: "screenshot --fullpage --clipboard",
       check: {
         args: {
           fullpage: { value: true },
           clipboard: { value: true },
           chrome: { value: false },
         },
       },
       exec: {
         output: new RegExp("^Copied to clipboard.$"),
       },
       post: Task.async(function*() {
         let imgSize = yield getImageSizeFromClipboard();
-        let pageSize = yield ContentTask.spawn(browser, {}, function*() {
-          return {
-            width: content.innerWidth +
-                   content.scrollMaxX - content.scrollMinX,
-            height: content.innerHeight +
-                    content.scrollMaxY - content.scrollMinY,
-          };
+        imgSize.scrollbarWidth = scrollbarSize.width;
+        imgSize.scrollbarHeight = scrollbarSize.height;
+        yield ContentTask.spawn(browser, imgSize, function*(imgSize) {
+          Assert.equal(imgSize.width,
+            (content.innerWidth + content.scrollMaxX - content.scrollMinX) - imgSize.scrollbarWidth,
+            "Image width matches page size minus scrollbar size");
+          Assert.equal(imgSize.height,
+            (content.innerHeight + content.scrollMaxY - content.scrollMinY) - imgSize.scrollbarHeight,
+            "Image height matches page size minus scrollbar size");
         });
-        is(imgSize.width, pageSize.width - scrollbarSize.width,
-           "Image width matches page size minus scrollbar size");
-        is(imgSize.height, pageSize.height - scrollbarSize.height,
-           "Image height matches page size minus scrollbar size");
       })
     },
     {
       setup: "screenshot --selector img#testImage --clipboard",
       check: {
         args: {
           clipboard: { value: true },
           chrome: { value: false },
         },
       },
       exec: {
         output: new RegExp("^Copied to clipboard.$"),
       },
       post: Task.async(function*() {
         let imgSize = yield getImageSizeFromClipboard();
-        let elemSize = yield ContentTask.spawn(browser, {}, function*() {
+        yield ContentTask.spawn(browser, imgSize, function*(imgSize) {
           let img = content.document.querySelector("img#testImage");
-          return {
-            width: img.clientWidth,
-            height: img.clientHeight,
-          };
+          Assert.equal(imgSize.width, img.clientWidth,
+             "Image width matches element size");
+          Assert.equal(imgSize.height, img.clientHeight,
+             "Image height matches element size");
         });
-        is(imgSize.width, elemSize.width,
-           "Image width matches element size");
-        is(imgSize.height, elemSize.height,
-           "Image height matches element size");
       })
     },
   ]);
 
   yield helpers.closeToolbar(options);
   yield helpers.closeTab(options);
 }
 
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -10,42 +10,47 @@
  *
  * This module is loaded lazily by devtools-clhandler.js, once the first
  * browser window is ready (i.e. fired browser-delayed-startup-finished event)
  **/
 
 const {Cc, Ci, Cu} = require("chrome");
 const Services = require("Services");
 const promise = require("promise");
+const Telemetry = require("devtools/client/shared/telemetry");
 const {gDevTools} = require("./devtools");
 
 // Load target and toolbox lazily as they need gDevTools to be fully initialized
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
 
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
-                                  "resource:///modules/CustomizableUI.jsm");
+loader.lazyImporter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
 
 const bundle = Services.strings.createBundle("chrome://devtools/locale/toolbox.properties");
 
+const TABS_OPEN_PEAK_HISTOGRAM = "DEVTOOLS_TABS_OPEN_PEAK_LINEAR";
+const TABS_OPEN_AVG_HISTOGRAM = "DEVTOOLS_TABS_OPEN_AVERAGE_LINEAR";
+const TABS_PINNED_PEAK_HISTOGRAM = "DEVTOOLS_TABS_PINNED_PEAK_LINEAR";
+const TABS_PINNED_AVG_HISTOGRAM = "DEVTOOLS_TABS_PINNED_AVERAGE_LINEAR";
+
 /**
  * gDevToolsBrowser exposes functions to connect the gDevTools instance with a
  * Firefox instance.
  */
 var gDevToolsBrowser = exports.gDevToolsBrowser = {
   /**
    * A record of the windows whose menus we altered, so we can undo the changes
    * as the window is closed
    */
   _trackedBrowserWindows: new Set(),
 
+  _telemetry: new Telemetry(),
+
   _tabStats: {
     peakOpen: 0,
     peakPinned: 0,
     histOpen: [],
     histPinned: []
   },
 
   /**
@@ -820,24 +825,44 @@ var gDevToolsBrowser = exports.gDevTools
       break;
       case "unload":
         // top-level browser window unload
         gDevToolsBrowser._forgetBrowserWindow(event.target.defaultView);
       break;
     }
   },
 
+  _pingTelemetry: function() {
+    let mean = function(arr) {
+      if (arr.length === 0) {
+        return 0;
+      }
+
+      let total = arr.reduce((a, b) => a + b);
+      return Math.ceil(total / arr.length);
+    };
+
+    let tabStats = gDevToolsBrowser._tabStats;
+    this._telemetry.log(TABS_OPEN_PEAK_HISTOGRAM, tabStats.peakOpen);
+    this._telemetry.log(TABS_OPEN_AVG_HISTOGRAM, mean(tabStats.histOpen));
+    this._telemetry.log(TABS_PINNED_PEAK_HISTOGRAM, tabStats.peakPinned);
+    this._telemetry.log(TABS_PINNED_AVG_HISTOGRAM, mean(tabStats.histPinned));
+  },
+
   /**
    * All browser windows have been closed, tidy up remaining objects.
    */
   destroy: function() {
     Services.prefs.removeObserver("devtools.", gDevToolsBrowser);
     Services.obs.removeObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
     Services.obs.removeObserver(gDevToolsBrowser.destroy, "quit-application");
 
+    gDevToolsBrowser._pingTelemetry();
+    gDevToolsBrowser._telemetry = null;
+
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
       gDevToolsBrowser._forgetBrowserWindow(win);
     }
   },
 }
 
 gDevTools.on("tool-registered", function(ev, toolId) {
   let toolDefinition = gDevTools._tools.get(toolId);
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -9,51 +9,60 @@ const promise = require("promise");
 
 // Load gDevToolsBrowser toolbox lazily as they need gDevTools to be fully initialized
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 
 const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} =
   require("devtools/client/definitions");
 const EventEmitter = require("devtools/shared/event-emitter");
-const Telemetry = require("devtools/client/shared/telemetry");
 const {JsonView} = require("devtools/client/jsonview/main");
 
-const TABS_OPEN_PEAK_HISTOGRAM = "DEVTOOLS_TABS_OPEN_PEAK_LINEAR";
-const TABS_OPEN_AVG_HISTOGRAM = "DEVTOOLS_TABS_OPEN_AVERAGE_LINEAR";
-const TABS_PINNED_PEAK_HISTOGRAM = "DEVTOOLS_TABS_PINNED_PEAK_LINEAR";
-const TABS_PINNED_AVG_HISTOGRAM = "DEVTOOLS_TABS_PINNED_AVERAGE_LINEAR";
-
 const FORBIDDEN_IDS = new Set(["toolbox", ""]);
 const MAX_ORDINAL = 99;
 
 /**
  * DevTools is a class that represents a set of developer tools, it holds a
  * set of tools and keeps track of open toolboxes in the browser.
  */
 this.DevTools = function DevTools() {
   this._tools = new Map();     // Map<toolId, tool>
   this._themes = new Map();    // Map<themeId, theme>
   this._toolboxes = new Map(); // Map<target, toolbox>
-  this._telemetry = new Telemetry();
 
   // destroy() is an observer's handler so we need to preserve context.
   this.destroy = this.destroy.bind(this);
   this._teardown = this._teardown.bind(this);
 
   // JSON Viewer for 'application/json' documents.
   JsonView.initialize();
 
   EventEmitter.decorate(this);
 
   Services.obs.addObserver(this._teardown, "devtools-unloaded", false);
   Services.obs.addObserver(this.destroy, "quit-application", false);
 };
 
 DevTools.prototype = {
+  registerDefaults() {
+    // Ensure registering items in the sorted order (getDefault* functions
+    // return sorted lists)
+    this.getDefaultTools().forEach(definition => this.registerTool(definition));
+    this.getDefaultThemes().forEach(definition => this.registerTheme(definition));
+  },
+
+  unregisterDefaults() {
+    for (let definition of this.getToolDefinitionArray()) {
+      this.unregisterTool(definition.id);
+    }
+    for (let definition of this.getThemeDefinitionArray()) {
+      this.unregisterTheme(definition.id);
+    }
+  },
+
   /**
    * Register a new developer tool.
    *
    * A definition is a light object that holds different information about a
    * developer tool. This object is not supposed to have any operational code.
    * See it as a "manifest".
    * The only actual code lives in the build() function, which will be used to
    * start an instance of this tool.
@@ -85,17 +94,17 @@ DevTools.prototype = {
 
     if (!toolId || FORBIDDEN_IDS.has(toolId)) {
       throw new Error("Invalid definition.id");
     }
 
     // Make sure that additional tools will always be able to be hidden.
     // When being called from main.js, defaultTools has not yet been exported.
     // But, we can assume that in this case, it is a default tool.
-    if (DefaultTools && DefaultTools.indexOf(toolDefinition) == -1) {
+    if (DefaultTools.indexOf(toolDefinition) == -1) {
       toolDefinition.visibilityswitch = "devtools." + toolId + ".enabled";
     }
 
     this._tools.set(toolId, toolDefinition);
 
     this.emit("tool-registered", toolId);
   },
 
@@ -144,16 +153,20 @@ DevTools.prototype = {
     for (let [key, value] of this._tools) {
       if (DefaultTools.indexOf(value) == -1) {
         tools.push(value);
       }
     }
     return tools.sort(this.ordinalSort);
   },
 
+  getDefaultThemes() {
+    return DefaultThemes.sort(this.ordinalSort);
+  },
+
   /**
    * Get a tool definition if it exists and is enabled.
    *
    * @param {string} toolId
    *        The id of the tool to show
    *
    * @return {ToolDefinition|null} tool
    *         The ToolDefinition for the id or null.
@@ -445,33 +458,16 @@ DevTools.prototype = {
   closeToolbox: function DT_closeToolbox(target) {
     let toolbox = this._toolboxes.get(target);
     if (toolbox == null) {
       return promise.resolve(false);
     }
     return toolbox.destroy().then(() => true);
   },
 
-  _pingTelemetry: function() {
-    let mean = function(arr) {
-      if (arr.length === 0) {
-        return 0;
-      }
-
-      let total = arr.reduce((a, b) => a + b);
-      return Math.ceil(total / arr.length);
-    };
-
-    let tabStats = gDevToolsBrowser._tabStats;
-    this._telemetry.log(TABS_OPEN_PEAK_HISTOGRAM, tabStats.peakOpen);
-    this._telemetry.log(TABS_OPEN_AVG_HISTOGRAM, mean(tabStats.histOpen));
-    this._telemetry.log(TABS_PINNED_PEAK_HISTOGRAM, tabStats.peakPinned);
-    this._telemetry.log(TABS_PINNED_AVG_HISTOGRAM, mean(tabStats.histPinned));
-  },
-
   /**
    * Called to tear down a tools provider.
    */
   _teardown: function DT_teardown() {
     for (let [target, toolbox] of this._toolboxes) {
       toolbox.destroy();
     }
   },
@@ -484,19 +480,16 @@ DevTools.prototype = {
     Services.obs.removeObserver(this._teardown, "devtools-unloaded");
 
     for (let [key, tool] of this.getToolDefinitionMap()) {
       this.unregisterTool(key, true);
     }
 
     JsonView.destroy();
 
-    this._pingTelemetry();
-    this._telemetry = null;
-
     // Cleaning down the toolboxes: i.e.
     //   for (let [target, toolbox] of this._toolboxes) toolbox.destroy();
     // Is taken care of by the gDevToolsBrowser.forgetBrowserWindow
   },
 
   /**
    * Iterator that yields each of the toolboxes.
    */
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* globals _Iterator, StopIteration */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
-const ToolDefinitions = require("devtools/client/main").Tools;
+const ToolDefinitions = require("devtools/client/definitions").Tools;
 const {CssLogic} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
 const promise = require("promise");
 const Services = require("Services");
 const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
 const {OutputParser} = require("devtools/client/shared/output-parser");
 const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
 const {createChild} = require("devtools/client/inspector/shared/utils");
--- a/devtools/client/inspector/layout/test/browser_layout_update-after-reload.js
+++ b/devtools/client/inspector/layout/test/browser_layout_update-after-reload.js
@@ -9,17 +9,17 @@
 add_task(function*() {
   yield addTab(URL_ROOT + "doc_layout_iframe1.html");
   let {inspector, view, testActor} = yield openLayoutView();
 
   info("Test that the layout-view works on the first page");
   yield assertLayoutView(inspector, view, testActor);
 
   info("Reload the page");
-  yield testActor.eval("content.location.reload();");
+  yield testActor.reload();
   yield inspector.once("markuploaded");
 
   info("Test that the layout-view works on the reloaded page");
   yield assertLayoutView(inspector, view, testActor);
 });
 
 function* assertLayoutView(inspector, view, testActor) {
   info("Selecting the test node");
--- a/devtools/client/inspector/markup/test/actor_events_form.js
+++ b/devtools/client/inspector/markup/test/actor_events_form.js
@@ -6,17 +6,16 @@
 // This test actor is used for testing the addition of custom form data
 // on NodeActor. Custom form property is set when 'form' event is sent
 // by NodeActor actor (see 'onNodeActorForm' method).
 
 const Events = require("sdk/event/core");
 const {ActorClass, Actor, FrontClass, Front, method} =
   require("devtools/server/protocol");
 
-const {Cu} = require("chrome");
 const {NodeActor} = require("devtools/server/actors/inspector");
 
 var EventsFormActor = ActorClass({
   typeName: "eventsFormActor",
 
   initialize: function() {
     Actor.prototype.initialize.apply(this, arguments);
   },
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -9,16 +9,17 @@ support-files =
   doc_markup_edit.html
   doc_markup_events.html
   doc_markup_events_form.html
   doc_markup_events_jquery.html
   doc_markup_events-overflow.html
   doc_markup_flashing.html
   doc_markup_html_mixed_case.html
   doc_markup_image_and_canvas.html
+  doc_markup_image_and_canvas_2.html
   doc_markup_links.html
   doc_markup_mutation.html
   doc_markup_navigation.html
   doc_markup_not_displayed.html
   doc_markup_pagesize_01.html
   doc_markup_pagesize_02.html
   doc_markup_search.html
   doc_markup_svg_attributes.html
--- a/devtools/client/inspector/markup/test/browser_markup_anonymous_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_01.js
@@ -9,36 +9,36 @@ const TEST_URL = URL_ROOT + "doc_markup_
 
 add_task(function*() {
   let {inspector} = yield openInspectorForURL(TEST_URL);
 
   let pseudo = yield getNodeFront("#pseudo", inspector);
 
   // Markup looks like: <div><::before /><span /><::after /></div>
   let children = yield inspector.walker.children(pseudo);
-  is (children.nodes.length, 3, "Children returned from walker");
+  is(children.nodes.length, 3, "Children returned from walker");
 
-  info ("Checking the ::before pseudo element");
+  info("Checking the ::before pseudo element");
   let before = children.nodes[0];
   yield isEditingMenuDisabled(before, inspector);
 
-  info ("Checking the normal child element");
+  info("Checking the normal child element");
   let span = children.nodes[1];
   yield isEditingMenuEnabled(span, inspector);
 
-  info ("Checking the ::after pseudo element");
+  info("Checking the ::after pseudo element");
   let after = children.nodes[2];
   yield isEditingMenuDisabled(after, inspector);
 
   let native = yield getNodeFront("#native", inspector);
 
   // Markup looks like: <div><video controls /></div>
   let nativeChildren = yield inspector.walker.children(native);
-  is (nativeChildren.nodes.length, 1, "Children returned from walker");
+  is(nativeChildren.nodes.length, 1, "Children returned from walker");
 
-  info ("Checking the video element");
+  info("Checking the video element");
   let video = nativeChildren.nodes[0];
-  ok (!video.isAnonymous, "<video> is not anonymous");
+  ok(!video.isAnonymous, "<video> is not anonymous");
 
   let videoChildren = yield inspector.walker.children(video);
-  is (videoChildren.nodes.length, 0,
+  is(videoChildren.nodes.length, 0,
     "No native children returned from walker for <video> by default");
 });
--- a/devtools/client/inspector/markup/test/browser_markup_anonymous_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_02.js
@@ -11,21 +11,21 @@ const TEST_URL = "chrome://devtools/cont
 
 add_task(function*() {
   let {inspector} = yield openInspectorForURL(TEST_URL);
 
   let toolbarbutton = yield getNodeFront("toolbarbutton", inspector);
   let children = yield inspector.walker.children(toolbarbutton);
 
   is(toolbarbutton.numChildren, 3, "Correct number of children");
-  is (children.nodes.length, 3, "Children returned from walker");
+  is(children.nodes.length, 3, "Children returned from walker");
 
   is(toolbarbutton.isAnonymous, false, "Toolbarbutton is not anonymous");
   yield isEditingMenuEnabled(toolbarbutton, inspector);
 
   for (let node of children.nodes) {
-    ok (node.isAnonymous, "Child is anonymous");
-    ok (node._form.isXBLAnonymous, "Child is XBL anonymous");
-    ok (!node._form.isShadowAnonymous, "Child is not shadow anonymous");
-    ok (!node._form.isNativeAnonymous, "Child is not native anonymous");
+    ok(node.isAnonymous, "Child is anonymous");
+    ok(node._form.isXBLAnonymous, "Child is XBL anonymous");
+    ok(!node._form.isShadowAnonymous, "Child is not shadow anonymous");
+    ok(!node._form.isNativeAnonymous, "Child is not native anonymous");
     yield isEditingMenuDisabled(node, inspector);
   }
 });
--- a/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
@@ -12,23 +12,23 @@ const TEST_URL = URL_ROOT + "doc_markup_
 add_task(function*() {
   Services.prefs.setBoolPref("dom.webcomponents.enabled", true);
 
   let {inspector} = yield openInspectorForURL(TEST_URL);
 
   let shadow = yield getNodeFront("#shadow", inspector.markup);
   let children = yield inspector.walker.children(shadow);
 
-  is (shadow.numChildren, 3, "Children of the shadow root are counted");
-  is (children.nodes.length, 3, "Children returned from walker");
+  is(shadow.numChildren, 3, "Children of the shadow root are counted");
+  is(children.nodes.length, 3, "Children returned from walker");
 
-  info ("Checking the ::before pseudo element");
+  info("Checking the ::before pseudo element");
   let before = children.nodes[0];
   yield isEditingMenuDisabled(before, inspector);
 
-  info ("Checking the <h3> shadow element");
+  info("Checking the <h3> shadow element");
   let shadowChild1 = children.nodes[1];
   yield isEditingMenuDisabled(shadowChild1, inspector);
 
-  info ("Checking the <select> shadow element");
+  info("Checking the <select> shadow element");
   let shadowChild2 = children.nodes[2];
   yield isEditingMenuDisabled(shadowChild2, inspector);
 });
--- a/devtools/client/inspector/markup/test/browser_markup_anonymous_04.js
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_04.js
@@ -1,36 +1,37 @@
 /* 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";
 
-// Test native anonymous content in the markupview with devtools.inspector.showAllAnonymousContent
-// set to true
+// Test native anonymous content in the markupview with
+// devtools.inspector.showAllAnonymousContent set to true
 const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
+const PREF = "devtools.inspector.showAllAnonymousContent";
 
 add_task(function*() {
-  Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
+  Services.prefs.setBoolPref(PREF, true);
 
   let {inspector} = yield openInspectorForURL(TEST_URL);
 
   let native = yield getNodeFront("#native", inspector);
 
   // Markup looks like: <div><video controls /></div>
   let nativeChildren = yield inspector.walker.children(native);
-  is (nativeChildren.nodes.length, 1, "Children returned from walker");
+  is(nativeChildren.nodes.length, 1, "Children returned from walker");
 
-  info ("Checking the video element");
+  info("Checking the video element");
   let video = nativeChildren.nodes[0];
-  ok (!video.isAnonymous, "<video> is not anonymous");
+  ok(!video.isAnonymous, "<video> is not anonymous");
 
   let videoChildren = yield inspector.walker.children(video);
-  is (videoChildren.nodes.length, 3, "<video> has native anonymous children");
+  is(videoChildren.nodes.length, 3, "<video> has native anonymous children");
 
   for (let node of videoChildren.nodes) {
-    ok (node.isAnonymous, "Child is anonymous");
-    ok (!node._form.isXBLAnonymous, "Child is not XBL anonymous");
-    ok (!node._form.isShadowAnonymous, "Child is not shadow anonymous");
-    ok (node._form.isNativeAnonymous, "Child is native anonymous");
+    ok(node.isAnonymous, "Child is anonymous");
+    ok(!node._form.isXBLAnonymous, "Child is not XBL anonymous");
+    ok(!node._form.isShadowAnonymous, "Child is not shadow anonymous");
+    ok(node._form.isNativeAnonymous, "Child is native anonymous");
     yield isEditingMenuDisabled(node, inspector);
   }
 });
--- a/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute.js
+++ b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute.js
@@ -17,55 +17,59 @@ const TEST_URL = URL_ROOT + "doc_markup_
 //  [
 //    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:-moz-box', 15, 23, true],
-  ['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: -moz-box', 16, 24, true],
-  [' ', 'style="display:  -moz-box', 17, 25, true],
-  ['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 :aliceblue ', 33, 42, true],
-  ['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 !important; ', 44, 55, true],
-  ['!', '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]
+  ["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:-moz-box", 15, 23, true],
+  ["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: -moz-box", 16, 24, true],
+  [" ", "style=\"display:  -moz-box", 17, 25, true],
+  ["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 :aliceblue ", 33, 42, true],
+  ["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 !important; ",
+   44, 55, true],
+  ["!", "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]
 ];
 
 add_task(function*() {
   info("Opening the inspector on the test URL");
   let {inspector} = yield openInspectorForURL(TEST_URL);
 
   yield inspector.markup.expandAll();
 
@@ -92,17 +96,18 @@ add_task(function*() {
   while (inspector.markup.undo.canUndo()) {
     yield undoChange(inspector);
   }
   yield onMutation;
 });
 
 function enterData(index, editor, inspector) {
   let [key] = TEST_DATA[index];
-  info("Entering test data " + index + ": " + key + ", expecting: [" + TEST_DATA[index].slice(1) + "]");
+  let expected = TEST_DATA[index].slice(1);
+  info(`Entering test data ${index}: ${key}, expecting: [${expected}]`);
 
   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);
@@ -126,28 +131,32 @@ function enterData(index, editor, inspec
     });
   }
   EventUtils.synthesizeKey(key, {}, inspector.panelWin);
 
   return def.promise;
 }
 
 function* checkData(index, editor, inspector) {
-  let [key, completion, selStart, selEnd, popupOpen] = TEST_DATA[index];
+  let [, completion, selStart, selEnd, popupOpen] = TEST_DATA[index];
   info("Test data " + index + " entered. Checking state.");
 
   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.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",
+      ok(editor.popup._panel.state != "open" &&
+         editor.popup._panel.state != "showing",
         "Popup is closed");
     }
   } else {
     let nodeFront = yield getNodeFront("#node14", inspector);
-    let editor = getContainerForNodeFront(nodeFront, inspector).editor;
-    let attr = editor.attrElements.get("style").querySelector(".editable");
-    is(attr.textContent, completion, "Correct value is persisted after pressing Enter");
+    let container = getContainerForNodeFront(nodeFront, inspector);
+    let attr = container.editor.attrElements.get("style")
+                                            .querySelector(".editable");
+    is(attr.textContent, completion,
+       "Correct value is persisted after pressing Enter");
   }
 }
--- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js
@@ -2,19 +2,20 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Check that pseudo-elements and anonymous nodes are not draggable.
 
 const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
+const PREF = "devtools.inspector.showAllAnonymousContent";
 
 add_task(function*() {
-  Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
+  Services.prefs.setBoolPref(PREF, true);
 
   let {inspector} = yield openInspectorForURL(TEST_URL);
 
   info("Expanding nodes below #test");
   let parentFront = yield getNodeFront("#test", inspector);
   yield inspector.markup.expandNode(parentFront);
   yield waitForMultipleChildrenUpdates(inspector);
 
--- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_tooltip.js
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_tooltip.js
@@ -1,8 +1,11 @@
+/* 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";
 
 // Test that tooltips don't appear when dragging over tooltip targets.
 
 const TEST_URL = "data:text/html;charset=utf8,<img src=\"about:logo\" /><div>";
 
 add_task(function*() {
   let {inspector} = yield openInspectorForURL(TEST_URL);
@@ -15,20 +18,20 @@ add_task(function*() {
   info("Check that the src attribute of the image is a valid tooltip target");
   let isValid = yield markup.tooltip.isValidHoverTarget(target);
   ok(isValid, "The element is a valid tooltip target");
 
   info("Start dragging the test div");
   yield simulateNodeDrag(inspector, "div");
 
   info("Now check that the src attribute of the image isn't a valid target");
-  try{
+  try {
     yield markup.tooltip.isValidHoverTarget(target);
     isValid = true;
-  } catch(e) {
+  } catch (e) {
     isValid = false;
   }
   ok(!isValid, "The element is not a valid tooltip target");
 
   info("Stop dragging the test div");
   yield simulateNodeDrop(inspector, "div");
 
   info("Check again the src attribute of the image");
--- a/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
@@ -2,18 +2,20 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const TEST_URL = URL_ROOT + "doc_markup_events-overflow.html";
 const TEST_DATA = [
   {
     desc: "editor overflows container",
-    initialScrollTop: -1, // scroll to bottom
-    headerToClick: 49, // last header
+    // scroll to bottom
+    initialScrollTop: -1,
+    // last header
+    headerToClick: 49,
     alignBottom: true,
     alignTop: false,
   },
   {
     desc: "header overflows the container",
     initialScrollTop: 2,
     headerToClick: 0,
     alignBottom: false,
@@ -31,17 +33,18 @@ const TEST_DATA = [
 add_task(function*() {
   let { inspector } = yield openInspectorForURL(TEST_URL);
 
   let markupContainer = yield getContainerForSelector("#events", inspector);
   let evHolder = markupContainer.elt.querySelector(".markupview-events");
   let tooltip = inspector.markup.tooltip;
 
   info("Clicking to open event tooltip.");
-  EventUtils.synthesizeMouseAtCenter(evHolder, {}, inspector.markup.doc.defaultView);
+  EventUtils.synthesizeMouseAtCenter(evHolder, {},
+    inspector.markup.doc.defaultView);
   yield tooltip.once("shown");
   info("EventTooltip visible.");
 
   let container = tooltip.content;
   let containerRect = container.getBoundingClientRect();
   let headers = container.querySelectorAll(".event-header");
 
   for (let data of TEST_DATA) {
@@ -70,21 +73,19 @@ add_task(function*() {
     // Wait for any scrolling to finish.
     yield promiseNextTick();
 
     if (data.alignTop) {
       let headerRect = header.getBoundingClientRect();
 
       is(Math.round(headerRect.top), Math.round(containerRect.top),
         "Clicked header is aligned with the container top.");
-
     } else if (data.alignBottom) {
       let editorRect = header.nextElementSibling.getBoundingClientRect();
 
       is(Math.round(editorRect.bottom), Math.round(containerRect.bottom),
         "Clicked event handler code is aligned with the container bottom.");
-
     } else {
       is(container.scrollTop, data.initialScrollTop,
         "Container did not scroll, as expected.");
     }
   }
 });
--- a/devtools/client/inspector/markup/test/browser_markup_events.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events.js
@@ -1,22 +1,23 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
 
 "use strict";
 
 // Test that markup view event bubbles show the correct event info for DOM
 // events.
 
 const TEST_URL = URL_ROOT + "doc_markup_events.html";
 
 loadHelperScript("helper_events_test_runner.js");
 
-const TEST_DATA = [
+const TEST_DATA = [ // eslint-disable-line
   {
     selector: "html",
     expected: [
       {
         type: "load",
         filename: TEST_URL,
         attributes: [
           "Bubbling",
@@ -31,51 +32,51 @@ const TEST_DATA = [
     expected: [
       {
         type: "mouseover",
         filename: TEST_URL + ":62",
         attributes: [
           "Capturing",
           "DOM2"
         ],
-        handler: 'function mouseoverHandler(event) {\n' +
-                 '  if (event.target.id !== "container") {\n' +
-                 '    let o