Merge m-c to b2g-inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 18 Jul 2014 15:54:49 -0400
changeset 216906 5e4b4dbfd7f4eefabf564430d1fd522c38bd0642
parent 216905 5e10e94a3c4c22399e2427156c4e07e30a696e4d (current diff)
parent 216855 50a8424cf1b8a579ba486eecc109459377a59ab3 (diff)
child 216907 74cef3a5bf045800a9b9cf41ef187bb0629a6e94
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound. a=merge
browser/base/content/test/social/browser_defaults.js
mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteAgentManager.java
mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteModel.java
mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteWordListAgent.java
mobile/android/search/res/raw/en_us.txt
toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
toolkit/components/places/tests/unifiedcomplete/test_416211.js
toolkit/components/places/tests/unifiedcomplete/test_416214.js
toolkit/components/places/tests/unifiedcomplete/test_417798.js
toolkit/components/places/tests/unifiedcomplete/test_418257.js
toolkit/components/places/tests/unifiedcomplete/test_422277.js
toolkit/components/places/tests/unifiedcomplete/test_autocomplete_functional.js
toolkit/components/places/tests/unifiedcomplete/test_autocomplete_on_value_removed_479089.js
toolkit/components/places/tests/unifiedcomplete/test_casing.js
toolkit/components/places/tests/unifiedcomplete/test_do_not_trim.js
toolkit/components/places/tests/unifiedcomplete/test_download_embed_bookmarks.js
toolkit/components/places/tests/unifiedcomplete/test_empty_search.js
toolkit/components/places/tests/unifiedcomplete/test_enabled.js
toolkit/components/places/tests/unifiedcomplete/test_escape_self.js
toolkit/components/places/tests/unifiedcomplete/test_ignore_protocol.js
toolkit/components/places/tests/unifiedcomplete/test_keyword_search.js
toolkit/components/places/tests/unifiedcomplete/test_keywords.js
toolkit/components/places/tests/unifiedcomplete/test_match_beginning.js
toolkit/components/places/tests/unifiedcomplete/test_multi_word_search.js
toolkit/components/places/tests/unifiedcomplete/test_queryurl.js
toolkit/components/places/tests/unifiedcomplete/test_special_search.js
toolkit/components/places/tests/unifiedcomplete/test_swap_protocol.js
toolkit/components/places/tests/unifiedcomplete/test_tabmatches.js
toolkit/components/places/tests/unifiedcomplete/test_trimming.js
toolkit/components/places/tests/unifiedcomplete/test_typed.js
toolkit/components/places/tests/unifiedcomplete/test_word_boundary_search.js
toolkit/components/places/tests/unifiedcomplete/test_zero_frecency.js
toolkit/components/places/tests/unifiedcomplete/xpcshell.ini
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -147,16 +147,17 @@ pref("browser.formfill.enable", true);
 /* spellcheck */
 pref("layout.spellcheckDefault", 0);
 
 /* block popups by default, and notify the user about blocked popups */
 pref("dom.disable_open_during_load", true);
 pref("privacy.popups.showBrowserMessage", true);
 
 pref("keyword.enabled", true);
+pref("browser.fixup.domainwhitelist.localhost", true);
 
 pref("accessibility.typeaheadfind", false);
 pref("accessibility.typeaheadfind.timeout", 5000);
 pref("accessibility.typeaheadfind.flashBar", 1);
 pref("accessibility.typeaheadfind.linksonly", false);
 pref("accessibility.typeaheadfind.casesensitive", 0);
 
 // SSL error page behaviour
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -254,16 +254,17 @@ pref("browser.uitour.themeOrigin", "http
 pref("browser.uitour.pinnedTabUrl", "https://support.mozilla.org/%LOCALE%/kb/pinned-tabs-keep-favorite-websites-open");
 pref("browser.uitour.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tour/");
 pref("browser.uitour.whitelist.add.260", "www.mozilla.org,support.mozilla.org");
 
 pref("browser.customizemode.tip0.shown", false);
 pref("browser.customizemode.tip0.learnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/customize");
 
 pref("keyword.enabled", true);
+pref("browser.fixup.domainwhitelist.localhost", true);
 
 pref("general.useragent.locale", "@AB_CD@");
 pref("general.skins.selectedSkin", "classic/1.0");
 
 pref("general.smoothScroll", true);
 #ifdef UNIX_BUT_NOT_MAC
 pref("general.autoScroll", false);
 #else
@@ -1520,23 +1521,22 @@ pref("loop.enabled", true);
 #else
 pref("loop.enabled", false);
 #endif
 
 pref("loop.server", "https://loop.services.mozilla.com");
 pref("loop.seenToS", "unseen");
 pref("loop.do_not_disturb", false);
 pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/Firefox-Long.ogg");
+pref("loop.retry_delay.start", 60000);
+pref("loop.retry_delay.limit", 300000);
 
 // serverURL to be assigned by services team
 pref("services.push.serverURL", "wss://push.services.mozilla.com/");
 
-// Default social providers
-pref("social.manifest.facebook", "{\"origin\":\"https://www.facebook.com\",\"name\":\"Facebook Share\",\"shareURL\":\"https://www.facebook.com/sharer/sharer.php?u=%{url}\",\"iconURL\":\"data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAX0lEQVQ4jWP4%2F%2F8%2FAyUYTFhHzjgDxP9JxGeQDSBVMxgTbUBCxer%2Fr999%2BQ8DJBuArJksA9A10s8AXIBoA0B%2BR%2FY%2FjD%2BEwoBoA1yT5v3PbdmCE8MAshhID%2FUMoDgzUYIBj0Cgi7ar4coAAAAASUVORK5CYII%3D\",\"icon32URL\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAADbklEQVRYCc1Xv08UQRj99tctexAuCEFjRE0kGBEtLDSGqIWNxkYKbTAxNlY2JhaGWltNtNFeKgsKKxITK43/gCYW+IsoRhA4D47bH7fn9+bcvdm5JR7sefolC3Ozu9978+bNN7PayUv3HN3umdY0Y6IWBtSJ0HSTarXqTOiuTep6Lj+tdxAcA8RAgSmwdd2aCDs0clldYALb/FvgYVhjmfliVA2XpjEgWo0Attn42Z6WH1RFor5ehwo9XQIUZMoVn4qlCoVMSo62EvD8Kh0b3U2Xz43R2PBO6mUCGDlAf65V6MadZzT/rUimoccc2kYA4BfPHqJb105RzjJigKhRq9kEJUBIjgYVuXeL7SAI6eD+Abp5dTwVHOmEHxT50d8WBYJqSOdPj5BjW8gZR8UNqFR2xagx/65XFYaMH+BGWwiYpi4UkBPPLxTp9v1Z+lHc4DWvCQXWmIy6EjITgKowVd5Jjv7N3Hd6y5esigoOwpkJIAmMpZpLJGdiaaC4F0UmAj6bD84GCEwmB/qxMmRilmnwb/mpjAocHh4UEoNAt5NLZB7oy9OJo0PxqkAtePdhiSqunyC1LQUwWMPQaOr6GRre258Ajn4cP7KHcEXhsxpXbj+lT19X2TMNGTLVAcjcalS8gDwsQ2UOMhH4k8FkcrEn5E5ub2sKohxLK2VR77Hl9RUcsrgeRIEiVOT6z+tDbIeLy+vk+kGTCbXxycet6xhl//3f6bJEkdHYhA+mLtDIvoH4ieev5+juoxdk5+pjhALYEdXIpEB5w+NlSKSzqVQ/+H7IO6BLtl3fngGMiqhGJgIwlM6qpyUGFjySdk8m0Zg0ubeD7X9OIDEFajltRQgUJaUKx69tdgaQa0FMADuahZPMFtcEwNPm2hA7ZI5sK4aoE2NvYI+o8hkCIe7CwTv68zS0q9Dk5vpbm/8FXxitSzmMFHpsGj0wyLUheTwD2Y9fVgh1Ae0EPUgD9241ZEnld+v5kgnVZ/8fE0brVh5BK+1oCqKKF72Dk7HwBsssB/pklU1dfChy3S659H5+uelgIb+8WRv1/uGTV9Sdb5wJFlfW6fPCalMhwhSU1j2xKwKbP838GcOwJja4TqO0bjdmXxYTy1EYjFdCWoCEYZhseH/GDL3yJPHnuW6YmT7P1SlIA4768Hke4vOcsX8BE346lLHhDUQAAAAASUVORK5CYII=\", \"icon64URL\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAACNNJREFUeNrtm3tw1NUVxz/399hHHkgCaCBGEFEEREVFYFQcSoOKdkZay4z+4dDpYIsjHWx1WoTMhFi1gzBSpVgVGbU4U1sHfPESKODwEEnRYDFAICEIeZIQshs2u/v73ds/drMsyW7YLEkl2Z6Z32yy+9v7u+fc7znne8+5KzgvAjDunzlv0M13PjDZ6c4cARj0WhEoaZ1tOn3yq9XLf/tNU0O1D5Ad7wq/OpxpaXOL1j5uZAwuaGlVgwNBhULRm0XXBG6HZrlNa9uRrzfM+3DlgjIgGMsA7rl/XDdHOnNf9vosTfVuvTsaQhdkZ4iykh2rHtqydvkxwI58BhjTfv7MmP55E9/1nLNdfU15ACkVvoAaMCRvRPa+re9+DgTaPjMAx+DrJv3M67Mz+6LybWLb4NfTHhxzx31DDhZvOtqGAgNwWbjGICV9XQJB0e/KobcOP1i8qTzaAEYgaDtNU/V5A9hSaUFLuQEt2gVQSgml+j4CUAIppYgK/m0GkCjZ9xGAUNAu0LUhgJRAAAIVzwBSqVRQH4hlAClRKZAFhOgEASoFECBR8QwgUyQGdJT/B8HzCEiBNKhUJzEgBYIgQsTJAkohe9oFZHgHKvQoHtZ9K3tewfiixXABLdoFeuSSEmkF+PH4QTz7+M3o+ENptzvGtS36uSwmjMpAYF10XCllHCYoe84FlLS555Zs5jx6J6ahY+iCl98pJiDNS1hwSZop+cm91zJmxEBefGsPlu1AxKC67V3gf5oGlZSMuz6Dp2fdhWnoAEwaN5T5hsYLb+4hKB1dcgelFDpB8ifk8thDt3DO5+fZxRvxBV0IjQR0EB3KfD1GhJS0GZnnYuGcKTgdF9ZWx4/No/BJjUUrdtJqm4iL+K5SCmSAiWMHMevhcQzNzaa6ron5SzfQ7HeiaSKx+au4m6HupcJKSYZdZVI4dypuV2yo3zoql0VP3cOiFV/Q4jdiGkGhQFqMGpbJL346kbE3DEYIQWNTC39Ysp4Gr4HQtZDyiRhA0NlmSHZbRM7pr1H0m6lckeHqdGXG3jCYoqfupeC17bT49fNRXIFSNrkDTGbNGM9dtw1D10M1DI/Xx3NLP6OqETRdDy1eglPT4rqA7K56gCIrXfHCvHwGZqUnBMtR113FS/N+xHPLtuJpDa1mVobg0emjmX7vqEjsUErhaw1Q8Mo6yk4F0A1HeOW7kIlFx/u7jworRabLpmhuPjmD+iG7YNDrrhnIS09P5cW/buOeO67lkftvJt3tDE06PE7Qsnh++QYOHPOim86wcVUS0+whJug0ghTMmcK1V2eH8m2UHP++nrwhAyIwjiXDcrN5vXAGhqGhFBeMYUvJ0re2sPfgGTTDGUZrEogVopMgmGQWUCgMEWTBE5MZPSLngnGqas/w9j92s31fJfmThvPM7HyMMKRj+qgmOiBHSsnr73/B5r1V6A53KD4k3bFS8dNgckFQoWPxu1kTGDc6N7JqzR4ff/+smE+2H8FSLkx3FluLawhaG3n2iXwcppGoV/Hemt18tK0c3UwLIfUS2nVafBeQJNUXkAF+/dht3H37cKSUBC2bTTv+w98++YazPg1dT0NoIUhruoMd+2sJrtjI7381rQM3iCVrN33N++tL0c30xFNdp0GQeFRYQhcRIO0gv5xxE/fdfSO2bfNVSTmrPtxLZW0A3XSh6VporaLG1XQHu0pOU/TaOhY8+QAuZ3w6vHnnQd74536EkZ50wOsSE0zcugolbWZOG8GM/LGUVdSw6sM97D/UgGa60QxXzMJDZAq6yb7SJgr//CkLn5pOmsvR4Z5dxUdZ9t6XoKfFjNyXkqZjuoBUCpGgCyhp8eDdQ5k++UaWrdrMlr2VSFxoZlpE8YtNWGgGXx9ppnDZpxTMfTCS8gAOlJ5g8ds7kCItTIi6j6FqMVxAC2sV2RB1ekmLCTcNpH+myeyFH7BxT1Voopoe4RKJXQqhmxw45mXh0o/xeH0opThcXs2iv2wmoFyhAnbC4yX+3PgISMDShrA5XHGa3d9UITRniIeTfHASmsGhEz7mL/mI2TPv4sU3t+KzHAnu7JKpCosOkcSIICAB5hZE0OiRCM0Iwb0b6LPQdI5W+Zn/yucoYYayRk+16eK1xqRMDAHtA0r3lep0lNAjO8kfpCpMqpTF4xZEUqA7rIlOCiKpgADVWXc4FQwgEfHPB5AiByTixIDUCYJx+wJoqdIcJV5VOAWygEZcF7BToT2upFDKtuz2BrAtf8v3mju972cBJX2exso6ok6N64BhOtM11xXXPBz6v6340PcuO+DZfaJkzWqgqY3L64Bqaaz0ZV45Mkc308dG2kd97FLSaq4v317gazr5HeCLRoACFTxbfeBw+oDhWYYj4/rw+30H+rb/VMPxXQsbKnbuABqJOi4vogyRiRB5/XNvvz3zytFTDEf61eF9b0dCKTS36c4afymTsgLeQ9Ly13X/aYnzE1Uy6PV7679trNy1xe+tKwPqAH/0Vla0qw65gH7AFeG/Y3Uy9P45o0bm3PTIaplM6lTK9jWf/OBUyQcrpdXaTIyfsXQb9QcLaAn7vJd2vxY5XxBpo8pwDmgFGsLKx1oeh8OVmUUSLXUlrWZPzbdLag9v+BjUqfDzepKAyDDcZbznGHG+1NmqSKHpVlfbadJqLW+o2LHobNX+PUB1WPkfnHwYyTmX6lI7Lehr3F576NM/+T3V3wH17f2w1xkg2ggXuSvga6p8p+bgmpVKWpXAmVh+2AsNEKogdYYAJa0GT03J4obyf60HTgKe6PTTqw0QOpcQ3wXs4LlDZyq2FXrrS4uBmjDxuCw3G5eIgA46yeC5ho11pWsWW35PWTibBC4Xf+9eBLRPg0q2+s5UvHG6bMNqJYPHw7nXutxZYvIIiMoCSgbrPVX/fv7syS+3AKfC5MOmF4iRpP6RjrId8O5vrNhS1NpUWQLUholUr6muXEoatP3emrWNR9e/avk9R8P+HuxNypPkrk93pGdnK0VtXemaN6UdOHo55vdE5b/0NKx+K4AxtAAAAABJRU5ErkJggg==\", \"description\":\"Easily share the web to your Facebook friends.\",\"author\":\"Facebook\",\"homepageURL\":\"https://www.facebook.com\",\"builtin\":\"true\",\"version\":1}");
-
 pref("social.sidebar.unload_timeout_ms", 10000);
 
 pref("dom.identity.enabled", false);
 
 // Block insecure active content on https pages
 pref("security.mixed_content.block_active_content", true);
 
 // 1 = allow MITM for certificate pinning checks.
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -271,16 +271,24 @@ var gPluginHandler = {
     if (eventType == "PluginRemoved") {
       let doc = event.target;
       let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
       if (browser)
         this._setPluginNotificationIcon(browser);
       return;
     }
 
+    if (eventType == "PluginCrashed" &&
+        !(event.target instanceof Ci.nsIObjectLoadingContent)) {
+      // If the event target is not a plugin object (i.e., an <object> or
+      // <embed> element), this call is for a window-global plugin.
+      this.pluginInstanceCrashed(event.target, event);
+      return;
+    }
+
     let plugin = event.target;
     let doc = plugin.ownerDocument;
 
     if (!(plugin instanceof Ci.nsIObjectLoadingContent))
       return;
 
     if (eventType == "PluginBindingAttached") {
       // The plugin binding fires this event when it is created.
@@ -1138,17 +1146,17 @@ var gPluginHandler = {
       // Submission is async, so we can't easily show failure UI.
       propertyBag.setPropertyAsBool("submittedCrashReport", true);
     }
 #endif
   },
 
   // Crashed-plugin event listener. Called for every instance of a
   // plugin in content.
-  pluginInstanceCrashed: function (plugin, aEvent) {
+  pluginInstanceCrashed: function (target, aEvent) {
     // Ensure the plugin and event are of the right type.
     if (!(aEvent instanceof Ci.nsIDOMCustomEvent))
       return;
 
     let propBag = aEvent.detail.QueryInterface(Ci.nsIPropertyBag2);
     let submittedReport = propBag.getPropertyAsBool("submittedCrashReport");
     let doPrompt        = true; // XXX followup for .getPropertyAsBool("doPrompt");
     let submitReports   = true; // XXX followup for .getPropertyAsBool("submitReports");
@@ -1156,60 +1164,48 @@ var gPluginHandler = {
     let pluginDumpID    = propBag.getPropertyAsAString("pluginDumpID");
     let browserDumpID   = propBag.getPropertyAsAString("browserDumpID");
 
     // Remap the plugin name to a more user-presentable form.
     pluginName = this.makeNicePluginName(pluginName);
 
     let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
 
-    //
-    // Configure the crashed-plugin placeholder.
-    //
+    let plugin = null, doc;
+    if (target instanceof Ci.nsIObjectLoadingContent) {
+      plugin = target;
+      doc = plugin.ownerDocument;
+    } else {
+      doc = target.document;
+      if (!doc) {
+        return;
+      }
+      // doPrompt is specific to the crashed plugin overlay, and
+      // therefore is not applicable for window-global plugins.
+      doPrompt = false;
+    }
 
-    // Force a layout flush so the binding is attached.
-    plugin.clientTop;
-    let overlay = this.getPluginUI(plugin, "main");
-    let statusDiv = this.getPluginUI(plugin, "submitStatus");
-    let doc = plugin.ownerDocument;
+    let status;
 #ifdef MOZ_CRASHREPORTER
-    let status;
-
     // Determine which message to show regarding crash reports.
     if (submittedReport) { // submitReports && !doPrompt, handled in observer
       status = "submitted";
     }
     else if (!submitReports && !doPrompt) {
       status = "noSubmit";
     }
-    else { // doPrompt
+    else if (!pluginDumpID) {
+      // If we don't have a minidumpID, we can't (or didn't) submit anything.
+      // This can happen if the plugin is killed from the task manager.
+      status = "noReport";
+    }
+    else {
       status = "please";
-      this.getPluginUI(plugin, "submitButton").addEventListener("click",
-        function (event) {
-          if (event.button != 0 || !event.isTrusted)
-            return;
-          this.submitReport(pluginDumpID, browserDumpID, plugin);
-          pref.setBoolPref("", optInCB.checked);
-        }.bind(this));
-      let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
-      let pref = Services.prefs.getBranch("dom.ipc.plugins.reportCrashURL");
-      optInCB.checked = pref.getBoolPref("");
     }
 
-    // If we don't have a minidumpID, we can't (or didn't) submit anything.
-    // This can happen if the plugin is killed from the task manager.
-    if (!pluginDumpID) {
-        status = "noReport";
-    }
-
-    statusDiv.setAttribute("status", status);
-
-    let helpIcon = this.getPluginUI(plugin, "helpIcon");
-    this.addLinkClickCallback(helpIcon, "openHelpPage");
-
     // If we're showing the link to manually trigger report submission, we'll
     // want to be able to update all the instances of the UI for this crash to
     // show an updated message when a report is submitted.
     if (doPrompt) {
       let observer = {
         QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                                Ci.nsISupportsWeakReference]),
         observe : function(subject, topic, data) {
@@ -1233,36 +1229,25 @@ var gPluginHandler = {
       // it from being GC. But I don't want to manually manage the reference's
       // lifetime (which should be no greater than the page).
       // Clever solution? Use a closue with an event listener on the document.
       // When the doc goes away, so do the listener references and the closure.
       doc.addEventListener("mozCleverClosureHack", observer, false);
     }
 #endif
 
-    let crashText = this.getPluginUI(plugin, "crashedText");
-    crashText.textContent = messageString;
-
     let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
-
-    let link = this.getPluginUI(plugin, "reloadLink");
-    this.addLinkClickCallback(link, "reloadPage", browser);
-
     let notificationBox = gBrowser.getNotificationBox(browser);
+    let isShowing = false;
 
-    let isShowing = this.shouldShowOverlay(plugin, overlay);
-
-    // Is the <object>'s size too small to hold what we want to show?
-    if (!isShowing) {
-      // First try hiding the crash report submission UI.
-      statusDiv.removeAttribute("status");
-
-      isShowing = this.shouldShowOverlay(plugin, overlay);
+    if (plugin) {
+      // If there's no plugin (an <object> or <embed> element), this call is
+      // for a window-global plugin. In this case, there's no overlay to show.
+      isShowing = _setUpPluginOverlay.call(this, plugin, doPrompt, browser);
     }
-    this.setVisibility(plugin, overlay, isShowing);
 
     if (isShowing) {
       // If a previous plugin on the page was too small and resulted in adding a
       // notification bar, then remove it because this plugin instance it big
       // enough to serve as in-content notification.
       hideNotificationBar();
       doc.mozNoPluginCrashedNotification = true;
     } else {
@@ -1325,10 +1310,59 @@ var gPluginHandler = {
       description.appendChild(link);
 
       // Remove the notfication when the page is reloaded.
       doc.defaultView.top.addEventListener("unload", function() {
         notificationBox.removeNotification(notification);
       }, false);
     }
 
+    // Configure the crashed-plugin placeholder.
+    // Returns true if the plugin overlay is visible.
+    function _setUpPluginOverlay(plugin, doPromptSubmit, browser) {
+      if (!plugin) {
+        return false;
+      }
+
+      // Force a layout flush so the binding is attached.
+      plugin.clientTop;
+      let overlay = this.getPluginUI(plugin, "main");
+      let statusDiv = this.getPluginUI(plugin, "submitStatus");
+
+      if (doPromptSubmit) {
+        this.getPluginUI(plugin, "submitButton").addEventListener("click",
+        function (event) {
+          if (event.button != 0 || !event.isTrusted)
+            return;
+          this.submitReport(pluginDumpID, browserDumpID, plugin);
+          pref.setBoolPref("", optInCB.checked);
+        }.bind(this));
+        let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
+        let pref = Services.prefs.getBranch("dom.ipc.plugins.reportCrashURL");
+        optInCB.checked = pref.getBoolPref("");
+      }
+
+      statusDiv.setAttribute("status", status);
+
+      let helpIcon = this.getPluginUI(plugin, "helpIcon");
+      this.addLinkClickCallback(helpIcon, "openHelpPage");
+
+      let crashText = this.getPluginUI(plugin, "crashedText");
+      crashText.textContent = messageString;
+
+      let link = this.getPluginUI(plugin, "reloadLink");
+      this.addLinkClickCallback(link, "reloadPage", browser);
+
+      let isShowing = this.shouldShowOverlay(plugin, overlay);
+
+      // Is the <object>'s size too small to hold what we want to show?
+      if (!isShowing) {
+        // First try hiding the crash report submission UI.
+        statusDiv.removeAttribute("status");
+
+        isShowing = this.shouldShowOverlay(plugin, overlay);
+      }
+      this.setVisibility(plugin, overlay, isShowing);
+
+      return isShowing;
+    }
   }
 };
--- a/browser/base/content/browser-webrtcUI.js
+++ b/browser/base/content/browser-webrtcUI.js
@@ -54,12 +54,17 @@ let WebrtcIndicator = {
 
     let browserWindow = streamData.browser.ownerDocument.defaultView;
     if (streamData.tab) {
       browserWindow.gBrowser.selectedTab = streamData.tab;
     } else {
       streamData.browser.focus();
     }
     browserWindow.focus();
-    PopupNotifications.getNotification("webRTC-sharingDevices",
-                                       streamData.browser).reshow();
+    let notif = PopupNotifications.getNotification("webRTC-sharingDevices",
+                                                   streamData.browser);
+    if (!notif) {
+      notif = PopupNotifications.getNotification("webRTC-sharingScreen",
+                                                 streamData.browser);
+    }
+    notif.reshow();
   }
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -17,16 +17,20 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/BrowserUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
                                   "resource://gre/modules/CharsetMenu.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
                                   "resource://gre/modules/ShortcutUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
+                                   "@mozilla.org/network/dns-service;1",
+                                   "nsIDNSService");
+
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gLastBrowserCharset = null;
 var gPrevCharset = null;
 var gProxyFavIcon = null;
 var gLastValidURLStr = "";
 var gInPrintPreviewMode = false;
 var gContextMenu = null; // nsContextMenu instance
@@ -740,16 +744,111 @@ const gFormSubmitObserver = {
 
       position = "after_start";
     }
 
     this.panel.openPopup(element, position, offset, 0);
   }
 };
 
+function gKeywordURIFixup(fixupInfo, topic, data) {
+  fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
+
+  // We get called irrespective of whether we did a keyword search, or
+  // whether the original input would be vaguely interpretable as a URL,
+  // so figure that out first.
+  let alternativeURI = fixupInfo.fixedURI;
+  if (!fixupInfo.fixupUsedKeyword || !alternativeURI) {
+    return;
+  }
+
+  // We should have a document loader...
+  let docshellRef = fixupInfo.consumer;
+  try {
+    docshellRef.QueryInterface(Ci.nsIDocumentLoader);
+  } catch (ex) {
+    return;
+  }
+
+  // ... from which we can deduce the browser
+  let browser = gBrowser.getBrowserForDocument(docshellRef.document);
+  if (!browser)
+    return;
+
+  // At this point we're still only just about to load this URI.
+  // When the async DNS lookup comes back, we may be in any of these states:
+  // 1) still on the previous URI, waiting for the preferredURI (keyword
+  //    search) to respond;
+  // 2) at the keyword search URI (preferredURI)
+  // 3) at some other page because the user stopped navigation.
+  // We keep track of the currentURI to detect case (1) in the DNS lookup
+  // callback.
+  let previousURI = browser.currentURI;
+
+  // now swap for a weak ref so we don't hang on to browser needlessly
+  // even if the DNS query takes forever
+  let weakBrowser = Cu.getWeakReference(browser);
+  browser = null;
+
+  // Additionally, we need the host of the parsed url
+  let hostName = alternativeURI.host;
+  // and the ascii-only host for the pref:
+  let asciiHost = alternativeURI.asciiHost;
+
+  let onLookupComplete = (request, record, status) => {
+    let browser = weakBrowser.get();
+    if (!Components.isSuccessCode(status) || !browser)
+      return;
+
+    let currentURI = browser.currentURI;
+    // If we're in case (3) (see above), don't show an info bar.
+    if (!currentURI.equals(previousURI) &&
+        !currentURI.equals(fixupInfo.preferredURI)) {
+      return;
+    }
+
+    // show infobar offering to visit the host
+    let notificationBox = gBrowser.getNotificationBox(browser);
+    if (notificationBox.getNotificationWithValue("keyword-uri-fixup"))
+      return;
+
+    let message = gNavigatorBundle.getFormattedString(
+      "keywordURIFixup.message", [hostName]);
+    let yesMessage = gNavigatorBundle.getFormattedString(
+      "keywordURIFixup.goTo", [hostName])
+
+    let buttons = [
+      {
+        label: yesMessage,
+        accessKey: gNavigatorBundle.getString("keywordURIFixup.goTo.accesskey"),
+        callback: function() {
+          let pref = "browser.fixup.domainwhitelist." + asciiHost;
+          Services.prefs.setBoolPref(pref, true);
+          openUILinkIn(alternativeURI.spec, "current");
+        }
+      },
+      {
+        label: gNavigatorBundle.getString("keywordURIFixup.dismiss"),
+        accessKey: gNavigatorBundle.getString("keywordURIFixup.dismiss.accesskey"),
+        callback: function() {
+          let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup");
+          notificationBox.removeNotification(notification, true);
+        }
+      }
+    ];
+    let notification =
+      notificationBox.appendNotification(message,"keyword-uri-fixup", null,
+                                         notificationBox.PRIORITY_INFO_HIGH,
+                                         buttons);
+    notification.persistence = 1;
+  };
+
+  gDNSService.asyncResolve(hostName, 0, onLookupComplete, Services.tm.mainThread);
+}
+
 var gBrowserInit = {
   delayedStartupFinished: false,
 
   onLoad: function() {
     gMultiProcessBrowser =
       window.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIWebNavigation)
       .QueryInterface(Ci.nsILoadContext)
@@ -1046,16 +1145,17 @@ var gBrowserInit = {
 
     Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
     Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
+    Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup", false);
 
     BrowserOffline.init();
     OfflineApps.init();
     IndexedDBPromptHelper.init();
     gFormSubmitObserver.init();
     gRemoteTabsUI.init();
 
     // Initialize the full zoom setting.
@@ -1349,16 +1449,17 @@ var gBrowserInit = {
 
       Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
       Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
+      Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
 
       try {
         gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
       } catch (ex) {
         Cu.reportError(ex);
       }
 
       if (typeof WindowsPrefSync !== 'undefined') {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -724,16 +724,18 @@
                 <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="plugin-install-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="mixed-content-blocked-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon" role="button"/>
+                <image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon" role="button"/>
+                <image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="servicesInstall-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="translate-notification-icon" class="notification-anchor-icon" role="button"/>
                 <image id="translated-notification-icon" class="notification-anchor-icon" role="button"/>
               </box>
               <!-- Use onclick instead of normal popup= syntax since the popup
                    code fires onmousedown, and hence eats our favicon drag events.
                    We only add the identity-box button to the tab order when the location bar
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -13,16 +13,29 @@
         <separator class="thin"/>
         <label value="&getUserMedia.selectCamera.label;"
                accesskey="&getUserMedia.selectCamera.accesskey;"
                control="webRTC-selectCamera-menulist"/>
         <menulist id="webRTC-selectCamera-menulist">
           <menupopup id="webRTC-selectCamera-menupopup"/>
         </menulist>
       </popupnotificationcontent>
+
+      <popupnotificationcontent id="webRTC-selectWindowOrScreen" orient="vertical">
+        <separator class="thin"/>
+        <label value="&getUserMedia.selectWindowOrScreen.label;"
+               accesskey="&getUserMedia.selectWindowOrScreen.accesskey;"
+               control="webRTC-selectWindow-menulist"/>
+        <menulist id="webRTC-selectWindow-menulist"
+                  oncommand="WebrtcIndicator.UIModule.updateMainActionLabel(this);">
+          <menupopup id="webRTC-selectWindow-menupopup"/>
+        </menulist>
+        <description id="webRTC-all-windows-shared" hidden="true">&getUserMedia.allWindowsShared.message;</description>
+      </popupnotificationcontent>
+
       <popupnotificationcontent id="webRTC-selectMicrophone" orient="vertical">
         <separator class="thin"/>
         <label value="&getUserMedia.selectMicrophone.label;"
                accesskey="&getUserMedia.selectMicrophone.accesskey;"
                control="webRTC-selectMicrophone-menulist"/>
         <menulist id="webRTC-selectMicrophone-menulist">
           <menupopup id="webRTC-selectMicrophone-menupopup"/>
         </menulist>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -396,16 +396,17 @@ skip-if = e10s # Bug ?????? - test uses 
 [browser_urlHighlight.js]
 [browser_urlbarAutoFillTrimURLs.js]
 [browser_urlbarCopying.js]
 skip-if = e10s # Bug 932651 - getClipboardData in specialpowersAPI.js not e10s friendly
 [browser_urlbarEnter.js]
 skip-if = e10s # Bug ?????? - obscure non-windows child process crashes on try
 [browser_urlbarRevert.js]
 skip-if = e10s # Bug ?????? - ESC reverted the location bar value - Got foobar, expected example.com
+[browser_urlbarSearchSingleWordNotification.js]
 [browser_urlbarStop.js]
 skip-if = e10s # Bug ????? - test calls gBrowser.contentWindow.stop
 [browser_urlbarTrimURLs.js]
 [browser_urlbar_search_healthreport.js]
 skip-if = e10s # Bug ?????? - FHR tests failing (either with "no data for today" or "2 records for today")
 [browser_utilityOverlay.js]
 [browser_visibleFindSelection.js]
 skip-if = e10s # Bug ?????? - test directly manipulates content
--- a/browser/base/content/test/general/browser_tabopen_reflows.js
+++ b/browser/base/content/test/general/browser_tabopen_reflows.js
@@ -102,18 +102,18 @@ function test() {
       // Remove reflow observer and clean up.
       docShell.removeWeakReflowObserver(observer);
       gBrowser.removeCurrentTab();
       finish();
     });
   });
 
   Services.prefs.setBoolPref(PREF_PRELOAD, false);
-  // set directory source to empty links
-  Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
+  // set directory source to dummy/empty links
+  Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, 'data:application/json,{"test":1}');
 }
 
 let observer = {
   reflow: function (start, end) {
     // Gather information about the current code path.
     let path = (new Error().stack).split("\n").slice(1).map(line => {
       return line.replace(/:\d+:\d+$/, "");
     }).join("|");
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_urlbarSearchSingleWordNotification.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+registerCleanupFunction(function() {
+  Services.prefs.clearUserPref("browser.fixup.domainwhitelist.localhost");
+});
+
+function promiseNotificationForTab(value, expected, tab=gBrowser.selectedTab) {
+  let deferred = Promise.defer();
+  let notificationBox = gBrowser.getNotificationBox(tab.linkedBrowser);
+  if (expected) {
+    waitForCondition(() => notificationBox.getNotificationWithValue(value) !== null,
+                     deferred.resolve, "Were expecting to get a notification");
+  } else {
+    setTimeout(() => {
+      is(notificationBox.getNotificationWithValue(value), null, "We are expecting to not get a notification");
+      deferred.resolve();
+    }, 1000);
+  }
+  return deferred.promise;
+}
+
+function* runURLBarSearchTest(valueToOpen, expectSearch, expectNotification) {
+  gURLBar.value = valueToOpen;
+  let expectedURI;
+  if (!expectSearch) {
+    expectedURI = "http://" + valueToOpen + "/";
+  } else {
+    yield new Promise(resolve => {
+      Services.search.init(resolve)
+    });
+    expectedURI = Services.search.defaultEngine.getSubmission(valueToOpen, null, "keyword").uri.spec;
+  }
+  gURLBar.focus();
+  let docLoadPromise = waitForDocLoadAndStopIt(expectedURI);
+  EventUtils.synthesizeKey("VK_RETURN", {});
+
+  yield docLoadPromise;
+
+  yield promiseNotificationForTab("keyword-uri-fixup", expectNotification);
+}
+
+add_task(function* test_navigate_full_domain() {
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+  yield* runURLBarSearchTest("www.mozilla.org", false, false);
+  gBrowser.removeTab(tab);
+});
+
+add_task(function* test_navigate_single_host() {
+  Services.prefs.setBoolPref("browser.fixup.domainwhitelist.localhost", false);
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+  yield* runURLBarSearchTest("localhost", true, true);
+
+  let notificationBox = gBrowser.getNotificationBox(tab.linkedBrowser);
+  let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup");
+  let docLoadPromise = waitForDocLoadAndStopIt("http://localhost/");
+  notification.querySelector(".notification-button-default").click();
+
+  // check pref value
+  let pref = "browser.fixup.domainwhitelist.localhost";
+  let prefValue = Services.prefs.getBoolPref(pref);
+  ok(prefValue, "Pref should have been toggled");
+
+  yield docLoadPromise;
+  gBrowser.removeTab(tab);
+
+  // Now try again with the pref set.
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+  yield* runURLBarSearchTest("localhost", false, false);
+  gBrowser.removeTab(tab);
+});
+
+add_task(function* test_navigate_invalid_url() {
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+  yield* runURLBarSearchTest("mozilla is awesome", true, false);
+  gBrowser.removeTab(tab);
+});
+
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -19,18 +19,18 @@ let {Promise, NewTabUtils, Sanitizer, cl
 let uri = Services.io.newURI("about:newtab", null, null);
 let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
 
 let isMac = ("nsILocalFileMac" in Ci);
 let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc);
 let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 let gWindow = window;
 
-// Default to empty directory links
-let gDirectorySource = "data:application/json,{}";
+// Default to dummy/empty directory links
+let gDirectorySource = 'data:application/json,{"test":1}';
 
 // The tests assume all three rows of sites are shown, but the window may be too
 // short to actually show three rows.  Resize it if necessary.
 let requiredInnerHeight =
   40 + 32 + // undo container + bottom margin
   44 + 32 + // search bar + bottom margin
   (3 * (150 + 32)) + // 3 rows * (tile height + title and bottom margin)
   100; // breathing room
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -57,15 +57,16 @@ run-if = crashreporter
 [browser_CTP_drag_drop.js]
 [browser_CTP_hide_overlay.js]
 [browser_CTP_iframe.js]
 [browser_CTP_multi_allow.js]
 [browser_CTP_nonplugins.js]
 [browser_CTP_notificationBar.js]
 [browser_CTP_outsideScrollArea.js]
 [browser_CTP_resize.js]
+[browser_globalplugin_crashinfobar.js]
 [browser_pageInfo_plugins.js]
 [browser_pluginnotification.js]
 [browser_pluginplaypreview.js]
 [browser_pluginplaypreview2.js]
 [browser_pluginCrashCommentAndURL.js]
 run-if = crashreporter
 [browser_plugins_added_dynamically.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js
@@ -0,0 +1,69 @@
+/* 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/. */
+
+let gTestBrowser = null;
+
+let propBagProperties = {
+  pluginName: "GlobalTestPlugin",
+  pluginDumpID: "1234",
+  browserDumpID: "5678",
+  submittedCrashReport: false
+}
+
+// Test that plugin crash submissions still work properly after
+// click-to-play activation.
+
+function test() {
+  waitForExplicitFinish();
+  let tab = gBrowser.loadOneTab("about:blank", { inBackground: false });
+  gTestBrowser = gBrowser.getBrowserForTab(tab);
+  gTestBrowser.addEventListener("PluginCrashed", onCrash, false);
+  gTestBrowser.addEventListener("load", onPageLoad, true);
+
+  registerCleanupFunction(function cleanUp() {
+    gTestBrowser.removeEventListener("PluginCrashed", onCrash, false);
+    gTestBrowser.removeEventListener("load", onPageLoad, true);
+    gBrowser.removeTab(tab);
+  });
+}
+
+function onPageLoad() {
+  executeSoon(generateCrashEvent);
+}
+
+function generateCrashEvent() {
+  let window = gTestBrowser.contentWindow;
+  let propBag = Cc["@mozilla.org/hash-property-bag;1"]
+                  .createInstance(Ci.nsIWritablePropertyBag);
+  for (let [name, val] of Iterator(propBagProperties)) {
+    propBag.setProperty(name, val);
+  }
+
+  let event = window.document.createEvent("CustomEvent");
+  event.initCustomEvent("PluginCrashed", true, true, propBag);
+  window.dispatchEvent(event);
+}
+
+
+function onCrash(event) {
+  let target = event.target;
+  is (target, gTestBrowser.contentWindow, "Event target is the window.");
+
+  let propBag = event.detail.QueryInterface(Ci.nsIPropertyBag2);
+  for (let [name, val] of Iterator(propBagProperties)) {
+    let type = typeof val;
+    let propVal = type == "string"
+                  ? propBag.getPropertyAsAString(name)
+                  : propBag.getPropertyAsBool(name);
+    is (propVal, val, "Correct property in detail propBag: " + name + ".");
+  }
+
+  let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
+  let notification = notificationBox.getNotificationWithValue("plugin-crashed");
+
+  ok(notification, "Infobar was shown.");
+  is(notification.priority, notificationBox.PRIORITY_WARNING_MEDIUM, "Correct priority.");
+  is(notification.getAttribute("label"), "The GlobalTestPlugin plugin has crashed.", "Correct message.");
+  finish();
+}
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -23,17 +23,16 @@ support-files =
   social_sidebar_empty.html
   social_window.html
   social_worker.js
   unchecked.jpg
 
 [browser_aboutHome_activation.js]
 [browser_addons.js]
 [browser_blocklist.js]
-[browser_defaults.js]
 [browser_share.js]
 [browser_social_activation.js]
 [browser_social_chatwindow.js]
 [browser_social_chatwindow_resize.js]
 [browser_social_chatwindowfocus.js]
 [browser_social_errorPage.js]
 [browser_social_flyout.js]
 [browser_social_isVisible.js]
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -3,17 +3,17 @@
 let AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
 
 const ADDON_TYPE_SERVICE     = "service";
 const ID_SUFFIX              = "@services.mozilla.org";
 const STRING_TYPE_NAME       = "type.%ID%.name";
 const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
 
-let manifest = { // builtin provider
+let manifest = {
   name: "provider 1",
   origin: "https://example.com",
   sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
   workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
 };
 let manifest2 = { // used for testing install
   name: "provider 2",
@@ -23,29 +23,25 @@ let manifest2 = { // used for testing in
   iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png",
   version: 1
 };
 
 function test() {
   waitForExplicitFinish();
 
   let prefname = getManifestPrefname(manifest);
-  setBuiltinManifestPref(prefname, manifest);
   // ensure that manifest2 is NOT showing as builtin
-  is(SocialService.getOriginActivationType(manifest.origin), "builtin", "manifest is builtin");
-  is(SocialService.getOriginActivationType(manifest2.origin), "foreign", "manifest2 is not builtin");
+  is(SocialService.getOriginActivationType(manifest.origin), "foreign", "manifest is foreign");
+  is(SocialService.getOriginActivationType(manifest2.origin), "foreign", "manifest2 is foreign");
 
   Services.prefs.setBoolPref("social.remote-install.enabled", true);
   runSocialTests(tests, undefined, undefined, function () {
     Services.prefs.clearUserPref("social.remote-install.enabled");
-    // clear our builtin pref
     ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
-    resetBuiltinManifestPref(prefname);
     // just in case the tests failed, clear these here as well
-    Services.prefs.clearUserPref("social.whitelist");
     Services.prefs.clearUserPref("social.directories");
     finish();
   });
 }
 
 function installListener(next, aManifest) {
   let expectEvent = "onInstalling";
   let prefname = getManifestPrefname(aManifest);
@@ -164,19 +160,19 @@ var tests = {
         is(addon.manifest.origin, manifest.origin, "provider about to be disabled");
         expectEvent = "onDisabled";
       }
     };
     AddonManager.addAddonListener(listener);
 
     expectEvent = "onEnabling";
     setManifestPref(prefname, manifest);
-    SocialService.addBuiltinProvider(manifest.origin, function(provider) {
+    SocialService.enableProvider(manifest.origin, function(provider) {
       expectEvent = "onDisabling";
-      SocialService.removeProvider(provider.origin, function() {
+      SocialService.disableProvider(provider.origin, function() {
         AddonManager.removeAddonListener(listener);
         Services.prefs.clearUserPref(prefname);
         next();
       });
     });
   },
   testForeignInstall: function(next) {
     AddonManager.addAddonListener(installListener(next, manifest2));
@@ -194,93 +190,17 @@ var tests = {
     let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       let installFrom = doc.nodePrincipal.origin;
       Services.prefs.setCharPref("social.whitelist", "");
       is(SocialService.getOriginActivationType(installFrom), "foreign", "testing foriegn install");
       Social.installProvider(doc, manifest2, function(addonManifest) {
         Services.prefs.clearUserPref("social.whitelist");
-        SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
-          Social.uninstallProvider(addonManifest.origin);
-          gBrowser.removeTab(tab);
-        });
-      });
-    });
-  },
-  testBuiltinInstallWithoutManifest: function(next) {
-    // send installProvider null for the manifest
-    AddonManager.addAddonListener(installListener(next, manifest));
-    let panel = document.getElementById("servicesInstall-notification");
-    PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
-      PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
-      info("servicesInstall-notification panel opened");
-      panel.button.click();
-    });
-
-    let prefname = getManifestPrefname(manifest);
-    let activationURL = manifest.origin + "/browser/browser/base/content/test/social/social_activate.html"
-    addTab(activationURL, function(tab) {
-      let doc = tab.linkedBrowser.contentDocument;
-      let installFrom = doc.nodePrincipal.origin;
-      is(SocialService.getOriginActivationType(installFrom), "builtin", "testing builtin install");
-      ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
-      Social.installProvider(doc, null, function(addonManifest) {
-        ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
-        SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
-          Social.uninstallProvider(addonManifest.origin);
-          gBrowser.removeTab(tab);
-        });
-      });
-    });
-  },
-  testBuiltinInstall: function(next) {
-    // send installProvider a json object for the manifest
-    AddonManager.addAddonListener(installListener(next, manifest));
-    let panel = document.getElementById("servicesInstall-notification");
-    PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
-      PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
-      info("servicesInstall-notification panel opened");
-      panel.button.click();
-    });
-
-    let prefname = getManifestPrefname(manifest);
-    let activationURL = manifest.origin + "/browser/browser/base/content/test/social/social_activate.html"
-    addTab(activationURL, function(tab) {
-      let doc = tab.linkedBrowser.contentDocument;
-      let installFrom = doc.nodePrincipal.origin;
-      is(SocialService.getOriginActivationType(installFrom), "builtin", "testing builtin install");
-      ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
-      Social.installProvider(doc, manifest, function(addonManifest) {
-        ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
-        SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
-          Social.uninstallProvider(addonManifest.origin);
-          gBrowser.removeTab(tab);
-        });
-      });
-    });
-  },
-  testWhitelistInstall: function(next) {
-    AddonManager.addAddonListener(installListener(next, manifest2));
-    let panel = document.getElementById("servicesInstall-notification");
-    PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
-      PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
-      info("servicesInstall-notification panel opened");
-      panel.button.click();
-    });
-
-    let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
-    addTab(activationURL, function(tab) {
-      let doc = tab.linkedBrowser.contentDocument;
-      let installFrom = doc.nodePrincipal.origin;
-      Services.prefs.setCharPref("social.whitelist", installFrom);
-      is(SocialService.getOriginActivationType(installFrom), "whitelist", "testing whitelist install");
-      Social.installProvider(doc, manifest2, function(addonManifest) {
-        Services.prefs.clearUserPref("social.whitelist");
-        SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
+        SocialService.enableProvider(addonManifest.origin, function(provider) {
           Social.uninstallProvider(addonManifest.origin);
           gBrowser.removeTab(tab);
         });
       });
     });
   },
   testDirectoryInstall: function(next) {
     AddonManager.addAddonListener(installListener(next, manifest2));
@@ -294,17 +214,17 @@ var tests = {
     let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       let installFrom = doc.nodePrincipal.origin;
       Services.prefs.setCharPref("social.directories", installFrom);
       is(SocialService.getOriginActivationType(installFrom), "directory", "testing directory install");
       Social.installProvider(doc, manifest2, function(addonManifest) {
         Services.prefs.clearUserPref("social.directories");
-        SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
+        SocialService.enableProvider(addonManifest.origin, function(provider) {
           Social.uninstallProvider(addonManifest.origin);
           gBrowser.removeTab(tab);
         });
       });
     });
   },
   testUpgradeProviderFromWorker: function(next) {
     // add the provider, change the pref, add it again. The provider at that
@@ -317,17 +237,17 @@ var tests = {
       panel.button.click();
     });
 
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       let installFrom = doc.nodePrincipal.origin;
       Services.prefs.setCharPref("social.whitelist", installFrom);
       Social.installProvider(doc, manifest2, function(addonManifest) {
-        SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
+        SocialService.enableProvider(addonManifest.origin, function(provider) {
           is(provider.manifest.version, 1, "manifest version is 1");
 
           // watch for the provider-update and test the new version
           SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
             if (topic != "provider-update")
               return;
             is(origin, addonManifest.origin, "provider updated")
             SocialService.unregisterProviderListener(providerListener);
--- a/browser/base/content/test/social/browser_blocklist.js
+++ b/browser/base/content/test/social/browser_blocklist.js
@@ -55,22 +55,22 @@ var tests = {
       Services.prefs.clearUserPref("social.manifest.good");
       resetBlocklist(next);
     }
     setManifestPref("social.manifest.good", manifest);
     setAndUpdateBlocklist(blocklistURL, function() {
       try {
         SocialService.addProvider(manifest, function(provider) {
           try {
-            SocialService.removeProvider(provider.origin, function() {
+            SocialService.disableProvider(provider.origin, function() {
               ok(true, "added and removed provider");
               finishTest(true);
             });
           } catch(e) {
-            ok(false, "SocialService.removeProvider threw exception: " + e);
+            ok(false, "SocialService.disableProvider threw exception: " + e);
             finishTest(false);
           }
         });
       } catch(e) {
         ok(false, "SocialService.addProvider threw exception: " + e);
         finishTest(false);
       }
     });
@@ -80,17 +80,17 @@ var tests = {
       ok(good, "Unable to add blocklisted provider");
       Services.prefs.clearUserPref("social.manifest.blocked");
       resetBlocklist(next);
     }
     setManifestPref("social.manifest.blocked", manifest_bad);
     setAndUpdateBlocklist(blocklistURL, function() {
       try {
         SocialService.addProvider(manifest_bad, function(provider) {
-          SocialService.removeProvider(provider.origin, function() {
+          SocialService.disableProvider(provider.origin, function() {
             ok(false, "SocialService.addProvider should throw blocklist exception");
             finishTest(false);
           });
         });
       } catch(e) {
         ok(true, "SocialService.addProvider should throw blocklist exception: " + e);
         finishTest(true);
       }
deleted file mode 100644
--- a/browser/base/content/test/social/browser_defaults.js
+++ /dev/null
@@ -1,14 +0,0 @@
-
-let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
-
-// this test ensures that any builtin providers have the builtin flag that we
-// need to help with "install" of a builtin.
-function test() {
-  let manifestPrefs = Services.prefs.getDefaultBranch("social.manifest.");
-  let prefs = manifestPrefs.getChildList("", []);
-  ok(prefs.length > 0, "we have builtin providers");
-  for (let pref of prefs) {
-    let manifest = JSON.parse(manifestPrefs.getComplexValue(pref, Ci.nsISupportsString).data);
-    ok(manifest.builtin, "manifest is builtin " + manifest.origin);
-  }
-}
--- a/browser/base/content/test/social/browser_share.js
+++ b/browser/base/content/test/social/browser_share.js
@@ -110,17 +110,17 @@ var tests = {
       checkSocialUI();
       // share should not be enabled since we only have about:blank page
       let shareButton = SocialShare.shareButton;
       is(shareButton.disabled, true, "share button is disabled");
       // verify the attribute for proper css
       is(shareButton.getAttribute("disabled"), "true", "share button attribute is disabled");
       // button should be visible
       is(shareButton.hidden, false, "share button is visible");
-      SocialService.removeProvider(manifest.origin, next);
+      SocialService.disableProvider(manifest.origin, next);
     });
   },
   testShareEnabledOnActivation: function(next) {
     // starting from *some* page, share should be visible and enabled when
     // activating provider
     let testData = corpus[0];
     loadURLInTab(testData.url, function(tab) {
       SocialService.addProvider(manifest, function(provider) {
@@ -158,17 +158,17 @@ var tests = {
       switch (topic) {
         case "got-share-data-message":
           gBrowser.removeTab(testTab);
           hasoptions(testData.options, e.data.result);
           testData = corpus[testIndex++];
           if (testData) {
             executeSoon(runOneTest);
           } else {
-            SocialService.removeProvider(manifest.origin, next);
+            SocialService.disableProvider(manifest.origin, next);
           }
           break;
       }
     }
     port.postMessage({topic: "test-init"});
     executeSoon(runOneTest);
   },
   testShareMicrodata: function(next) {
@@ -222,17 +222,17 @@ var tests = {
       });
 
       port.onmessage = function (e) {
         let topic = e.data.topic;
         switch (topic) {
           case "got-share-data-message":
             is(JSON.stringify(e.data.result), expecting, "microdata data ok");
             gBrowser.removeTab(testTab);
-            SocialService.removeProvider(manifest.origin, next);
+            SocialService.disableProvider(manifest.origin, next);
             break;
         }
       }
       port.postMessage({topic: "test-init"});
   
       let url = "https://example.com/browser/browser/base/content/test/social/microdata.html"
       addTab(url, function(tab) {
         testTab = tab;
--- a/browser/base/content/test/social/browser_social_activation.js
+++ b/browser/base/content/test/social/browser_social_activation.js
@@ -1,56 +1,50 @@
 /* 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/. */
 
 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
 
 let tabsToRemove = [];
 
+
+function removeAllProviders(callback) {
+  // all the providers may have been added.
+  function removeProviders() {
+    if (Social.providers.length < 1) {
+      executeSoon(function() {
+        is(Social.providers.length, 0, "all providers removed");
+        executeSoon(callback);
+      });
+      return;
+    }
+
+    // a full install sets the manifest into a pref, addProvider alone doesn't,
+    // make sure we uninstall if the manifest was added.
+    if (Social.providers[0].manifest) {
+      SocialService.uninstallProvider(Social.providers[0].origin, removeProviders);
+    } else {
+      SocialService.disableProvider(Social.providers[0].origin, removeProviders);
+    }
+  }
+  removeProviders();
+}
+
 function postTestCleanup(callback) {
   // any tabs opened by the test.
   for (let tab of tabsToRemove)
     gBrowser.removeTab(tab);
   tabsToRemove = [];
   // theses tests use the notification panel but don't bother waiting for it
   // to fully open - the end result is that the panel might stay open
   //SocialUI.activationPanel.hidePopup();
 
-  Services.prefs.clearUserPref("social.whitelist");
-
-  // all providers may have had their manifests added.
-  for (let manifest of gProviders)
-    Services.prefs.clearUserPref("social.manifest." + manifest.origin);
-
   // all the providers may have been added.
-  let providers = gProviders.slice(0)
-  function removeProviders() {
-    if (providers.length < 1) {
-      executeSoon(function() {
-        is(Social.providers.length, 0, "all providers removed");
-        callback();
-      });
-      return;
-    }
-
-    let provider = providers.pop();
-    try {
-      SocialService.removeProvider(provider.origin, removeProviders);
-    } catch(ex) {
-      removeProviders();
-    }
-  }
-  removeProviders();
-}
-
-function addBuiltinManifest(manifest) {
-  let prefname = getManifestPrefname(manifest);
-  setBuiltinManifestPref(prefname, manifest);
-  return prefname;
+  removeAllProviders(callback);
 }
 
 function addTab(url, callback) {
   let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
   tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
     tab.linkedBrowser.removeEventListener("load", tabLoad, true);
     tabsToRemove.push(tab);
     executeSoon(function() {callback(tab)});
@@ -205,120 +199,95 @@ var tests = {
     // At this stage none of our providers exist, so we expect failure.
     Services.prefs.setBoolPref("social.remote-install.enabled", false);
     activateProvider(gTestDomains[0], function() {
       is(SocialUI.enabled, false, "SocialUI is not enabled");
       let panel = document.getElementById("servicesInstall-notification");
       ok(panel.hidden, "activation panel still hidden");
       checkSocialUI();
       Services.prefs.clearUserPref("social.remote-install.enabled");
-      next();
+      removeAllProviders(next);
     });
   },
-
+  
   testIFrameActivation: function(next) {
-    Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     activateIFrameProvider(gTestDomains[0], function() {
       is(SocialUI.enabled, false, "SocialUI is not enabled");
       ok(!SocialSidebar.provider, "provider is not installed");
       let panel = document.getElementById("servicesInstall-notification");
       ok(panel.hidden, "activation panel still hidden");
       checkSocialUI();
-      Services.prefs.clearUserPref("social.whitelist");
-      next();
+      removeAllProviders(next);
     });
   },
-
+  
   testActivationFirstProvider: function(next) {
-    Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     // first up we add a manifest entry for a single provider.
     activateOneProvider(gProviders[0], false, function() {
       // we deactivated leaving no providers left, so Social is disabled.
       ok(!SocialSidebar.provider, "should be no provider left after disabling");
       checkSocialUI();
-      Services.prefs.clearUserPref("social.whitelist");
-      next();
+      removeAllProviders(next);
     });
   },
-
-  testActivationBuiltin: function(next) {
-    let prefname = addBuiltinManifest(gProviders[0]);
-    is(SocialService.getOriginActivationType(gTestDomains[0]), "builtin", "manifest is builtin");
-    // first up we add a manifest entry for a single provider.
-    activateOneProvider(gProviders[0], false, function() {
-      // we deactivated leaving no providers left, so Social is disabled.
-      ok(!SocialSidebar.provider, "should be no provider left after disabling");
-      checkSocialUI();
-      resetBuiltinManifestPref(prefname);
-      next();
-    });
-  },
-
+  
   testActivationMultipleProvider: function(next) {
     // The trick with this test is to make sure that Social.providers[1] is
     // the current provider when doing the undo - this makes sure that the
     // Social code doesn't fallback to Social.providers[0], which it will
     // do in some cases (but those cases do not include what this test does)
     // first enable the 2 providers
-    Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     SocialService.addProvider(gProviders[0], function() {
       SocialService.addProvider(gProviders[1], function() {
         checkSocialUI();
         // activate the last provider.
-        let prefname = addBuiltinManifest(gProviders[2]);
         activateOneProvider(gProviders[2], false, function() {
           // we deactivated - the first provider should be enabled.
           is(SocialSidebar.provider.origin, Social.providers[1].origin, "original provider should have been reactivated");
           checkSocialUI();
-          Services.prefs.clearUserPref("social.whitelist");
-          resetBuiltinManifestPref(prefname);
-          next();
+          removeAllProviders(next);
         });
       });
     });
   },
 
   testAddonManagerDoubleInstall: function(next) {
-    Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     // Create a new tab and load about:addons
     let blanktab = gBrowser.addTab();
     gBrowser.selectedTab = blanktab;
     BrowserOpenAddonsMgr('addons://list/service');
 
     is(blanktab, gBrowser.selectedTab, "Current tab should be blank tab");
 
     gBrowser.selectedBrowser.addEventListener("load", function tabLoad() {
       gBrowser.selectedBrowser.removeEventListener("load", tabLoad, true);
       let browser = blanktab.linkedBrowser;
       is(browser.currentURI.spec, "about:addons", "about:addons should load into blank tab.");
 
-      let prefname = addBuiltinManifest(gProviders[0]);
       activateOneProvider(gProviders[0], true, function() {
         info("first activation completed");
-        is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html");
+        is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html", "postActivationURL loaded");
         gBrowser.removeTab(gBrowser.selectedTab);
-        is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_activate.html");
+        is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_activate.html", "activation page selected");
         gBrowser.removeTab(gBrowser.selectedTab);
         tabsToRemove.pop();
         // uninstall the provider
         clickAddonRemoveButton(blanktab, function(addon) {
           checkSocialUI();
           activateOneProvider(gProviders[0], true, function() {
             info("second activation completed");
-            is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html");
+            is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html", "postActivationURL loaded");
             gBrowser.removeTab(gBrowser.selectedTab);
 
             // after closing the addons tab, verify provider is still installed
             gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() {
               gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
               AddonManager.getAddonsByTypes(["service"], function(aAddons) {
                 is(aAddons.length, 1, "there can be only one");
-                Services.prefs.clearUserPref("social.whitelist");
-                resetBuiltinManifestPref(prefname);
-                next();
+                removeAllProviders(next);
               });
             });
 
             // verify only one provider in list
             AddonManager.getAddonsByTypes(["service"], function(aAddons) {
               is(aAddons.length, 1, "there can be only one");
 
               let doc = blanktab.linkedBrowser.contentDocument;
--- a/browser/base/content/test/social/browser_social_marks.js
+++ b/browser/base/content/test/social/browser_social_marks.js
@@ -84,17 +84,17 @@ var tests = {
       let button = document.getElementById(id);
       is(button.disabled, true, "mark button is disabled");
       // verify the attribute for proper css
       is(button.getAttribute("disabled"), "true", "mark button attribute is disabled");
       // button should be visible
       is(button.hidden, false, "mark button is visible");
 
       checkSocialUI(window);
-      SocialService.removeProvider(manifest2.origin, next);
+      SocialService.disableProvider(manifest2.origin, next);
     });
   },
   testNoButtonOnEnable: function(next) {
     // we expect the addon install dialog to appear, we need to accept the
     // install from the dialog.
     let panel = document.getElementById("servicesInstall-notification");
     PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
       PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
@@ -102,17 +102,17 @@ var tests = {
       panel.button.click();
     });
 
     let activationURL = manifest3.origin + "/browser/browser/base/content/test/social/social_activate.html"
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       Social.installProvider(doc, manifest3, function(addonManifest) {
         // enable the provider so we know the button would have appeared
-        SocialService.addBuiltinProvider(manifest3.origin, function(provider) {
+        SocialService.enableProvider(manifest3.origin, function(provider) {
           is(provider.origin, manifest3.origin, "provider is installed");
           let id = SocialMarks._toolbarHelper.idFromOrigin(provider.origin);
           let widget = CustomizableUI.getWidget(id);
           ok(!widget || !widget.forWindow(window).node, "no button added to widget set");
           Social.uninstallProvider(manifest3.origin, function() {
             gBrowser.removeTab(tab);
             next();
           });
@@ -129,17 +129,17 @@ var tests = {
       panel.button.click();
     });
 
     // enable the provider now
     let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       Social.installProvider(doc, manifest2, function(addonManifest) {
-        SocialService.addBuiltinProvider(manifest2.origin, function(provider) {
+        SocialService.enableProvider(manifest2.origin, function(provider) {
           is(provider.origin, manifest2.origin, "provider is installed");
           let id = SocialMarks._toolbarHelper.idFromOrigin(manifest2.origin);
           let widget = CustomizableUI.getWidget(id).forWindow(window)
           ok(widget.node, "button added to widget set");
 
           // bypass widget go directly to dom, check attribute states
           let button = document.getElementById(id);
           is(button.disabled, false, "mark button is disabled");
@@ -329,17 +329,17 @@ var tests = {
       SocialMarks.markLink(manifest2.origin, url, target);
     });
   },
 
   testButtonOnDisable: function(next) {
     // enable the provider now
     let provider = Social._getProviderFromOrigin(manifest2.origin);
     ok(provider, "provider is installed");
-    SocialService.removeProvider(manifest2.origin, function() {
+    SocialService.disableProvider(manifest2.origin, function() {
       let id = SocialMarks._toolbarHelper.idFromOrigin(manifest2.origin);
       waitForCondition(function() {
                         // getWidget now returns null since we've destroyed the widget
                         return !CustomizableUI.getWidget(id)
                        },
                        function() {
                          checkSocialUI(window);
                          Social.uninstallProvider(manifest2.origin, next);
@@ -376,17 +376,17 @@ var tests = {
 
       let activationURL = manifest.origin + "/browser/browser/base/content/test/social/social_activate.html"
       let id = SocialMarks._toolbarHelper.idFromOrigin(manifest.origin);
       let toolbar = document.getElementById("nav-bar");
       addTab(activationURL, function(tab) {
         let doc = tab.linkedBrowser.contentDocument;
         Social.installProvider(doc, manifest, function(addonManifest) {
           // enable the provider so we know the button would have appeared
-          SocialService.addBuiltinProvider(manifest.origin, function(provider) {
+          SocialService.enableProvider(manifest.origin, function(provider) {
             waitForCondition(function() { return CustomizableUI.getWidget(id) },
                              function() {
               gBrowser.removeTab(tab);
               installed.push(manifest.origin);
               // checkSocialUI will properly check where the menus are located
               checkSocialUI(window);
               executeSoon(function() {
                 addProviders(callback);
--- a/browser/base/content/test/social/browser_social_sidebar.js
+++ b/browser/base/content/test/social/browser_social_sidebar.js
@@ -65,17 +65,17 @@ function doTest() {
     checkShown(false);
 
     browser.addEventListener("socialFrameShow", function sidebarshow() {
       browser.removeEventListener("socialFrameShow", sidebarshow);
 
       checkShown(true);
 
       // disable social.
-      SocialService.removeProvider(SocialSidebar.provider.origin, function() {
+      SocialService.disableProvider(SocialSidebar.provider.origin, function() {
         checkShown(false);
         is(Social.providers.length, 0, "no providers left");
         defaultFinishChecks();
         // Finish the test
         executeSoon(finish);
       });
     });
 
--- a/browser/base/content/test/social/browser_social_status.js
+++ b/browser/base/content/test/social/browser_social_status.js
@@ -63,17 +63,17 @@ var tests = {
       panel.button.click();
     })
 
     let activationURL = manifest3.origin + "/browser/browser/base/content/test/social/social_activate.html"
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       Social.installProvider(doc, manifest3, function(addonManifest) {
         // enable the provider so we know the button would have appeared
-        SocialService.addBuiltinProvider(manifest3.origin, function(provider) {
+        SocialService.enableProvider(manifest3.origin, function(provider) {
           is(provider.origin, manifest3.origin, "provider is installed");
           let id = SocialStatus._toolbarHelper.idFromOrigin(provider.origin);
           let widget = CustomizableUI.getWidget(id);
           ok(!widget || !widget.forWindow(window).node, "no button added to widget set");
           Social.uninstallProvider(manifest3.origin, function() {
             gBrowser.removeTab(tab);
             next();
           });
@@ -89,17 +89,17 @@ var tests = {
       panel.button.click();
     });
 
     // enable the provider now
     let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
     addTab(activationURL, function(tab) {
       let doc = tab.linkedBrowser.contentDocument;
       Social.installProvider(doc, manifest2, function(addonManifest) {
-        SocialService.addBuiltinProvider(manifest2.origin, function(provider) {
+        SocialService.enableProvider(manifest2.origin, function(provider) {
           is(provider.origin, manifest2.origin, "provider is installed");
           let id = SocialStatus._toolbarHelper.idFromOrigin(manifest2.origin);
           let widget = CustomizableUI.getWidget(id).forWindow(window);
           ok(widget.node, "button added to widget set");
           checkSocialUI(window);
           gBrowser.removeTab(tab);
           next();
         });
@@ -151,17 +151,17 @@ var tests = {
       }
     };
     port.postMessage({topic: "test-init"});
   },
   testButtonOnDisable: function(next) {
     // enable the provider now
     let provider = Social._getProviderFromOrigin(manifest2.origin);
     ok(provider, "provider is installed");
-    SocialService.removeProvider(manifest2.origin, function() {
+    SocialService.disableProvider(manifest2.origin, function() {
       let id = SocialStatus._toolbarHelper.idFromOrigin(manifest2.origin);
       waitForCondition(function() { return !document.getElementById(id) },
                        function() {
                         Social.uninstallProvider(manifest2.origin, next);
                        }, "button does not exist after disabling the provider");
     });
   }
 }
--- a/browser/base/content/test/social/browser_social_window.js
+++ b/browser/base/content/test/social/browser_social_window.js
@@ -105,18 +105,18 @@ let tests = {
             // now init is complete, open a second window
             openWindowAndWaitForInit(window, function(w2) {
               ok(w1.SocialSidebar.opened, "w1 sidebar is open");
               ok(w2.SocialSidebar.opened, "w2 sidebar is open");
               checkSocialUI(w2);
               checkSocialUI(w1);
 
               // disable social and re-check
-              SocialService.removeProvider(manifest.origin, function() {
-                SocialService.removeProvider(manifest2.origin, function() {
+              SocialService.disableProvider(manifest.origin, function() {
+                SocialService.disableProvider(manifest2.origin, function() {
                   ok(!Social.enabled, "social is disabled");
                   is(Social.providers.length, 0, "no providers");
                   ok(!w1.SocialSidebar.opened, "w1 sidebar is closed");
                   ok(!w2.SocialSidebar.opened, "w2 sidebar is closed");
                   checkSocialUI(w2);
                   checkSocialUI(w1);
                   Services.prefs.clearUserPref("social.manifest.test");
                   cbnext();
@@ -156,17 +156,17 @@ let tests = {
               // global state should now be no sidebar gets opened on new window
               closeWindow(w1, function() {
                 ok(!Services.prefs.prefHasUserValue("social.sidebar.provider"), "3. global state unset");
                 ok(!SocialSidebar.opened, "3. main sidebar is still closed");
                 openWindowAndWaitForInit(window, function(w1) {
                   ok(!Services.prefs.prefHasUserValue("social.sidebar.provider"), "4. global state unset");
                   ok(!SocialSidebar.opened, "4. main sidebar is still closed");
                   ok(!w1.SocialSidebar.opened, "4. window sidebar is closed");
-                  SocialService.removeProvider(manifest.origin, function() {
+                  SocialService.disableProvider(manifest.origin, function() {
                     Services.prefs.clearUserPref("social.manifest.test");
                     cbnext();
                   });
                 });
               });
             });
           });
         });        
@@ -175,18 +175,18 @@ let tests = {
   },
 
   // Check per window sidebar functionality, including migration from using
   // prefs to using session state, and state inheritance of windows (new windows
   // inherit state from the opener).
   testPerWindowSidebar: function(cbnext) {
     function finishCheck() {
       // disable social and re-check
-      SocialService.removeProvider(manifest.origin, function() {
-        SocialService.removeProvider(manifest2.origin, function() {
+      SocialService.disableProvider(manifest.origin, function() {
+        SocialService.disableProvider(manifest2.origin, function() {
           ok(!Social.enabled, "social is disabled");
           is(Social.providers.length, 0, "no providers");
           Services.prefs.clearUserPref("social.manifest.test");
           cbnext();
         });
       });
     }
 
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -101,24 +101,24 @@ function runSocialTestWithProvider(manif
     if (finishCount == manifests.length)
       Task.spawn(finishCleanUp).then(finishcallback || defaultFinishChecks);
   }
   function removeAddedProviders(cleanup) {
     manifests.forEach(function (m) {
       // If we're "cleaning up", don't call finish when done.
       let callback = cleanup ? function () {} : finishIfDone;
       // Similarly, if we're cleaning up, catch exceptions from removeProvider
-      let removeProvider = SocialService.removeProvider.bind(SocialService);
+      let removeProvider = SocialService.disableProvider.bind(SocialService);
       if (cleanup) {
         removeProvider = function (origin, cb) {
           try {
-            SocialService.removeProvider(origin, cb);
+            SocialService.disableProvider(origin, cb);
           } catch (ex) {
             // Ignore "provider doesn't exist" errors.
-            if (ex.message.indexOf("SocialService.removeProvider: no provider with origin") == 0)
+            if (ex.message.indexOf("SocialService.disableProvider: no provider with origin") == 0)
               return;
             info("Failed to clean up provider " + origin + ": " + ex);
           }
         }
       }
       removeProvider(m.origin, callback);
     });
   }
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -67,16 +67,17 @@
         this.inputField.addEventListener("mousemove", this, false);
         this.inputField.addEventListener("mouseout", this, false);
         this.inputField.addEventListener("overflow", this, false);
         this.inputField.addEventListener("underflow", this, false);
 
         try {
           if (this._prefs.getBoolPref("unifiedcomplete")) {
             this.setAttribute("autocompletesearch", "unifiedcomplete");
+            this.mSearchNames = null;
           }
         } catch (ex) {}
 
         const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
         var textBox = document.getAnonymousElementByAttribute(this,
                                                 "anonid", "textbox-input-box");
         var cxmenu = document.getAnonymousElementByAttribute(textBox,
                                             "anonid", "input-box-contextmenu");
@@ -607,16 +608,17 @@
               case "unifiedcomplete":
                 let useUnifiedComplete = false;
                 try {
                   useUnifiedComplete = this._prefs.getBoolPref(aData);
                 } catch (ex) {}
                 this.setAttribute("autocompletesearch",
                                   useUnifiedComplete ? "unifiedcomplete"
                                                      : "urlinline history");
+                this.mSearchNames = null;
             }
           }
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
--- a/browser/components/loop/MozLoopPushHandler.jsm
+++ b/browser/components/loop/MozLoopPushHandler.jsm
@@ -3,36 +3,56 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Timer.jsm");
 
 this.EXPORTED_SYMBOLS = ["MozLoopPushHandler"];
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 
 /**
  * We don't have push notifications on desktop currently, so this is a
  * workaround to get them going for us.
- *
- * XXX Handle auto-reconnections if connection fails for whatever reason
- * (bug 1013248).
  */
 let MozLoopPushHandler = {
   // This is the uri of the push server.
   pushServerUri: Services.prefs.getCharPref("services.push.serverURL"),
   // This is the channel id we're using for notifications
   channelID: "8b1081ce-9b35-42b5-b8f5-3ff8cb813a50",
+  // This is the UserAgent UUID assigned by the PushServer
+  uaID: undefined,
   // Stores the push url if we're registered and we have one.
   pushUrl: undefined,
+  // Set to true once the channelID has been registered with the PushServer.
+  registered: false,
+
+  _minRetryDelay_ms: (() => {
+    try {
+      return Services.prefs.getIntPref("loop.retry_delay.start")
+    }
+    catch (e) {
+      return 60000 // 1 minute
+    }
+  })(),
+
+  _maxRetryDelay_ms: (() => {
+    try {
+      return Services.prefs.getIntPref("loop.retry_delay.limit")
+    }
+    catch (e) {
+      return 300000 // 5 minutes
+    }
+  })(),
 
    /**
     * Starts a connection to the push socket server. On
     * connection, it will automatically say hello and register the channel
     * id with the server.
     *
     * Register callback parameters:
     * - {String|null} err: Encountered error, if any
@@ -46,109 +66,196 @@ let MozLoopPushHandler = {
     *                     registered.
     * @param {Function} notificationCallback Callback to be called when a
     *                     push notification is received (may be called multiple
     *                     times).
     * @param {Object} mockPushHandler Optional, test-only object, to allow
     *                                 the websocket to be mocked for tests.
     */
   initialize: function(registerCallback, notificationCallback, mockPushHandler) {
-    if (Services.io.offline) {
-      registerCallback("offline");
-      return;
+    if (mockPushHandler) {
+      this._mockPushHandler = mockPushHandler;
     }
 
     this._registerCallback = registerCallback;
     this._notificationCallback = notificationCallback;
-
-    if (mockPushHandler) {
-      // For tests, use the mock instance.
-      this._websocket = mockPushHandler;
-    } else {
-      this._websocket = Cc["@mozilla.org/network/protocol;1?name=wss"]
-        .createInstance(Ci.nsIWebSocketChannel);
-    }
-    this._websocket.protocol = "push-notification";
-
-    let pushURI = Services.io.newURI(this.pushServerUri, null, null);
-    this._websocket.asyncOpen(pushURI, this.pushServerUri, this, null);
+    this._openSocket();
   },
 
   /**
    * Listener method, handles the start of the websocket stream.
    * Sends a hello message to the server.
    *
    * @param {nsISupports} aContext Not used
    */
   onStart: function() {
-    let helloMsg = { messageType: "hello", uaid: "", channelIDs: [] };
-    this._websocket.sendMsg(JSON.stringify(helloMsg));
+    this._retryEnd();
+    // If a uaID has already been assigned, assume this is a re-connect
+    // and send the uaID in order to re-synch with the
+    // PushServer. If a registration has been completed, send the channelID.
+    let helloMsg = { messageType: "hello",
+		     uaid: this.uaID,
+		     channelIDs: this.registered ? [this.channelID] :[] };
+    this._retryOperation(() => this.onStart(), this._maxRetryDelay_ms);
+    try { // in case websocket has closed before this handler is run
+      this._websocket.sendMsg(JSON.stringify(helloMsg));
+    }
+    catch (e) {console.warn("MozLoopPushHandler::onStart websocket.sendMsg() failure");}
   },
 
   /**
    * Listener method, called when the websocket is closed.
    *
    * @param {nsISupports} aContext Not used
    * @param {nsresult} aStatusCode Reason for stopping (NS_OK = successful)
    */
   onStop: function(aContext, aStatusCode) {
-    // XXX We really should be handling auto-reconnect here, this will be
-    // implemented in bug 994151. For now, just log a warning, so that a
-    // developer can find out it has happened and not get too confused.
     Cu.reportError("Loop Push server web socket closed! Code: " + aStatusCode);
-    this.pushUrl = undefined;
+    this._retryOperation(() => this._openSocket());
   },
 
   /**
    * Listener method, called when the websocket is closed by the server.
    * If there are errors, onStop may be called without ever calling this
    * method.
    *
    * @param {nsISupports} aContext Not used
    * @param {integer} aCode the websocket closing handshake close code
    * @param {String} aReason the websocket closing handshake close reason
    */
   onServerClose: function(aContext, aCode) {
-    // XXX We really should be handling auto-reconnect here, this will be
-    // implemented in bug 994151. For now, just log a warning, so that a
-    // developer can find out it has happened and not get too confused.
     Cu.reportError("Loop Push server web socket closed (server)! Code: " + aCode);
-    this.pushUrl = undefined;
+    this._retryOperation(() => this._openSocket());
   },
 
   /**
    * Listener method, called when the websocket receives a message.
    *
    * @param {nsISupports} aContext Not used
    * @param {String} aMsg The message data
    */
   onMessageAvailable: function(aContext, aMsg) {
     let msg = JSON.parse(aMsg);
 
     switch(msg.messageType) {
       case "hello":
-        this._registerChannel();
+        this._retryEnd();
+	if (this.uaID !== msg.uaid) {
+	  this.uaID = msg.uaid;
+          this._registerChannel();
+	}
         break;
+
       case "register":
-        this.pushUrl = msg.pushEndpoint;
-        this._registerCallback(null, this.pushUrl);
+        this._onRegister(msg);
         break;
+
       case "notification":
-        msg.updates.forEach(function(update) {
+        msg.updates.forEach((update) => {
           if (update.channelID === this.channelID) {
             this._notificationCallback(update.version);
           }
-        }.bind(this));
+        });
         break;
     }
   },
 
   /**
+   * Handles the PushServer registration response.
+   *
+   * @param {} msg PushServer to UserAgent registration response (parsed from JSON).
+   */
+  _onRegister: function(msg) {
+    switch (msg.status) {
+      case 200:
+        this._retryEnd(); // reset retry mechanism
+	this.registered = true;
+        if (this.pushUrl !== msg.pushEndpoint) {
+          this.pushUrl = msg.pushEndpoint;
+          this._registerCallback(null, this.pushUrl);
+        }
+        break;
+
+      case 500:
+        // retry the registration request after a suitable delay
+        this._retryOperation(() => this._registerChannel());
+        break;
+
+      case 409:
+        this._registerCallback("error: PushServer ChannelID already in use");
+	break;
+
+      default:
+        this._registerCallback("error: PushServer registration failure, status = " + msg.status);
+	break;
+    }
+  },
+
+  /**
+   * Attempts to open a websocket.
+   *
+   * A new websocket interface is used each time. If an onStop callback
+   * was received, calling asyncOpen() on the same interface will
+   * trigger a "alreay open socket" exception even though the channel
+   * is logically closed.
+   */
+  _openSocket: function() {
+    if (this._mockPushHandler) {
+      // For tests, use the mock instance.
+      this._websocket = this._mockPushHandler;
+    } else if (!Services.io.offline) {
+      this._websocket = Cc["@mozilla.org/network/protocol;1?name=wss"]
+                        .createInstance(Ci.nsIWebSocketChannel);
+    } else {
+      this._registerCallback("offline");
+      console.warn("MozLoopPushHandler - IO offline");
+      return;
+    }
+
+    this._websocket.protocol = "push-notification";
+    let uri = Services.io.newURI(this.pushServerUri, null, null);
+    this._websocket.asyncOpen(uri, this.pushServerUri, this, null);
+  },
+
+  /**
    * Handles registering a service
    */
   _registerChannel: function() {
-    this._websocket.sendMsg(JSON.stringify({
-      messageType: "register",
-      channelID: this.channelID
-    }));
+    this.registered = false;
+    try { // in case websocket has closed
+      this._websocket.sendMsg(JSON.stringify({messageType: "register",
+                                              channelID: this.channelID}));
+    }
+    catch (e) {console.warn("MozLoopPushHandler::_registerChannel websocket.sendMsg() failure");}
+  },
+
+  /**
+   * Method to handle retrying UserAgent to PushServer request following
+   * a retry back-off scheme managed by this function.
+   *
+   * @param {function} delayedOp Function to call after current delay is satisfied
+   *
+   * @param {number} [optional] retryDelay This parameter will be used as the initial delay
+   */
+  _retryOperation: function(delayedOp, retryDelay) {
+    if (!this._retryCount) {
+      this._retryDelay = retryDelay || this._minRetryDelay_ms;
+      this._retryCount = 1;
+    } else {
+      let nextDelay = this._retryDelay * 2;
+      this._retryDelay = nextDelay > this._maxRetryDelay_ms ? this._maxRetryDelay_ms : nextDelay;
+      this._retryCount += 1;
+    }
+    this._timeoutID = setTimeout(delayedOp, this._retryDelay);
+  },
+
+  /**
+   * Method used to reset the retry delay back-off logic.
+   *
+   */
+  _retryEnd: function() {
+    if (this._retryCount) {
+      clearTimeout(this._timeoutID);
+      this._retryCount = 0;
+    }
   }
 };
 
--- a/browser/components/loop/test/xpcshell/head.js
+++ b/browser/components/loop/test/xpcshell/head.js
@@ -13,16 +13,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
                                   "resource:///modules/loop/MozLoopPushHandler.jsm");
 
 const kMockWebSocketChannelName = "Mock WebSocket Channel";
 const kWebSocketChannelContractID = "@mozilla.org/network/protocol;1?name=wss";
 
 const kServerPushUrl = "http://localhost:3456";
+const kEndPointUrl = "http://example.com/fake";
+const kUAID = "f47ac11b-58ca-4372-9567-0e02b2c3d479";
 
 // Fake loop server
 var loopServer;
 
 function setupFakeLoopServer() {
   loopServer = new HttpServer();
   loopServer.start(-1);
 
@@ -62,17 +64,18 @@ let mockPushHandler = {
   }
 };
 
 /**
  * Mock nsIWebSocketChannel for tests. This mocks the WebSocketChannel, and
  * enables us to check parameters and return messages similar to the push
  * server.
  */
-let MockWebSocketChannel = function() {
+let MockWebSocketChannel = function(initRegStatus) {
+  this.initRegStatus = initRegStatus;
 };
 
 MockWebSocketChannel.prototype = {
   QueryInterface: XPCOMUtils.generateQI(Ci.nsIWebSocketChannel),
 
   /**
    * nsIWebSocketChannel implementations.
    * See nsIWebSocketChannel.idl for API details.
@@ -81,33 +84,51 @@ MockWebSocketChannel.prototype = {
     this.uri = aURI;
     this.origin = aOrigin;
     this.listener = aListener;
     this.context = aContext;
 
     this.listener.onStart(this.context);
   },
 
-  notify: function(version) {
-    this.listener.onMessageAvailable(this.context,
-      JSON.stringify({
-        messageType: "notification", updates: [{
-          channelID: "8b1081ce-9b35-42b5-b8f5-3ff8cb813a50",
-          version: version
-        }]
-    }));
-  },
-
   sendMsg: function(aMsg) {
     var message = JSON.parse(aMsg);
 
     switch(message.messageType) {
       case "hello":
         this.listener.onMessageAvailable(this.context,
-          JSON.stringify({messageType: "hello"}));
+          JSON.stringify({messageType: "hello",
+                          uaid: kUAID}));
         break;
       case "register":
+        this.channelID = message.channelID;
+        let statusCode = 200;
+        if (this.initRegStatus) {
+          statusCode = this.initRegStatus;
+          this.initRegStatus = 0;
+        }
         this.listener.onMessageAvailable(this.context,
-          JSON.stringify({messageType: "register", pushEndpoint: "http://example.com/fake"}));
+          JSON.stringify({messageType: "register",
+                          status: statusCode,
+                          channelID: this.channelID,
+                          pushEndpoint: kEndPointUrl}));
         break;
     }
-  }
+  },
+
+  notify: function(version) {
+    this.listener.onMessageAvailable(this.context,
+      JSON.stringify({
+        messageType: "notification", updates: [{
+          channelID: this.channelID,
+          version: version
+        }]
+    }));
+  },
+
+  stop: function (err) {
+    this.listener.onStop(this.context, err || -1);
+  },
+
+  serverClose: function (err) {
+    this.listener.onServerClose(this.context, err || -1);
+  },
 };
--- a/browser/components/loop/test/xpcshell/test_looppush_initialize.js
+++ b/browser/components/loop/test/xpcshell/test_looppush_initialize.js
@@ -1,36 +1,63 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
-
-add_test(function test_initalize_offline() {
-  Services.io.offline = true;
-
-  MozLoopPushHandler.initialize(function(err) {
-    Assert.equal(err, "offline", "Should error with 'offline' when offline");
+{
+  add_test(function test_initalize_offline() {
+    Services.io.offline = true;
 
-    Services.io.offline = false;
-    run_next_test();
+    MozLoopPushHandler.initialize(function(err) {
+      Assert.equal(err, "offline", "Should error with 'offline' when offline");
+
+      Services.io.offline = false;
+      run_next_test();
+    });
   });
-});
 
-add_test(function test_initalize_websocket() {
   let mockWebSocket = new MockWebSocketChannel();
 
-  MozLoopPushHandler.initialize(function(err) {
-    Assert.equal(err, null, "Should return null for success");
+  add_test(function test_initalize_websocket() {
+    MozLoopPushHandler.initialize(
+      function(err, url) {
+        Assert.equal(err, null, "Should return null for success");
+        Assert.equal(url, kEndPointUrl, "Should return push server application URL");
+        Assert.equal(mockWebSocket.uri.prePath, kServerPushUrl,
+                     "Should have the url from preferences");
+        Assert.equal(mockWebSocket.origin, kServerPushUrl,
+                     "Should have the origin url from preferences");
+        Assert.equal(mockWebSocket.protocol, "push-notification",
+                     "Should have the protocol set to push-notifications");
+        mockWebSocket.notify(15);
+      },
+      function(version) {
+        Assert.equal(version, 15, "Should have version number 15");
+        run_next_test();
+      }, 
+      mockWebSocket);
+  });
 
-    Assert.equal(mockWebSocket.uri.prePath, kServerPushUrl,
-                 "Should have the url from preferences");
-    Assert.equal(mockWebSocket.origin, kServerPushUrl,
-                 "Should have the origin url from preferences");
-    Assert.equal(mockWebSocket.protocol, "push-notification",
-                 "Should have the protocol set to push-notifications");
+  add_test(function test_reconnect_websocket() {
+    MozLoopPushHandler.uaID = undefined;
+    MozLoopPushHandler.pushUrl = undefined; //Do this to force a new registration callback.
+    mockWebSocket.stop();
+  });
+
+  add_test(function test_reopen_websocket() {
+    MozLoopPushHandler.uaID = undefined;
+    MozLoopPushHandler.pushUrl = undefined; //Do this to force a new registration callback.
+    mockWebSocket.serverClose();
+  });
+
+  add_test(function test_retry_registration() {
+    MozLoopPushHandler.uaID = undefined;
+    MozLoopPushHandler.pushUrl = undefined; //Do this to force a new registration callback.
+    mockWebSocket.initRegStatus = 500;
+    mockWebSocket.stop();
+  });
+
+  function run_test() {
+    Services.prefs.setCharPref("services.push.serverURL", kServerPushUrl);
+    Services.prefs.setIntPref("loop.retry_delay.start", 10); // 10 ms
+    Services.prefs.setIntPref("loop.retry_delay.limit", 20); // 20 ms
 
     run_next_test();
-  }, function() {}, mockWebSocket);
-});
-
-function run_test() {
-  Services.prefs.setCharPref("services.push.serverURL", kServerPushUrl);
-
-  run_next_test();
-};
+  };
+}
--- a/browser/components/shell/src/nsWindowsShellService.h
+++ b/browser/components/shell/src/nsWindowsShellService.h
@@ -11,19 +11,20 @@
 #include "nsIWindowsShellService.h"
 #include "nsITimer.h"
 
 #include <windows.h>
 #include <ole2.h>
 
 class nsWindowsShellService : public nsIWindowsShellService
 {
+  virtual ~nsWindowsShellService();
+
 public:
   nsWindowsShellService();
-  virtual ~nsWindowsShellService();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHELLSERVICE
   NS_DECL_NSIWINDOWSSHELLSERVICE
 
 protected:
   bool IsDefaultBrowserVista(bool aCheckAllTypes, bool* aIsDefaultBrowser);
   nsresult LaunchControlPanelDefaultPrograms();
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -202,19 +202,20 @@ let UI = {
     win.classList.remove("busy-undetermined");
     this.updateCommands();
     this._busyPromise = null;
   },
 
   setupBusyTimeout: function() {
     this.cancelBusyTimeout();
     this._busyTimeout = setTimeout(() => {
+      let busyPromise = this._busyPromise;
       this.unbusy();
       UI.reportError("error_operationTimeout", this._busyOperationDescription);
-      this._busyPromise.reject("promise timeout: " + this._busyOperationDescription);
+      busyPromise.reject("promise timeout: " + this._busyOperationDescription);
     }, 30000);
   },
 
   cancelBusyTimeout: function() {
     clearTimeout(this._busyTimeout);
   },
 
   busyWithProgressUntil: function(promise, operationDescription) {
@@ -805,18 +806,22 @@ let Cmds = {
       runtimeappsHeaderNode.setAttribute("hidden", "true");
     }
 
     let runtimeAppsNode = document.querySelector("#project-panel-runtimeapps");
     while (runtimeAppsNode.hasChildNodes()) {
       runtimeAppsNode.firstChild.remove();
     }
 
-    for (let i = 0; i < AppManager.webAppsStore.object.all.length; i++) {
-      let app = AppManager.webAppsStore.object.all[i];
+    let sortedApps = AppManager.webAppsStore.object.all;
+    sortedApps = sortedApps.sort((a, b) => {
+      return a.name > b.name;
+    });
+    for (let i = 0; i < sortedApps.length; i++) {
+      let app = sortedApps[i];
       let panelItemNode = document.createElement("toolbarbutton");
       panelItemNode.className = "panel-item";
       panelItemNode.setAttribute("label", app.name);
       panelItemNode.setAttribute("image", app.iconURL);
       runtimeAppsNode.appendChild(panelItemNode);
       panelItemNode.addEventListener("click", () => {
         UI.hidePanels();
         AppManager.selectedProject = {
--- a/browser/devtools/webide/webide-prefs.js
+++ b/browser/devtools/webide/webide-prefs.js
@@ -1,15 +1,15 @@
 # -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 pref("devtools.webide.showProjectEditor", true);
-pref("devtools.webide.templatesURL", "http://code.cdn.mozilla.net/templates/list.json");
+pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json");
 pref("devtools.webide.autoinstallADBHelper", true);
 pref("devtools.webide.lastprojectlocation", "");
 pref("devtools.webide.restoreLastProject", true);
 pref("devtools.webide.enableLocalRuntime", false);
 pref("devtools.webide.addonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/index.json");
 pref("devtools.webide.simulatorAddonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/#VERSION#/#OS#/fxos_#SLASHED_VERSION#_simulator-#OS#-latest.xpi");
 pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozilla.org");
 pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -1581,17 +1581,17 @@ Experiments.ExperimentEntry.prototype = 
         condition: () => !data.disabled },
       { name: "frozen",
         condition: () => !data.frozen || this._enabled },
       { name: "startTime",
         condition: () => now >= data.startTime },
       { name: "endTime",
         condition: () => now < data.endTime },
       { name: "maxStartTime",
-        condition: () => !data.maxStartTime || now <= data.maxStartTime },
+        condition: () => this._startDate || !data.maxStartTime || now <= data.maxStartTime },
       { name: "maxActiveSeconds",
         condition: () => !this._startDate || now <= (startSec + maxActive) },
       { name: "appName",
         condition: () => !data.appName || data.appName.indexOf(app.name) != -1 },
       { name: "minBuildID",
         condition: () => !data.minBuildID || app.platformBuildID >= data.minBuildID },
       { name: "maxBuildID",
         condition: () => !data.maxBuildID || app.platformBuildID <= data.maxBuildID },
--- a/browser/experiments/test/xpcshell/test_api.js
+++ b/browser/experiments/test/xpcshell/test_api.js
@@ -1534,16 +1534,76 @@ add_task(function* testEnabledAfterResta
   Assert.equal(addons[0].isActive, false, "And not active.");
 
   yield experiments.updateManifest();
   Assert.ok(addons[0].isActive, "It activates when the manifest is evaluated.");
 
   yield testCleanup(experiments);
 });
 
+// If experiment add-ons were ever started, maxStartTime shouldn't be evaluated
+// anymore. Ensure that if maxStartTime is passed but experiment has started
+// already, maxStartTime does not cause deactivation.
+
+add_task(function* testMaxStartTimeEvaluation() {
+
+  // Dates the following tests are based on.
+
+  let startDate    = new Date(2014, 5, 1, 12);
+  let now          = futureDate(startDate, 10   * MS_IN_ONE_DAY);
+  let maxStartDate = futureDate(startDate, 100  * MS_IN_ONE_DAY);
+  let endDate      = futureDate(startDate, 1000 * MS_IN_ONE_DAY);
+
+  defineNow(gPolicy, now);
+
+  // The manifest data we test with.
+  // We set a value for maxStartTime.
+
+  gManifestObject = {
+    "version": 1,
+    experiments: [
+      {
+        id:               EXPERIMENT1_ID,
+        xpiURL:           gDataRoot + EXPERIMENT1_XPI_NAME,
+        xpiHash:          EXPERIMENT1_XPI_SHA1,
+        startTime:        dateToSeconds(startDate),
+        endTime:          dateToSeconds(endDate),
+        maxActiveSeconds: 1000 * SEC_IN_ONE_DAY,
+        maxStartTime:     dateToSeconds(maxStartDate),
+        appName:          ["XPCShell"],
+        channel:          ["nightly"],
+      },
+    ],
+  };
+
+  let experiments = new Experiments.Experiments(gPolicy);
+
+  let addons = yield getExperimentAddons();
+  Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
+
+  yield experiments.updateManifest();
+  let fromManifest = yield experiments.getExperiments();
+  Assert.equal(fromManifest.length, 1, "A single experiment is known.");
+
+  addons = yield getExperimentAddons();
+  Assert.equal(addons.length, 1, "A single experiment add-on is installed.");
+  Assert.ok(addons[0].isActive, "That experiment is active.");
+
+  dump("Setting current time to maxStartTime + 100 days and reloading manifest\n");
+  now = futureDate(maxStartDate, 100 * MS_IN_ONE_DAY);
+  defineNow(gPolicy, now);
+  yield experiments.updateManifest();
+
+  addons = yield getExperimentAddons();
+  Assert.equal(addons.length, 1, "The experiment is still there.");
+  Assert.ok(addons[0].isActive, "It is still active.");
+
+  yield testCleanup(experiments);
+});
+
 // Test coverage for an add-on uninstall disabling the experiment and that it stays
 // disabled over restarts.
 add_task(function* test_foreignUninstallAndRestart() {
   let experiments = new Experiments.Experiments(gPolicy);
 
   gManifestObject = {
     "version": 1,
     experiments: [
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -704,18 +704,21 @@ just addresses the organization to follo
 
 <!ENTITY social.markpageMenu.accesskey "P">
 <!ENTITY social.markpageMenu.label "Save Page To…">
 <!ENTITY social.marklinkMenu.accesskey "L">
 <!ENTITY social.marklinkMenu.label "Save Link To…">
 
 <!ENTITY getUserMedia.selectCamera.label "Camera to share:">
 <!ENTITY getUserMedia.selectCamera.accesskey "C">
+<!ENTITY getUserMedia.selectWindowOrScreen.label "Window or screen to share:">
+<!ENTITY getUserMedia.selectWindowOrScreen.accesskey "W">
 <!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
 <!ENTITY getUserMedia.selectMicrophone.accesskey "M">
+<!ENTITY getUserMedia.allWindowsShared.message "All visible windows on your screen will be shared.">
 
 <!ENTITY webrtcIndicatorButton.label "Camera / Microphone Access">
 <!ENTITY webrtcIndicatorButton.tooltip "Display sites you are currently sharing your camera or microphone with">
 
 <!ENTITY loopCallButton.tooltip "Invite someone to talk">
 
 <!ENTITY mixedContentBlocked.moreinfo "Most websites will still work properly even when this content is blocked.">
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -107,16 +107,27 @@ installPlugin.ignoreButton.accesskey=N
 
 crashedpluginsMessage.title=The %S plugin has crashed.
 crashedpluginsMessage.reloadButton.label=Reload page
 crashedpluginsMessage.reloadButton.accesskey=R
 crashedpluginsMessage.submitButton.label=Submit a crash report
 crashedpluginsMessage.submitButton.accesskey=S
 crashedpluginsMessage.learnMore=Learn More…
 
+# Keyword fixup messages
+# LOCALIZATION NOTE (keywordURIFixup.message): Used when the user tries to visit
+# a local host page, by the time the DNS request recognizes it, we have already
+# loaded a search page for the given word.  An infobar then asks to the user
+# whether he rather wanted to visit the host.  %S is the recognized host.
+keywordURIFixup.message=Did you mean to go to %S?
+keywordURIFixup.goTo=Yes, take me to %S
+keywordURIFixup.goTo.accesskey=Y
+keywordURIFixup.dismiss=No thanks
+keywordURIFixup.dismiss.accesskey=N
+
 ## Plugin doorhanger strings
 # LOCALIZATION NOTE (pluginActivateNew.message): Used for newly-installed
 # plugins which are not known to be unsafe. %1$S is the plugin name and %2$S
 # is the site domain.
 pluginActivateNew.message=Allow %2$S to run "%1$S"?
 pluginActivateMultiple.message=Allow %S to run plugins?
 pluginActivate.learnMore=Learn More…
 # LOCALIZATION NOTE (pluginActivateOutdated.message, pluginActivateOutdated.label):
@@ -479,37 +490,49 @@ identity.newIdentity.description = Enter
 identity.next.label = Next
 identity.next.accessKey = n
 # LOCALIZATION NOTE: shown in the popup notification when a user successfully logs into a website
 # LOCALIZATION NOTE (identity.loggedIn.description): %S is the user's identity (e.g. user@example.com)
 identity.loggedIn.description = Signed in as: %S
 identity.loggedIn.signOut.label = Sign Out
 identity.loggedIn.signOut.accessKey = O
 
-# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message, getUserMedia.shareCameraAndMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
+# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message,
+#                    getUserMedia.shareScreen.message, getUserMedia.shareCameraAndMicrophone.message,
+#                    getUserMedia.shareScreenAndMicrophone.message):
+#  %S is the website origin (e.g. www.mozilla.org)
+getUserMedia.shareCamera.message = Would you like to share your camera with %S?
+getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
+getUserMedia.shareScreen.message = Would you like to share your screen with %S?
+getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
+getUserMedia.shareScreenAndMicrophone.message = Would you like to share your microphone and screen with %S?
+getUserMedia.noVideo.label = No Video
+getUserMedia.noWindowOrScreen.label = No Window or Screen
+getUserMedia.noAudio.label = No Audio
+getUserMedia.shareEntireScreen.label = Entire screen
 # LOCALIZATION NOTE (getUserMedia.shareSelectedDevices.label):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The number of devices can be either one or two.
-getUserMedia.shareCamera.message = Would you like to share your camera with %S?
-getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
-getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
-getUserMedia.noVideo.label = No Video
-getUserMedia.noAudio.label = No Audio
 getUserMedia.shareSelectedDevices.label = Share Selected Device;Share Selected Devices
 getUserMedia.shareSelectedDevices.accesskey = S
+getUserMedia.shareScreen.label = Share Screen
+getUserMedia.shareWindow.label = Share Selected Window
+getUserMedia.shareSelectedItems.label = Share Selected Items
 getUserMedia.always.label = Always Share
 getUserMedia.always.accesskey = A
 getUserMedia.denyRequest.label = Don't Share
 getUserMedia.denyRequest.accesskey = D
 getUserMedia.never.label = Never Share
 getUserMedia.never.accesskey = N
 getUserMedia.sharingCamera.message2 = You are currently sharing your camera with this page.
 getUserMedia.sharingMicrophone.message2 = You are currently sharing your microphone with this page.
 getUserMedia.sharingCameraAndMicrophone.message2 = You are currently sharing your camera and microphone with this page.
+getUserMedia.sharingScreen.message = You are currently sharing your screen with this page.
+getUserMedia.sharingWindow.message = You are currently sharing a window with this page.
 getUserMedia.continueSharing.label = Continue Sharing
 getUserMedia.continueSharing.accesskey = C
 getUserMedia.stopSharing.label = Stop Sharing
 getUserMedia.stopSharing.accesskey = S
 
 # Mixed Content Blocker Doorhanger Notification
 # LOCALIZATION NOTE - %S is brandShortName
 mixedContentBlocked.message = %S has blocked content that isn't secure.
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -247,16 +247,17 @@ pref("privacy.popups.showBrowserMessage"
 
 // Metro Firefox keeps this set to -1 when donottrackheader.enabled is false.
 pref("privacy.donottrackheader.value", -1);
 
 /* disable opening windows with the dialog feature */
 pref("dom.disable_window_open_dialog_feature", true);
 
 pref("keyword.enabled", true);
+pref("browser.fixup.domainwhitelist.localhost", true);
 
 pref("accessibility.typeaheadfind", false);
 pref("accessibility.typeaheadfind.timeout", 5000);
 pref("accessibility.typeaheadfind.flashBar", 1);
 pref("accessibility.typeaheadfind.linksonly", false);
 pref("accessibility.typeaheadfind.casesensitive", 0);
 
 // Trun on F7 caret browsing hot key
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -173,19 +173,19 @@ this.Social = {
   },
 
   uninstallProvider: function(origin, aCallback) {
     SocialService.uninstallProvider(origin, aCallback);
   },
 
   // Activation functionality
   activateFromOrigin: function (origin, callback) {
-    // For now only "builtin" providers can be activated.  It's OK if the
-    // provider has already been activated - we still get called back with it.
-    SocialService.addBuiltinProvider(origin, callback);
+    // It's OK if the provider has already been activated - we still get called
+    // back with it.
+    SocialService.enableProvider(origin, callback);
   },
 
   // Page Marking functionality
   isURIMarked: function(origin, aURI, aCallback) {
     promiseGetAnnotation(aURI).then(function(val) {
       if (val) {
         let providerList = JSON.parse(val);
         val = providerList.indexOf(origin) >= 0;
--- a/browser/modules/test/unit/social/test_social.js
+++ b/browser/modules/test/unit/social/test_social.js
@@ -15,18 +15,18 @@ function testStartupEnabled() {
   do_check_eq(Social.providers.length, 2, "two social providers enabled");
   do_check_true(Social.providers[0].enabled, "provider 0 is enabled");
   do_check_true(Social.providers[1].enabled, "provider 1 is enabled");
   run_next_test();
 }
 
 function testDisableAfterStartup() {
   let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
-  SocialService.removeProvider(Social.providers[0].origin, function() {
+  SocialService.disableProvider(Social.providers[0].origin, function() {
     do_wait_observer("social:providers-changed", function() {
       do_check_eq(Social.enabled, false, "Social is disabled");
       do_check_eq(Social.providers.length, 0, "no social providers available");
       do_test_finished();
       run_next_test();
     });
-    SocialService.removeProvider(Social.providers[0].origin)
+    SocialService.disableProvider(Social.providers[0].origin)
   });
 }
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -47,16 +47,32 @@ this.webrtcUI = {
                 browserWindow.gBrowser._getTabForContentWindow(contentWindow.top);
       activeStreams.push({
         uri: contentWindow.location.href,
         tab: tab,
         browser: browser
       });
     }
     return activeStreams;
+  },
+
+  updateMainActionLabel: function(aMenuList) {
+    let type = aMenuList.selectedItem.getAttribute("devicetype");
+    let document = aMenuList.ownerDocument;
+    document.getElementById("webRTC-all-windows-shared").hidden = type != "Screen";
+
+    // If we are also requesting audio in addition to screen sharing,
+    // always use a generic label.
+    if (!document.getElementById("webRTC-selectMicrophone").hidden)
+      type = "";
+
+    let bundle = document.defaultView.gNavigatorBundle;
+    let stringId = "getUserMedia.share" + (type || "SelectedItems") + ".label";
+    let popupnotification = aMenuList.parentNode.parentNode;
+    popupnotification.setAttribute("buttonlabel", bundle.getString(stringId));
   }
 }
 
 function getBrowserForWindowId(aWindowID) {
   return getBrowserForWindow(Services.wm.getOuterWindowWithId(aWindowID));
 }
 
 function getBrowserForWindow(aContentWindow) {
@@ -89,89 +105,106 @@ function denyRequest(aCallID, aError) {
   let msg = null;
   if (aError) {
     msg = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
     msg.data = aError;
   }
   Services.obs.notifyObservers(msg, "getUserMedia:response:deny", aCallID);
 }
 
-function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevices, aSecure) {
+function prompt(aContentWindow, aCallID, aAudio, aVideo, aDevices, aSecure) {
   let audioDevices = [];
   let videoDevices = [];
+
+  // MediaStreamConstraints defines video as 'boolean or MediaTrackConstraints'.
+  let sharingScreen = aVideo && typeof(aVideo) != "boolean" &&
+                      aVideo.mediaSource != "camera";
   for (let device of aDevices) {
     device = device.QueryInterface(Ci.nsIMediaDevice);
     switch (device.type) {
       case "audio":
-        if (aAudioRequested)
+        if (aAudio)
           audioDevices.push(device);
         break;
       case "video":
-        if (aVideoRequested)
+        // Verify that if we got a camera, we haven't requested a screen share,
+        // or that if we requested a screen share we aren't getting a camera.
+        if (aVideo && (device.mediaSource == "camera") != sharingScreen)
           videoDevices.push(device);
         break;
     }
   }
 
-  let requestType;
-  if (audioDevices.length && videoDevices.length)
-    requestType = "CameraAndMicrophone";
-  else if (audioDevices.length)
-    requestType = "Microphone";
-  else if (videoDevices.length)
-    requestType = "Camera";
-  else {
+  let requestTypes = [];
+  if (videoDevices.length)
+    requestTypes.push(sharingScreen ? "Screen" : "Camera");
+  if (audioDevices.length)
+    requestTypes.push("Microphone");
+
+  if (!requestTypes.length) {
     denyRequest(aCallID, "NO_DEVICES_FOUND");
     return;
   }
 
   let uri = aContentWindow.document.documentURIObject;
   let browser = getBrowserForWindow(aContentWindow);
   let chromeDoc = browser.ownerDocument;
   let chromeWin = chromeDoc.defaultView;
   let stringBundle = chromeWin.gNavigatorBundle;
-  let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message",
-                                                [ uri.host ]);
+  let stringId = "getUserMedia.share" + requestTypes.join("And") + ".message";
+  let message = stringBundle.getFormattedString(stringId, [uri.host]);
 
+  let mainLabel;
+  if (sharingScreen) {
+    mainLabel = stringBundle.getString("getUserMedia.shareSelectedItems.label");
+  }
+  else {
+    let string = stringBundle.getString("getUserMedia.shareSelectedDevices.label");
+    mainLabel = PluralForm.get(requestTypes.length, string);
+  }
   let mainAction = {
-    label: PluralForm.get(requestType == "CameraAndMicrophone" ? 2 : 1,
-                          stringBundle.getString("getUserMedia.shareSelectedDevices.label")),
+    label: mainLabel,
     accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"),
     // The real callback will be set during the "showing" event. The
     // empty function here is so that PopupNotifications.show doesn't
     // reject the action.
     callback: function() {}
   };
 
   let secondaryActions = [
     {
       label: stringBundle.getString("getUserMedia.denyRequest.label"),
       accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
       callback: function () {
         denyRequest(aCallID);
       }
-    },
-    {
+    }
+  ];
+
+  if (!sharingScreen) { // Bug 1037438: implement 'never' for screen sharing.
+    secondaryActions.push({
       label: stringBundle.getString("getUserMedia.never.label"),
       accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
       callback: function () {
         denyRequest(aCallID);
         // Let someone save "Never" for http sites so that they can be stopped from
         // bothering you with doorhangers.
         let perms = Services.perms;
         if (audioDevices.length)
           perms.add(uri, "microphone", perms.DENY_ACTION);
         if (videoDevices.length)
           perms.add(uri, "camera", perms.DENY_ACTION);
       }
-    }
-  ];
+    });
+  }
 
-  if (aSecure) {
-    // Don't show the 'Always' action if the connection isn't secure.
+  if (aSecure && !sharingScreen) {
+    // Don't show the 'Always' action if the connection isn't secure, or for
+    // screen sharing (because we can't guess which window the user wants to
+    // share without prompting).
     secondaryActions.unshift({
       label: stringBundle.getString("getUserMedia.always.label"),
       accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
       callback: function () {
         mainAction.callback(true);
       }
     });
   }
@@ -180,17 +213,21 @@ function prompt(aContentWindow, aCallID,
     eventCallback: function(aTopic, aNewBrowser) {
       if (aTopic == "swapping")
         return true;
 
       let chromeDoc = this.browser.ownerDocument;
 
       if (aTopic == "shown") {
         let PopupNotifications = chromeDoc.defaultView.PopupNotifications;
-        let popupId = requestType == "Microphone" ? "Microphone" : "Devices";
+        let popupId = "Devices";
+        if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
+          popupId = "Microphone";
+        if (requestTypes.indexOf("Screen") != -1)
+          popupId = "Screen";
         PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-share" + popupId);
       }
 
       if (aTopic != "showing")
         return false;
 
       // DENY_ACTION is handled immediately by MediaManager, but handling
       // of ALLOW_ACTION is delayed until the popupshowing event
@@ -201,16 +238,20 @@ function prompt(aContentWindow, aCallID,
         let micPerm = perms.testExactPermission(uri, "microphone");
         if (micPerm == perms.PROMPT_ACTION)
           micPerm = perms.UNKNOWN_ACTION;
 
         let camPerm = perms.testExactPermission(uri, "camera");
         if (camPerm == perms.PROMPT_ACTION)
           camPerm = perms.UNKNOWN_ACTION;
 
+        // Screen sharing shouldn't follow the camera permissions.
+        if (videoDevices.length && sharingScreen)
+          camPerm = perms.UNKNOWN_ACTION;
+
         // We don't check that permissions are set to ALLOW_ACTION in this
         // test; only that they are set. This is because if audio is allowed
         // and video is denied persistently, we don't want to show the prompt,
         // and will grant audio access immediately.
         if ((!audioDevices.length || micPerm) && (!videoDevices.length || camPerm)) {
           // All permissions we were about to request are already persistently set.
           let allowedDevices = Cc["@mozilla.org/supports-array;1"]
                                  .createInstance(Ci.nsISupportsArray);
@@ -230,43 +271,90 @@ function prompt(aContentWindow, aCallID,
 
         let deviceIndex = 0;
         for (let device of devices) {
           addDeviceToList(menupopup, device.name, deviceIndex);
           deviceIndex++;
         }
       }
 
-      function addDeviceToList(menupopup, deviceName, deviceIndex) {
+      function listScreenShareDevices(menupopup, devices) {
+        while (menupopup.lastChild)
+          menupopup.removeChild(menupopup.lastChild);
+
+        // "No Window or Screen" is the default because we can't pick a
+        // 'default' window to share.
+        addDeviceToList(menupopup,
+                        stringBundle.getString("getUserMedia.noWindowOrScreen.label"),
+                        "-1");
+
+        // Then add the 'Entire screen' item if mozGetUserMediaDevices returned it.
+        for (let i = 0; i < devices.length; ++i) {
+          if (devices[i].mediaSource == "screen") {
+            menupopup.appendChild(chromeDoc.createElement("menuseparator"));
+            addDeviceToList(menupopup,
+                            stringBundle.getString("getUserMedia.shareEntireScreen.label"),
+                            i, "Screen");
+            break;
+          }
+        }
+
+        // Finally add all the window names.
+        let separatorNeeded = true;
+        for (let i = 0; i < devices.length; ++i) {
+          if (devices[i].mediaSource == "window") {
+            if (separatorNeeded) {
+              menupopup.appendChild(chromeDoc.createElement("menuseparator"));
+              separatorNeeded = false;
+            }
+            addDeviceToList(menupopup, devices[i].name, i, "Window");
+          }
+        }
+
+        // Always re-select the "No Window or Screen" item.
+        chromeDoc.getElementById("webRTC-selectWindow-menulist").removeAttribute("value");
+        chromeDoc.getElementById("webRTC-all-windows-shared").hidden = true;
+      }
+
+      function addDeviceToList(menupopup, deviceName, deviceIndex, type) {
         let menuitem = chromeDoc.createElement("menuitem");
         menuitem.setAttribute("value", deviceIndex);
         menuitem.setAttribute("label", deviceName);
         menuitem.setAttribute("tooltiptext", deviceName);
+        if (type)
+          menuitem.setAttribute("devicetype", type);
         menupopup.appendChild(menuitem);
       }
 
-      chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length;
+      chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length || sharingScreen;
+      chromeDoc.getElementById("webRTC-selectWindowOrScreen").hidden = !sharingScreen || !videoDevices.length;
       chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length;
 
       let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup");
+      let windowMenupopup = chromeDoc.getElementById("webRTC-selectWindow-menupopup");
       let micMenupopup = chromeDoc.getElementById("webRTC-selectMicrophone-menupopup");
-      listDevices(camMenupopup, videoDevices);
+      if (sharingScreen)
+        listScreenShareDevices(windowMenupopup, videoDevices);
+      else
+        listDevices(camMenupopup, videoDevices);
       listDevices(micMenupopup, audioDevices);
-      if (requestType == "CameraAndMicrophone") {
+      if (requestTypes.length == 2) {
         let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
-        addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
+        if (!sharingScreen)
+          addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
         addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
       }
 
       this.mainAction.callback = function(aRemember) {
         let allowedDevices = Cc["@mozilla.org/supports-array;1"]
                                .createInstance(Ci.nsISupportsArray);
         let perms = Services.perms;
         if (videoDevices.length) {
-          let videoDeviceIndex = chromeDoc.getElementById("webRTC-selectCamera-menulist").value;
+          let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
+          let videoDeviceIndex = chromeDoc.getElementById(listId).value;
           let allowCamera = videoDeviceIndex != "-1";
           if (allowCamera)
             allowedDevices.AppendElement(videoDevices[videoDeviceIndex]);
           if (aRemember) {
             perms.add(uri, "camera",
                       allowCamera ? perms.ALLOW_ACTION : perms.DENY_ACTION);
           }
         }
@@ -287,18 +375,21 @@ function prompt(aContentWindow, aCallID,
         }
 
         Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID);
       };
       return false;
     }
   };
 
-  let anchorId = requestType == "Microphone" ? "webRTC-shareMicrophone-notification-icon"
-                                             : "webRTC-shareDevices-notification-icon";
+  let anchorId = "webRTC-shareDevices-notification-icon";
+  if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
+    anchorId = "webRTC-shareMicrophone-notification-icon";
+  if (requestTypes.indexOf("Screen") != -1)
+    anchorId = "webRTC-shareScreen-notification-icon";
   chromeWin.PopupNotifications.show(browser, "webRTC-shareDevices", message,
                                     anchorId, mainAction, secondaryActions, options);
 }
 
 function updateIndicators() {
   webrtcUI.showGlobalIndicator =
     MediaManagerService.activeMediaCaptureWindows.Count() > 0;
 
@@ -306,82 +397,115 @@ function updateIndicators() {
   while (e.hasMoreElements())
     e.getNext().WebrtcIndicator.updateButton();
 
   for (let {browser: browser} of webrtcUI.activeStreams)
     showBrowserSpecificIndicator(browser);
 }
 
 function showBrowserSpecificIndicator(aBrowser) {
-  let hasVideo = {};
-  let hasAudio = {};
+  let camera = {}, microphone = {}, screen = {}, window = {};
   MediaManagerService.mediaCaptureWindowState(aBrowser.contentWindow,
-                                              hasVideo, hasAudio);
+                                              camera, microphone, screen, window);
   let captureState;
-  if (hasVideo.value && hasAudio.value) {
+  if (camera.value && microphone.value) {
     captureState = "CameraAndMicrophone";
-  } else if (hasVideo.value) {
+  } else if (camera.value) {
     captureState = "Camera";
-  } else if (hasAudio.value) {
+  } else if (microphone.value) {
     captureState = "Microphone";
-  } else {
+  } else if (!screen.value && !window.value) {
     Cu.reportError("showBrowserSpecificIndicator: got neither video nor audio access");
     return;
   }
 
   let chromeWin = aBrowser.ownerDocument.defaultView;
   let stringBundle = chromeWin.gNavigatorBundle;
 
-  let message = stringBundle.getString("getUserMedia.sharing" + captureState + ".message2");
-
   let uri = aBrowser.contentWindow.document.documentURIObject;
   let windowId = aBrowser.contentWindow
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindowUtils)
                          .currentInnerWindowID;
   let mainAction = {
     label: stringBundle.getString("getUserMedia.continueSharing.label"),
     accessKey: stringBundle.getString("getUserMedia.continueSharing.accesskey"),
     callback: function () {},
     dismiss: true
   };
   let secondaryActions = [{
     label: stringBundle.getString("getUserMedia.stopSharing.label"),
     accessKey: stringBundle.getString("getUserMedia.stopSharing.accesskey"),
     callback: function () {
       let perms = Services.perms;
-      if (hasVideo.value &&
+      if (camera.value &&
           perms.testExactPermission(uri, "camera") == perms.ALLOW_ACTION)
         perms.remove(uri.host, "camera");
-      if (hasAudio.value &&
+      if (microphone.value &&
           perms.testExactPermission(uri, "microphone") == perms.ALLOW_ACTION)
         perms.remove(uri.host, "microphone");
 
       Services.obs.notifyObservers(null, "getUserMedia:revoke", windowId);
+
+      // Performing an action from a notification removes it, but if the page
+      // uses screensharing and a device, we may have another notification to remove.
+      let outerWindowID = Services.wm.getCurrentInnerWindowWithId(windowId)
+                                     .QueryInterface(Ci.nsIInterfaceRequestor)
+                                     .getInterface(Ci.nsIDOMWindowUtils)
+                                     .outerWindowID;
+      removeBrowserSpecificIndicator(null, null, outerWindowID);
     }
   }];
   let options = {
     hideNotNow: true,
     dismissed: true,
     eventCallback: function(aTopic) {
       if (aTopic == "shown") {
         let PopupNotifications = this.browser.ownerDocument.defaultView.PopupNotifications;
         let popupId = captureState == "Microphone" ? "Microphone" : "Devices";
         PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-sharing" + popupId);
       }
       return aTopic == "swapping";
     }
   };
-  let anchorId = captureState == "Microphone" ? "webRTC-sharingMicrophone-notification-icon"
-                                              : "webRTC-sharingDevices-notification-icon";
-  chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message,
-                                    anchorId, mainAction, secondaryActions, options);
+  if (captureState) {
+    let anchorId = captureState == "Microphone" ? "webRTC-sharingMicrophone-notification-icon"
+                                                : "webRTC-sharingDevices-notification-icon";
+    let message = stringBundle.getString("getUserMedia.sharing" + captureState + ".message2");
+    chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message,
+                                      anchorId, mainAction, secondaryActions, options);
+  }
+
+  // Now handle the screen sharing indicator.
+  if (!screen.value && !window.value)
+    return;
+
+  options = {
+    hideNotNow: true,
+    dismissed: true,
+    eventCallback: function(aTopic) {
+      if (aTopic == "shown") {
+        let PopupNotifications = this.browser.ownerDocument.defaultView.PopupNotifications;
+        PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-sharingScreen");
+      }
+      return aTopic == "swapping";
+    }
+  };
+  // If we are sharing both a window and the screen, show 'Screen'.
+  let stringId = "getUserMedia.sharing" + (screen.value ? "Screen" : "Window") + ".message";
+  chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingScreen",
+                                    stringBundle.getString(stringId),
+                                    "webRTC-sharingScreen-notification-icon",
+                                    mainAction, secondaryActions, options);
 }
 
 function removeBrowserSpecificIndicator(aSubject, aTopic, aData) {
   let browser = getBrowserForWindowId(aData);
   let PopupNotifications = browser.ownerDocument.defaultView.PopupNotifications;
-  let notification = PopupNotifications &&
-                     PopupNotifications.getNotification("webRTC-sharingDevices",
-                                                        browser);
-  if (notification)
-    PopupNotifications.remove(notification);
+  if (!PopupNotifications)
+    return;
+
+  for (let notifId of ["webRTC-sharingDevices", "webRTC-sharingScreen"]) {
+    let notification = PopupNotifications.getNotification(notifId, browser);
+    if (notification)
+      PopupNotifications.remove(notification);
+  }
 }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1226,16 +1226,21 @@ toolbarbutton[sdk-button="true"][cui-are
   list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
 }
 
 .popup-notification-icon[popupid="webRTC-sharingMicrophone"],
 .popup-notification-icon[popupid="webRTC-shareMicrophone"] {
   list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-64.png);
 }
 
+.popup-notification-icon[popupid="webRTC-sharingScreen"],
+.popup-notification-icon[popupid="webRTC-shareScreen"] {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-64.png);
+}
+
 .popup-notification-icon[popupid="pointerLock"] {
   list-style-image: url(chrome://browser/skin/pointerLock-64.png);
 }
 
 /* Notification icon box */
 #notification-popup-box {
   position: relative;
   background-color: #fff;
@@ -1372,16 +1377,26 @@ toolbarbutton[sdk-button="true"][cui-are
   list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-16.png);
 }
 
 .webRTC-sharingMicrophone-notification-icon,
 #webRTC-sharingMicrophone-notification-icon {
   list-style-image: url(chrome://browser/skin/webRTC-sharingMicrophone-16.png);
 }
 
+.webRTC-shareScreen-notification-icon,
+#webRTC-shareScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
+}
+
+.webRTC-sharingScreen-notification-icon,
+#webRTC-sharingScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16.png);
+}
+
 .web-notifications-notification-icon,
 #web-notifications-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-16.png);
 }
 
 #pointerLock-notification-icon {
   list-style-image: url(chrome://browser/skin/pointerLock-16.png);
 }
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -69,16 +69,19 @@ browser.jar:
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/webRTC-shareDevice-16.png
   skin/classic/browser/webRTC-shareDevice-64.png
   skin/classic/browser/webRTC-sharingDevice-16.png
   skin/classic/browser/webRTC-shareMicrophone-16.png
   skin/classic/browser/webRTC-shareMicrophone-64.png
   skin/classic/browser/webRTC-sharingMicrophone-16.png
+  skin/classic/browser/webRTC-shareScreen-16.png      (../shared/webrtc/webRTC-shareScreen-16.png)
+  skin/classic/browser/webRTC-shareScreen-64.png      (../shared/webrtc/webRTC-shareScreen-64.png)
+  skin/classic/browser/webRTC-sharingScreen-16.png    (../shared/webrtc/webRTC-sharingScreen-16.png)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
   skin/classic/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
   skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
   skin/classic/browser/customizableui/info-icon-customizeTip.png  (../shared/customizableui/info-icon-customizeTip.png)
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3565,16 +3565,38 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 @media (min-resolution: 2dppx) {
   .webRTC-sharingMicrophone-notification-icon,
   #webRTC-sharingMicrophone-notification-icon {
     list-style-image: url(chrome://browser/skin/webRTC-sharingMicrophone-16@2x.png);
   }
 }
 
+.webRTC-shareScreen-notification-icon,
+#webRTC-shareScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
+}
+@media (min-resolution: 2dppx) {
+  .webRTC-shareScreen-notification-icon,
+  #webRTC-shareScreen-notification-icon {
+    list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16@2x.png);
+  }
+}
+
+.webRTC-sharingScreen-notification-icon,
+#webRTC-sharingScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16.png);
+}
+@media (min-resolution: 2dppx) {
+  .webRTC-sharingScreen-notification-icon,
+  #webRTC-sharingScreen-notification-icon {
+    list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16@2x.png);
+  }
+}
+
 .web-notifications-notification-icon,
 #web-notifications-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-16.png);
 }
 @media (min-resolution: 2dppx) {
   .web-notifications-notification-icon,
   #web-notifications-notification-icon {
     list-style-image: url(chrome://browser/skin/notification-16@2x.png);
@@ -3887,16 +3909,27 @@ menulist.translate-infobar-element > .me
 }
 @media (min-resolution: 2dppx) {
   .popup-notification-icon[popupid="webRTC-sharingMicrophone"],
   .popup-notification-icon[popupid="webRTC-shareMicrophone"] {
     list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-64@2x.png);
   }
 }
 
+.popup-notification-icon[popupid="webRTC-sharingScreen"],
+.popup-notification-icon[popupid="webRTC-shareScreen"] {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-64.png);
+}
+@media (min-resolution: 2dppx) {
+  .popup-notification-icon[popupid="webRTC-sharingScreen"],
+  .popup-notification-icon[popupid="webRTC-shareScreen"] {
+    list-style-image: url(chrome://browser/skin/webRTC-shareScreen-64@2x.png);
+  }
+}
+
 /* Popup Buttons */
 #identity-popup-more-info-button {
   @hudButton@
   margin: 10px 0 0;
   min-height: 0px;
 }
 
 #identity-popup-more-info-button:focus {
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -120,16 +120,22 @@ browser.jar:
   skin/classic/browser/webRTC-sharingDevice-16.png
   skin/classic/browser/webRTC-sharingDevice-16@2x.png
   skin/classic/browser/webRTC-shareMicrophone-16.png
   skin/classic/browser/webRTC-shareMicrophone-16@2x.png
   skin/classic/browser/webRTC-shareMicrophone-64.png
   skin/classic/browser/webRTC-shareMicrophone-64@2x.png
   skin/classic/browser/webRTC-sharingMicrophone-16.png
   skin/classic/browser/webRTC-sharingMicrophone-16@2x.png
+  skin/classic/browser/webRTC-shareScreen-16.png      (../shared/webrtc/webRTC-shareScreen-16.png)
+  skin/classic/browser/webRTC-shareScreen-16@2x.png   (../shared/webrtc/webRTC-shareScreen-16@2x.png)
+  skin/classic/browser/webRTC-shareScreen-64.png      (../shared/webrtc/webRTC-shareScreen-64.png)
+  skin/classic/browser/webRTC-shareScreen-64@2x.png   (../shared/webrtc/webRTC-shareScreen-64@2x.png)
+  skin/classic/browser/webRTC-sharingScreen-16.png    (../shared/webrtc/webRTC-sharingScreen-16.png)
+  skin/classic/browser/webRTC-sharingScreen-16@2x.png (../shared/webrtc/webRTC-sharingScreen-16@2x.png)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
   skin/classic/browser/customizableui/customize-illustration@2x.png  (../shared/customizableui/customize-illustration@2x.png)
   skin/classic/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
   skin/classic/browser/customizableui/customize-illustration-rtl@2x.png  (../shared/customizableui/customize-illustration-rtl@2x.png)
   skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5779047366753cf26b867526e5ee4cd760af19b6
GIT binary patch
literal 15506
zc%1E<%a7Yc9LI+q5Tq=v1X3%6gzHj7B^-|(KVmn@HchhZQc_mi1+waa`Y}$dCU$B&
zO?K&FRk&0_6)Y`+3W**-;!r_bA=HWkr798!)IWd=a4Cfg;!&wR&}E#*ZZ>gNMf8Bs
zSh5}eX1?E#=Qj_}CC8`tJv$bAECv7=o0`ne;QQwAeBge3ed3R04c{KDPtN%OY<wg<
z?*pgb-wMF4mOWbvO2y|@!>z?N)74SDRjY&WNR7AZnsEpPl#a@_lcs<D;!B#c%{2Y8
zScJuT4pr>Q1rP0C*f(n|95NJ>9?!&5EfpKoP@qw*TGjE@R+?`6s`z&pW@)NjB{-C(
zvta>MDo#^5*F%&T7a0Tc93?ApP84OCe}>{AC$NxXxdg*0svxKkQr&~j#PBucnU*?}
zpXlQ7Q<|;>L0x6pX0sV@CgQGFW;sPsSje+H&)^!2Kko!ui*fwz9h06<9{Gl6*8|&i
zsPI|S-A0h6=@`{_blX*{7mF(>$M0qj=aFq`b(V`mwpL@KlzuRSto4=t?0g-uGst%v
zo`G=n?a@YsBf&p0DjY#h5NU;5v%XQ3$TU_o^@dk%C)zYvR7Ew+@v)AJ)YU6);QAGJ
z1ahT#Bsj3G5eW=SM#@mj9w7@$I<m~FF4gX0^##wy!(XfBU851UYbu|?1##0hRm+kj
z0VXU)5-o)h6f42#x?(a(L4q7FLzzz`Q|w5aAyfoHRwlBNlH{SFC>&Q1vdTmuDNFpG
z0?ctqKHIZj&vwf31I;l|*Ve|iJSQeKn1oD1kxfPrk;EvX1sR0IL{d|d2}$RB)_wBf
zH%6X)1ep__TWe34y5{*P9CK;9H|Y9P8VcLPU$uwd30V>pp~*9*&RdLNK#oaTCS(N5
zg0dj;oFU?D4#}h3&DCsl79SkIgq4w{CS@5V3{Jxs6R#|sEHfIEAfwBcjznG)Bwp*W
z%*fQTLkSqp&ZO-Jt~WpMieaAiqr+>Xy@INJmw8-iFJ1#w%?OvQjIZHW317P}Hd++X
z1%_^zQWDAx2l0X{@VdYxb<torix(wJlN8HDJ^xW%u!hmBASV;9;Y?^M&T^8<i)w<y
zk9Z~TS+#48Ht}66XlfqHmT@m+Iv<VcURP4N9R5h)kH*tQ&9I#?xU=WGRyZ19R5YiI
z%naKT_5wp)#CH1Vzu0$oH>Lkw-zAc2`|<zNcb(|k>DZgw!_zmM*J{o~8jjDpZZ*0=
zoUpb*gQ6%ul*i{lwxObmVlUeRH3vM3u4!BI5gu!LY@p_#2i~2TTA<yPOMl%yAJb4R
z*Q;qk#;$2)#MYg1id_x%)m&YRb8gl3@N1z=BE?39`&8lgJ^Ym7F`>1m2$y1qVuSTd
z;WVr^+GR2i@xgN8wJYr}9+b|lFP&RQx?1$2V+R?&D;Ws)slrW2r`7xM#v5fMQA~&_
zb}&4sn6_J}TFTq_6XmxvB6T%`1(CwJQNp<)VP~rJZIL_sZ??Mu$9sQvW%D03VekDx
zAOu1n1VSJLLLdY}AOu1n1VSJLLLdY}AOu1n1VSJLLLdY}AOu1n1VSJLLLdY}AOu1n
z1VSJLLLdY}AOu1n1VSJLa*rXg)xXms$4t{rdOUOel=>L}fHEd$iU2I^0ATSo0B$ei
z`_BM0AOOD{1VDWkfGzIQD_>9Gx+kad+1b{2-!7dWyR!SC!uLBq)VDnK&7&vJE}pw_
zVMFosr*C}qDD~V+S$*+F<>K|ly{~V)b*gdUgKNvXu5SJ9<10JmgKx#A%4e8APwXyR
ze;v5&-vW<seDlDa%}d9Z=yMz1x%_Y?cINH(#%^y~`uwt`-xT3d{_u(OH<z!L5O)c@
w`oo=@+de8ic`1GITIr|Xy~**tKXTyc__nim8pk$;{~Deu?8|?$=isrw0jROsv;Y7A
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6c8bd361383a573b54dd4ea783484c46533b91c4
GIT binary patch
literal 15584
zc%1E<duSX*9LKi_sJSNfQJ<~YWlJGS_x62zyWU-!-d#+vmxeUbL@0J{cQ&`=?)Kc>
zB$xalX)96%6-q^|r4$R&{!^v?QEUTk1q;?H_)pa$wF-)l;;Til2I}lRlFQv*sMtS5
zW?^q{e>30jXXZCMv-8hB-{1FGM_@&OAc&6Mp2PsUlHPgSE$G*on!1WEZH1m;hai?N
z_s%86-hKBI#G^%XaL64>ZB#WY7X-SMhQVU4KzK)FU9kYPQRtFsIAZ3b)GsGaQlzOz
zsV!29O%>uWWA;qhaPw5(pf)wCDLS<-7Kjv8WRQa{Ad9(d-cgHDs^qJp|6Z7($Wj$|
zG)i@O1>{hwpNv~JB&DE4Yb?)^vJ&JZNtXGwB+qgp!*UEK(43-*qRO&l<)C5#^c%5t
zLmfzTS8(VqN@ZNPpfb$F#6)mH2wL_C!zqfwusp-_G^#;6lX({u>AbVLY*O_}Ku5F9
zf@@lN(z^#~Ys`&ORDi5GD*eh8QmHvq-l=2{<&i0Z0>cGaCYNLUl#V+9jm4GD;A8<Z
z1JJR?Yz?C7tNo3V<L*B(N{++4>uZI2v$&C;NY~~xg)uu@O0=#qFbi{t;~*X9t1DzI
z*K#se6XaZRQ?P3qO%mvpG?l@k*+k}*lx4AbU8FR`>L%Mnk3YyJEN#r|S8pPQ3WB<+
ztA-H@iL7AIA<0l^Q85HMomO-@EQVN)msyz?!V#vaO#{jYA$AMlq`)hzs3;tl6uXq}
zWLOUI8<K3C3-eu7`^9W~^NtJh8m!ow$QH0bm&CM2iz4t^4p5^7U8jX~niaz`1eye^
z);03R7DL+{hkCbd<w_H#0Bi?(&s>zMK6JGy^@OFzUo{)s1dR|Yu{uxdY2KhkjpgXD
zp|iAT7_2NxJf}%0n+<Y5cYQt^<;4dD5Ml1gQp2(g1&sp;qob9DlVuvPA(l?dMjA>y
z5JNnuvUK0nG7Sl6%ubK#xRyOx*F-N*o8i+7qt$|}d4qYJ>n~aZRG@iFR?GpYDPFT{
zV$DT9Lm+dq06<!#MPTT(sB3^$0H3D$w8D#8T2WY;tNJ$^f(49;49v&8HJoN;nv({(
zkjhIcA40ciC9hhQYBe`;EW@1uHtZTfqYx{<H2SMUNyg*oC4pWVohhK1c`x{I)pwz=
zIY7&R{0P)zOjTG7Gz<|_9;5%_*xlHa{&!<1$f_9rUyWTkx^OyH=XT@t_2#u2w;@3B
zLCebecZd@gcBo$D=LZw$tjjj5sDe_>c3sUnkCdgG#-xwOf)T5$S?_^%XF71f4Y}0T
zt?|({)v)XwaARf;j6kN4ABiyY!J3-$YjNDlS~hAHj0q9OFI=PY-tXS46g?B5G)2&I
z)DWw$Kjclr>{zKx=2>(wocCLi))v=GhZmO)FCv{UdLnPSF}@<H3)iT;O-Q-do6*J_
z#zK-LND-z!T(9Uii!eKsFwrZ@@!BEq6-|ACuW-1TaJWHOo+>q4<nsQTscgW}-k+J<
z{QFJVn}1*khF}PWU<ig_2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_
z2!>z?hF}PWU<ig_2!>z?hF}PWU<iiXWJqBCb6S|!qtpboE_S7J=g;0(x?0abiXf)e
z5XAIOg81_~y8b{AV=O`ZxQ!sx*9hW1Yxh$hbfdc8^d`Cnix)1x%DvFibw~26T?e|)
zTs)m<|2TBz!80p9eD0aEpRaFkzc=zCmC-M?-F<rLTB=jy-)FzrczfuhKVIG&xOVc$
zkw=an48Qc-;lTcTh?e!zO7`8@J1tKx`|INNRlNtP2MW@yhnB>JpT7NM{n^>JwcB$?
zgKs)_P9OVj_nB31Z{KtN@bN>fTMvAFY3vpET<fnr=T@G2D05=}`EzgW-gB(u<XLvp
z)vfw6@$s48r(Sz@2}#VhO}}AHzrKa&U4EeT^32SxyLQFD-?67}&9$vgYtIE@>7~y;
moo#79db)GA<9hVeC^5c+`*QVr`)0gvkb9GTi6a}f?f4r@>i)z4
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3a2daa3eacbf5fb6f86c8a5355eb162d5f564103
GIT binary patch
literal 15965
zc%1E<Yitx%6vuC&yjDxvsNvz4$u`I%%+6zHc6KJaLrb^V61LP*VCxr{ow?hN-QC&l
zOxtaZ3B?ym#00_ejuI2~gAx-%P*jiv6a9dM5TZ6lh>50Xf+56!3E-W5wA<a;N`%Ci
z+(~A3_n!MdzdPsLduHy3eWkta$r{&U7XYB9wI$SnJ{#<FZZ*1st)FZ}*LkUyE)#%<
z9<k3VaNy8F00QUq&WIHWuai_G;ZZar3O$)b$`*mZs!U2zd!a={VYi+Py04u)<tB73
z=-$kS>2RtE_UJ9cX}Dpyty3NDRYlFcN_GV@5^|7$mO^9_@uVqbg6^ze34OPlDL0Yr
zV)X{yjkbY^gxiTGBMk}O!;>n_GK3&{7@ijdb|t~m3`fxn#dt|ZlsHbJX(E5PWf!^z
z(ppUF2(8KE(66Ao$FfoqMGX!PdIr57Bi&6gq9{@{OR+48dXVN&(o!;H(yY%pDfkIN
zQ%&nBOE;2){ac9|{Z`QJb`eEK{=O2a2~g6^#|}l2$|xy{@z9i$NK+>?ss404J2XwD
zU>qi3(lU{banz-H49hTkj0(sJb44&3Z8$vrcoPXnrD-(}Sko)b&Y={fI-qIvr&ZWI
zfNrJU$(5zYV$PmyOU1Fnj9x)zTXHgaQWrq?DmA5{Vj1a9!-&hdS=L^N5z*9?omIrL
zu%hZoyK#AdDit~TgCTUvNb4nOhNQinv|r+V5-pJE3r!a}$@MJp5jHeEHdOGzkx0kz
zos7?()hzNSO!z43S?U9=AWgB9d-77;x5$sCN--myP%K$bDBX}sCA$OEWMfg!iFqY8
z8F3?xh6QCNKshx#szQd^Z%^#TqzX+GSyP^p-t3ZFt#nUqEg0guoGTofq3YV?5|I3Y
z0KF=skesTas=x>Wsn9-}j0&+R<XMIDu}Xl-yQt(E1<0czw?1#v8rH}9IWNmHOf&2E
z`&qx}=Y?j@=Wl9U<LoHcI(6v9ahdMM1KG7GwI)qVLFU#_b}Tv?3(sk>XjJu)kckN-
z$NIda7}YpZ<V8VL7*5ngm`&|ed70}19od{C=!*%0=+!h7!I)Yxf@RLiK<m>wdNL^S
zqCSPVdZT_(<u!Xk@==nbHJ((^ZxX^N3Um~*G1gZ=IJhZ8wdi&nha4u*2H00A%h?*9
zQqm^0D^$>3C{gY*fvw3_I!RIO>MyI=Wvm6LDXo=ddU-nUEd*uq%8bDtn4GTs6!og?
z#57{oppu4--6+z|wGlmmr_@F#VOhlgVZq%;DokbI{u04!kXE8-NeM9#tt+qSC6yS*
zk}RaXwBN6Cd@TB}5>cu8{+EfE1gJeqvKwkLRS*^${#UuXr<#a!=k$D<S{Vz~w!D%C
z?AI^53ZpS8*%YCNd|6v*|A;*e;{92hz|!cT82g%+7Mn|@UDHdurjbsXHYRmTX7iGg
z<|38-j+lE-*?_h)Q1<b>mk&^-&83QVJp<#BkdB^5racZHm)DdU9EDw#gk5FAT&jvT
z9=Tl&mEYE&9ThdPrE;1u5B|Uq48af#!4M3=5DdW(48af#!4M3=5DdW(48af#!4M3=
z5DdW(48af#!4M3=5DdW(48af#!4M3=5DdW(48af#!4M32z!2BupOj%z3%Up0tK>KS
z9RCaeK&UMpVE~4g0I=<O0B+qypW^`Z(*RuE3V^f^fCa|xEoavNP&KbL)YzH%;rH(w
zUEAAhXJ5X%w=UHGg75Ir`q0ixADkRnx?_I(&gA){^&cn37Cn9O(`KS&<o)lSxG@rW
z{W`P#?uohu=Uz4r{rc8g;_;f=hK3)j+6JzAm=(A0EIl#z`sn8I{onkxt@qs}5BJ@v
zK0kU{pLL!&HgeiU?Dm{_v#;)%zO!}nFaGrP!5vp_&)9c`7@Om{@Y;J@zHN)tetyCH
zDzk6qFOkvtb3gj>%%k-2KdQIsvu|#id1UX4r}i&+`*6*QD|6;mRo6Cv+~u)Z%k+lL
z2Y+h@&)&RNzh_a~((#1@qX%|9_r*)qwe7)%v9*_ui>KSxuX}azQ}V#VJxAm-o5rec
i{k$8@c&F-E6<E1zdE&;RlZ)*?fVVccg+5%pb^G6eYGF_S
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..caeedeb80e774936d05d08e035e55f1a2c522559
GIT binary patch
literal 15148
zc%1E<eQXnD9LJvt2o6!hP*FlS4oF<W^<LWRwP)Ko+Kp{koTJ7rny~BL)9qY)SMF|W
zHw{kFM8h8@Xw)dm9~vVDi7(MeL<A8NgGQs6_yS>nxF8Cm{DCo`_*`GMzFZqc{(PR~
z?t0Jj`+h&qJuknzfBJau`n3z1Z*B$vEa>iv^r7p7eaxGU&Z@BUHgvfv)75VRFu%n<
zn!t107X#q$QDbo{9=%<Z^|VV;^aOO}(ivcvKwB;&$-~eh5^zw}g5;U^KO_lN36gj7
zQ6`!R!y&b6+<+U#*T>}XVcD;cZK38sPDBQ2Xh}paozhG(7bNq(BKmDt(<G4(v4(?W
zyWK#<qrF5}Hz2{gcuHnCmJs|dmgfb5TTO5b>!BHzX5AF)7d;-4VTfWOL(S+MFqEX&
z7wIhG&|Q!mvaF0q)7fm+m36yxV~}S3em~7{G{;dWf-=W6OUhB2xuRfF@`*rGHq?xz
z>Kb9+O9_3%3X)_qQC5o2mCj6qG_yE%XcXz3l%ZJ{LpzBSd0LYhF;e-iDKZUHFby@!
zL^{?{ml@J6-5k>EAg7z_f>Cdy(Z>BvryZ51)iG){R+_P~45a&@sgD>k>=;Flvck#A
zQj<w%C%dI?-(gOzBePoyve1k!fSy$h8&I-zBc|)AP+^tzmd1z(hx4n7xFssds%BTV
z2Iy*$lOK$rBZRbWieV|n9b<eV?-dz=LO&R$%%l)m<q_2tH91!D@K8v{@-fzHuWFt{
zx6>YFk<}h(2Pu*z{VSL9*fJkQ7L&S>maLGPmIfi6(FOzbOl4W*^tuwmdP+A?w_u13
z&`#Bks)#O+*bBQ|lc9-5)(lllZ+^?Ikp?SkOARSCR7f0(F00DS77%@c0NpYxQ65=A
zS%DP<N@BbWl@O8%$a9j%%Si#cXi?7^4UkJgwqAF*lk;*ukDKFIwuAHee4Nkk<An~7
z*B5T@bT%r~R(8F7TpB%iAio#IZq2kL)Z87(_eDj0VObCBV-r4#bwh^oaJ~fP6F7n5
z880jF9>%YD-TA4ll-F37=tXPgKU$Z`Ypm}uRP-*8QvVa-U{izYQtgo&b67GP;HG->
zn$Ox9$uOavlY(R^DUme-Ta(Xiq9oh-J|yQit`eXtYU|6?OP#<Pd3{E92x^TIPFYla
z6H`xGS;>IygJ`6kvmtutR%AmbVa<p)*cp{GRGRhL84cKp-(C#}Amzs!rI(tz>iD=l
ztErKEo4_%s(5!tfO3Rz8rTvYi{SBluO?PRk72=ALs_HV8{dOv3$PMU23WmHq@8$z^
zb#=9(SIxmxJffm~Vdh7K^A$zaEso0my2}0<Wnr?)K6(mYRCMu^3VqGd)1Pxr3+(b2
z48af#!4M3=5DdW(48af#!4M3=5DdW(48af#!4M3=5DdW(48af#!4M3=5DdW(48af#
z!4M3=5DdW(48af#!4M3=kjo5dp810$)RZ8ZCEG#^_qs;`0EFDt7X@H^6#x@k0QmDF
zx}F4JgaP2^0RY5j0k~1$w((FW08RIHN7`e#?@t|Ldp_=6H0R4DZ@#_gk@nTG4DAZf
zo_}~il3K|dzS}>!@YD<Zd$YD4=v=h!z(+mvH@!BryyepqJAPQXvi0mWXWBNmeD%P~
zzwVk1D$0>(zS+0+^yb~}*2&YyjTa9eUv~2EBTewB9lNH;`@^dr>SmW7RSsTVjDC7g
z%Z25_o+Z+pYcI}WKELzWy2*FX-SnF_?}|eE_4=ddjcs4=dScK1bH6ZOY!dF-JU4aX
zzN1t0TTh<6Zry-8%6_m9UI{L(yt;UD=jg<i&xV_R`b&G`dHBcqz5Fbv!1#}=rY^jG
yW^CTX`R!BhJa%@O`R#)*{eG~cGOBlDXfF8V)}@Em#$P#ZS9Eu*kL+GE@bEu=j$Lj5
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6011caf90748782775e410d2241f9c1f3226017c
GIT binary patch
literal 15506
zc%1E<Ply{;9LL{UsqMN|(MqES9jCTd=w#lT%%9AdWV=mv-G!zu+ZA_D?)-U^3{7Un
znb~f3PgOx%6bj;@h(b}1QW1I(JXo<xFA9Qq5JUwjs0WdHQN;C4lG)8Blci!0B5z<a
z$?v`I_w(NG{h4>k+1cZd?-{*+6acViX1X#*o_EFfuASub!RzZfdF=M57efGUzbC#&
zz-zB(0C=qB%r8U>)nl6JHM6?q893W&`XIgv<1Jq|PvVF%aLsXx>@OdD$TE&qWS^3%
zu<Dm_-I-ns@QJnK^XA$~Q?=Of(rBTj5rZa<bf(p8xS`f6vTa|De2>FC%e1RRCyVSv
zT)-?;XPL4WU`EPHoCyWQC~6i-lA;KQ837`Zhlocx4yl?bY7jEri!F_k&q83?+FWI-
zizB~^Y(0v6jptWZR<bL(tQXXHq^c?p1zr$1QiBUu-AHe7Zn(c=lK50`Xa<fSIiAbJ
zzjeb~j*2Wh%Jf{_el`7Sbpz#w-RzM(@-5xxQ5N#eCZDDZqd9DEuMFo`eaz3{&|3~n
zOselsH<~;Z{S%|fQ`n7Ctw?XSH%b#(=7z>!4jS!5TPBYixJfu6(V<kGU-u#}tb0R{
z8^uGxkz)@@U|cd(hFi`MSzOYQl{R&S_7H1N1P+=0dZXf*%W=PEDkV~owH!;cZCMs!
z&gNvvRyk3%bDUwQ7MB-ghy(>HLM~t6huRFFQV@zdH6g2c0g9@M(4;t_PEF<&SvWcg
z%P22QB=+0c&bVQuyC&}1I>c7Ap_Nmx$w{V><3z)RoC?Vr%iDsa8U_-O3=``f`CyB2
z;GDwNRNyt+3&z)j5XW<_$R?AnH>I(#J^eLjuua&Op#&{~vkbxJL=z&?91C)yZ9_$r
z1Y}Ain*;JRcWW~noy~^?2w`JpX?aD#ITPswW09Q&DGH}U8FGeV8(0!_Q5N)sWu>N;
zA4tGpcBY*$@`BaACdPR>fuGqLO$r*$4d!v9zhn>4bTi(vN}*1g5;waiHe8f41g2qH
zavmxiiYJ387$TQ9B$GpATFAC8tG0y`|6xP0g|Sk{ZYkcwx!6>iN3teJS`Lw)WG7Fo
z+O>w8gq|I(=mDOnkx?jhUK+E>P%`B*c}b9$#-XZiI&K_%H1XXk91bw+x?96miBE({
zU|@*&&KUg{$L_|K^uHUsocezoyH0fLa!l6t;PQ>vwN?(WPU7>P*GTUW61R4!UzFyD
zE9CCWHdHl9O0wNov(KaIS&qG$;<07K`fB!jkkgr^NBRx9^w#b1u}sbOf~FpooTgsG
zyzkZu{ARGH=H^~p_8MM5nuSZb0-qM{QN{1~_*F{ggx+2vsK5`z`s**m%doNBE>i?Z
zE*{07U1@J|zjSeX>Ebrh&7vn=Cn^bDNnf}}6(2%6y*@z>-nb-7Qcf!H{o#Jatkc4c
zg^EL7QDHkHa#zz|kSbgpCR`j4c9u%d5xH~z=DP=Qa`xvp4*zKj_U2y{LLn4FArwL(
z6ha{sLLn4FArwL(6ha{sLLn4FArwL(6ha{sLLn4FArwL(6ha{sLLn4FArwL(6ha{s
zLLn4FArwL(HyJXz`FC3ET19q+9WR~BT(|}RV9e>cDgbK-066_T0Kc!3=Z^p^LjZnS
z0zf+tz&`Jlr$3n@bzhySOw6~wHvU{+AK!7{^x0>R9(ix%u>Y0#>BnPZV{+@uzwpx1
zq1x3yzJ2K%aOT<_J9oYywZ>Lo%wFDg=N0GW_4_iJ-!hrZ`{Wsrd*RnibmpO-*Y`&I
zE+3J1>?rRYdG|I1;jxeIJ#*#O!`Ht%J9Y3<{mrYVZy`qKet6{J`XzYGd^<n(`Q6{Y
i@z(j>&IMxxg4Kf$yz>RK_*wj~;hD+fm5WE0p8FeGi_Osh
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bac3900bcddf2e83570eadf454a6cda4f4f8fe58
GIT binary patch
literal 15585
zc%1E<TWl0n7{|u~Dy3W!Ov>ef%>q`Vvvc3s8Fv?#-L0*%ZPONQBSy~7oas*3omppQ
zyW7T)Moq*A3;~1&Z%qV4d?3USeNeCv#i%bNK1on38WNR=CPu}C=$XB=+r2axc`)Wo
zGP^t9IsgCfobO!byzC2oJr6B!UDrww#PaU0SU>vqxc8m6qfdK${8#j~G}|?76U4IB
z?tL3^<dro9@jyWz9CC&d+hoPe_<(9Ap|6n167Ch;Qpf^j7j!&HIHDUN@3r?n@OpGL
z<b6y?(1~mmru43H3l5C;3@YQh6iM}N3AY9dGBU_O2Y3pZv|-DIkhkb7qu*|r@_LF@
zoLwPr#4Yd)CHg#3(}EtsCy)xwG9FR#F@hk9?EM~=W_XHbD25{$N#=Q(rak4$8*W9P
zK}*%-{#a)jhn_;-l;dP&ipuBnzC7nMtr3cmB#EL~ie*VugS5vC2NX!d-dHlJ_{5;C
zSbEmcO~d0pgQS^rLSApHr|K&AE0awmW>JP+&K}AmRRCFv@zGQ!Lp4&`PCwKZR@#GO
zSxEIm+ss)CMAbJoHj0lr|HLRh3Js^B73$5xMvX+OGONkvtaLHaszSju%pi`9bWB5C
zHf1`doidvsXN#MH9bIdZK)0l+3>NezGPk583(x6-#UYk^EgemNkdB#3&h1xsEQ|_#
zs;<hK=J)e7r;&a^lSp3DI5L@(R5HN(X@(VPk>vtGs;NyKssV!U<N|Sym1tg)7$(j~
zq|SIi^s`&zbd(9Ok&697w%vy907HRgTOHYQEG?*_Dv_L@Q%Igq@}v}CAt`A*kR(my
z7*45JSIO&J3@v>Ws-2dZDK3~Sux#kgxsbOq>8exe3X9WU*6Z5@H9sxTDod(KRwH?Z
zX2^i1(j>2Gw8#rAqX;OQb@E2;>RdKTn-2;g!tBhF1EL5yg#ieoqMd~iMH0|{noNpX
z5(+Hf{Vb@k)P||0>Jm_&oi5#WOlz#BiEf?-;8XLXm4dW>lX;x&FWLiSptxIB*aoO6
zZnLXm%|#7{Kx9M?05lnRps6IUDu9##n<Uw!#PUi~l4y~s_%|DZd5nAt8ew-2Cuxyn
zgh9qHvx3a}(IeW)D^|r?%}s1mbMnA~kr6Zs;nGW^uQHULXcWC9&`aZ<1W<It4c=Sv
zoiA(-P*T7cfohnl2rGfQA)-oS^j{pin_JTVZtOToX8vDeSBlPGj+M1tzkJ<wEk`W~
zP<+rd(~UdC@bf!VD{ABiW9Y8QHZ7rqSF&AGv&JJ~s=7ATz+>Ks)zqx@K&LYmIN+vS
zs_RzysEVwaRt7j>Jp)D{l{H3!)LgKt=G<N!HPfbrngzpLkZKgJQn~MU_f?AK1Sl>M
zv>nyOYU>ZV%P^fQmWeElE{btK%hKxNTIuk@(%}W9b4A+?-3ha0Nlmy)<sL#xy&gaZ
zZy5Fq0w)Bi+HkF+PcOjqP)tX!DBEoZ$Cfp<1r3G6&4j~s!qQTyIwF_O-&FYkj?Vtn
z?BT!Bg1z+@hF}PWU<ig_2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_
z2!>z?hF}PWU<ig_2!>z?hF}PWU<ig_$SsDn&i$Ph8fwU!_ihRAJxa8=|I$^u`V#~(
zzL_8<o+XIuf1&U12_i=m#1A_OLY^duc60xeA9bR--*(3$gN2J%51np3N8i=i`(dPG
z<m<EX9j{(`dd0hKJ(rKqT=lG2yZH6<8!miuwxjRhnTb79JMLIUI{PM*tL!)XA3k&7
zt4og@+%t2a<+Y_Pk*UcsWpMrEqV3P;%;`4r%lwA7wyj^68*S_TJoeeAKds#K&B|%w
z$3L#Ge(|@>U6WJCe!s!(JK6QpvBmtv+tEj#J9K*QbZ7;&=<%s5??3TIXKL-mcffsz
ze{R3gd*#8+AMZbWZc|%JWTs>Bi8D*i?_P4MZ9+czi@kBY`|8&Aqk-=x+rL<r8g3bw
oxx8j~i+qhb23Fm8dD<rK?qE)NFC72V{S$I`yeD>I>&|EX1|q)tQ~&?~
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2208,16 +2208,21 @@ toolbarbutton.bookmark-item[dragover="tr
   list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
 }
 
 .popup-notification-icon[popupid="webRTC-sharingMicrophone"],
 .popup-notification-icon[popupid="webRTC-shareMicrophone"] {
   list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-64.png);
 }
 
+.popup-notification-icon[popupid="webRTC-sharingScreen"],
+.popup-notification-icon[popupid="webRTC-shareScreen"] {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-64.png);
+}
+
 .popup-notification-icon[popupid="pointerLock"] {
   list-style-image: url(chrome://browser/skin/pointerLock-64.png);
 }
 
 /* Notification icon box */
 #notification-popup-box {
   position: relative;
   background-color: #fff;
@@ -2348,16 +2353,26 @@ toolbarbutton.bookmark-item[dragover="tr
   list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-16.png);
 }
 
 .webRTC-sharingMicrophone-notification-icon,
 #webRTC-sharingMicrophone-notification-icon {
   list-style-image: url(chrome://browser/skin/webRTC-sharingMicrophone-16.png);
 }
 
+.webRTC-shareScreen-notification-icon,
+#webRTC-shareScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
+}
+
+.webRTC-sharingScreen-notification-icon,
+#webRTC-sharingScreen-notification-icon {
+  list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16.png);
+}
+
 .web-notifications-notification-icon,
 #web-notifications-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-16.png);
 }
 
 #pointerLock-notification-icon {
   list-style-image: url(chrome://browser/skin/pointerLock-16.png);
 }
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -90,16 +90,19 @@ browser.jar:
         skin/classic/browser/notification-pluginAlert.png            (../shared/plugins/notification-pluginAlert.png)
         skin/classic/browser/notification-pluginBlocked.png          (../shared/plugins/notification-pluginBlocked.png)
         skin/classic/browser/webRTC-shareDevice-16.png
         skin/classic/browser/webRTC-shareDevice-64.png
         skin/classic/browser/webRTC-sharingDevice-16.png
         skin/classic/browser/webRTC-shareMicrophone-16.png
         skin/classic/browser/webRTC-shareMicrophone-64.png
         skin/classic/browser/webRTC-sharingMicrophone-16.png
+        skin/classic/browser/webRTC-shareScreen-16.png               (../shared/webrtc/webRTC-shareScreen-16.png)
+        skin/classic/browser/webRTC-shareScreen-64.png               (../shared/webrtc/webRTC-shareScreen-64.png)
+        skin/classic/browser/webRTC-sharingScreen-16.png             (../shared/webrtc/webRTC-sharingScreen-16.png)
         skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
         skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
         skin/classic/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
         skin/classic/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
         skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
         skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
         skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
         skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
@@ -499,16 +502,19 @@ browser.jar:
         skin/classic/aero/browser/notification-pluginAlert.png      (../shared/plugins/notification-pluginAlert.png)
         skin/classic/aero/browser/notification-pluginBlocked.png    (../shared/plugins/notification-pluginBlocked.png)
         skin/classic/aero/browser/webRTC-shareDevice-16.png
         skin/classic/aero/browser/webRTC-shareDevice-64.png
         skin/classic/aero/browser/webRTC-sharingDevice-16.png
         skin/classic/aero/browser/webRTC-shareMicrophone-16.png
         skin/classic/aero/browser/webRTC-shareMicrophone-64.png
         skin/classic/aero/browser/webRTC-sharingMicrophone-16.png
+        skin/classic/aero/browser/webRTC-shareScreen-16.png               (../shared/webrtc/webRTC-shareScreen-16.png)
+        skin/classic/aero/browser/webRTC-shareScreen-64.png               (../shared/webrtc/webRTC-shareScreen-64.png)
+        skin/classic/aero/browser/webRTC-sharingScreen-16.png             (../shared/webrtc/webRTC-sharingScreen-16.png)
         skin/classic/aero/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
         skin/classic/aero/browser/customizableui/customize-illustration.png  (../shared/customizableui/customize-illustration.png)
         skin/classic/aero/browser/customizableui/customize-illustration-rtl.png  (../shared/customizableui/customize-illustration-rtl.png)
         skin/classic/aero/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
         skin/classic/aero/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
         skin/classic/aero/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
         skin/classic/aero/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
         skin/classic/aero/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -73,17 +73,17 @@ DEBUGGER_INFO = {
     "requiresEscapedArgs": True
   },
 
   # valgrind doesn't explain much about leaks unless you set the
   # '--leak-check=full' flag. But there are a lot of objects that are
   # semi-deliberately leaked, so we set '--show-possibly-lost=no' to avoid
   # uninteresting output from those objects. We set '--smc-check==all-non-file'
   # and '--vex-iropt-register-updates=allregs-at-mem-access' so that valgrind
-  # deals properly with JIT'd JavaScript code.  
+  # deals properly with JIT'd JavaScript code.
   "valgrind": {
     "interactive": False,
     "args": " ".join(["--leak-check=full",
                       "--show-possibly-lost=no",
                       "--smc-check=all-non-file",
                       "--vex-iropt-register-updates=allregs-at-mem-access"])
   }
 }
@@ -597,48 +597,50 @@ class ShutdownLeaks(object):
   def __init__(self, logger):
     self.logger = logger
     self.tests = []
     self.leakedWindows = {}
     self.leakedDocShells = set()
     self.currentTest = None
     self.seenShutdown = False
 
-  def log(self, line):
-    if line[2:11] == "DOMWINDOW":
-      self._logWindow(line)
-    elif line[2:10] == "DOCSHELL":
-      self._logDocShell(line)
-    elif line.startswith("TEST-START"):
-      fileName = line.split(" ")[-1].strip().replace("chrome://mochitests/content/browser/", "")
+  def log(self, message):
+    if message['action'] == 'log':
+        line = message['message']
+        if line[2:11] == "DOMWINDOW":
+          self._logWindow(line)
+        elif line[2:10] == "DOCSHELL":
+          self._logDocShell(line)
+    elif message['action'] == 'test_start':
+      fileName = message['test'].replace("chrome://mochitests/content/browser/", "")
       self.currentTest = {"fileName": fileName, "windows": set(), "docShells": set()}
-    elif line.startswith("INFO TEST-END"):
+    elif message['action'] == 'test_end':
       # don't track a test if no windows or docShells leaked
       if self.currentTest and (self.currentTest["windows"] or self.currentTest["docShells"]):
         self.tests.append(self.currentTest)
       self.currentTest = None
-    elif line.startswith("INFO TEST-START | Shutdown"):
+    elif message['action'] == 'suite_end':
       self.seenShutdown = True
 
   def process(self):
     for test in self._parseLeakingTests():
       for url, count in self._zipLeakedWindows(test["leakedWindows"]):
-        self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d window(s) until shutdown [url = %s]", test["fileName"], count, url)
+        self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d window(s) until shutdown [url = %s]" % (test["fileName"], count, url))
 
       if test["leakedDocShells"]:
-        self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d docShell(s) until shutdown", test["fileName"], len(test["leakedDocShells"]))
+        self.logger("TEST-UNEXPECTED-FAIL | %s | leaked %d docShell(s) until shutdown" % (test["fileName"], len(test["leakedDocShells"])))
 
   def _logWindow(self, line):
     created = line[:2] == "++"
     pid = self._parseValue(line, "pid")
     serial = self._parseValue(line, "serial")
 
     # log line has invalid format
     if not pid or not serial:
-      self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>", line)
+      self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>" % line)
       return
 
     key = pid + "." + serial
 
     if self.currentTest:
       windows = self.currentTest["windows"]
       if created:
         windows.add(key)
@@ -649,17 +651,17 @@ class ShutdownLeaks(object):
 
   def _logDocShell(self, line):
     created = line[:2] == "++"
     pid = self._parseValue(line, "pid")
     id = self._parseValue(line, "id")
 
     # log line has invalid format
     if not pid or not id:
-      self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>", line)
+      self.logger("TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line <%s>" % line)
       return
 
     key = pid + "." + id
 
     if self.currentTest:
       docShells = self.currentTest["docShells"]
       if created:
         docShells.add(key)
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -16,21 +16,23 @@ import mozcrash
 # signatures for logcat messages that we don't care about much
 fennecLogcatFilters = [ "The character encoding of the HTML document was not declared",
                         "Use of Mutation Events is deprecated. Use MutationObserver instead.",
                         "Unexpected value from nativeGetEnabledTags: 0" ]
 
 class RemoteAutomation(Automation):
     _devicemanager = None
 
-    def __init__(self, deviceManager, appName = '', remoteLog = None):
+    def __init__(self, deviceManager, appName = '', remoteLog = None,
+                 processArgs=None):
         self._devicemanager = deviceManager
         self._appName = appName
         self._remoteProfile = None
         self._remoteLog = remoteLog
+        self._processArgs = processArgs or {};
 
         # Default our product to fennec
         self._product = "fennec"
         self.lastTestSeen = "remoteautomation.py"
         Automation.__init__(self)
 
     def setDeviceManager(self, deviceManager):
         self._devicemanager = deviceManager
@@ -179,116 +181,142 @@ class RemoteAutomation(Automation):
 #TODO: figure out which platform require NO_EM_RESTART
 #        return app, ['--environ:NO_EM_RESTART=1'] + args
         return app, args
 
     def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = None):
         if stdout == None or stdout == -1 or stdout == subprocess.PIPE:
             stdout = self._remoteLog
 
-        return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd, self._appName)
+        return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd, self._appName,
+                             **self._processArgs)
 
     # be careful here as this inner class doesn't have access to outer class members
     class RProcess(object):
         # device manager process
         dm = None
-        def __init__(self, dm, cmd, stdout = None, stderr = None, env = None, cwd = None, app = None):
+        def __init__(self, dm, cmd, stdout=None, stderr=None, env=None, cwd=None, app=None,
+                     messageLogger=None):
             self.dm = dm
             self.stdoutlen = 0
             self.lastTestSeen = "remoteautomation.py"
             self.proc = dm.launchProcess(cmd, stdout, cwd, env, True)
+            self.messageLogger = messageLogger
+
             if (self.proc is None):
                 if cmd[0] == 'am':
                     self.proc = stdout
                 else:
                     raise Exception("unable to launch process")
             self.procName = cmd[0].split('/')[-1]
             if cmd[0] == 'am' and cmd[1] == "instrument":
                 self.procName = app
                 print "Robocop process name: "+self.procName
 
             # Setting timeout at 1 hour since on a remote device this takes much longer
             self.timeout = 3600
             # The benefit of the following sleep is unclear; it was formerly 15 seconds
             time.sleep(1)
 
+            # Used to buffer log messages until we meet a line break
+            self.logBuffer = ""
+
         @property
         def pid(self):
             pid = self.dm.processExist(self.procName)
             # HACK: we should probably be more sophisticated about monitoring
             # running processes for the remote case, but for now we'll assume
             # that this method can be called when nothing exists and it is not
             # an error
             if pid is None:
                 return 0
             return pid
 
-        @property
-        def stdout(self):
+        def read_stdout(self):
             """ Fetch the full remote log file using devicemanager and return just
-                the new log entries since the last call (as a multi-line string).
+                the new log entries since the last call (as a list of messages or lines).
             """
-            if self.dm.fileExists(self.proc):
-                try:
-                    newLogContent = self.dm.pullFile(self.proc, self.stdoutlen)
-                except DMError:
-                    # we currently don't retry properly in the pullFile
-                    # function in dmSUT, so an error here is not necessarily
-                    # the end of the world
-                    return ''
-                self.stdoutlen += len(newLogContent)
-                # Match the test filepath from the last TEST-START line found in the new
-                # log content. These lines are in the form:
-                # 1234 INFO TEST-START | /filepath/we/wish/to/capture.html\n
+            if not self.dm.fileExists(self.proc):
+                return []
+            try:
+                newLogContent = self.dm.pullFile(self.proc, self.stdoutlen)
+            except DMError:
+                # we currently don't retry properly in the pullFile
+                # function in dmSUT, so an error here is not necessarily
+                # the end of the world
+                return []
+            if not newLogContent:
+                return []
+
+            self.stdoutlen += len(newLogContent)
+
+            if self.messageLogger is None:
                 testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", newLogContent)
                 if testStartFilenames:
                     self.lastTestSeen = testStartFilenames[-1]
-                return newLogContent.strip('\n').strip()
-            else:
-                return ''
+                print newLogContent
+                return [newLogContent]
+
+            self.logBuffer += newLogContent
+            lines = self.logBuffer.split('\n')
+            if not lines:
+                return
+
+            # We only keep the last (unfinished) line in the buffer
+            self.logBuffer = lines[-1]
+            del lines[-1]
+            messages = []
+            for line in lines:
+                # This passes the line to the logger (to be logged or buffered)
+                # and returns a list of structured messages (dict) or None, depending on the log
+                parsed_messages = self.messageLogger.write(line)
+                for message in parsed_messages:
+                    if message['action'] == 'test_start':
+                        self.lastTestSeen = message['test']
+                messages += parsed_messages
+            return messages
 
         @property
         def getLastTestSeen(self):
             return self.lastTestSeen
 
         # Wait for the remote process to end (or for its activity to go to background).
         # While waiting, periodically retrieve the process output and print it.
         # If the process is still running after *timeout* seconds, return 1;
         # If the process is still running but no output is received in *noOutputTimeout*
         # seconds, return 2;
         # Else, once the process exits/goes to background, return 0.
         def wait(self, timeout = None, noOutputTimeout = None):
             timer = 0
             noOutputTimer = 0
-            interval = 20 
+            interval = 20
 
             if timeout == None:
                 timeout = self.timeout
 
             status = 0
             while (self.dm.getTopActivity() == self.procName):
                 # retrieve log updates every 60 seconds
-                if timer % 60 == 0: 
-                    t = self.stdout
-                    if t != '':
-                        print t
+                if timer % 60 == 0:
+                    messages = self.read_stdout()
+                    if messages:
                         noOutputTimer = 0
 
                 time.sleep(interval)
                 timer += interval
                 noOutputTimer += interval
                 if (timer > timeout):
                     status = 1
                     break
                 if (noOutputTimeout and noOutputTimer > noOutputTimeout):
                     status = 2
                     break
 
             # Flush anything added to stdout during the sleep
-            print self.stdout
+            self.read_stdout()
 
             return status
 
         def kill(self, stagedShutdown = False):
             if stagedShutdown:
                 # Trigger an ANR report with "kill -3" (SIGQUIT)
                 self.dm.killProcess(self.procName, 3)
                 time.sleep(3)
--- a/build/mobile/robocop/FennecMochitestAssert.java
+++ b/build/mobile/robocop/FennecMochitestAssert.java
@@ -7,17 +7,16 @@ package org.mozilla.gecko;
 import java.util.LinkedList;
 
 import android.os.SystemClock;
 
 public class FennecMochitestAssert implements Assert {
     private LinkedList<testInfo> mTestList = new LinkedList<testInfo>();
 
     // Internal state variables to make logging match up with existing mochitests
-    private int mLineNumber = 0;
     private int mPassed = 0;
     private int mFailed = 0;
     private int mTodo = 0;
 
     // Used to write the first line of the test file
     private boolean mLogStarted = false;
 
     // Used to write the test-start/test-end log lines
@@ -40,35 +39,35 @@ public class FennecMochitestAssert imple
     }
 
     /** Set the filename used for dumpLog. */
     public void setLogFile(String filename) {
         FennecNativeDriver.setLogFile(filename);
 
         String message;
         if (!mLogStarted) {
-            dumpLog(Integer.toString(mLineNumber++) + " INFO SimpleTest START");
+            dumpLog("SimpleTest START");
             mLogStarted = true;
         }
 
         if (mLogTestName != "") {
             long diff = SystemClock.uptimeMillis() - mStartTime;
-            message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+            message = "TEST-END | " + mLogTestName;
             message += " | finished in " + diff + "ms";
             dumpLog(message);
             mLogTestName = "";
         }
     }
 
     public void setTestName(String testName) {
         String[] nameParts = testName.split("\\.");
         mLogTestName = nameParts[nameParts.length - 1];
         mStartTime = SystemClock.uptimeMillis();
 
-        dumpLog(Integer.toString(mLineNumber++) + " INFO TEST-START | " + mLogTestName);
+        dumpLog("TEST-START | " + mLogTestName);
     }
 
     class testInfo {
         public boolean mResult;
         public String mName;
         public String mDiag;
         public boolean mTodo;
         public boolean mInfo;
@@ -90,17 +89,17 @@ public class FennecMochitestAssert imple
         }
         if (test.mResult)
         {
             resultString = passString;
         }
         String diag = test.mName;
         if (test.mDiag != null) diag += " - " + test.mDiag;
 
-        String message = Integer.toString(mLineNumber++) + " INFO " + resultString + " | " + mLogTestName + " | " + diag;
+        String message = resultString + " | " + mLogTestName + " | " + diag;
         dumpLog(message);
 
         if (test.mInfo) {
             // do not count TEST-INFO messages
         } else if (test.mTodo) {
             mTodo++;
         } else if (isError) {
             mFailed++;
@@ -112,31 +111,31 @@ public class FennecMochitestAssert imple
         }
     }
 
     public void endTest() {
         String message;
 
         if (mLogTestName != "") {
             long diff = SystemClock.uptimeMillis() - mStartTime;
-            message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+            message = "TEST-END | " + mLogTestName;
             message += " | finished in " + diff + "ms";
             dumpLog(message);
             mLogTestName = "";
         }
 
-        message = Integer.toString(mLineNumber++) + " INFO TEST-START | Shutdown";
+        message = "TEST-START | Shutdown";
         dumpLog(message);
-        message = Integer.toString(mLineNumber++) + " INFO Passed: " + Integer.toString(mPassed);
+        message = "Passed: " + Integer.toString(mPassed);
         dumpLog(message);
-        message = Integer.toString(mLineNumber++) + " INFO Failed: " + Integer.toString(mFailed);
+        message = "Failed: " + Integer.toString(mFailed);
         dumpLog(message);
-        message = Integer.toString(mLineNumber++) + " INFO Todo: " + Integer.toString(mTodo);
+        message = "Todo: " + Integer.toString(mTodo);
         dumpLog(message);
-        message = Integer.toString(mLineNumber++) + " INFO SimpleTest FINISHED";
+        message = "SimpleTest FINISHED";
         dumpLog(message);
     }
 
     public void ok(boolean condition, String name, String diag) {
         testInfo test = new testInfo(condition, name, diag, false, false);
         _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
         mTestList.add(test);
     }
--- a/configure.in
+++ b/configure.in
@@ -499,16 +499,18 @@ case "$target" in
         fi
         AC_SUBST(MSVS_VERSION)
         AC_SUBST(MSVC_C_RUNTIME_DLL)
         AC_SUBST(MSVC_CXX_RUNTIME_DLL)
 
         # Disable SEH on clang-cl because it doesn't implement them yet.
         if test -z "$CLANG_CL"; then
             AC_DEFINE(HAVE_SEH_EXCEPTIONS)
+        else
+            AC_DEFINE_UNQUOTED(GTEST_HAS_SEH, 0)
         fi
 
         if test -n "$WIN32_REDIST_DIR"; then
           if test ! -d "$WIN32_REDIST_DIR"; then
             AC_MSG_ERROR([Invalid Win32 Redist directory: ${WIN32_REDIST_DIR}])
           fi
           WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd`
         fi
@@ -3871,17 +3873,16 @@ MOZ_LOCALE_SWITCHER=
 MOZ_ANDROID_SEARCH_ACTIVITY=
 MOZ_ANDROID_MLS_STUMBLER=
 ACCESSIBILITY=1
 MOZ_TIME_MANAGER=
 MOZ_PAY=
 MOZ_AUDIO_CHANNEL_MANAGER=
 NSS_NO_LIBPKIX=
 MOZ_CONTENT_SANDBOX=
-MOZ_CONTENT_SANDBOX_REPORTER=1
 JSGC_USE_EXACT_ROOTING=
 JSGC_GENERATIONAL=
 
 case "$target_os" in
     mingw*)
         NS_ENABLE_TSF=1
         AC_DEFINE(NS_ENABLE_TSF)
         ;;
@@ -6437,27 +6438,16 @@ MOZ_ARG_ENABLE_BOOL(content-sandbox,
     MOZ_CONTENT_SANDBOX=)
 
 if test -n "$MOZ_CONTENT_SANDBOX"; then
     AC_DEFINE(MOZ_CONTENT_SANDBOX)
 fi
 
 AC_SUBST(MOZ_CONTENT_SANDBOX)
 
-MOZ_ARG_ENABLE_BOOL(content-sandbox-reporter,
-[ --enable-content-sandbox-reporter        Enable syscall reporter to troubleshoot syscalls denied by the content-processes sandbox],
-    MOZ_CONTENT_SANDBOX_REPORTER=1,
-    MOZ_CONTENT_SANDBOX_REPORTER=)
-
-if test -n "$MOZ_CONTENT_SANDBOX_REPORTER"; then
-    AC_DEFINE(MOZ_CONTENT_SANDBOX_REPORTER)
-fi
-
-AC_SUBST(MOZ_CONTENT_SANDBOX_REPORTER)
-
 dnl ========================================================
 dnl =
 dnl = Module specific options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Individual module options)
 
 dnl ========================================================
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2986,16 +2986,24 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
       nsAutoCString scheme;
 
       rv = mChannel->GetURI(getter_AddRefs(uri));
       if (NS_SUCCEEDED(rv)) {
         uri->GetScheme(scheme);
         if (scheme.LowerCaseEqualsLiteral("app") ||
             scheme.LowerCaseEqualsLiteral("jar")) {
           mIsMappedArrayBuffer = true;
+          if (XRE_GetProcessType() != GeckoProcessType_Default) {
+            nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(mChannel);
+            // For memory mapping from child process, we need to get file
+            // descriptor of the JAR file opened remotely on the parent proess.
+            // Set this to make sure that file descriptor can be obtained by
+            // child process.
+            jarChannel->EnsureChildFd();
+          }
         }
       }
     }
     // Start reading from the channel
     rv = mChannel->AsyncOpen(listener, nullptr);
   }
 
   if (NS_FAILED(rv)) {
--- a/content/base/test/test_fileapi.html
+++ b/content/base/test/test_fileapi.html
@@ -144,17 +144,16 @@ is(r.readyState, FileReader.EMPTY,
   "readyState in test reader get result without reading");
 is(r.error, null,
   "no error in test reader get result without reading");
 is(r.result, null,
   "result in test reader get result without reading");
 
 // Test loading an empty file works (and doesn't crash!)
 var emptyFile = createFileWithData("");
-dump("hello nurse");
 r = new FileReader();
 r.onload = getLoadHandler("", 0, "empty no encoding reading");
 r.readAsText(emptyFile, "");
 expectedTestCount++;
 
 r = new FileReader();
 r.onload = getLoadHandler("", 0, "empty utf8 reading");
 r.readAsText(emptyFile, "utf8");
@@ -422,18 +421,18 @@ function getLoadHandlerForArrayBuffer(ex
     testHasRun();
   }
 }
 
 function testHasRun() {
  //alert(testRanCounter);
  ++testRanCounter;
  if (testRanCounter == expectedTestCount) {
-    is(onloadHasRunText, true, "onload text should have fired by now"); 
-    is(onloadHasRunBinary, true, "onload binary should have fired by now"); 
+    is(onloadHasRunText, true, "onload text should have fired by now");
+    is(onloadHasRunBinary, true, "onload binary should have fired by now");
     SimpleTest.finish();
   }
 }
 
 function createFileWithData(fileData) {
   var dirSvc = SpecialPowers.Cc["@mozilla.org/file/directory_service;1"].getService(SpecialPowers.Ci.nsIProperties);
   var testFile = dirSvc.get("ProfD", SpecialPowers.Ci.nsIFile);
   testFile.append("fileAPItestfile" + fileNum);
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -307,29 +307,16 @@ AudioNodeStream::ObtainInputBlock(AudioC
     }
     MediaStream* s = mInputs[i]->GetSource();
     AudioNodeStream* a = static_cast<AudioNodeStream*>(s);
     MOZ_ASSERT(a == s->AsAudioNodeStream());
     if (a->IsAudioParamStream()) {
       continue;
     }
 
-    // It is possible for mLastChunks to be empty here, because `a` might be a
-    // AudioNodeStream that has not been scheduled yet, because it is further
-    // down the graph _but_ as a connection to this node. Because we enforce the
-    // presence of at least one DelayNode, with at least one block of delay, and
-    // because the output of a DelayNode when it has been fed less that
-    // `delayTime` amount of audio is silence, we can simply continue here,
-    // because this input would not influence the output of this node. Next
-    // iteration, a->mLastChunks.IsEmpty() will be false, and everthing will
-    // work as usual.
-    if (a->mLastChunks.IsEmpty()) {
-      continue;
-    }
-
     AudioChunk* chunk = &a->mLastChunks[mInputs[i]->OutputNumber()];
     MOZ_ASSERT(chunk);
     if (chunk->IsNull() || chunk->mChannelData.IsEmpty()) {
       continue;
     }
 
     inputChunks.AppendElement(chunk);
     outputChannelCount =
@@ -448,17 +435,17 @@ AudioNodeStream::ProcessInput(GraphTime 
   uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount());
   mLastChunks.SetLength(outputCount);
 
   // Consider this stream blocked if it has already finished output. Normally
   // mBlocked would reflect this, but due to rounding errors our audio track may
   // appear to extend slightly beyond aFrom, so we might not be blocked yet.
   bool blocked = mFinished || mBlocked.GetAt(aFrom);
   // If the stream has finished at this time, it will be blocked.
-  if (mMuted || blocked) {
+  if (blocked || InMutedCycle()) {
     for (uint16_t i = 0; i < outputCount; ++i) {
       mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
     }
   } else {
     // We need to generate at least one input
     uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
     OutputChunks inputChunks;
     inputChunks.SetLength(maxInputs);
@@ -494,16 +481,42 @@ AudioNodeStream::ProcessInput(GraphTime 
       // of the depending streams have finished their output as well, so now
       // it's time to mark this stream as finished.
       FinishOutput();
     }
   }
 }
 
 void
+AudioNodeStream::ProduceOutputBeforeInput(GraphTime aFrom)
+{
+  MOZ_ASSERT(mEngine->AsDelayNodeEngine());
+  MOZ_ASSERT(mEngine->OutputCount() == 1,
+             "DelayNodeEngine output count should be 1");
+  MOZ_ASSERT(!InMutedCycle(), "DelayNodes should break cycles");
+  mLastChunks.SetLength(1);
+
+  // Consider this stream blocked if it has already finished output. Normally
+  // mBlocked would reflect this, but due to rounding errors our audio track may
+  // appear to extend slightly beyond aFrom, so we might not be blocked yet.
+  bool blocked = mFinished || mBlocked.GetAt(aFrom);
+  // If the stream has finished at this time, it will be blocked.
+  if (blocked) {
+    mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
+  } else {
+    mEngine->ProduceBlockBeforeInput(&mLastChunks[0]);
+    NS_ASSERTION(mLastChunks[0].GetDuration() == WEBAUDIO_BLOCK_SIZE,
+                 "Invalid WebAudio chunk size");
+    if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
+      mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
+    }
+  }
+}
+
+void
 AudioNodeStream::AdvanceOutputSegment()
 {
   StreamBuffer::Track* track = EnsureTrack(AUDIO_TRACK, mSampleRate);
   AudioSegment* segment = track->Get<AudioSegment>();
 
   if (mKind == MediaStreamGraph::EXTERNAL_STREAM) {
     segment->AppendAndConsumeChunk(&mLastChunks[0]);
   } else {
--- a/content/media/AudioNodeStream.h
+++ b/content/media/AudioNodeStream.h
@@ -10,17 +10,16 @@
 #include "mozilla/dom/AudioNodeBinding.h"
 #include "AudioSegment.h"
 
 namespace mozilla {
 
 namespace dom {
 struct ThreeDPoint;
 class AudioParamTimeline;
-class DelayNodeEngine;
 class AudioContext;
 }
 
 class ThreadSharedFloatArrayBufferList;
 class AudioNodeEngine;
 
 /**
  * An AudioNodeStream produces one audio track with ID AUDIO_TRACK.
@@ -50,18 +49,17 @@ public:
                   MediaStreamGraph::AudioNodeStreamKind aKind,
                   TrackRate aSampleRate)
     : ProcessedMediaStream(nullptr),
       mEngine(aEngine),
       mSampleRate(aSampleRate),
       mKind(aKind),
       mNumberOfInputChannels(2),
       mMarkAsFinishedAfterThisBlock(false),
-      mAudioParamStream(false),
-      mMuted(false)
+      mAudioParamStream(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
     mChannelCountMode = ChannelCountMode::Max;
     mChannelInterpretation = ChannelInterpretation::Speakers;
     // AudioNodes are always producing data
     mHasCurrentData = true;
     MOZ_COUNT_CTOR(AudioNodeStream);
   }
@@ -102,28 +100,27 @@ public:
 
   // Graph thread only
   void SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
                                   double aStreamTime);
   void SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
                                       ChannelCountMode aChannelCountMoe,
                                       ChannelInterpretation aChannelInterpretation);
   virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
+  /**
+   * Produce the next block of output, before input is provided.
+   * ProcessInput() will be called later, and it then should not change
+   * the output.  This is used only for DelayNodeEngine in a feedback loop.
+   */
+  void ProduceOutputBeforeInput(GraphTime aFrom);
   TrackTicks GetCurrentPosition();
   bool IsAudioParamStream() const
   {
     return mAudioParamStream;
   }
-  void Mute() {
-    mMuted = true;
-  }
-
-  void Unmute() {
-    mMuted = false;
-  }
 
   const OutputChunks& LastChunks() const
   {
     return mLastChunks;
   }
   virtual bool MainThreadNeedsUpdates() const MOZ_OVERRIDE
   {
     // Only source and external streams need updates on the main thread.
@@ -190,15 +187,13 @@ protected:
   // The mixing modes
   ChannelCountMode mChannelCountMode;
   ChannelInterpretation mChannelInterpretation;
   // Whether the stream should be marked as finished as soon
   // as the current time range has been computed block by block.
   bool mMarkAsFinishedAfterThisBlock;
   // Whether the stream is an AudioParamHelper stream.
   bool mAudioParamStream;
-  // Whether the stream is muted. Access only on the MediaStreamGraph thread.
-  bool mMuted;
 };
 
 }
 
 #endif /* MOZILLA_AUDIONODESTREAM_H_ */
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -253,17 +253,18 @@ AudioStream::AudioStream()
 {
   // keep a ref in case we shut down later than nsLayoutStatics
   mLatencyLog = AsyncLatencyLogger::Get(true);
 }
 
 AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
-  Shutdown();
+  MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream,
+             "Should've called Shutdown() before deleting an AudioStream");
   if (mDumpFile) {
     fclose(mDumpFile);
   }
 }
 
 size_t
 AudioStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
@@ -312,27 +313,28 @@ nsresult AudioStream::EnsureTimeStretche
     mTimeStretcher->setChannels(mOutChannels);
     mTimeStretcher->setPitch(1.0);
   }
   return NS_OK;
 }
 
 nsresult AudioStream::SetPlaybackRate(double aPlaybackRate)
 {
+  // MUST lock since the rate transposer is used from the cubeb callback,
+  // and rate changes can cause the buffer to be reallocated
+  MonitorAutoLock mon(mMonitor);
+
   NS_ASSERTION(aPlaybackRate > 0.0,
                "Can't handle negative or null playbackrate in the AudioStream.");
   // Avoid instantiating the resampler if we are not changing the playback rate.
   // GetPreservesPitch/SetPreservesPitch don't need locking before calling
   if (aPlaybackRate == mAudioClock.GetPlaybackRate()) {
     return NS_OK;
   }
 
-  // MUST lock since the rate transposer is used from the cubeb callback,
-  // and rate changes can cause the buffer to be reallocated
-  MonitorAutoLock mon(mMonitor);
   if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return NS_ERROR_FAILURE;
   }
 
   mAudioClock.SetPlaybackRateUnlocked(aPlaybackRate);
   mOutRate = mInRate / aPlaybackRate;
 
   if (mAudioClock.GetPreservesPitch()) {
@@ -342,24 +344,25 @@ nsresult AudioStream::SetPlaybackRate(do
     mTimeStretcher->setTempo(1.0f);
     mTimeStretcher->setRate(aPlaybackRate);
   }
   return NS_OK;
 }
 
 nsresult AudioStream::SetPreservesPitch(bool aPreservesPitch)
 {
+  // MUST lock since the rate transposer is used from the cubeb callback,
+  // and rate changes can cause the buffer to be reallocated
+  MonitorAutoLock mon(mMonitor);
+
   // Avoid instantiating the timestretcher instance if not needed.
   if (aPreservesPitch == mAudioClock.GetPreservesPitch()) {
     return NS_OK;
   }
 
-  // MUST lock since the rate transposer is used from the cubeb callback,
-  // and rate changes can cause the buffer to be reallocated
-  MonitorAutoLock mon(mMonitor);
   if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return NS_ERROR_FAILURE;
   }
 
   if (aPreservesPitch == true) {
     mTimeStretcher->setTempo(mAudioClock.GetPlaybackRate());
     mTimeStretcher->setRate(1.0f);
   } else {
@@ -369,16 +372,17 @@ nsresult AudioStream::SetPreservesPitch(
 
   mAudioClock.SetPreservesPitch(aPreservesPitch);
 
   return NS_OK;
 }
 
 int64_t AudioStream::GetWritten()
 {
+  MonitorAutoLock mon(mMonitor);
   return mWritten;
 }
 
 /*static*/ int AudioStream::MaxNumberOfChannels()
 {
   cubeb* cubebContext = GetCubebContext();
   uint32_t maxNumberOfChannels;
   if (cubebContext &&
@@ -528,17 +532,20 @@ AudioStream::Init(int32_t aNumChannels, 
     RefPtr<AudioInitTask> init = new AudioInitTask(this, aLatencyRequest, params);
     init->Dispatch();
     return NS_OK;
   }
   // High latency - open synchronously
   nsresult rv = OpenCubeb(params, aLatencyRequest);
   // See if we need to start() the stream, since we must do that from this
   // thread for now (cubeb API issue)
-  CheckForStart();
+  {
+    MonitorAutoLock mon(mMonitor);
+    CheckForStart();
+  }
   return rv;
 }
 
 // This code used to live inside AudioStream::Init(), but on Mac (others?)
 // it has been known to take 300-800 (or even 8500) ms to execute(!)
 nsresult
 AudioStream::OpenCubeb(cubeb_stream_params &aParams,
                        LatencyRequest aLatencyRequest)
@@ -595,16 +602,17 @@ AudioStream::OpenCubeb(cubeb_stream_para
   }
 
   return NS_OK;
 }
 
 void
 AudioStream::CheckForStart()
 {
+  mMonitor.AssertCurrentThreadOwns();
   if (mState == INITIALIZED) {
     // Start the stream right away when low latency has been requested. This means
     // that the DataCallback will feed silence to cubeb, until the first frames
     // are written to this AudioStream.  Also start if a start has been queued.
     if (mLatencyRequest == LowLatency || mNeedsStart) {
       StartUnlocked(); // mState = STARTED or ERRORED
       mNeedsStart = false;
       PR_LOG(gAudioStreamLog, PR_LOG_WARNING,
@@ -774,19 +782,22 @@ AudioStream::Start()
 void
 AudioStream::StartUnlocked()
 {
   mMonitor.AssertCurrentThreadOwns();
   if (!mCubebStream) {
     mNeedsStart = true;
     return;
   }
-  MonitorAutoUnlock mon(mMonitor);
   if (mState == INITIALIZED) {
-    int r = cubeb_stream_start(mCubebStream);
+    int r;
+    {
+      MonitorAutoUnlock mon(mMonitor);
+      r = cubeb_stream_start(mCubebStream);
+    }
     mState = r == CUBEB_OK ? STARTED : ERRORED;
     LOG(("AudioStream: started %p, state %s", this, mState == STARTED ? "STARTED" : "ERRORED"));
   }
 }
 
 void
 AudioStream::Pause()
 {
@@ -823,31 +834,29 @@ AudioStream::Resume()
   if (mState != ERRORED && r == CUBEB_OK) {
     mState = STARTED;
   }
 }
 
 void
 AudioStream::Shutdown()
 {
+  MonitorAutoLock mon(mMonitor);
   LOG(("AudioStream: Shutdown %p, state %d", this, mState));
-  {
-    MonitorAutoLock mon(mMonitor);
-    if (mState == STARTED || mState == RUNNING) {
-      MonitorAutoUnlock mon(mMonitor);
-      Pause();
-    }
-    MOZ_ASSERT(mState != STARTED && mState != RUNNING); // paranoia
-    mState = SHUTDOWN;
-  }
-  // Must not try to shut down cubeb from within the lock!  wasapi may still
-  // call our callback after Pause()/stop()!?! Bug 996162
+
   if (mCubebStream) {
+    MonitorAutoUnlock mon(mMonitor);
+    // Force stop to put the cubeb stream in a stable state before deletion.
+    cubeb_stream_stop(mCubebStream);
+    // Must not try to shut down cubeb from within the lock!  wasapi may still
+    // call our callback after Pause()/stop()!?! Bug 996162
     mCubebStream.reset();
   }
+
+  mState = SHUTDOWN;
 }
 
 int64_t
 AudioStream::GetPosition()
 {
   MonitorAutoLock mon(mMonitor);
   return mAudioClock.GetPositionUnlocked();
 }
@@ -902,16 +911,17 @@ AudioStream::IsPaused()
 {
   MonitorAutoLock mon(mMonitor);
   return mState == STOPPED;
 }
 
 void
 AudioStream::GetBufferInsertTime(int64_t &aTimeMs)
 {
+  mMonitor.AssertCurrentThreadOwns();
   if (mInserts.Length() > 0) {
     // Find the right block, but don't leave the array empty
     while (mInserts.Length() > 1 && mReadPoint >= mInserts[0].mFrames) {
       mReadPoint -= mInserts[0].mFrames;
       mInserts.RemoveElementAt(0);
     }
     // offset for amount already read
     // XXX Note: could misreport if we couldn't find a block in the right timeframe
@@ -919,16 +929,17 @@ AudioStream::GetBufferInsertTime(int64_t
   } else {
     aTimeMs = INT64_MAX;
   }
 }
 
 long
 AudioStream::GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTimeMs)
 {
+  mMonitor.AssertCurrentThreadOwns();
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
 
   // Flush the timestretcher pipeline, if we were playing using a playback rate
   // other than 1.0.
   uint32_t flushedFrames = 0;
   if (mTimeStretcher && mTimeStretcher->numSamples()) {
     flushedFrames = mTimeStretcher->receiveSamples(reinterpret_cast<AudioDataValue*>(wpos), aFrames);
     wpos += FramesToBytes(flushedFrames);
@@ -950,16 +961,17 @@ AudioStream::GetUnprocessed(void* aBuffe
   return BytesToFrames(available) + flushedFrames;
 }
 
 // Get unprocessed samples, and pad the beginning of the buffer with silence if
 // there is not enough data.
 long
 AudioStream::GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t& aTimeMs)
 {
+  mMonitor.AssertCurrentThreadOwns();
   uint32_t toPopBytes = FramesToBytes(aFrames);
   uint32_t available = std::min(toPopBytes, mBuffer.Length());
   uint32_t silenceOffset = toPopBytes - available;
 
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
 
   memset(wpos, 0, silenceOffset);
   wpos += silenceOffset;
@@ -974,16 +986,17 @@ AudioStream::GetUnprocessedWithSilencePa
   GetBufferInsertTime(aTimeMs);
 
   return aFrames;
 }
 
 long
 AudioStream::GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTimeMs)
 {
+  mMonitor.AssertCurrentThreadOwns();
   long processedFrames = 0;
 
   // We need to call the non-locking version, because we already have the lock.
   if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return 0;
   }
 
   uint8_t* wpos = reinterpret_cast<uint8_t*>(aBuffer);
@@ -1016,16 +1029,17 @@ AudioStream::GetTimeStretched(void* aBuf
 
   return processedFrames;
 }
 
 long
 AudioStream::DataCallback(void* aBuffer, long aFrames)
 {
   MonitorAutoLock mon(mMonitor);
+  MOZ_ASSERT(mState != SHUTDOWN, "No data callback after shutdown");
   uint32_t available = std::min(static_cast<uint32_t>(FramesToBytes(aFrames)), mBuffer.Length());
   NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames");
   AudioDataValue* output = reinterpret_cast<AudioDataValue*>(aBuffer);
   uint32_t underrunFrames = 0;
   uint32_t servicedFrames = 0;
   int64_t insertTime;
 
   // NOTE: wasapi (others?) can call us back *after* stop()/Shutdown() (mState == SHUTDOWN)
@@ -1126,16 +1140,18 @@ AudioStream::DataCallback(void* aBuffer,
 
   return servicedFrames;
 }
 
 void
 AudioStream::StateCallback(cubeb_state aState)
 {
   MonitorAutoLock mon(mMonitor);
+  MOZ_ASSERT(mState != SHUTDOWN, "No state callback after shutdown");
+  LOG(("AudioStream: StateCallback %p, mState=%d cubeb_state=%d", this, mState, aState));
   if (aState == CUBEB_STATE_DRAINED) {
     mState = DRAINED;
   } else if (aState == CUBEB_STATE_ERROR) {
     LOG(("AudioStream::StateCallback() state %d cubeb error", mState));
     mState = ERRORED;
   }
   mon.NotifyAll();
 }
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/dom/RecordErrorEvent.h"
 #include "nsTArray.h"
 #include "DOMMediaStream.h"
 #include "EncodedBufferCache.h"
 #include "nsIDOMFile.h"
 #include "mozilla/dom/BlobEvent.h"
 #include "nsIPrincipal.h"
 #include "nsMimeTypes.h"
+#include "nsProxyRelease.h"
 
 #include "mozilla/dom/AudioStreamTrack.h"
 #include "mozilla/dom/VideoStreamTrack.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gMediaRecorderLog;
 #define LOG(type, msg) PR_LOG(gMediaRecorderLog, type, msg)
 #else
@@ -144,32 +145,48 @@ class MediaRecorder::Session: public nsI
     nsString mEventName;
   };
 
   // Record thread task and it run in Media Encoder thread.
   // Fetch encoded Audio/Video data from MediaEncoder.
   class ExtractRunnable : public nsRunnable
   {
   public:
-    ExtractRunnable(Session *aSession)
+    ExtractRunnable(already_AddRefed<Session>&& aSession)
       : mSession(aSession) {}
 
+    ~ExtractRunnable()
+    {
+      if (mSession) {
+        NS_WARNING("~ExtractRunnable something wrong the mSession should null");
+        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+        NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
+        NS_ProxyRelease(mainThread, mSession);
+      }
+    }
+
     NS_IMETHODIMP Run()
     {
       MOZ_ASSERT(NS_GetCurrentThread() == mSession->mReadThread);
 
       LOG(PR_LOG_DEBUG, ("Session.ExtractRunnable shutdown = %d", mSession->mEncoder->IsShutdown()));
       if (!mSession->mEncoder->IsShutdown()) {
         mSession->Extract(false);
-        NS_DispatchToCurrentThread(new ExtractRunnable(mSession));
+        nsRefPtr<nsIRunnable> event = new ExtractRunnable(mSession.forget());
+        if (NS_FAILED(NS_DispatchToCurrentThread(event))) {
+          NS_WARNING("Failed to dispatch ExtractRunnable to encoder thread");
+        }
       } else {
         // Flush out remaining encoded data.
         mSession->Extract(true);
         // Destroy this Session object in main thread.
-        NS_DispatchToMainThread(new DestroyRunnable(already_AddRefed<Session>(mSession)));
+        if (NS_FAILED(NS_DispatchToMainThread(
+                        new DestroyRunnable(mSession.forget())))) {
+          MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+        }
       }
       return NS_OK;
     }
 
   private:
     nsRefPtr<Session> mSession;
   };
 
@@ -224,17 +241,20 @@ class MediaRecorder::Session: public nsI
       // We need to switch MediaRecorder to "Stop" state first to make sure
       // MediaRecorder is not associated with this Session anymore, then, it's
       // safe to delete this Session.
       // Also avoid to run if this session already call stop before
       if (!mSession->mStopIssued) {
         ErrorResult result;
         mSession->mStopIssued = true;
         recorder->Stop(result);
-        NS_DispatchToMainThread(new DestroyRunnable(mSession.forget()));
+        if (NS_FAILED(NS_DispatchToMainThread(
+                        new DestroyRunnable(mSession.forget())))) {
+          MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
+        }
         return NS_OK;
       }
 
       // Dispatch stop event and clear MIME type.
       mSession->mMimeType = NS_LITERAL_STRING("");
       recorder->SetMimeType(mSession->mMimeType);
       recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
       recorder->RemoveSession(mSession);
@@ -256,17 +276,16 @@ public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
     : mRecorder(aRecorder),
       mTimeSlice(aTimeSlice),
       mStopIssued(false),
       mCanRetrieveData(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    AddRef();
     mEncodedBufferCache = new EncodedBufferCache(MAX_ALLOW_MEMORY_BUFFER);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
 
   void Start()
   {
     LOG(PR_LOG_DEBUG, ("Session.Start %p", this));
     MOZ_ASSERT(NS_IsMainThread());
@@ -355,18 +374,21 @@ private:
     // Whether push encoded data back to onDataAvailable automatically or we
     // need a flush.
     bool pushBlob = false;
     if ((mTimeSlice > 0) &&
         ((TimeStamp::Now()-mLastBlobTimeStamp).ToMilliseconds() > mTimeSlice)) {
       pushBlob = true;
     }
     if (pushBlob || aForceFlush) {
-      NS_DispatchToMainThread(new PushBlobRunnable(this));
-      mLastBlobTimeStamp = TimeStamp::Now();
+      if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+        MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
+      } else {
+        mLastBlobTimeStamp = TimeStamp::Now();
+      }
     }
   }
 
   // Bind media source with MediaEncoder to receive raw media data.
   void SetupStreams()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -427,30 +449,37 @@ private:
         return;
       }
     }
 
     // In case source media stream does not notify track end, recieve
     // shutdown notification and stop Read Thread.
     nsContentUtils::RegisterShutdownObserver(this);
 
-    mReadThread->Dispatch(new ExtractRunnable(this), NS_DISPATCH_NORMAL);
+    nsRefPtr<nsIRunnable> event = new ExtractRunnable(this);
+    if (NS_FAILED(mReadThread->Dispatch(event, NS_DISPATCH_NORMAL))) {
+      NS_WARNING("Failed to dispatch ExtractRunnable at beginning");
+    }
   }
   // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
     if (NS_FAILED(rv)) {
       mRecorder->NotifyError(rv);
     }
 
     CleanupStreams();
+    if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+      MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
+    }
     // Destroy this session object in main thread.
-    NS_DispatchToMainThread(new PushBlobRunnable(this));
-    NS_DispatchToMainThread(new DestroyRunnable(already_AddRefed<Session>(this)));
+    if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
+      MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
+    }
   }
   void CleanupStreams()
   {
     if (mInputPort.get()) {
       mInputPort->Destroy();
       mInputPort = nullptr;
     }
 
@@ -603,20 +632,27 @@ MediaRecorder::Start(const Optional<int3
       aResult.Throw(NS_ERROR_INVALID_ARG);
       return;
     }
 
     timeSlice = aTimeSlice.Value();
   }
 
   mState = RecordingState::Recording;
-  // Start a session
-
+  // Start a session.
+  // Add session's reference here and pass to ExtractRunnable since the
+  // MediaRecorder doesn't hold any reference to Session. Also
+  // the DoSessionEndTask need this reference for DestroyRunnable.
+  // Note that the reference count is not balance here due to the
+  // DestroyRunnable will destroyed the last reference.
+  nsRefPtr<Session> session = new Session(this, timeSlice);
+  Session* rawPtr;
+  session.forget(&rawPtr);
   mSessions.AppendElement();
-  mSessions.LastElement() = new Session(this, timeSlice);
+  mSessions.LastElement() = rawPtr;
   mSessions.LastElement()->Start();
 }
 
 void
 MediaRecorder::Stop(ErrorResult& aResult)
 {
   LOG(PR_LOG_DEBUG, ("MediaRecorder.Stop %p", this));
   if (mState == RecordingState::Inactive) {
@@ -686,19 +722,21 @@ CreateAndDispatchBlobEventRunnable::Run(
 void
 MediaRecorder::RequestData(ErrorResult& aResult)
 {
   if (mState != RecordingState::Recording) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   MOZ_ASSERT(mSessions.Length() > 0);
-  NS_DispatchToMainThread(
-    new CreateAndDispatchBlobEventRunnable(mSessions.LastElement()->GetEncodedData(),
-                                           this));
+  if (NS_FAILED(NS_DispatchToMainThread(
+                  new CreateAndDispatchBlobEventRunnable(
+                    mSessions.LastElement()->GetEncodedData(), this)))) {
+    MOZ_ASSERT(false, "NS_DispatchToMainThread CreateAndDispatchBlobEventRunnable failed");
+  }
 }
 
 JSObject*
 MediaRecorder::WrapObject(JSContext* aCx)
 {
   return MediaRecorderBinding::Wrap(aCx, this);
 }
 
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1,15 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaStreamGraphImpl.h"
-#include "mozilla/LinkedList.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "nsContentUtils.h"
 #include "nsIAppShell.h"
 #include "nsIObserver.h"
@@ -85,17 +84,17 @@ MediaStreamGraphImpl::FinishStream(Media
 
   SetStreamOrderDirty();
 }
 
 void
 MediaStreamGraphImpl::AddStream(MediaStream* aStream)
 {
   aStream->mBufferStartTime = mCurrentTime;
-  *mStreams.AppendElement() = already_AddRefed<MediaStream>(aStream);
+  mStreams.AppendElement(aStream);
   STREAM_LOG(PR_LOG_DEBUG, ("Adding media stream %p to the graph", aStream));
 
   SetStreamOrderDirty();
 }
 
 void
 MediaStreamGraphImpl::RemoveStream(MediaStream* aStream)
 {
@@ -108,18 +107,18 @@ MediaStreamGraphImpl::RemoveStream(Media
       if (mStreamUpdates[i].mStream == aStream) {
         mStreamUpdates[i].mStream = nullptr;
       }
     }
   }
 
   SetStreamOrderDirty();
 
-  // This unrefs the stream, probably destroying it
   mStreams.RemoveElement(aStream);
+  NS_RELEASE(aStream); // probably destroying it
 
   STREAM_LOG(PR_LOG_DEBUG, ("Removing media stream %p from the graph", aStream));
 }
 
 void
 MediaStreamGraphImpl::UpdateConsumptionState(SourceMediaStream* aStream)
 {
   MediaStreamListener::Consumption state =
@@ -525,80 +524,16 @@ MediaStreamGraphImpl::MarkConsumed(Media
     return;
   }
   // Mark all the inputs to this stream as consumed
   for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
     MarkConsumed(ps->mInputs[i]->mSource);
   }
 }
 
-void
-MediaStreamGraphImpl::UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream>* aStack,
-                                                 already_AddRefed<MediaStream> aStream)
-{
-  nsRefPtr<MediaStream> stream = aStream;
-  NS_ASSERTION(!stream->mHasBeenOrdered, "stream should not have already been ordered");
-  if (stream->mIsOnOrderingStack) {
-    MediaStream* iter = aStack->getLast();
-    AudioNodeStream* ns = stream->AsAudioNodeStream();
-    bool delayNodePresent = ns ? ns->Engine()->AsDelayNodeEngine() != nullptr : false;
-    bool cycleFound = false;
-    if (iter) {
-      do {
-        cycleFound = true;
-        iter->AsProcessedStream()->mInCycle = true;
-        AudioNodeStream* ns = iter->AsAudioNodeStream();
-        if (ns && ns->Engine()->AsDelayNodeEngine()) {
-          delayNodePresent = true;
-        }
-        iter = iter->getPrevious();
-      } while (iter && iter != stream);
-    }
-    if (cycleFound && !delayNodePresent) {
-      // If we have detected a cycle, the previous loop should exit with stream
-      // == iter, or the node is connected to itself. Go back in the cycle and
-      // mute all nodes we find, or just mute the node itself.
-      if (!iter) {
-        // The node is connected to itself.
-        // There can't be a non-AudioNodeStream here, because only AudioNodes
-        // can be self-connected.
-        iter = aStack->getLast();
-        MOZ_ASSERT(iter->AsAudioNodeStream());
-        iter->AsAudioNodeStream()->Mute();
-      } else {
-        MOZ_ASSERT(iter);
-        do {
-          AudioNodeStream* nodeStream = iter->AsAudioNodeStream();
-          if (nodeStream) {
-            nodeStream->Mute();
-          }
-        } while((iter = iter->getNext()));
-      }
-    }
-    return;
-  }
-  ProcessedMediaStream* ps = stream->AsProcessedStream();
-  if (ps) {
-    aStack->insertBack(stream);
-    stream->mIsOnOrderingStack = true;
-    for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
-      MediaStream* source = ps->mInputs[i]->mSource;
-      if (!source->mHasBeenOrdered) {
-        nsRefPtr<MediaStream> s = source;
-        UpdateStreamOrderForStream(aStack, s.forget());
-      }
-    }
-    aStack->popLast();
-    stream->mIsOnOrderingStack = false;
-  }
-
-  stream->mHasBeenOrdered = true;
-  *mStreams.AppendElement() = stream.forget();
-}
-
 static void AudioMixerCallback(AudioDataValue* aMixedBuffer,
                                AudioSampleFormat aFormat,
                                uint32_t aChannels,
                                uint32_t aFrames,
                                uint32_t aSampleRate)
 {
   // Need an api to register mixer callbacks, bug 989921
 #ifdef MOZ_WEBRTC
@@ -610,55 +545,206 @@ static void AudioMixerCallback(AudioData
     }
   }
 #endif
 }
 
 void
 MediaStreamGraphImpl::UpdateStreamOrder()
 {
-  mOldStreams.SwapElements(mStreams);
-  mStreams.ClearAndRetainStorage();
   bool shouldMix = false;
-  for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
-    MediaStream* stream = mOldStreams[i];
-    stream->mHasBeenOrdered = false;
+  // Value of mCycleMarker for unvisited streams in cycle detection.
+  const uint32_t NOT_VISITED = UINT32_MAX;
+  // Value of mCycleMarker for ordered streams in muted cycles.
+  const uint32_t IN_MUTED_CYCLE = 1;
+
+  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+    MediaStream* stream = mStreams[i];
     stream->mIsConsumed = false;
-    stream->mIsOnOrderingStack = false;
     stream->mInBlockingSet = false;
     if (stream->AsSourceStream() &&
         stream->AsSourceStream()->NeedsMixing()) {
       shouldMix = true;
     }
-    ProcessedMediaStream* ps = stream->AsProcessedStream();
-    if (ps) {
-      ps->mInCycle = false;
-      AudioNodeStream* ns = ps->AsAudioNodeStream();
-      if (ns) {
-        ns->Unmute();
-      }
-    }
   }
 
   if (!mMixer && shouldMix) {
     mMixer = new AudioMixer(AudioMixerCallback);
   } else if (mMixer && !shouldMix) {
     mMixer = nullptr;
   }
 
-  mozilla::LinkedList<MediaStream> stack;
-  for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
-    nsRefPtr<MediaStream>& s = mOldStreams[i];
+  // The algorithm for finding cycles is based on Tim Leslie's iterative
+  // implementation [1][2] of Pearce's variant [3] of Tarjan's strongly
+  // connected components (SCC) algorithm.  There are variations (a) to
+  // distinguish whether streams in SCCs of size 1 are in a cycle and (b) to
+  // re-run the algorithm over SCCs with breaks at DelayNodes.
+  //
+  // [1] http://www.timl.id.au/?p=327
+  // [2] https://github.com/scipy/scipy/blob/e2c502fca/scipy/sparse/csgraph/_traversal.pyx#L582
+  // [3] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.102.1707
+  //
+  // There are two stacks.  One for the depth-first search (DFS),
+  mozilla::LinkedList<MediaStream> dfsStack;
+  // and another for streams popped from the DFS stack, but still being
+  // considered as part of SCCs involving streams on the stack.
+  mozilla::LinkedList<MediaStream> sccStack;
+
+  // An index into mStreams for the next stream found with no unsatisfied
+  // upstream dependencies.
+  uint32_t orderedStreamCount = 0;
+
+  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+    MediaStream* s = mStreams[i];
     if (s->IsIntrinsicallyConsumed()) {
       MarkConsumed(s);
     }
-    if (!s->mHasBeenOrdered) {
-      UpdateStreamOrderForStream(&stack, s.forget());
+    ProcessedMediaStream* ps = s->AsProcessedStream();
+    if (ps) {
+      // The dfsStack initially contains a list of all processed streams in
+      // unchanged order.
+      dfsStack.insertBack(s);
+      ps->mCycleMarker = NOT_VISITED;
+    } else {
+      // SourceMediaStreams have no inputs and so can be ordered now.
+      mStreams[orderedStreamCount] = s;
+      ++orderedStreamCount;
     }
   }
+
+  // mNextStackMarker corresponds to "index" in Tarjan's algorithm.  It is a
+  // counter to label mCycleMarker on the next visited stream in the DFS
+  // uniquely in the set of visited streams that are still being considered.
+  //
+  // In this implementation, the counter descends so that the values are
+  // strictly greater than the values that mCycleMarker takes when the stream
+  // has been ordered (0 or IN_MUTED_CYCLE).
+  //
+  // Each new stream labelled, as the DFS searches upstream, receives a value
+  // less than those used for all other streams being considered.
+  uint32_t nextStackMarker = NOT_VISITED - 1;
+  // Reset list of DelayNodes in cycles stored at the tail of mStreams.
+  mFirstCycleBreaker = mStreams.Length();
+
+  // Rearrange dfsStack order as required to DFS upstream and pop streams
+  // in processing order to place in mStreams.
+  while (auto ps = static_cast<ProcessedMediaStream*>(dfsStack.getFirst())) {
+    const auto& inputs = ps->mInputs;
+    MOZ_ASSERT(ps->AsProcessedStream());
+    if (ps->mCycleMarker == NOT_VISITED) {
+      // Record the position on the visited stack, so that any searches
+      // finding this stream again know how much of the stack is in the cycle.
+      ps->mCycleMarker = nextStackMarker;
+      --nextStackMarker;
+      // Not-visited input streams should be processed first.
+      // SourceMediaStreams have already been ordered.
+      for (uint32_t i = inputs.Length(); i--; ) {
+        auto input = inputs[i]->mSource->AsProcessedStream();
+        if (input && input->mCycleMarker == NOT_VISITED) {
+          input->remove();
+          dfsStack.insertFront(input);
+        }
+      }
+      continue;
+    }
+
+    // Returning from DFS.  Pop from dfsStack.
+    ps->remove();
+
+    // cycleStackMarker keeps track of the highest marker value on any
+    // upstream stream, if any, found receiving input, directly or indirectly,
+    // from the visited stack (and so from |ps|, making a cycle).  In a
+    // variation from Tarjan's SCC algorithm, this does not include |ps|
+    // unless it is part of the cycle.
+    uint32_t cycleStackMarker = 0;
+    for (uint32_t i = inputs.Length(); i--; ) {
+      auto input = inputs[i]->mSource->AsProcessedStream();
+      if (input) {
+        cycleStackMarker = std::max(cycleStackMarker, input->mCycleMarker);
+      }
+    }
+
+    if (cycleStackMarker <= IN_MUTED_CYCLE) {
+      // All inputs have been ordered and their stack markers have been removed.
+      // This stream is not part of a cycle.  It can be processed next.
+      ps->mCycleMarker = 0;
+      mStreams[orderedStreamCount] = ps;
+      ++orderedStreamCount;
+      continue;
+    }
+
+    // A cycle has been found.  Record this stream for ordering when all
+    // streams in this SCC have been popped from the DFS stack.
+    sccStack.insertFront(ps);
+
+    if (cycleStackMarker > ps->mCycleMarker) {
+      // Cycles have been found that involve streams that remain on the stack.
+      // Leave mCycleMarker indicating the most downstream (last) stream on
+      // the stack known to be part of this SCC.  In this way, any searches on
+      // other paths that find |ps| will know (without having to traverse from
+      // this stream again) that they are part of this SCC (i.e. part of an
+      // intersecting cycle).
+      ps->mCycleMarker = cycleStackMarker;
+      continue;
+    }
+
+    // |ps| is the root of an SCC involving no other streams on dfsStack, the
+    // complete SCC has been recorded, and streams in this SCC are part of at
+    // least one cycle.
+    MOZ_ASSERT(cycleStackMarker == ps->mCycleMarker);
+    // If there are DelayNodes in this SCC, then they may break the cycles.
+    bool haveDelayNode = false;
+    auto next = static_cast<ProcessedMediaStream*>(sccStack.getFirst());
+    // Streams in this SCC are identified by mCycleMarker <= cycleStackMarker.
+    // (There may be other streams later in sccStack from other incompletely
+    // searched SCCs, involving streams still on dfsStack.)
+    //
+    // DelayNodes in cycles must behave differently from those not in cycles,
+    // so all DelayNodes in the SCC must be identified.
+    while (next && next->mCycleMarker <= cycleStackMarker) {
+      auto ns = next->AsAudioNodeStream();
+      // Get next before perhaps removing from list below.
+      next = static_cast<ProcessedMediaStream*>(next->getNext());
+      if (ns && ns->Engine()->AsDelayNodeEngine()) {
+        haveDelayNode = true;
+        // DelayNodes break cycles by producing their output in a
+        // preprocessing phase; they do not need to be ordered before their
+        // consumers.  Order them at the tail of mStreams so that they can be
+        // handled specially.  Do so now, so that DFS ignores them.
+        ns->remove();
+        ns->mCycleMarker = 0;
+        --mFirstCycleBreaker;
+        mStreams[mFirstCycleBreaker] = ns;
+      }
+    }
+    auto after_scc = next;
+    while ((next = static_cast<ProcessedMediaStream*>(sccStack.popFirst()))
+           != after_scc) {
+      if (haveDelayNode) {
+        // Return streams to the DFS stack again (to order and detect cycles
+        // without delayNodes).  Any of these streams that are still inputs
+        // for streams on the visited stack must be returned to the front of
+        // the stack to be ordered before their dependents.  We know that none
+        // of these streams need input from streams on the visited stack, so
+        // they can all be searched and ordered before the current stack head
+        // is popped.
+        next->mCycleMarker = NOT_VISITED;
+        dfsStack.insertFront(next);
+      } else {
+        // Streams in cycles without any DelayNodes must be muted, and so do
+        // not need input and can be ordered now.  They must be ordered before
+        // their consumers so that their muted output is available.
+        next->mCycleMarker = IN_MUTED_CYCLE;
+        mStreams[orderedStreamCount] = next;
+        ++orderedStreamCount;
+      }
+    }
+  }
+
+  MOZ_ASSERT(orderedStreamCount == mFirstCycleBreaker);
 }
 
 void
 MediaStreamGraphImpl::RecomputeBlocking(GraphTime aEndBlockingDecisions)
 {
   bool blockingDecisionsWillChange = false;
 
   STREAM_LOG(PR_LOG_DEBUG+1, ("Media graph %p computing blocking for time %f",
@@ -1162,19 +1248,26 @@ RoundUpToNextAudioBlock(TrackRate aSampl
 }
 
 void
 MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
                                                         TrackRate aSampleRate,
                                                         GraphTime aFrom,
                                                         GraphTime aTo)
 {
+  MOZ_ASSERT(aStreamIndex <= mFirstCycleBreaker,
+             "Cycle breaker is not AudioNodeStream?");
   GraphTime t = aFrom;
   while (t < aTo) {
     GraphTime next = RoundUpToNextAudioBlock(aSampleRate, t);
+    for (uint32_t i = mFirstCycleBreaker; i < mStreams.Length(); ++i) {
+      auto ns = static_cast<AudioNodeStream*>(mStreams[i]);
+      MOZ_ASSERT(ns->AsAudioNodeStream());
+      ns->ProduceOutputBeforeInput(t);
+    }
     for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) {
       ProcessedMediaStream* ps = mStreams[i]->AsProcessedStream();
       if (ps) {
         ps->ProcessInput(t, next, (next == aTo) ? ProcessedMediaStream::ALLOW_FINISH : 0);
       }
     }
     t = next;
   }
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -655,19 +655,16 @@ protected:
    * unblock it until there's more data.
    */
   bool mHasCurrentData;
   /**
    * True if mHasCurrentData is true and we've notified listeners.
    */
   bool mNotifiedHasCurrentData;
 
-  // Temporary data for ordering streams by dependency graph
-  bool mHasBeenOrdered;
-  bool mIsOnOrderingStack;
   // True if the stream is being consumed (i.e. has track data being played,
   // or is feeding into some stream that is being consumed).
   bool mIsConsumed;
   // Temporary data for computing blocking status of streams
   // True if we've added this stream to the set of streams we're computing
   // blocking for.
   bool mInBlockingSet;
   // True if this stream should be blocked in this phase.
@@ -1014,17 +1011,17 @@ private:
 /**
  * This stream processes zero or more input streams in parallel to produce
  * its output. The details of how the output is produced are handled by
  * subclasses overriding the ProcessInput method.
  */
 class ProcessedMediaStream : public MediaStream {
 public:
   ProcessedMediaStream(DOMMediaStream* aWrapper)
-    : MediaStream(aWrapper), mAutofinish(false), mInCycle(false)
+    : MediaStream(aWrapper), mAutofinish(false)
   {}
 
   // Control API.
   /**
    * Allocates a new input port attached to source aStream.
    * This stream can be removed by calling MediaInputPort::Remove().
    */
   already_AddRefed<MediaInputPort> AllocateInputPort(MediaStream* aStream,
@@ -1083,17 +1080,20 @@ public:
   virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0;
   void SetAutofinishImpl(bool aAutofinish) { mAutofinish = aAutofinish; }
 
   /**
    * Forward SetTrackEnabled() to the input MediaStream(s) and translate the ID
    */
   virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) {};
 
-  bool InCycle() const { return mInCycle; }
+  // Only valid after MediaStreamGraphImpl::UpdateStreamOrder() has run.
+  // A DelayNode is considered to break a cycle and so this will not return
+  // true for echo loops, only for muted cycles.
+  bool InMutedCycle() const { return mCycleMarker; }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
     size_t amount = MediaStream::SizeOfExcludingThis(aMallocSizeOf);
     // Not owned:
     // - mInputs elements
     amount += mInputs.SizeOfExcludingThis(aMallocSizeOf);
     return amount;
@@ -1105,19 +1105,20 @@ public:
   }
 
 protected:
   // This state is all accessed only on the media graph thread.
 
   // The list of all inputs that are currently enabled or waiting to be enabled.
   nsTArray<MediaInputPort*> mInputs;
   bool mAutofinish;
-  // True if and only if this stream is in a cycle.
-  // Updated by MediaStreamGraphImpl::UpdateStreamOrder.
-  bool mInCycle;
+  // After UpdateStreamOrder(), mCycleMarker is either 0 or 1 to indicate
+  // whether this stream is in a muted cycle.  During ordering it can contain
+  // other marker values - see MediaStreamGraphImpl::UpdateStreamOrder().
+  uint32_t mCycleMarker;
 };
 
 /**
  * Initially, at least, we will have a singleton MediaStreamGraph per
  * process.  Each OfflineAudioContext object creates its own MediaStreamGraph
  * object too.
  */
 class MediaStreamGraph {
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -227,22 +227,16 @@ public:
    */
   void ExtractPendingInput(SourceMediaStream* aStream,
                            GraphTime aDesiredUpToTime,
                            bool* aEnsureNextIteration);
   /**
    * Update "have enough data" flags in aStream.
    */
   void UpdateBufferSufficiencyState(SourceMediaStream* aStream);
-  /*
-   * If aStream hasn't already been ordered, push it onto aStack and order
-   * its children.
-   */
-  void UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream>* aStack,
-                                  already_AddRefed<MediaStream> aStream);
   /**
    * Mark aStream and all its inputs (recursively) as consumed.
    */
   static void MarkConsumed(MediaStream* aStream);
   /**
    * Sort mStreams so that every stream not in a cycle is after any streams
    * it depends on, and every stream in a cycle is marked as being in a cycle.
    * Also sets mIsConsumed on every stream.
@@ -421,22 +415,28 @@ public:
    * Readonly after initialization on the main thread.
    */
   nsCOMPtr<nsIThread> mThread;
 
   // The following state is managed on the graph thread only, unless
   // mLifecycleState > LIFECYCLE_RUNNING in which case the graph thread
   // is not running and this state can be used from the main thread.
 
-  nsTArray<nsRefPtr<MediaStream> > mStreams;
   /**
-   * mOldStreams is used as temporary storage for streams when computing the
-   * order in which we compute them.
+   * The graph keeps a reference to each stream.
+   * References are maintained manually to simplify reordering without
+   * unnecessary thread-safe refcount changes.
    */
-  nsTArray<nsRefPtr<MediaStream> > mOldStreams;
+  nsTArray<MediaStream*> mStreams;
+  /**
+   * Streams from mFirstCycleBreaker to the end of mStreams produce output
+   * before they receive input.  They correspond to DelayNodes that are in
+   * cycles.
+   */
+  uint32_t mFirstCycleBreaker;
   /**
    * The current graph time for the current iteration of the RunThread control
    * loop.
    */
   GraphTime mCurrentTime;
   /**
    * Blocking decisions and all stream contents have been computed up to this
    * time. The next batch of updates from the main thread will be processed
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -63,72 +63,75 @@ public:
   bool IsWaitingMediaResources() MOZ_OVERRIDE
   {
     return mDecoders.IsEmpty() && mPendingDecoders.IsEmpty();
   }
 
   void RequestAudioData() MOZ_OVERRIDE
   {
     if (!GetAudioReader()) {
-      MSE_DEBUG("%p DecodeAudioFrame called with no audio reader", this);
+      MSE_DEBUG("%p MSR::RequestAudioData called with no audio reader", this);
       MOZ_ASSERT(mPendingDecoders.IsEmpty());
       GetCallback()->OnDecodeError();
       return;
     }
     GetAudioReader()->RequestAudioData();
   }
 
   void OnAudioDecoded(AudioData* aSample)
   {
     GetCallback()->OnAudioDecoded(aSample);
   }
 
   void OnAudioEOS()
   {
+    MSE_DEBUG("%p OnAudioEOS %d (%p) EOS (readers=%u)",
+              this, mActiveAudioDecoder, mDecoders[mActiveAudioDecoder].get(), mDecoders.Length());
     GetCallback()->OnAudioEOS();
   }
 
   void RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE
   {
     if (!GetVideoReader()) {
-      MSE_DEBUG("%p DecodeVideoFrame called with no video reader", this);
+      MSE_DEBUG("%p MSR::RequestVideoData called with no video reader", this);
       MOZ_ASSERT(mPendingDecoders.IsEmpty());
       GetCallback()->OnDecodeError();
       return;
     }
     mTimeThreshold = aTimeThreshold;
+    SwitchVideoReaders(SWITCH_OPTIONAL);
     GetVideoReader()->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
   }
 
   void OnVideoDecoded(VideoData* aSample)
   {
     if (mDropVideoBeforeThreshold) {
       if (aSample->mTime < mTimeThreshold) {
+        MSE_DEBUG("%p MSR::OnVideoDecoded VideoData mTime %lld below mTimeThreshold %lld",
+                  this, aSample->mTime, mTimeThreshold);
         delete aSample;
         GetVideoReader()->RequestVideoData(false, mTimeThreshold);
-      } else {
-        mDropVideoBeforeThreshold = false;
-        GetCallback()->OnVideoDecoded(aSample);
+        return;
       }
-    } else {
-      GetCallback()->OnVideoDecoded(aSample);
+      mDropVideoBeforeThreshold = false;
     }
+    GetCallback()->OnVideoDecoded(aSample);
   }
 
   void OnVideoEOS()
   {
     // End of stream. See if we can switch to another video decoder.
-    MSE_DEBUG("%p MSR::DecodeVF %d (%p) returned false (readers=%u)",
+    MSE_DEBUG("%p MSR::OnVideoEOS %d (%p) (readers=%u)",
               this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
-    if (MaybeSwitchVideoReaders()) {
+    if (SwitchVideoReaders(SWITCH_FORCED)) {
       // Success! Resume decoding with next video decoder.
       RequestVideoData(false, mTimeThreshold);
     } else {
       // End of stream.
-      MSE_DEBUG("%p MSR::DecodeVF %d (%p) EOS (readers=%u)",
+      MSE_DEBUG("%p MSR::OnVideoEOS %d (%p) EOS (readers=%u)",
                 this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
       GetCallback()->OnVideoEOS();
     }
   }
 
   void OnDecodeError() {
     GetCallback()->OnDecodeError();
   }
@@ -170,43 +173,43 @@ public:
   void InitializePendingDecoders();
 
   bool IsShutdown() {
     ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
     return mDecoder->IsShutdown();
   }
 
 private:
-
   // These are read and written on the decode task queue threads.
   int64_t mTimeThreshold;
   bool mDropVideoBeforeThreshold;
 
-  enum SwitchState {
-    SWITCHSTATE_SEEKING,
-    SWITCHSTATE_PLAYING
+  enum SwitchType {
+    SWITCH_OPTIONAL,
+    SWITCH_FORCED
   };
 
-  bool MaybeSwitchVideoReaders(SwitchState aState = SWITCHSTATE_PLAYING) {
+  bool SwitchVideoReaders(SwitchType aType) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     MOZ_ASSERT(mActiveVideoDecoder != -1);
 
     InitializePendingDecoders();
 
     for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
       SubBufferDecoder* decoder = mDecoders[i];
       if (decoder->IsDiscarded() || !decoder->GetReader()->GetMediaInfo().HasVideo()) {
         continue;
       }
 
-      if (aState == SWITCHSTATE_SEEKING || mTimeThreshold >= mDecoders[i]->GetMediaStartTime()) {
+      if (aType == SWITCH_FORCED || mTimeThreshold >= mDecoders[i]->GetMediaStartTime()) {
         GetVideoReader()->SetIdle();
 
         mActiveVideoDecoder = i;
-        MSE_DEBUG("%p MSR::DecodeVF switching to %d", this, mActiveVideoDecoder);
+        mDropVideoBeforeThreshold = true;
+        MSE_DEBUG("%p MSR::SwitchVideoReaders(%d) switching to %d", this, aType, mActiveVideoDecoder);
         return true;
       }
     }
 
     return false;
   }
 
   MediaDecoderReader* GetAudioReader() {
@@ -479,17 +482,17 @@ MediaSourceReader::Seek(int64_t aTime, i
 
   // Loop until we have the requested time range in the source buffers.
   // This is a workaround for our lack of async functionality in the
   // MediaDecoderStateMachine. Bug 979104 implements what we need and
   // we'll remove this for an async approach based on that in bug XXXXXXX.
   while (!mMediaSource->ActiveSourceBuffers()->AllContainsTime(target)
          && !IsShutdown()) {
     mMediaSource->WaitForData();
-    MaybeSwitchVideoReaders(SWITCHSTATE_SEEKING);
+    SwitchVideoReaders(SWITCH_FORCED);
   }
 
   if (IsShutdown()) {
     return NS_OK;
   }
 
   ResetDecode();
   if (GetAudioReader()) {
--- a/content/media/webaudio/AudioNode.h
+++ b/content/media/webaudio/AudioNode.h
@@ -102,20 +102,16 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioNode,
                                            DOMEventTargetHelper)
 
   virtual AudioBufferSourceNode* AsAudioBufferSourceNode() {
     return nullptr;
   }
 
-  virtual const DelayNode* AsDelayNode() const {
-    return nullptr;
-  }
-
   AudioContext* GetParentObject() const
   {
     return mContext;
   }
 
   AudioContext* Context() const
   {
     return mContext;
--- a/content/media/webaudio/DelayNode.cpp
+++ b/content/media/webaudio/DelayNode.cpp
@@ -37,17 +37,17 @@ public:
     // Keep the default value in sync with the default value in DelayNode::DelayNode.
     , mDelay(0.f)
     // Use a smoothing range of 20ms
     , mBuffer(std::max(aMaxDelayTicks,
                        static_cast<double>(WEBAUDIO_BLOCK_SIZE)),
               WebAudioUtils::ComputeSmoothingRate(0.02,
                                                   mDestination->SampleRate()))
     , mMaxDelay(aMaxDelayTicks)
-    , mLastOutputPosition(-1)
+    , mHaveProducedBeforeInput(false)
     , mLeftOverData(INT32_MIN)
   {
   }
 
   virtual DelayNodeEngine* AsDelayNodeEngine()
   {
     return this;
   }
@@ -105,63 +105,62 @@ public:
           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
       }
       *aOutput = aInput;
       return;
     }
 
     mBuffer.Write(aInput);
 
-    UpdateOutputBlock(aOutput);
+    // Skip output update if mLastChunks has already been set by
+    // ProduceBlockBeforeInput() when in a cycle.
+    if (!mHaveProducedBeforeInput) {
+      UpdateOutputBlock(aOutput, 0.0);
+    }
+    mHaveProducedBeforeInput = false;
     mBuffer.NextBlock();
   }
 
-  void UpdateOutputBlock(AudioChunk* aOutput)
+  void UpdateOutputBlock(AudioChunk* aOutput, double minDelay)
   {
-    TrackTicks tick = mSource->GetCurrentPosition();
-    if (tick == mLastOutputPosition) {
-      return; // mLastChunks is already set on the stream
-    }
-
-    mLastOutputPosition = tick;
-    bool inCycle = mSource->AsProcessedStream()->InCycle();
-    double minDelay = inCycle ? static_cast<double>(WEBAUDIO_BLOCK_SIZE) : 0.0;
     double maxDelay = mMaxDelay;
     double sampleRate = mSource->SampleRate();
     ChannelInterpretation channelInterpretation =
       mSource->GetChannelInterpretation();
     if (mDelay.HasSimpleValue()) {
       // If this DelayNode is in a cycle, make sure the delay value is at least
       // one block, even if that is greater than maxDelay.
       double delayFrames = mDelay.GetValue() * sampleRate;
       double delayFramesClamped =
         std::max(minDelay, std::min(delayFrames, maxDelay));
       mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation);
     } else {
       // Compute the delay values for the duration of the input AudioChunk
       // If this DelayNode is in a cycle, make sure the delay value is at least
       // one block.
+      TrackTicks tick = mSource->GetCurrentPosition();
       double computedDelay[WEBAUDIO_BLOCK_SIZE];
       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
         double delayAtTick = mDelay.GetValueAtTime(tick, counter) * sampleRate;
         double delayAtTickClamped =
           std::max(minDelay, std::min(delayAtTick, maxDelay));
         computedDelay[counter] = delayAtTickClamped;
       }
       mBuffer.Read(computedDelay, aOutput, channelInterpretation);
     }
   }
 
   virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) MOZ_OVERRIDE
   {
     if (mLeftOverData <= 0) {
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     } else {
-      UpdateOutputBlock(aOutput);
+      UpdateOutputBlock(aOutput, WEBAUDIO_BLOCK_SIZE);
     }
+    mHaveProducedBeforeInput = true;
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     // Not owned:
     // - mSource - probably not owned
     // - mDestination - probably not owned
@@ -175,17 +174,17 @@ public:
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   AudioParamTimeline mDelay;
   DelayBuffer mBuffer;
   double mMaxDelay;
-  TrackTicks mLastOutputPosition;
+  bool mHaveProducedBeforeInput;
   // How much data we have in our buffer which needs to be flushed out when our inputs
   // finish.
   int32_t mLeftOverData;
 };
 
 DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
   : AudioNode(aContext,
               2,
--- a/content/media/webaudio/DelayNode.h
+++ b/content/media/webaudio/DelayNode.h
@@ -25,21 +25,16 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   AudioParam* DelayTime() const
   {
     return mDelay;
   }
 
-  virtual const DelayNode* AsDelayNode() const MOZ_OVERRIDE
-  {
-    return this;
-  }
-
   virtual const char* NodeType() const
   {
     return "DelayNode";
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
 
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -35,37 +35,47 @@ enum MediaEngineState {
 };
 
 // We only support 1 audio and 1 video track for now.
 enum {
   kVideoTrack = 1,
   kAudioTrack = 2
 };
 
+// includes everything from dom::MediaSourceEnum (really video sources), plus audio sources
+enum MediaSourceType {
+  Camera = (int) dom::MediaSourceEnum::Camera,
+  Screen = (int) dom::MediaSourceEnum::Screen,
+  Application = (int) dom::MediaSourceEnum::Application,
+  Window, // = (int) dom::MediaSourceEnum::Window, // XXX bug 1038926
+  //Browser = (int) dom::MediaSourceEnum::Browser, // proposed in WG, unclear if it's useful
+  Microphone
+};
+
 class MediaEngine
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine)
 
   static const int DEFAULT_VIDEO_FPS = 30;
   static const int DEFAULT_VIDEO_MIN_FPS = 10;
   static const int DEFAULT_43_VIDEO_WIDTH = 640;
   static const int DEFAULT_43_VIDEO_HEIGHT = 480;
   static const int DEFAULT_169_VIDEO_WIDTH = 1280;
   static const int DEFAULT_169_VIDEO_HEIGHT = 720;
   static const int DEFAULT_AUDIO_TIMER_MS = 10;
 
   /* Populate an array of video sources in the nsTArray. Also include devices
    * that are currently unavailable. */
-  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+  virtual void EnumerateVideoDevices(MediaSourceType,
                                      nsTArray<nsRefPtr<MediaEngineVideoSource> >*) = 0;
 
   /* Populate an array of audio sources in the nsTArray. Also include devices
    * that are currently unavailable. */
-  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+  virtual void EnumerateAudioDevices(MediaSourceType,
                                      nsTArray<nsRefPtr<MediaEngineAudioSource> >*) = 0;
 
 protected:
   virtual ~MediaEngine() {}
 };
 
 /**
  * Common abstract base class for audio and video sources.
@@ -114,16 +124,19 @@ public:
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) = 0;
 
   /* Returns true if a source represents a fake capture device and
    * false otherwise
    */
   virtual bool IsFake() = 0;
 
+  /* Returns the type of media source (camera, microphone, screen, window, etc) */
+  virtual const MediaSourceType GetMediaSource() = 0;
+
   /* Return false if device is currently allocated or started */
   bool IsAvailable() {
     if (mState == kAllocated || mState == kStarted) {
       return false;
     } else {
       return true;
     }
   }
@@ -180,18 +193,18 @@ private:
   }
 };
 
 class MediaEngineVideoSource : public MediaEngineSource
 {
 public:
   virtual ~MediaEngineVideoSource() {}
 
-  virtual const dom::MediaSourceEnum GetMediaSource() {
-      return dom::MediaSourceEnum::Camera;
+  virtual const MediaSourceType GetMediaSource() {
+      return MediaSourceType::Camera;
   }
   /* This call reserves but does not start the device. */
   virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs) = 0;
 };
 
 /**
  * Audio source and friends.
--- a/content/media/webrtc/MediaEngineDefault.cpp
+++ b/content/media/webrtc/MediaEngineDefault.cpp
@@ -473,37 +473,37 @@ MediaEngineDefaultAudioSource::Notify(ns
   channels.AppendElement(dest);
   segment.AppendFrames(buffer.forget(), channels, AUDIO_FRAME_LENGTH);
   mSource->AppendToTrack(mTrackID, &segment);
 
   return NS_OK;
 }
 
 void
-MediaEngineDefault::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
+MediaEngineDefault::EnumerateVideoDevices(MediaSourceType aMediaSource,
                                           nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
   MutexAutoLock lock(mMutex);
 
   // only supports camera sources (for now).  See Bug 1038241
-  if (aMediaSource != dom::MediaSourceEnum::Camera) {
+  if (aMediaSource != MediaSourceType::Camera) {
     return;
   }
 
   // We once had code here to find a VideoSource with the same settings and re-use that.
   // This no longer is possible since the resolution is being set in Allocate().
 
   nsRefPtr<MediaEngineVideoSource> newSource = new MediaEngineDefaultVideoSource();
   mVSources.AppendElement(newSource);
   aVSources->AppendElement(newSource);
 
   return;
 }
 
 void
-MediaEngineDefault::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
+MediaEngineDefault::EnumerateAudioDevices(MediaSourceType aMediaSource,
                                           nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
   MutexAutoLock lock(mMutex);
   int32_t len = mASources.Length();
 
   // aMediaSource is ignored for audio devices (for now).
 
   for (int32_t i = 0; i < len; i++) {
     nsRefPtr<MediaEngineAudioSource> source = mASources.ElementAt(i);
--- a/content/media/webrtc/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -56,16 +56,20 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime);
 
   virtual bool IsFake() {
     return true;
   }
 
+  virtual const MediaSourceType GetMediaSource() {
+    return MediaSourceType::Camera;
+  }
+
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
 protected:
   ~MediaEngineDefaultVideoSource();
 
   friend class MediaEngineDefault;
 
@@ -112,16 +116,20 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime) {}
 
   virtual bool IsFake() {
     return true;
   }
 
+  virtual const MediaSourceType GetMediaSource() {
+    return MediaSourceType::Microphone;
+  }
+
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
 protected:
   ~MediaEngineDefaultAudioSource();
 
   TrackID mTrackID;
   nsCOMPtr<nsITimer> mTimer;
@@ -133,19 +141,19 @@ protected:
 
 class MediaEngineDefault : public MediaEngine
 {
 public:
   MediaEngineDefault()
   : mMutex("mozilla::MediaEngineDefault")
   {}
 
-  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+  virtual void EnumerateVideoDevices(MediaSourceType,
                                      nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
-  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+  virtual void EnumerateAudioDevices(MediaSourceType,
                                      nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 
 private:
   ~MediaEngineDefault() {}
 
   Mutex mMutex;
   // protected with mMutex:
 
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -42,16 +42,17 @@ GetUserMediaLog()
 #undef LOG
 #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
 
 namespace mozilla {
 
 MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
     : mMutex("mozilla::MediaEngineWebRTC")
     , mScreenEngine(nullptr)
+    , mWinEngine(nullptr)
     , mAppEngine(nullptr)
     , mVideoEngine(nullptr)
     , mVoiceEngine(nullptr)
     , mVideoEngineInit(false)
     , mAudioEngineInit(false)
     , mScreenEngineInit(false)
     , mAppEngineInit(false)
 {
@@ -67,24 +68,24 @@ MediaEngineWebRTC::MediaEngineWebRTC(Med
   // XXX
   gFarendObserver = new AudioOutputObserver();
 
   NS_NewNamedThread("AudioGUM", getter_AddRefs(mThread));
   MOZ_ASSERT(mThread);
 }
 
 void
-MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
+MediaEngineWebRTC::EnumerateVideoDevices(MediaSourceType aMediaSource,
                                          nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
  #ifdef MOZ_B2G_CAMERA
-  if (aMediaSource != dom::MediaSourceEnum::Camera) {
+  if (aMediaSource != MediaSourceType::Camera) {
     // only supports camera sources
     return;
   }
 
   /**
    * We still enumerate every time, in case a new device was plugged in since
    * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
    * new devices (with or without new engine creation) and accordingly adjust.
@@ -132,39 +133,50 @@ MediaEngineWebRTC::EnumerateVideoDevices
 
   if (webrtc::VideoEngine::SetAndroidObjects(jvm) != 0) {
     LOG(("VieCapture:SetAndroidObjects Failed"));
     return;
   }
 #endif
 
   switch (aMediaSource) {
-    case dom::MediaSourceEnum::Application:
+    case MediaSourceType::Window:
+      mWinEngineConfig.Set<webrtc::CaptureDeviceInfo>(
+          new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Window));
+      if (!mWinEngine) {
+        if (!(mWinEngine = webrtc::VideoEngine::Create(mWinEngineConfig))) {
+          return;
+        }
+      }
+      videoEngine = mWinEngine;
+      videoEngineInit = &mWinEngineInit;
+      break;
+    case MediaSourceType::Application:
       mAppEngineConfig.Set<webrtc::CaptureDeviceInfo>(
           new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Application));
       if (!mAppEngine) {
         if (!(mAppEngine = webrtc::VideoEngine::Create(mAppEngineConfig))) {
           return;
         }
       }
       videoEngine = mAppEngine;
       videoEngineInit = &mAppEngineInit;
       break;
-    case dom::MediaSourceEnum::Screen:
+    case MediaSourceType::Screen:
       mScreenEngineConfig.Set<webrtc::CaptureDeviceInfo>(
           new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Screen));
       if (!mScreenEngine) {
         if (!(mScreenEngine = webrtc::VideoEngine::Create(mScreenEngineConfig))) {
           return;
         }
       }
       videoEngine = mScreenEngine;
       videoEngineInit = &mScreenEngineInit;
       break;
-    case dom::MediaSourceEnum::Camera:
+    case MediaSourceType::Camera:
       // fall through
     default:
       if (!mVideoEngine) {
         if (!(mVideoEngine = webrtc::VideoEngine::Create())) {
           return;
         }
       }
       videoEngine = mVideoEngine;
@@ -254,17 +266,17 @@ MediaEngineWebRTC::EnumerateVideoDevices
   if (mHasTabVideoSource)
     aVSources->AppendElement(new MediaEngineTabVideoSource());
 
   return;
 #endif
 }
 
 void
-MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
+MediaEngineWebRTC::EnumerateAudioDevices(MediaSourceType aMediaSource,
                                          nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources)
 {
   ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
   ScopedCustomReleasePtr<webrtc::VoEHardware> ptrVoEHw;
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
 #ifdef MOZ_WIDGET_ANDROID
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -91,17 +91,17 @@ class MediaEngineWebRTCVideoSource : pub
                                    , public mozilla::hal::ScreenConfigurationObserver
 #else
                                    , public webrtc::ExternalRenderer
 #endif
 {
 public:
 #ifdef MOZ_B2G_CAMERA
   MediaEngineWebRTCVideoSource(int aIndex,
-                               dom::MediaSourceEnum aMediaSource = dom::MediaSourceEnum::Camera)
+                               MediaSourceType aMediaSource = MediaSourceType::Camera)
     : mCameraControl(nullptr)
     , mCallbackMonitor("WebRTCCamera.CallbackMonitor")
     , mRotation(0)
     , mBackCamera(false)
     , mCaptureIndex(aIndex)
     , mMediaSource(aMediaSource)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
@@ -122,17 +122,17 @@ public:
   /**
    * Does DeliverFrame() support a null buffer and non-null handle
    * (video texture)?
    * XXX Investigate!  Especially for Android/B2G
    */
   virtual bool IsTextureSupported() { return false; }
 
   MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr, int aIndex,
-                               dom::MediaSourceEnum aMediaSource = dom::MediaSourceEnum::Camera)
+                               MediaSourceType aMediaSource = MediaSourceType::Camera)
     : mVideoEngine(aVideoEnginePtr)
     , mCaptureIndex(aIndex)
     , mFps(-1)
     , mMinFps(-1)
     , mMediaSource(aMediaSource)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
@@ -164,17 +164,17 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime);
 
   virtual bool IsFake() {
     return false;
   }
 
-  virtual const dom::MediaSourceEnum GetMediaSource() {
+  virtual const MediaSourceType GetMediaSource() {
     return mMediaSource;
   }
 
 #ifndef MOZ_B2G_CAMERA
   NS_DECL_THREADSAFE_ISUPPORTS
 #else
   // We are subclassed from CameraControlListener, which implements a
   // threadsafe reference-count for us.
@@ -242,17 +242,17 @@ private:
   webrtc::ViECapture* mViECapture;
   webrtc::ViERender* mViERender;
 #endif
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
 
   int mCaptureIndex;
   int mFps; // Track rate (30 fps by default)
   int mMinFps; // Min rate we want to accept
-  dom::MediaSourceEnum mMediaSource; // source of media (camera | application | screen)
+  MediaSourceType mMediaSource; // source of media (camera | application | screen)
 
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
   // image changes).  Note that mSources is not accessed from other threads
   // for video and is not protected.
   Monitor mMonitor; // Monitor for processing WebRTC frames.
   int mWidth, mHeight;
   nsRefPtr<layers::Image> mImage;
@@ -322,16 +322,20 @@ public:
                           TrackID aId,
                           StreamTime aDesiredTime,
                           TrackTicks &aLastEndTime);
 
   virtual bool IsFake() {
     return false;
   }
 
+  virtual const MediaSourceType GetMediaSource() {
+    return MediaSourceType::Microphone;
+  }
+
   // VoEMediaProcess.
   void Process(int channel, webrtc::ProcessingTypes type,
                int16_t audio10ms[], int length,
                int samplingFreq, bool isStereo);
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
 protected:
@@ -385,19 +389,19 @@ class MediaEngineWebRTC : public MediaEn
 {
 public:
   MediaEngineWebRTC(MediaEnginePrefs &aPrefs);
 
   // Clients should ensure to clean-up sources video/audio sources
   // before invoking Shutdown on this class.
   void Shutdown();
 
-  virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
+  virtual void EnumerateVideoDevices(MediaSourceType,
                                     nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
-  virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
+  virtual void EnumerateAudioDevices(MediaSourceType,
                                     nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 private:
   ~MediaEngineWebRTC() {
     Shutdown();
 #ifdef MOZ_B2G_CAMERA
     AsyncLatencyLogger::Get()->Release();
 #endif
     // XXX
@@ -405,28 +409,31 @@ private:
   }
 
   nsCOMPtr<nsIThread> mThread;
 
   Mutex mMutex;
 
   // protected with mMutex:
   webrtc::VideoEngine* mScreenEngine;
+  webrtc::VideoEngine* mWinEngine;
   webrtc::VideoEngine* mAppEngine;
   webrtc::VideoEngine* mVideoEngine;
   webrtc::VoiceEngine* mVoiceEngine;
 
   // specialized configurations
   webrtc::Config mAppEngineConfig;
+  webrtc::Config mWinEngineConfig;
   webrtc::Config mScreenEngineConfig;
 
   // Need this to avoid unneccesary WebRTC calls while enumerating.
   bool mVideoEngineInit;
   bool mAudioEngineInit;
   bool mScreenEngineInit;
+  bool mWinEngineInit;
   bool mAppEngineInit;
   bool mHasTabVideoSource;
 
   // Store devices we've already seen in a hashtable for quick return.
   // Maps UUID to MediaEngineSource (one set for audio, one for video).
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
 };
--- a/content/media/webrtc/MediaTrackConstraints.h
+++ b/content/media/webrtc/MediaTrackConstraints.h
@@ -41,16 +41,18 @@ public:
           mUnsupportedRequirement = true;
         }
       }
     }
 
     // treat MediaSource special because it's always required
     mRequired.mMediaSource = mMediaSource;
 
+    // we guarantee (int) equivalence from MediaSourceEnum ->MediaSourceType
+    // (but not the other way)
     if (mMediaSource != dom::MediaSourceEnum::Camera && mAdvanced.WasPassed()) {
       // iterate through advanced, forcing mediaSource to match "root"
       auto& array = mAdvanced.Value();
       for (uint32_t i = 0; i < array.Length(); i++) {
         if (array[i].mMediaSource == dom::MediaSourceEnum::Camera) {
           array[i].mMediaSource = mMediaSource;
         }
       }
--- a/content/media/webspeech/synth/ipc/test/file_ipc.html
+++ b/content/media/webspeech/synth/ipc/test/file_ipc.html
@@ -32,44 +32,40 @@
         sendAsyncMessage("test:SpeechSynthesis:ipcTestComplete", {
           result: JSON.stringify(TestRunner._failedTests)
         });
 
         if (oldComplete) {
           oldComplete();
         }
       };
-
-      let oldLog = TestRunner.log;
-      TestRunner.log = function(msg) {
+      TestRunner.structuredLogger._dumpMessage = function(msg) {
         sendAsyncMessage("test:SpeechSynthesis:ipcTestMessage", { msg: msg });
       }
     }
 
-    let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
-
+    let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+    function validStructuredMessage(message) {
+      return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
+    }
     function onTestMessage(data) {
-      let message = SpecialPowers.wrap(data).json.msg;
-      let match = regex.exec(message);
-      if (match) {
-        let state = match[1];
-        let details = match[2] + " | " + match[3];
+      let message = SpecialPowers.wrap(data).data.msg;
 
-        switch (state) {
-          case "TEST-PASS":
-          case "TEST-KNOWN-FAIL":
-            ok(true, details);
-            break;
+      if (validStructuredMessage(message)) {
+        if (message.test === undefined || message.message === undefined) {
+            return;
+        }
 
-          case "TEST-UNEXPECTED-FAIL":
-          case "TEST-UNEXPECTED-PASS":
-            ok(false, details);
+        let details = message.test + " | " + message.message;
+
+        switch(message.action) {
+          case "test_status":
+          case "test_end":
+            ok(message.expected === undefined, message.test, message.message);
             break;
-
-          case "TEST-DEBUG-INFO":
           default:
             info(details);
         }
       }
     }
 
     function onTestComplete() {
       let comp = SpecialPowers.wrap(SpecialPowers.Components);
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -119,21 +119,34 @@ nsDefaultURIFixup::CreateExposableURI(ns
     return NS_OK;
 }
 
 /* nsIURI createFixupURI (in nsAUTF8String aURIText, in unsigned long aFixupFlags); */
 NS_IMETHODIMP
 nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupFlags,
                                   nsIInputStream **aPostData, nsIURI **aURI)
 {
+  nsCOMPtr<nsIURIFixupInfo> fixupInfo;
+  nsresult rv = GetFixupURIInfo(aStringURI, aFixupFlags, aPostData,
+                                getter_AddRefs(fixupInfo));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  fixupInfo->GetPreferredURI(aURI);
+  return rv;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI, uint32_t aFixupFlags,
+                                   nsIInputStream **aPostData, nsIURIFixupInfo **aInfo)
+{
     NS_ENSURE_ARG(!aStringURI.IsEmpty());
-    NS_ENSURE_ARG_POINTER(aURI);
 
     nsresult rv;
-    *aURI = nullptr;
+    nsRefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(aStringURI);
+    NS_ADDREF(*aInfo = info);
 
     nsAutoCString uriString(aStringURI);
     uriString.Trim(" ");  // Cleanup the empty spaces that might be on each end.
 
     // Eliminate embedded newlines, which single-line text fields now allow:
     uriString.StripChars("\r\n");
 
     NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
@@ -144,36 +157,45 @@ nsDefaultURIFixup::CreateFixupURI(const 
     ioService->ExtractScheme(aStringURI, scheme);
     
     // View-source is a pseudo scheme. We're interested in fixing up the stuff
     // after it. The easiest way to do that is to call this method again with the
     // "view-source:" lopped off and then prepend it again afterwards.
 
     if (scheme.LowerCaseEqualsLiteral("view-source"))
     {
-        nsCOMPtr<nsIURI> uri;
+        nsCOMPtr<nsIURIFixupInfo> uriInfo;
         uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
 
-        rv =  CreateFixupURI(Substring(uriString,
+        rv = GetFixupURIInfo(Substring(uriString,
                                        sizeof("view-source:") - 1,
                                        uriString.Length() -
                                          (sizeof("view-source:") - 1)),
-                             newFixupFlags, aPostData, getter_AddRefs(uri));
+                             newFixupFlags, aPostData, getter_AddRefs(uriInfo));
         if (NS_FAILED(rv))
             return NS_ERROR_FAILURE;
         nsAutoCString spec;
+        nsCOMPtr<nsIURI> uri;
+        uriInfo->GetPreferredURI(getter_AddRefs(uri));
+        if (!uri)
+            return NS_ERROR_FAILURE;
         uri->GetSpec(spec);
         uriString.AssignLiteral("view-source:");
         uriString.Append(spec);
     }
     else {
         // Check for if it is a file URL
-        FileURIFixup(uriString, aURI);
-        if(*aURI)
+        nsCOMPtr<nsIURI> uri;
+        FileURIFixup(uriString, getter_AddRefs(uri));
+        if (uri)
+        {
+            uri.swap(info->mFixedURI);
+            info->mPreferredURI = info->mFixedURI;
             return NS_OK;
+        }
 
 #if defined(XP_WIN)
         // Not a file URL, so translate '\' to '/' for convenience in the common protocols
         // e.g. catch
         //
         //   http:\\broken.com\address
         //   http:\\broken.com/blah
         //   broken.com\blah
@@ -218,16 +240,18 @@ nsDefaultURIFixup::CreateFixupURI(const 
                 "Failed to observe \"browser.fixup.typo.scheme\"");
 
       rv = Preferences::AddBoolVarCache(&sFixupKeywords, "keyword.enabled",
                                         sFixupKeywords);
       MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to observe \"keyword.enabled\"");
       sInitializedPrefCaches = true;
     }
 
+    info->mInputHasProtocol = !scheme.IsEmpty();
+
     // Fix up common scheme typos.
     if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
 
         // Fast-path for common cases.
         if (scheme.IsEmpty() ||
             scheme.LowerCaseEqualsLiteral("http") ||
             scheme.LowerCaseEqualsLiteral("https") ||
             scheme.LowerCaseEqualsLiteral("ftp") ||
@@ -261,120 +285,107 @@ nsDefaultURIFixup::CreateFixupURI(const 
     }
 
     // Now we need to check whether "scheme" is something we don't
     // really know about.
     nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
     
     ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
     extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default");
-    
+
+    nsCOMPtr<nsIURI> uri;
     if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
         // Just try to create an URL out of it
-        rv = NS_NewURI(aURI, uriString, nullptr);
+        rv = NS_NewURI(getter_AddRefs(uri), uriString, nullptr);
+        if (NS_SUCCEEDED(rv)) {
+            info->mFixedURI = uri;
+            // Figure out whether this had a domain or just a single hostname:
+            nsAutoCString host;
+            uri->GetHost(host);
+            info->mInputHostHasDot = host.FindChar('.') != kNotFound;
+        }
 
-        if (!*aURI && rv != NS_ERROR_MALFORMED_URI) {
+        if (!uri && rv != NS_ERROR_MALFORMED_URI) {
             return rv;
         }
     }
 
-    if (*aURI && ourHandler == extHandler && sFixupKeywords &&
+    if (uri && ourHandler == extHandler && sFixupKeywords &&
         (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
         nsCOMPtr<nsIExternalProtocolService> extProtService =
             do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
         if (extProtService) {
             bool handlerExists = false;
             rv = extProtService->ExternalProtocolHandlerExists(scheme.get(), &handlerExists);
             if (NS_FAILED(rv)) {
                 return rv;
             }
             // This basically means we're dealing with a theoretically valid
             // URI... but we have no idea how to load it. (e.g. "christmas:humbug")
             // It's more likely the user wants to search, and so we
             // chuck this over to their preferred search provider instead:
             if (!handlerExists) {
-                NS_RELEASE(*aURI);
-                KeywordToURI(uriString, aPostData, aURI);
+                nsresult rv = KeywordToURI(uriString, aPostData, getter_AddRefs(uri));
+                if (NS_SUCCEEDED(rv) && uri) {
+                  info->mFixupUsedKeyword = true;
+                }
             }
         }
     }
     
-    if (*aURI) {
+    if (uri) {
         if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI)
-            MakeAlternateURI(*aURI);
+            MakeAlternateURI(uri);
+        info->mPreferredURI = uri;
         return NS_OK;
     }
 
+    // Fix up protocol string before calling KeywordURIFixup, because
+    // it cares about the hostname of such URIs:
+    nsCOMPtr<nsIURI> uriWithProtocol;
+    // NB: this rv gets returned at the end of this method if we never
+    // do a keyword fixup after this (because the pref or the flags passed
+    // might not let us).
+    rv = FixupURIProtocol(uriString, getter_AddRefs(uriWithProtocol));
+    if (uriWithProtocol) {
+        info->mFixedURI = uriWithProtocol;
+        nsAutoCString host;
+        uriWithProtocol->GetHost(host);
+        info->mInputHostHasDot = host.FindChar('.') != kNotFound;
+    }
+
     // See if it is a keyword
     // Test whether keywords need to be fixed up
     if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP)) {
-        KeywordURIFixup(uriString, aPostData, aURI);
-        if(*aURI)
+        KeywordURIFixup(uriString, info, aPostData);
+        if (info->mPreferredURI)
             return NS_OK;
     }
 
-    // Prune duff protocol schemes
-    //
-    //   ://totallybroken.url.com
-    //   //shorthand.url.com
-    //
-    if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://")))
-    {
-        uriString = StringTail(uriString, uriString.Length() - 3);
-    }
-    else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//")))
-    {
-        uriString = StringTail(uriString, uriString.Length() - 2);
-    }
-
-    // Add ftp:// or http:// to front of url if it has no spec
-    //
-    // Should fix:
-    //
-    //   no-scheme.com
-    //   ftp.no-scheme.com
-    //   ftp4.no-scheme.com
-    //   no-scheme.com/query?foo=http://www.foo.com
-    //
-    int32_t schemeDelim = uriString.Find("://",0);
-    int32_t firstDelim = uriString.FindCharInSet("/:");
-    if (schemeDelim <= 0 ||
-        (firstDelim != -1 && schemeDelim > firstDelim)) {
-        // find host name
-        int32_t hostPos = uriString.FindCharInSet("/:?#");
-        if (hostPos == -1) 
-            hostPos = uriString.Length();
-
-        // extract host name
-        nsAutoCString hostSpec;
-        uriString.Left(hostSpec, hostPos);
-
-        // insert url spec corresponding to host name
-        if (IsLikelyFTP(hostSpec))
-            uriString.InsertLiteral("ftp://", 0);
-        else 
-            uriString.InsertLiteral("http://", 0);
-    } // end if checkprotocol
-
-    rv = NS_NewURI(aURI, uriString, nullptr);
-
     // Did the caller want us to try an alternative URI?
     // If so, attempt to fixup http://foo into http://www.foo.com
 
-    if (*aURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
-        MakeAlternateURI(*aURI);
+    if (info->mFixedURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
+        MakeAlternateURI(info->mFixedURI);
     }
 
     // If we still haven't been able to construct a valid URI, try to force a
     // keyword match.  This catches search strings with '.' or ':' in them.
-    if (!*aURI && sFixupKeywords)
+    if (info->mFixedURI)
+    {
+        info->mPreferredURI = info->mFixedURI;
+    }
+    else if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP))
     {
-        KeywordToURI(aStringURI, aPostData, aURI);
-        if(*aURI)
+        rv = KeywordToURI(aStringURI, aPostData, getter_AddRefs(info->mPreferredURI));
+        if (NS_SUCCEEDED(rv) && info->mPreferredURI)
+        {
+            info->mFixupUsedKeyword = true;
             return NS_OK;
+        }
     }
 
     return rv;
 }
 
 NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
                                               nsIInputStream **aPostData,
                                               nsIURI **aURI)
@@ -703,16 +714,71 @@ nsresult nsDefaultURIFixup::ConvertFileT
             NS_GetURLSpecFromFile(filePath, aOut);
             return NS_OK;
         }
     }
 
     return NS_ERROR_FAILURE;
 }
 
+
+nsresult
+nsDefaultURIFixup::FixupURIProtocol(const nsACString & aURIString,
+                                         nsIURI** aURI)
+{
+    nsAutoCString uriString(aURIString);
+    *aURI = nullptr;
+
+    // Prune duff protocol schemes
+    //
+    //   ://totallybroken.url.com
+    //   //shorthand.url.com
+    //
+    if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://")))
+    {
+        uriString = StringTail(uriString, uriString.Length() - 3);
+    }
+    else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//")))
+    {
+        uriString = StringTail(uriString, uriString.Length() - 2);
+    }
+
+    // Add ftp:// or http:// to front of url if it has no spec
+    //
+    // Should fix:
+    //
+    //   no-scheme.com
+    //   ftp.no-scheme.com
+    //   ftp4.no-scheme.com
+    //   no-scheme.com/query?foo=http://www.foo.com
+    //
+    int32_t schemeDelim = uriString.Find("://",0);
+    int32_t firstDelim = uriString.FindCharInSet("/:");
+    if (schemeDelim <= 0 ||
+        (firstDelim != -1 && schemeDelim > firstDelim)) {
+        // find host name
+        int32_t hostPos = uriString.FindCharInSet("/:?#");
+        if (hostPos == -1)
+            hostPos = uriString.Length();
+
+        // extract host name
+        nsAutoCString hostSpec;
+        uriString.Left(hostSpec, hostPos);
+
+        // insert url spec corresponding to host name
+        if (IsLikelyFTP(hostSpec))
+            uriString.InsertLiteral("ftp://", 0);
+        else
+            uriString.InsertLiteral("http://", 0);
+    } // end if checkprotocol
+
+    return NS_NewURI(aURI, uriString, nullptr);
+}
+
+
 bool nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString &aUrl)
 {
     // Oh dear, the protocol is invalid. Test if the protocol might
     // actually be a url without a protocol:
     //
     //   http://www.faqs.org/rfcs/rfc1738.html
     //   http://www.faqs.org/rfcs/rfc2396.html
     //
@@ -825,26 +891,27 @@ bool nsDefaultURIFixup::PossiblyByteExpa
         if (*iter >= 0x0080 && *iter <= 0x00FF)
             return true;
         ++iter;
     }
     return false;
 }
 
 void nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString,
-                                        nsIInputStream **aPostData,
-                                        nsIURI** aURI)
+                                        nsDefaultURIFixupInfo* aFixupInfo,
+                                        nsIInputStream **aPostData)
 {
     // These are keyword formatted strings
     // "what is mozilla"
     // "what is mozilla?"
     // "docshell site:mozilla.org" - has no dot/colon in the first space-separated substring
     // "?mozilla" - anything that begins with a question mark
     // "?site:mozilla.org docshell"
     // Things that have a quote before the first dot/colon
+    // "mozilla" - checked against a whitelist to see if it's a host or not
 
     // These are not keyword formatted strings
     // "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?"
     // "www.blah.com stuff"
     // "nonQualifiedHost:80" - first space-separated substring contains a colon, doesn't start with "?"
     // "nonQualifiedHost:80 args"
     // "nonQualifiedHost?"
     // "nonQualifiedHost?args"
@@ -860,28 +927,133 @@ void nsDefaultURIFixup::KeywordURIFixup(
     if (spaceLoc == 0) {
         // Treat this as not found
         spaceLoc = uint32_t(kNotFound);
     }
     uint32_t qMarkLoc = uint32_t(aURIString.FindChar('?'));
     uint32_t quoteLoc = std::min(uint32_t(aURIString.FindChar('"')),
                                uint32_t(aURIString.FindChar('\'')));
 
+    nsresult rv;
     if (((spaceLoc < dotLoc || quoteLoc < dotLoc) &&
          (spaceLoc < colonLoc || quoteLoc < colonLoc) &&
          (spaceLoc < qMarkLoc || quoteLoc < qMarkLoc)) ||
         qMarkLoc == 0)
     {
-        KeywordToURI(aURIString, aPostData, aURI);
+        rv = KeywordToURI(aURIString, aPostData,
+                          getter_AddRefs(aFixupInfo->mPreferredURI));
+        if (NS_SUCCEEDED(rv) && aFixupInfo->mPreferredURI)
+        {
+            aFixupInfo->mFixupUsedKeyword = true;
+        }
+    }
+    else if (dotLoc == uint32_t(kNotFound) && colonLoc == uint32_t(kNotFound) &&
+             qMarkLoc == uint32_t(kNotFound))
+    {
+        nsAutoCString asciiHost;
+        if (NS_SUCCEEDED(aFixupInfo->mFixedURI->GetAsciiHost(asciiHost)) &&
+            !asciiHost.IsEmpty())
+        {
+            // Check if this domain is whitelisted as an actual
+            // domain (which will prevent a keyword query)
+            nsAutoCString pref("browser.fixup.domainwhitelist.");
+            pref.Append(asciiHost);
+            if (Preferences::GetBool(pref.get(), false))
+            {
+                return;
+            }
+        }
+        // If we get here, we don't have a valid URI, or we did but the
+        // host is not whitelisted, so we do a keyword search *anyway*:
+        rv = KeywordToURI(aURIString, aPostData,
+                          getter_AddRefs(aFixupInfo->mPreferredURI));
+        if (NS_SUCCEEDED(rv) && aFixupInfo->mPreferredURI)
+        {
+            aFixupInfo->mFixupUsedKeyword = true;
+        }
     }
 }
 
-
 nsresult NS_NewURIFixup(nsIURIFixup **aURIFixup)
 {
     nsDefaultURIFixup *fixup = new nsDefaultURIFixup;
     if (fixup == nullptr)
     {
         return NS_ERROR_OUT_OF_MEMORY;
     }
     return fixup->QueryInterface(NS_GET_IID(nsIURIFixup), (void **) aURIFixup);
 }
 
+
+/* Implementation of nsIURIFixupInfo */
+NS_IMPL_ISUPPORTS(nsDefaultURIFixupInfo, nsIURIFixupInfo)
+
+nsDefaultURIFixupInfo::nsDefaultURIFixupInfo(const nsACString& aOriginalInput):
+    mFixupUsedKeyword(false),
+    mInputHasProtocol(false),
+    mInputHostHasDot(false)
+{
+  mOriginalInput = aOriginalInput;
+}
+
+
+nsDefaultURIFixupInfo::~nsDefaultURIFixupInfo()
+{
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetConsumer(nsISupports** aConsumer)
+{
+    *aConsumer = mConsumer;
+    NS_IF_ADDREF(*aConsumer);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::SetConsumer(nsISupports* aConsumer)
+{
+    mConsumer = aConsumer;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetPreferredURI(nsIURI** aPreferredURI)
+{
+    *aPreferredURI = mPreferredURI;
+    NS_IF_ADDREF(*aPreferredURI);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetFixedURI(nsIURI** aFixedURI)
+{
+    *aFixedURI = mFixedURI;
+    NS_IF_ADDREF(*aFixedURI);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetFixupUsedKeyword(bool* aOut)
+{
+    *aOut = mFixupUsedKeyword;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetInputHasProtocol(bool* aOut)
+{
+    *aOut = mInputHasProtocol;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetInputHostHasDot(bool* aOut)
+{
+    *aOut = mInputHostHasDot;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetOriginalInput(nsACString& aInput)
+{
+    aInput = mOriginalInput;
+    return NS_OK;
+}
--- a/docshell/base/nsDefaultURIFixup.h
+++ b/docshell/base/nsDefaultURIFixup.h
@@ -4,32 +4,59 @@
  * 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/. */
 
 #ifndef NSDEFAULTURIFIXUP_H
 #define NSDEFAULTURIFIXUP_H
 
 #include "nsIURIFixup.h"
 
+class nsDefaultURIFixupInfo;
+
 /* Header file */
 class nsDefaultURIFixup : public nsIURIFixup
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIURIFIXUP
 
     nsDefaultURIFixup();
 
 protected:
     virtual ~nsDefaultURIFixup();
 
 private:
     /* additional members */
     nsresult FileURIFixup(const nsACString &aStringURI, nsIURI** aURI);
     nsresult ConvertFileToStringURI(const nsACString& aIn, nsCString& aOut);
-    void KeywordURIFixup(const nsACString &aStringURI, nsIInputStream** aPostData, nsIURI** aURI);
+    nsresult FixupURIProtocol(const nsACString& aIn, nsIURI** aURI);
+    void KeywordURIFixup(const nsACString &aStringURI,
+                         nsDefaultURIFixupInfo* aFixupInfo,
+                         nsIInputStream** aPostData);
     bool PossiblyByteExpandedFileName(const nsAString& aIn);
     bool PossiblyHostPortUrl(const nsACString& aUrl);
     bool MakeAlternateURI(nsIURI *aURI);
     bool IsLikelyFTP(const nsCString& aHostSpec);
 };
 
+class nsDefaultURIFixupInfo : public nsIURIFixupInfo
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIURIFIXUPINFO
+
+    nsDefaultURIFixupInfo(const nsACString& aOriginalInput);
+
+    friend class nsDefaultURIFixup;
+
+protected:
+    virtual ~nsDefaultURIFixupInfo();
+
+private:
+    nsCOMPtr<nsISupports> mConsumer;
+    nsCOMPtr<nsIURI> mPreferredURI;
+    nsCOMPtr<nsIURI> mFixedURI;
+    bool mFixupUsedKeyword;
+    bool mInputHasProtocol;
+    bool mInputHostHasDot;
+    nsAutoCString mOriginalInput;
+};
 #endif
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4357,25 +4357,39 @@ nsDocShell::LoadURIWithBase(const char16
         uint32_t fixupFlags = 0;
         if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
           fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
         }
         if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
           fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS;
         }
         nsCOMPtr<nsIInputStream> fixupStream;
-        rv = sURIFixup->CreateFixupURI(uriString, fixupFlags,
-                                       getter_AddRefs(fixupStream),
-                                       getter_AddRefs(uri));
+        nsCOMPtr<nsIURIFixupInfo> fixupInfo;
+        rv = sURIFixup->GetFixupURIInfo(uriString, fixupFlags,
+                                        getter_AddRefs(fixupStream),
+                                        getter_AddRefs(fixupInfo));
+
+        if (NS_SUCCEEDED(rv)) {
+            fixupInfo->GetPreferredURI(getter_AddRefs(uri));
+            fixupInfo->SetConsumer(GetAsSupports(this));
+        }
+
         if (fixupStream) {
             // CreateFixupURI only returns a post data stream if it succeeded
             // and changed the URI, in which case we should override the
             // passed-in post data.
             postStream = fixupStream;
         }
+
+        if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
+            nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
+            if (serv) {
+                serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI);
+            }
+        }
     }
     // else no fixup service so just use the URI we created and see
     // what happens
 
     if (NS_ERROR_MALFORMED_URI == rv) {
         DisplayLoadError(rv, uri, aURI, nullptr);
     }
 
--- a/docshell/base/nsIURIFixup.idl
+++ b/docshell/base/nsIURIFixup.idl
@@ -5,19 +5,70 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIInputStream;
 
 /**
+ * Interface indicating what we found/corrected when fixing up a URI
+ */
+[scriptable, uuid(c9b6cc32-c24e-4283-adaa-9290577fd609)]
+interface nsIURIFixupInfo : nsISupports
+{
+  /**
+   * Consumer that asked for fixed up URI.
+   */
+  attribute nsISupports consumer;
+
+  /**
+   * Our best guess as to what URI the consumer will want. Might
+   * be null if we couldn't salvage anything (for instance, because
+   * the input was invalid as a URI and FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP
+   * was not passed)
+   */
+  readonly attribute nsIURI preferredURI;
+
+  /**
+   * The fixed-up original input, *never* using a keyword search.
+   * (might be null if the original input was not recoverable as
+   * a URL, e.g. "foo bar"!)
+   */
+  readonly attribute nsIURI fixedURI;
+
+  /**
+   * Whether the preferred option ended up using a keyword search.
+   */
+  readonly attribute boolean fixupUsedKeyword;
+
+  /**
+   * Whether we think there was a protocol specified in some way,
+   * even if we corrected it (e.g. "ttp://foo.com/bar")
+   */
+  readonly attribute boolean inputHasProtocol;
+
+  /**
+   * Whether the input included a dot in the hostname, e.g. "mozilla.org"
+   * rather than just "mozilla". This makes a difference in terms of when we
+   * decide to do a keyword search or not.
+   */
+  readonly attribute boolean inputHostHasDot;
+
+  /**
+   * The original input
+   */
+  readonly attribute AUTF8String originalInput;
+};
+
+
+/**
  * Interface implemented by objects capable of fixing up strings into URIs
  */
-[scriptable, uuid(731877f8-973b-414c-b772-9ca1f3fffb7e)]
+[scriptable, uuid(80d4932e-bb2e-4afb-98e0-de9cc9ea7d82)]
 interface nsIURIFixup : nsISupports
 {
     /** No fixup flags. */
     const unsigned long FIXUP_FLAG_NONE = 0;
 
     /**
      * Allow the fixup to use a keyword lookup service to complete the URI.
      * The fixup object implementer should honour this flag and only perform
@@ -59,16 +110,30 @@ interface nsIURIFixup : nsISupports
      * @param aFixupFlags Flags that govern ways the URI may be fixed up.
      * @param aPostData   The POST data to submit with the returned
      *                    URI (see nsISearchSubmission).
      */
     nsIURI createFixupURI(in AUTF8String aURIText, in unsigned long aFixupFlags,
                           [optional] out nsIInputStream aPostData);
 
     /**
+     * Same as createFixupURI, but returns information about what it corrected
+     * (e.g. whether we could rescue the URI or "just" generated a keyword
+     * search URI instead).
+     *
+     * @param aURIText    Candidate URI.
+     * @param aFixupFlags Flags that govern ways the URI may be fixed up.
+     * @param aPostData   The POST data to submit with the returned
+     *                    URI (see nsISearchSubmission).
+     */
+    nsIURIFixupInfo getFixupURIInfo(in AUTF8String aURIText,
+                                    in unsigned long aFixupFlags,
+                                    [optional] out nsIInputStream aPostData);
+
+    /**
      * Converts the specified keyword string into a URI.  Note that it's the
      * caller's responsibility to check whether keywords are enabled and
      * whether aKeyword is a sensible keyword.
      *
      * @param aKeyword  The keyword string to convert into a URI
      * @param aPostData The POST data to submit to the returned URI
      *                  (see nsISearchSubmission).
      *
new file mode 100644
--- /dev/null
+++ b/docshell/test/unit/test_nsDefaultURIFixup_info.js
@@ -0,0 +1,147 @@
+let urifixup = Cc["@mozilla.org/docshell/urifixup;1"].
+               getService(Ci.nsIURIFixup);
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+let prefList = ["browser.fixup.typo.scheme", "keyword.enabled"];
+for (let pref of prefList) {
+  Services.prefs.setBoolPref(pref, true);
+}
+
+const kSearchEngineID = "test_urifixup_search_engine";
+const kSearchEngineURL = "http://www.example.org/?search={searchTerms}";
+Services.search.addEngineWithDetails(kSearchEngineID, "", "", "", "get",
+                                     kSearchEngineURL);
+
+let oldDefaultEngine = Services.search.defaultEngine;
+Services.search.defaultEngine = Services.search.getEngineByName(kSearchEngineID);
+
+let selectedName = Services.search.defaultEngine.name;
+do_check_eq(selectedName, kSearchEngineID);
+
+do_register_cleanup(function() {
+  if (oldDefaultEngine) {
+    Services.search.defaultEngine = oldDefaultEngine;
+  }
+  let engine = Services.search.getEngineByName(kSearchEngineID);
+  if (engine) {
+    Services.search.removeEngine(engine);
+  }
+  Services.prefs.clearUserPref("keyword.enabled");
+  Services.prefs.clearUserPref("browser.fixup.typo.scheme");
+});
+
+let flagInputs = [
+  urifixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
+  urifixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI,
+  urifixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
+];
+
+flagInputs.concat([
+  flagInputs[0] | flagInputs[1],
+  flagInputs[1] | flagInputs[2],
+  flagInputs[0] | flagInputs[2],
+  flagInputs[0] | flagInputs[1] | flagInputs[2]
+]);
+
+let testcases = [
+  ["http://www.mozilla.org", "http://www.mozilla.org/"],
+  ["://www.mozilla.org", "http://www.mozilla.org/"],
+  ["www.mozilla.org", "http://www.mozilla.org/"],
+  ["http://mozilla/", "http://mozilla/"],
+  ["127.0.0.1", "http://127.0.0.1/"],
+  ["1234", "http://1234/"],
+  ["host/foo.txt", "http://host/foo.txt"],
+  ["mozilla", "http://mozilla/"],
+  ["mozilla is amazing", null],
+  ["", null],
+];
+
+if (Services.appinfo.OS.toLowerCase().startsWith("win")) {
+  testcases.push(["C:\\some\\file.txt", "file:///C:/some/file.txt"]);
+} else {
+  testcases.push(["/some/file.txt", "file:///some/file.txt"]);
+}
+
+function run_test() {
+  for (let [testInput, expectedFixedURI] of testcases) {
+    for (let flags of flagInputs) {
+      let info;
+      let fixupURIOnly = null;
+      try {
+        fixupURIOnly = urifixup.createFixupURI(testInput, flags);
+      } catch (ex) {
+        do_check_eq(expectedFixedURI, null);
+      }
+
+      try {
+        info = urifixup.getFixupURIInfo(testInput, flags);
+      } catch (ex) {
+        // Both APIs should return an error in the same cases.
+        do_check_eq(expectedFixedURI, null);
+        do_check_eq(fixupURIOnly, null);
+        continue;
+      }
+
+      // Both APIs should then also be using the same spec.
+      do_check_eq(fixupURIOnly.spec, info.preferredURI.spec);
+
+      let isFileURL = expectedFixedURI && expectedFixedURI.startsWith("file");
+
+      // Check the fixedURI:
+      let alternateURI = flags & urifixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI;
+      if (!isFileURL && alternateURI && !info.inputHostHasDot && info.fixedURI) {
+        let originalURI = Services.io.newURI(expectedFixedURI, null, null);
+        do_check_eq(info.fixedURI.host, "www." + originalURI.host + ".com");
+      } else {
+        do_check_eq(info.fixedURI && info.fixedURI.spec, expectedFixedURI);
+      }
+
+      // Check booleans on input:
+      if (isFileURL) {
+        do_check_eq(info.inputHasProtocol, testInput.startsWith("file:"));
+        do_check_eq(info.inputHostHasDot, false);
+      } else {
+        // The duff protocol doesn't count, so > 0 rather than -1:
+        do_check_eq(info.inputHasProtocol, testInput.indexOf(":") > 0);
+        let dotIndex = testInput.indexOf(".");
+        let slashIndex = testInput.replace("://", "").indexOf("/");
+        slashIndex = slashIndex == -1 ? testInput.length : slashIndex;
+        do_check_eq(info.inputHostHasDot, dotIndex != -1 && slashIndex > dotIndex);
+      }
+
+      let couldDoKeywordLookup = flags & urifixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
+      // Check the preferred URI
+      if (info.inputHostHasDot || info.inputHasProtocol) {
+        // In these cases, we should never be doing a keyword lookup and
+        // the fixed URI should be preferred:
+        do_check_eq(info.preferredURI.spec, info.fixedURI.spec);
+      } else if (!isFileURL && couldDoKeywordLookup && testInput.indexOf(".") == -1) {
+        // Otherwise, and assuming we're allowed, there will be a search URI:
+        let urlparamInput = testInput.replace(/ /g, '+');
+        let searchURL = kSearchEngineURL.replace("{searchTerms}", urlparamInput);
+        do_check_eq(info.preferredURI.spec, searchURL);
+      } else if (info.fixedURI) {
+        // This is for lack of keyword lookup, combined with hostnames with no
+        // protocol:
+        do_check_eq(info.fixedURI, info.preferredURI);
+        if (isFileURL) {
+          do_check_eq(info.fixedURI.host, "");
+        } else {
+          let hostMatch = testInput.match(/(?:[^:\/]*:\/\/)?([^\/]+)(\/|$)/);
+          let host = hostMatch ? hostMatch[1] : "";
+          if (alternateURI) {
+            do_check_eq(info.fixedURI.host, "www." + host + ".com");
+          } else {
+            do_check_eq(info.fixedURI.host, host);
+          }
+        }
+      } else {
+        do_check_true(false, "There should be no cases where we got here, " +
+                             "there's no keyword lookup, and no fixed URI." +
+                             "Offending input: " + testInput);
+      }
+      do_check_eq(testInput, info.originalInput);
+    }
+  }
+}
--- a/docshell/test/unit/xpcshell.ini
+++ b/docshell/test/unit/xpcshell.ini
@@ -2,13 +2,15 @@
 head = head_docshell.js
 tail = 
 
 [test_bug414201_jfif.js]
 [test_bug442584.js]
 [test_nsDefaultURIFixup.js]
 [test_nsDefaultURIFixup_search.js]
 skip-if = os == 'android'
+[test_nsDefaultURIFixup_info.js]
+skip-if = os == 'android'
 [test_nsIDownloadHistory.js]
 [test_pb_notification.js]
 # Bug 751575: unrelated JS changes cause timeouts on random platforms
 skip-if = true
 [test_privacy_transition.js]
--- a/dom/apps/tests/file_bug_945152.html
+++ b/dom/apps/tests/file_bug_945152.html
@@ -18,16 +18,23 @@
 
   function ok(p, msg) {
     if (p)
       sendMessage("OK: " + msg);
     else
       sendMessage("KO: " + msg);
   }
 
+  function is(a, b, msg) {
+    if (a == b)
+      sendMessage("OK: " + a + " == " + b + " - " + msg);
+    else
+      sendMessage("KO: " + a + " != " + b + " - " + msg);
+  }
+
   function testXHR(file, data_head, mapped, cb) {
     var xhr = new XMLHttpRequest();
     xhr.open('GET', file);
     xhr.responseType = 'arraybuffer';
     xhr.onreadystatechange = function xhrReadystatechange() {
       if (xhr.readyState !== xhr.DONE) {
         return;
       }
@@ -36,18 +43,18 @@
         if (mapped) {
           ok(ct.indexOf("mem-mapped") != -1, "Data is memory-mapped");
         } else {
           ok(ct.indexOf("mem-mapped") == -1, "Data is not memory-mapped");
         }
         var data = xhr.response;
         ok(data, "Data is non-null");
         var str = String.fromCharCode.apply(null, Uint8Array(data));
-        ok(str.length == data_head.length + gPaddingSize, "Data size is correct");
-        ok(str.slice(0, data_head.length) == data_head, "Data head is correct");
+        is(str.length, data_head.length + gPaddingSize, "Data size is correct");
+        is(str.slice(0, data_head.length), data_head, "Data head is correct");
         ok(str.slice(data_head.length) == gPadding, "Data padding is correct");
         cb();
       } else {
         ok(false, "XHR error: " + xhr.status + " - " + xhr.statusText + "\n");
         cb();
       }
     }
     xhr.send();
--- a/dom/apps/tests/test_bug_945152.html
+++ b/dom/apps/tests/test_bug_945152.html
@@ -86,18 +86,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     var req = navigator.mozApps.installPackage(gSJS + '?getManifest=1');
     req.onerror = mozAppsError;
     req.onsuccess = function() {
       ok(true, "Application installed");
     };
     yield undefined;
 
-    // Launch app.
-    launchApp(app, continueTest);
+    // Launch app non-OOP.
+    info("Launch app with non-OOP");
+    launchApp(app, continueTest, false);
+    yield undefined;
+
+    // Launch app OOP.
+    info("Launch app with OOP");
+    launchApp(app, continueTest, true);
     yield undefined;
 
     // Uninstall app.
     var req = navigator.mozApps.mgmt.uninstall(app);
     req.onerror = mozAppsError;
     req.onsuccess = continueTest;
     yield undefined;
 
@@ -111,19 +117,20 @@ https://bugzilla.mozilla.org/show_bug.cg
     var url = gSJS + '?createApp=1';
     xhr.addEventListener("load", function() { is(xhr.responseText, "OK", "createApp OK"); cb(); });
     xhr.addEventListener("error", event => xhrError(event, url));
     xhr.addEventListener("abort", event => xhrAbort(url));
     xhr.open('GET', url, true);
     xhr.send();
   }
 
-  function launchApp(app, cb) {
+  function launchApp(app, cb, oop) {
     // Set up the app.
     var ifr = document.createElement('iframe');
+    ifr.setAttribute('remote', oop ? 'true' : 'false');
     ifr.setAttribute('mozbrowser', 'true');
     ifr.setAttribute('mozapp', app.manifestURL);
     ifr.setAttribute('src', app.origin + app.manifest.launch_path);
     var domParent = document.getElementById('container');
 
     // Set us up to listen for messages from the app.
     var listener = function(e) {
       var message = e.detail.message;
--- a/dom/devicestorage/ipc/test_ipc.html
+++ b/dom/devicestorage/ipc/test_ipc.html
@@ -31,44 +31,40 @@
           result: JSON.stringify(TestRunner._failedTests)
         });
 
         if (oldComplete) {
           oldComplete();
         }
       };
 
-      let oldLog = TestRunner.log;
-      TestRunner.log = function(msg) {
+      TestRunner.structuredLogger._dumpMessage = function(msg) {
         sendAsyncMessage("test:DeviceStorage:ipcTestMessage", { msg: msg });
       }
     }
 
-    let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
-
+    let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+    function validStructuredMessage(message) {
+      return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
+    }
     function onTestMessage(data) {
-      let message = SpecialPowers.wrap(data).json.msg;
-      let match = regex.exec(message);
-      if (match) {
-        let state = match[1];
-        let details = match[2] + " | " + match[3];
+      let message = SpecialPowers.wrap(data).data.msg;
 
-        switch (state) {
-          case "TEST-PASS":
-          case "TEST-KNOWN-FAIL":
-            ok(true, details);
-            break;
+      if (validStructuredMessage(message)) {
+        if (message.test === undefined || message.message === undefined) {
+          return;
+        }
 
-          case "TEST-UNEXPECTED-FAIL":
-          case "TEST-UNEXPECTED-PASS":
-            ok(false, details);
+        switch (message.action) {
+          case "test_status":
+          case "test_end":
+            ok(message.expected === undefined, message.test, message.message);
             break;
-
-          case "TEST-DEBUG-INFO":
           default:
+            let details = message.test + " | " + message.message;
             info(details);
         }
       }
     }
 
     function onTestComplete() {
       let comp = SpecialPowers.wrap(SpecialPowers.Components);
       let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/dom/HashChangeEvent.h"
 #include "mozilla/dom/PageTransitionEvent.h"
 #include "mozilla/dom/PopStateEvent.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/InternalMutationEvent.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
 
 namespace mozilla {
 
@@ -395,16 +396,20 @@ EventDispatcher::Dispatch(nsISupports* a
   PROFILER_LABEL("EventDispatcher", "Dispatch",
     js::ProfileEntry::Category::EVENTS);
 
   NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!");
   NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched,
                  NS_ERROR_DOM_INVALID_STATE_ERR);
   NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
 
+#ifdef NIGHTLY_BUILD
+  MOZ_RELEASE_ASSERT(!mozilla::ipc::ProcessingUrgentMessages());
+#endif
+
   // If we're dispatching an already created DOMEvent object, make
   // sure it is initialized!
   // If aTargets is non-null, the event isn't going to be dispatched.
   NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets,
                  NS_ERROR_DOM_INVALID_STATE_ERR);
 
   nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
 
--- a/dom/events/test/test_bug603008.html
+++ b/dom/events/test/test_bug603008.html
@@ -422,17 +422,17 @@ function testPreventDefault() {
      { name: "touchend", prevent: true }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false, doPrevent: true },
      { name: "touchend", prevent: false }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false },
-     { name: "touchend", prevent: false, doPrevent: true }]
+     { name: "touchend", prevent: true, doPrevent: true }]
   ];
 
   var dotest = function(aTest) {
     if (aTest.doPrevent) {
       target.addEventListener(aTest.name, preventFunction, false);
     }
 
     if (aTest.name == "touchmove") {
--- a/dom/imptests/testharnessreport.js
+++ b/dom/imptests/testharnessreport.js
@@ -37,21 +37,27 @@ var W3CTest = {
 
   /**
    * Reference to the TestRunner object in the parent frame.
    */
   "runner": parent === this ? null : parent.TestRunner || parent.wrappedJSObject.TestRunner,
 
   /**
    * Prefixes for the error logging. Indexed first by int(todo) and second by
-   * int(result).
+   * int(result). Also contains the test's status, and expected status.
    */
   "prefixes": [
-    ["TEST-UNEXPECTED-FAIL", "TEST-PASS"],
-    ["TEST-KNOWN-FAIL", "TEST-UNEXPECTED-PASS"]
+    [
+      {status: 'FAIL', expected: 'PASS', message: "TEST-UNEXPECTED-FAIL"},
+      {status: 'PASS', expected: 'PASS', message: "TEST-PASS"}
+    ],
+    [
+      {status: 'FAIL', expected: 'FAIL', message: "TEST-KNOWN-FAIL"},
+      {status: 'PASS', expected: 'FAIL', message: "TEST-UNEXPECTED-PASS"}
+    ]
   ],
 
   /**
    * Prefix of the path to parent of the the failures directory.
    */
   "pathprefix": "/tests/dom/imptests/",
 
   /**
@@ -128,24 +134,31 @@ var W3CTest = {
     return aTest.name + (aTest.message ? ": " + aTest.message : "");
   },
 
   /**
    * Lets the test runner know about a test result.
    */
   "_log": function(test) {
     var url = this.getURL();
-    var msg = this.prefixes[+test.todo][+test.result] + " | ";
-    if (url) {
-      msg += url;
-    }
-    msg += " | " + this.formatTestMessage(test);
+    var message = this.formatTestMessage(test);
+    var result = this.prefixes[+test.todo][+test.result];
+
     if (this.runner) {
-      this.runner[(test.result === !test.todo) ? "log" : "error"](msg);
+      this.runner.structuredLogger.testStatus(url,
+                                              test.name,
+                                              result.status,
+                                              result.expected,
+                                              message);
     } else {
+      var msg = result.message + " | ";
+      if (url) {
+        msg += url;
+      }
+      msg += " | " + this.formatTestMessage(test);
       dump(msg + "\n");
     }
   },
 
   /**
    * Logs a message about collapsed messages (if any), and resets the counter.
    */
   "_logCollapsedMessages": function() {
--- a/dom/indexedDB/ipc/test_ipc.html
+++ b/dom/indexedDB/ipc/test_ipc.html
@@ -44,54 +44,43 @@
 
         sendAsyncMessage("test:indexedDB:ipcTestComplete");
 
         if (oldComplete) {
           oldComplete();
         }
       };
 
-      function sendTestMessage(msg) {
+      TestRunner.structuredLogger._dumpMessage = function(msg) {
         sendAsyncMessage("test:indexedDB:ipcTestMessage", { msg: msg });
       }
-
-      TestRunner.log = sendTestMessage;
-      TestRunner.error = sendTestMessage;
     }
 
-    let regexString =
-      "^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL" +
-      "|TEST-DEBUG-INFO|TEST-INFO) \\| ([^\\|]+) \\|(.*)";
-
-    let regex = new RegExp(regexString);
-
     let seenTestMessage = false;
 
+    let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+    function validStructuredMessage(message) {
+      return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
+    }
     function onTestMessage(data) {
       seenTestMessage = true;
       let message = SpecialPowers.wrap(data).data.msg;
-      let match = regex.exec(message);
-      if (match) {
-        let state = match[1];
-        let details = match[2] + " | " + match[3];
 
-        switch (state) {
-          case "TEST-PASS":
-          case "TEST-KNOWN-FAIL":
-            ok(true, details);
+      if (validStructuredMessage(message)) {
+        if (message.test === undefined || message.message === undefined) {
+          return;
+        }
+
+        switch (message.action) {
+          case "test_status":
+          case "test_end":
+            ok(message.expected === undefined, message.test, message.message);
             break;
-
-          case "TEST-UNEXPECTED-FAIL":
-          case "TEST-UNEXPECTED-PASS":
-            ok(false, details);
-            break;
-
-          case "TEST-INFO":
-          case "TEST-DEBUG-INFO":
           default:
+            let details = message.test + " | " + message.message;
             info(details);
         }
       }
     }
 
     let usingChildProcess = false;
 
     function onProcessType(data) {
--- a/dom/inputmethod/mochitest/test_bug960946.html
+++ b/dom/inputmethod/mochitest/test_bug960946.html
@@ -25,17 +25,17 @@ var result = ["keydown", "keypress", "ke
 inputmethod_setup(function() {
   runTest();
 });
 
 // The frame script running in file_test_backspace_event.html.
 function appFrameScript() {
   let input = content.document.getElementById('test-input');
   input.onkeydown = input.onkeypress = input.onkeyup = function(event) {
-    dump('key event was fired in file_test_backspace_event.html.');
+    dump('key event was fired in file_test_backspace_event.html.\n');
     sendAsyncMessage('test:KeyBoard:keyEvent', {'type':event.type});
   };
 }
 
 function runTest() {
   let im = navigator.mozInputMethod;
 
   im.oninputcontextchange = function() {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -724,16 +724,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mPendingTouchPreventedResponse(false)
+  , mTouchEndIsClick(Unknown)
   , mIgnoreKeyPressEvent(false)
   , mActiveElementManager(new ActiveElementManager())
   , mHasValidInnerSize(false)
   , mUniqueId(0)
 {
   if (!sActiveDurationMsSet) {
     Preferences::AddIntVarCache(&sActiveDurationMs,
                                 "ui.touch_activation.duration_ms",
@@ -1799,16 +1800,20 @@ TabChild::RecvHandleDoubleTap(const CSSP
 
 bool
 TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
+  if (mTouchEndIsClick == IsNotClick) {
+    return true;
+  }
+
   LayoutDevicePoint currentPoint = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();;
 
   MessageLoop::current()->PostDelayedTask(
     FROM_HERE,
     NewRunnableMethod(this, &TabChild::FireSingleTapEvent, currentPoint),
     sActiveDurationMs);
   return true;
 }
@@ -1889,17 +1894,17 @@ TabChild::RecvNotifyAPZStateChange(const
   }
   case APZStateChange::StartPanning:
   {
     mActiveElementManager->HandlePanStart();
     break;
   }
   case APZStateChange::EndTouch:
   {
-    mActiveElementManager->HandleTouchEnd(aArg);
+    mTouchEndIsClick = (aArg ? IsClick : IsNotClick);
     break;
   }
   default:
     // APZStateChange has a 'sentinel' value, and the compiler complains
     // if an enumerator is not handled and there is no 'default' case.
     break;
   }
   return true;
@@ -2108,34 +2113,43 @@ TabChild::RecvRealTouchEvent(const Widge
   if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
+    mTouchEndIsClick = Unknown;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
       SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
       mPendingTouchPreventedResponse = false;
     }
     if (isTouchPrevented) {
       SendContentReceivedTouch(aGuid, isTouchPrevented);
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
     }
     break;
   }
 
-  case NS_TOUCH_MOVE:
   case NS_TOUCH_END:
-  case NS_TOUCH_CANCEL: {
+    if (isTouchPrevented && mTouchEndIsClick == IsClick) {
+      mTouchEndIsClick = IsNotClick;
+    }
+    // fall through
+  case NS_TOUCH_CANCEL:
+    if (mTouchEndIsClick != Unknown) {
+      mActiveElementManager->HandleTouchEnd(mTouchEndIsClick == IsClick);
+    }
+    // fall through
+  case NS_TOUCH_MOVE: {
     if (mPendingTouchPreventedResponse) {
       MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
       SendContentReceivedTouch(mPendingTouchPreventedGuid, isTouchPrevented);
       mPendingTouchPreventedResponse = false;
     }
     break;
   }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -586,16 +586,23 @@ private:
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
     bool mPendingTouchPreventedResponse;
     ScrollableLayerGuid mPendingTouchPreventedGuid;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
+    enum ClickState {
+      Unknown,
+      IsClick,
+      IsNotClick
+    };
+    ClickState mTouchEndIsClick;
+
     bool mIgnoreKeyPressEvent;
     nsRefPtr<ActiveElementManager> mActiveElementManager;
     bool mHasValidInnerSize;
     uint64_t mUniqueId;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -306,17 +306,16 @@ VideoDevice::VideoDevice(MediaEngineVide
 #endif // MOZ_B2G_CAMERA
 
   // Kludge to test user-facing cameras on OSX.
   if (mName.Find(NS_LITERAL_STRING("Face")) != -1) {
     mHasFacingMode = true;
     mFacingMode = dom::VideoFacingModeEnum::User;
   }
 
-  // dom::MediaSourceEnum::Camera;
   mMediaSource = aSource->GetMediaSource();
 }
 
 AudioDevice::AudioDevice(MediaEngineAudioSource* aSource)
   : MediaDevice(aSource) {}
 
 NS_IMETHODIMP
 MediaDevice::GetName(nsAString& aName)
@@ -362,19 +361,24 @@ MediaDevice::GetFacingMode(nsAString& aF
     aFacingMode.Truncate(0);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MediaDevice::GetMediaSource(nsAString& aMediaSource)
 {
-
-  aMediaSource.Assign(NS_ConvertUTF8toUTF16(
-    dom::MediaSourceEnumValues::strings[uint32_t(mMediaSource)].value));
+  if (mMediaSource == MediaSourceType::Microphone) {
+    aMediaSource.Assign(NS_LITERAL_STRING("microphone"));
+  } else if (mMediaSource == MediaSourceType::Window) { // this will go away
+    aMediaSource.Assign(NS_LITERAL_STRING("window"));
+  } else { // all the rest are shared
+    aMediaSource.Assign(NS_ConvertUTF8toUTF16(
+      dom::MediaSourceEnumValues::strings[uint32_t(mMediaSource)].value));
+  }
   return NS_OK;
 }
 
 MediaEngineVideoSource*
 VideoDevice::GetSource()
 {
   return static_cast<MediaEngineVideoSource*>(&*mSource);
 }
@@ -754,28 +758,29 @@ static bool SatisfyConstraintSet(const M
 typedef nsTArray<nsCOMPtr<nsIMediaDevice> > SourceSet;
 
 // Source getter that constrains list returned
 
 template<class SourceType, class ConstraintsType>
 static SourceSet *
   GetSources(MediaEngine *engine,
              ConstraintsType &aConstraints,
-             void (MediaEngine::* aEnumerate)(dom::MediaSourceEnum, nsTArray<nsRefPtr<SourceType> >*),
+             void (MediaEngine::* aEnumerate)(MediaSourceType, nsTArray<nsRefPtr<SourceType> >*),
              const char* media_device_name = nullptr)
 {
   ScopedDeletePtr<SourceSet> result(new SourceSet);
 
   const SourceType * const type = nullptr;
   nsString deviceName;
   // First collect sources
   SourceSet candidateSet;
   {
     nsTArray<nsRefPtr<SourceType> > sources;
-    (engine->*aEnumerate)(aConstraints.mMediaSource, &sources);
+    // all MediaSourceEnums are contained in MediaSourceType
+    (engine->*aEnumerate)((MediaSourceType)((int)aConstraints.mMediaSource), &sources);
     /**
       * We're allowing multiple tabs to access the same camera for parity
       * with Chrome.  See bug 811757 for some of the issues surrounding
       * this decision.  To disallow, we'd filter by IsAvailable() as we used
       * to.
       */
     for (uint32_t len = sources.Length(), i = 0; i < len; i++) {
       sources[i]->GetName(deviceName);
@@ -1909,17 +1914,18 @@ WindowsHashToArrayFunc (const uint64_t& 
     // access and windows that are currently capturing media. We want
     // to return only the latter. See bug 975177.
     bool capturing = false;
     if (aData) {
       uint32_t length = aData->Length();
       for (uint32_t i = 0; i < length; ++i) {
         nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
           aData->ElementAt(i);
-        if (listener->CapturingVideo() || listener->CapturingAudio()) {
+        if (listener->CapturingVideo() || listener->CapturingAudio() ||
+            listener->CapturingScreen() || listener->CapturingWindow()) {
           capturing = true;
           break;
         }
       }
     }
 
     if (capturing)
       array->AppendElement(window);
@@ -1940,34 +1946,39 @@ MediaManager::GetActiveMediaCaptureWindo
   mActiveWindows.EnumerateRead(WindowsHashToArrayFunc, array);
 
   *aArray = array;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow, bool* aVideo,
-                                      bool* aAudio)
+                                      bool* aAudio, bool *aScreenShare,
+                                      bool* aWindowShare)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   *aVideo = false;
   *aAudio = false;
+  *aScreenShare = false;
+  *aWindowShare = false;
 
-  nsresult rv = MediaCaptureWindowStateInternal(aWindow, aVideo, aAudio);
+  nsresult rv = MediaCaptureWindowStateInternal(aWindow, aVideo, aAudio, aScreenShare, aWindowShare);
 #ifdef DEBUG
   nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
-  LOG(("%s: window %lld capturing %s %s", __FUNCTION__, piWin ? piWin->WindowID() : -1,
-       *aVideo ? "video" : "", *aAudio ? "audio" : ""));
+  LOG(("%s: window %lld capturing %s %s %s %s", __FUNCTION__, piWin ? piWin->WindowID() : -1,
+       *aVideo ? "video" : "", *aAudio ? "audio" : "",
+       *aScreenShare ? "screenshare" : "",  *aWindowShare ? "windowshare" : ""));
 #endif
   return rv;
 }
 
 nsresult
 MediaManager::MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
-                                              bool* aAudio)
+                                              bool* aAudio, bool *aScreenShare,
+                                              bool* aWindowShare)
 {
   // We need to return the union of all streams in all innerwindows that
   // correspond to that outerwindow.
 
   // Iterate the docshell tree to find all the child windows, find
   // all the listeners for each one, get the booleans, and merge the
   // results.
   nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
@@ -1986,37 +1997,37 @@ MediaManager::MediaCaptureWindowStateInt
           nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
             listeners->ElementAt(i);
           if (listener->CapturingVideo()) {
             *aVideo = true;
           }
           if (listener->CapturingAudio()) {
             *aAudio = true;
           }
-          if (*aAudio && *aVideo) {
-            return NS_OK; // no need to continue iterating
+          if (listener->CapturingScreen()) {
+            *aScreenShare = true;
+          }
+          if (listener->CapturingWindow()) {
+            *aWindowShare = true;
           }
         }
       }
     }
 
     // iterate any children of *this* window (iframes, etc)
     nsCOMPtr<nsIDocShell> docShell = piWin->GetDocShell();
     if (docShell) {
       int32_t i, count;
       docShell->GetChildCount(&count);
       for (i = 0; i < count; ++i) {
         nsCOMPtr<nsIDocShellTreeItem> item;
         docShell->GetChildAt(i, getter_AddRefs(item));
         nsCOMPtr<nsPIDOMWindow> win = item ? item->GetWindow() : nullptr;
 
-        MediaCaptureWindowStateInternal(win, aVideo, aAudio);
-        if (*aAudio && *aVideo) {
-          return NS_OK; // no need to continue iterating
-        }
+        MediaCaptureWindowStateInternal(win, aVideo, aAudio, aScreenShare, aWindowShare);
       }
     }
   }
   return NS_OK;
 }
 
 void
 MediaManager::StopMediaStreams()
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -100,26 +100,39 @@ public:
   }
 
   // mVideo/AudioSource are set by Activate(), so we assume they're capturing
   // if set and represent a real capture device.
   bool CapturingVideo()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     return mVideoSource && !mStopped &&
+           mVideoSource->GetMediaSource() == MediaSourceType::Camera &&
            (!mVideoSource->IsFake() ||
             Preferences::GetBool("media.navigator.permission.fake"));
   }
   bool CapturingAudio()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     return mAudioSource && !mStopped &&
            (!mAudioSource->IsFake() ||
             Preferences::GetBool("media.navigator.permission.fake"));
   }
+  bool CapturingScreen()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+    return mVideoSource && !mStopped &&
+           mVideoSource->GetMediaSource() == MediaSourceType::Screen;
+  }
+  bool CapturingWindow()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+    return mVideoSource && !mStopped &&
+           mVideoSource->GetMediaSource() == MediaSourceType::Window;
+  }
 
   void SetStopped()
   {
     mStopped = true;
   }
 
   // implement in .cpp to avoid circular dependency with MediaOperationRunnable
   // Can be invoked from EITHER MainThread or MSG thread
@@ -481,17 +494,17 @@ public:
 
 protected:
   virtual ~MediaDevice() {}
   MediaDevice(MediaEngineSource* aSource);
   nsString mName;
   nsString mID;
   bool mHasFacingMode;
   dom::VideoFacingModeEnum mFacingMode;
-  dom::MediaSourceEnum mMediaSource;
+  MediaSourceType mMediaSource;
   nsRefPtr<MediaEngineSource> mSource;
 };
 
 class VideoDevice : public MediaDevice
 {
 public:
   VideoDevice(MediaEngineVideoSource* aSource);
   NS_IMETHOD GetType(nsAString& aType);
@@ -574,17 +587,18 @@ private:
   void GetPrefs(nsIPrefBranch *aBranch, const char *aData);
 
   // Make private because we want only one instance of this class
   MediaManager();
 
   ~MediaManager() {}
 
   nsresult MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
-                                           bool* aAudio);
+                                           bool* aAudio, bool *aScreenShare,
+                                           bool* aWindowShare);
 
   void StopMediaStreams();
 
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
   nsRefPtrHashtable<nsStringHashKey, GetUserMediaRunnable> mActiveCallbacks;
   nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
   // Always exists
--- a/dom/media/nsIMediaManager.idl
+++ b/dom/media/nsIMediaManager.idl
@@ -7,17 +7,18 @@
 interface nsISupportsArray;
 interface nsIDOMWindow;
 
 %{C++
 #define NS_MEDIAMANAGERSERVICE_CID {0xabc622ea, 0x9655, 0x4123, {0x80, 0xd9, 0x22, 0x62, 0x1b, 0xdd, 0x54, 0x65}}
 #define MEDIAMANAGERSERVICE_CONTRACTID "@mozilla.org/mediaManagerService;1"
 %}
 
-[scriptable, builtinclass, uuid(2efff6ab-0e3e-4cc4-8f9b-4aaca59a1140)]
+[scriptable, builtinclass, uuid(f431b523-4536-4ba7-a2c1-7e1bf670d32a)]
 interface nsIMediaManagerService : nsISupports
 {
   /* return a array of inner windows that have active captures */
   readonly attribute nsISupportsArray activeMediaCaptureWindows;
 
   /* Get the capture state for the given window and all descendant windows (iframes, etc) */
-  void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio);
+  void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio,
+                               [optional] out boolean aScreenShare, [optional] out boolean aWindowShare);
 };
--- a/dom/media/tests/ipc/test_ipc.html
+++ b/dom/media/tests/ipc/test_ipc.html
@@ -19,18 +19,18 @@
     SimpleTest.requestLongerTimeout(100);
 
     // Disable crash observers as it breaks later tests.
     function iframeScriptFirst() {
       SpecialPowers.prototype.registerProcessCrashObservers = function() { };
       SpecialPowers.prototype.unregisterProcessCrashObservers = function() { };
 
       content.wrappedJSObject.RunSet.reloadAndRunAll({
-          preventDefault: function() { },
-          __exposedProps__: { preventDefault: 'r' }
+        preventDefault: function() { },
+        __exposedProps__: { preventDefault: 'r' }
       });
     }
 
     function iframeScriptSecond() {
       let TestRunner = content.wrappedJSObject.TestRunner;
       let oldComplete = TestRunner.onComplete;
 
       TestRunner.onComplete = function() {
@@ -39,47 +39,41 @@
         sendAsyncMessage("test:PeerConnection:ipcTestComplete", {
           result: JSON.stringify(TestRunner._failedTests)
         });
 
         if (oldComplete) {
           oldComplete();
         }
       };
-      let oldLog = TestRunner.log;
-      TestRunner.log = function(msg) {
+
+      TestRunner.structuredLogger._dumpMessage = function(msg) {
         sendAsyncMessage("test:PeerConnection:ipcTestMessage", { msg: msg });
-      };
-      TestRunner.error = function(msg) {
-        sendAsyncMessage("test:PeerConnection:ipcTestMessage", { msg: msg });
-      };
+      }
     }
 
-    let regex = /^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO) \| ([^\|]+) \|(.*)/;
-
+    let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
+    function validStructuredMessage(message) {
+      return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
+    }
     function onTestMessage(data) {
-      let message = SpecialPowers.wrap(data).json.msg;
-      let match = regex.exec(message);
-      if (match) {
-        let state = match[1];
-        let details = match[2] + " | " + match[3];
+      let message = SpecialPowers.wrap(data).data.msg;
 
-        switch (state) {
-          case "TEST-PASS":
-          case "TEST-KNOWN-FAIL":
-            ok(true, details);
-            break;
+      if (validStructuredMessage(message)) {
+        if (message.test === undefined || message.message === undefined) {
+          return;
+        }
 
-          case "TEST-UNEXPECTED-FAIL":
-          case "TEST-UNEXPECTED-PASS":
-            ok(false, details);
+        switch (message.action) {
+          case "test_status":
+          case "test_end":
+            ok(message.expected === undefined, message.test, message.message);
             break;
-
-          case "TEST-DEBUG-INFO":
           default:
+            let details = message.test + " | " + message.message;
             info(details);
         }
       }
     }
 
     function onTestComplete() {
       let comp = SpecialPowers.wrap(SpecialPowers.Components);
       let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -20,16 +20,20 @@ skip-if = toolkit == 'gonk' # b2g(Bug 96
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_bug1013809.html]
 skip-if = (toolkit == 'gonk' && debug) # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
 [test_dataChannel_noOffer.html]
 [test_getUserMedia_basicAudio.html]
 skip-if = (toolkit == 'gonk' && debug) # debug-only failure
 [test_getUserMedia_basicVideo.html]
 skip-if = (toolkit == 'gonk' && debug) # debug-only failure
+[test_getUserMedia_basicScreenshare.html]
+skip-if = toolkit == 'gonk' || toolkit == 'android' # no screenshare on b2g/android
+[test_getUserMedia_basicWindowshare.html]
+skip-if = toolkit == 'gonk' || toolkit == 'android' # no windowshare on b2g/android
 [test_getUserMedia_basicVideoAudio.html]
 skip-if = (toolkit == 'gonk' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange
 [test_getUserMedia_constraints.html]
 skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 907352, backwards-compatible behavior on mobile only
 [test_getUserMedia_constraints_mobile.html]
 skip-if = toolkit != 'gonk' && toolkit != 'android' # Bug 907352, backwards-compatible behavior on mobile only
 [test_getUserMedia_exceptions.html]
 [test_getUserMedia_gumWithinGum.html]
@@ -50,16 +54,20 @@ skip-if = toolkit == 'gonk' # b2g(Bug 10
 [test_peerConnection_basicAudio.html]
 skip-if = (toolkit == 'gonk' && debug) # Bug 962984, test fail on b2g debug build
 [test_peerConnection_basicAudioVideo.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicAudioVideoCombined.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicVideo.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+[test_peerConnection_basicScreenshare.html]
+skip-if = toolkit == 'gonk' || toolkit == 'android' # no screenshare on b2g/android
+[test_peerConnection_basicWindowshare.html]
+skip-if = toolkit == 'gonk' || toolkit == 'android' # no windowshare on b2g/android
 [test_peerConnection_bug822674.html]
 [test_peerConnection_bug825703.html]
 [test_peerConnection_bug827843.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
 [test_peerConnection_bug835370.html]
 [test_peerConnection_bug1013809.html]
 skip-if = (toolkit == 'gonk' && debug) # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=983504
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Screenshare Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=983504">mozGetUserMedia Basic Screenshare Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+  /**
+   * Run a test to verify that we can complete a start and stop media playback
+   * cycle for an screenshare LocalMediaStream on a video HTMLMediaElement.
+   */
+  runTest(function () {
+    var testVideo = document.getElementById('testVideo');
+    var constraints = {
+      video: {
+        mandatory:{
+          chromeMediaSource:'screen',
+          maxWidth:screen.availWidth,
+          maxHeight:screen.availHeight
+        },
+        optional:[]
+      }
+    };
+
+    getUserMedia(constraints, function (aStream) {
+      checkMediaStreamTracks(constraints, aStream);
+
+      var playback = new LocalMediaStreamPlayback(testVideo, aStream);
+      playback.playMediaWithStreamStop(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
+
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicWindowshare.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=983504
+-->
+<head>
+  <meta charset="utf-8">
+  <title>mozGetUserMedia Basic Windowshare Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038926">mozGetUserMedia Basic Windowshare Test</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <video id="testVideo"></video>
+</div>
+<pre id="test">
+<script type="application/javascript">
+  /**
+   * Run a test to verify that we can complete a start and stop media playback
+   * cycle for an screenshare LocalMediaStream on a video HTMLMediaElement.
+   */
+  runTest(function () {
+    var testVideo = document.getElementById('testVideo');
+    var constraints = {
+      video: {
+        mandatory:{
+          chromeMediaSource:'window',
+          maxWidth:screen.availWidth,
+          maxHeight:screen.availHeight
+        },
+        optional:[]
+      }
+    };
+
+    getUserMedia(constraints, function (aStream) {
+      checkMediaStreamTracks(constraints, aStream);
+
+      var playback = new LocalMediaStreamPlayback(testVideo, aStream);
+      playback.playMediaWithStreamStop(false, function () {
+        aStream.stop();
+        SimpleTest.finish();
+      }, generateErrorCallback());
+
+    }, generateErrorCallback());
+
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_basicScreenshare.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1039666",
+    title: "Basic screenshare-only peer connection"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    var constraints = {
+      video: {
+        mandatory:{
+          chromeMediaSource:'screen',
+          maxWidth:screen.availWidth,
+          maxHeight:screen.availHeight
+        },
+        optional:[]
+      }
+    };
+    test.setMediaConstraints([constraints], [constraints]);
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_basicWindowshare.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+  <script type="application/javascript" src="templates.js"></script>
+  <script type="application/javascript" src="turnConfig.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1038926",
+    title: "Basic windowshare-only peer connection"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    var constraints = {
+      video: {
+        mandatory:{
+          chromeMediaSource:'window',
+          maxWidth:screen.availWidth,
+          maxHeight:screen.availHeight
+        },
+        optional:[]
+      }
+    };
+    test.setMediaConstraints([constraints], [constraints]);
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/test/mochitest/test_npobject_getters.html
+++ b/dom/plugins/test/mochitest/test_npobject_getters.html
@@ -1,16 +1,16 @@
 <head>
   <title>NPNV*NPObject accessibility tests</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="utils.js"></script>
 
 <body onload="runTests()">
   <script class="testbody" type="application/javascript">
-  dump('lastScript');
+  dump('lastScript\n');
 
   SimpleTest.waitForExplicitFinish();
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   function runTests() {
     ok(document.getElementById('plugin1').pluginFoundElement, "plugin1.pluginFoundElement (NPNVPluginElementNPObject)", document.getElementById('plugin1').pluginFoundElement);
     ok(window.pluginFoundWindow, "window.pluginFoundWindow (NPNVWindowNPObject)", window.pluginFoundWindow);
 
--- a/dom/webidl/Constraints.webidl
+++ b/dom/webidl/Constraints.webidl
@@ -12,17 +12,18 @@ enum VideoFacingModeEnum {
     "environment",
     "left",
     "right"
 };
 
 enum MediaSourceEnum {
     "camera",
     "screen",
-    "application"
+    "application",
+    "window"
 };
 
 dictionary ConstrainLongRange {
     long min = -2147483647; // +1 works around windows compiler bug
     long max = 2147483647;
 };
 
 dictionary ConstrainDoubleRange {
--- a/dom/workers/test/rvals_worker.js
+++ b/dom/workers/test/rvals_worker.js
@@ -2,12 +2,12 @@ onmessage = function(evt) {
   postMessage(postMessage('ignore') == undefined);
 
   var id = setInterval(function() {}, 200);
   postMessage(clearInterval(id) == undefined);
 
   id = setTimeout(function() {}, 200);
   postMessage(clearTimeout(id) == undefined);
 
-  postMessage(dump(42) == undefined);
+  postMessage(dump(42 + '\n') == undefined);
 
   postMessage('finished');
 }
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -150,16 +150,22 @@ LayerManager::CreateImageContainer()
 
 already_AddRefed<ImageContainer>
 LayerManager::CreateAsynchronousImageContainer()
 {
   nsRefPtr<ImageContainer> container = new ImageContainer(ImageContainer::ENABLE_ASYNC);
   return container.forget();
 }
 
+bool
+LayerManager::AreComponentAlphaLayersEnabled()
+{
+  return gfxPrefs::ComponentAlphaEnabled();
+}
+
 //--------------------------------------------------
 // Layer
 
 Layer::Layer(LayerManager* aManager, void* aImplData) :
   mManager(aManager),
   mParent(nullptr),
   mNextSibling(nullptr),
   mPrevSibling(nullptr),
@@ -1018,42 +1024,49 @@ ContainerLayer::DefaultComputeEffectiveT
   } else {
     ComputeEffectiveTransformForMaskLayer(Matrix4x4());
   }
 }
 
 void
 ContainerLayer::DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurfaceCopy)
 {
-  bool supportsComponentAlphaChildren = false;
+  if (!(GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA_DESCENDANT) ||
+      !Manager()->AreComponentAlphaLayersEnabled()) {
+    mSupportsComponentAlphaChildren = false;
+    if (aNeedsSurfaceCopy) {
+      *aNeedsSurfaceCopy = false;
+    }
+    return;
+  }
+
+  mSupportsComponentAlphaChildren = false;
   bool needsSurfaceCopy = false;
   CompositionOp blendMode = GetEffectiveMixBlendMode();
   if (UseIntermediateSurface()) {
     if (GetEffectiveVisibleRegion().GetNumRects() == 1 &&
         (GetContentFlags() & Layer::CONTENT_OPAQUE))
     {
-      supportsComponentAlphaChildren = true;
+      mSupportsComponentAlphaChildren = true;
     } else {
       gfx::Matrix transform;
       if (HasOpaqueAncestorLayer(this) &&
           GetEffectiveTransform().Is2D(&transform) &&
           !gfx::ThebesMatrix(transform).HasNonIntegerTranslation() &&
           blendMode == gfx::CompositionOp::OP_OVER) {
-        supportsComponentAlphaChildren = true;
+        mSupportsComponentAlphaChildren = true;
         needsSurfaceCopy = true;
       }
     }
   } else if (blendMode == gfx::CompositionOp::OP_OVER) {
-    supportsComponentAlphaChildren =
+    mSupportsComponentAlphaChildren =
       (GetContentFlags() & Layer::CONTENT_OPAQUE) ||
       (GetParent() && GetParent()->SupportsComponentAlphaChildren());
   }
 
-  mSupportsComponentAlphaChildren = supportsComponentAlphaChildren &&
-                                    gfxPrefs::ComponentAlphaEnabled();
   if (aNeedsSurfaceCopy) {
     *aNeedsSurfaceCopy = mSupportsComponentAlphaChildren && needsSurfaceCopy;
   }
 }
 
 void
 ContainerLayer::ComputeEffectiveTransformsForChildren(const Matrix4x4& aTransformToSurface)
 {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -303,22 +303,30 @@ public:
    */
   virtual void Composite() {}
 
   virtual bool HasShadowManagerInternal() const { return false; }
   bool HasShadowManager() const { return HasShadowManagerInternal(); }
 
   bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; }
 
+
+  /**
+   * Returns true if the layer manager can't render component alpha
+   * layers, and layer building should do it's best to avoid
+   * creating them.
+   */
+  virtual bool ShouldAvoidComponentAlphaLayers() { return false; }
+
   /**
    * Returns true if this LayerManager can properly support layers with
-   * SurfaceMode::SURFACE_COMPONENT_ALPHA. This can include disabling component
-   * alpha if required.
+   * SurfaceMode::SURFACE_COMPONENT_ALPHA. LayerManagers that can't will use
+   * transparent surfaces (and lose subpixel-AA for text).
    */
-  virtual bool AreComponentAlphaLayersEnabled() { return true; }
+  virtual bool AreComponentAlphaLayersEnabled();
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the root layer. The root layer is initially null. If there is
    * no root layer, EndTransaction won't draw anything.
    */
   virtual void SetRoot(Layer* aLayer) = 0;
   /**
@@ -743,32 +751,38 @@ public:
      * require per-component alpha for optimal fidelity. However, there is no
      * guarantee that component alpha will be supported for this layer at
      * paint time.
      * This should never be set at the same time as CONTENT_OPAQUE.
      */
     CONTENT_COMPONENT_ALPHA = 0x02,
 
     /**
+     * If this is set then one of the descendant layers of this one has
+     * CONTENT_COMPONENT_ALPHA set.
+     */
+    CONTENT_COMPONENT_ALPHA_DESCENDANT = 0x04,
+
+    /**
      * If this is set then this layer is part of a preserve-3d group, and should
      * be sorted with sibling layers that are also part of the same group.
      */
-    CONTENT_PRESERVE_3D = 0x04,
+    CONTENT_PRESERVE_3D = 0x08,
     /**
      * This indicates that the transform may be changed on during an empty
      * transaction where there is no possibility of redrawing the content, so the
      * implementation should be ready for that.
      */
-    CONTENT_MAY_CHANGE_TRANSFORM = 0x08,
+    CONTENT_MAY_CHANGE_TRANSFORM = 0x10,
 
     /**
      * Disable subpixel AA for this layer. This is used if the display isn't suited
      * for subpixel AA like hidpi or rotated content.
      */
-    CONTENT_DISABLE_SUBPIXEL_AA = 0x10
+    CONTENT_DISABLE_SUBPIXEL_AA = 0x20
   };
   /**
    * CONSTRUCTION PHASE ONLY
    * This lets layout make some promises about what will be drawn into the
    * visible region of the ThebesLayer. This enables internal quality
    * and performance optimizations.
    */
   void SetContentFlags(uint32_t aFlags)
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -460,20 +460,18 @@ RotatedContentBuffer::BeginPaint(ThebesL
     }
 
     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
 #if defined(MOZ_GFX_OPTIMIZE_MOBILE) || defined(MOZ_WIDGET_GONK)
       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
 #else
       if (!aLayer->GetParent() ||
           !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
-          !aLayer->Manager()->IsCompositingCheap() ||
           !aLayer->AsShadowableLayer() ||
-          !aLayer->AsShadowableLayer()->HasShadow() ||
-          !gfxPrefs::ComponentAlphaEnabled()) {
+          !aLayer->AsShadowableLayer()->HasShadow()) {
         mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
       } else {
         result.mContentType = gfxContentType::COLOR;
       }
 #endif
     }
 
     if ((aFlags & PAINT_WILL_RESAMPLE) &&
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -883,26 +883,36 @@ APZCTreeManager::HandOffFling(AsyncPanZo
   // Tell |next| to start a fling with the transformed velocity.
   return next->TakeOverFling(transformedVelocity);
 }
 
 bool
 APZCTreeManager::FlushRepaintsForOverscrollHandoffChain()
 {
   MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
-  if (mOverscrollHandoffChain.length() == 0) {
-    return false;
-  }
   for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
     nsRefPtr<AsyncPanZoomController> item = mOverscrollHandoffChain[i];
     if (item) {
       item->FlushRepaintForOverscrollHandoff();
     }
   }
-  return true;
+  return mOverscrollHandoffChain.length() > 0;
+}
+
+bool
+APZCTreeManager::CancelAnimationsForOverscrollHandoffChain()
+{
+  MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
+  for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
+    nsRefPtr<AsyncPanZoomController> item = mOverscrollHandoffChain[i];
+    if (item) {
+      item->CancelAnimation();
+    }
+  }
+  return mOverscrollHandoffChain.length() > 0;
 }
 
 bool
 APZCTreeManager::CanBePanned(AsyncPanZoomController* aApzc)
 {
   MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
 
   // Find |aApzc| in the handoff chain.
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -288,16 +288,17 @@ public:
    *                  pixels per millisecond
    * Returns true iff. another APZC accepted the handed-off fling. The caller
    * (|aApzc|) uses this return value to determine whether it should consume
    * the excess fling itself by going into an overscroll fling.
    */
   bool HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity);
 
   bool FlushRepaintsForOverscrollHandoffChain();
+  bool CancelAnimationsForOverscrollHandoffChain();
 
   /**
    * Determine whether |aApzc|, or any APZC along its overscroll handoff chain,
    * has room to be panned.
    * Expects the overscroll handoff chain to already be built.
    */
   bool CanBePanned(AsyncPanZoomController* aApzc);
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -808,27 +808,55 @@ AsyncPanZoomController::GetTouchStartTol
   return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
 }
 
 /* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
 {
   return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
 }
 
+void
+AsyncPanZoomController::CancelAnimationForHandoffChain()
+{
+  APZCTreeManager* treeManagerLocal = mTreeManager;
+  if (treeManagerLocal && treeManagerLocal->CancelAnimationsForOverscrollHandoffChain()) {
+    return;
+  }
+  NS_WARNING("Overscroll handoff chain was empty in CancelAnimationForHandoffChain! This should not be the case.");
+  // Graceful handling of error condition
+  CancelAnimation();
+}
+
 nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
   AssertOnControllerThread();
 
   if (aEvent.mInputType != MULTITOUCH_INPUT) {
     return HandleInputEvent(aEvent);
   }
 
   TouchBlockState* block = nullptr;
   if (aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
     block = StartNewTouchBlock(false);
     APZC_LOG("%p started new touch block %p\n", this, block);
+
+    // We want to cancel animations here as soon as possible (i.e. without waiting for
+    // content responses) because a finger has gone down and we don't want to keep moving
+    // the content under the finger. However, to prevent "future" touchstart events from
+    // interfering with "past" animations (i.e. from a previous touch block that is still
+    // being processed) we only do this animation-cancellation if there are no older
+    // touch blocks still in the queue.
+    if (block == CurrentTouchBlock()) {
+      if (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
+        // If we're already in a fast fling, then we want the touch event to stop the fling
+        // and to disallow the touch event from being used as part of a fling.
+        block->DisallowSingleTap();
+      }
+      CancelAnimationForHandoffChain();
+    }
+
     if (mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) {
       // Content may intercept the touch events and prevent-default them. So we schedule
       // a timeout to give content time to do that.
       ScheduleContentResponseTimeout();
     } else {
       // Content won't prevent-default this, so we can just pretend like we scheduled
       // a timeout and it expired. Note that we will still receive a ContentReceivedTouch
       // callback for this block, and so we need to make sure we adjust the touch balance.
@@ -947,29 +975,18 @@ nsEventStatus AsyncPanZoomController::Ha
 
 nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-start in state %d\n", this, mState);
   mPanDirRestricted = false;
   ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
 
   switch (mState) {
     case FLING:
-      if (GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
-        // This is ugly. Hopefully bug 1009733 can reorganize how events
-        // flow through APZC and change it so that events are handed to the
-        // gesture listener *after* we deal with them here. This should allow
-        // removal of this ugly code.
-        nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
-        if (listener) {
-          listener->CancelSingleTouchDown();
-        }
-      }
-      // Fall through.
     case ANIMATING_ZOOM:
-      CancelAnimation();
+      CancelAnimationForHandoffChain();
       // Fall through.
     case NOTHING: {
       mX.StartTouch(point.x, aEvent.mTime);
       mY.StartTouch(point.y, aEvent.mTime);
       APZCTreeManager* treeManagerLocal = mTreeManager;
       nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
       if (treeManagerLocal && controller) {
         bool touchCanBePan = treeManagerLocal->CanBePanned(this);
@@ -1285,17 +1302,17 @@ AsyncPanZoomController::ConvertToGecko(c
   return false;
 }
 
 nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
 
   mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
   mY.StartTouch(aEvent.mPanStartPoint.y, aEvent.mTime);
-  CancelAnimation();
+  CancelAnimationForHandoffChain();
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-cancelled in state %d\n", this, mState);
 
   mX.CancelTouch();
@@ -1402,27 +1419,29 @@ nsEventStatus AsyncPanZoomController::On
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     CSSPoint geckoScreenPoint;
     if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
-      int32_t modifiers = WidgetModifiersToDOMModifiers(aModifiers);
+      if (!CurrentTouchBlock()->SetSingleTapOccurred()) {
+        return nsEventStatus_eIgnore;
+      }
       // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
       // calling controller->HandleSingleTap directly might mean that content receives
       // the single tap message before the corresponding touch-up. To avoid that we
       // schedule the singletap message to run on the next spin of the event loop.
       // See bug 965381 for the issue this was causing.
       controller->PostDelayedTask(
         NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
-                          geckoScreenPoint, modifiers, GetGuid()),
+                          geckoScreenPoint, WidgetModifiersToDOMModifiers(aModifiers),
+                          GetGuid()),
         0);
-      CurrentTouchBlock()->SetSingleTapOccurred();
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 void AsyncPanZoomController::OnTouchEndOrCancel() {
   if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
@@ -1775,16 +1794,20 @@ void AsyncPanZoomController::StartAnimat
   ScheduleComposite();
 }
 
 void AsyncPanZoomController::CancelAnimation() {
   ReentrantMonitorAutoEnter lock(mMonitor);
   APZC_LOG("%p running CancelAnimation in state %d\n", this, mState);
   SetState(NOTHING);
   mAnimation = nullptr;
+  // Since there is no animation in progress now the axes should
+  // have no velocity either.
+  mX.SetVelocity(0);
+  mY.SetVelocity(0);
   // Setting the state to nothing and cancelling the animation can
   // preempt normal mechanisms for relieving overscroll, so we need to clear
   // overscroll here.
   if (mX.IsOverscrolled() || mY.IsOverscrolled()) {
     mX.ClearOverscroll();
     mY.ClearOverscroll();
     RequestContentRepaint();
     ScheduleComposite();
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -532,16 +532,22 @@ protected:
    * mozbrowserasyncscroll events in some conditions, this function ensures
    * that the last mozbrowserasyncscroll event will be fired after a period of
    * time.
    */
   void FireAsyncScrollOnTimeout();
 
 private:
   /**
+   * Cancel animations all the way up the overscroll handoff chain if possible,
+   * or just the local APZC if not.
+   */
+  void CancelAnimationForHandoffChain();
+
+  /**
    * Helper to set the current state. Holds the monitor before actually setting
    * it and fires content controller events based on state changes. Always set
    * the state using this call, do not set it directly.
    */
   void SetState(PanZoomState aState);
 
   /**
    * Convert ScreenPoint relative to this APZC to CSSPoint relative
--- a/gfx/layers/apz/src/GestureEventListener.cpp
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -104,33 +104,16 @@ nsEventStatus GestureEventListener::Hand
     mTouches.Clear();
     rv = HandleInputTouchCancel();
     break;
   }
 
   return rv;
 }
 
-void GestureEventListener::CancelSingleTouchDown()
-{
-  GEL_LOG("Cancelling touch-down while in state %d\n", mState);
-
-  switch (mState) {
-  case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
-    CancelLongTapTimeoutTask();
-    CancelMaxTapTimeoutTask();
-    SetState(GESTURE_NONE);
-    break;
-  default:
-    NS_WARNING("IgnoreLastTouchStart() called while in unexpected state");
-    SetState(GESTURE_NONE);
-    break;
-  }
-}
-
 int32_t GestureEventListener::GetLastTouchIdentifier() const
 {
   if (mTouches.Length() != 1) {
     NS_WARNING("GetLastTouchIdentifier() called when last touch event "
                "did not have one touch");
   }
   return mTouches.IsEmpty() ? -1 : mTouches[0].mIdentifier;
 }
--- a/gfx/layers/apz/src/GestureEventListener.h
+++ b/gfx/layers/apz/src/GestureEventListener.h
@@ -49,22 +49,16 @@ public:
   /**
    * General input handler for a touch event. If the touch event is not a part
    * of a gesture, then we pass it along to AsyncPanZoomController. Otherwise,
    * it gets consumed here and never forwarded along.
    */
   nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent);
 
   /**
-   * Cancels any tap-related timeouts and clears any state that was set because
-   * we recently processed a touch-start.
-   */
-  void CancelSingleTouchDown();
-
-  /**
    * Returns the identifier of the touch in the last touch event processed by
    * this GestureEventListener. This should only be called when the last touch
    * event contained only one touch.
    */
   int32_t GetLastTouchIdentifier() const;
 
 private:
   // Private destructor, to discourage deletion outside of Release():
--- a/gfx/layers/apz/src/TouchBlockState.cpp
+++ b/gfx/layers/apz/src/TouchBlockState.cpp
@@ -23,16 +23,17 @@ static const TouchBlockState::TouchBehav
     AllowedTouchBehavior::PINCH_ZOOM |
     AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
 
 TouchBlockState::TouchBlockState()
   : mAllowedTouchBehaviorSet(false)
   , mPreventDefault(false)
   , mContentResponded(false)
   , mContentResponseTimerExpired(false)
+  , mSingleTapDisallowed(false)
   , mSingleTapOccurred(false)
 {
   TBS_LOG("Creating %p\n", this);
 }
 
 bool
 TouchBlockState::SetContentResponse(bool aPreventDefault)
 {
@@ -99,20 +100,31 @@ TouchBlockState::IsReadyForHandling() co
 bool
 TouchBlockState::IsDefaultPrevented() const
 {
   MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
   return mPreventDefault;
 }
 
 void
+TouchBlockState::DisallowSingleTap()
+{
+  TBS_LOG("%p disallowing single-tap\n", this);
+  mSingleTapDisallowed = true;
+}
+
+bool
 TouchBlockState::SetSingleTapOccurred()
 {
-  TBS_LOG("%p setting single-tap occurred\n", this);
-  mSingleTapOccurred = true;
+  TBS_LOG("%p attempting to set single-tap occurred; disallowed=%d\n", this, mSingleTapDisallowed);
+  if (!mSingleTapDisallowed) {
+    mSingleTapOccurred = true;
+    return true;
+  }
+  return false;
 }
 
 bool
 TouchBlockState::SingleTapOccurred() const
 {
   return mSingleTapOccurred;
 }
 
--- a/gfx/layers/apz/src/TouchBlockState.h
+++ b/gfx/layers/apz/src/TouchBlockState.h
@@ -80,19 +80,24 @@ public:
    */
   bool IsReadyForHandling() const;
   /**
    * @return true iff web content cancelled this block of events.
    */
   bool IsDefaultPrevented() const;
 
   /**
-   * Set a flag that indicates that this touch block triggered a single tap event.
+   * Set a flag that disables setting the single-tap flag on this block.
    */
-  void SetSingleTapOccurred();
+  void DisallowSingleTap();
+  /**
+   * Set a flag that indicates that this touch block triggered a single tap event.
+   * @return true iff DisallowSingleTap was not previously called.
+   */
+  bool SetSingleTapOccurred();
   /**
    * @return true iff SetSingleTapOccurred was previously called on this block.
    */
   bool SingleTapOccurred() const;
 
   /**
    * @return true iff there are pending events in this touch block.
    */
@@ -130,16 +135,17 @@ public:
   bool TouchActionAllowsPanningXY() const;
 
 private:
   nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
   bool mAllowedTouchBehaviorSet;
   bool mPreventDefault;
   bool mContentResponded;
   bool mContentResponseTimerExpired;
+  bool mSingleTapDisallowed;
   bool mSingleTapOccurred;
   nsTArray<MultiTouchInput> mEvents;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_TouchBlockState_h
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -92,33 +92,18 @@ BasicCompositor::CreateRenderTarget(cons
   return rt.forget();
 }
 
 TemporaryRef<CompositingRenderTarget>
 BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect,
                                               const CompositingRenderTarget *aSource,
                                               const IntPoint &aSourcePoint)
 {
-  RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
-  RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect);
-
-  DrawTarget *source;
-  if (aSource) {
-    const BasicCompositingRenderTarget* sourceSurface =
-      static_cast<const BasicCompositingRenderTarget*>(aSource);
-    source = sourceSurface->mDrawTarget;
-  } else {
-    source = mDrawTarget;
-  }
-
-  RefPtr<SourceSurface> snapshot = source->Snapshot();
-
-  IntRect sourceRect(aSourcePoint, aRect.Size());
-  rt->mDrawTarget->CopySurface(snapshot, sourceRect, IntPoint(0, 0));
-  return rt.forget();
+  MOZ_CRASH("Shouldn't be called!");
+  return nullptr;
 }
 
 TemporaryRef<DataTextureSource>
 BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
 {
   RefPtr<DataTextureSource> result = new DataTextureSourceBasic();
   return result.forget();
 }
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -100,17 +100,17 @@ public:
   virtual bool IsInactiveLayerManager() { return mType == BLM_INACTIVE; }
 
   virtual void BeginTransaction();
   virtual void BeginTransactionWithTarget(gfxContext* aTarget);
   virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT);
   virtual void EndTransaction(DrawThebesLayerCallback aCallback,
                               void* aCallbackData,
                               EndTransactionFlags aFlags = END_DEFAULT);
-  virtual bool AreComponentAlphaLayersEnabled() { return !IsWidgetLayerManager(); }
+  virtual bool ShouldAvoidComponentAlphaLayers() { return IsWidgetLayerManager(); }
 
   void AbortTransaction();
 
   virtual void SetRoot(Layer* aLayer);
 
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -532,16 +532,23 @@ ClientLayerManager::Hold(Layer* aLayer)
 bool
 ClientLayerManager::IsCompositingCheap()
 {
   // Whether compositing is cheap depends on the parent backend.
   return mForwarder->mShadowManager &&
          LayerManager::IsCompositingCheap(mForwarder->GetCompositorBackendType());
 }
 
+bool
+ClientLayerManager::AreComponentAlphaLayersEnabled()
+{
+  return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC &&
+         LayerManager::AreComponentAlphaLayersEnabled();
+}
+
 void
 ClientLayerManager::SetIsFirstPaint()
 {
   mForwarder->SetIsFirstPaint();
 }
 
 TextureClientPool*
 ClientLayerManager::GetTexturePool(SurfaceFormat aFormat)
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -140,16 +140,19 @@ public:
 
   void* GetThebesLayerCallbackData() const
   { return mThebesLayerCallbackData; }
 
   CompositorChild* GetRemoteRenderer();
 
   CompositorChild* GetCompositorChild();
 
+  // Disable component alpha layers with the software compositor.
+  virtual bool ShouldAvoidComponentAlphaLayers() { return !IsCompositingCheap(); }
+
   /**
    * Called for each iteration of a progressive tile update. Updates
    * aMetrics with the current scroll offset and scale being used to composite
    * the primary scrollable layer in this manager, to determine what area
    * intersects with the target composition bounds.
    * aDrawingCritical will be true if the current drawing operation is using
    * the critical displayport.
    * Returns true if the update should continue, or false if it should be
@@ -180,16 +183,18 @@ public:
 
   virtual void DidComposite(uint64_t aTransactionId);
 
   virtual bool SupportsMixBlendModes(EnumSet<gfx::CompositionOp>& aMixBlendModes) MOZ_OVERRIDE
   {
    return (GetTextureFactoryIdentifier().mSupportedBlendModes & aMixBlendModes) == aMixBlendModes;
   }
 
+  virtual bool AreComponentAlphaLayersEnabled() MOZ_OVERRIDE;
+
   // Log APZ test data for the current paint. We supply the paint sequence
   // number ourselves, and take care of calling APZTestData::StartNewPaint()
   // when a new paint is started.
   void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
                                   const std::string& aKey,
                                   const std::string& aValue)
   {
     mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -139,16 +139,23 @@ LayerManagerComposite::Destroy()
 }
 
 void
 LayerManagerComposite::UpdateRenderBounds(const nsIntRect& aRect)
 {
   mRenderBounds = aRect;
 }
 
+bool
+LayerManagerComposite::AreComponentAlphaLayersEnabled()
+{
+  return Compositor::GetBackend() != LayersBackend::LAYERS_BASIC &&
+         LayerManager::AreComponentAlphaLayersEnabled();
+}
+
 void
 LayerManagerComposite::BeginTransaction()
 {
   mInTransaction = true;
   
   if (!mCompositor->Ready()) {
     return;
   }
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -147,16 +147,18 @@ public:
   {
     MOZ_CRASH("Shouldn't be called for composited layer manager");
   }
   virtual void GetBackendName(nsAString& name) MOZ_OVERRIDE
   {
     MOZ_CRASH("Shouldn't be called for composited layer manager");
   }
 
+  virtual bool AreComponentAlphaLayersEnabled() MOZ_OVERRIDE;
+
   virtual TemporaryRef<DrawTarget>
     CreateOptimalMaskDrawTarget(const IntSize &aSize) MOZ_OVERRIDE;
 
   virtual const char* Name() const MOZ_OVERRIDE { return ""; }
 
   enum WorldTransforPolicy {
     ApplyWorldTransform,
     DontApplyWorldTransform
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -193,20 +193,26 @@ protected:
 
     testStartTime = TimeStamp::Now();
     AsyncPanZoomController::SetFrameTime(testStartTime);
 
     mcc = new NiceMock<MockContentControllerDelayed>();
     tm = new TestAPZCTreeManager();
     apzc = new TestAsyncPanZoomController(0, mcc, tm, mGestureBehavior);
     apzc->SetFrameMetrics(TestFrameMetrics());
+
+    // Since most tests pass inputs directly to the APZC instead of going through
+    // the tree manager, we need to build the overscroll handoff chain explicitly
+    // for panning and animation-cancelling to work correctly.
+    tm->BuildOverscrollHandoffChain(apzc);
   }
 
   virtual void TearDown()
   {
+    tm->ClearOverscrollHandoffChain();
     apzc->Destroy();
   }
 
   void SetMayHaveTouchListeners()
   {
     apzc->GetFrameMetrics().mMayHaveTouchListeners = true;
   }
 
@@ -246,52 +252,52 @@ ApzcDown(AsyncPanZoomController* apzc, i
 static nsEventStatus
 ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int& aTime)
 {
   MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
   return apzc->ReceiveInputEvent(mti);
 }
 
-static nsEventStatus
-ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime,
-        int aTapLength, MockContentControllerDelayed* mcc = nullptr)
+static void
+ApzcTap(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength,
+        nsEventStatus (*aOutEventStatuses)[2] = nullptr)
 {
-  nsEventStatus status = ApzcDown(apzc, aX, aY, aTime);
-  if (mcc != nullptr) {
-    // There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but
-    // if we were provided a non-null mcc we want to clear them.
-    mcc->CheckHasDelayedTask();
-    mcc->ClearDelayedTask();
-    mcc->CheckHasDelayedTask();
-    mcc->ClearDelayedTask();
+  nsEventStatus status = ApzcDown(aApzc, aX, aY, aTime);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[0] = status;
+  }
+  aTime += aTapLength;
+  status = ApzcUp(aApzc, aX, aY, aTime);
+  if (aOutEventStatuses) {
+    (*aOutEventStatuses)[1] = status;
   }
-  EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
-  aTime += aTapLength;
-  return ApzcUp(apzc, aX, aY, aTime);
+}
+
+static void
+ApzcTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime, int aTapLength)
+{
+  nsEventStatus statuses[2];
+  ApzcTap(aApzc, aX, aY, aTime, aTapLength, &statuses);
+  EXPECT_EQ(nsEventStatus_eConsumeNoDefault, statuses[0]);
+  EXPECT_EQ(nsEventStatus_eIgnore, statuses[1]);
 }
 
 static void
 ApzcPan(AsyncPanZoomController* aApzc,
-        TestAPZCTreeManager* aTreeManager,
         int& aTime,
         int aTouchStartY,
         int aTouchEndY,
         bool aKeepFingerDown = false,
         nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
         nsEventStatus (*aOutEventStatuses)[4] = nullptr)
 {
   const int TIME_BETWEEN_TOUCH_EVENT = 100;
   const int OVERCOME_TOUCH_TOLERANCE = 100;
 
-  // Since we're passing inputs directly to the APZC instead of going through
-  // the tree manager, we need to build the overscroll handoff chain explicitly
-  // for panning to work correctly.
-  aTreeManager->BuildOverscrollHandoffChain(aApzc);
-
   // Make sure the move is large enough to not be handled as a tap
   nsEventStatus status = ApzcDown(aApzc, 10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE, aTime);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
 
@@ -323,38 +329,33 @@ ApzcPan(AsyncPanZoomController* aApzc,
   } else {
     status = (nsEventStatus)-1;
   }
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[3] = status;
   }
 
   aTime += TIME_BETWEEN_TOUCH_EVENT;
-
-  // Since we've explicitly built the overscroll handoff chain before
-  // touch-start, we need to explicitly clear it after touch-end.
-  aTreeManager->ClearOverscrollHandoffChain();
 }
 
 /*
  * Dispatches mock touch events to the apzc and checks whether apzc properly
  * consumed them and triggered scrolling behavior.
  */
 static void
 ApzcPanAndCheckStatus(AsyncPanZoomController* aApzc,
-                      TestAPZCTreeManager* aTreeManager,
                       int& aTime,
                       int aTouchStartY,
                       int aTouchEndY,
                       bool expectIgnoredPan,
                       bool hasTouchListeners,
                       nsTArray<uint32_t>* aAllowedTouchBehaviors)
 {
   nsEventStatus statuses[4]; // down, move, move, up
-  ApzcPan(aApzc, aTreeManager, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
+  ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
 
   nsEventStatus touchStartStatus;
   if (hasTouchListeners || gfxPrefs::TouchActionEnabled()) {
     // APZC shouldn't consume the start event now, instead queueing it up
     // waiting for content's response and/or allowed behavior.
     touchStartStatus = nsEventStatus_eConsumeDoDefault;
   } else {
     // APZC should go into the touching state and therefore consume the event.
@@ -374,16 +375,26 @@ ApzcPanAndCheckStatus(AsyncPanZoomContro
     // APZC should go into the panning state and therefore consume the event.
     touchMoveStatus = nsEventStatus_eConsumeNoDefault;
   }
   EXPECT_EQ(touchMoveStatus, statuses[1]);
   EXPECT_EQ(touchMoveStatus, statuses[2]);
 }
 
 static void
+ApzcPanNoFling(AsyncPanZoomController* aApzc,
+               int& aTime,
+               int aTouchStartY,
+               int aTouchEndY)
+{
+  ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY);
+  aApzc->CancelAnimation();
+}
+
+static void
 ApzcPinchWithPinchInput(AsyncPanZoomController* aApzc,
                         int aFocusX, int aFocusY, float aScale,
                         nsEventStatus (*aOutEventStatuses)[3] = nullptr)
 {
   nsEventStatus actualStatus = aApzc->HandleGestureEvent(
     PinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
                       0, TimeStamp(), ScreenPoint(aFocusX, aFocusY),
                       10.0, 10.0, 0));
@@ -791,29 +802,33 @@ protected:
     int touchEnd = 10;
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
 
     nsTArray<uint32_t> allowedTouchBehaviors;
     allowedTouchBehaviors.AppendElement(aBehavior);
 
     // Pan down
-    ApzcPanAndCheckStatus(apzc, tm, time, touchStart, touchEnd, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
+    ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
     apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
     if (aShouldTriggerScroll) {
       EXPECT_EQ(ScreenPoint(0, -(touchEnd-touchStart)), pointOut);
       EXPECT_NE(ViewTransform(), viewTransformOut);
     } else {
       EXPECT_EQ(ScreenPoint(), pointOut);
       EXPECT_EQ(ViewTransform(), viewTransformOut);
     }
 
+    // Clear the fling from the previous pan, or stopping it will
+    // consume the next touchstart
+    apzc->CancelAnimation();
+
     // Pan back
-    ApzcPanAndCheckStatus(apzc, tm, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
+    ApzcPanAndCheckStatus(apzc, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
     apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
     EXPECT_EQ(ScreenPoint(), pointOut);
     EXPECT_EQ(ViewTransform(), viewTransformOut);
   }
 
   void DoPanWithPreventDefaultTest()
   {
@@ -823,17 +838,17 @@ protected:
     int touchStart = 50;
     int touchEnd = 10;
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
 
     // Pan down
     nsTArray<uint32_t> allowedTouchBehaviors;
     allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
-    ApzcPanAndCheckStatus(apzc, tm, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
+    ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
 
     // Send the signal that content has handled and preventDefaulted the touch
     // events. This flushes the event queue.
     apzc->ContentReceivedTouch(true);
     // Run all pending tasks (this should include at least the
     // prevent-default timer).
     EXPECT_LE(1, mcc->RunThroughDelayedTasks());
 
@@ -891,17 +906,17 @@ TEST_F(APZCBasicTester, Fling) {
 
   int time = 0;
   int touchStart = 50;
   int touchEnd = 10;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // Fling down. Each step scroll further down
-  ApzcPan(apzc, tm, time, touchStart, touchEnd);
+  ApzcPan(apzc, time, touchStart, touchEnd);
   ScreenPoint lastPoint;
   for (int i = 1; i < 50; i+=1) {
     apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(i), &viewTransformOut, pointOut);
     EXPECT_GT(pointOut.y, lastPoint.y);
     lastPoint = pointOut;
   }
 }
 
@@ -914,64 +929,115 @@ protected:
   // is at a slow velocity, and we check that the tap does trigger sending
   // a tap to content. See bug 1022956.
   void DoFlingStopTest(bool aSlow) {
     int time = 0;
     int touchStart = 50;
     int touchEnd = 10;
 
     // Start the fling down.
-    ApzcPan(apzc, tm, time, touchStart, touchEnd);
+    ApzcPan(apzc, time, touchStart, touchEnd);
     // The touchstart from the pan will leave some cancelled tasks in the queue, clear them out
-    EXPECT_EQ(2, mcc->RunThroughDelayedTasks());
+    while (mcc->RunThroughDelayedTasks());
 
     // If we want to tap while the fling is fast, let the fling advance for 10ms only. If we want
     // the fling to slow down more, advance to 2000ms. These numbers may need adjusting if our
     // friction and threshold values change, but they should be deterministic at least.
     int timeDelta = aSlow ? 2000 : 10;
     int tapCallsExpected = aSlow ? 1 : 0;
-    int delayedTasksExpected = aSlow ? 3 : 2;
 
     // Advance the fling animation by timeDelta milliseconds.
     ScreenPoint pointOut;
     ViewTransform viewTransformOut;
     apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(timeDelta), &viewTransformOut, pointOut);
 
     // Deliver a tap to abort the fling. Ensure that we get a HandleSingleTap
     // call out of it if and only if the fling is slow.
     EXPECT_CALL(*mcc, HandleSingleTap(_, 0, apzc->GetGuid())).Times(tapCallsExpected);
-    ApzcTap(apzc, 10, 10, time, 0, nullptr);
-    EXPECT_EQ(delayedTasksExpected, mcc->RunThroughDelayedTasks());
+    ApzcTap(apzc, 10, 10, time, 0);
+    while (mcc->RunThroughDelayedTasks());
 
     // Verify that we didn't advance any further after the fling was aborted, in either case.
     ScreenPoint finalPointOut;
     apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(timeDelta + 1000), &viewTransformOut, finalPointOut);
     EXPECT_EQ(pointOut.x, finalPointOut.x);
     EXPECT_EQ(pointOut.y, finalPointOut.y);
 
     apzc->AssertStateIsReset();
   }
+
+  void DoFlingStopWithSlowListener(bool aPreventDefault) {
+    SetMayHaveTouchListeners();
+
+    int time = 0;
+    int touchStart = 50;
+    int touchEnd = 10;
+
+    // Start the fling down.
+    ApzcPan(apzc, time, touchStart, touchEnd);
+    apzc->ContentReceivedTouch(false);
+    while (mcc->RunThroughDelayedTasks());
+
+    // Sample the fling a couple of times to ensure it's going.
+    ScreenPoint point, finalPoint;
+    ViewTransform viewTransform;
+    apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(10), &viewTransform, point);
+    apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(20), &viewTransform, finalPoint);
+    EXPECT_GT(finalPoint.y, point.y);
+
+    // Now we put our finger down to stop the fling
+    ApzcDown(apzc, 10, 10, time);
+
+    // Re-sample to make sure it hasn't moved
+    apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(30), &viewTransform, point);
+    EXPECT_EQ(finalPoint.x, point.x);
+    EXPECT_EQ(finalPoint.y, point.y);
+
+    // respond to the touchdown that stopped the fling.
+    // even if we do a prevent-default on it, the animation should remain stopped.
+    apzc->ContentReceivedTouch(aPreventDefault);
+    while (mcc->RunThroughDelayedTasks());
+
+    // Verify the page hasn't moved
+    apzc->SampleContentTransformForFrame(testStartTime + TimeDuration::FromMilliseconds(100), &viewTransform, point);
+    EXPECT_EQ(finalPoint.x, point.x);
+    EXPECT_EQ(finalPoint.y, point.y);
+
+    // clean up
+    ApzcUp(apzc, 10, 10, time);
+    while (mcc->RunThroughDelayedTasks());
+
+    apzc->AssertStateIsReset();
+  }
 };
 
 TEST_F(APZCFlingStopTester, FlingStop) {
   DoFlingStopTest(false);
 }
 
 TEST_F(APZCFlingStopTester, FlingStopTap) {
   DoFlingStopTest(true);
 }
 
+TEST_F(APZCFlingStopTester, FlingStopSlowListener) {
+  DoFlingStopWithSlowListener(false);
+}
+
+TEST_F(APZCFlingStopTester, FlingStopPreventDefault) {
+  DoFlingStopWithSlowListener(true);
+}
+
 TEST_F(APZCBasicTester, OverScrollPanning) {
   SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
 
   // Pan sufficiently to hit overscroll behavior
   int time = 0;
   int touchStart = 500;
   int touchEnd = 10;
-  ApzcPan(apzc, tm, time, touchStart, touchEnd);
+  ApzcPan(apzc, time, touchStart, touchEnd);
   EXPECT_TRUE(apzc->IsOverscrolled());
 
   // Note that in the calls to SampleContentTransformForFrame below, the time
   // increment used is sufficiently large for the animation to have completed. However,
   // any single call to SampleContentTransformForFrame will not finish an animation
   // *and* also proceed through the following animation, if there is one.
   // Therefore the minimum number of calls to go from an overscroll-inducing pan
   // to a reset state is 3; these are documented further below.
@@ -1001,17 +1067,17 @@ TEST_F(APZCBasicTester, OverScrollPannin
 
 TEST_F(APZCBasicTester, OverScrollAbort) {
   SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
 
   // Pan sufficiently to hit overscroll behavior
   int time = 0;
   int touchStart = 500;
   int touchEnd = 10;
-  ApzcPan(apzc, tm, time, touchStart, touchEnd);
+  ApzcPan(apzc, time, touchStart, touchEnd);
   EXPECT_TRUE(apzc->IsOverscrolled());
 
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // This sample call will run to the end of the non-overscrolling fling animation
   // and will schedule the overscrolling fling animation (see comment in OverScrollPanning
   // above for more explanation).
@@ -1028,51 +1094,57 @@ TEST_F(APZCBasicTester, OverScrollAbort)
 TEST_F(APZCBasicTester, OverScrollPanningAbort) {
   SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
 
   // Pan sufficiently to hit overscroll behaviour. Keep the finger down so
   // the pan does not end.
   int time = 0;
   int touchStart = 500;
   int touchEnd = 10;
-  ApzcPan(apzc, tm, time, touchStart, touchEnd,
+  ApzcPan(apzc, time, touchStart, touchEnd,
           true);                   // keep finger down
   EXPECT_TRUE(apzc->IsOverscrolled());
 
   // Check that calling CancelAnimation() while the user is still panning
   // (and thus no fling or snap-back animation has had a chance to start)
   // clears the overscroll.
   apzc->CancelAnimation();
   EXPECT_FALSE(apzc->IsOverscrolled());
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, ShortPress) {
   MakeApzcUnzoomable();
 
   int time = 0;
-  nsEventStatus status = ApzcTap(apzc, 10, 10, time, 100, mcc.get());
-  EXPECT_EQ(nsEventStatus_eIgnore, status);
+  ApzcTapAndCheckStatus(apzc, 10, 10, time, 100);
+  // There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but
+  // we want to clear those.
+  mcc->ClearDelayedTask();
+  mcc->ClearDelayedTask();
 
   // This verifies that the single tap notification is sent after the
   // touchdown is fully processed. The ordering here is important.
   mcc->CheckHasDelayedTask();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   mcc->RunDelayedTask();
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, MediumPress) {
   MakeApzcUnzoomable();
 
   int time = 0;
-  nsEventStatus status = ApzcTap(apzc, 10, 10, time, 400, mcc.get());
-  EXPECT_EQ(nsEventStatus_eIgnore, status);
+  ApzcTapAndCheckStatus(apzc, 10, 10, time, 400);
+  // There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but
+  // we want to clear those.
+  mcc->ClearDelayedTask();
+  mcc->ClearDelayedTask();
 
   // This verifies that the single tap notification is sent after the
   // touchdown is fully processed. The ordering here is important.
   mcc->CheckHasDelayedTask();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   mcc->RunDelayedTask();
 
@@ -1596,17 +1668,19 @@ TEST_F(APZCTreeManagerTester, HitTesting
   // Silence GMock warnings about "uninteresting mock function calls".
   EXPECT_CALL(*mcc, PostDelayedTask(_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   // This first pan will move the APZC by 50 pixels, and dispatch a paint request.
   // Since this paint request is in the queue to Gecko, transformToGecko will
   // take it into account.
-  ApzcPan(apzcroot, manager, time, 100, 50);
+  manager->BuildOverscrollHandoffChain(apzcroot);
+  ApzcPanNoFling(apzcroot, time, 100, 50);
+  manager->ClearOverscrollHandoffChain();
 
   // Hit where layers[3] used to be. It should now hit the root.
   hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
   EXPECT_EQ(apzcroot, hit.get());
   // transformToApzc doesn't unapply the root's own async transform
   EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
   // and transformToGecko unapplies it and then reapplies it, because by the
   // time the event being transformed reaches Gecko the new paint request will
@@ -1622,17 +1696,19 @@ TEST_F(APZCTreeManagerTester, HitTesting
   // transformToGecko reapplies both the css transform and the async transform
   // because we have already issued a paint request with it.
   EXPECT_EQ(gfxPoint(25, 25), transformToGecko.Transform(gfxPoint(12.5, 75)));
 
   // This second pan will move the APZC by another 50 pixels but since the paint
   // request dispatched above has not "completed", we will not dispatch another
   // one yet. Now we have an async transform on top of the pending paint request
   // transform.
-  ApzcPan(apzcroot, manager, time, 100, 50);
+  manager->BuildOverscrollHandoffChain(apzcroot);
+  ApzcPanNoFling(apzcroot, time, 100, 50);
+  manager->ClearOverscrollHandoffChain();
 
   // Hit where layers[3] used to be. It should now hit the root.
   hit = GetTargetAPZC(manager, ScreenPoint(75, 75), transformToApzc, transformToGecko);
   EXPECT_EQ(apzcroot, hit.get());
   // transformToApzc doesn't unapply the root's own async transform
   EXPECT_EQ(gfxPoint(75, 75), transformToApzc.Transform(gfxPoint(75, 75)));
   // transformToGecko unapplies the full async transform of -100 pixels, and then
   // reapplies the "D" transform of -50 leading to an overall adjustment of +50
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -602,27 +602,17 @@ gfxUtils::DrawPixelSnapped(gfxContext*  
 
     aFilter = ReduceResamplingFilter(aFilter, aImageRect.Width(), aImageRect.Height(), aSourceRect.Width(), aSourceRect.Height());
 
     gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
 
     // On Mobile, we don't ever want to do this; it has the potential for
     // allocating very large temporary surfaces, especially since we'll
     // do full-page snapshots often (see bug 749426).
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-    // If the pattern translation is large we can get into trouble with pixman's
-    // 16 bit coordinate limits. For now, we only do this on platforms where
-    // we know we have the pixman limits. 16384.0 is a somewhat arbitrary
-    // large number to make sure we avoid the expensive fmod when we can, but
-    // still maintain a safe margin from the actual limit
-    if (doTile && (userSpaceToImageSpace._32 > 16384.0 || userSpaceToImageSpace._31 > 16384.0)) {
-        userSpaceToImageSpace._31 = fmod(userSpaceToImageSpace._31, aImageRect.width);
-        userSpaceToImageSpace._32 = fmod(userSpaceToImageSpace._32, aImageRect.height);
-    }
-#else
+#ifndef MOZ_GFX_OPTIMIZE_MOBILE
     // OK now, the hard part left is to account for the subimage sampling
     // restriction. If all the transforms involved are just integer
     // translations, then we assume no resampling will occur so there's
     // nothing to do.
     // XXX if only we had source-clipping in cairo!
     if (aContext->CurrentMatrix().HasNonIntegerTranslation() ||
         aUserSpaceToImageSpace.HasNonIntegerTranslation()) {
         if (doTile || !aSubimage.Contains(aImageRect)) {
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -78,16 +78,18 @@ using namespace mozilla::image;
 static const char *kFeatureLevelPref =
   "gfx.direct3d.last_used_feature_level_idx";
 static const int kSupportedFeatureLevels[] =
   { D3D10_FEATURE_LEVEL_10_1, D3D10_FEATURE_LEVEL_10_0,
     D3D10_FEATURE_LEVEL_9_3 };
 
 class GfxD2DSurfaceReporter MOZ_FINAL : public nsIMemoryReporter
 {
+    ~GfxD2DSurfaceReporter() {}
+
 public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData, bool aAnonymize)
     {
         nsresult rv;
 
--- a/image/decoders/icon/win/nsIconChannel.h
+++ b/image/decoders/icon/win/nsIconChannel.h
@@ -22,25 +22,26 @@
 #include "nsIIconURI.h"
 
 #include <windows.h>
 
 class nsIFile;
 
 class nsIconChannel MOZ_FINAL : public nsIChannel, public nsIStreamListener
 {
+  ~nsIconChannel();
+
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIREQUEST
   NS_DECL_NSICHANNEL
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
 
   nsIconChannel();
-  ~nsIconChannel();
 
   nsresult Init(nsIURI* uri);
 
 protected:
   nsCOMPtr<nsIURI> mUrl;
   nsCOMPtr<nsIURI> mOriginalURI;
   int64_t          mContentLength;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
--- a/image/src/ClippedImage.cpp
+++ b/image/src/ClippedImage.cpp
@@ -8,22 +8,23 @@
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 
 #include "ClippedImage.h"
 #include "Orientation.h"
 #include "SVGImageContext.h"
 
-using namespace mozilla;
-using namespace mozilla::gfx;
-using mozilla::layers::LayerManager;
-using mozilla::layers::ImageContainer;
 
 namespace mozilla {
+
+using namespace gfx;
+using layers::LayerManager;
+using layers::ImageContainer;
+
 namespace image {
 
 class ClippedImageCachedSurface
 {
 public:
   ClippedImageCachedSurface(TemporaryRef<SourceSurface> aSurface,
                             const nsIntSize& aViewportSize,
                             const SVGImageContext* aSVGContext,
--- a/image/src/ClippedImage.h
+++ b/image/src/ClippedImage.h
@@ -21,31 +21,31 @@ class DrawSingleTileCallback;
  * An Image wrapper that clips an image against a rectangle. Right now only
  * absolute coordinates in pixels are supported.
  *
  * XXX(seth): There a known (performance, not correctness) issue with
  * GetImageContainer. See the comments for that method for more information.
  */
 class ClippedImage : public ImageWrapper
 {
-  typedef mozilla::gfx::SourceSurface SourceSurface;
+  typedef gfx::SourceSurface SourceSurface;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   NS_IMETHOD GetWidth(int32_t* aWidth) MOZ_OVERRIDE;
   NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE;
-  NS_IMETHOD_(mozilla::TemporaryRef<SourceSurface>)
+  NS_IMETHOD_(TemporaryRef<SourceSurface>)
     GetFrame(uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE;
-  NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager,
-                               mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE;
+  NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
+                               layers::ImageContainer** _retval) MOZ_OVERRIDE;
   NS_IMETHOD Draw(gfxContext* aContext,
                   GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
                   const SVGImageContext* aSVGContext,
                   uint32_t aWhichFrame,
@@ -55,17 +55,17 @@ public:
   NS_IMETHOD_(nsIntRect) GetImageSpaceInvalidationRect(const nsIntRect& aRect) MOZ_OVERRIDE;
 
 protected:
   ClippedImage(Image* aImage, nsIntRect aClip);
 
   virtual ~ClippedImage();
 
 private:
-  mozilla::TemporaryRef<SourceSurface>
+  TemporaryRef<SourceSurface>
     GetFrameInternal(const nsIntSize& aViewportSize,
                      const SVGImageContext* aSVGContext,
                      uint32_t aWhichFrame,
                      uint32_t aFlags);
   bool ShouldClip();
   bool MustCreateSurface(gfxContext* aContext,
                          const gfxMatrix& aTransform,
                          const gfxRect& aSourceRect,
--- a/image/src/DiscardTracker.cpp
+++ b/image/src/DiscardTracker.cpp
@@ -19,17 +19,17 @@ static const char* sDiscardTimeoutPref =
 /* static */ bool DiscardTracker::sInitialized = false;
 /* static */ bool DiscardTracker::sTimerOn = false;
 /* static */ Atomic<bool> DiscardTracker::sDiscardRunnablePending(false);
 /* static */ uint64_t DiscardTracker::sCurrentDecodedImageBytes = 0;
 /* static */ uint32_t DiscardTracker::sMinDiscardTimeoutMs = 10000;
 /* static */ uint32_t DiscardTracker::sMaxDecodedImageKB = 42 * 1024;
 /* static */ uint32_t DiscardTracker::sHardLimitDecodedImageKB = 0;
 /* static */ PRLock * DiscardTracker::sAllocationLock = nullptr;
-/* static */ mozilla::Mutex* DiscardTracker::sNodeListMutex = nullptr;
+/* static */ Mutex* DiscardTracker::sNodeListMutex = nullptr;
 /* static */ Atomic<bool> DiscardTracker::sShutdown(false);
 
 /*
  * When we notice we're using too much memory for decoded images, we enqueue a
  * DiscardRunnable, which runs this code.
  */
 NS_IMETHODIMP
 DiscardTracker::DiscardRunnable::Run()
--- a/image/src/DiscardTracker.h
+++ b/image/src/DiscardTracker.h
@@ -118,23 +118,23 @@ class DiscardTracker
     static void MaybeDiscardSoon();
     static void TimerCallback(nsITimer *aTimer, void *aClosure);
     static void DiscardNow();
 
     static LinkedList<Node> sDiscardableImages;
     static nsCOMPtr<nsITimer> sTimer;
     static bool sInitialized;
     static bool sTimerOn;
-    static mozilla::Atomic<bool> sDiscardRunnablePending;
+    static Atomic<bool> sDiscardRunnablePending;
     static uint64_t sCurrentDecodedImageBytes;
     static uint32_t sMinDiscardTimeoutMs;
     static uint32_t sMaxDecodedImageKB;
     static uint32_t sHardLimitDecodedImageKB;
     // Lock for safegarding the 64-bit sCurrentDecodedImageBytes
     static PRLock *sAllocationLock;
-    static mozilla::Mutex* sNodeListMutex;
+    static Mutex* sNodeListMutex;
     static Atomic<bool> sShutdown;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif /* mozilla_imagelib_DiscardTracker_h_ */
--- a/image/src/FrameAnimator.cpp
+++ b/image/src/FrameAnimator.cpp
@@ -3,18 +3,18 @@
  * 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 "FrameAnimator.h"
 #include "FrameBlender.h"
 
 #include "imgIContainer.h"
 
-using namespace mozilla::image;
-using namespace mozilla;
+namespace mozilla {
+namespace image {
 
 FrameAnimator::FrameAnimator(FrameBlender& aFrameBlender,
                              uint16_t aAnimationMode)
   : mCurrentAnimationFrameIndex(0)
   , mLoopCounter(-1)
   , mFrameBlender(aFrameBlender)
   , mAnimationMode(aAnimationMode)
   , mDoneDecoding(false)
@@ -172,17 +172,17 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
 
   // If we're here, we successfully advanced the frame.
   ret.frameAdvanced = true;
 
   return ret;
 }
 
 FrameAnimator::RefreshResult
-FrameAnimator::RequestRefresh(const mozilla::TimeStamp& aTime)
+FrameAnimator::RequestRefresh(const TimeStamp& aTime)
 {
   // only advance the frame if the current time is greater than or
   // equal to the current frame's end time.
   TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
 
   // By default, an empty RefreshResult.
   RefreshResult ret;
 
@@ -258,9 +258,10 @@ FrameAnimator::GetCurrentAnimationFrameI
 }
 
 nsIntRect
 FrameAnimator::GetFirstFrameRefreshArea() const
 {
   return mFirstFrameRefreshArea;
 }
 
-
+} // namespace image
+} // namespace mozilla
--- a/image/src/FrameAnimator.h
+++ b/image/src/FrameAnimator.h
@@ -56,17 +56,17 @@ public:
 
   /**
    * Re-evaluate what frame we're supposed to be on, and do whatever blending
    * is necessary to get us to that frame.
    *
    * Returns the result of that blending, including whether the current frame
    * changed and what the resulting dirty rectangle is.
    */
-  RefreshResult RequestRefresh(const mozilla::TimeStamp& aTime);
+  RefreshResult RequestRefresh(const TimeStamp& aTime);
 
   /**
    * Call when this image is finished decoding so we know that there aren't any
    * more frames coming.
    */
   void SetDoneDecoding(bool aDone);
 
   /**
@@ -131,24 +131,24 @@ private: // methods
    * lived animation frames.
    *
    * @param aTime the time that the animation should advance to. This will
    *              typically be <= TimeStamp::Now().
    *
    * @returns a RefreshResult that shows whether the frame was successfully
    *          advanced, and its resulting dirty rect.
    */
-  RefreshResult AdvanceFrame(mozilla::TimeStamp aTime);
+  RefreshResult AdvanceFrame(TimeStamp aTime);
 
   /**
    * Get the time the frame we're currently displaying is supposed to end.
    *
    * In the error case, returns an "infinity" timestamp.
    */
-  mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
+  TimeStamp GetCurrentImgFrameEndTime() const;
 
 private: // data
   //! Area of the first frame that needs to be redrawn on subsequent loops.
   nsIntRect mFirstFrameRefreshArea;
 
   //! the time that the animation advanced to the current frame
   TimeStamp mCurrentAnimationFrameTime;
 
--- a/image/src/FrameBlender.cpp
+++ b/image/src/FrameBlender.cpp
@@ -5,19 +5,16 @@
 
 #include "FrameBlender.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "MainThreadUtils.h"
 
 #include "pixman.h"
 
-using namespace mozilla;
-using namespace mozilla::image;
-
 namespace mozilla {
 namespace image {
 
 FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
  : mFrames(aSequenceToUse)
  , mAnim(nullptr)
  , mLoopCount(-1)
 {
--- a/image/src/FrameBlender.h
+++ b/image/src/FrameBlender.h
@@ -7,21 +7,21 @@
 #ifndef mozilla_imagelib_FrameBlender_h_
 #define mozilla_imagelib_FrameBlender_h_
 
 #include "mozilla/MemoryReporting.h"
 #include "gfxTypes.h"
 #include "FrameSequence.h"
 #include "nsCOMPtr.h"
 
-class imgFrame;
-
 namespace mozilla {
 namespace image {
 
+class imgFrame;
+
 /**
  * FrameBlender stores and gives access to imgFrames. It also knows how to
  * blend frames from previous to next, looping if necessary.
  *
  * All logic about when and whether to blend are external to FrameBlender.
  */
 class FrameBlender
 {
@@ -73,17 +73,17 @@ public:
   void SetLoopCount(int32_t aLoopCount);
   int32_t GetLoopCount() const;
 
   void Discard();
 
   void SetSize(nsIntSize aSize) { mSize = aSize; }
 
   size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
-                                                 mozilla::MallocSizeOf aMallocSizeOf) const;
+                                                 MallocSizeOf aMallocSizeOf) const;
 
   void ResetAnimation();
 
   // "Blend" method indicates how the current image is combined with the
   // previous image.
   enum FrameBlendMethod {
     // All color components of the frame, including alpha, overwrite the current
     // contents of the frame's output buffer region
--- a/image/src/FrameSequence.cpp
+++ b/image/src/FrameSequence.cpp
@@ -1,18 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FrameSequence.h"
 
-using namespace mozilla;
-using namespace mozilla::image;
-
 namespace mozilla {
 namespace image {
 
 FrameSequence::~FrameSequence()
 {
   ClearFrames();
 }
 
--- a/image/src/FrameSequence.h
+++ b/image/src/FrameSequence.h
@@ -169,17 +169,17 @@ public:
    * Remove (and delete) all frames.
    */
   void ClearFrames();
 
   /* The total number of frames in this image. */
   uint32_t GetNumFrames() const;
 
   size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
-                                                 mozilla::MallocSizeOf aMallocSizeOf) const;
+                                                 MallocSizeOf aMallocSizeOf) const;
 
 private: // data
   //! All the frames of the image
   nsTArray<FrameDataPair> mFrames;
 };
 
 } // namespace image
 } // namespace mozilla
--- a/image/src/FrozenImage.cpp
+++ b/image/src/FrozenImage.cpp
@@ -1,18 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FrozenImage.h"
 
-using namespace mozilla::gfx;
+namespace mozilla {
 
-namespace mozilla {
+using namespace gfx;
+
 namespace image {
 
 NS_IMPL_ISUPPORTS_INHERITED0(FrozenImage, ImageWrapper)
 
 nsIntRect
 FrozenImage::FrameRect(uint32_t /* aWhichFrame - ignored */)
 {
   return InnerImage()->FrameRect(FRAME_FIRST);
@@ -81,17 +82,17 @@ FrozenImage::Draw(gfxContext* aContext,
                   uint32_t aFlags)
 {
   return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace,
                             aFill, aSubimage, aViewportSize, aSVGContext,
                             FRAME_FIRST, aFlags);
 }
 
 NS_IMETHODIMP_(void)
-FrozenImage::RequestRefresh(const mozilla::TimeStamp& aTime)
+FrozenImage::RequestRefresh(const TimeStamp& aTime)
 {
   // Do nothing.
 }
 
 NS_IMETHODIMP
 FrozenImage::GetAnimationMode(uint16_t* aAnimationMode)
 {
   *aAnimationMode = kNormalAnimMode;
--- a/image/src/FrozenImage.h
+++ b/image/src/FrozenImage.h
@@ -21,17 +21,17 @@ namespace image {
  * because any imgIContainer method that is affected by animation gets its
  * aWhichFrame argument set to FRAME_FIRST when it passes through FrozenImage.
  *
  * XXX(seth): There a known (performance, not correctness) issue with
  * GetImageContainer. See the comments for that method for more information.
  */
 class FrozenImage : public ImageWrapper
 {
-  typedef mozilla::gfx::SourceSurface SourceSurface;
+  typedef gfx::SourceSurface SourceSurface;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
   virtual void IncrementAnimationConsumers() MOZ_OVERRIDE;
   virtual void DecrementAnimationConsumers() MOZ_OVERRIDE;
 
@@ -45,17 +45,17 @@ public:
                   GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
                   const SVGImageContext* aSVGContext,
                   uint32_t aWhichFrame,
                   uint32_t aFlags) MOZ_OVERRIDE;
-  NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime) MOZ_OVERRIDE;
+  NS_IMETHOD_(void) RequestRefresh(const TimeStamp& aTime) MOZ_OVERRIDE;
   NS_IMETHOD GetAnimationMode(uint16_t* aAnimationMode) MOZ_OVERRIDE;
   NS_IMETHOD SetAnimationMode(uint16_t aAnimationMode) MOZ_OVERRIDE;
   NS_IMETHOD ResetAnimation() MOZ_OVERRIDE;
   NS_IMETHOD_(float) GetFrameIndex(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
 protected:
   FrozenImage(Image* aImage) : ImageWrapper(aImage) { }
   virtual ~FrozenImage() { }
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -75,18 +75,18 @@ public:
    * The size, in bytes, occupied by the significant data portions of the image.
    * This includes both compressed source data and decoded frames.
    */
   virtual uint32_t SizeOfData() = 0;
 
   /**
    * The components that make up SizeOfData().
    */
-  virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
-  virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+  virtual size_t HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const = 0;
+  virtual size_t HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const = 0;
   virtual size_t NonHeapSizeOfDecoded() const = 0;
   virtual size_t OutOfProcessSizeOfDecoded() const = 0;
 
   /**
    * Gets the size of the memory taken up for the parsed vector image's
    * document (e.g. SVGDocument), and returns the document's URL via the
    * aDocURL outparam.
    */
@@ -190,17 +190,17 @@ protected:
    * Helper for RequestRefresh.
    *
    * If we've had a "recent" refresh (i.e. if this image is being used in
    * multiple documents & some other document *just* called RequestRefresh() on
    * this image with a timestamp close to aTime), this method returns true.
    *
    * Otherwise, this method updates mLastRefreshTime to aTime & returns false.
    */
-  bool HadRecentRefresh(const mozilla::TimeStamp& aTime);
+  bool HadRecentRefresh(const TimeStamp& aTime);
 
   /**
    * Decides whether animation should or should not be happening,
    * and makes sure the right thing is being done.
    */
   virtual void EvaluateAnimation();
 
   /**
--- a/image/src/ImageFactory.cpp
+++ b/image/src/ImageFactory.cpp
@@ -231,17 +231,17 @@ ImageFactory::CreateRasterImage(nsIReque
       if (NS_FAILED(rv) || NS_FAILED(rv2)) {
         NS_WARNING("About to hit OOM in imagelib!");
       }
     }
   }
 
   nsAutoCString ref;
   aURI->GetRef(ref);
-  mozilla::net::nsMediaFragmentURIParser parser(ref);
+  net::nsMediaFragmentURIParser parser(ref);
   if (parser.HasResolution()) {
     newImage->SetRequestedResolution(parser.GetResolution());
   }
 
   if (parser.HasSampleSize()) {
       /* Get our principal */
       nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
       nsCOMPtr<nsIPrincipal> principal;
--- a/image/src/ImageMetadata.cpp
+++ b/image/src/ImageMetadata.cpp
@@ -6,17 +6,18 @@
 
 #include "ImageMetadata.h"
 
 #include "RasterImage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsXPCOMCID.h"
 
-using namespace mozilla::image;
+namespace mozilla {
+namespace image {
 
 void
 ImageMetadata::SetOnImage(RasterImage* image)
 {
   if (mHotspotX != -1 && mHotspotY != -1) {
     nsCOMPtr<nsISupportsPRUint32> intwrapx = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
     nsCOMPtr<nsISupportsPRUint32> intwrapy = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
     intwrapx->SetData(mHotspotX);
@@ -26,8 +27,11 @@ ImageMetadata::SetOnImage(RasterImage* i
   }
 
   image->SetLoopCount(mLoopCount);
 
   for (uint32_t i = 0; i < image->GetNumFrames(); i++) {
     image->SetFrameAsNonPremult(i, mIsNonPremultiplied);
   }
 }
+
+} // namespace image
+} // namespace mozilla
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -5,22 +5,23 @@
 
 #include "ImageWrapper.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "Orientation.h"
 
 #include "mozilla/MemoryReporting.h"
 
-using mozilla::gfx::DataSourceSurface;
-using mozilla::gfx::SourceSurface;
-using mozilla::layers::LayerManager;
-using mozilla::layers::ImageContainer;
+namespace mozilla {
 
-namespace mozilla {
+using gfx::DataSourceSurface;
+using gfx::SourceSurface;
+using layers::LayerManager;
+using layers::ImageContainer;
+
 namespace image {
 
 // Inherited methods from Image.
 
 nsresult
 ImageWrapper::Init(const char* aMimeType, uint32_t aFlags)
 {
   return mInnerImage->Init(aMimeType, aFlags);
@@ -40,23 +41,23 @@ ImageWrapper::FrameRect(uint32_t aWhichF
 
 uint32_t
 ImageWrapper::SizeOfData()
 {
   return mInnerImage->SizeOfData();
 }
 
 size_t
-ImageWrapper::HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
+ImageWrapper::HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
 {
   return mInnerImage->HeapSizeOfSourceWithComputedFallback(aMallocSizeOf);
 }
 
 size_t
-ImageWrapper::HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const
+ImageWrapper::HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const
 {
   return mInnerImage->HeapSizeOfDecodedWithComputedFallback(aMallocSizeOf);
 }
 
 size_t
 ImageWrapper::NonHeapSizeOfDecoded() const
 {
   return mInnerImage->NonHeapSizeOfDecoded();
@@ -271,17 +272,17 @@ ImageWrapper::UnlockImage()
 
 NS_IMETHODIMP
 ImageWrapper::RequestDiscard()
 {
   return mInnerImage->RequestDiscard();
 }
 
 NS_IMETHODIMP_(void)
-ImageWrapper::RequestRefresh(const mozilla::TimeStamp& aTime)
+ImageWrapper::RequestRefresh(const TimeStamp& aTime)
 {
   return mInnerImage->RequestRefresh(aTime);
 }
 
 NS_IMETHODIMP
 ImageWrapper::GetAnimationMode(uint16_t* aAnimationMode)
 {
   return mInnerImage->GetAnimationMode(aAnimationMode);
@@ -307,17 +308,17 @@ ImageWrapper::GetFrameIndex(uint32_t aWh
 
 NS_IMETHODIMP_(int32_t)
 ImageWrapper::GetFirstFrameDelay()
 {
   return mInnerImage->GetFirstFrameDelay();
 }
 
 NS_IMETHODIMP_(void)
-ImageWrapper::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
+ImageWrapper::SetAnimationStartTime(const TimeStamp& aTime)
 {
   mInnerImage->SetAnimationStartTime(aTime);
 }
 
 NS_IMETHODIMP_(nsIntRect)
 ImageWrapper::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
 {
   return mInnerImage->GetImageSpaceInvalidationRect(aRect);
--- a/image/src/ImageWrapper.h
+++ b/image/src/ImageWrapper.h
@@ -23,18 +23,18 @@ public:
 
   // Inherited methods from Image.
   virtual nsresult Init(const char* aMimeType, uint32_t aFlags) MOZ_OVERRIDE;
 
   virtual already_AddRefed<imgStatusTracker> GetStatusTracker() MOZ_OVERRIDE;
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   virtual uint32_t SizeOfData() MOZ_OVERRIDE;
-  virtual size_t HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
-  virtual size_t HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t HeapSizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t HeapSizeOfDecodedWithComputedFallback(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
   virtual size_t NonHeapSizeOfDecoded() const MOZ_OVERRIDE;
   virtual size_t OutOfProcessSizeOfDecoded() const MOZ_OVERRIDE;
 
   virtual size_t HeapSizeOfVectorImageDocument(nsACString* aDocURL = nullptr) const MOZ_OVERRIDE {
     return mInnerImage->HeapSizeOfVectorImageDocument(aDocURL);
   }
 
   virtual void IncrementAnimationConsumers() MOZ_OVERRIDE;
--- a/image/src/OrientedImage.cpp
+++ b/image/src/OrientedImage.cpp
@@ -6,23 +6,24 @@
 #include <algorithm>
 
 #include "gfxDrawable.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 
 #include "OrientedImage.h"
 
-using namespace mozilla::gfx;
-
 using std::swap;
-using mozilla::layers::LayerManager;
-using mozilla::layers::ImageContainer;
 
 namespace mozilla {
+
+using namespace gfx;