Merge m-c to b-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 09 Aug 2015 16:40:04 -0700
changeset 288671 717ba6380b6020ee154a8ec240727fce4e3a5e56
parent 288670 8cba0a38a1c338fb7c5ad610c289f8973c9ef171 (current diff)
parent 288668 0e269a1f1beb284a630f1a8ca92c05254333f6f1 (diff)
child 288672 4f7ff69834fe6bef017c999cbcff03cfdc7ca8b1
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone42.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 b-i
browser/components/extensions/bootstrap.js
browser/components/extensions/prepare.py
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -991,16 +991,19 @@ pref("gfx.canvas.azure.accelerated", tru
 pref("gfx.canvas.skiagl.dynamic-cache", true);
 
 // Limit skia to canvases the size of the device screen or smaller
 pref("gfx.canvas.max-size-for-skia-gl", -1);
 
 // enable fence with readpixels for SurfaceStream
 pref("gfx.gralloc.fence-with-readpixels", true);
 
+// enable screen mirroring to external display
+pref("gfx.screen-mirroring.enabled", true);
+
 // The url of the page used to display network error details.
 pref("b2g.neterror.url", "net_error.html");
 
 // The origin used for the shared themes uri space.
 pref("b2g.theme.origin", "app://theme.gaiamobile.org");
 pref("dom.mozApps.themable", true);
 pref("dom.mozApps.selected_theme", "default_theme.gaiamobile.org");
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -449,21 +449,17 @@ pref("browser.tabs.drawInTitlebar", true
 
 // When tabs opened by links in other tabs via a combination of
 // browser.link.open_newwindow being set to 3 and target="_blank" etc are
 // closed:
 // true   return to the tab that opened this tab (its owner)
 // false  return to the adjacent tab (old default)
 pref("browser.tabs.selectOwnerOnClose", true);
 
-#ifdef RELEASE_BUILD
-pref("browser.tabs.showAudioPlayingIcon", false);
-#else
 pref("browser.tabs.showAudioPlayingIcon", true);
-#endif
 
 pref("browser.ctrlTab.previews", false);
 
 // By default, do not export HTML at shutdown.
 // If true, at shutdown the bookmarks in your menu and toolbar will
 // be exported as HTML to the bookmarks.html file.
 pref("browser.bookmarks.autoExportHTML",          false);
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -571,17 +571,17 @@ searchbar[oneoffui] {
 }
 
 toolbarbutton.bookmark-item {
   max-width: 13em;
 }
 
 /* Apply crisp rendering for favicons at exactly 2dppx resolution */
 @media (resolution: 2dppx) {
-  .alltabs-popup > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon,
+  #alltabs-popup > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon,
   .menuitem-with-favicon > .menu-iconic-left > .menu-iconic-icon {
     image-rendering: -moz-crisp-edges;
   }
 
   .bookmark-item > .toolbarbutton-icon,
   .bookmark-item > .menu-iconic-left > .menu-iconic-icon,
   #personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon {
     image-rendering: -moz-crisp-edges;
--- a/browser/base/content/test/general/browser_datachoices_notification.js
+++ b/browser/base/content/test/general/browser_datachoices_notification.js
@@ -19,16 +19,29 @@ const PREF_BRANCH = "datareporting.polic
 const PREF_DRS_ENABLED = "datareporting.healthreport.service.enabled";
 const PREF_BYPASS_NOTIFICATION = PREF_BRANCH + "dataSubmissionPolicyBypassNotification";
 const PREF_CURRENT_POLICY_VERSION = PREF_BRANCH + "currentPolicyVersion";
 const PREF_ACCEPTED_POLICY_VERSION = PREF_BRANCH + "dataSubmissionPolicyAcceptedVersion";
 const PREF_ACCEPTED_POLICY_DATE = PREF_BRANCH + "dataSubmissionPolicyNotifiedTime";
 
 const TEST_POLICY_VERSION = 37;
 
+function fakeShowPolicyTimeout(set, clear) {
+  let reportingPolicy =
+    Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm", {}).Policy;
+  reportingPolicy.setShowInfobarTimeout = set;
+  reportingPolicy.clearShowInfobarTimeout = clear;
+}
+
+function sendSessionRestoredNotification() {
+  let reportingPolicyImpl =
+    Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm", {}).TelemetryReportingPolicyImpl;
+  reportingPolicyImpl.observe(null, "sessionstore-windows-restored", null);
+}
+
 /**
  * Wait for a tick.
  */
 function promiseNextTick() {
   return new Promise(resolve => executeSoon(resolve));
 }
 
 /**
@@ -51,16 +64,31 @@ function promiseWaitForAlertActive(aNoti
  * @return {Promise} Resolved when the notification is closed.
  */
 function promiseWaitForNotificationClose(aNotification) {
   let deferred = PromiseUtils.defer();
   waitForNotificationClose(aNotification, deferred.resolve);
   return deferred.promise;
 }
 
+function triggerInfoBar(expectedTimeoutMs) {
+  let showInfobarCallback = null;
+  let timeoutMs = null;
+  fakeShowPolicyTimeout((callback, timeout) => {
+    showInfobarCallback = callback;
+    timeoutMs = timeout;
+  }, () => {});
+  sendSessionRestoredNotification();
+  Assert.ok(!!showInfobarCallback, "Must have a timer callback.");
+  if (expectedTimeoutMs !== undefined) {
+    Assert.equal(timeoutMs, expectedTimeoutMs, "Timeout should match");
+  }
+  showInfobarCallback();
+}
+
 let checkInfobarButton = Task.async(function* (aNotification) {
   // Check that the button on the data choices infobar does the right thing.
   let buttons = aNotification.getElementsByTagName("button");
   Assert.equal(buttons.length, 1, "There is 1 button in the data reporting notification.");
   let button = buttons[0];
 
   // Add an observer to ensure the "advanced" pane opened (but don't bother
   // closing it - we close the entire window when done.)
@@ -125,21 +153,21 @@ add_task(function* test_single_window(){
   Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), 0,
                "No version should be set on init.");
   Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, 0), 0,
                "No date should be set on init.");
   Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
             "User not notified about datareporting policy.");
 
   let alertShownPromise = promiseWaitForAlertActive(notificationBox);
-  // This should be false and trigger the Infobar.
   Assert.ok(!TelemetryReportingPolicy.canUpload(),
-            "User should not be allowed to upload and the infobar should be triggered.");
+            "User should not be allowed to upload.");
 
   // Wait for the infobar to be displayed.
+  triggerInfoBar(10 * 1000);
   yield alertShownPromise;
 
   Assert.equal(notificationBox.allNotifications.length, 1, "Notification Displayed.");
   Assert.ok(TelemetryReportingPolicy.canUpload(), "User should be allowed to upload now.");
 
   yield promiseNextTick();
   let promiseClosed = promiseWaitForNotificationClose(notificationBox.currentNotification);
   yield checkInfobarButton(notificationBox.currentNotification);
@@ -180,20 +208,21 @@ add_task(function* test_multiple_windows
   Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, 0), 0, "No date should be set on init.");
   Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), "User not notified about datareporting policy.");
 
   let showAlertPromises = [
     promiseWaitForAlertActive(notificationBoxes[0]),
     promiseWaitForAlertActive(notificationBoxes[1])
   ];
 
-  // This should be false and trigger the Infobar.
   Assert.ok(!TelemetryReportingPolicy.canUpload(),
-            "User should not be allowed to upload and the infobar should be triggered.");
+            "User should not be allowed to upload.");
 
+  // Wait for the infobars.
+  triggerInfoBar(10 * 1000);
   yield Promise.all(showAlertPromises);
 
   // Both notification were displayed. Close one and check that both gets closed.
   let closeAlertPromises = [
     promiseWaitForNotificationClose(notificationBoxes[0].currentNotification),
     promiseWaitForNotificationClose(notificationBoxes[1].currentNotification)
   ];
   notificationBoxes[0].currentNotification.close();
--- a/browser/base/content/test/general/browser_urlbarSearchSuggestionsNotification.js
+++ b/browser/base/content/test/general/browser_urlbarSearchSuggestionsNotification.js
@@ -1,19 +1,22 @@
 const SUGGEST_ALL_PREF = "browser.search.suggest.enabled";
 const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
 const CHOICE_PREF = "browser.urlbar.userMadeSearchSuggestionsChoice";
 const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
 
 // Must run first.
 add_task(function* prepare() {
+  // The test makes only sense if unified complete is enabled.
+  Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
   let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME);
   let oldCurrentEngine = Services.search.currentEngine;
   Services.search.currentEngine = engine;
   registerCleanupFunction(function () {
+    Services.prefs.clearUserPref("browser.urlbar.unifiedcomplete");
     Services.search.currentEngine = oldCurrentEngine;
     Services.prefs.clearUserPref(SUGGEST_ALL_PREF);
     Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF);
 
     // Disable the notification for future tests so it doesn't interfere with
     // them.  clearUserPref() won't work because by default the pref is false.
     Services.prefs.setBoolPref(CHOICE_PREF, true);
 
--- a/browser/base/content/test/social/browser_social_window.js
+++ b/browser/base/content/test/social/browser_social_window.js
@@ -63,16 +63,17 @@ let manifest2 = { // used for testing in
   origin: "https://test1.example.com",
   workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
   sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html",
   iconURL: "https://test1.example.com/browser/browser/base/content/test/general/moz.png",
 };
 
 function test() {
   waitForExplicitFinish();
+  requestLongerTimeout(2);
   runSocialTests(tests, undefined, postTestCleanup);
 }
 
 let tests = {
   // check when social is totally disabled at startup (ie, no providers enabled)
   testInactiveStartup: function(cbnext) {
     is(Social.providers.length, 0, "needs zero providers to start this test.");
     ok(!SocialService.hasEnabledProviders, "no providers are enabled");
deleted file mode 100644
--- a/browser/components/extensions/prepare.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python
-# 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/.
-
-import argparse
-import json
-import uuid
-import sys
-import os.path
-
-parser = argparse.ArgumentParser(description='Create install.rdf from manifest.json')
-parser.add_argument('--locale')
-parser.add_argument('--profile')
-parser.add_argument('--uuid')
-parser.add_argument('dir')
-args = parser.parse_args()
-
-manifestFile = os.path.join(args.dir, 'manifest.json')
-manifest = json.load(open(manifestFile))
-
-locale = args.locale
-if not locale:
-    locale = manifest.get('default_locale', 'en-US')
-
-def process_locale(s):
-    if s.startswith('__MSG_') and s.endswith('__'):
-        tag = s[6:-2]
-        path = os.path.join(args.dir, '_locales', locale, 'messages.json')
-        data = json.load(open(path))
-        return data[tag]['message']
-    else:
-        return s
-
-id = args.uuid
-if not id:
-    id = '{' + str(uuid.uuid4()) + '}'
-
-name = process_locale(manifest['name'])
-desc = process_locale(manifest['description'])
-version = manifest['version']
-
-installFile = open(os.path.join(args.dir, 'install.rdf'), 'w')
-print >>installFile, '<?xml version="1.0"?>'
-print >>installFile, '<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"'
-print >>installFile, '     xmlns:em="http://www.mozilla.org/2004/em-rdf#">'
-print >>installFile
-print >>installFile, '  <Description about="urn:mozilla:install-manifest">'
-print >>installFile, '    <em:id>{}</em:id>'.format(id)
-print >>installFile, '    <em:type>2</em:type>'
-print >>installFile, '    <em:name>{}</em:name>'.format(name)
-print >>installFile, '    <em:description>{}</em:description>'.format(desc)
-print >>installFile, '    <em:version>{}</em:version>'.format(version)
-print >>installFile, '    <em:bootstrap>true</em:bootstrap>'
-
-print >>installFile, '    <em:targetApplication>'
-print >>installFile, '      <Description>'
-print >>installFile, '        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>'
-print >>installFile, '        <em:minVersion>4.0</em:minVersion>'
-print >>installFile, '        <em:maxVersion>50.0</em:maxVersion>'
-print >>installFile, '      </Description>'
-print >>installFile, '    </em:targetApplication>'
-
-print >>installFile, '  </Description>'
-print >>installFile, '</RDF>'
-installFile.close()
-
-bootstrapPath = os.path.join(os.path.dirname(sys.argv[0]), 'bootstrap.js')
-data = open(bootstrapPath).read()
-boot = open(os.path.join(args.dir, 'bootstrap.js'), 'w')
-boot.write(data)
-boot.close()
-
-if args.profile:
-    os.system('mkdir -p {}/extensions'.format(args.profile))
-    output = open(args.profile + '/extensions/' + id, 'w')
-    print >>output, os.path.realpath(args.dir)
-    output.close()
-else:
-    dir = os.path.realpath(args.dir)
-    if dir[-1] == os.sep:
-        dir = dir[:-1]
-    os.system('cd "{}"; zip ../"{}".xpi -r *'.format(args.dir, os.path.basename(dir)))
--- a/browser/components/preferences/in-content/tests/browser_privacypane_1.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_1.js
@@ -1,12 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
+  requestLongerTimeout(2);
+
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
 
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -55,28 +55,16 @@ document.addEventListener("DOMContentLoa
                         .getService(Ci.nsIURLFormatter).formatURLPref;
   document.getElementById("startTour").setAttribute("href",
                      formatURLPref("privacy.trackingprotection.introURL"));
   document.getElementById("learnMore").setAttribute("href",
                      formatURLPref("app.support.baseURL") + "private-browsing");
 
   // Update state that depends on preferences.
   prefObserver.observe();
-
-  // This check can be removed when Tracking Protection is always available.
-  let tpUIEnabled = false;
-  try {
-    tpUIEnabled = Services.prefs.getBoolPref("privacy.trackingprotection.ui.enabled");
-  } catch (ex) {
-    // The preference is not available.
-  }
-  if (!tpUIEnabled) {
-    document.getElementById("trackingProtectionSection")
-            .setAttribute("hidden", "true");
-  }
 }, false);
 
 function openPrivateWindow() {
   // Ask chrome to open a private window
   document.dispatchEvent(
     new CustomEvent("AboutPrivateBrowsingOpenWindow", {bubbles:true}));
 }
 
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
@@ -37,51 +37,51 @@
            style="width: &aboutPrivateBrowsing.width;">
         <div class="sectionHeader">&aboutPrivateBrowsing.title;</div>
         <p>&aboutPrivateBrowsing.subtitle;</p>
         <div id="list-area">
           <div>
             <div class="list-header">&aboutPrivateBrowsing.info.forgotten;</div>
             <ul id="forgotten">
               <li>&aboutPrivateBrowsing.info.history;</li>
-              <li>&aboutPrivateBrowsing.info.search;</li>
+              <li>&aboutPrivateBrowsing.info.searches;</li>
               <li>&aboutPrivateBrowsing.info.cookies;</li>
               <li>&aboutPrivateBrowsing.info.temporaryFiles;</li>
             </ul>
           </div>
           <div>
             <div class="list-header">&aboutPrivateBrowsing.info.kept;</div>
             <ul id="kept">
               <li>&aboutPrivateBrowsing.info.downloads;</li>
               <li>&aboutPrivateBrowsing.info.bookmarks;</li>
             </ul>
           </div>
         </div>
-        <p>&aboutPrivateBrowsing.note;</p>
+        <p>&aboutPrivateBrowsing.note1;</p>
         <a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore;</a>
       </div>
       <div id="trackingProtectionSection"
            style="width: &trackingProtection.width;">
         <div class="sectionHeader">&trackingProtection.title;
           <span id="tpEnabled"
                 style="width: &trackingProtection.state.width;"
                 class="showTpEnabled">&trackingProtection.state.enabled;</span>
           <span id="tpDisabled"
                 style="width: &trackingProtection.state.width;"
                 class="showTpDisabled">&trackingProtection.state.disabled;</span>
         </div>
         <p id="tpDiagram"/>
-        <p>&trackingProtection.description;</p>
+        <p>&trackingProtection.description1;</p>
         <!-- Use text links to implement plain styled buttons without an href. -->
         <label xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
                id="disableTrackingProtection"
                class="text-link showTpEnabled"
                value="&trackingProtection.disable;"/>
         <label xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
                id="enableTrackingProtection"
                class="text-link showTpDisabled"
                value="&trackingProtection.enable;"/>
         <p id="tpStartTour"
-           class="showTpEnabled"><a id="startTour">&trackingProtection.startTour;</a></p>
+           class="showTpEnabled"><a id="startTour">&trackingProtection.startTour1;</a></p>
       </div>
     </div>
   </body>
 </html>
--- a/browser/components/privatebrowsing/test/browser/browser.ini
+++ b/browser/components/privatebrowsing/test/browser/browser.ini
@@ -12,16 +12,17 @@ support-files =
   browser_privatebrowsing_protocolhandler_page.html
   browser_privatebrowsing_windowtitle_page.html
   head.js
   popup.html
   title.sjs
 
 [browser_privatebrowsing_DownloadLastDirWithCPS.js]
 [browser_privatebrowsing_about.js]
+tags = trackingprotection
 [browser_privatebrowsing_aboutHomeButtonAfterWindowClose.js]
 [browser_privatebrowsing_aboutSessionRestore.js]
 [browser_privatebrowsing_cache.js]
 [browser_privatebrowsing_certexceptionsui.js]
 [browser_privatebrowsing_concurrent.js]
 [browser_privatebrowsing_cookieacceptdialog.js]
 [browser_privatebrowsing_crh.js]
 [browser_privatebrowsing_downloadLastDir.js]
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
@@ -41,24 +41,22 @@ function* testLinkOpensUrl({ win, tab, e
      `Clicking ${elementId} opened ${expectedUrl} in the same tab.`);
 }
 
 /**
  * Tests the links in "about:privatebrowsing".
  */
 add_task(function* test_links() {
   // Use full version and change the remote URLs to prevent network access.
-  Services.prefs.setBoolPref("privacy.trackingprotection.ui.enabled", true);
   Services.prefs.setCharPref("app.support.baseURL", "https://example.com/");
   Services.prefs.setCharPref("privacy.trackingprotection.introURL",
                              "https://example.com/tour");
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref("privacy.trackingprotection.introURL");
     Services.prefs.clearUserPref("app.support.baseURL");
-    Services.prefs.clearUserPref("privacy.trackingprotection.ui.enabled");
   });
 
   let { win, tab } = yield openAboutPrivateBrowsing();
 
   yield testLinkOpensTab({ win, tab,
     elementId: "learnMore",
     expectedUrl: "https://example.com/private-browsing",
   });
@@ -72,22 +70,20 @@ add_task(function* test_links() {
 });
 
 /**
  * Tests the action to disable and re-enable Tracking Protection in
  * "about:privatebrowsing".
  */
 add_task(function* test_toggleTrackingProtection() {
   // Use tour version but disable Tracking Protection.
-  Services.prefs.setBoolPref("privacy.trackingprotection.ui.enabled", true);
   Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled",
                              true);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref("privacy.trackingprotection.pbmode.enabled");
-    Services.prefs.clearUserPref("privacy.trackingprotection.ui.enabled");
   });
 
   let { win, tab } = yield openAboutPrivateBrowsing();
 
   // Set up the observer for the preference change before triggering the action.
   let prefBranch =
       Services.prefs.getBranch("privacy.trackingprotection.pbmode.");
   let waitForPrefChanged = () => new Promise(resolve => {
--- a/browser/locales/en-US/chrome/browser/aboutPrivateBrowsing.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutPrivateBrowsing.dtd
@@ -7,45 +7,51 @@
 <!ENTITY privatebrowsingpage.openPrivateWindow.accesskey "P">
 
 <!ENTITY privateBrowsing.title                 "Private Browsing">
 
 <!-- LOCALIZATION NOTE (aboutPrivateBrowsing.width):
      Width of the Private Browsing section.
      -->
 <!ENTITY aboutPrivateBrowsing.width            "25em">
+
+<!-- LOCALIZATION NOTE (aboutPrivateBrowsing.subtitle,
+     aboutPrivateBrowsing.info.forgotten, aboutPrivateBrowsing.info.kept):
+     These strings will be replaced by aboutPrivateBrowsing.forgotten and
+     aboutPrivateBrowsing.kept when the new visual design lands (bug 1192625).
+     -->
 <!ENTITY aboutPrivateBrowsing.title            "You're browsing privately">
 <!ENTITY aboutPrivateBrowsing.subtitle         "In this window, &brandShortName; will not remember any history.">
 
 <!ENTITY aboutPrivateBrowsing.forgotten        "In this window, &brandShortName; will not remember:">
 <!ENTITY aboutPrivateBrowsing.info.forgotten   "Forgotten">
 <!ENTITY aboutPrivateBrowsing.info.history     "History">
-<!ENTITY aboutPrivateBrowsing.info.search      "Searches">
+<!ENTITY aboutPrivateBrowsing.info.searches    "Searches">
 <!ENTITY aboutPrivateBrowsing.info.cookies     "Cookies">
 <!ENTITY aboutPrivateBrowsing.info.temporaryFiles "Temporary Files">
 
 <!ENTITY aboutPrivateBrowsing.kept             "&brandShortName; will keep:">
 <!ENTITY aboutPrivateBrowsing.info.kept        "Kept">
 <!ENTITY aboutPrivateBrowsing.info.downloads   "Downloads">
 <!ENTITY aboutPrivateBrowsing.info.bookmarks   "Bookmarks">
 
-<!ENTITY aboutPrivateBrowsing.note             "Please note that your employer or Internet service provider can still track the pages you visit.">
+<!ENTITY aboutPrivateBrowsing.note1            "Please note that your employer or Internet service provider can still track the pages you visit.">
 <!ENTITY aboutPrivateBrowsing.learnMore        "Learn More.">
 
 <!-- LOCALIZATION NOTE (trackingProtection.width):
      Width of the Tracking Protection section. This should be enough to
      accommodate the title as well as the enabled or disabled indicator.
      -->
 <!ENTITY trackingProtection.width              "22em">
 <!ENTITY trackingProtection.title              "Tracking Protection">
 
 <!-- LOCALIZATION NOTE (trackingProtection.state.width):
      Width of the element representing the enabled or disabled indicator.
      -->
 <!ENTITY trackingProtection.state.width        "6ch">
 <!ENTITY trackingProtection.state.enabled      "ON">
 <!ENTITY trackingProtection.state.disabled     "OFF">
 
-<!ENTITY trackingProtection.description        "Private Windows now block parts of the page that may track your browsing activity.">
+<!ENTITY trackingProtection.description1       "Private Windows now block parts of the page that may track your browsing activity.">
 
 <!ENTITY trackingProtection.disable            "Turn Tracking Protection Off">
 <!ENTITY trackingProtection.enable             "Turn Tracking Protection On">
-<!ENTITY trackingProtection.startTour          "See how this works">
+<!ENTITY trackingProtection.startTour1         "See how this works">
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -82,17 +82,18 @@
   min-height: 0.1px;
   max-height: 0;
   transition: min-height 170ms ease-out, max-height 170ms ease-out, visibility 170ms linear;
 }
 
 #TabsToolbar:not([collapsed="true"]) + #nav-bar {
   border-top: 1px solid hsla(0,0%,0%,.3) !important;
   background-clip: padding-box;
-  margin-top: -1px; /* Move up into the TabsToolbar for the inner highlight at the top of the nav-bar */
+  /* Move up into the TabsToolbar for the inner highlight at the top of the nav-bar */
+  margin-top: calc(-1 * var(--navbar-tab-toolbar-highlight-overlap));
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
 #nav-bar {
   background-image: linear-gradient(@toolbarHighlight@, transparent);
   box-shadow: 0 1px 0 @toolbarHighlight@ inset;
@@ -1563,17 +1564,17 @@ richlistitem[type~="action"][actiontype=
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down[notifybgtab] {
   background-color: Highlight;
   transition: none;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: var(--tab-toolbar-navbar-overlap);
+  margin-bottom: var(--navbar-tab-toolbar-highlight-overlap);
 }
 
 #alltabs-button {
   list-style-image: url("chrome://browser/skin/tabbrowser/alltabs.png");
 }
 
 #TabsToolbar[brighttext] > #alltabs-button,
 #TabsToolbar[brighttext] > toolbarpaletteitem > #alltabs-button {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -202,29 +202,29 @@ toolbarseparator {
 
 /* Draw the bottom border of the tabs toolbar when it's not using
    -moz-appearance: toolbar. */
 #main-window:-moz-any([sizemode="fullscreen"],[customize-entered]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #main-window:not([tabsintitlebar]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-lwtheme {
   border-top: 1px solid hsla(0,0%,0%,.3);
   background-clip: padding-box;
-  margin-top: calc(-1 * var(--tab-toolbar-navbar-overlap));
+  margin-top: calc(-1 * var(--navbar-tab-toolbar-highlight-overlap));
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
 /* Always draw a border on Yosemite to ensure the border is well-defined there
  * (the default border is too light). */
 @media (-moz-mac-yosemite-theme) {
   #main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:not(:-moz-lwtheme) {
     border-top: 1px solid hsla(0,0%,0%,.2);
     background-clip: padding-box;
-    margin-top: calc(-1 * var(--tab-toolbar-navbar-overlap));
+    margin-top: calc(-1 * var(--navbar-tab-toolbar-highlight-overlap));
     /* Position the toolbar above the bottom of background tabs */
     position: relative;
     z-index: 1;
   }
 }
 
 #nav-bar-customization-target {
   padding: 4px;
@@ -2842,17 +2842,17 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 #TabsToolbar .toolbarbutton-1:not([type="menu-button"]),
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   padding: 0 1px;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: var(--tab-toolbar-navbar-overlap);
+  margin-bottom: var(--navbar-tab-toolbar-highlight-overlap);
 }
 
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   padding-left: 4px;
   padding-right: 4px;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -3,16 +3,17 @@
 % file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 /* devedition.css is loaded in browser.xul after browser.css when it is
    preffed on.  The bulk of the styling is here in the shared file, but
    there are overrides for each platform in their devedition.css files. */
 
 :root {
   --tab-toolbar-navbar-overlap: 0px;
+  --navbar-tab-toolbar-highlight-overlap: 0px;
   --space-above-tabbar: 0px;
   --toolbarbutton-text-shadow: none;
   --backbutton-urlbar-overlap: 0px;
 }
 
 :root[devtoolstheme="dark"] {
   /* Chrome */
   --chrome-background-color: #1C2126;
@@ -305,21 +306,16 @@ searchbar:not([oneoffui]) .search-go-but
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down,
 .tabbrowser-arrowscrollbox > .scrollbutton-up {
   background-color: var(--tab-background-color);
   border-color: transparent;
 }
 
-.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]),
-.tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
-  margin-bottom: 0;
-}
-
 .tabbrowser-tab {
   /* We normally rely on other tab elements for pointer events, but this
      theme hides those so we need it set here instead */
   pointer-events: auto;
 }
 
 .tabbrowser-tab[pinned][titlechanged]:not([visuallyselected="true"]) > .tab-stack > .tab-content {
   background-image: var(--pinned-tab-glow);
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -1,16 +1,17 @@
 %if 0
 /* 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/. */
 %endif
 
 :root {
   --tab-toolbar-navbar-overlap: 1px;
+  --navbar-tab-toolbar-highlight-overlap: 1px;
   --tab-min-height: 31px;
 }
 #TabsToolbar {
   --tab-separator-image: url(chrome://browser/skin/tabbrowser/tab-separator.png);
   --tab-separator-size: 3px 100%;
   --tab-separator-opacity: 1;
   --tab-stroke-background-size: auto 100%;
 }
@@ -258,17 +259,17 @@
 }
 
 /* Tab Overflow */
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]),
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
   background-image: url(chrome://browser/skin/tabbrowser/tab-overflow-indicator.png);
   background-size: 100% 100%;
   width: 14px;
-  margin-bottom: var(--tab-toolbar-navbar-overlap);
+  margin-bottom: var(--navbar-tab-toolbar-highlight-overlap);
   pointer-events: none;
   position: relative;
   z-index: 3; /* the selected tab's z-index + 1 */
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:-moz-locale-dir(rtl),
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:-moz-locale-dir(ltr) {
   transform: scaleX(-1);
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -303,17 +303,18 @@
         rgb(207, 214, 188) 0, rgb(207, 214, 188) 1px,
         rgb(224, 226, 200) 1px, rgb(224, 226, 200) 2px,
         rgb(214, 216, 190) 2px, rgb(214, 216, 190) 3px,
         transparent 3px);
   }
 }
 
 #TabsToolbar:not([collapsed="true"]) + #nav-bar {
-  margin-top: -1px; /* Move up into the TabsToolbar for the inner highlight at the top of the nav-bar */
+  /* Move up into the TabsToolbar for the inner highlight at the top of the nav-bar */
+  margin-top: calc(-1 * var(--navbar-tab-toolbar-highlight-overlap));
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
 #nav-bar {
   background-clip: padding-box;
   background-image: linear-gradient(@toolbarHighlight@, transparent);
@@ -923,17 +924,17 @@ toolbarbutton[constrain-size="true"][cui
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-appearance: none;
   border-style: none;
   padding: 0 3px;
 }
 
 #TabsToolbar .toolbarbutton-1 {
-  margin-bottom: var(--tab-toolbar-navbar-overlap);
+  margin-bottom: var(--navbar-tab-toolbar-highlight-overlap);
 }
 
 #TabsToolbar .toolbarbutton-1:not([disabled=true]):hover,
 #TabsToolbar .toolbarbutton-1[open],
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover {
   background-image: linear-gradient(transparent, rgba(255,255,255,.5)),
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -30,84 +30,69 @@ support-files =
   bipbop/bipbop8.m4s^headers^ bipbop/bipbop_audio8.m4s^headers^ bipbop/bipbop_video8.m4s^headers^
   bipbop/bipbop9.m4s^headers^ bipbop/bipbop_audio9.m4s^headers^ bipbop/bipbop_video9.m4s^headers^
   bipbop/bipbop10.m4s^headers^ bipbop/bipbop_audio10.m4s^headers^ bipbop/bipbop_video10.m4s^headers^
   bipbop/bipbop11.m4s^headers^ bipbop/bipbop_audio11.m4s^headers^ bipbop/bipbop_video11.m4s^headers^
   bipbop/bipbop12.m4s^headers^ bipbop/bipbop_video12.m4s^headers^
   bipbop/bipbop13.m4s^headers^ bipbop/bipbop_video13.m4s^headers^
 
 [test_BufferedSeek.html]
-skip-if = true # bug 1182946
 [test_BufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_BufferingWait.html]
-skip-if = true # bug 1182946
+skip-if = true # bug 1190776
 [test_BufferingWait_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_EndOfStream.html]
 skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946
 [test_EndOfStream_mp4.html]
 skip-if = (toolkit == 'android' || buildapp == 'mulet') || ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_DurationUpdated.html]
-skip-if = true # bug 1182946
 [test_DurationUpdated_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_FrameSelection.html]
-skip-if = true # bug 1182946
 [test_HaveMetadataUnbufferedSeek.html]
-skip-if = true # bug 1182946
 [test_HaveMetadataUnbufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_LoadedMetadataFired.html]
-skip-if = true # bug 1182946
 [test_LoadedMetadataFired_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_MediaSource.html]
-skip-if = true # bug 1182946
 [test_MediaSource_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_MediaSource_disabled.html]
 [test_MultipleInitSegments.html]
-skip-if = true # bug 1182946
 [test_MultipleInitSegments_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableAfterEndOfStream.html]
-skip-if = true # bug 1182946
 [test_SeekableAfterEndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableAfterEndOfStreamSplit.html]
-skip-if = true # bug 1182946
 [test_SeekableAfterEndOfStreamSplit_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableBeforeEndOfStream.html]
-skip-if = true # bug 1182946
 [test_SeekableBeforeEndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableBeforeEndOfStreamSplit.html]
-skip-if = true # bug 1182946
 [test_SeekableBeforeEndOfStreamSplit_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekNoData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekTwice_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SetModeThrows.html]
-skip-if = true # bug 1182946
 [test_SplitAppendDelay.html]
-skip-if = true # bug 1182946
 [test_SplitAppendDelay_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SplitAppend.html]
-skip-if = true # bug 1182946
 [test_SplitAppend_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_TimestampOffset_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_TruncatedDuration.html]
-skip-if = true # bug 1182946
 [test_TruncatedDuration_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_WaitingOnMissingData.html]
 skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized.
 [test_WaitingOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_WaitingToEndedTransition_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
--- a/dom/media/mediasource/test/test_BufferingWait.html
+++ b/dom/media/mediasource/test/test_BufferingWait.html
@@ -31,18 +31,18 @@ runWithMSE(function(ms, v) {
           }
         });
       });
     }
 
     fetchWithXHR("seek.webm", function(arrayBuffer) {
       sb.addEventListener('error', (e) => { ok(false, "Got Error: " + e); SimpleTest.finish(); });
       loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 0, 318))().then(
-      loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 318, 25223-318))).then(
-      loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 25223, 46712-25223))).then(
+      loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 318, 25523-318))).then(
+      loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 25523, 46712-25523))).then(
       /* Note - Missing |46712, 67833 - 46712| segment here corresponding to (0.8, 1.2] */
       /* Note - Missing |67833, 88966 - 67833| segment here corresponding to (1.2, 1.6]  */
       loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 88966))).then(function() {
         var promise = waitUntilTime(0.7);
         info("Playing video. It should play for a bit, then fire 'waiting'");
         v.play();
         return promise;
       }).then(function() {
--- a/dom/media/mediasource/test/test_HaveMetadataUnbufferedSeek_mp4.html
+++ b/dom/media/mediasource/test/test_HaveMetadataUnbufferedSeek_mp4.html
@@ -28,16 +28,19 @@ runWithMSE(function (ms, v) {
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "readyState is >= CURRENT_DATA");
       v.currentTime = target;
     });
 
     v.addEventListener("seeking", function () {
       is(v.readyState, v.HAVE_METADATA, "readyState is HAVE_METADATA");
       fetchWithXHR("bipbop/bipbop2s.mp4", function (arrayBuffer) {
         // 25819 is the offset of the first media segment's end
+        sb.addEventListener("updateend", function () {
+          ms.endOfStream();
+        });
         sb.appendBuffer(new Uint8Array(arrayBuffer, 25819));
       });
     });
 
     v.addEventListener("seeked", function () {
       SimpleTest.finish();
     });
   });
--- a/dom/media/mediasource/test/test_SeekableAfterEndOfStreamSplit.html
+++ b/dom/media/mediasource/test/test_SeekableAfterEndOfStreamSplit.html
@@ -12,22 +12,24 @@
 
 SimpleTest.waitForExplicitFinish();
 
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
-      sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 25223));
+      // 25523 is the offset of the first media segment's end
+      sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 25523));
       var updateCount = 0;
       sb.addEventListener("updateend", function () {
         updateCount++;
         if (updateCount == 1) {
-          sb.appendBuffer(new Uint8Array(arrayBuffer, 25223));
+          // 25523 is the offset of the first media segment's end
+          sb.appendBuffer(new Uint8Array(arrayBuffer, 25523));
         }
         else if (updateCount == 2) {
           ms.endOfStream();
         }
       });
     });
 
     var target = 2;
--- a/dom/media/mediasource/test/test_SeekableBeforeEndOfStreamSplit.html
+++ b/dom/media/mediasource/test/test_SeekableBeforeEndOfStreamSplit.html
@@ -12,20 +12,20 @@
 
 SimpleTest.waitForExplicitFinish();
 
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
-      sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 25223));
+      sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 25523));
       sb.addEventListener("updateend", function () {
         sb.removeEventListener('updateend', arguments.callee);
-        sb.appendBuffer(new Uint8Array(arrayBuffer, 25223));
+        sb.appendBuffer(new Uint8Array(arrayBuffer, 25523));
       });
     });
 
     var target = 2;
 
     v.addEventListener("loadedmetadata", function () {
       ok(v.seekable.length, "Resource is seekable");
       ok(v.seekable.length &&
--- a/dom/media/mediasource/test/test_SetModeThrows.html
+++ b/dom/media/mediasource/test/test_SetModeThrows.html
@@ -1,33 +1,33 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>MSE: append initialization only</title>
+  <title>MSE: append initialization</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="mediasource.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
-// This test should be removed when we implement sequence mode in bug 1116353 
+// MSE supports setting mode now. make sure it does not throw.
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     sb.mode = "segments";
     ok("true", "Setting to segments does not throw");
     try {
       sb.mode = "sequence";
-      ok(false, "Should have thrown");
-    } catch (e) { ok(/supported/.test(e), "Correctly threw not supported: " + e); }
+      ok("true", "Setting to sequence does not throw");
+    } catch (e) { ok(false, "Should not throw setting mode to sequence: " + e); }
 
     SimpleTest.finish();
   });
 });
 
 </script>
 </pre>
 </body>
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -253,21 +253,28 @@ public:
 };
 
 
 bool AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
   if (!AndroidBridge::Bridge() || (AndroidBridge::Bridge()->GetAPIVersion() < 16)) {
     return false;
   }
+
   if (aMimeType.EqualsLiteral("video/mp4") ||
       aMimeType.EqualsLiteral("video/avc")) {
     return true;
   }
-  return static_cast<bool>(mozilla::CreateDecoder(aMimeType));
+
+  MediaCodec::LocalRef ref = mozilla::CreateDecoder(aMimeType);
+  if (!ref) {
+    return false;
+  }
+  ref->Release();
+  return true;
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateVideoDecoder(
                                 const VideoInfo& aConfig,
                                 layers::LayersBackend aLayersBackend,
                                 layers::ImageContainer* aImageContainer,
                                 FlushableTaskQueue* aVideoTaskQueue,
--- a/dom/media/test/test_eme_canvas_blocked.html
+++ b/dom/media/test/test_eme_canvas_blocked.html
@@ -34,17 +34,17 @@ function startTest(test, token)
     } catch (ex) {
       threwError = true;
     }
     ok(threwError, TimeStamp(token) + " - Should throw an error when trying to draw EME video to canvas.");
     manager.finished(token);
   });
 
   LoadTestWithManagedLoadToken(test, v, manager, token,
-                               { onlyLoadFirstFragments:2, noEndOfStream:true });
+                               { onlyLoadFirstFragments:2, noEndOfStream:false });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
 if (!IsMacOSSnowLeopardOrEarlier()) {
   SimpleTest.waitForExplicitFinish();
--- a/dom/media/test/test_eme_persistent_sessions.html
+++ b/dom/media/test/test_eme_persistent_sessions.html
@@ -142,17 +142,17 @@ function startTest(test, token)
         });
 
       },
       sessionType: "persistent",
     }
   );
 
   LoadTestWithManagedLoadToken(test, v, manager, token,
-                               { onlyLoadFirstFragments:2, noEndOfStream:true });
+                               { onlyLoadFirstFragments:2, noEndOfStream:false });
 }
 
 function beginTest() {
   manager.runTests(gEMETests.filter(t => t.sessionCount === 1), startTest);
 }
 
 if (!IsMacOSSnowLeopardOrEarlier()) {
   SimpleTest.waitForExplicitFinish();
--- a/dom/media/test/test_eme_stream_capture_blocked_case1.html
+++ b/dom/media/test/test_eme_stream_capture_blocked_case1.html
@@ -28,17 +28,17 @@ function startTest(test, token)
   var v1 = SetupEME(test, case1token,  { onSetKeysFail: setKeysFailed });
   var context = new AudioContext();
   var node = context.createMediaElementSource(v1);
   v1.addEventListener("loadeddata", function(ev) {
     ok(false, TimeStamp(case1token) + " should never reach loadeddata, as setMediaKeys should fail");
   });
   manager.started(case1token);
   LoadTestWithManagedLoadToken(test, v1, manager, case1token,
-                               { onlyLoadFirstFragments:2, noEndOfStream:true });
+                               { onlyLoadFirstFragments:2, noEndOfStream:false });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
 if (!IsMacOSSnowLeopardOrEarlier()) {
   SimpleTest.waitForExplicitFinish();
--- a/dom/media/test/test_eme_stream_capture_blocked_case2.html
+++ b/dom/media/test/test_eme_stream_capture_blocked_case2.html
@@ -31,17 +31,17 @@ function startTest(test, token)
     } catch (e) {
       threw = true;
     }
     ok(threw, "Should throw an error creating a MediaElementSource on an EME video.");
     manager.finished(case2token);
   });
   manager.started(case2token);
   LoadTestWithManagedLoadToken(test, v2, manager, case2token,
-                               { onlyLoadFirstFragments:2, noEndOfStream:true });
+                               { onlyLoadFirstFragments:2, noEndOfStream:false });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
 if (!IsMacOSSnowLeopardOrEarlier()) {
   SimpleTest.waitForExplicitFinish();
--- a/dom/media/test/test_eme_stream_capture_blocked_case3.html
+++ b/dom/media/test/test_eme_stream_capture_blocked_case3.html
@@ -30,17 +30,17 @@ function startTest(test, token)
     } catch (e) {
       threw = true;
     }
     ok(threw, TimeStamp(case3token) + " Should throw an error calling mozCaptureStreamUntilEnded an EME video.");
     manager.finished(case3token);
   });
   manager.started(case3token);
   LoadTestWithManagedLoadToken(test, v3, manager, case3token,
-                               { onlyLoadFirstFragments:2, noEndOfStream:true });
+                               { onlyLoadFirstFragments:2, noEndOfStream:false });
 }
 
 function beginTest() {
   manager.runTests(gEMETests, startTest);
 }
 
 if (!IsMacOSSnowLeopardOrEarlier()) {
   SimpleTest.waitForExplicitFinish();
--- a/dom/media/webspeech/synth/SpeechSynthesis.cpp
+++ b/dom/media/webspeech/synth/SpeechSynthesis.cpp
@@ -106,27 +106,29 @@ SpeechSynthesis::Pending() const
   default:
     return true;
   }
 }
 
 bool
 SpeechSynthesis::Speaking() const
 {
-  if (mSpeechQueue.IsEmpty()) {
-    return false;
+  if (!mSpeechQueue.IsEmpty() &&
+      mSpeechQueue.ElementAt(0)->GetState() == SpeechSynthesisUtterance::STATE_SPEAKING) {
+    return true;
   }
 
-  return mSpeechQueue.ElementAt(0)->GetState() == SpeechSynthesisUtterance::STATE_SPEAKING;
+  // Returns global speaking state if global queue is enabled. Or false.
+  return nsSynthVoiceRegistry::GetInstance()->IsSpeaking();
 }
 
 bool
 SpeechSynthesis::Paused() const
 {
-  return mHoldQueue ||
+  return mHoldQueue || (mCurrentTask && mCurrentTask->IsPrePaused()) ||
          (!mSpeechQueue.IsEmpty() && mSpeechQueue.ElementAt(0)->IsPaused());
 }
 
 void
 SpeechSynthesis::Speak(SpeechSynthesisUtterance& aUtterance)
 {
   if (aUtterance.mState != SpeechSynthesisUtterance::STATE_NONE) {
     // XXX: Should probably raise an error
@@ -173,25 +175,28 @@ SpeechSynthesis::AdvanceQueue()
   }
 
   return;
 }
 
 void
 SpeechSynthesis::Cancel()
 {
-  if (mCurrentTask) {
-    if (mSpeechQueue.Length() > 1) {
-      // Remove all queued utterances except for current one.
-      mSpeechQueue.RemoveElementsAt(1, mSpeechQueue.Length() - 1);
-    }
-    mCurrentTask->Cancel();
+  if (!mSpeechQueue.IsEmpty() &&
+      mSpeechQueue.ElementAt(0)->GetState() == SpeechSynthesisUtterance::STATE_SPEAKING) {
+    // Remove all queued utterances except for current one, we will remove it
+    // in OnEnd
+    mSpeechQueue.RemoveElementsAt(1, mSpeechQueue.Length() - 1);
   } else {
     mSpeechQueue.Clear();
   }
+
+  if (mCurrentTask) {
+    mCurrentTask->Cancel();
+  }
 }
 
 void
 SpeechSynthesis::Pause()
 {
   if (Paused()) {
     return;
   }
@@ -264,10 +269,20 @@ SpeechSynthesis::GetVoices(nsTArray< nsR
   mVoiceCache.Clear();
 
   for (uint32_t i = 0; i < aResult.Length(); i++) {
     SpeechSynthesisVoice* voice = aResult[i];
     mVoiceCache.Put(voice->mUri, voice);
   }
 }
 
+// For testing purposes, allows us to cancel the current task that is
+// misbehaving, and flush the queue.
+void
+SpeechSynthesis::ForceEnd()
+{
+  if (mCurrentTask) {
+    mCurrentTask->ForceEnd();
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/SpeechSynthesis.h
+++ b/dom/media/webspeech/synth/SpeechSynthesis.h
@@ -49,16 +49,18 @@ public:
   void Pause();
 
   void Resume();
 
   void OnEnd(const nsSpeechTask* aTask);
 
   void GetVoices(nsTArray< nsRefPtr<SpeechSynthesisVoice> >& aResult);
 
+  void ForceEnd();
+
 private:
   virtual ~SpeechSynthesis();
 
   void AdvanceQueue();
 
   nsCOMPtr<nsPIDOMWindow> mParent;
 
   nsTArray<nsRefPtr<SpeechSynthesisUtterance> > mSpeechQueue;
--- a/dom/media/webspeech/synth/ipc/PSpeechSynthesis.ipdl
+++ b/dom/media/webspeech/synth/ipc/PSpeechSynthesis.ipdl
@@ -10,33 +10,38 @@ include protocol PSpeechSynthesisRequest
 namespace mozilla {
 namespace dom {
 
 struct RemoteVoice {
   nsString voiceURI;
   nsString name;
   nsString lang;
   bool localService;
+  bool queued;
 };
 
 sync protocol PSpeechSynthesis
 {
   manager PContent;
   manages PSpeechSynthesisRequest;
 
 child:
 
     VoiceAdded(RemoteVoice aVoice);
 
     VoiceRemoved(nsString aUri);
 
     SetDefaultVoice(nsString aUri, bool aIsDefault);
 
+    IsSpeakingChanged(bool aIsSpeaking);
+
 parent:
     __delete__();
 
     PSpeechSynthesisRequest(nsString aText, nsString aUri, nsString aLang,
                             float aVolume, float aRate, float aPitch);
-    sync ReadVoiceList() returns (RemoteVoice[] aVoices, nsString[] aDefaults);
+
+    sync ReadVoicesAndState() returns (RemoteVoice[] aVoices,
+                                       nsString[] aDefaults, bool aIsSpeaking);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/ipc/PSpeechSynthesisRequest.ipdl
+++ b/dom/media/webspeech/synth/ipc/PSpeechSynthesisRequest.ipdl
@@ -16,16 +16,18 @@ async protocol PSpeechSynthesisRequest
  parent:
 
   Pause();
 
   Resume();
 
   Cancel();
 
+  ForceEnd();
+
  child:
 
   __delete__(bool aIsError, float aElapsedTime, uint32_t aCharIndex);
 
   OnStart(nsString aUri);
 
   OnPause(float aElapsedTime, uint32_t aCharIndex);
 
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.cpp
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.cpp
@@ -35,16 +35,23 @@ SpeechSynthesisChild::RecvVoiceRemoved(c
 bool
 SpeechSynthesisChild::RecvSetDefaultVoice(const nsString& aUri,
                                           const bool& aIsDefault)
 {
   nsSynthVoiceRegistry::RecvSetDefaultVoice(aUri, aIsDefault);
   return true;
 }
 
+bool
+SpeechSynthesisChild::RecvIsSpeakingChanged(const bool& aIsSpeaking)
+{
+  nsSynthVoiceRegistry::RecvIsSpeakingChanged(aIsSpeaking);
+  return true;
+}
+
 PSpeechSynthesisRequestChild*
 SpeechSynthesisChild::AllocPSpeechSynthesisRequestChild(const nsString& aText,
                                                         const nsString& aLang,
                                                         const nsString& aUri,
                                                         const float& aVolume,
                                                         const float& aRate,
                                                         const float& aPitch)
 {
@@ -172,10 +179,17 @@ SpeechTaskChild::Resume()
 
 void
 SpeechTaskChild::Cancel()
 {
   MOZ_ASSERT(mActor);
   mActor->SendCancel();
 }
 
+void
+SpeechTaskChild::ForceEnd()
+{
+  MOZ_ASSERT(mActor);
+  mActor->SendForceEnd();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
@@ -23,16 +23,18 @@ class SpeechSynthesisChild : public PSpe
 
 public:
   bool RecvVoiceAdded(const RemoteVoice& aVoice) override;
 
   bool RecvVoiceRemoved(const nsString& aUri) override;
 
   bool RecvSetDefaultVoice(const nsString& aUri, const bool& aIsDefault) override;
 
+  bool RecvIsSpeakingChanged(const bool& aIsSpeaking) override;
+
 protected:
   SpeechSynthesisChild();
   virtual ~SpeechSynthesisChild();
 
   PSpeechSynthesisRequestChild* AllocPSpeechSynthesisRequestChild(const nsString& aLang,
                                                                   const nsString& aUri,
                                                                   const nsString& aText,
                                                                   const float& aVolume,
@@ -83,16 +85,18 @@ public:
   NS_IMETHOD SendAudioNative(int16_t* aData, uint32_t aDataLen) override;
 
   virtual void Pause() override;
 
   virtual void Resume() override;
 
   virtual void Cancel() override;
 
+  virtual void ForceEnd() override;
+
 private:
   SpeechSynthesisRequestChild* mActor;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.cpp
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.cpp
@@ -20,20 +20,22 @@ SpeechSynthesisParent::~SpeechSynthesisP
 
 void
 SpeechSynthesisParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Implement me! Bug 1005141
 }
 
 bool
-SpeechSynthesisParent::RecvReadVoiceList(InfallibleTArray<RemoteVoice>* aVoices,
-                                         InfallibleTArray<nsString>* aDefaults)
+SpeechSynthesisParent::RecvReadVoicesAndState(InfallibleTArray<RemoteVoice>* aVoices,
+                                              InfallibleTArray<nsString>* aDefaults,
+                                              bool* aIsSpeaking)
 {
-  nsSynthVoiceRegistry::GetInstance()->SendVoices(aVoices, aDefaults);
+  nsSynthVoiceRegistry::GetInstance()->SendVoicesAndState(aVoices, aDefaults,
+                                                          aIsSpeaking);
   return true;
 }
 
 PSpeechSynthesisRequestParent*
 SpeechSynthesisParent::AllocPSpeechSynthesisRequestParent(const nsString& aText,
                                                           const nsString& aLang,
                                                           const nsString& aUri,
                                                           const float& aVolume,
@@ -112,16 +114,24 @@ SpeechSynthesisRequestParent::RecvResume
 bool
 SpeechSynthesisRequestParent::RecvCancel()
 {
   MOZ_ASSERT(mTask);
   mTask->Cancel();
   return true;
 }
 
+bool
+SpeechSynthesisRequestParent::RecvForceEnd()
+{
+  MOZ_ASSERT(mTask);
+  mTask->ForceEnd();
+  return true;
+}
+
 // SpeechTaskParent
 
 nsresult
 SpeechTaskParent::DispatchStartImpl(const nsAString& aUri)
 {
   MOZ_ASSERT(mActor);
   if(NS_WARN_IF(!(mActor->SendOnStart(nsString(aUri))))) {
     return NS_ERROR_FAILURE;
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.h
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisParent.h
@@ -19,18 +19,19 @@ class SpeechSynthesisRequestParent;
 class SpeechSynthesisParent : public PSpeechSynthesisParent
 {
   friend class ContentParent;
   friend class SpeechSynthesisRequestParent;
 
 public:
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
-  bool RecvReadVoiceList(InfallibleTArray<RemoteVoice>* aVoices,
-                         InfallibleTArray<nsString>* aDefaults) override;
+  bool RecvReadVoicesAndState(InfallibleTArray<RemoteVoice>* aVoices,
+                              InfallibleTArray<nsString>* aDefaults,
+                              bool* aIsSpeaking) override;
 
 protected:
   SpeechSynthesisParent();
   virtual ~SpeechSynthesisParent();
   PSpeechSynthesisRequestParent* AllocPSpeechSynthesisRequestParent(const nsString& aText,
                                                                     const nsString& aLang,
                                                                     const nsString& aUri,
                                                                     const float& aVolume,
@@ -61,16 +62,18 @@ protected:
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual bool RecvPause() override;
 
   virtual bool RecvResume() override;
 
   virtual bool RecvCancel() override;
+
+  virtual bool RecvForceEnd() override;
 };
 
 class SpeechTaskParent : public nsSpeechTask
 {
   friend class SpeechSynthesisRequestParent;
 public:
   SpeechTaskParent(float aVolume, const nsAString& aUtterance)
     : nsSpeechTask(aVolume, aUtterance) {}
--- a/dom/media/webspeech/synth/nsISynthVoiceRegistry.idl
+++ b/dom/media/webspeech/synth/nsISynthVoiceRegistry.idl
@@ -2,31 +2,32 @@
 /* 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 "nsISupports.idl"
 
 interface nsISpeechService;
 
-[scriptable, builtinclass, uuid(53dcc868-4193-4c3c-a1d9-fe5a0a6af2fb)]
+[scriptable, builtinclass, uuid(dac09c3a-156e-4025-a4ab-bc88b0ea92e7)]
 interface nsISynthVoiceRegistry : nsISupports
 {
   /**
    * Register a speech synthesis voice.
    *
-   * @param aService      the service that provides this voice.
-   * @param aUri          a unique identifier for this voice.
-   * @param aName         human-readable name for this voice.
-   * @param aLang         a BCP 47 language tag.
-   * @param aLocalService true if service does not require network.
+   * @param aService          the service that provides this voice.
+   * @param aUri              a unique identifier for this voice.
+   * @param aName             human-readable name for this voice.
+   * @param aLang             a BCP 47 language tag.
+   * @param aLocalService     true if service does not require network.
+   * @param aQueuesUtterances true if voice only speaks one utterance at a time
    */
   void addVoice(in nsISpeechService aService, in DOMString aUri,
                 in DOMString aName, in DOMString aLang,
-                in boolean aLocalService);
+                in boolean aLocalService, in boolean aQueuesUtterances);
 
   /**
    * Remove a speech synthesis voice.
    *
    * @param aService the service that was used to add the voice.
    * @param aUri     a unique identifier of an existing voice.
    */
   void removeVoice(in nsISpeechService aService, in DOMString aUri);
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "AudioSegment.h"
 #include "nsSpeechTask.h"
 #include "SpeechSynthesis.h"
+#include "nsSynthVoiceRegistry.h"
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with nsSpeechTask::GetCurrentTime().
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
 #undef LOG
@@ -28,25 +29,25 @@ public:
     mSpeechTask(aSpeechTask),
     mStarted(false)
   {
   }
 
   void DoNotifyStarted()
   {
     if (mSpeechTask) {
-      mSpeechTask->DispatchStartImpl();
+      mSpeechTask->DispatchStartInner();
     }
   }
 
   void DoNotifyFinished()
   {
     if (mSpeechTask) {
-      mSpeechTask->DispatchEndImpl(mSpeechTask->GetCurrentTime(),
-                                   mSpeechTask->GetCurrentCharOffset());
+      mSpeechTask->DispatchEndInner(mSpeechTask->GetCurrentTime(),
+                                    mSpeechTask->GetCurrentCharOffset());
     }
   }
 
   virtual void NotifyEvent(MediaStreamGraph* aGraph,
                            MediaStreamListener::MediaStreamGraphEvent event) override
   {
     switch (event) {
       case EVENT_FINISHED:
@@ -91,27 +92,33 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTask)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSpeechTask)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSpeechTask)
 
 nsSpeechTask::nsSpeechTask(SpeechSynthesisUtterance* aUtterance)
   : mUtterance(aUtterance)
+  , mInited(false)
+  , mPrePaused(false)
+  , mPreCanceled(false)
   , mCallback(nullptr)
   , mIndirectAudio(false)
 {
   mText = aUtterance->mText;
   mVolume = aUtterance->Volume();
 }
 
 nsSpeechTask::nsSpeechTask(float aVolume, const nsAString& aText)
   : mUtterance(nullptr)
   , mVolume(aVolume)
   , mText(aText)
+  , mInited(false)
+  , mPrePaused(false)
+  , mPreCanceled(false)
   , mCallback(nullptr)
   , mIndirectAudio(false)
 {
 }
 
 nsSpeechTask::~nsSpeechTask()
 {
   LOG(LogLevel::Debug, ("~nsSpeechTask"));
@@ -125,20 +132,26 @@ nsSpeechTask::~nsSpeechTask()
 
   if (mPort) {
     mPort->Destroy();
     mPort = nullptr;
   }
 }
 
 void
-nsSpeechTask::BindStream(ProcessedMediaStream* aStream)
+nsSpeechTask::Init(ProcessedMediaStream* aStream)
 {
-  mStream = MediaStreamGraph::GetInstance()->CreateSourceStream(nullptr);
-  mPort = aStream->AllocateInputPort(mStream, 0);
+  if (aStream) {
+    mStream = MediaStreamGraph::GetInstance()->CreateSourceStream(nullptr);
+    mPort = aStream->AllocateInputPort(mStream, 0);
+    mIndirectAudio = false;
+  } else {
+    mIndirectAudio = true;
+  }
+  mInited = true;
 }
 
 void
 nsSpeechTask::SetChosenVoiceURI(const nsAString& aUri)
 {
   mChosenVoiceURI = aUri;
 }
 
@@ -148,23 +161,24 @@ nsSpeechTask::Setup(nsISpeechTaskCallbac
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   LOG(LogLevel::Debug, ("nsSpeechTask::Setup"));
 
   mCallback = aCallback;
 
   if (mIndirectAudio) {
+    MOZ_ASSERT(!mStream);
     if (argc > 0) {
       NS_WARNING("Audio info arguments in Setup() are ignored for indirect audio services.");
     }
     return NS_OK;
   }
 
-  // mStream is set up in BindStream() that should be called before this.
+  // mStream is set up in Init() that should be called before this.
   MOZ_ASSERT(mStream);
 
   mStream->AddListener(new SynthStreamListener(this));
 
   // XXX: Support more than one channel
   if(NS_WARN_IF(!(aChannels == 1))) {
     return NS_ERROR_FAILURE;
   }
@@ -289,16 +303,23 @@ nsSpeechTask::SendAudioImpl(nsRefPtr<moz
 NS_IMETHODIMP
 nsSpeechTask::DispatchStart()
 {
   if (!mIndirectAudio) {
     NS_WARNING("Can't call DispatchStart() from a direct audio speech service");
     return NS_ERROR_FAILURE;
   }
 
+  return DispatchStartInner();
+}
+
+nsresult
+nsSpeechTask::DispatchStartInner()
+{
+  nsSynthVoiceRegistry::GetInstance()->SetIsSpeaking(true);
   return DispatchStartImpl();
 }
 
 nsresult
 nsSpeechTask::DispatchStartImpl()
 {
   return DispatchStartImpl(mChosenVoiceURI);
 }
@@ -324,16 +345,26 @@ nsSpeechTask::DispatchStartImpl(const ns
 NS_IMETHODIMP
 nsSpeechTask::DispatchEnd(float aElapsedTime, uint32_t aCharIndex)
 {
   if (!mIndirectAudio) {
     NS_WARNING("Can't call DispatchEnd() from a direct audio speech service");
     return NS_ERROR_FAILURE;
   }
 
+  return DispatchEndInner(aElapsedTime, aCharIndex);
+}
+
+nsresult
+nsSpeechTask::DispatchEndInner(float aElapsedTime, uint32_t aCharIndex)
+{
+  if (!mPreCanceled) {
+    nsSynthVoiceRegistry::GetInstance()->SpeakNext();
+  }
+
   return DispatchEndImpl(aElapsedTime, aCharIndex);
 }
 
 nsresult
 nsSpeechTask::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchEnd\n"));
 
@@ -384,19 +415,21 @@ nsSpeechTask::DispatchPauseImpl(float aE
   if(NS_WARN_IF(mUtterance->mPaused)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   mUtterance->mPaused = true;
-  mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("pause"),
-                                           aCharIndex, aElapsedTime,
-                                           EmptyString());
+  if (mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING) {
+    mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("pause"),
+                                             aCharIndex, aElapsedTime,
+                                             EmptyString());
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSpeechTask::DispatchResume(float aElapsedTime, uint32_t aCharIndex)
 {
   if (!mIndirectAudio) {
     NS_WARNING("Can't call DispatchResume() from a direct audio speech service");
@@ -414,19 +447,22 @@ nsSpeechTask::DispatchResumeImpl(float a
   if(NS_WARN_IF(!(mUtterance->mPaused))) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   mUtterance->mPaused = false;
-  mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("resume"),
-                                           aCharIndex, aElapsedTime,
-                                           EmptyString());
+  if (mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING) {
+    mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("resume"),
+                                             aCharIndex, aElapsedTime,
+                                             EmptyString());
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSpeechTask::DispatchError(float aElapsedTime, uint32_t aCharIndex)
 {
   if (!mIndirectAudio) {
     NS_WARNING("Can't call DispatchError() from a direct audio speech service");
@@ -512,32 +548,47 @@ nsSpeechTask::Pause()
 
   if (mCallback) {
     DebugOnly<nsresult> rv = mCallback->OnPause();
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to call onPause() callback");
   }
 
   if (mStream) {
     mStream->ChangeExplicitBlockerCount(1);
+  }
+
+  if (!mInited) {
+    mPrePaused = true;
+  }
+
+  if (!mIndirectAudio) {
     DispatchPauseImpl(GetCurrentTime(), GetCurrentCharOffset());
   }
 }
 
 void
 nsSpeechTask::Resume()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   if (mCallback) {
     DebugOnly<nsresult> rv = mCallback->OnResume();
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to call onResume() callback");
   }
 
   if (mStream) {
     mStream->ChangeExplicitBlockerCount(-1);
+  }
+
+  if (mPrePaused) {
+    mPrePaused = false;
+    nsSynthVoiceRegistry::GetInstance()->ResumeQueue();
+  }
+
+  if (!mIndirectAudio) {
     DispatchResumeImpl(GetCurrentTime(), GetCurrentCharOffset());
   }
 }
 
 void
 nsSpeechTask::Cancel()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
@@ -546,20 +597,41 @@ nsSpeechTask::Cancel()
 
   if (mCallback) {
     DebugOnly<nsresult> rv = mCallback->OnCancel();
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to call onCancel() callback");
   }
 
   if (mStream) {
     mStream->ChangeExplicitBlockerCount(1);
-    DispatchEndImpl(GetCurrentTime(), GetCurrentCharOffset());
+  }
+
+  if (!mInited) {
+    mPreCanceled = true;
+  }
+
+  if (!mIndirectAudio) {
+    DispatchEndInner(GetCurrentTime(), GetCurrentCharOffset());
   }
 }
 
+void
+nsSpeechTask::ForceEnd()
+{
+  if (mStream) {
+    mStream->ChangeExplicitBlockerCount(1);
+  }
+
+  if (!mInited) {
+    mPreCanceled = true;
+  }
+
+  DispatchEndInner(GetCurrentTime(), GetCurrentCharOffset());
+}
+
 float
 nsSpeechTask::GetCurrentTime()
 {
   return mStream ? (float)(mStream->GetCurrentTime() / 1000000.0) : 0;
 }
 
 uint32_t
 nsSpeechTask::GetCurrentCharOffset()
--- a/dom/media/webspeech/synth/nsSpeechTask.h
+++ b/dom/media/webspeech/synth/nsSpeechTask.h
@@ -32,28 +32,38 @@ public:
   nsSpeechTask(float aVolume, const nsAString& aText);
 
   virtual void Pause();
 
   virtual void Resume();
 
   virtual void Cancel();
 
+  virtual void ForceEnd();
+
   float GetCurrentTime();
 
   uint32_t GetCurrentCharOffset();
 
   void SetSpeechSynthesis(SpeechSynthesis* aSpeechSynthesis);
 
-  void SetIndirectAudio(bool aIndirectAudio) { mIndirectAudio = aIndirectAudio; }
-
-  void BindStream(ProcessedMediaStream* aStream);
+  void Init(ProcessedMediaStream* aStream);
 
   void SetChosenVoiceURI(const nsAString& aUri);
 
+  bool IsPreCanceled()
+  {
+    return mPreCanceled;
+  };
+
+  bool IsPrePaused()
+  {
+    return mPrePaused;
+  }
+
 protected:
   virtual ~nsSpeechTask();
 
   nsresult DispatchStartImpl();
 
   virtual nsresult DispatchStartImpl(const nsAString& aUri);
 
   virtual nsresult DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex);
@@ -72,21 +82,31 @@ protected:
                                     float aElapsedTime, uint32_t aCharIndex);
 
   nsRefPtr<SpeechSynthesisUtterance> mUtterance;
 
   float mVolume;
 
   nsString mText;
 
+  bool mInited;
+
+  bool mPrePaused;
+
+  bool mPreCanceled;
+
 private:
   void End();
 
   void SendAudioImpl(nsRefPtr<mozilla::SharedBuffer>& aSamples, uint32_t aDataLen);
 
+  nsresult DispatchStartInner();
+
+  nsresult DispatchEndInner(float aElapsedTime, uint32_t aCharIndex);
+
   nsRefPtr<SourceMediaStream> mStream;
 
   nsRefPtr<MediaInputPort> mPort;
 
   nsCOMPtr<nsISpeechTaskCallback> mCallback;
 
   uint32_t mChannels;
 
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -12,16 +12,17 @@
 #include "SpeechSynthesisVoice.h"
 #include "nsSynthVoiceRegistry.h"
 #include "nsSpeechTask.h"
 
 #include "nsString.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 
 #include "SpeechSynthesisChild.h"
 #include "SpeechSynthesisParent.h"
 
 #undef LOG
 extern PRLogModuleInfo* GetSpeechSynthLog();
 #define LOG(type, msg) MOZ_LOG(GetSpeechSynthLog(), type, msg)
@@ -67,93 +68,140 @@ namespace dom {
 class VoiceData final
 {
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~VoiceData() {}
 
 public:
   VoiceData(nsISpeechService* aService, const nsAString& aUri,
-            const nsAString& aName, const nsAString& aLang, bool aIsLocal)
+            const nsAString& aName, const nsAString& aLang,
+            bool aIsLocal, bool aQueuesUtterances)
     : mService(aService)
     , mUri(aUri)
     , mName(aName)
     , mLang(aLang)
-    , mIsLocal(aIsLocal) {}
+    , mIsLocal(aIsLocal)
+    , mIsQueued(aQueuesUtterances) {}
 
   NS_INLINE_DECL_REFCOUNTING(VoiceData)
 
   nsCOMPtr<nsISpeechService> mService;
 
   nsString mUri;
 
   nsString mName;
 
   nsString mLang;
 
   bool mIsLocal;
+
+  bool mIsQueued;
+};
+
+// GlobalQueueItem
+
+class GlobalQueueItem final
+{
+private:
+  // Private destructor, to discourage deletion outside of Release():
+  ~GlobalQueueItem() {}
+
+public:
+  GlobalQueueItem(VoiceData* aVoice, nsSpeechTask* aTask, const nsAString& aText,
+                  const float& aVolume, const float& aRate, const float& aPitch)
+    : mVoice(aVoice)
+    , mTask(aTask)
+    , mText(aText)
+    , mVolume(aVolume)
+    , mRate(aRate)
+    , mPitch(aPitch) {}
+
+  NS_INLINE_DECL_REFCOUNTING(GlobalQueueItem)
+
+  nsRefPtr<VoiceData> mVoice;
+
+  nsRefPtr<nsSpeechTask> mTask;
+
+  nsString mText;
+
+  float mVolume;
+
+  float mRate;
+
+  float mPitch;
+
+  bool mIsLocal;
 };
 
 // nsSynthVoiceRegistry
 
 static StaticRefPtr<nsSynthVoiceRegistry> gSynthVoiceRegistry;
+static bool sForceGlobalQueue = false;
 
 NS_IMPL_ISUPPORTS(nsSynthVoiceRegistry, nsISynthVoiceRegistry)
 
 nsSynthVoiceRegistry::nsSynthVoiceRegistry()
   : mSpeechSynthChild(nullptr)
+  , mUseGlobalQueue(false)
+  , mIsSpeaking(false)
 {
   if (XRE_IsContentProcess()) {
 
     mSpeechSynthChild = new SpeechSynthesisChild();
     ContentChild::GetSingleton()->SendPSpeechSynthesisConstructor(mSpeechSynthChild);
 
     InfallibleTArray<RemoteVoice> voices;
     InfallibleTArray<nsString> defaults;
+    bool isSpeaking;
 
-    mSpeechSynthChild->SendReadVoiceList(&voices, &defaults);
+    mSpeechSynthChild->SendReadVoicesAndState(&voices, &defaults, &isSpeaking);
 
     for (uint32_t i = 0; i < voices.Length(); ++i) {
       RemoteVoice voice = voices[i];
       AddVoiceImpl(nullptr, voice.voiceURI(),
                    voice.name(), voice.lang(),
-                   voice.localService());
+                   voice.localService(), voice.queued());
     }
 
     for (uint32_t i = 0; i < defaults.Length(); ++i) {
       SetDefaultVoice(defaults[i], true);
     }
+
+    mIsSpeaking = isSpeaking;
   }
 }
 
 nsSynthVoiceRegistry::~nsSynthVoiceRegistry()
 {
   LOG(LogLevel::Debug, ("~nsSynthVoiceRegistry"));
 
   // mSpeechSynthChild's lifecycle is managed by the Content protocol.
   mSpeechSynthChild = nullptr;
 
   if (mStream) {
     if (!mStream->IsDestroyed()) {
-     mStream->Destroy();
-   }
+      mStream->Destroy();
+    }
 
-   mStream = nullptr;
+    mStream = nullptr;
   }
 
   mUriVoiceMap.Clear();
 }
 
 nsSynthVoiceRegistry*
 nsSynthVoiceRegistry::GetInstance()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gSynthVoiceRegistry) {
     gSynthVoiceRegistry = new nsSynthVoiceRegistry();
+    Preferences::AddBoolVarCache(&sForceGlobalQueue,
+                                 "media.webspeech.synth.force_global_queue");
   }
 
   return gSynthVoiceRegistry;
 }
 
 already_AddRefed<nsSynthVoiceRegistry>
 nsSynthVoiceRegistry::GetInstanceForService()
 {
@@ -161,34 +209,37 @@ nsSynthVoiceRegistry::GetInstanceForServ
 
   return registry.forget();
 }
 
 void
 nsSynthVoiceRegistry::Shutdown()
 {
   LOG(LogLevel::Debug, ("[%s] nsSynthVoiceRegistry::Shutdown()",
-                     (XRE_IsContentProcess()) ? "Content" : "Default"));
+                        (XRE_IsContentProcess()) ? "Content" : "Default"));
   gSynthVoiceRegistry = nullptr;
 }
 
 void
-nsSynthVoiceRegistry::SendVoices(InfallibleTArray<RemoteVoice>* aVoices,
-                                 InfallibleTArray<nsString>* aDefaults)
+nsSynthVoiceRegistry::SendVoicesAndState(InfallibleTArray<RemoteVoice>* aVoices,
+                                         InfallibleTArray<nsString>* aDefaults,
+                                         bool* aIsSpeaking)
 {
   for (uint32_t i=0; i < mVoices.Length(); ++i) {
     nsRefPtr<VoiceData> voice = mVoices[i];
 
     aVoices->AppendElement(RemoteVoice(voice->mUri, voice->mName, voice->mLang,
-                                       voice->mIsLocal));
+                                       voice->mIsLocal, voice->mIsQueued));
   }
 
   for (uint32_t i=0; i < mDefaultVoices.Length(); ++i) {
     aDefaults->AppendElement(mDefaultVoices[i]->mUri);
   }
+
+  *aIsSpeaking = IsSpeaking();
 }
 
 void
 nsSynthVoiceRegistry::RecvRemoveVoice(const nsAString& aUri)
 {
   // If we dont have a local instance of the registry yet, we will recieve current
   // voices at contruction time.
   if(!gSynthVoiceRegistry) {
@@ -204,50 +255,63 @@ nsSynthVoiceRegistry::RecvAddVoice(const
   // If we dont have a local instance of the registry yet, we will recieve current
   // voices at contruction time.
   if(!gSynthVoiceRegistry) {
     return;
   }
 
   gSynthVoiceRegistry->AddVoiceImpl(nullptr, aVoice.voiceURI(),
                                     aVoice.name(), aVoice.lang(),
-                                    aVoice.localService());
+                                    aVoice.localService(), aVoice.queued());
 }
 
 void
 nsSynthVoiceRegistry::RecvSetDefaultVoice(const nsAString& aUri, bool aIsDefault)
 {
   // If we dont have a local instance of the registry yet, we will recieve current
   // voices at contruction time.
   if(!gSynthVoiceRegistry) {
     return;
   }
 
   gSynthVoiceRegistry->SetDefaultVoice(aUri, aIsDefault);
 }
 
+void
+nsSynthVoiceRegistry::RecvIsSpeakingChanged(bool aIsSpeaking)
+{
+  // If we dont have a local instance of the registry yet, we will get the
+  // speaking state on construction.
+  if(!gSynthVoiceRegistry) {
+    return;
+  }
+
+  gSynthVoiceRegistry->mIsSpeaking = aIsSpeaking;
+}
+
 NS_IMETHODIMP
 nsSynthVoiceRegistry::AddVoice(nsISpeechService* aService,
                                const nsAString& aUri,
                                const nsAString& aName,
                                const nsAString& aLang,
-                               bool aLocalService)
+                               bool aLocalService,
+                               bool aQueuesUtterances)
 {
   LOG(LogLevel::Debug,
-      ("nsSynthVoiceRegistry::AddVoice uri='%s' name='%s' lang='%s' local=%s",
+      ("nsSynthVoiceRegistry::AddVoice uri='%s' name='%s' lang='%s' local=%s queued=%s",
        NS_ConvertUTF16toUTF8(aUri).get(), NS_ConvertUTF16toUTF8(aName).get(),
        NS_ConvertUTF16toUTF8(aLang).get(),
-       aLocalService ? "true" : "false"));
+       aLocalService ? "true" : "false",
+       aQueuesUtterances ? "true" : "false"));
 
   if(NS_WARN_IF(XRE_IsContentProcess())) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return AddVoiceImpl(aService, aUri, aName, aLang,
-                      aLocalService);
+  return AddVoiceImpl(aService, aUri, aName, aLang, aLocalService, aQueuesUtterances);
 }
 
 NS_IMETHODIMP
 nsSynthVoiceRegistry::RemoveVoice(nsISpeechService* aService,
                                   const nsAString& aUri)
 {
   LOG(LogLevel::Debug,
       ("nsSynthVoiceRegistry::RemoveVoice uri='%s' (%s)",
@@ -263,16 +327,32 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpe
   if(NS_WARN_IF(!(aService == retval->mService))) {
     return NS_ERROR_INVALID_ARG;
   }
 
   mVoices.RemoveElement(retval);
   mDefaultVoices.RemoveElement(retval);
   mUriVoiceMap.Remove(aUri);
 
+  if (retval->mIsQueued && !sForceGlobalQueue) {
+    // Check if this is the last queued voice, and disable the global queue if
+    // it is.
+    bool queued = false;
+    for (uint32_t i = 0; i < mVoices.Length(); i++) {
+      VoiceData* voice = mVoices[i];
+      if (voice->mIsQueued) {
+        queued = true;
+        break;
+      }
+    }
+    if (!queued) {
+      mUseGlobalQueue = false;
+    }
+  }
+
   nsTArray<SpeechSynthesisParent*> ssplist;
   GetAllSpeechSynthActors(ssplist);
 
   for (uint32_t i = 0; i < ssplist.Length(); ++i)
     unused << ssplist[i]->SendVoiceRemoved(nsString(aUri));
 
   return NS_OK;
 }
@@ -390,38 +470,41 @@ nsSynthVoiceRegistry::GetVoiceName(const
   return NS_OK;
 }
 
 nsresult
 nsSynthVoiceRegistry::AddVoiceImpl(nsISpeechService* aService,
                                    const nsAString& aUri,
                                    const nsAString& aName,
                                    const nsAString& aLang,
-                                   bool aLocalService)
+                                   bool aLocalService,
+                                   bool aQueuesUtterances)
 {
   bool found = false;
   mUriVoiceMap.GetWeak(aUri, &found);
   if(NS_WARN_IF(found)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsRefPtr<VoiceData> voice = new VoiceData(aService, aUri, aName, aLang,
-                                            aLocalService);
+                                            aLocalService, aQueuesUtterances);
 
   mVoices.AppendElement(voice);
   mUriVoiceMap.Put(aUri, voice);
+  mUseGlobalQueue |= aQueuesUtterances;
 
   nsTArray<SpeechSynthesisParent*> ssplist;
   GetAllSpeechSynthActors(ssplist);
 
   if (!ssplist.IsEmpty()) {
     mozilla::dom::RemoteVoice ssvoice(nsString(aUri),
                                       nsString(aName),
                                       nsString(aLang),
-                                      aLocalService);
+                                      aLocalService,
+                                      aQueuesUtterances);
 
     for (uint32_t i = 0; i < ssplist.Length(); ++i) {
       unused << ssplist[i]->SendVoiceAdded(ssvoice);
     }
   }
 
   return NS_OK;
 }
@@ -569,45 +652,139 @@ void
 nsSynthVoiceRegistry::Speak(const nsAString& aText,
                             const nsAString& aLang,
                             const nsAString& aUri,
                             const float& aVolume,
                             const float& aRate,
                             const float& aPitch,
                             nsSpeechTask* aTask)
 {
-  LOG(LogLevel::Debug,
-      ("nsSynthVoiceRegistry::Speak text='%s' lang='%s' uri='%s' rate=%f pitch=%f",
-       NS_ConvertUTF16toUTF8(aText).get(), NS_ConvertUTF16toUTF8(aLang).get(),
-       NS_ConvertUTF16toUTF8(aUri).get(), aRate, aPitch));
+  MOZ_ASSERT(XRE_IsParentProcess());
 
   VoiceData* voice = FindBestMatch(aUri, aLang);
 
   if (!voice) {
     NS_WARNING("No voices found.");
     aTask->DispatchError(0, 0);
     return;
   }
 
   aTask->SetChosenVoiceURI(voice->mUri);
 
-  LOG(LogLevel::Debug, ("nsSynthVoiceRegistry::Speak - Using voice URI: %s",
-                     NS_ConvertUTF16toUTF8(voice->mUri).get()));
+  if (mUseGlobalQueue || sForceGlobalQueue) {
+    LOG(LogLevel::Debug,
+        ("nsSynthVoiceRegistry::Speak queueing text='%s' lang='%s' uri='%s' rate=%f pitch=%f",
+         NS_ConvertUTF16toUTF8(aText).get(), NS_ConvertUTF16toUTF8(aLang).get(),
+         NS_ConvertUTF16toUTF8(aUri).get(), aRate, aPitch));
+    nsRefPtr<GlobalQueueItem> item = new GlobalQueueItem(voice, aTask, aText,
+                                                         aVolume, aRate, aPitch);
+    mGlobalQueue.AppendElement(item);
+
+    if (mGlobalQueue.Length() == 1) {
+      SpeakImpl(item->mVoice, item->mTask, item->mText, item->mVolume, item->mRate,
+                item->mPitch);
+    }
+  } else {
+    SpeakImpl(voice, aTask, aText, aVolume, aRate, aPitch);
+  }
+}
+
+void
+nsSynthVoiceRegistry::SpeakNext()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  LOG(LogLevel::Debug,
+      ("nsSynthVoiceRegistry::SpeakNext %d", mGlobalQueue.IsEmpty()));
+
+  SetIsSpeaking(false);
+
+  if (mGlobalQueue.IsEmpty()) {
+    return;
+  }
+
+  mGlobalQueue.RemoveElementAt(0);
+
+  while (!mGlobalQueue.IsEmpty()) {
+    nsRefPtr<GlobalQueueItem> item = mGlobalQueue.ElementAt(0);
+    if (item->mTask->IsPreCanceled()) {
+      mGlobalQueue.RemoveElementAt(0);
+      continue;
+    }
+    if (!item->mTask->IsPrePaused()) {
+      SpeakImpl(item->mVoice, item->mTask, item->mText, item->mVolume,
+                item->mRate, item->mPitch);
+    }
+    break;
+  }
+}
+
+void
+nsSynthVoiceRegistry::ResumeQueue()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  LOG(LogLevel::Debug,
+      ("nsSynthVoiceRegistry::ResumeQueue %d", mGlobalQueue.IsEmpty()));
+
+  if (mGlobalQueue.IsEmpty()) {
+    return;
+  }
+
+  nsRefPtr<GlobalQueueItem> item = mGlobalQueue.ElementAt(0);
+  if (!item->mTask->IsPrePaused()) {
+    SpeakImpl(item->mVoice, item->mTask, item->mText, item->mVolume,
+              item->mRate, item->mPitch);
+  }
+}
+
+bool
+nsSynthVoiceRegistry::IsSpeaking()
+{
+  return mIsSpeaking;
+}
+
+void
+nsSynthVoiceRegistry::SetIsSpeaking(bool aIsSpeaking)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  // Only set to 'true' if global queue is enabled.
+  mIsSpeaking = aIsSpeaking && (mUseGlobalQueue || sForceGlobalQueue);
+
+  nsTArray<SpeechSynthesisParent*> ssplist;
+  GetAllSpeechSynthActors(ssplist);
+  for (uint32_t i = 0; i < ssplist.Length(); ++i) {
+    unused << ssplist[i]->SendIsSpeakingChanged(aIsSpeaking);
+  }
+}
+
+void
+nsSynthVoiceRegistry::SpeakImpl(VoiceData* aVoice,
+                                nsSpeechTask* aTask,
+                                const nsAString& aText,
+                                const float& aVolume,
+                                const float& aRate,
+                                const float& aPitch)
+{
+  LOG(LogLevel::Debug,
+      ("nsSynthVoiceRegistry::SpeakImpl queueing text='%s' uri='%s' rate=%f pitch=%f",
+       NS_ConvertUTF16toUTF8(aText).get(), NS_ConvertUTF16toUTF8(aVoice->mUri).get(),
+       aRate, aPitch));
 
   SpeechServiceType serviceType;
 
-  DebugOnly<nsresult> rv = voice->mService->GetServiceType(&serviceType);
+  DebugOnly<nsresult> rv = aVoice->mService->GetServiceType(&serviceType);
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to get speech service type");
 
   if (serviceType == nsISpeechService::SERVICETYPE_INDIRECT_AUDIO) {
-    aTask->SetIndirectAudio(true);
+    aTask->Init(nullptr);
   } else {
     if (!mStream) {
       mStream = MediaStreamGraph::GetInstance()->CreateTrackUnionStream(nullptr);
     }
-    aTask->BindStream(mStream);
+    aTask->Init(mStream);
   }
 
-  voice->mService->Speak(aText, voice->mUri, aVolume, aRate, aPitch, aTask);
+  aVoice->mService->Speak(aText, aVoice->mUri, aVolume, aRate, aPitch, aTask);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.h
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.h
@@ -18,67 +18,93 @@ class nsISpeechService;
 namespace mozilla {
 namespace dom {
 
 class RemoteVoice;
 class SpeechSynthesisUtterance;
 class SpeechSynthesisChild;
 class nsSpeechTask;
 class VoiceData;
+class GlobalQueueItem;
 
 class nsSynthVoiceRegistry final : public nsISynthVoiceRegistry
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISYNTHVOICEREGISTRY
 
   nsSynthVoiceRegistry();
 
   already_AddRefed<nsSpeechTask> SpeakUtterance(SpeechSynthesisUtterance& aUtterance,
                                                 const nsAString& aDocLang);
 
   void Speak(const nsAString& aText, const nsAString& aLang,
              const nsAString& aUri, const float& aVolume,  const float& aRate,
              const float& aPitch, nsSpeechTask* aTask);
 
-  void SendVoices(InfallibleTArray<RemoteVoice>* aVoices,
-                  InfallibleTArray<nsString>* aDefaults);
+  void SendVoicesAndState(InfallibleTArray<RemoteVoice>* aVoices,
+                          InfallibleTArray<nsString>* aDefaults,
+                          bool* aIsSpeaking);
+
+  void SpeakNext();
+
+  void ResumeQueue();
+
+  bool IsSpeaking();
+
+  void SetIsSpeaking(bool aIsSpeaking);
 
   static nsSynthVoiceRegistry* GetInstance();
 
   static already_AddRefed<nsSynthVoiceRegistry> GetInstanceForService();
 
   static void RecvRemoveVoice(const nsAString& aUri);
 
   static void RecvAddVoice(const RemoteVoice& aVoice);
 
   static void RecvSetDefaultVoice(const nsAString& aUri, bool aIsDefault);
 
+  static void RecvIsSpeakingChanged(bool aIsSpeaking);
+
   static void Shutdown();
 
 private:
   virtual ~nsSynthVoiceRegistry();
 
   VoiceData* FindBestMatch(const nsAString& aUri, const nsAString& lang);
 
   bool FindVoiceByLang(const nsAString& aLang, VoiceData** aRetval);
 
   nsresult AddVoiceImpl(nsISpeechService* aService,
                         const nsAString& aUri,
                         const nsAString& aName,
                         const nsAString& aLang,
-                        bool aLocalService);
+                        bool aLocalService,
+                        bool aQueuesUtterances);
 
-  nsTArray<nsRefPtr<VoiceData> > mVoices;
+  void SpeakImpl(VoiceData* aVoice,
+                 nsSpeechTask* aTask,
+                 const nsAString& aText,
+                 const float& aVolume,
+                 const float& aRate,
+                 const float& aPitch);
 
-  nsTArray<nsRefPtr<VoiceData> > mDefaultVoices;
+  nsTArray<nsRefPtr<VoiceData>> mVoices;
+
+  nsTArray<nsRefPtr<VoiceData>> mDefaultVoices;
 
   nsRefPtrHashtable<nsStringHashKey, VoiceData> mUriVoiceMap;
 
   SpeechSynthesisChild* mSpeechSynthChild;
 
   nsRefPtr<ProcessedMediaStream> mStream;
+
+  bool mUseGlobalQueue;
+
+  nsTArray<nsRefPtr<GlobalQueueItem>> mGlobalQueue;
+
+  bool mIsSpeaking;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webspeech/synth/pico/nsPicoService.cpp
+++ b/dom/media/webspeech/synth/pico/nsPicoService.cpp
@@ -514,19 +514,21 @@ PicoAddVoiceTraverser(const nsAString& a
   }
 
   VoiceTraverserData* data = static_cast<VoiceTraverserData*>(aUserArg);
 
   nsAutoString name;
   name.AssignLiteral("Pico ");
   name.Append(aVoice->mLanguage);
 
+  // This service is multi-threaded and can handle more than one utterance at a
+  // time before previous utterances end. So, aQueuesUtterances == false
   DebugOnly<nsresult> rv =
     data->mRegistry->AddVoice(
-      data->mService, aUri, name, aVoice->mLanguage, true);
+      data->mService, aUri, name, aVoice->mLanguage, true, false);
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to add voice");
 
   return PL_DHASH_NEXT;
 }
 
 void
 nsPicoService::Init()
 {
--- a/dom/media/webspeech/synth/test/common.js
+++ b/dom/media/webspeech/synth/test/common.js
@@ -1,18 +1,19 @@
 function synthTestQueue(aTestArgs, aEndFunc) {
   var utterances = [];
   for (var i in aTestArgs) {
     var uargs = aTestArgs[i][0];
-    var u = new SpeechSynthesisUtterance(uargs.text);
+    var win = uargs.win || window;
+    var u = new win.SpeechSynthesisUtterance(uargs.text);
 
-    delete uargs.text;
-
-    for (var attr in uargs)
-      u[attr] = uargs[attr];
+    if (uargs.args) {
+      for (var attr in uargs.args)
+        u[attr] = uargs.args[attr];
+    }
 
     function onend_handler(e) {
       is(e.target, utterances.shift(), "Target matches utterances");
       ok(!speechSynthesis.speaking, "speechSynthesis is not speaking.");
 
       isnot(e.eventType, 'error', "Error in utterance");
 
       if (utterances.length) {
@@ -38,14 +39,32 @@ function synthTestQueue(aTestArgs, aEndF
     u.addEventListener('error', onend_handler);
 
     u.addEventListener(
       'error', function onerror_handler(e) {
         ok(false, "Error in speech utterance '" + e.target.text + "'");
       });
 
     utterances.push(u);
-    speechSynthesis.speak(u);
+    win.speechSynthesis.speak(u);
   }
 
   ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet.");
   ok(speechSynthesis.pending, "speechSynthesis has an utterance queued.");
 }
+
+function loadFrame(frameId) {
+  return new Promise(function(resolve, reject) {
+    var frame = document.getElementById(frameId);
+    frame.addEventListener('load', function (e) {
+      frame.contentWindow.document.title = frameId;
+      resolve(frame);
+    });
+    frame1.src = 'data:text/html,' + encodeURI('<html><head></head><body></body></html>');
+  });
+}
+
+function testSynthState(win, expectedState) {
+  for (var attr in expectedState) {
+    is(win.speechSynthesis[attr], expectedState[attr],
+      win.document.title + ": '" + attr + '" does not match');
+  }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/webspeech/synth/test/file_global_queue.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1188099
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1188099: Global queue should correctly schedule utterances</title>
+  <script type="application/javascript">
+    window.SimpleTest = parent.SimpleTest;
+    window.info = parent.info;
+    window.is = parent.is;
+    window.isnot = parent.isnot;
+    window.ok = parent.ok;
+    window.todo = parent.todo;
+  </script>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a>
+<iframe id="frame1"></iframe>
+<iframe id="frame2"></iframe>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+  Promise.all([loadFrame('frame1'), loadFrame('frame2')]).then(function ([frame1, frame2]) {
+    var win1 = frame1.contentWindow;
+    var win2 = frame2.contentWindow;
+    var utterance1 = new win1.SpeechSynthesisUtterance("hello, losers");
+    var utterance2 = new win1.SpeechSynthesisUtterance("hello, losers three");
+    var utterance3 = new win2.SpeechSynthesisUtterance("hello, losers too");
+    var eventOrder = ['start1', 'end1', 'start3', 'end3', 'start2', 'end2'];
+    utterance1.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start1', 'start1');
+      testSynthState(win1, { speaking: true, pending: true });
+      testSynthState(win2, { speaking: true, pending: true });
+    });
+    utterance1.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end1', 'end1');
+    });
+    utterance3.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start3', 'start3');
+      testSynthState(win1, { speaking: true, pending: true });
+      testSynthState(win2, { speaking: true, pending: false });
+    });
+    utterance3.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end3', 'end3');
+    });
+    utterance2.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start2', 'start2');
+      testSynthState(win1, { speaking: true, pending: false });
+      testSynthState(win2, { speaking: true, pending: false });
+    });
+    utterance2.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end2', 'end2');
+      testSynthState(win1, { speaking: false, pending: false });
+      testSynthState(win2, { speaking: false, pending: false });
+      SimpleTest.finish();
+    });
+    win1.speechSynthesis.speak(utterance1);
+    win1.speechSynthesis.speak(utterance2);
+    win2.speechSynthesis.speak(utterance3);
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/webspeech/synth/test/file_global_queue_cancel.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1188099
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1188099: Calling cancel() should work correctly with global queue</title>
+  <script type="application/javascript">
+    window.SimpleTest = parent.SimpleTest;
+    window.info = parent.info;
+    window.is = parent.is;
+    window.isnot = parent.isnot;
+    window.ok = parent.ok;
+    window.todo = parent.todo;
+  </script>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a>
+<iframe id="frame1"></iframe>
+<iframe id="frame2"></iframe>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+  Promise.all([loadFrame('frame1'), loadFrame('frame2')]).then(function ([frame1, frame2]) {
+    var win1 = frame1.contentWindow;
+    var win2 = frame2.contentWindow;
+
+    var utterance1 = new win1.SpeechSynthesisUtterance(
+      "u1: Donec ac nunc feugiat, posuere");
+    utterance1.lang = 'it-IT-noend';
+    var utterance2 = new win1.SpeechSynthesisUtterance("u2: hello, losers too");
+    utterance2.lang = 'it-IT-noend';
+    var utterance3 = new win1.SpeechSynthesisUtterance("u3: hello, losers three");
+
+    var utterance4 = new win2.SpeechSynthesisUtterance("u4: hello, losers same!");
+    utterance4.lang = 'it-IT-noend';
+    var utterance5 = new win2.SpeechSynthesisUtterance("u5: hello, losers too");
+    utterance5.lang = 'it-IT-noend';
+
+    var eventOrder = ['start1', 'end1', 'start2', 'end2'];
+    utterance1.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start1', 'start1');
+      testSynthState(win1, { speaking: true, pending: true });
+      testSynthState(win2, { speaking: true, pending: true });
+      win2.speechSynthesis.cancel();
+      SpecialPowers.wrap(win1.speechSynthesis).forceEnd();
+
+    });
+    utterance1.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end1', 'end1');
+      testSynthState(win1, { pending: true });
+      testSynthState(win2, { pending: false });
+    });
+    utterance2.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start2', 'start2');
+      testSynthState(win1, { speaking: true, pending: true });
+      testSynthState(win2, { speaking: true, pending: false });
+      win1.speechSynthesis.cancel();
+    });
+    utterance2.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end2', 'end2');
+      testSynthState(win1, { speaking: false, pending: false });
+      testSynthState(win2, { speaking: false, pending: false });
+      SimpleTest.finish();
+    });
+
+    function wrongUtterance(e) {
+      ok(false, 'This shall not be uttered: "' + e.target.text + '"');
+    }
+
+    utterance3.addEventListener('start', wrongUtterance);
+    utterance4.addEventListener('start', wrongUtterance);
+    utterance5.addEventListener('start', wrongUtterance);
+
+    win1.speechSynthesis.speak(utterance1);
+    win1.speechSynthesis.speak(utterance2);
+    win1.speechSynthesis.speak(utterance3);
+    win2.speechSynthesis.speak(utterance4);
+    win2.speechSynthesis.speak(utterance5);
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/webspeech/synth/test/file_global_queue_pause.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1188099
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1188099: Calling pause() should work correctly with global queue</title>
+  <script type="application/javascript">
+    window.SimpleTest = parent.SimpleTest;
+    window.info = parent.info;
+    window.is = parent.is;
+    window.isnot = parent.isnot;
+    window.ok = parent.ok;
+    window.todo = parent.todo;
+  </script>
+  <script type="application/javascript" src="common.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a>
+<iframe id="frame1"></iframe>
+<iframe id="frame2"></iframe>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+  Promise.all([loadFrame('frame1'), loadFrame('frame2')]).then(function ([frame1, frame2]) {
+    var win1 = frame1.contentWindow;
+    var win2 = frame2.contentWindow;
+
+    var utterance1 = new win1.SpeechSynthesisUtterance("Speak utterance 1.");
+    utterance1.lang = 'it-IT-noend';
+    var utterance2 = new win2.SpeechSynthesisUtterance("Speak utterance 2.");
+    var utterance3 = new win1.SpeechSynthesisUtterance("Speak utterance 3.");
+    var utterance4 = new win2.SpeechSynthesisUtterance("Speak utterance 4.");
+    var eventOrder = ['start1', 'pause1', 'resume1', 'end1', 'start2', 'end2',
+      'start4', 'end4', 'start3', 'end3'];
+
+    utterance1.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start1', 'start1');
+      win1.speechSynthesis.pause();
+    });
+    utterance1.addEventListener('pause', function(e) {
+      var expectedEvent = eventOrder.shift()
+      is(expectedEvent, 'pause1', 'pause1');
+      testSynthState(win1, { speaking: true, pending: false, paused: true});
+      testSynthState(win2, { speaking: true, pending: true, paused: false});
+
+      if (expectedEvent == 'pause1') {
+        win1.speechSynthesis.resume();
+      }
+    });
+    utterance1.addEventListener('resume', function(e) {
+      is(eventOrder.shift(), 'resume1', 'resume1');
+      testSynthState(win1, { speaking: true, pending: false, paused: false});
+      testSynthState(win2, { speaking: true, pending: true, paused: false});
+
+      win2.speechSynthesis.pause();
+
+      testSynthState(win1, { speaking: true, pending: false, paused: false});
+      // 1188099: currently, paused state is not gaurenteed to be immediate.
+      testSynthState(win2, { speaking: true, pending: true });
+
+      // We now make the utterance end.
+      SpecialPowers.wrap(win1.speechSynthesis).forceEnd();
+    });
+    utterance1.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end1', 'end1');
+      testSynthState(win1, { speaking: false, pending: false, paused: false});
+      testSynthState(win2, { speaking: false, pending: true, paused: true});
+
+      win2.speechSynthesis.resume();
+    });
+
+    utterance2.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start2', 'start2');
+      testSynthState(win1, { speaking: true, pending: false, paused: false});
+      testSynthState(win2, { speaking: true, pending: false, paused: false});
+    });
+    utterance2.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end2', 'end2');
+      testSynthState(win1, { speaking: false, pending: false, paused: false});
+      testSynthState(win2, { speaking: false, pending: false, paused: false});
+
+      win1.speechSynthesis.pause();
+
+      testSynthState(win1, { speaking: false, pending: false, paused: true});
+      testSynthState(win2, { speaking: false, pending: false, paused: false});
+
+      win1.speechSynthesis.speak(utterance3);
+      win2.speechSynthesis.speak(utterance4);
+
+      testSynthState(win1, { speaking: false, pending: true, paused: true});
+      testSynthState(win2, { speaking: false, pending: true, paused: false});
+    });
+
+    utterance4.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start4', 'start4');
+      testSynthState(win1, { speaking: true, pending: true, paused: true});
+      testSynthState(win2, { speaking: true, pending: false, paused: false});
+
+      win1.speechSynthesis.resume();
+    });
+    utterance4.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end4', 'end4');
+      testSynthState(win1, { speaking: false, pending: true, paused: false});
+      testSynthState(win2, { speaking: false, pending: false, paused: false});
+    });
+
+    utterance3.addEventListener('start', function(e) {
+      is(eventOrder.shift(), 'start3', 'start3');
+      testSynthState(win1, { speaking: true, pending: false, paused: false});
+      testSynthState(win2, { speaking: true, pending: false, paused: false});
+    });
+
+    utterance3.addEventListener('end', function(e) {
+      is(eventOrder.shift(), 'end3', 'end3');
+      testSynthState(win1, { speaking: false, pending: false, paused: false});
+      testSynthState(win2, { speaking: false, pending: false, paused: false});
+
+      SimpleTest.finish();
+    });
+
+    win1.speechSynthesis.speak(utterance1);
+    win2.speechSynthesis.speak(utterance2);
+  });
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/webspeech/synth/test/file_indirect_service_events.html
+++ b/dom/media/webspeech/synth/test/file_indirect_service_events.html
@@ -21,73 +21,82 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 1155034 **/
 
-function test_with_events() {
-  info('test_with_events');
-  var utterance = new SpeechSynthesisUtterance("never end, callback events");
-  utterance.lang = 'it-IT-noend';
+function testFunc(done_cb) {
+  function test_with_events() {
+    info('test_with_events');
+    var utterance = new SpeechSynthesisUtterance("never end, callback events");
+    utterance.lang = 'it-IT-noend';
+
+    utterance.addEventListener('start', function(e) {
+      info('start test_with_events');
+      speechSynthesis.pause();
+    // Wait to see if we get some bad events we didn't expect.
+    });
 
-  utterance.addEventListener('start', function(e) {
-    speechSynthesis.pause();
-  // Wait to see if we get some bad events we didn't expect.
-  });
+    utterance.addEventListener('pause', function(e) {
+      is(e.charIndex, 1, 'pause event charIndex matches service arguments');
+      is(e.elapsedTime, 1.5, 'pause event elapsedTime matches service arguments');
+      speechSynthesis.resume();
+    });
+
+    utterance.addEventListener('resume', function(e) {
+      is(e.charIndex, 1, 'resume event charIndex matches service arguments');
+      is(e.elapsedTime, 1.5, 'resume event elapsedTime matches service arguments');
+      speechSynthesis.cancel();
+    });
+
+    utterance.addEventListener('end', function(e) {
+      ok(e.charIndex, 1, 'resume event charIndex matches service arguments');
+      ok(e.elapsedTime, 1.5, 'end event elapsedTime matches service arguments');
+      test_no_events();
+    });
 
-  utterance.addEventListener('pause', function(e) {
-    is(e.charIndex, 1, 'pause event charIndex matches service arguments');
-    is(e.elapsedTime, 1.5, 'pause event elapsedTime matches service arguments');
-    speechSynthesis.resume();
-  });
+    info('start speak');
+    speechSynthesis.speak(utterance);
+  }
+
+  function forbiddenEvent(e) {
+    ok(false, 'no "' + e.type + '" event was explicitly dispatched from the service')
+  }
 
-  utterance.addEventListener('resume', function(e) {
-    is(e.charIndex, 1, 'resume event charIndex matches service arguments');
-    is(e.elapsedTime, 1.5, 'resume event elapsedTime matches service arguments');
-    speechSynthesis.cancel();
-  });
+  function test_no_events() {
+    info('test_no_events');
+    var utterance = new SpeechSynthesisUtterance("never end");
+    utterance.lang = "it-IT-noevents-noend";
+    utterance.addEventListener('start', function(e) {
+      speechSynthesis.pause();
+      // Wait to see if we get some bad events we didn't expect.
+      setTimeout(function() {
+        ok(true, 'didn\'t get any unwanted events');
+        utterance.removeEventListener('end', forbiddenEvent);
+        SpecialPowers.wrap(speechSynthesis).forceEnd();
+        done_cb();
+      }, 1000);
+    });
 
-  utterance.addEventListener('end', function(e) {
-    ok(e.charIndex, 1, 'resume event charIndex matches service arguments');
-    ok(e.elapsedTime, 1.5, 'end event elapsedTime matches service arguments');
-    test_no_events();
-  });
+    utterance.addEventListener('pause', forbiddenEvent);
+    utterance.addEventListener('end', forbiddenEvent);
 
-  speechSynthesis.speak(utterance);
+    speechSynthesis.speak(utterance);
+  }
+
+  test_with_events();
 }
 
-function test_no_events() {
-  var utterance = new SpeechSynthesisUtterance("never end");
-  utterance.lang = "it-IT-noevents-noend";
-  utterance.addEventListener('start', function(e) {
-    speechSynthesis.pause();
-    // Wait to see if we get some bad events we didn't expect.
-    setTimeout(function() {
-      SimpleTest.finish();
-    }, 1000);
-  });
-
-  utterance.addEventListener('pause', function(e) {
-    ok(false, 'no pause event was explicitly dispatched from the service')
-    speechSynthesis.resume();
-  });
-
-  utterance.addEventListener('resume', function(e) {
-    ok(false, 'no resume event was explicitly dispatched from the service')
-    speechSynthesis.cancel();
-  });
-
-  utterance.addEventListener('end', function(e) {
-    ok(false, 'no end event was explicitly dispatched from the service')
-  });
-
-  speechSynthesis.speak(utterance);
-}
-
-test_with_events();
+// Run test with no global queue, and then run it with a global queue.
+testFunc(function() {
+  SpecialPowers.pushPrefEnv(
+    { set: [['media.webspeech.synth.force_global_queue', true]] }, function() {
+      testFunc(SimpleTest.finish)
+    });
+});
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/file_speech_cancel.html
+++ b/dom/media/webspeech/synth/test/file_speech_cancel.html
@@ -21,69 +21,80 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 1150315 **/
 
-var gotEndEvent = false;
-// A long utterance that we will interrupt.
-var utterance = new SpeechSynthesisUtterance("Donec ac nunc feugiat, posuere " +
-  "mauris id, pharetra velit. Donec fermentum orci nunc, sit amet maximus" +
-  "dui tincidunt ut. Sed ultricies ac nisi a laoreet. Proin interdum," +
-  "libero maximus hendrerit posuere, lorem risus egestas nisl, a" +
-  "ultricies massa justo eu nisi. Duis mattis nibh a ligula tincidunt" +
-  "tincidunt non eu erat. Sed bibendum varius vulputate. Cras leo magna," +
-  "ornare ac posuere vel, luctus id metus. Mauris nec quam ac augue" +
-  "consectetur bibendum. Integer a commodo tortor. Duis semper dolor eu" +
-  "facilisis facilisis. Etiam venenatis turpis est, quis tincidunt velit" +
-  "suscipit a. Cras semper orci in sapien rhoncus bibendum. Suspendisse" +
-  "eu ex lobortis, finibus enim in, condimentum quam. Maecenas eget dui" +
-  "ipsum. Aliquam tortor leo, interdum eget congue ut, tempor id elit.");
-utterance.addEventListener('start', function(e) {
-  ok(true, 'start utterance 1');
-  speechSynthesis.cancel();
-  speechSynthesis.speak(utterance2);
-});
+function testFunc(done_cb) {
+  var gotEndEvent = false;
+  // A long utterance that we will interrupt.
+  var utterance = new SpeechSynthesisUtterance("Donec ac nunc feugiat, posuere " +
+    "mauris id, pharetra velit. Donec fermentum orci nunc, sit amet maximus" +
+    "dui tincidunt ut. Sed ultricies ac nisi a laoreet. Proin interdum," +
+    "libero maximus hendrerit posuere, lorem risus egestas nisl, a" +
+    "ultricies massa justo eu nisi. Duis mattis nibh a ligula tincidunt" +
+    "tincidunt non eu erat. Sed bibendum varius vulputate. Cras leo magna," +
+    "ornare ac posuere vel, luctus id metus. Mauris nec quam ac augue" +
+    "consectetur bibendum. Integer a commodo tortor. Duis semper dolor eu" +
+    "facilisis facilisis. Etiam venenatis turpis est, quis tincidunt velit" +
+    "suscipit a. Cras semper orci in sapien rhoncus bibendum. Suspendisse" +
+    "eu ex lobortis, finibus enim in, condimentum quam. Maecenas eget dui" +
+    "ipsum. Aliquam tortor leo, interdum eget congue ut, tempor id elit.");
+  utterance.addEventListener('start', function(e) {
+    ok(true, 'start utterance 1');
+    speechSynthesis.cancel();
+    info('cancel!');
+    speechSynthesis.speak(utterance2);
+    info('speak??');
+  });
 
-var utterance2 = new SpeechSynthesisUtterance("Proin ornare neque vitae " +
-  "risus mattis rutrum. Suspendisse a velit ut est convallis aliquet." +
-  "Nullam ante elit, malesuada vel luctus rutrum, ultricies nec libero." +
-  "Praesent eu iaculis orci. Sed nisl diam, sodales ac purus et," +
-  "volutpat interdum tortor. Nullam aliquam porta elit et maximus. Cras" +
-  "risus lectus, elementum vel sodales vel, ultricies eget lectus." +
-  "Curabitur velit lacus, mollis vel finibus et, molestie sit amet" +
-  "sapien. Proin vitae dolor ac augue posuere efficitur ac scelerisque" +
-  "diam. Nulla sed odio elit.");
-utterance2.addEventListener('start', function() {
-  speechSynthesis.cancel();
-  speechSynthesis.speak(utterance3);
-});
-utterance2.addEventListener('end', function(e) {
-  gotEndEvent = true;
-});
+  var utterance2 = new SpeechSynthesisUtterance("Proin ornare neque vitae " +
+    "risus mattis rutrum. Suspendisse a velit ut est convallis aliquet." +
+    "Nullam ante elit, malesuada vel luctus rutrum, ultricies nec libero." +
+    "Praesent eu iaculis orci. Sed nisl diam, sodales ac purus et," +
+    "volutpat interdum tortor. Nullam aliquam porta elit et maximus. Cras" +
+    "risus lectus, elementum vel sodales vel, ultricies eget lectus." +
+    "Curabitur velit lacus, mollis vel finibus et, molestie sit amet" +
+    "sapien. Proin vitae dolor ac augue posuere efficitur ac scelerisque" +
+    "diam. Nulla sed odio elit.");
+  utterance2.addEventListener('start', function() {
+    info('start');
+    speechSynthesis.cancel();
+    speechSynthesis.speak(utterance3);
+  });
+  utterance2.addEventListener('end', function(e) {
+    gotEndEvent = true;
+  });
 
-var utterance3 = new SpeechSynthesisUtterance("Hello, world 3!");
-utterance3.addEventListener('start', function() {
-  ok(gotEndEvent, "didn't get start event for this utterance");
-});
-utterance3.addEventListener('end', function(e) {
-  SimpleTest.finish();
-});
+  var utterance3 = new SpeechSynthesisUtterance("Hello, world 3!");
+  utterance3.addEventListener('start', function() {
+    ok(gotEndEvent, "didn't get start event for this utterance");
+  });
+  utterance3.addEventListener('end', done_cb);
 
-// Speak/cancel while paused (Bug 1187105)
-speechSynthesis.pause();
-speechSynthesis.speak(new SpeechSynthesisUtterance("hello."));
-ok(speechSynthesis.pending, "paused speechSynthesis has an utterance queued.");
-speechSynthesis.cancel();
-ok(!speechSynthesis.pending, "paused speechSynthesis has no utterance queued.");
-speechSynthesis.resume();
+  // Speak/cancel while paused (Bug 1187105)
+  speechSynthesis.pause();
+  speechSynthesis.speak(new SpeechSynthesisUtterance("hello."));
+  ok(speechSynthesis.pending, "paused speechSynthesis has an utterance queued.");
+  speechSynthesis.cancel();
+  ok(!speechSynthesis.pending, "paused speechSynthesis has no utterance queued.");
+  speechSynthesis.resume();
 
-speechSynthesis.speak(utterance);
-ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet.");
-ok(speechSynthesis.pending, "speechSynthesis has an utterance queued.");
+  speechSynthesis.speak(utterance);
+  ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet.");
+  ok(speechSynthesis.pending, "speechSynthesis has an utterance queued.");
+}
+
+// Run test with no global queue, and then run it with a global queue.
+testFunc(function() {
+  SpecialPowers.pushPrefEnv(
+    { set: [['media.webspeech.synth.force_global_queue', true]] }, function() {
+      testFunc(SimpleTest.finish)
+    });
+});
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/file_speech_queue.html
+++ b/dom/media/webspeech/synth/test/file_speech_queue.html
@@ -39,40 +39,47 @@ for (var voice of speechSynthesis.getVoi
 }
 
 ok(langUriMap['en-JM'], 'No English-Jamaican voice');
 ok(langUriMap['en-GB'], 'No English-British voice');
 ok(langUriMap['en-CA'], 'No English-Canadian voice');
 ok(langUriMap['fr-CA'], 'No French-Canadian voice');
 ok(langUriMap['es-MX'], 'No Spanish-Mexican voice');
 
-synthTestQueue(
-  [[{text: "Hello, world."},
-    { uri: langUriMap['en-JM'] }],
-   [{text: "Bonjour tout le monde .", lang: "fr", rate: 0.5, pitch: 0.75},
-    { uri: langUriMap['fr-CA'], rate: 0.5, pitch: 0.75}],
-   [{text: "How are you doing?", lang: "en-GB"},
-    { rate: 1, pitch: 1, uri: langUriMap['en-GB']}],
-   [{text: "¡hasta mañana!", lang: "es-MX"},
-    { uri: langUriMap['es-MX'] }]],
-  function () {
-    var test_data = [];
-    var voices = speechSynthesis.getVoices();
-    for (var voice of voices) {
-      if (voice.voiceURI.indexOf('urn:moz-tts:fake-direct') < 0) {
-        continue;
+function testFunc(done_cb) {
+  synthTestQueue(
+    [[{text: "Hello, world."},
+      { uri: langUriMap['en-JM'] }],
+     [{text: "Bonjour tout le monde .",
+       args: { lang: "fr", rate: 0.5, pitch: 0.75 }},
+      { uri: langUriMap['fr-CA'], rate: 0.5, pitch: 0.75}],
+     [{text: "How are you doing?", args: { lang: "en-GB" } },
+      { rate: 1, pitch: 1, uri: langUriMap['en-GB']}],
+     [{text: "¡hasta mañana!", args: { lang: "es-MX" } },
+      { uri: langUriMap['es-MX'] }]],
+    function () {
+      var test_data = [];
+      var voices = speechSynthesis.getVoices();
+      for (var voice of voices) {
+        if (voice.voiceURI.indexOf('urn:moz-tts:fake-direct') < 0) {
+          continue;
+        }
+        test_data.push([{text: "Hello world", args: { voice: voice} },
+                        {uri: voice.voiceURI}]);
       }
-      test_data.push([{text: "Hello world", voice: voice},
-                      {uri: voice.voiceURI}]);
-    }
+
+      synthTestQueue(test_data, done_cb);
+    });
+}
 
-    synthTestQueue(test_data,
-                   function () {
-                     SimpleTest.finish();
-                   });
-  });
-
+// Run test with no global queue, and then run it with a global queue.
+testFunc(function() {
+  SpecialPowers.pushPrefEnv(
+    { set: [['media.webspeech.synth.force_global_queue', true]] }, function() {
+      testFunc(SimpleTest.finish)
+    });
+});
 
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/mochitest.ini
+++ b/dom/media/webspeech/synth/test/mochitest.ini
@@ -2,14 +2,20 @@
 tags=msg
 support-files =
   common.js
   file_setup.html
   file_speech_queue.html
   file_speech_simple.html
   file_speech_cancel.html
   file_indirect_service_events.html
+  file_global_queue.html
+  file_global_queue_cancel.html
+  file_global_queue_pause.html
 
 [test_setup.html]
 [test_speech_queue.html]
 [test_speech_simple.html]
 [test_speech_cancel.html]
 [test_indirect_service_events.html]
+[test_global_queue.html]
+[test_global_queue_cancel.html]
+[test_global_queue_pause.html]
--- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
+++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
@@ -282,17 +282,19 @@ nsFakeSynthServices::~nsFakeSynthService
 static void
 AddVoices(nsISpeechService* aService, const VoiceDetails* aVoices, uint32_t aLength)
 {
   nsSynthVoiceRegistry* registry = nsSynthVoiceRegistry::GetInstance();
   for (uint32_t i = 0; i < aLength; i++) {
     NS_ConvertUTF8toUTF16 name(aVoices[i].name);
     NS_ConvertUTF8toUTF16 uri(aVoices[i].uri);
     NS_ConvertUTF8toUTF16 lang(aVoices[i].lang);
-    registry->AddVoice(aService, uri, name, lang, true);
+    // These services can handle more than one utterance at a time and have
+    // several speaking simultaniously. So, aQueuesUtterances == false
+    registry->AddVoice(aService, uri, name, lang, true, false);
     if (aVoices[i].defaultVoice) {
       registry->SetDefaultVoice(uri, true);
     }
   }
 }
 
 void
 nsFakeSynthServices::Init()
copy from dom/media/webspeech/synth/test/test_speech_queue.html
copy to dom/media/webspeech/synth/test/test_global_queue.html
--- a/dom/media/webspeech/synth/test/test_speech_queue.html
+++ b/dom/media/webspeech/synth/test/test_global_queue.html
@@ -1,33 +1,34 @@
 <!DOCTYPE HTML>
-<html lang="en-US">
+<html>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=525444
+https://bugzilla.mozilla.org/show_bug.cgi?id=1188099
 -->
 <head>
   <meta charset="utf-8">
-  <title>Test for Bug 525444: Web Speech API, check speech synth queue</title>
+  <title>Test for Bug 1188099: Global queue should correctly schedule utterances</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a>
 <p id="display"></p>
 <iframe id="testFrame"></iframe>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
-                          function() { document.getElementById("testFrame").src = "file_speech_queue.html"; });
+SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true],
+                                  ['media.webspeech.synth.force_global_queue', true]] },
+                          function() { document.getElementById("testFrame").src = "file_global_queue.html"; });
 
 </script>
 </pre>
 </body>
-</html>
+</html>
\ No newline at end of file
copy from dom/media/webspeech/synth/test/test_speech_queue.html
copy to dom/media/webspeech/synth/test/test_global_queue_cancel.html
--- a/dom/media/webspeech/synth/test/test_speech_queue.html
+++ b/dom/media/webspeech/synth/test/test_global_queue_cancel.html
@@ -1,33 +1,34 @@
 <!DOCTYPE HTML>
-<html lang="en-US">
+<html>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=525444
+https://bugzilla.mozilla.org/show_bug.cgi?id=1188099
 -->
 <head>
   <meta charset="utf-8">
-  <title>Test for Bug 525444: Web Speech API, check speech synth queue</title>
+  <title>Test for Bug 1188099: Calling cancel() should work correctly with global queue</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a>
 <p id="display"></p>
 <iframe id="testFrame"></iframe>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
-                          function() { document.getElementById("testFrame").src = "file_speech_queue.html"; });
+SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true],
+                                  ['media.webspeech.synth.force_global_queue', true]] },
+                          function() { document.getElementById("testFrame").src = "file_global_queue_cancel.html"; });
 
 </script>
 </pre>
 </body>
-</html>
+</html>
\ No newline at end of file
copy from dom/media/webspeech/synth/test/test_speech_queue.html
copy to dom/media/webspeech/synth/test/test_global_queue_pause.html
--- a/dom/media/webspeech/synth/test/test_speech_queue.html
+++ b/dom/media/webspeech/synth/test/test_global_queue_pause.html
@@ -1,33 +1,34 @@
 <!DOCTYPE HTML>
-<html lang="en-US">
+<html>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=525444
+https://bugzilla.mozilla.org/show_bug.cgi?id=1188099
 -->
 <head>
   <meta charset="utf-8">
-  <title>Test for Bug 525444: Web Speech API, check speech synth queue</title>
+  <title>Test for Bug 1188099: Calling pause() should work correctly with global queue</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a>
 <p id="display"></p>
 <iframe id="testFrame"></iframe>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
-                          function() { document.getElementById("testFrame").src = "file_speech_queue.html"; });
+SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true],
+                                  ['media.webspeech.synth.force_global_queue', true]] },
+                          function() { document.getElementById("testFrame").src = "file_global_queue_pause.html"; });
 
 </script>
 </pre>
 </body>
-</html>
+</html>
\ No newline at end of file
--- a/dom/media/webspeech/synth/test/test_indirect_service_events.html
+++ b/dom/media/webspeech/synth/test/test_indirect_service_events.html
@@ -19,15 +19,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 1155034 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
-                          function() { document.getElementById("testFrame").src = "file_indirect_service_events.html"; });
+SpecialPowers.pushPrefEnv(
+  { set: [['media.webspeech.synth.enabled', true],
+          ['media.webspeech.synth.force_global_queue', false]] },
+  function() { document.getElementById("testFrame").src = "file_indirect_service_events.html"; });
+
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/test_speech_cancel.html
+++ b/dom/media/webspeech/synth/test/test_speech_cancel.html
@@ -19,15 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 1150315 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
-                          function() { document.getElementById("testFrame").src = "file_speech_cancel.html"; });
+SpecialPowers.pushPrefEnv(
+  { set: [['media.webspeech.synth.enabled', true],
+          ['media.webspeech.synth.force_global_queue', false]] },
+  function() { document.getElementById("testFrame").src = "file_speech_cancel.html"; });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/test/test_speech_queue.html
+++ b/dom/media/webspeech/synth/test/test_speech_queue.html
@@ -19,15 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
-                          function() { document.getElementById("testFrame").src = "file_speech_queue.html"; });
+SpecialPowers.pushPrefEnv(
+  { set: [['media.webspeech.synth.enabled', true],
+          ['media.webspeech.synth.force_global_queue', false]] },
+  function() { document.getElementById("testFrame").src = "file_speech_queue.html"; });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webspeech/synth/windows/SapiService.cpp
+++ b/dom/media/webspeech/synth/windows/SapiService.cpp
@@ -273,18 +273,21 @@ SapiService::RegisterVoices()
     }
 
     nsAutoString uri;
     uri.AssignLiteral("urn:moz-tts:sapi:");
     uri.Append(description);
     uri.AppendLiteral("?");
     uri.Append(locale);
 
+    // This service can only speak one utterance at a time, se we set
+    // aQueuesUtterances to true in order to track global state and schedule
+    // access to this service.
     rv = registry->AddVoice(this, uri, nsDependentString(description), locale,
-                            true);
+                            true, true);
     CoTaskMemFree(description);
     if (NS_FAILED(rv)) {
       continue;
     }
 
     mVoices.Put(uri, voiceToken);
   }
 
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -1,17 +1,17 @@
 /* jshint moz: true, esnext: true */
 /* 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/. */
 
 "use strict";
 
 // Don't modify this, instead set dom.push.debug.
-let gDebuggingEnabled = true;
+let gDebuggingEnabled = false;
 
 function debug(s) {
   if (gDebuggingEnabled) {
     dump("-*- PushService.jsm: " + s + "\n");
   }
 }
 
 const Cc = Components.classes;
--- a/dom/webidl/SpeechSynthesis.webidl
+++ b/dom/webidl/SpeechSynthesis.webidl
@@ -18,9 +18,13 @@ interface SpeechSynthesis {
 
   [UnsafeInPrerendering]
   void speak(SpeechSynthesisUtterance utterance);
   void cancel();
   void pause();
   [UnsafeInPrerendering]
   void resume();
   sequence<SpeechSynthesisVoice> getVoices();
+
+  [ChromeOnly]
+  // Force an utterance to end. Circumvents bad speech service implementations.
+  void forceEnd();
 };
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -622,17 +622,19 @@ nsEditor::GetSelectionController(nsISele
   *aSel = nullptr; // init out param
   nsCOMPtr<nsISelectionController> selCon;
   if (mSelConWeak) {
     selCon = do_QueryReferent(mSelConWeak);
   } else {
     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
     selCon = do_QueryInterface(presShell);
   }
-  NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
+  if (!selCon) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
   NS_ADDREF(*aSel = selCon);
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsEditor::DeleteSelection(EDirection aAction, EStripWrappers aStripWrappers)
 {
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -50,21 +50,27 @@
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion, etc
 #ifdef MOZ_WIDGET_ANDROID
 #include <android/log.h>
 #include "AndroidBridge.h"
+#endif
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 #include "opengl/CompositorOGL.h"
 #include "GLContextEGL.h"
 #include "GLContextProvider.h"
 #include "ScopedGLHelpers.h"
 #endif
+#ifdef MOZ_WIDGET_GONK
+#include "nsScreenManagerGonk.h"
+#include "nsWindow.h"
+#endif
 #include "GeckoProfiler.h"
 #include "TextRenderer.h"               // for TextRenderer
 
 class gfxContext;
 
 namespace mozilla {
 namespace layers {
 
@@ -297,17 +303,17 @@ LayerManagerComposite::EndTransaction(co
     // The results of our drawing always go directly into a pixel buffer,
     // so we don't need to pass any global transform here.
     mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
 
     nsIntRegion opaque;
     ApplyOcclusionCulling(mRoot, opaque);
 
     Render();
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
     RenderToPresentationSurface();
 #endif
     mGeometryChanged = false;
   } else {
     // Modified layer tree
     mGeometryChanged = true;
   }
 
@@ -775,17 +781,17 @@ LayerManagerComposite::Render()
     composer2D->Render(mCompositor->GetWidget());
   }
 
   mCompositor->GetWidget()->PostRender(this);
 
   RecordFrame();
 }
 
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class ScopedCompositorProjMatrix {
 public:
   ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix):
     mCompositor(aCompositor),
     mOriginalProjMatrix(mCompositor->GetProjMatrix())
   {
     mCompositor->SetProjMatrix(aProjMatrix);
   }
@@ -849,16 +855,17 @@ public:
   }
 private:
   GLContextEGL* const mContext;
 };
 
 void
 LayerManagerComposite::RenderToPresentationSurface()
 {
+#ifdef MOZ_WIDGET_ANDROID
   if (!AndroidBridge::Bridge()) {
     return;
   }
 
   void* window = AndroidBridge::Bridge()->GetPresentationWindow();
 
   if (!window) {
     return;
@@ -881,76 +888,124 @@ LayerManagerComposite::RenderToPresentat
   GLContextEGL* egl = GLContextEGL::Cast(gl);
 
   if (!egl) {
     return;
   }
 
   const IntSize windowSize = AndroidBridge::Bridge()->GetNativeWindowSize(window);
 
+#elif defined(MOZ_WIDGET_GONK)
+  CompositorOGL* compositor = static_cast<CompositorOGL*>(mCompositor.get());
+  nsScreenGonk* screen = static_cast<nsWindow*>(mCompositor->GetWidget())->GetScreen();
+  if (!screen->IsPrimaryScreen()) {
+    // Only primary screen support mirroring
+    return;
+  }
+
+  nsWindow* mirrorScreenWidget = screen->GetMirroringWidget();
+  if (!mirrorScreenWidget) {
+    // No mirroring
+    return;
+  }
+
+  nsScreenGonk* mirrorScreen = mirrorScreenWidget->GetScreen();
+  if (!mirrorScreen->GetTopWindows().IsEmpty()) {
+    return;
+  }
+
+  EGLSurface surface = mirrorScreen->GetEGLSurface();
+  if (surface == LOCAL_EGL_NO_SURFACE) {
+    // Create GLContext
+    nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateForWindow(mirrorScreenWidget);
+    mirrorScreenWidget->SetNativeData(NS_NATIVE_OPENGL_CONTEXT,
+                                      reinterpret_cast<uintptr_t>(gl.get()));
+    surface = mirrorScreen->GetEGLSurface();
+    if (surface == LOCAL_EGL_NO_SURFACE) {
+      // Failed to create EGLSurface
+      return;
+    }
+  }
+  GLContext* gl = compositor->gl();
+  GLContextEGL* egl = GLContextEGL::Cast(gl);
+  const IntSize windowSize = mirrorScreen->GetNaturalBounds().Size();
+#endif
+
   if ((windowSize.width <= 0) || (windowSize.height <= 0)) {
     return;
   }
 
+  ScreenRotation rotation = compositor->GetScreenRotation();
+
   const int actualWidth = windowSize.width;
   const int actualHeight = windowSize.height;
 
   const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize();
+  const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height);
 
-  const int pageWidth = originalSize.width;
-  const int pageHeight = originalSize.height;
+  int pageWidth = originalSize.width;
+  int pageHeight = originalSize.height;
+  if (rotation == ROTATION_90 || rotation == ROTATION_270) {
+    pageWidth = originalSize.height;
+    pageHeight = originalSize.width;
+  }
 
   float scale = 1.0;
 
   if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) {
     const float scaleWidth = (float)actualWidth / (float)pageWidth;
     const float scaleHeight = (float)actualHeight / (float)pageHeight;
     scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight;
   }
 
   const gfx::IntSize actualSize(actualWidth, actualHeight);
   ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize);
 
   const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0);
-  ScopedCompositorRenderOffset overrideRenderOffset(compositor, offset);
   ScopedContextSurfaceOverride overrideSurface(egl, surface);
 
+  Matrix viewMatrix = ComputeTransformForRotation(originalRect,
+                                                  rotation);
+  viewMatrix.Invert(); // unrotate
+  viewMatrix.PostScale(scale, scale);
+  viewMatrix.PostTranslate(offset.x, offset.y);
+  Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);
+
+  mRoot->ComputeEffectiveTransforms(matrix);
+  nsIntRegion opaque;
+  ApplyOcclusionCulling(mRoot, opaque);
+
   nsIntRegion invalid;
   Rect bounds(0.0f, 0.0f, scale * pageWidth, (float)actualHeight);
   Rect rect, actualBounds;
 
   mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds);
 
-  // Override the projection matrix since the presentation frame buffer
-  // is probably not the same size as the device frame buffer. The override
-  // projection matrix also scales the content to fit into the presentation
-  // frame buffer.
-  Matrix viewMatrix;
-  viewMatrix.PreTranslate(-1.0, 1.0);
-  viewMatrix.PreScale((2.0f * scale) / (float)actualWidth, (2.0f * scale) / (float)actualHeight);
-  viewMatrix.PreScale(1.0f, -1.0f);
-  viewMatrix.PreTranslate((int)((float)offset.x / scale), offset.y);
-
-  Matrix4x4 projMatrix = Matrix4x4::From2D(viewMatrix);
-
-  ScopedCompositorProjMatrix overrideProjMatrix(compositor, projMatrix);
-
   // The Java side of Fennec sets a scissor rect that accounts for
   // chrome such as the URL bar. Override that so that the entire frame buffer
   // is cleared.
-  ScopedScissorRect screen(egl, 0, 0, actualWidth, actualHeight);
+  ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
   egl->fClearColor(0.0, 0.0, 0.0, 0.0);
   egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
 
-  const IntRect clipRect = IntRect(0, 0, (int)(scale * pageWidth), actualHeight);
+  const IntRect clipRect = IntRect(0, 0, actualWidth, actualHeight);
+
   RootLayer()->Prepare(RenderTargetPixel::FromUntyped(clipRect));
   RootLayer()->RenderLayer(clipRect);
 
   mCompositor->EndFrame();
-  mCompositor->SetDispAcquireFence(mRoot);
+  mCompositor->SetDispAcquireFence(mRoot); // Call after EndFrame()
+
+#ifdef MOZ_WIDGET_GONK
+  nsRefPtr<Composer2D> composer2D;
+  composer2D = mCompositor->GetWidget()->GetComposer2D();
+  if (composer2D) {
+    composer2D->Render(mirrorScreenWidget);
+  }
+#endif
 }
 #endif
 
 static void
 SubtractTransformedRegion(nsIntRegion& aRegion,
                           const nsIntRegion& aRegionToSubtract,
                           const Matrix4x4& aTransform)
 {
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -296,17 +296,17 @@ private:
                                              nsIntRegion& aScreenRegion,
                                              nsIntRegion& aLowPrecisionScreenRegion,
                                              const gfx::Matrix4x4& aTransform);
 
   /**
    * Render the current layer tree to the active target.
    */
   void Render();
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
   void RenderToPresentationSurface();
 #endif
 
   /**
    * Render debug overlays such as the FPS/FrameCounter above the frame.
    */
   void RenderDebugOverlay(const gfx::Rect& aBounds);
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -258,16 +258,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.vsync.compositor",                  VsyncAlignedCompositor, bool, false);
   // On b2g, in really bad cases, I've seen up to 80 ms delays between touch events and the main thread
   // processing them. So 80 ms / 16 = 5 vsync events. Double it up just to be on the safe side, so 10.
   DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count",  CompositorUnobserveCount, int32_t, 10);
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.vsync.hw-vsync.enabled",            HardwareVsyncEnabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.vsync.refreshdriver",               VsyncAlignedRefreshDriver, bool, false);
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
+  DECL_GFX_PREF(Once, "gfx.screen-mirroring.enabled",          ScreenMirroringEnabled, bool, false);
 
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
   DECL_GFX_PREF(Live, "gl.require-hardware",                   RequireHardwareGL, bool, false);
 
   DECL_GFX_PREF(Once, "image.cache.size",                      ImageCacheSize, int32_t, 5*1024*1024);
   DECL_GFX_PREF(Once, "image.cache.timeweight",                ImageCacheTimeWeight, int32_t, 500);
   DECL_GFX_PREF(Live, "image.decode-immediately.enabled",      ImageDecodeImmediatelyEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -94,17 +94,16 @@ using JS::CallArgs;
 using JS::CallNonGenericMethod;
 using JS::CallReceiver;
 using JS::CompileOptions;
 using JS::IsAcceptableThis;
 using JS::NativeImpl;
 using JS::OwningCompileOptions;
 using JS::ReadOnlyCompileOptions;
 using JS::SourceBufferHolder;
-using JS::TransitiveCompileOptions;
 
 using JS::Rooted;
 using JS::RootedFunction;
 using JS::RootedId;
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
 using JS::RootedSymbol;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5762,23 +5762,23 @@ BytecodeEmitter::emitFunction(ParseNode*
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             SharedContext* outersc = sc;
 
             if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
                 funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
-            // Inherit most things (principals, version, etc) from the
-            // parent.  Use default values for the rest.
+            // Inherit most things (principals, version, etc) from the parent.
             Rooted<JSScript*> parent(cx, script);
-            MOZ_ASSERT(parent->getVersion() == parser->options().version);
-            MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
-            const TransitiveCompileOptions& transitiveOptions = parser->options();
-            CompileOptions options(cx, transitiveOptions);
+            CompileOptions options(cx, parser->options());
+            options.setMutedErrors(parent->mutedErrors())
+                   .setNoScriptRval(false)
+                   .setForEval(false)
+                   .setVersion(parent->getVersion());
 
             Rooted<JSObject*> enclosingScope(cx, enclosingStaticScope());
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
                                                           parent->staticLevel() + 1,
                                                           sourceObject,
                                                           funbox->bufStart, funbox->bufEnd));
             if (!script)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3808,47 +3808,41 @@ AutoFile::open(JSContext* cx, const char
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_OPEN,
                                  filename, "No such file or directory");
             return false;
         }
     }
     return true;
 }
 
+JSObject * const JS::ReadOnlyCompileOptions::nullObjectPtr = nullptr;
+
 void
-JS::TransitiveCompileOptions::copyPODTransitiveOptions(const TransitiveCompileOptions& rhs)
-{
-    mutedErrors_ = rhs.mutedErrors_;
+JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions& rhs)
+{
     version = rhs.version;
     versionSet = rhs.versionSet;
     utf8 = rhs.utf8;
+    lineno = rhs.lineno;
+    column = rhs.column;
+    forEval = rhs.forEval;
+    noScriptRval = rhs.noScriptRval;
     selfHostingMode = rhs.selfHostingMode;
     canLazilyParse = rhs.canLazilyParse;
     strictOption = rhs.strictOption;
     extraWarningsOption = rhs.extraWarningsOption;
     werrorOption = rhs.werrorOption;
     asmJSOption = rhs.asmJSOption;
     forceAsync = rhs.forceAsync;
     installedFile = rhs.installedFile;
     sourceIsLazy = rhs.sourceIsLazy;
     introductionType = rhs.introductionType;
     introductionLineno = rhs.introductionLineno;
     introductionOffset = rhs.introductionOffset;
     hasIntroductionInfo = rhs.hasIntroductionInfo;
-};
-
-void
-JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions& rhs)
-{
-    copyPODTransitiveOptions(rhs);
-    lineno = rhs.lineno;
-    column = rhs.column;
-    isRunOnce = rhs.isRunOnce;
-    forEval = rhs.forEval;
-    noScriptRval = rhs.noScriptRval;
 }
 
 JS::OwningCompileOptions::OwningCompileOptions(JSContext* cx)
     : ReadOnlyCompileOptions(),
       runtime(GetRuntime(cx)),
       elementRoot(cx),
       elementAttributeNameRoot(cx),
       introductionScriptRoot(cx)
@@ -3863,16 +3857,17 @@ JS::OwningCompileOptions::~OwningCompile
     js_free(const_cast<char*>(introducerFilename_));
 }
 
 bool
 JS::OwningCompileOptions::copy(JSContext* cx, const ReadOnlyCompileOptions& rhs)
 {
     copyPODOptions(rhs);
 
+    setMutedErrors(rhs.mutedErrors());
     setElement(rhs.element());
     setElementAttributeName(rhs.elementAttributeName());
     setIntroductionScript(rhs.introductionScript());
 
     return setFileAndLine(cx, rhs.filename(), rhs.lineno) &&
            setSourceMapURL(cx, rhs.sourceMapURL()) &&
            setIntroducerFilename(cx, rhs.introducerFilename());
 }
@@ -4270,17 +4265,18 @@ CompileFunction(JSContext* cx, const Rea
     if (!fun)
         return false;
 
     // Make sure the static scope chain matches up when we have a
     // non-syntactic scope.
     MOZ_ASSERT_IF(!enclosingDynamicScope->is<GlobalObject>(),
                   HasNonSyntacticStaticScopeChain(enclosingStaticScope));
 
-    if (!frontend::CompileFunctionBody(cx, fun, optionsArg, formals, srcBuf, enclosingStaticScope))
+    CompileOptions options(cx, optionsArg);
+    if (!frontend::CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingStaticScope))
         return false;
 
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileFunction(JSContext* cx, AutoObjectVector& scopeChain,
                     const ReadOnlyCompileOptions& options,
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3379,48 +3379,42 @@ namespace JS {
  * compilation options where a worker thread can find them, and then return
  * immediately. The worker thread will come along at some later point, and use
  * the options.
  *
  * The compiler itself just needs to be able to access a collection of options;
  * it doesn't care who owns them, or what's keeping them alive. It does its own
  * addrefs/copies/tracing/etc.
  *
- * Furthermore, in some cases compile options are propagated from one entity to
- * another (e.g. from a scriipt to a function defined in that script).  This
- * involves copying over some, but not all, of the options.
- *
- * So, we have a class hierarchy that reflects these four use cases:
+ * So, we have a class hierarchy that reflects these three use cases:
  *
- * - TransitiveCompileOptions is the common base class, representing options
- *   that should get propagated from a script to functions defined in that
- *   script.  This is never instantiated directly.
- *
- * - ReadOnlyCompileOptions is the only subclass of TransitiveCompileOptions,
- *   representing a full set of compile options.  It can be used by code that
- *   simply needs to access options set elsewhere, like the compiler.  This,
- *   again, is never instantiated directly.
+ * - ReadOnlyCompileOptions is the common base class. It can be used by code
+ *   that simply needs to access options set elsewhere, like the compiler.
  *
  * - The usual CompileOptions class must be stack-allocated, and holds
  *   non-owning references to the filename, element, and so on. It's derived
  *   from ReadOnlyCompileOptions, so the compiler can use it.
  *
  * - OwningCompileOptions roots / copies / reference counts of all its values,
  *   and unroots / frees / releases them when it is destructed. It too is
  *   derived from ReadOnlyCompileOptions, so the compiler accepts it.
  */
 
 /*
  * The common base class for the CompileOptions hierarchy.
  *
- * Use this in code that needs to propagate compile options from one compilation
- * unit to another.
+ * Use this in code that only needs to access compilation options created
+ * elsewhere, like the compiler. Don't instantiate this class (the constructor
+ * is protected anyway); instead, create instances only of the derived classes:
+ * CompileOptions and OwningCompileOptions.
  */
-class JS_FRIEND_API(TransitiveCompileOptions)
+class JS_FRIEND_API(ReadOnlyCompileOptions)
 {
+    friend class CompileOptions;
+
   protected:
     // The Web Platform allows scripts to be loaded from arbitrary cross-origin
     // sources. This allows an attack by which a malicious website loads a
     // sensitive file (say, a bank statement) cross-origin (using the user's
     // cookies), and sniffs the generated syntax errors (via a window.onerror
     // handler) for juicy morsels of its contents.
     //
     // To counter this attack, HTML5 specifies that script errors should be
@@ -3432,24 +3426,29 @@ class JS_FRIEND_API(TransitiveCompileOpt
     const char* filename_;
     const char* introducerFilename_;
     const char16_t* sourceMapURL_;
 
     // This constructor leaves 'version' set to JSVERSION_UNKNOWN. The structure
     // is unusable until that's set to something more specific; the derived
     // classes' constructors take care of that, in ways appropriate to their
     // purpose.
-    TransitiveCompileOptions()
+    ReadOnlyCompileOptions()
       : mutedErrors_(false),
         filename_(nullptr),
         introducerFilename_(nullptr),
         sourceMapURL_(nullptr),
         version(JSVERSION_UNKNOWN),
         versionSet(false),
         utf8(false),
+        lineno(1),
+        column(0),
+        isRunOnce(false),
+        forEval(false),
+        noScriptRval(false),
         selfHostingMode(false),
         canLazilyParse(true),
         strictOption(false),
         extraWarningsOption(false),
         werrorOption(false),
         asmJSOption(false),
         forceAsync(false),
         installedFile(false),
@@ -3457,33 +3456,39 @@ class JS_FRIEND_API(TransitiveCompileOpt
         introductionType(nullptr),
         introductionLineno(0),
         introductionOffset(0),
         hasIntroductionInfo(false)
     { }
 
     // Set all POD options (those not requiring reference counts, copies,
     // rooting, or other hand-holding) to their values in |rhs|.
-    void copyPODTransitiveOptions(const TransitiveCompileOptions& rhs);
+    void copyPODOptions(const ReadOnlyCompileOptions& rhs);
 
   public:
     // Read-only accessors for non-POD options. The proper way to set these
     // depends on the derived type.
     bool mutedErrors() const { return mutedErrors_; }
     const char* filename() const { return filename_; }
     const char* introducerFilename() const { return introducerFilename_; }
     const char16_t* sourceMapURL() const { return sourceMapURL_; }
     virtual JSObject* element() const = 0;
     virtual JSString* elementAttributeName() const = 0;
     virtual JSScript* introductionScript() const = 0;
 
     // POD options.
     JSVersion version;
     bool versionSet;
     bool utf8;
+    unsigned lineno;
+    unsigned column;
+    // isRunOnce only applies to non-function scripts.
+    bool isRunOnce;
+    bool forEval;
+    bool noScriptRval;
     bool selfHostingMode;
     bool canLazilyParse;
     bool strictOption;
     bool extraWarningsOption;
     bool werrorOption;
     bool asmJSOption;
     bool forceAsync;
     bool installedFile;  // 'true' iff pre-compiling js file in packaged app
@@ -3492,65 +3497,17 @@ class JS_FRIEND_API(TransitiveCompileOpt
     // |introductionType| is a statically allocated C string:
     // one of "eval", "Function", or "GeneratorFunction".
     const char* introductionType;
     unsigned introductionLineno;
     uint32_t introductionOffset;
     bool hasIntroductionInfo;
 
   private:
-    void operator=(const TransitiveCompileOptions&) = delete;
-};
-
-/*
- * The class representing a full set of compile options.
- *
- * Use this in code that only needs to access compilation options created
- * elsewhere, like the compiler. Don't instantiate this class (the constructor
- * is protected anyway); instead, create instances only of the derived classes:
- * CompileOptions and OwningCompileOptions.
- */
-class JS_FRIEND_API(ReadOnlyCompileOptions) : public TransitiveCompileOptions
-{
-    friend class CompileOptions;
-
-  protected:
-    ReadOnlyCompileOptions()
-      : TransitiveCompileOptions(),
-        lineno(1),
-        column(0),
-        isRunOnce(false),
-        forEval(false),
-        noScriptRval(false)
-    { }
-
-    // Set all POD options (those not requiring reference counts, copies,
-    // rooting, or other hand-holding) to their values in |rhs|.
-    void copyPODOptions(const ReadOnlyCompileOptions& rhs);
-
-  public:
-    // Read-only accessors for non-POD options. The proper way to set these
-    // depends on the derived type.
-    bool mutedErrors() const { return mutedErrors_; }
-    const char* filename() const { return filename_; }
-    const char* introducerFilename() const { return introducerFilename_; }
-    const char16_t* sourceMapURL() const { return sourceMapURL_; }
-    virtual JSObject* element() const = 0;
-    virtual JSString* elementAttributeName() const = 0;
-    virtual JSScript* introductionScript() const = 0;
-
-    // POD options.
-    unsigned lineno;
-    unsigned column;
-    // isRunOnce only applies to non-function scripts.
-    bool isRunOnce;
-    bool forEval;
-    bool noScriptRval;
-
-  private:
+    static JSObject * const nullObjectPtr;
     void operator=(const ReadOnlyCompileOptions&) = delete;
 };
 
 /*
  * Compilation options, with dynamic lifetime. An instance of this type
  * makes a copy of / holds / roots all dynamically allocated resources
  * (principals; elements; strings) that it refers to. Its destructor frees
  * / drops / unroots them. This is heavier than CompileOptions, below, but
@@ -3655,32 +3612,18 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
   public:
     explicit CompileOptions(JSContext* cx, JSVersion version = JSVERSION_UNKNOWN);
     CompileOptions(js::ContextFriendFields* cx, const ReadOnlyCompileOptions& rhs)
       : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
         introductionScriptRoot(cx)
     {
         copyPODOptions(rhs);
 
+        mutedErrors_ = rhs.mutedErrors_;
         filename_ = rhs.filename();
-        introducerFilename_ = rhs.introducerFilename();
-        sourceMapURL_ = rhs.sourceMapURL();
-        elementRoot = rhs.element();
-        elementAttributeNameRoot = rhs.elementAttributeName();
-        introductionScriptRoot = rhs.introductionScript();
-    }
-
-    CompileOptions(js::ContextFriendFields* cx, const TransitiveCompileOptions& rhs)
-      : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
-        introductionScriptRoot(cx)
-    {
-        copyPODTransitiveOptions(rhs);
-
-        filename_ = rhs.filename();
-        introducerFilename_ = rhs.introducerFilename();
         sourceMapURL_ = rhs.sourceMapURL();
         elementRoot = rhs.element();
         elementAttributeNameRoot = rhs.elementAttributeName();
         introductionScriptRoot = rhs.introductionScript();
     }
 
     JSObject* element() const override { return elementRoot; }
     JSString* elementAttributeName() const override { return elementAttributeNameRoot; }
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -32,17 +32,16 @@ typedef AutoVectorRooter<jsid> AutoIdVec
 class CallArgs;
 
 template <typename T>
 class Rooted;
 
 class JS_FRIEND_API(CompileOptions);
 class JS_FRIEND_API(ReadOnlyCompileOptions);
 class JS_FRIEND_API(OwningCompileOptions);
-class JS_FRIEND_API(TransitiveCompileOptions);
 class JS_PUBLIC_API(CompartmentOptions);
 
 class Value;
 struct Zone;
 
 } /* namespace JS */
 
 namespace js {
--- a/layout/style/test/chrome/bug418986-2.js
+++ b/layout/style/test/chrome/bug418986-2.js
@@ -54,17 +54,19 @@ let suppressed_toggles = [
   "-moz-windows-glass",
 ];
 
 // Possible values for '-moz-os-version'
 let windows_versions = [
   "windows-xp",
   "windows-vista",
   "windows-win7",
-  "windows-win8"];
+  "windows-win8",
+  "windows-win10",
+];
 
 // Possible values for '-moz-windows-theme'
 let windows_themes = [
   "aero",
   "luna-blue",
   "luna-olive",
   "luna-silver",
   "royale",
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -208,16 +208,19 @@ class RefTest(object):
 
         # Ensure that telemetry is disabled, so we don't connect to the telemetry
         # server in the middle of the tests.
         prefs['toolkit.telemetry.enabled'] = False
         prefs['toolkit.telemetry.unified'] = False
         # Likewise for safebrowsing.
         prefs['browser.safebrowsing.enabled'] = False
         prefs['browser.safebrowsing.malware.enabled'] = False
+        # Likewise for tracking protection.
+        prefs['privacy.trackingprotection.enabled'] = False
+        prefs['privacy.trackingprotection.pbmode.enabled'] = False
         # And for snippets.
         prefs['browser.snippets.enabled'] = False
         prefs['browser.snippets.syncPromo.enabled'] = False
         prefs['browser.snippets.firstrunHomepage.enabled'] = False
         # And for useragent updates.
         prefs['general.useragent.updates.enabled'] = False
         # And for webapp updates.  Yes, it is supposed to be an integer.
         prefs['browser.webapps.checkForUpdates'] = 0
--- a/mfbt/Variant.h
+++ b/mfbt/Variant.h
@@ -112,20 +112,25 @@ struct VariantImplementation<N, T> {
 
   template<typename Variant>
   static void destroy(Variant& aV) {
     aV.template as<T>().~T();
   }
 
   template<typename Variant>
   static bool
-  equal(const Variant& aLhs, const Variant& aRhs)
-  {
+  equal(const Variant& aLhs, const Variant& aRhs) {
       return aLhs.template as<T>() == aRhs.template as<T>();
   }
+
+  template<typename Matcher, typename ConcreteVariant>
+  static typename Matcher::ReturnType
+  match(Matcher& aMatcher, ConcreteVariant& aV) {
+    return aMatcher.match(aV.template as<T>());
+  }
 };
 
 // VariantImplementation for some variant type T.
 template<size_t N, typename T, typename... Ts>
 struct VariantImplementation<N, T, Ts...>
 {
   // The next recursive VariantImplementation.
   using Next = VariantImplementation<N + 1, Ts...>;
@@ -166,16 +171,37 @@ struct VariantImplementation<N, T, Ts...
   static bool equal(const Variant& aLhs, const Variant& aRhs) {
     if (aLhs.template is<T>()) {
       MOZ_ASSERT(aRhs.template is<T>());
       return aLhs.template as<T>() == aRhs.template as<T>();
     } else {
       return Next::equal(aLhs, aRhs);
     }
   }
+
+  template<typename Matcher, typename ConcreteVariant>
+  static typename Matcher::ReturnType
+  match(Matcher& aMatcher, ConcreteVariant& aV)
+  {
+    if (aV.template is<T>()) {
+      return aMatcher.match(aV.template as<T>());
+    } else {
+      // If you're seeing compilation errors here like "no matching
+      // function for call to 'match'" then that means that the
+      // Matcher doesn't exhaust all variant types. There must exist a
+      // Matcher::match(T&) for every variant type T.
+      //
+      // If you're seeing compilation errors here like "cannot
+      // initialize return object of type <...> with an rvalue of type
+      // <...>" then that means that the Matcher::match(T&) overloads
+      // are returning different types. They must all return the same
+      // Matcher::ReturnType type.
+      return Next::match(aMatcher, aV);
+    }
+  }
 };
 
 } // namespace detail
 
 /**
  * # mozilla::Variant
  *
  * A variant / tagged union / heterogenous disjoint union / sum-type template
@@ -224,16 +250,46 @@ struct VariantImplementation<N, T, Ts...
  *     v.as<SomeRandomType>(); // <--- Compiler error!
  *
  * Additionally, you can turn a `Variant` that `is<T>` into a `T` by moving it
  * out of the containing `Variant` instance with the `extract<T>` method:
  *
  *     Variant<UniquePtr<A>, B, C> v(MakeUnique<A>());
  *     auto ptr = v.extract<UniquePtr<A>>();
  *
+ * Finally, you can exhaustively match on the contained variant and branch into
+ * different code paths depending which type is contained. This is preferred to
+ * manually checking every variant type T with is<T>() because it provides
+ * compile-time checking that you handled every type, rather than runtime
+ * assertion failures.
+ *
+ *     // Bad!
+ *     char* foo(Variant<A, B, C, D>& v) {
+ *       if (v.is<A>()) {
+ *         return ...;
+ *       } else if (v.is<B>()) {
+ *         return ...;
+ *       } else {
+ *         return doSomething(v.as<C>()); // Forgot about case D!
+ *       }
+ *     }
+ *
+ *     // Good!
+ *     struct FooMatcher
+ *     {
+ *       using ReturnType = char*;
+ *       ReturnType match(A& a) { ... }
+ *       ReturnType match(B& b) { ... }
+ *       ReturnType match(C& c) { ... }
+ *       ReturnType match(D& d) { ... } // Compile-time error to forget D!
+ *     }
+ *     char* foo(Variant<A, B, C, D>& v) {
+ *       return v.match(FooMatcher());
+ *     }
+ *
  * ## Examples
  *
  * A tree is either an empty leaf, or a node with a value and two children:
  *
  *     struct Leaf { };
  *
  *     template<typename T>
  *     struct Node
@@ -376,13 +432,29 @@ public:
    */
   template<typename T>
   T extract() {
     static_assert(detail::IsVariant<T, Ts...>::value,
                   "provided a type not found in this Variant's type list");
     MOZ_ASSERT(is<T>());
     return T(Move(as<T>()));
   }
+
+  // Exhaustive matching of all variant types no the contained value.
+
+  /** Match on an immutable const reference. */
+  template<typename Matcher>
+  typename Matcher::ReturnType
+  match(Matcher& aMatcher) const {
+    return Impl::match(aMatcher, *this);
+  }
+
+  /**  Match on a mutable non-const reference. */
+  template<typename Matcher>
+  typename Matcher::ReturnType
+  match(Matcher& aMatcher) {
+    return Impl::match(aMatcher, *this);
+  }
 };
 
 } // namespace mozilla
 
 #endif /* mozilla_Variant_h */
--- a/mfbt/tests/TestVariant.cpp
+++ b/mfbt/tests/TestVariant.cpp
@@ -118,20 +118,63 @@ testEquality()
   MOZ_RELEASE_ASSERT(v1 == v1);
   MOZ_RELEASE_ASSERT(v2 == v2);
   MOZ_RELEASE_ASSERT(v3 == v3);
   MOZ_RELEASE_ASSERT(v4 == v4);
   MOZ_RELEASE_ASSERT(v5 == v5);
   MOZ_RELEASE_ASSERT(v6 == v6);
 }
 
+struct Describer
+{
+  static const char* little;
+  static const char* medium;
+  static const char* big;
+
+  using ReturnType = const char*;
+
+  const char* match(const uint8_t&) { return little; }
+  const char* match(const uint32_t&) { return medium; }
+  const char* match(const uint64_t&) { return big; }
+};
+
+const char* Describer::little = "little";
+const char* Describer::medium = "medium";
+const char* Describer::big = "big";
+
+static void
+testMatching()
+{
+  printf("testMatching\n");
+  using V = Variant<uint8_t, uint32_t, uint64_t>;
+
+  Describer desc;
+
+  V v1(uint8_t(1));
+  V v2(uint32_t(2));
+  V v3(uint64_t(3));
+
+  MOZ_RELEASE_ASSERT(v1.match(desc) == Describer::little);
+  MOZ_RELEASE_ASSERT(v2.match(desc) == Describer::medium);
+  MOZ_RELEASE_ASSERT(v3.match(desc) == Describer::big);
+
+  const V& constRef1 = v1;
+  const V& constRef2 = v2;
+  const V& constRef3 = v3;
+
+  MOZ_RELEASE_ASSERT(constRef1.match(desc) == Describer::little);
+  MOZ_RELEASE_ASSERT(constRef2.match(desc) == Describer::medium);
+  MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::big);
+}
+
 int
 main()
 {
   testSimple();
   testCopy();
   testMove();
   testDestructor();
   testEquality();
+  testMatching();
 
   printf("TestVariant OK!\n");
   return 0;
 }
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -3964,11 +3964,14 @@ public class BrowserApp extends GeckoApp
     }
 
     @Override
     protected StartupAction getStartupAction(final String passedURL) {
         final boolean inGuestMode = GeckoProfile.get(this).inGuestMode();
         if (inGuestMode) {
             return StartupAction.GUEST;
         }
+        if (RestrictedProfiles.isRestrictedProfile(this)) {
+            return StartupAction.RESTRICTED;
+        }
         return (passedURL == null ? StartupAction.NORMAL : StartupAction.URL);
     }
 }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -132,17 +132,18 @@ public abstract class GeckoApp
     private static final String LOGTAG = "GeckoApp";
     private static final int ONE_DAY_MS = 1000*60*60*24;
 
     public static enum StartupAction {
         NORMAL,     /* normal application start */
         URL,        /* launched with a passed URL */
         PREFETCH,   /* launched with a passed URL that we prefetch */
         WEBAPP,     /* launched as a webapp runtime */
-        GUEST       /* launched in guest browsing */
+        GUEST,      /* launched in guest browsing */
+        RESTRICTED  /* launched with restricted profile */
     }
 
     public static final String ACTION_ALERT_CALLBACK       = "org.mozilla.gecko.ACTION_ALERT_CALLBACK";
     public static final String ACTION_HOMESCREEN_SHORTCUT  = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_DEBUG                = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_LAUNCH_SETTINGS      = "org.mozilla.gecko.SETTINGS";
     public static final String ACTION_LOAD                 = "org.mozilla.gecko.LOAD";
     public static final String ACTION_INIT_PW              = "org.mozilla.gecko.INIT_PW";
--- a/mobile/android/base/home/HomeConfigPrefsBackend.java
+++ b/mobile/android/base/home/HomeConfigPrefsBackend.java
@@ -69,34 +69,25 @@ class HomeConfigPrefsBackend implements 
 
     private State loadDefaultConfig() {
         final ArrayList<PanelConfig> panelConfigs = new ArrayList<PanelConfig>();
 
         panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.TOP_SITES,
                                                   EnumSet.of(PanelConfig.Flags.DEFAULT_PANEL)));
 
         panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.BOOKMARKS));
-        panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.READING_LIST));
-
-        final PanelConfig historyEntry = createBuiltinPanelConfig(mContext, PanelType.HISTORY);
-        final PanelConfig recentTabsEntry = createBuiltinPanelConfig(mContext, PanelType.RECENT_TABS);
+        panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.HISTORY));
 
-        // We disable Synced Tabs for guest mode profiles.
-        final PanelConfig remoteTabsEntry;
+        // We disable Synced Tabs for guest mode / restricted profiles.
         if (RestrictedProfiles.isAllowed(mContext, Restriction.DISALLOW_MODIFY_ACCOUNTS)) {
-            remoteTabsEntry = createBuiltinPanelConfig(mContext, PanelType.REMOTE_TABS);
-        } else {
-            remoteTabsEntry = null;
+            panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.REMOTE_TABS));
         }
 
-        panelConfigs.add(historyEntry);
-        panelConfigs.add(recentTabsEntry);
-        if (remoteTabsEntry != null) {
-            panelConfigs.add(remoteTabsEntry);
-        }
+        panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.RECENT_TABS));
+        panelConfigs.add(createBuiltinPanelConfig(mContext, PanelType.READING_LIST));
 
         return new State(panelConfigs, true);
     }
 
     /**
      * Iterate through the panels to check if they are all disabled.
      */
     private static boolean allPanelsAreDisabled(JSONArray jsonPanels) throws JSONException {
--- a/mobile/android/tests/browser/robocop/components/AboutHomeComponent.java
+++ b/mobile/android/tests/browser/robocop/components/AboutHomeComponent.java
@@ -29,20 +29,20 @@ import com.jayway.android.robotium.solo.
  * A class representing any interactions that take place on the Awesomescreen.
  */
 public class AboutHomeComponent extends BaseComponent {
     private static final String LOGTAG = AboutHomeComponent.class.getSimpleName();
 
     private static final List<PanelType> PANEL_ORDERING = Arrays.asList(
             PanelType.TOP_SITES,
             PanelType.BOOKMARKS,
-            PanelType.READING_LIST,
             PanelType.HISTORY,
+            PanelType.REMOTE_TABS,
             PanelType.RECENT_TABS,
-            PanelType.REMOTE_TABS
+            PanelType.READING_LIST
     );
 
     // The percentage of the panel to swipe between 0 and 1. This value was set through
     // testing: 0.55f was tested on try and fails on armv6 devices.
     private static final float SWIPE_PERCENTAGE = 0.70f;
 
     public AboutHomeComponent(final UITestContext testContext) {
         super(testContext);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1082,17 +1082,17 @@ pref("content.sink.pending_event_mode", 
 //   2 = openAbused
 pref("privacy.popups.disable_from_plugins", 2);
 
 // send "do not track" HTTP header, disabled by default
 pref("privacy.donottrackheader.enabled",    false);
 // Enforce tracking protection in all modes
 pref("privacy.trackingprotection.enabled",  false);
 // Enforce tracking protection in Private Browsing mode
-pref("privacy.trackingprotection.pbmode.enabled",  false);
+pref("privacy.trackingprotection.pbmode.enabled",  true);
 
 pref("dom.event.contextmenu.enabled",       true);
 pref("dom.event.clipboardevents.enabled",   true);
 #if defined(XP_WIN) && !defined(RELEASE_BUILD)
 pref("dom.event.highrestimestamp.enabled",  true);
 #else
 pref("dom.event.highrestimestamp.enabled",  false);
 #endif
--- a/testing/mozharness/mozharness/mozilla/testing/testbase.py
+++ b/testing/mozharness/mozharness/mozilla/testing/testbase.py
@@ -476,22 +476,17 @@ 2. running via buildbot and running the 
             self.dump_config(file_path=os.path.join(dirs['abs_log_dir'], 'treeconfig.json'),
                              config=self.tree_config)
 
         if (self.buildbot_config and 'properties' in self.buildbot_config and
             self.buildbot_config['properties'].get('branch') == 'try'):
             try_config_path = os.path.join(test_install_dir, 'config', 'mozharness',
                                            'try_arguments.py')
             known_try_arguments = parse_config_file(try_config_path)
-            comments = self.buildbot_config['sourcestamp']['changes'][-1]['comments']
-            if not comments and 'try_syntax' in self.buildbot_config['properties']:
-                # If we don't find try syntax in the usual place, check for it in an
-                # alternate property available to tools using self-serve.
-                comments = self.buildbot_config['properties']['try_syntax']
-            self.parse_extra_try_arguments(comments, known_try_arguments)
+            self.set_extra_try_arguments(known_try_arguments)
 
         self.tree_config.lock()
 
     def structured_output(self, suite_category):
         """Defines whether structured logging is in use in this configuration. This
         may need to be replaced with data from a different config at the resolution
         of bug 1070041 and related bugs.
         """
--- a/testing/mozharness/mozharness/mozilla/testing/try_tools.py
+++ b/testing/mozharness/mozharness/mozilla/testing/try_tools.py
@@ -4,30 +4,58 @@
 # 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/.
 # ***** END LICENSE BLOCK *****
 
 import argparse
 import os
 import re
 
+from mozharness.base.transfer import TransferMixin
 
-class TryToolsMixin(object):
+
+class TryToolsMixin(TransferMixin):
     """Utility functions for an interface between try syntax and out test harnesses.
     Requires log and script mixins."""
 
     harness_extra_args = None
     try_test_paths = []
 
-    def parse_extra_try_arguments(self, msg, known_try_arguments):
-        """Given a commit message, parse to extract additional arguments to pass
-        on to the test harness command line.
+    def _extract_try_message(self):
+        msg = self.buildbot_config['sourcestamp']['changes'][-1]['comments']
+        if len(msg) == 1024:
+            # This commit message was potentially truncated, get the full message
+            # from hg.
+            props = self.buildbot_config['properties']
+            rev = props['revision']
+            repo = props['repo_path']
+            url = 'https://hg.mozilla.org/%s/json-pushes?changeset=%s&full=1' % (repo, rev)
+
+            pushinfo = self.load_json_from_url(url)
+            for k, v in pushinfo.items():
+                if isinstance(v, dict) and 'changesets' in v:
+                    msg = v['changesets'][-1]['desc']
+
+        if not msg and 'try_syntax' in self.buildbot_config['properties']:
+            # If we don't find try syntax in the usual place, check for it in an
+            # alternate property available to tools using self-serve.
+            msg = self.buildbot_config['properties']['try_syntax']
+
+        return msg
+
+    def set_extra_try_arguments(self, known_try_arguments):
+        """Finds a commit message and parses it for extra arguments to pass to the test
+        harness command line and test paths used to filter manifests.
 
         Extracting arguments from a commit message taken directly from the try_parser.
         """
+        msg = self._extract_try_message()
+        if not msg:
+            return
+
         all_try_args = None
         for line in msg.splitlines():
             if 'try: ' in line:
                 # Autoland adds quotes to try strings that will confuse our
                 # args later on.
                 if line.startswith('"') and line.endswith('"'):
                     line = line[1:-1]
                 # Allow spaces inside of [filter expressions]
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -161,17 +161,17 @@ user_pref("layout.css.ruby.enabled", tru
 // Enable unicode-range for testing
 user_pref("layout.css.unicode-range.enabled", true);
 
 // Disable spammy layout warnings because they pollute test logs
 user_pref("layout.spammy_warnings.enabled", false);
 
 // Enable Media Source Extensions for testing
 user_pref("media.mediasource.mp4.enabled", true);
-user_pref("media.mediasource.webm.enabled", false);
+user_pref("media.mediasource.webm.enabled", true);
 
 // Enable mozContacts
 user_pref("dom.mozContacts.enabled", true);
 
 // Enable mozSettings
 user_pref("dom.mozSettings.enabled", true);
 
 // Make sure the disk cache doesn't get auto disabled
--- a/testing/talos/talos.json
+++ b/testing/talos/talos.json
@@ -1,16 +1,16 @@
 {
     "talos.zip": {
         "url": "http://talos-bundles.pvt.build.mozilla.org/zips/talos.a6052c33d420.zip",
         "path": ""
     },
     "global": {
         "talos_repo": "https://hg.mozilla.org/build/talos",
-        "talos_revision": "d44548b8feb9"
+        "talos_revision": "c7446ecc3bfb"
     },
     "extra_options": {
         "android": [ "--apkPath=%(apk_path)s" ]
     },
     "suites": {
         "chromez": {
             "tests": ["tresize", "tcanvasmark"]
         },
--- a/testing/web-platform/meta/media-source/SourceBuffer-abort-readyState.html.ini
+++ b/testing/web-platform/meta/media-source/SourceBuffer-abort-readyState.html.ini
@@ -1,11 +1,8 @@
 [SourceBuffer-abort-readyState.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [SourceBuffer#abort() (video/webm; codecs="vorbis,vp8") : If the readyState attribute of the parent media source is not in the "open" state then throw an INVALID_STATE_ERR exception and abort these steps.]
-    expected: FAIL
-
   [SourceBuffer#abort() (video/mp4) : If the readyState attribute of the parent media source is not in the "open" state then throw an INVALID_STATE_ERR exception and abort these steps.]
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/SourceBuffer-abort-removed.html.ini
+++ b/testing/web-platform/meta/media-source/SourceBuffer-abort-removed.html.ini
@@ -1,11 +1,8 @@
 [SourceBuffer-abort-removed.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [SourceBuffer#abort (video/webm; codecs="vorbis,vp8") : if this object has been removed from the sourceBuffers attribute of the parent media source, then throw an INVALID_STATE_ERR exception and abort these steps.]
-    expected: FAIL
-
   [SourceBuffer#abort (video/mp4) : if this object has been removed from the sourceBuffers attribute of the parent media source, then throw an INVALID_STATE_ERR exception and abort these steps.]
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/SourceBuffer-abort-updating.html.ini
+++ b/testing/web-platform/meta/media-source/SourceBuffer-abort-updating.html.ini
@@ -1,11 +1,8 @@
 [SourceBuffer-abort-updating.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [SourceBuffer#abort() (video/webm; codecs="vorbis,vp8") : Check the algorithm when the updating attribute is true.]
-    expected: FAIL
-
   [SourceBuffer#abort() (video/mp4) : Check the algorithm when the updating attribute is true.]
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/SourceBuffer-abort.html.ini
+++ b/testing/web-platform/meta/media-source/SourceBuffer-abort.html.ini
@@ -1,11 +1,8 @@
 [SourceBuffer-abort.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [SourceBuffer#abort() (video/webm; codecs="vorbis,vp8"): Check the values of appendWindowStart and appendWindowEnd.]
-    expected: FAIL
-
   [SourceBuffer#abort() (video/mp4): Check the values of appendWindowStart and appendWindowEnd.]
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/interfaces.html.ini
+++ b/testing/web-platform/meta/media-source/interfaces.html.ini
@@ -1,4 +1,43 @@
 [interfaces.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  expected: ERROR
+  [AudioTrack interface: attribute kind]
+    expected: FAIL
+  [AudioTrack interface: attribute language]
+    expected: FAIL
+  [AudioTrack interface: attribute sourceBuffer]
+    expected: FAIL
+  [VideoTrack interface: attribute kind]
+    expected: FAIL
+  [VideoTrack interface: attribute language]
+    expected: FAIL
+  [VideoTrack interface: attribute sourceBuffer]
+    expected: FAIL
+  [TextTrack interface: attribute kind]
+    expected: FAIL
+  [TextTrack interface: attribute language]
+    expected: FAIL
+  [TextTrack interface: attribute sourceBuffer]
+    expected: FAIL
+  [SourceBuffer interface: attribute audioTracks]
+    expected: FAIL
+  [SourceBuffer interface: attribute videoTracks]
+    expected: FAIL
+  [SourceBuffer interface: attribute textTracks]
+    expected: FAIL
+  [SourceBuffer interface: operation appendStream(Stream,unsigned long long)]
+    expected: FAIL
+  [SourceBuffer interface: sourceBuffer must inherit property "audioTracks" with the proper type (4)]
+    expected: FAIL
+  [SourceBuffer interface: sourceBuffer must inherit property "videoTracks" with the proper type (5)]
+    expected: FAIL
+  [SourceBuffer interface: sourceBuffer must inherit property "textTracks" with the proper type (6)]
+    expected: FAIL
+  [SourceBuffer interface: sourceBuffer must inherit property "appendStream" with the proper type (11)]
+    expected: FAIL
+  [SourceBuffer interface: calling appendStream(Stream,unsigned long long) on sourceBuffer with too few arguments must throw TypeError]
+    expected: FAIL
+  [VideoPlaybackQuality interface: attribute totalFrameDelay]
+    expected: FAIL
+  [VideoPlaybackQuality interface: video.getVideoPlaybackQuality() must inherit property "totalFrameDelay" with the proper type (4)]
+    expected: FAIL
--- a/testing/web-platform/meta/media-source/mediasource-addsourcebuffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-addsourcebuffer.html.ini
@@ -1,39 +1,12 @@
 [mediasource-addsourcebuffer.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test addSourceBuffer() with Vorbis and VP8]
-    expected: FAIL
-
-  [Test addSourceBuffer() with Vorbis and VP8 in separate SourceBuffers]
-    expected: FAIL
-
-  [Test addSourceBuffer() in 'ended' state.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test addSourceBuffer() video only]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test addSourceBuffer() audio only]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
   [Test addSourceBuffer() with AAC and H.264]
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
 
   [Test addSourceBuffer() with AAC and H.264 in separate SourceBuffers]
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test addSourceBuffer() QuotaExceededError.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
@@ -1,108 +1,3 @@
 [mediasource-append-buffer.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test SourceBuffer.appendBuffer() event dispatching.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test SourceBuffer.appendBuffer() call during a pending appendBuffer().]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test SourceBuffer.abort() call during a pending appendBuffer().]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test SourceBuffer.appendBuffer() triggering an 'ended' to 'open' transition.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test zero byte SourceBuffer.appendBuffer() call triggering an 'ended' to 'open' transition.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test MediaSource.removeSourceBuffer() call during a pending appendBuffer().]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test set MediaSource.duration during a pending appendBuffer() for one of its SourceBuffers.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test MediaSource.endOfStream() during a pending appendBuffer() for one of its SourceBuffers.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test set SourceBuffer.timestampOffset during a pending appendBuffer().]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appending an empty ArrayBufferView.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appending a neutered ArrayBufferView.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appending an empty ArrayBuffer.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appending a neutered ArrayBuffer.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appendBuffer with partial init segments.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appendBuffer with partial media segments.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test abort in the middle of an initialization segment.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test abort after removing sourcebuffer.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test abort after readyState is ended following init segment and media segment.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test abort after appendBuffer update ends.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appending null.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appending after removeSourceBuffer().]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-appendwindow.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-appendwindow.html.ini
@@ -1,38 +1,3 @@
 [mediasource-appendwindow.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test correctly reset appendWindowStart and appendWindowEnd values]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test set wrong values to appendWindowStart and appendWindowEnd.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test set correct values to appendWindowStart and appendWindowEnd.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appendwindow throw error when mediasource object is not associated with a sourebuffer.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test set appendWindowStart and appendWindowEnd when source buffer updating.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test appendWindowStart and appendWindowEnd value after a sourceBuffer.abort().]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test read appendWindowStart and appendWindowEnd initial values.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-buffered.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-buffered.html.ini
@@ -1,12 +1,14 @@
 [mediasource-buffered.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  disabled:
-    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1182945
-    if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1182945
   [Demuxed content with different lengths]
     expected: FAIL
 
   [Muxed content with different lengths]
     expected: FAIL
 
+  [Demuxed content with an empty buffered range on one SourceBuffer] # Bug 1192164
+    expected:
+      if os == "linux": FAIL
+      if (os == "win") and (version == "5.1.2600"): FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-closed.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-closed.html.ini
@@ -1,28 +1,3 @@
 [mediasource-closed.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test addSourceBuffer() while closed.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test removeSourceBuffer() while closed.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting duration while open->closed.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test getting duration while open->closed.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test sourcebuffer.abort when closed.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-duration-boundaryconditions.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-duration-boundaryconditions.html.ini
@@ -1,68 +1,3 @@
 [mediasource-duration-boundaryconditions.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Set duration to 2^31 - 1]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to 1]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to Number.MAX_VALUE]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to Number.MIN_VALUE]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to Number.MAX_VALUE - 1]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to Number.MIN_VALUE - 1]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to Number.POSITIVE_INFINITY]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to Number.NEGATIVE_INFINITY]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to lowest value.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to a negative double.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to a positive double.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to zero]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Set duration to NaN]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-getvideoplaybackquality.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-getvideoplaybackquality.html.ini
@@ -1,8 +1,4 @@
 [mediasource-getvideoplaybackquality.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test HTMLVideoElement.getVideoPlaybackQuality() with MediaSource API]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
 
--- a/testing/web-platform/meta/media-source/mediasource-is-type-supported.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-is-type-supported.html.ini
@@ -1,27 +1,33 @@
 [mediasource-is-type-supported.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test valid WebM type "video/webm;codecs="vp8""]
+  [Test invalid MIME format "video/webm"] # Bug 1191833
     expected: FAIL
 
-  [Test valid WebM type "video/webm;codecs="vorbis""]
+  [Test invalid MIME format "video/webm;"] # Bug 1191833
+    expected: FAIL
+
+  [Test invalid MIME format "video/webm;codecs"] # Bug 1191833
     expected: FAIL
 
-  [Test valid WebM type "video/webm;codecs="vp8,vorbis""]
+  [Test invalid MIME format "video/webm;codecs="] # Bug 1191833
+    expected: FAIL
+
+  [Test invalid MIME format "video/webm;codecs=""] # Bug 1191833
     expected: FAIL
 
-  [Test valid WebM type "video/webm;codecs="vorbis, vp8""]
+  [Test invalid MIME format "video/webm;codecs="""] # Bug 1191833
     expected: FAIL
 
-  [Test valid WebM type "audio/webm;codecs="vorbis""]
+  [Test valid WebM type "AUDIO/WEBM;CODECS="vorbis""] # Bug 1191833
     expected: FAIL
 
-  [Test valid WebM type "AUDIO/WEBM;CODECS="vorbis""]
+  [Test invalid mismatch between major type and codec ID "audio/webm;codecs="vp8""] # Bug 1191833
     expected: FAIL
 
   [Test valid MP4 type "audio/mp4;codecs="mp4a.67""]
     expected: FAIL
 
   [Test valid MP4 type "video/mp4;codecs="avc1.4d001e""]
     expected:
       if os == "linux": FAIL
--- a/testing/web-platform/meta/media-source/mediasource-play.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-play.html.ini
@@ -1,8 +1,3 @@
 [mediasource-play.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test normal playback case with MediaSource API]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini
@@ -1,8 +1,3 @@
 [mediasource-redundant-seek.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test redundant fully prebuffered seek]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-remove.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-remove.html.ini
@@ -1,70 +1,17 @@
 [mediasource-remove.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
   [Test removing all appended data.]
-    expected: FAIL
+    expected:
+      if not (os == "linux" or ((os == "win") and (version == "5.1.2600"))): FAIL
 
   [Test removing beginning of appended data.]
-    expected: FAIL
+    expected:
+      if not (os == "linux" or ((os == "win") and (version == "5.1.2600"))): FAIL
 
   [Test removing the middle of appended data.]
-    expected: FAIL
+    expected:
+      if not (os == "linux" or ((os == "win") and (version == "5.1.2600"))): FAIL
 
   [Test removing the end of appended data.]
     expected: FAIL
-
-  [Test remove with an negative start.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove with non-finite start.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove with a start beyond the duration.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove with a start larger than the end.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove with a NEGATIVE_INFINITY end.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove with a NaN end.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove after SourceBuffer removed from mediaSource.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove while update pending.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test aborting a remove operation.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove with a start at the duration.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test remove transitioning readyState from 'ended' to 'open'.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-removesourcebuffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-removesourcebuffer.html.ini
@@ -1,23 +1,3 @@
 [mediasource-removesourcebuffer.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test addSourceBuffer(), removeSourceBuffer(), addSourceBuffer() sequence.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test calling removeSourceBuffer() twice with the same object.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test calling removeSourceBuffer() in ended state.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test removesourcebuffer event on activeSourceBuffers.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini
@@ -1,16 +1,4 @@
 [mediasource-seek-during-pending-seek.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  disabled:
-    if os == "mac": https://bugzilla.mozilla.org/show_bug.cgi?id=1183523
-    if (os == "win") and (version != "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1183523
-  [Test seeking to a new location before transitioning beyond HAVE_METADATA.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test seeking to a new location during a pending seek.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1183523
--- a/testing/web-platform/meta/media-source/mediasource-sourcebuffer-mode.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-sourcebuffer-mode.html.ini
@@ -1,33 +1,7 @@
 [mediasource-sourcebuffer-mode.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test initial value of SourceBuffer.mode is "segments"]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.mode]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting a removed SourceBuffer's mode]
+  [Test setting SourceBuffer.mode triggers parent MediaSource 'ended' to 'open' transition.] # Bug 1192165
     expected:
       if os == "linux": FAIL
       if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.mode while still updating]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.mode triggers parent MediaSource 'ended' to 'open' transition.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.mode and SourceBuffer.timestampOffset while parsing media segment.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-sourcebufferlist.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-sourcebufferlist.html.ini
@@ -1,13 +1,3 @@
 [mediasource-sourcebufferlist.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test SourceBufferList event dispatching.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test that only 1 removesourcebuffer event fires on each SourceBufferList when the MediaSource closes.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/testing/web-platform/meta/media-source/mediasource-timestamp-offset.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-timestamp-offset.html.ini
@@ -1,78 +1,3 @@
 [mediasource-timestamp-offset.html]
   type: testharness
   prefs: [media.mediasource.enabled:true]
-  [Test setting SourceBuffer.timestampOffset to a positive number.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to a negative number.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to zero.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to positive infinity.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to negative infinity.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to NaN.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to undefined.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to null.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to false.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to true.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to a number string.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting SourceBuffer.timestampOffset to an empty string.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting timestampOffset in 'ended' state causes a transition to 'open'.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test getting the initial value of timestampOffset.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [Test setting timestampoffset after removing the sourcebuffer.]
-    expected:
-      if os == "linux": FAIL
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -136,16 +136,20 @@ let Management = {
     this.emitter.on(hook, callback);
   },
 
   // Ask to run all the callbacks that are registered for a given hook.
   emit(hook, ...args) {
     this.lazyInit();
     this.emitter.emit(hook, ...args);
   },
+
+  off(hook, callback) {
+    this.emitter.off(hook, callback);
+  }
 };
 
 // A MessageBroker that's used to send and receive messages for
 // extension pages (which run in the chrome process).
 let globalBroker = new MessageBroker([Services.mm, Services.ppmm]);
 
 // An extension page is an execution context for any extension content
 // that runs in the chrome process. It's used for background pages
@@ -524,23 +528,23 @@ Extension.prototype = {
     this.onShutdown.add(obj);
   },
 
   forgetOnClose(obj) {
     this.onShutdown.delete(obj);
   },
 
   startup() {
-    GlobalManager.init(this);
-
     return Promise.all([this.readManifest(), this.readLocaleMessages()]).then(([manifest, messages]) => {
       if (this.hasShutdown) {
         return;
       }
 
+      GlobalManager.init(this);
+
       this.manifest = manifest;
       this.localeMessages = messages;
 
       Management.emit("startup", this);
 
       this.runManifest(manifest);
     }).catch(e => {
       dump(`Extension error: ${e} ${e.fileName}:${e.lineNumber}\n`);
--- a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
+++ b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
@@ -332,57 +332,57 @@ let TelemetryReportingPolicyImpl = {
    */
   canUpload: function() {
     // If data submission is disabled, there's no point in showing the infobar. Just
     // forbid to upload.
     if (!this.dataSubmissionEnabled) {
       return false;
     }
 
-    // Make sure the user is notified of the current policy. If he isn't, don't try
-    // to upload anything.
-    if (!this._ensureUserNotified()) {
-      return false;
-    }
-
-    // Submission is enabled and user is notified: upload is allowed.
-    return true;
+    // Submission is enabled. We enable upload if user is notified or we need to bypass
+    // the policy.
+    const bypassNotification = Preferences.get(PREF_BYPASS_NOTIFICATION, false);
+    return this.isUserNotifiedOfCurrentPolicy || bypassNotification;
   },
 
   /**
    * Migrate the data policy preferences, if needed.
    */
   _migratePreferences: function() {
     // Current prefs are mostly the same than the old ones, except for some deprecated ones.
     for (let pref of DEPRECATED_FHR_PREFS) {
       Preferences.reset(pref);
     }
   },
 
   /**
-   * Make sure the user is notified about the policy before allowing upload.
-   * @return {Boolean} True if the user was notified, false otherwise.
+   * Show the data choices infobar if the user wasn't already notified and data submission
+   * is enabled.
    */
-  _ensureUserNotified: function() {
-    const BYPASS_NOTIFICATION = Preferences.get(PREF_BYPASS_NOTIFICATION, false);
-    if (this.isUserNotifiedOfCurrentPolicy || BYPASS_NOTIFICATION) {
-      return true;
+  _showInfobar: function() {
+    if (!this.dataSubmissionEnabled) {
+      this._log.trace("_showInfobar - Data submission disabled by the policy.");
+      return;
     }
 
-    this._log.trace("ensureUserNotified - User not notified, notifying now.");
-    if (this._notificationInProgress) {
-      this._log.trace("ensureUserNotified - User not notified, notification in progress.");
-      return false;
+    const bypassNotification = Preferences.get(PREF_BYPASS_NOTIFICATION, false);
+    if (this.isUserNotifiedOfCurrentPolicy || bypassNotification) {
+      this._log.trace("_showInfobar - User already notified or bypassing the policy.");
+      return;
     }
 
+    if (this._notificationInProgress) {
+      this._log.trace("_showInfobar - User not notified, notification already in progress.");
+      return;
+    }
+
+    this._log.trace("_showInfobar - User not notified, notifying now.");
     this._notificationInProgress = true;
     let request = new NotifyPolicyRequest(this._log);
     Observers.notify("datareporting:notify-data-policy:request", request);
-
-    return false;
   },
 
   /**
    * Called when the user is notified with the infobar.
    */
   _infobarShownCallback: function() {
     this._log.trace("_infobarShownCallback");
     this._recordNotificationData();
@@ -407,13 +407,13 @@ let TelemetryReportingPolicyImpl = {
     }
 
     const isFirstRun = Preferences.get(PREF_FIRST_RUN, true);
     const delay =
       isFirstRun ? NOTIFICATION_DELAY_FIRST_RUN_MSEC: NOTIFICATION_DELAY_NEXT_RUNS_MSEC;
 
     this._startupNotificationTimerId = Policy.setShowInfobarTimeout(
         // Calling |canUpload| eventually shows the infobar, if needed.
-        () => this.canUpload(), delay);
+        () => this._showInfobar(), delay);
     // We performed at least a run, flip the firstRun preference.
     Preferences.set(PREF_FIRST_RUN, false);
   },
 };
--- a/toolkit/components/telemetry/TelemetrySend.jsm
+++ b/toolkit/components/telemetry/TelemetrySend.jsm
@@ -108,16 +108,30 @@ function isV4PingFormat(aPing) {
  * Check if the provided ping is a deletion ping.
  * @param {Object} aPing The ping to check.
  * @return {Boolean} True if the ping is a deletion ping, false otherwise.
  */
 function isDeletionPing(aPing) {
   return isV4PingFormat(aPing) && (aPing.type == PING_TYPE_DELETION);
 }
 
+/**
+ * Save the provided ping as a pending ping. If it's a deletion ping, save it
+ * to a special location.
+ * @param {Object} aPing The ping to save.
+ * @return {Promise} A promise resolved when the ping is saved.
+ */
+function savePing(aPing) {
+  if (isDeletionPing(aPing)) {
+    return TelemetryStorage.saveDeletionPing(aPing);
+  } else {
+    return TelemetryStorage.savePendingPing(aPing);
+  }
+}
+
 function tomorrow(date) {
   let d = new Date(date);
   d.setDate(d.getDate() + 1);
   return d;
 }
 
 /**
  * @return {String} This returns a string with the gzip compressed data.
@@ -668,17 +682,17 @@ let TelemetrySendImpl = {
       this._log.trace("submitPing - Telemetry is not allowed to send pings.");
       return Promise.resolve();
     }
 
     if (!this.canSendNow) {
       // Sending is disabled or throttled, add this to the persisted pending pings.
       this._log.trace("submitPing - can't send ping now, persisting to disk - " +
                       "canSendNow: " + this.canSendNow);
-      return TelemetryStorage.savePendingPing(ping);
+      return savePing(ping);
     }
 
     // Let the scheduler trigger sending pings if possible.
     // As a safety mechanism, this resets any currently active throttling.
     this._log.trace("submitPing - can send pings, trying to send now");
     this._currentPings.set(ping.id, ping);
     SendScheduler.triggerSendingPings(true);
     return Promise.resolve();
@@ -711,21 +725,17 @@ let TelemetrySendImpl = {
     for (let current of currentPings) {
       let ping = current;
       let p = Task.spawn(function*() {
         try {
           yield this._doPing(ping, ping.id, false);
         } catch (ex) {
           this._log.info("sendPings - ping " + ping.id + " not sent, saving to disk", ex);
           // Deletion pings must be saved to a special location.
-          if (isDeletionPing(ping)) {
-            yield TelemetryStorage.saveDeletionPing(ping);
-          } else {
-            yield TelemetryStorage.savePendingPing(ping);
-          }
+          yield savePing(ping);
         } finally {
           this._currentPings.delete(ping.id);
         }
       }.bind(this));
 
       this._trackPendingPingTask(p);
       pingSends.push(p);
     }
@@ -1017,17 +1027,17 @@ let TelemetrySendImpl = {
     })];
     p.push(SendScheduler.waitOnSendTask());
     return Promise.all(p);
   },
 
   _persistCurrentPings: Task.async(function*() {
     for (let [id, ping] of this._currentPings) {
       try {
-        yield TelemetryStorage.savePendingPing(ping);
+        yield savePing(ping);
         this._log.trace("_persistCurrentPings - saved ping " + id);
       } catch (ex) {
         this._log.error("_persistCurrentPings - failed to save ping " + id, ex);
       } finally {
         this._currentPings.delete(id);
       }
     }
   }),
--- a/toolkit/devtools/server/tests/mochitest/chrome.ini
+++ b/toolkit/devtools/server/tests/mochitest/chrome.ini
@@ -42,16 +42,18 @@ skip-if = buildapp == 'mulet'
 [test_framerate_02.html]
 skip-if = buildapp == 'mulet'
 [test_framerate_03.html]
 skip-if = buildapp == 'mulet'
 [test_framerate_04.html]
 skip-if = buildapp == 'mulet'
 [test_framerate_05.html]
 skip-if = buildapp == 'mulet'
+[test_framerate_06.html]
+skip-if = buildapp == 'mulet'
 [test_getProcess.html]
 skip-if = buildapp == 'mulet'
 [test_inspector-anonymous.html]
 [test_inspector-changeattrs.html]
 [test_inspector-changevalue.html]
 [test_inspector-dead-nodes.html]
 [test_inspector_getImageData.html]
 skip-if = buildapp == 'mulet'
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/test_framerate_06.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 1171489 - Tests if the framerate actor does not record timestamps from multiple frames. 
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Framerate actor test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script>
+
+window.onload = function() {
+  SimpleTest.waitForExplicitFinish();
+  var {FramerateFront} = require("devtools/server/actors/framerate");
+  var {TargetFactory} = require("devtools/framework/target");
+
+  var url = document.getElementById("testContent").href;
+  attachURL(url, onTab);
+
+  function onTab(_, client, form, contentDoc) {
+    var contentWin = contentDoc.defaultView;
+    var chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+    var selectedTab = chromeWin.gBrowser.selectedTab;
+
+    var target = TargetFactory.forTab(selectedTab);
+    var front = FramerateFront(client, form);
+
+    front.startRecording().then(() => {
+      window.setTimeout(() => {
+        // Wait for the iframe to be loaded again
+        window.addEventListener("message", function loaded (event) {
+          if (event.data === "ready") {
+            window.removeEventListener("message", loaded);
+            window.setTimeout(() => {
+              front.stopRecording().then(ticks => {
+                onRecordingStopped(client, ticks);
+              });
+            }, 1000);
+          }
+        });
+        contentWin.location.reload();
+      }, 1000);
+    });
+  }
+
+  function onRecordingStopped(client, ticks) {
+    var diffs = [];
+
+    info(`Got ${ticks.length} ticks.`);
+
+    for (var i = 1; i < ticks.length; i++) {
+      var prev = ticks[i - 1];
+      var curr = ticks[i];
+      diffs.push(curr - prev);
+      info(curr + " - " + (curr - prev));
+    }
+
+    // 1000 / 60 => 16.666... so we shouldn't get more than diffs of 16.66.. but
+    // when we get ticks from other frames they're usually at diffs of < 1. Sometimes
+    // ticks can still be less than 16ms even on one frame (usually following a very slow
+    // frame), so use a low number (2) to be our threshold
+    var THRESHOLD = 2;
+    ok(ticks.length >= 60, "we should have 2 seconds worth of ticks, atleast 60 ticks");
+    var belowThreshold = diffs.filter(v => v <= THRESHOLD);
+    ok(belowThreshold.length <= 10, "we should have very few frames less than the threshold");
+
+    client.close(() => {
+      DebuggerServer.destroy();
+      SimpleTest.finish()
+    });
+  }
+}
+</script>
+</pre>
+<a id="testContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
+</body>
+</html>
--- a/toolkit/devtools/shared/framerate.js
+++ b/toolkit/devtools/shared/framerate.js
@@ -87,12 +87,13 @@ let Framerate = exports.Framerate = Clas
     this._ticks.push(this.tabActor.docShell.now() - this._startTime);
   },
 
   /**
    * When the content window for the tab actor is created.
    */
   _onGlobalCreated: function (win) {
     if (this._recording) {
+      this._contentWin.cancelAnimationFrame(this._rafID);
       this._rafID = this._contentWin.requestAnimationFrame(this._onRefreshDriverTick);
     }
   }
 });
--- a/toolkit/locales/en-US/chrome/passwordmgr/passwordManager.dtd
+++ b/toolkit/locales/en-US/chrome/passwordmgr/passwordManager.dtd
@@ -17,16 +17,19 @@
 <!ENTITY      treehead.timePasswordChanged.label "Last Changed">
 <!ENTITY      treehead.timesUsed.label           "Times Used">
 
 <!ENTITY      remove.label                    "Remove">
 <!ENTITY      remove.accesskey                "R">
 <!ENTITY      removeall.label                 "Remove All">
 <!ENTITY      removeall.accesskey             "A">
 
+<!ENTITY      addLogin.label                  "Add Login">
+<!ENTITY      addLogin.accesskey              "L">
+
 <!ENTITY      import.label                    "Import…">
 <!ENTITY      import.accesskey                "I">
 
 <!ENTITY      filter.label                    "Search:">
 <!ENTITY      filter.accesskey                "S">
 
 <!ENTITY      windowClose.key                 "w">
 <!ENTITY      focusSearch1.key                "f">
--- a/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties
+++ b/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties
@@ -49,17 +49,21 @@ notifyBarDontChangeButtonAccessKey = D
 userSelectText = Please confirm which user you are changing the password for
 hidePasswords=Hide Passwords
 hidePasswordsAccessKey=P
 showPasswords=Show Passwords
 showPasswordsAccessKey=P
 noMasterPasswordPrompt=Are you sure you wish to show your passwords?
 removeAllPasswordsPrompt=Are you sure you wish to remove all passwords?
 removeAllPasswordsTitle=Remove all passwords
+removeLoginPrompt=Are you sure you wish to remove this login?
+removeLoginTitle=Remove login
 loginsSpielAll=Passwords for the following sites are stored on your computer:
 loginsSpielFiltered=The following passwords match your search:
 # LOCALIZATION NOTE (loginHostAge):
 # This is used to show the context menu login items with their age.
 # 1st string is the username for the login, 2nd is the login's age.
 loginHostAge=%1$S (%2$S)
 # LOCALIZATION NOTE (noUsername):
 # String is used on the context menu when a login doesn't have a username.
 noUsername=No username
+duplicateLoginTitle=Login already exists
+duplicateLogin=A duplicate login already exists.
--- a/toolkit/mozapps/extensions/AddonPathService.cpp
+++ b/toolkit/mozapps/extensions/AddonPathService.cpp
@@ -9,22 +9,24 @@
 #include "nsIURI.h"
 #include "nsXULAppAPI.h"
 #include "jsapi.h"
 #include "nsServiceManagerUtils.h"
 #include "nsLiteralString.h"
 #include "nsThreadUtils.h"
 #include "nsIIOService.h"
 #include "nsNetUtil.h"
+#include "nsIAddonPolicyService.h"
 #include "nsIFileURL.h"
 #include "nsIResProtocolHandler.h"
 #include "nsIChromeRegistry.h"
 #include "nsIJARURI.h"
 #include "nsJSUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "mozilla/AddonPathService.h"
 #include "mozilla/Omnijar.h"
 
 #include <algorithm>
 
 namespace mozilla {
 
 struct PathEntryComparator
@@ -60,16 +62,28 @@ AddonPathService::GetInstance()
 {
   if (!sInstance) {
     sInstance = new AddonPathService();
   }
   NS_ADDREF(sInstance);
   return sInstance;
 }
 
+static JSAddonId*
+ConvertAddonId(const nsAString& addonIdString)
+{
+  AutoSafeJSContext cx;
+  JS::RootedValue strv(cx);
+  if (!mozilla::dom::ToJSValue(cx, addonIdString, &strv)) {
+    return nullptr;
+  }
+  JS::RootedString str(cx, strv.toString());
+  return JS::NewAddonId(cx, str);
+}
+
 JSAddonId*
 AddonPathService::Find(const nsAString& path)
 {
   // Use binary search to find the nearest entry that is <= |path|.
   PathEntryComparator comparator;
   unsigned index = mPaths.IndexOfFirstElementGt(PathEntry(path, nullptr), comparator);
   if (index == 0) {
     return nullptr;
@@ -102,21 +116,17 @@ AddonPathService::FindAddonId(const nsAS
   }
 
   return sInstance->Find(path);
 }
 
 NS_IMETHODIMP
 AddonPathService::InsertPath(const nsAString& path, const nsAString& addonIdString)
 {
-  AutoSafeJSContext cx;
-  JS::RootedString str(cx, JS_NewUCStringCopyN(cx,
-                                               addonIdString.BeginReading(),
-                                               addonIdString.Length()));
-  JSAddonId* addonId = JS::NewAddonId(cx, str);
+  JSAddonId* addonId = ConvertAddonId(addonIdString);
 
   // Add the new path in sorted order.
   PathEntryComparator comparator;
   mPaths.InsertElementSorted(PathEntry(path, addonId), comparator);
   return NS_OK;
 }
 
 static nsresult
@@ -193,18 +203,32 @@ ResolveURI(nsIURI* aURI, nsAString& out)
 
 JSAddonId*
 MapURIToAddonID(nsIURI* aURI)
 {
   if (!NS_IsMainThread() || !XRE_IsParentProcess()) {
     return nullptr;
   }
 
+  bool equals;
+  nsresult rv;
+  if (NS_SUCCEEDED(aURI->SchemeIs("moz-extension", &equals)) && equals) {
+    nsCOMPtr<nsIAddonPolicyService> service = do_GetService("@mozilla.org/addons/policy-service;1");
+    if (service) {
+      nsString addonId;
+      rv = service->ExtensionURIToAddonId(aURI, addonId);
+      if (NS_FAILED(rv))
+        return nullptr;
+
+      return ConvertAddonId(addonId);
+    }
+  }
+
   nsAutoString filePath;
-  nsresult rv = ResolveURI(aURI, filePath);
+  rv = ResolveURI(aURI, filePath);
   if (NS_FAILED(rv))
     return nullptr;
 
   nsCOMPtr<nsIFile> greJar = Omnijar::GetPath(Omnijar::GRE);
   nsCOMPtr<nsIFile> appJar = Omnijar::GetPath(Omnijar::APP);
   if (greJar && appJar) {
     nsAutoString greJarString, appJarString;
     if (NS_FAILED(greJar->GetPath(greJarString)) || NS_FAILED(appJar->GetPath(appJarString)))
rename from browser/components/extensions/bootstrap.js
rename to toolkit/mozapps/extensions/internal/WebExtensionBootstrap.js
--- a/browser/components/extensions/bootstrap.js
+++ b/toolkit/mozapps/extensions/internal/WebExtensionBootstrap.js
@@ -3,18 +3,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/Extension.jsm");
 
 let extension;
 
+function install(data, reason)
+{
+}
+
 function startup(data, reason)
 {
   extension = new Extension(data);
   extension.startup();
 }
 
 function shutdown(data, reason)
 {
   extension.shutdown();
 }
+
+function uninstall(data, reason)
+{
+}
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -49,16 +49,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "ChromeRegistry",
                                    "@mozilla.org/chrome/chrome-registry;1",
                                    "nsIChromeRegistry");
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "ResProtocolHandler",
                                    "@mozilla.org/network/protocol;1?name=resource",
                                    "nsIResProtocolHandler");
+XPCOMUtils.defineLazyServiceGetter(this,
+                                   "AddonPolicyService",
+                                   "@mozilla.org/addons/policy-service;1",
+                                   "nsIAddonPolicyService");
 
 XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
   let certUtils = {};
   Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
   return certUtils;
 });
 
 const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
@@ -112,17 +116,18 @@ const URI_EXTENSION_STRINGS           = 
 const STRING_TYPE_NAME                = "type.%ID%.name";
 
 const DIR_EXTENSIONS                  = "extensions";
 const DIR_STAGE                       = "staged";
 const DIR_TRASH                       = "trash";
 
 const FILE_DATABASE                   = "extensions.json";
 const FILE_OLD_CACHE                  = "extensions.cache";
-const FILE_INSTALL_MANIFEST           = "install.rdf";
+const FILE_RDF_MANIFEST               = "install.rdf";
+const FILE_WEB_MANIFEST               = "manifest.json";
 const FILE_XPI_ADDONS_LIST            = "extensions.ini";
 
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_APPDIR                      = "XCurProcD";
 const KEY_TEMPDIR                     = "TmpD";
 const KEY_APP_DISTRIBUTION            = "XREAppDist";
 
 const KEY_APP_PROFILE                 = "app-profile";
@@ -193,23 +198,31 @@ const TYPES = {
   extension: 2,
   theme: 4,
   locale: 8,
   multipackage: 32,
   dictionary: 64,
   experiment: 128,
 };
 
+// Some add-on types that we track internally are presented as other types
+// externally
+const TYPE_ALIASES = {
+  "webextension": "extension",
+};
+
 const RESTARTLESS_TYPES = new Set([
+  "webextension",
   "dictionary",
   "experiment",
   "locale",
 ]);
 
 const SIGNED_TYPES = new Set([
+  "webextension",
   "extension",
   "experiment",
 ]);
 
 // Whether add-on signing is required.
 function mustSign(aType) {
   if (!SIGNED_TYPES.has(aType))
     return false;
@@ -633,16 +646,75 @@ function createAddonDetails(id, aAddon) 
     id: id || aAddon.id,
     type: aAddon.type,
     version: aAddon.version,
     multiprocessCompatible: aAddon.multiprocessCompatible
   };
 }
 
 /**
+ * Converts an internal add-on type to the type presented through the API.
+ *
+ * @param  aType
+ *         The internal add-on type
+ * @return an external add-on type
+ */
+function getExternalType(aType) {
+  if (aType in TYPE_ALIASES)
+    return TYPE_ALIASES[aType];
+  return aType;
+}
+
+function getManifestFileForDir(aDir) {
+  let file = aDir.clone();
+  file.append(FILE_WEB_MANIFEST);
+  if (file.exists() && file.isFile())
+    return file;
+  file.leafName = FILE_RDF_MANIFEST;
+  if (file.exists() && file.isFile())
+    return file;
+  return null;
+}
+
+function getManifestEntryForZipReader(aZipReader) {
+  if (aZipReader.hasEntry(FILE_WEB_MANIFEST))
+    return FILE_WEB_MANIFEST;
+  if (aZipReader.hasEntry(FILE_RDF_MANIFEST))
+    return FILE_RDF_MANIFEST;
+  return null;
+}
+
+/**
+ * Converts a list of API types to a list of API types and any aliases for those
+ * types.
+ *
+ * @param  aTypes
+ *         An array of types or null for all types
+ * @return an array of types or null for all types
+ */
+function getAllAliasesForTypes(aTypes) {
+  if (!aTypes)
+    return null;
+
+  // Build a set of all requested types and their aliases
+  let typeset = new Set(aTypes);
+
+  for (let alias of Object.keys(TYPE_ALIASES)) {
+    // Ignore any requested internal types
+    typeset.delete(alias);
+
+    // Add any alias for the internal type
+    if (typeset.has(TYPE_ALIASES[alias]))
+      typeset.add(alias);
+  }
+
+  return [...typeset];
+}
+
+/**
  * Converts an RDF literal, resource or integer into a string.
  *
  * @param  aLiteral
  *         The RDF object to convert
  * @return a string if the object could be converted or null
  */
 function getRDFValue(aLiteral) {
   if (aLiteral instanceof Ci.nsIRDFLiteral)
@@ -665,16 +737,106 @@ function getRDFValue(aLiteral) {
  *         The property to read
  * @return a string if the property existed or null
  */
 function getRDFProperty(aDs, aResource, aProperty) {
   return getRDFValue(aDs.GetTarget(aResource, EM_R(aProperty), true));
 }
 
 /**
+ * Reads an AddonInternal object from a manifest stream.
+ *
+ * @param  aStream
+ *         An open stream to read the manifest from
+ * @return an AddonInternal object
+ * @throws if the install manifest in the stream is corrupt or could not
+ *         be read
+ */
+function loadManifestFromWebManifest(aStream) {
+  let decoder = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+  let manifest = decoder.decodeFromStream(aStream, aStream.available());
+
+  function findProp(obj, current, properties) {
+    if (properties.length == 0)
+      return obj;
+
+    let field = properties[0];
+    current += "." + field;
+    if (!obj || !(field in obj)) {
+      throw new Error("Manifest file was missing required property " + current.substring(1));
+    }
+
+    return findProp(obj[field], current, properties.slice(1));
+  }
+
+  function getProp(path) {
+    return findProp(manifest, "", path.split("."));
+  }
+
+  function getOptionalProp(path, defValue = null) {
+    try {
+      return findProp(manifest, "", path.split("."));
+    }
+    catch (e) {
+      return defValue;
+    }
+  }
+
+  let mVersion = getProp("manifest_version");
+  if (mVersion != 2) {
+    throw new Error("Expected manifest_version to be 2 but was " + mVersion);
+  }
+
+  let addon = new AddonInternal();
+  addon.id = getProp("applications.gecko.id");
+  if (!gIDTest.test(addon.id))
+    throw new Error("Illegal add-on ID " + addon.id);
+  addon.version = getProp("version");
+  addon.type = "webextension";
+  addon.unpack = false;
+  addon.strictCompatibility = true;
+  addon.bootstrap = true;
+  addon.hasBinaryComponents = false;
+  addon.multiprocessCompatible = true;
+  addon.internalName = null;
+  addon.updateURL = null;
+  addon.updateKey = null;
+  addon.optionsURL = null;
+  addon.optionsType = null;
+  addon.aboutURL = null;
+  addon.iconURL = null;
+  addon.icon64URL = null;
+  addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+
+  addon.defaultLocale = {
+    name: getProp("name"),
+    description: getOptionalProp("description"),
+    creator: null,
+    homepageURL: null,
+
+    developers: null,
+    translators: null,
+    contributors: null,
+  }
+
+  addon.targetApplications = [{
+    id: TOOLKIT_ID,
+    minVersion: "42a1",
+    maxVersion: "*",
+  }];
+
+  addon.locales = [];
+  addon.targetPlatforms = [];
+  addon.userDisabled = false;
+  addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;
+
+  return addon;
+}
+
+/**
  * Reads an AddonInternal object from an RDF stream.
  *
  * @param  aUri
  *         The URI that the manifest is being read from
  * @param  aStream
  *         An open stream to read the RDF from
  * @return an AddonInternal object
  * @throws if the install manifest in the RDF stream is corrupt or could not
@@ -925,48 +1087,49 @@ function loadManifestFromRDF(aUri, aStre
     addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
     addon.updateURL = null;
     addon.updateKey = null;
 
     addon.targetApplications = [];
     addon.targetPlatforms = [];
   }
 
+  return addon;
+}
+
+function defineSyncGUID(aAddon) {
   // Load the storage service before NSS (nsIRandomGenerator),
   // to avoid a SQLite initialization error (bug 717904).
   let storage = Services.storage;
 
   // Define .syncGUID as a lazy property which is also settable
-  Object.defineProperty(addon, "syncGUID", {
+  Object.defineProperty(aAddon, "syncGUID", {
     get: () => {
-
       // Generate random GUID used for Sync.
       // This was lifted from util.js:makeGUID() from services-sync.
       let rng = Cc["@mozilla.org/security/random-generator;1"].
         createInstance(Ci.nsIRandomGenerator);
       let bytes = rng.generateRandomBytes(9);
       let byte_string = [String.fromCharCode(byte) for each (byte in bytes)]
                         .join("");
       // Base64 encode
       let guid = btoa(byte_string).replace(/\+/g, '-')
         .replace(/\//g, '_');
 
-      delete addon.syncGUID;
-      addon.syncGUID = guid;
+      delete aAddon.syncGUID;
+      aAddon.syncGUID = guid;
       return guid;
     },
     set: (val) => {
-      delete addon.syncGUID;
-      addon.syncGUID = val;
+      delete aAddon.syncGUID;
+      aAddon.syncGUID = val;
     },
     configurable: true,
     enumerable: true,
   });
-
-  return addon;
 }
 
 /**
  * Loads an AddonInternal object from an add-on extracted in a directory.
  *
  * @param  aDir
  *         The nsIFile directory holding the add-on
  * @return an AddonInternal object
@@ -984,43 +1147,52 @@ let loadManifestFromDir = Task.async(fun
     let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
     let entry;
     while ((entry = entries.nextFile))
       size += getFileSize(entry);
     entries.close();
     return size;
   }
 
-  let file = aDir.clone();
-  file.append(FILE_INSTALL_MANIFEST);
-  if (!file.exists() || !file.isFile())
+  function loadFromRDF(aFile, aStream) {
+    let addon = loadManifestFromRDF(Services.io.newFileURI(aFile), aStream);
+
+    let file = aDir.clone();
+    file.append("chrome.manifest");
+    let chromeManifest = ChromeManifestParser.parseSync(Services.io.newFileURI(file));
+    addon.hasBinaryComponents = ChromeManifestParser.hasType(chromeManifest,
+                                                             "binary-component");
+    return addon;
+  }
+
+  let file = getManifestFileForDir(aDir);
+  if (!file) {
     throw new Error("Directory " + aDir.path + " does not contain a valid " +
                     "install manifest");
+  }
 
   let fis = Cc["@mozilla.org/network/file-input-stream;1"].
             createInstance(Ci.nsIFileInputStream);
   fis.init(file, -1, -1, false);
   let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
             createInstance(Ci.nsIBufferedInputStream);
   bis.init(fis, 4096);
 
   try {
-    let addon = loadManifestFromRDF(Services.io.newFileURI(file), bis);
+    let addon = file.leafName == FILE_WEB_MANIFEST ?
+                loadManifestFromWebManifest(bis) :
+                loadFromRDF(file, bis);
+
     addon._sourceBundle = aDir.clone();
     addon.size = getFileSize(aDir);
-
-    file = aDir.clone();
-    file.append("chrome.manifest");
-    let chromeManifest = ChromeManifestParser.parseSync(Services.io.newFileURI(file));
-    addon.hasBinaryComponents = ChromeManifestParser.hasType(chromeManifest,
-                                                             "binary-component");
-
     addon.signedState = yield verifyDirSignedState(aDir, addon);
-
     addon.appDisabled = !isUsableAddon(addon);
+
+    defineSyncGUID(addon);
+
     return addon;
   }
   finally {
     bis.close();
     fis.close();
   }
 });
 
@@ -1028,44 +1200,61 @@ let loadManifestFromDir = Task.async(fun
  * Loads an AddonInternal object from an nsIZipReader for an add-on.
  *
  * @param  aZipReader
  *         An open nsIZipReader for the add-on's files
  * @return an AddonInternal object
  * @throws if the XPI file does not contain a valid install manifest
  */
 let loadManifestFromZipReader = Task.async(function* loadManifestFromZipReader(aZipReader) {
-  let zis = aZipReader.getInputStream(FILE_INSTALL_MANIFEST);
-  let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
-            createInstance(Ci.nsIBufferedInputStream);
-  bis.init(zis, 4096);
-
-  try {
-    let uri = buildJarURI(aZipReader.file, FILE_INSTALL_MANIFEST);
-    let addon = loadManifestFromRDF(uri, bis);
-    addon._sourceBundle = aZipReader.file;
-
-    addon.size = 0;
-    let entries = aZipReader.findEntries(null);
-    while (entries.hasMore())
-      addon.size += aZipReader.getEntry(entries.getNext()).realSize;
+  function loadFromRDF(aStream) {
+    let uri = buildJarURI(aZipReader.file, FILE_RDF_MANIFEST);
+    let addon = loadManifestFromRDF(uri, aStream);
 
     // Binary components can only be loaded from unpacked addons.
     if (addon.unpack) {
       uri = buildJarURI(aZipReader.file, "chrome.manifest");
       let chromeManifest = ChromeManifestParser.parseSync(uri);
       addon.hasBinaryComponents = ChromeManifestParser.hasType(chromeManifest,
                                                                "binary-component");
     } else {
       addon.hasBinaryComponents = false;
     }
 
+    return addon;
+  }
+
+  let entry = getManifestEntryForZipReader(aZipReader);
+  if (!entry) {
+    throw new Error("File " + aZipReader.file.path + " does not contain a valid " +
+                    "install manifest");
+  }
+
+  let zis = aZipReader.getInputStream(entry);
+  let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
+            createInstance(Ci.nsIBufferedInputStream);
+  bis.init(zis, 4096);
+
+  try {
+    let addon = entry == FILE_WEB_MANIFEST ?
+                loadManifestFromWebManifest(bis) :
+                loadFromRDF(bis);
+
+    addon._sourceBundle = aZipReader.file;
+
+    addon.size = 0;
+    let entries = aZipReader.findEntries(null);
+    while (entries.hasMore())
+      addon.size += aZipReader.getEntry(entries.getNext()).realSize;
+
     addon.signedState = yield verifyZipSignedState(aZipReader.file, addon);
-
     addon.appDisabled = !isUsableAddon(addon);
+
+    defineSyncGUID(addon);
+
     return addon;
   }
   finally {
     bis.close();
     zis.close();
   }
 });
 
@@ -1631,25 +1820,22 @@ XPIState.prototype = {
       let [modFile, modTime, items] = recursiveLastModifiedTime(aFile);
       XPIProvider._mostRecentlyModifiedFile[aId] = modFile;
       XPIProvider.setTelemetry(aId, "scan_items", items);
       if (modTime != this.scanTime) {
         this.scanTime = modTime;
         changed = true;
       }
     }
-    // if the add-on is disabled, modified time is the install.rdf time, if any.
-    // If {path}/install.rdf doesn't exist, we assume this is a packed .xpi and use
+    // if the add-on is disabled, modified time is the install manifest time, if
+    // any. If no manifest exists, we assume this is a packed .xpi and use
     // the time stamp of {path}
     try {
-      // Get the install.rdf update time, if any.
-      // XXX This will eventually also need to check for package.json or whatever
-      // the new manifest is named.
-      let maniFile = aFile.clone();
-      maniFile.append(FILE_INSTALL_MANIFEST);
+      // Get the install manifest update time, if any.
+      let maniFile = getManifestFileForDir(aFile);
       if (!(aId in XPIProvider._mostRecentlyModifiedFile)) {
         XPIProvider._mostRecentlyModifiedFile[aId] = maniFile.leafName;
       }
       let maniTime = maniFile.lastModifiedTime;
       if (maniTime != this.manifestTime) {
         this.manifestTime = maniTime;
         changed = true;
       }
@@ -2651,22 +2837,21 @@ this.XPIProvider = {
           seenFiles.push(stageDirEntry.leafName);
           continue;
         }
 
         changed = true;
 
         if (isDir) {
           // Check if the directory contains an install manifest.
-          let manifest = stageDirEntry.clone();
-          manifest.append(FILE_INSTALL_MANIFEST);
+          let manifest = getManifestFileForDir(stageDirEntry);
 
           // If the install manifest doesn't exist uninstall this add-on in this
           // install location.
-          if (!manifest.exists()) {
+          if (!manifest) {
             logger.debug("Processing uninstall of " + id + " in " + aLocation.name);
             try {
               aLocation.uninstallAddon(id);
               seenFiles.push(stageDirEntry.leafName);
             }
             catch (e) {
               logger.error("Failed to uninstall add-on " + id + " in " + aLocation.name, e);
             }
@@ -3950,17 +4135,19 @@ this.XPIProvider = {
    * Called to get Addons of a particular type.
    *
    * @param  aTypes
    *         An array of types to fetch. Can be null to get all types.
    * @param  aCallback
    *         A callback to pass an array of Addons to
    */
   getAddonsByTypes: function XPI_getAddonsByTypes(aTypes, aCallback) {
-    XPIDatabase.getVisibleAddons(aTypes, function getAddonsByTypes_getVisibleAddons(aAddons) {
+    let typesToGet = getAllAliasesForTypes(aTypes);
+
+    XPIDatabase.getVisibleAddons(typesToGet, function getAddonsByTypes_getVisibleAddons(aAddons) {
       aCallback([createWrapper(a) for each (a in aAddons)]);
     });
   },
 
   /**
    * Obtain an Addon having the specified Sync GUID.
    *
    * @param  aGUID
@@ -3979,17 +4166,19 @@ this.XPIProvider = {
    *
    * @param  aTypes
    *         An array of types to fetch. Can be null to get all types
    * @param  aCallback
    *         A callback to pass an array of Addons to
    */
   getAddonsWithOperationsByTypes:
   function XPI_getAddonsWithOperationsByTypes(aTypes, aCallback) {
-    XPIDatabase.getVisibleAddonsWithPendingOperations(aTypes,
+    let typesToGet = getAllAliasesForTypes(aTypes);
+
+    XPIDatabase.getVisibleAddonsWithPendingOperations(typesToGet,
       function getAddonsWithOpsByTypes_getVisibleAddonsWithPendingOps(aAddons) {
       let results = [createWrapper(a) for each (a in aAddons)];
       XPIProvider.installs.forEach(function(aInstall) {
         if (aInstall.state == AddonManager.STATE_INSTALLED &&
             !(aInstall.addon.inDatabase))
           results.push(createWrapper(aInstall.addon));
       });
       aCallback(results);
@@ -4003,17 +4192,17 @@ this.XPIProvider = {
    * @param  aTypes
    *         An array of types or null to get all types
    * @param  aCallback
    *         A callback to pass the array of AddonInstalls to
    */
   getInstallsByTypes: function XPI_getInstallsByTypes(aTypes, aCallback) {
     let results = [];
     this.installs.forEach(function(aInstall) {
-      if (!aTypes || aTypes.indexOf(aInstall.type) >= 0)
+      if (!aTypes || aTypes.indexOf(getExternalType(aInstall.type)) >= 0)
         results.push(aInstall.wrapper);
     });
     aCallback(results);
   },
 
   /**
    * Synchronously map a URI to the corresponding Addon ID.
    *
@@ -4023,16 +4212,20 @@ this.XPIProvider = {
    *
    * @param  aURI
    *         nsIURI to map or null
    * @return string containing the Addon ID
    * @see    AddonManager.mapURIToAddonID
    * @see    amIAddonManager.mapURIToAddonID
    */
   mapURIToAddonID: function XPI_mapURIToAddonID(aURI) {
+    if (aURI.scheme == "moz-extension") {
+      return AddonPolicyService.extensionURIToAddonId(aURI);
+    }
+
     let resolved = this._resolveURIToFile(aURI);
     if (!resolved || !(resolved instanceof Ci.nsIFileURL))
       return null;
 
     for (let [id, path] of this._addonFileMap) {
       if (resolved.file.path.startsWith(path))
         return id;
     }
@@ -4438,16 +4631,18 @@ this.XPIProvider = {
                                     metadata: { addonID: aId } });
       logger.error("Attempted to load bootstrap scope from missing directory " + aFile.path);
       return;
     }
 
     let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
     if (aType == "dictionary")
       uri = "resource://gre/modules/addons/SpellCheckDictionaryBootstrap.js"
+    else if (aType == "webextension")
+      uri = "resource://gre/modules/addons/WebExtensionBootstrap.js"
 
     this.bootstrapScopes[aId] =
       new Cu.Sandbox(principal, { sandboxName: uri,
                                   wantGlobalProperties: ["indexedDB"],
                                   addonId: aId,
                                   metadata: { addonID: aId, URI: uri } });
 
     let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
@@ -6142,21 +6337,23 @@ AddonInstall.createUpdate = function AI_
  */
 function AddonInstallWrapper(aInstall) {
 #ifdef MOZ_EM_DEBUG
   this.__defineGetter__("__AddonInstallInternal__", function AIW_debugGetter() {
     return aInstall;
   });
 #endif
 
-  ["name", "type", "version", "icons", "releaseNotesURI", "file", "state", "error",
+  ["name", "version", "icons", "releaseNotesURI", "file", "state", "error",
    "progress", "maxProgress", "certificate", "certName"].forEach(function(aProp) {
     this.__defineGetter__(aProp, function AIW_propertyGetter() aInstall[aProp]);
   }, this);
 
+  this.__defineGetter__("type", () => getExternalType(aInstall.type));
+
   this.__defineGetter__("iconURL", function AIW_iconURL() aInstall.icons[32]);
 
   this.__defineGetter__("existingAddon", function AIW_existingAddonGetter() {
     return createWrapper(aInstall.existingAddon);
   });
   this.__defineGetter__("addon", function AIW_addonGetter() createWrapper(aInstall.addon));
   this.__defineGetter__("sourceURI", function AIW_sourceURIGetter() aInstall.sourceURI);
 
@@ -6729,24 +6926,26 @@ function AddonWrapper(aAddon) {
     if (repositoryAddon && (aProp in repositoryAddon) &&
         (objValue === undefined || objValue === null)) {
       return [repositoryAddon[aProp], true];
     }
 
     return [objValue, false];
   }
 
-  ["id", "syncGUID", "version", "type", "isCompatible", "isPlatformCompatible",
+  ["id", "syncGUID", "version", "isCompatible", "isPlatformCompatible",
    "providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
    "softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents",
    "strictCompatibility", "compatibilityOverrides", "updateURL",
    "getDataDirectory", "multiprocessCompatible", "signedState"].forEach(function(aProp) {
      this.__defineGetter__(aProp, function AddonWrapper_propertyGetter() aAddon[aProp]);
   }, this);
 
+  this.__defineGetter__("type", () => getExternalType(aAddon.type));
+
   ["fullDescription", "developerComments", "eula", "supportURL",
    "contributionURL", "contributionAmount", "averageRating", "reviewCount",
    "reviewURL", "totalDownloads", "weeklyDownloads", "dailyUsers",
    "repositoryStatus"].forEach(function(aProp) {
     this.__defineGetter__(aProp, function AddonWrapper_repoPropertyGetter() {
       if (aAddon._repositoryAddon)
         return aAddon._repositoryAddon[aProp];
 
--- a/toolkit/mozapps/extensions/internal/moz.build
+++ b/toolkit/mozapps/extensions/internal/moz.build
@@ -8,16 +8,17 @@ EXTRA_JS_MODULES.addons += [
     'AddonLogging.jsm',
     'AddonRepository.jsm',
     'AddonRepository_SQLiteMigrator.jsm',
     'AddonUpdateChecker.jsm',
     'Content.js',
     'GMPProvider.jsm',
     'LightweightThemeImageOptimizer.jsm',
     'SpellCheckDictionaryBootstrap.js',
+    'WebExtensionBootstrap.js',
 ]
 
 # Don't ship unused providers on Android
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     EXTRA_JS_MODULES.addons += [
         'PluginProvider.jsm',
     ]
 
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/webextension_1/manifest.json
@@ -0,0 +1,10 @@
+{
+  "name": "Web Extension Name",
+  "version": "1.0",
+  "manifest_version": 2,
+  "applications": {
+    "gecko": {
+      "id": "webextension1@tests.mozilla.org"
+    }
+  }
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -765,16 +765,72 @@ function writeInstallRDFToDir(aData, aDi
 function writeInstallRDFForExtension(aData, aDir, aId, aExtraFile) {
   if (TEST_UNPACKED) {
     return writeInstallRDFToDir(aData, aDir, aId, aExtraFile);
   }
   return writeInstallRDFToXPI(aData, aDir, aId, aExtraFile);
 }
 
 /**
+ * Writes a manifest.json manifest into an extension using the properties passed
+ * in a JS object.
+ *
+ * @param   aManifest
+ *          The data to write
+ * @param   aDir
+ *          The install directory to add the extension to
+ * @param   aId
+ *          An optional string to override the default installation aId
+ * @return  A file pointing to where the extension was installed
+ */
+function writeWebManifestForExtension(aData, aDir, aId = undefined) {
+  if (!aId)
+    aId = aData.applications.gecko.id;
+
+  if (TEST_UNPACKED) {
+    let dir = aDir.clone();
+    dir.append(aId);
+    if (!dir.exists())
+      dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+    let file = dir.clone();
+    file.append("manifest.json");
+    if (file.exists())
+      file.remove(true);
+
+    let data = JSON.stringify(aData);
+    let fos = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+              createInstance(AM_Ci.nsIFileOutputStream);
+    fos.init(file,
+             FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+             FileUtils.PERMS_FILE, 0);
+    fos.write(data, data.length);
+    fos.close();
+
+    return dir;
+  }
+  else {
+    let file = aDir.clone();
+    file.append(aId + ".xpi");
+
+    let stream = AM_Cc["@mozilla.org/io/string-input-stream;1"].
+                 createInstance(AM_Ci.nsIStringInputStream);
+    stream.setData(JSON.stringify(aData), -1);
+    let zipW = AM_Cc["@mozilla.org/zipwriter;1"].
+               createInstance(AM_Ci.nsIZipWriter);
+    zipW.open(file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE);
+    zipW.addEntryStream("manifest.json", 0, AM_Ci.nsIZipWriter.COMPRESSION_NONE,
+                        stream, false);
+    zipW.close();
+
+    return file;
+  }
+}
+
+/**
  * Writes an install.rdf manifest into a packed extension using the properties passed
  * in a JS object. The objects should contain a property for each property to
  * appear in the RDF. The object may contain an array of objects with id,
  * minVersion and maxVersion in the targetApplications property to give target
  * application compatibility.
  *
  * @param   aData
  *          The object holding data about the add-on
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js
@@ -0,0 +1,189 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "webextension1@tests.mozilla.org";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+startupManager();
+
+const { GlobalManager, Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+function promiseAddonStartup() {
+  return new Promise(resolve => {
+    let listener = (extension) => {
+      Management.off("startup", listener);
+      resolve(extension);
+    }
+
+    Management.on("startup", listener);
+  });
+}
+
+add_task(function*() {
+  do_check_eq(GlobalManager.count, 0);
+  do_check_false(GlobalManager.extensionMap.has(ID));
+
+  yield Promise.all([
+    promiseInstallAllFiles([do_get_addon("webextension_1")], true),
+    promiseAddonStartup()
+  ]);
+
+  do_check_eq(GlobalManager.count, 1);
+  do_check_true(GlobalManager.extensionMap.has(ID));
+
+  let addon = yield promiseAddonByID(ID);
+  do_check_neq(addon, null);
+  do_check_eq(addon.version, "1.0");
+  do_check_eq(addon.name, "Web Extension Name");
+  do_check_true(addon.isCompatible);
+  do_check_false(addon.appDisabled);
+  do_check_true(addon.isActive);
+  do_check_eq(addon.type, "extension");
+  do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+  // Should persist through a restart
+  yield promiseShutdownManager();
+
+  do_check_eq(GlobalManager.count, 0);
+  do_check_false(GlobalManager.extensionMap.has(ID));
+
+  startupManager();
+  yield promiseAddonStartup();
+
+  do_check_eq(GlobalManager.count, 1);
+  do_check_true(GlobalManager.extensionMap.has(ID));
+
+  addon = yield promiseAddonByID(ID);
+  do_check_neq(addon, null);
+  do_check_eq(addon.version, "1.0");
+  do_check_eq(addon.name, "Web Extension Name");
+  do_check_true(addon.isCompatible);
+  do_check_false(addon.appDisabled);
+  do_check_true(addon.isActive);
+  do_check_eq(addon.type, "extension");
+  do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+  let file = getFileForAddon(profileDir, ID);
+  do_check_true(file.exists());
+
+  addon.userDisabled = true;
+
+  do_check_eq(GlobalManager.count, 0);
+  do_check_false(GlobalManager.extensionMap.has(ID));
+
+  addon.userDisabled = false;
+  yield promiseAddonStartup();
+
+  do_check_eq(GlobalManager.count, 1);
+  do_check_true(GlobalManager.extensionMap.has(ID));
+
+  addon.uninstall();
+
+  do_check_eq(GlobalManager.count, 0);
+  do_check_false(GlobalManager.extensionMap.has(ID));
+
+  yield promiseShutdownManager();
+});
+
+// Writing the manifest direct to the profile should work
+add_task(function*() {
+  writeWebManifestForExtension({
+    name: "Web Extension Name",
+    version: "1.0",
+    manifest_version: 2,
+    applications: {
+      gecko: {
+        id: ID
+      }
+    }
+  }, profileDir);
+
+  startupManager();
+
+  let addon = yield promiseAddonByID(ID);
+  do_check_neq(addon, null);
+  do_check_eq(addon.version, "1.0");
+  do_check_eq(addon.name, "Web Extension Name");
+  do_check_true(addon.isCompatible);
+  do_check_false(addon.appDisabled);
+  do_check_true(addon.isActive);
+  do_check_eq(addon.type, "extension");
+  do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+  let file = getFileForAddon(profileDir, ID);
+  do_check_true(file.exists());
+
+  addon.uninstall();
+
+  yield promiseRestartManager();
+});
+
+// Missing ID should cause a failure
+add_task(function*() {
+  writeWebManifestForExtension({
+    name: "Web Extension Name",
+    version: "1.0",
+    manifest_version: 2,
+  }, profileDir, ID);
+
+  yield promiseRestartManager();
+
+  let addon = yield promiseAddonByID(ID);
+  do_check_eq(addon, null);
+
+  let file = getFileForAddon(profileDir, ID);
+  do_check_false(file.exists());
+
+  yield promiseRestartManager();
+});
+
+// Missing version should cause a failure
+add_task(function*() {
+  writeWebManifestForExtension({
+    name: "Web Extension Name",
+    manifest_version: 2,
+    applications: {
+      gecko: {
+        id: ID
+      }
+    }
+  }, profileDir);
+
+  yield promiseRestartManager();
+
+  let addon = yield promiseAddonByID(ID);
+  do_check_eq(addon, null);
+
+  let file = getFileForAddon(profileDir, ID);
+  do_check_false(file.exists());
+
+  yield promiseRestartManager();
+});
+
+// Incorrect manifest version should cause a failure
+add_task(function*() {
+  writeWebManifestForExtension({
+    name: "Web Extension Name",
+    version: "1.0",
+    manifest_version: 1,
+    applications: {
+      gecko: {
+        id: ID
+      }
+    }
+  }, profileDir);
+
+  yield promiseRestartManager();
+
+  let addon = yield promiseAddonByID(ID);
+  do_check_eq(addon, null);
+
+  let file = getFileForAddon(profileDir, ID);
+  do_check_false(file.exists());
+
+  yield promiseRestartManager();
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -280,9 +280,10 @@ skip-if = os == "android"
 run-sequentially = Uses global XCurProcD dir.
 [test_upgrade_strictcompat.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 run-sequentially = Uses global XCurProcD dir.
 [test_overrideblocklist.js]
 run-sequentially = Uses global XCurProcD dir.
 [test_sourceURI.js]
+[test_webextension.js]
 [test_bootstrap_globals.js]
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -21,9 +21,10 @@ skip-if = appname != "firefox"
 [test_provider_markSafe.js]
 [test_provider_shutdown.js]
 [test_provider_unsafe_access_shutdown.js]
 [test_provider_unsafe_access_startup.js]
 [test_shutdown.js]
 [test_XPIcancel.js]
 [test_XPIStates.js]
 
+
 [include:xpcshell-shared.ini]
copy from widget/nsIWidget.h
copy to widget/IMEData.h
--- a/widget/nsIWidget.h
+++ b/widget/IMEData.h
@@ -1,250 +1,42 @@
 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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/. */
 
-#ifndef nsIWidget_h__
-#define nsIWidget_h__
+#ifndef mozilla_widget_IMEData_h_
+#define mozilla_widget_IMEData_h_
 
-#include "nsISupports.h"
-#include "nsColor.h"
+#include "nsPoint.h"
 #include "nsRect.h"
 #include "nsStringGlue.h"
 
-#include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
-#include "nsWidgetInitData.h"
-#include "nsTArray.h"
-#include "nsITheme.h"
-#include "nsITimer.h"
-#include "nsXULAppAPI.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/EventForwards.h"
-#include "mozilla/layers/LayersTypes.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/gfx/Point.h"
-#include "nsDataHashtable.h"
-#include "nsIObserver.h"
-#include "FrameMetrics.h"
-#include "Units.h"
-
-// forward declarations
-class   nsIRollupListener;
-class   imgIContainer;
-class   nsIContent;
-class   ViewWrapper;
-class   nsIWidgetListener;
-class   nsIntRegion;
-class   nsIScreen;
-class   nsIRunnable;
-
 namespace mozilla {
-class CompositorVsyncDispatcher;
-class WritingMode;
-namespace dom {
-class TabChild;
-} // namespace dom
-namespace plugins {
-class PluginWidgetChild;
-} // namespace plugins
-namespace layers {
-class Composer2D;
-class Compositor;
-class CompositorChild;
-class LayerManager;
-class LayerManagerComposite;
-class PLayerTransactionChild;
-struct ScrollableLayerGuid;
-} // namespace layers
-namespace gfx {
-class DrawTarget;
-class SourceSurface;
-} // namespace gfx
-namespace widget {
-class TextEventDispatcher;
-} // namespace widget
-} // namespace mozilla
-
-/**
- * Callback function that processes events.
- *
- * The argument is actually a subtype (subclass) of WidgetEvent which carries
- * platform specific information about the event. Platform specific code
- * knows how to deal with it.
- *
- * The return value determines whether or not the default action should take
- * place.
- */
-typedef nsEventStatus (* EVENT_CALLBACK)(mozilla::WidgetGUIEvent* aEvent);
-
-// Hide the native window system's real window type so as to avoid
-// including native window system types and APIs. This is necessary
-// to ensure cross-platform code.
-typedef void* nsNativeWidget;
 
-/**
- * Flags for the GetNativeData and SetNativeData functions
- */
-#define NS_NATIVE_WINDOW      0
-#define NS_NATIVE_GRAPHIC     1
-#define NS_NATIVE_TMP_WINDOW  2
-#define NS_NATIVE_WIDGET      3
-#define NS_NATIVE_DISPLAY     4
-#define NS_NATIVE_REGION      5
-#define NS_NATIVE_OFFSETX     6
-#define NS_NATIVE_OFFSETY     7
-#define NS_NATIVE_PLUGIN_PORT 8
-#define NS_NATIVE_SCREEN      9
-// The toplevel GtkWidget containing this nsIWidget:
-#define NS_NATIVE_SHELLWIDGET 10
-// Has to match to NPNVnetscapeWindow, and shareable across processes
-// HWND on Windows and XID on X11
-#define NS_NATIVE_SHAREABLE_WINDOW 11
-#define NS_NATIVE_OPENGL_CONTEXT   12
-#ifdef XP_MACOSX
-#define NS_NATIVE_PLUGIN_PORT_QD    100
-#define NS_NATIVE_PLUGIN_PORT_CG    101
-#endif
-#ifdef XP_WIN
-#define NS_NATIVE_TSF_THREAD_MGR       100
-#define NS_NATIVE_TSF_CATEGORY_MGR     101
-#define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
-#define NS_NATIVE_ICOREWINDOW          103 // winrt specific
-#define NS_NATIVE_CHILD_WINDOW         104
-#endif
-#if defined(MOZ_WIDGET_GTK)
-// set/get nsPluginNativeWindowGtk, e10s specific
-#define NS_NATIVE_PLUGIN_OBJECT_PTR    104
-#endif
-// See RegisterPluginWindowForRemoteUpdates
-#define NS_NATIVE_PLUGIN_ID            105
-
-#define NS_IWIDGET_IID \
-{ 0x483BF75C, 0xF909, 0x45C3, \
-  { 0x95, 0xBE, 0x41, 0x89, 0xDB, 0xCE, 0x2E, 0x13 } };
-
-/*
- * Window shadow styles
- * Also used for the -moz-window-shadow CSS property
- */
-
-#define NS_STYLE_WINDOW_SHADOW_NONE             0
-#define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
-#define NS_STYLE_WINDOW_SHADOW_MENU             2
-#define NS_STYLE_WINDOW_SHADOW_TOOLTIP          3
-#define NS_STYLE_WINDOW_SHADOW_SHEET            4
-
-/**
- * Transparency modes
- */
-
-enum nsTransparencyMode {
-  eTransparencyOpaque = 0,  // Fully opaque
-  eTransparencyTransparent, // Parts of the window may be transparent
-  eTransparencyGlass,       // Transparent parts of the window have Vista AeroGlass effect applied
-  eTransparencyBorderlessGlass // As above, but without a border around the opaque areas when there would otherwise be one with eTransparencyGlass
-};
-
-/**
- * Cursor types.
- */
+class WritingMode;
 
-enum nsCursor {   ///(normal cursor,       usually rendered as an arrow)
-                eCursor_standard, 
-                  ///(system is busy,      usually rendered as a hourglass or watch)
-                eCursor_wait, 
-                  ///(Selecting something, usually rendered as an IBeam)
-                eCursor_select, 
-                  ///(can hyper-link,      usually rendered as a human hand)
-                eCursor_hyperlink, 
-                  ///(north/south/west/east edge sizing)
-                eCursor_n_resize,
-                eCursor_s_resize,
-                eCursor_w_resize,
-                eCursor_e_resize,
-                  ///(corner sizing)
-                eCursor_nw_resize,
-                eCursor_se_resize,
-                eCursor_ne_resize,
-                eCursor_sw_resize,
-                eCursor_crosshair,
-                eCursor_move,
-                eCursor_help,
-                eCursor_copy, // CSS3
-                eCursor_alias,
-                eCursor_context_menu,
-                eCursor_cell,
-                eCursor_grab,
-                eCursor_grabbing,
-                eCursor_spinning,
-                eCursor_zoom_in,
-                eCursor_zoom_out,
-                eCursor_not_allowed,
-                eCursor_col_resize,
-                eCursor_row_resize,
-                eCursor_no_drop,
-                eCursor_vertical_text,
-                eCursor_all_scroll,
-                eCursor_nesw_resize,
-                eCursor_nwse_resize,
-                eCursor_ns_resize,
-                eCursor_ew_resize,
-                eCursor_none,
-                // This one better be the last one in this list.
-                eCursorCount
-                }; 
-
-enum nsTopLevelWidgetZPlacement { // for PlaceBehind()
-  eZPlacementBottom = 0,  // bottom of the window stack
-  eZPlacementBelow,       // just below another widget
-  eZPlacementTop          // top of the window stack
-};
-
-/**
- * Before the OS goes to sleep, this topic is notified.
- */
-#define NS_WIDGET_SLEEP_OBSERVER_TOPIC "sleep_notification"
-
-/**
- * After the OS wakes up, this topic is notified.
- */
-#define NS_WIDGET_WAKE_OBSERVER_TOPIC "wake_notification"
-
-/**
- * Before the OS suspends the current process, this topic is notified.  Some
- * OS will kill processes that are suspended instead of resuming them.
- * For that reason this topic may be useful to safely close down resources.
- */
-#define NS_WIDGET_SUSPEND_PROCESS_OBSERVER_TOPIC "suspend_process_notification"
-
-/**
- * After the current process resumes from being suspended, this topic is
- * notified.
- */
-#define NS_WIDGET_RESUME_PROCESS_OBSERVER_TOPIC "resume_process_notification"
+} // namespace mozilla
 
 /**
  * Preference for receiving IME updates
  *
  * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
  * change and/or selection change and call nsIWidget::NotifyIME() with
  * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
  * Please note that the text change observing cost is very expensive especially
  * on an HTML editor has focus.
  * If the IME implementation on a particular platform doesn't care about
  * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
  * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
  * If the IME implementation needs notifications even while our process is
  * deactive, it should also set NOTIFY_DURING_DEACTIVE.
  */
-struct nsIMEUpdatePreference {
-
+struct nsIMEUpdatePreference final
+{
   typedef uint8_t Notifications;
 
   enum : Notifications
   {
     NOTIFY_NOTHING                       = 0,
     NOTIFY_SELECTION_CHANGE              = 1 << 0,
     NOTIFY_TEXT_CHANGE                   = 1 << 1,
     NOTIFY_POSITION_CHANGE               = 1 << 2,
@@ -317,35 +109,37 @@ struct nsIMEUpdatePreference {
   {
     return !!(mWantUpdates & NOTIFY_DURING_DEACTIVE);
   }
 
   Notifications mWantUpdates;
 };
 
 
-/* 
+/**
  * Contains IMEStatus plus information about the current 
  * input context that the IME can use as hints if desired.
  */
 
 namespace mozilla {
 namespace widget {
 
-struct IMEState {
+struct IMEState final
+{
   /**
    * IME enabled states, the mEnabled value of
    * SetInputContext()/GetInputContext() should be one value of following
    * values.
    *
    * WARNING: If you change these values, you also need to edit:
    *   nsIDOMWindowUtils.idl
    *   nsContentUtils::GetWidgetStatusFromIMEStatus
    */
-  enum Enabled {
+  enum Enabled
+  {
     /**
      * 'Disabled' means the user cannot use IME. So, the IME open state should
      * be 'closed' during 'disabled'.
      */
     DISABLED,
     /**
      * 'Enabled' means the user can use IME.
      */
@@ -367,17 +161,18 @@ struct IMEState {
   };
   Enabled mEnabled;
 
   /**
    * IME open states the mOpen value of SetInputContext() should be one value of
    * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE.  GetInputContext() should return
    * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED.
    */
-  enum Open {
+  enum Open
+  {
     /**
      * 'Unsupported' means the platform cannot return actual IME open state.
      * This value is used only by GetInputContext().
      */
     OPEN_STATE_NOT_SUPPORTED,
     /**
      * 'Don't change' means the widget shouldn't change IME open state when
      * SetInputContext() is called.
@@ -395,20 +190,25 @@ struct IMEState {
      * as ASCII character inputs on mobile device).  Even if IME is closed by
      * this value, users should be able to open IME by theirselves.
      * Web contents can specify this value by |ime-mode: inactive;|.
      */
     CLOSED
   };
   Open mOpen;
 
-  IMEState() : mEnabled(ENABLED), mOpen(DONT_CHANGE_OPEN_STATE) { }
+  IMEState()
+    : mEnabled(ENABLED)
+    , mOpen(DONT_CHANGE_OPEN_STATE)
+  {
+  }
 
-  explicit IMEState(Enabled aEnabled, Open aOpen = DONT_CHANGE_OPEN_STATE) :
-    mEnabled(aEnabled), mOpen(aOpen)
+  explicit IMEState(Enabled aEnabled, Open aOpen = DONT_CHANGE_OPEN_STATE)
+    : mEnabled(aEnabled)
+    , mOpen(aOpen)
   {
   }
 
   // Returns true if the user can input characters.
   // This means that a plain text editor, an HTML editor, a password editor or
   // a plain text editor whose ime-mode is "disabled".
   bool IsEditable() const
   {
@@ -419,22 +219,24 @@ struct IMEState {
   // a plain text editor whose ime-mode is "disabled" or a windowless plugin
   // has focus.
   bool MaybeEditable() const
   {
     return IsEditable() || mEnabled == PLUGIN;
   }
 };
 
-struct InputContext {
+struct InputContext final
+{
   InputContext()
     : mNativeIMEContext(nullptr)
     , mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT)
     , mMayBeIMEUnaware(false)
-  {}
+  {
+  }
 
   bool IsPasswordEditor() const
   {
     return mHTMLInputType.LowerCaseEqualsLiteral("password");
   }
 
   IMEState mIMEState;
 
@@ -448,17 +250,18 @@ struct InputContext {
   nsString mActionHint;
 
   /* Native IME context for the widget.  This doesn't come from the argument of
      SetInputContext().  If there is only one context in the process, this may
      be nullptr. */
   void* mNativeIMEContext;
 
   /**
-   * mOrigin indicates whether this focus event refers to main or remote content.
+   * mOrigin indicates whether this focus event refers to main or remote
+   * content.
    */
   enum Origin
   {
     // Adjusting focus of content on the main process
     ORIGIN_MAIN,
     // Adjusting focus of content in a remote process
     ORIGIN_CONTENT
   };
@@ -483,96 +286,82 @@ struct InputContext {
   {
     if (XRE_IsParentProcess()) {
       return IsOriginMainProcess();
     }
     return IsOriginContentProcess();
   }
 };
 
-struct InputContextAction {
+struct InputContextAction final
+{
   /**
    * mCause indicates what action causes calling nsIWidget::SetInputContext().
    * It must be one of following values.
    */
-  enum Cause {
+  enum Cause
+  {
     // The cause is unknown but originated from content. Focus might have been
     // changed by content script.
     CAUSE_UNKNOWN,
     // The cause is unknown but originated from chrome. Focus might have been
     // changed by chrome script.
     CAUSE_UNKNOWN_CHROME,
     // The cause is user's keyboard operation.
     CAUSE_KEY,
     // The cause is user's mouse operation.
     CAUSE_MOUSE
   };
   Cause mCause;
 
   /**
    * mFocusChange indicates what happened for focus.
    */
-  enum FocusChange {
+  enum FocusChange
+  {
     FOCUS_NOT_CHANGED,
     // A content got focus.
     GOT_FOCUS,
     // Focused content lost focus.
     LOST_FOCUS,
     // Menu got pseudo focus that means focused content isn't changed but
     // keyboard events will be handled by menu.
     MENU_GOT_PSEUDO_FOCUS,
     // Menu lost pseudo focus that means focused content will handle keyboard
     // events.
     MENU_LOST_PSEUDO_FOCUS
   };
   FocusChange mFocusChange;
 
-  bool ContentGotFocusByTrustedCause() const {
+  bool ContentGotFocusByTrustedCause() const
+  {
     return (mFocusChange == GOT_FOCUS &&
             mCause != CAUSE_UNKNOWN);
   }
 
-  bool UserMightRequestOpenVKB() const {
+  bool UserMightRequestOpenVKB() const
+  {
     return (mFocusChange == FOCUS_NOT_CHANGED &&
             mCause == CAUSE_MOUSE);
   }
 
-  InputContextAction() :
-    mCause(CAUSE_UNKNOWN), mFocusChange(FOCUS_NOT_CHANGED)
+  InputContextAction()
+    : mCause(CAUSE_UNKNOWN)
+    , mFocusChange(FOCUS_NOT_CHANGED)
   {
   }
 
   explicit InputContextAction(Cause aCause,
-                              FocusChange aFocusChange = FOCUS_NOT_CHANGED) :
-    mCause(aCause), mFocusChange(aFocusChange)
+                              FocusChange aFocusChange = FOCUS_NOT_CHANGED)
+    : mCause(aCause)
+    , mFocusChange(aFocusChange)
   {
   }
 };
 
-/**
- * Size constraints for setting the minimum and maximum size of a widget.
- * Values are in device pixels.
- */
-struct SizeConstraints {
-  SizeConstraints()
-    : mMaxSize(NS_MAXSIZE, NS_MAXSIZE)
-  {
-  }
-
-  SizeConstraints(mozilla::LayoutDeviceIntSize aMinSize,
-                  mozilla::LayoutDeviceIntSize aMaxSize)
-  : mMinSize(aMinSize),
-    mMaxSize(aMaxSize)
-  {
-  }
-
-  mozilla::LayoutDeviceIntSize mMinSize;
-  mozilla::LayoutDeviceIntSize mMaxSize;
-};
-
 // IMEMessage is shared by IMEStateManager and TextComposition.
 // Update values in GeckoEditable.java if you make changes here.
 // XXX Negative values are used in Android...
 typedef int8_t IMEMessageType;
 enum IMEMessage : IMEMessageType
 {
   // This is used by IMENotification internally.  This means that the instance
   // hasn't been initialized yet.
@@ -598,17 +387,18 @@ enum IMEMessage : IMEMessageType
   // (some platforms may not support)
   REQUEST_TO_CANCEL_COMPOSITION
 };
 
 struct IMENotification final
 {
   IMENotification()
     : mMessage(NOTIFY_IME_OF_NOTHING)
-  {}
+  {
+  }
 
   IMENotification(const IMENotification& aOther)
   {
     Assign(aOther);
   }
 
   ~IMENotification()
   {
@@ -937,1773 +727,12 @@ struct IMENotification final
       case NOTIFY_IME_OF_TEXT_CHANGE:
         return mTextChangeData.mCausedByComposition;
       default:
         return false;
     }
   }
 };
 
-struct AutoObserverNotifier {
-  AutoObserverNotifier(nsIObserver* aObserver,
-                       const char* aTopic)
-    : mObserver(aObserver)
-    , mTopic(aTopic)
-  {
-  }
-
-  void SkipNotification()
-  {
-    mObserver = nullptr;
-  }
-
-  uint64_t SaveObserver()
-  {
-    if (!mObserver) {
-      return 0;
-    }
-    uint64_t observerId = ++sObserverId;
-    sSavedObservers.Put(observerId, mObserver);
-    SkipNotification();
-    return observerId;
-  }
-
-  ~AutoObserverNotifier()
-  {
-    if (mObserver) {
-      mObserver->Observe(nullptr, mTopic, nullptr);
-    }
-  }
-
-  static void NotifySavedObserver(const uint64_t& aObserverId,
-                                  const char* aTopic)
-  {
-    nsCOMPtr<nsIObserver> observer = sSavedObservers.Get(aObserverId);
-    if (!observer) {
-      MOZ_ASSERT(aObserverId == 0, "We should always find a saved observer for nonzero IDs");
-      return;
-    }
-
-    sSavedObservers.Remove(aObserverId);
-    observer->Observe(nullptr, aTopic, nullptr);
-  }
-
-private:
-  nsCOMPtr<nsIObserver> mObserver;
-  const char* mTopic;
-
-private:
-  static uint64_t sObserverId;
-  static nsDataHashtable<nsUint64HashKey, nsCOMPtr<nsIObserver>> sSavedObservers;
-};
-
 } // namespace widget
 } // namespace mozilla
 
-/**
- * The base class for all the widgets. It provides the interface for
- * all basic and necessary functionality.
- */
-class nsIWidget : public nsISupports {
-  protected:
-    typedef mozilla::dom::TabChild TabChild;
-
-  public:
-    typedef mozilla::layers::Composer2D Composer2D;
-    typedef mozilla::layers::CompositorChild CompositorChild;
-    typedef mozilla::layers::FrameMetrics FrameMetrics;
-    typedef mozilla::layers::LayerManager LayerManager;
-    typedef mozilla::layers::LayerManagerComposite LayerManagerComposite;
-    typedef mozilla::layers::LayersBackend LayersBackend;
-    typedef mozilla::layers::PLayerTransactionChild PLayerTransactionChild;
-    typedef mozilla::layers::ZoomConstraints ZoomConstraints;
-    typedef mozilla::widget::IMEMessage IMEMessage;
-    typedef mozilla::widget::IMENotification IMENotification;
-    typedef mozilla::widget::IMEState IMEState;
-    typedef mozilla::widget::InputContext InputContext;
-    typedef mozilla::widget::InputContextAction InputContextAction;
-    typedef mozilla::widget::SizeConstraints SizeConstraints;
-    typedef mozilla::widget::TextEventDispatcher TextEventDispatcher;
-    typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher;
-
-    // Used in UpdateThemeGeometries.
-    struct ThemeGeometry {
-      // The ThemeGeometryType value for the themed widget, see
-      // nsITheme::ThemeGeometryTypeForWidget.
-      nsITheme::ThemeGeometryType mType;
-      // The device-pixel rect within the window for the themed widget
-      nsIntRect mRect;
-
-      ThemeGeometry(nsITheme::ThemeGeometryType aType, const nsIntRect& aRect)
-        : mType(aType)
-        , mRect(aRect)
-      { }
-    };
-
-    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWIDGET_IID)
-
-    nsIWidget()
-      : mLastChild(nullptr)
-      , mPrevSibling(nullptr)
-      , mOnDestroyCalled(false)
-      , mWindowType(eWindowType_child)
-      , mZIndex(0)
-
-    {
-      ClearNativeTouchSequence(nullptr);
-    }
-
-        
-    /**
-     * Create and initialize a widget. 
-     *
-     * All the arguments can be null in which case a top level window
-     * with size 0 is created. The event callback function has to be
-     * provided only if the caller wants to deal with the events this
-     * widget receives.  The event callback is basically a preprocess
-     * hook called synchronously. The return value determines whether
-     * the event goes to the default window procedure or it is hidden
-     * to the os. The assumption is that if the event handler returns
-     * false the widget does not see the event. The widget should not 
-     * automatically clear the window to the background color. The 
-     * calling code must handle paint messages and clear the background 
-     * itself. 
-     *
-     * In practice at least one of aParent and aNativeParent will be null. If
-     * both are null the widget isn't parented (e.g. context menus or
-     * independent top level windows).
-     *
-     * The dimensions given in aRect are specified in the parent's
-     * coordinate system, or for parentless widgets such as top-level
-     * windows, in global CSS pixels.
-     *
-     * @param     aParent       parent nsIWidget
-     * @param     aNativeParent native parent widget
-     * @param     aRect         the widget dimension
-     * @param     aInitData     data that is used for widget initialization
-     *
-     */
-    NS_IMETHOD Create(nsIWidget        *aParent,
-                      nsNativeWidget   aNativeParent,
-                      const nsIntRect  &aRect,
-                      nsWidgetInitData *aInitData = nullptr) = 0;
-
-    /**
-     * Allocate, initialize, and return a widget that is a child of
-     * |this|.  The returned widget (if nonnull) has gone through the
-     * equivalent of CreateInstance(widgetCID) + Create(...).
-     *
-     * |CreateChild()| lets widget backends decide whether to parent
-     * the new child widget to this, nonnatively parent it, or both.
-     * This interface exists to support the PuppetWidget backend,
-     * which is entirely non-native.  All other params are the same as
-     * for |Create()|.
-     *
-     * |aForceUseIWidgetParent| forces |CreateChild()| to only use the
-     * |nsIWidget*| this, not its native widget (if it exists), when
-     * calling |Create()|.  This is a timid hack around poorly
-     * understood code, and shouldn't be used in new code.
-     */
-    virtual already_AddRefed<nsIWidget>
-    CreateChild(const nsIntRect  &aRect,
-                nsWidgetInitData *aInitData = nullptr,
-                bool             aForceUseIWidgetParent = false) = 0;
-
-    /**
-     * Attach to a top level widget. 
-     *
-     * In cases where a top level chrome widget is being used as a content
-     * container, attach a secondary listener and update the device
-     * context. The primary widget listener will continue to be called for
-     * notifications relating to the top-level window, whereas other
-     * notifications such as painting and events will instead be called via
-     * the attached listener. SetAttachedWidgetListener should be used to
-     * assign the attached listener.
-     *
-     * aUseAttachedEvents if true, events are sent to the attached listener
-     * instead of the normal listener.
-     */
-    NS_IMETHOD AttachViewToTopLevel(bool aUseAttachedEvents) = 0;
-
-    /**
-     * Accessor functions to get and set the attached listener. Used by
-     * nsView in connection with AttachViewToTopLevel above.
-     */
-    virtual void SetAttachedWidgetListener(nsIWidgetListener* aListener) = 0;
-    virtual nsIWidgetListener* GetAttachedWidgetListener() = 0;
-    virtual void SetPreviouslyAttachedWidgetListener(nsIWidgetListener* aListener) = 0;
-    virtual nsIWidgetListener* GetPreviouslyAttachedWidgetListener() = 0;
-
-    /**
-     * Accessor functions to get and set the listener which handles various
-     * actions for the widget.
-     */
-    //@{
-    virtual nsIWidgetListener* GetWidgetListener() = 0;
-    virtual void SetWidgetListener(nsIWidgetListener* alistener) = 0;
-    //@}
-
-    /**
-     * Close and destroy the internal native window. 
-     * This method does not delete the widget.
-     */
-
-    NS_IMETHOD Destroy(void) = 0;
-
-    /**
-     * Destroyed() returns true if Destroy() has been called already.
-     * Otherwise, false.
-     */
-    bool Destroyed() const { return mOnDestroyCalled; }
-
-
-    /**
-     * Reparent a widget
-     *
-     * Change the widget's parent. Null parents are allowed.
-     *
-     * @param     aNewParent   new parent 
-     */
-    NS_IMETHOD SetParent(nsIWidget* aNewParent) = 0;
-
-    /**
-     * Return the parent Widget of this Widget or nullptr if this is a 
-     * top level window
-     *
-     * @return the parent widget or nullptr if it does not have a parent
-     *
-     */
-    virtual nsIWidget* GetParent(void) = 0;
-
-    /**
-     * Return the top level Widget of this Widget
-     *
-     * @return the top level widget
-     */
-    virtual nsIWidget* GetTopLevelWidget() = 0;
-
-    /**
-     * Return the top (non-sheet) parent of this Widget if it's a sheet,
-     * or nullptr if this isn't a sheet (or some other error occurred).
-     * Sheets are only supported on some platforms (currently only OS X).
-     *
-     * @return the top (non-sheet) parent widget or nullptr
-     *
-     */
-    virtual nsIWidget* GetSheetWindowParent(void) = 0;
-
-    /**
-     * Return the physical DPI of the screen containing the window ...
-     * the number of device pixels per inch.
-     */
-    virtual float GetDPI() = 0;
-
-    /**
-     * Returns the CompositorVsyncDispatcher associated with this widget
-     */
-    virtual CompositorVsyncDispatcher* GetCompositorVsyncDispatcher() = 0;
-
-    /**
-     * Return the default scale factor for the window. This is the
-     * default number of device pixels per CSS pixel to use. This should
-     * depend on OS/platform settings such as the Mac's "UI scale factor"
-     * or Windows' "font DPI". This will take into account Gecko preferences
-     * overriding the system setting.
-     */
-    mozilla::CSSToLayoutDeviceScale GetDefaultScale();
-
-    /**
-     * Return the Gecko override of the system default scale, if any;
-     * returns <= 0.0 if the system scale should be used as-is.
-     * nsIWidget::GetDefaultScale() [above] takes this into account.
-     * It is exposed here so that code that wants to check for a
-     * default-scale override without having a widget on hand can
-     * easily access the same value.
-     * Note that any scale override is a browser-wide value, whereas
-     * the default GetDefaultScale value (when no override is present)
-     * may vary between widgets (or screens).
-     */
-    static double DefaultScaleOverride();
-
-    /**
-     * Return the first child of this widget.  Will return null if
-     * there are no children.
-     */
-    nsIWidget* GetFirstChild() const {
-        return mFirstChild;
-    }
-    
-    /**
-     * Return the last child of this widget.  Will return null if
-     * there are no children.
-     */
-    nsIWidget* GetLastChild() const {
-        return mLastChild;
-    }
-
-    /**
-     * Return the next sibling of this widget
-     */
-    nsIWidget* GetNextSibling() const {
-        return mNextSibling;
-    }
-    
-    /**
-     * Set the next sibling of this widget
-     */
-    void SetNextSibling(nsIWidget* aSibling) {
-        mNextSibling = aSibling;
-    }
-    
-    /**
-     * Return the previous sibling of this widget
-     */
-    nsIWidget* GetPrevSibling() const {
-        return mPrevSibling;
-    }
-
-    /**
-     * Set the previous sibling of this widget
-     */
-    void SetPrevSibling(nsIWidget* aSibling) {
-        mPrevSibling = aSibling;
-    }
-
-    /**
-     * Show or hide this widget
-     *
-     * @param aState true to show the Widget, false to hide it
-     *
-     */
-    NS_IMETHOD Show(bool aState) = 0;
-
-    /**
-     * Make the window modal
-     *
-     */
-    NS_IMETHOD SetModal(bool aModal) = 0;
-
-    /**
-     * The maximum number of simultaneous touch contacts supported by the device.
-     * In the case of devices with multiple digitizers (e.g. multiple touch screens),
-     * the value will be the maximum of the set of maximum supported contacts by
-     * each individual digitizer.
-     */
-    virtual uint32_t GetMaxTouchPoints() const = 0;
-
-    /**
-     * Returns whether the window is visible
-     *
-     */
-    virtual bool IsVisible() const = 0;
-
-    /**
-     * Perform platform-dependent sanity check on a potential window position.
-     * This is guaranteed to work only for top-level windows.
-     *
-     * @param aAllowSlop: if true, allow the window to slop offscreen;
-     *                    the window should be partially visible. if false,
-     *                    force the entire window onscreen (or at least
-     *                    the upper-left corner, if it's too large).
-     * @param aX in: an x position expressed in screen coordinates.
-     *           out: the x position constrained to fit on the screen(s).
-     * @param aY in: an y position expressed in screen coordinates.
-     *           out: the y position constrained to fit on the screen(s).
-     * @return vapid success indication. but see also the parameters.
-     *
-     **/
-    NS_IMETHOD ConstrainPosition(bool aAllowSlop,
-                                 int32_t *aX,
-                                 int32_t *aY) = 0;
-
-    /**
-     * NOTE:
-     *
-     * For a top-level window widget, the "parent's coordinate system" is the
-     * "global" display pixel coordinate space, *not* device pixels (which
-     * may be inconsistent between multiple screens, at least in the Mac OS
-     * case with mixed hi-dpi and lo-dpi displays). This applies to all the
-     * following Move and Resize widget APIs.
-     *
-     * The display-/device-pixel distinction becomes important for (at least)
-     * Mac OS X with Hi-DPI (retina) displays, and Windows when the UI scale
-     * factor is set to other than 100%.
-     *
-     * The Move and Resize methods take floating-point parameters, rather than
-     * integer ones. This is important when manipulating top-level widgets,
-     * where the coordinate system may not be an integral multiple of the
-     * device-pixel space.
-     **/
-
-    /**
-     * Move this widget.
-     *
-     * Coordinates refer to the top-left of the widget.  For toplevel windows
-     * with decorations, this is the top-left of the titlebar and frame .
-     *
-     * @param aX the new x position expressed in the parent's coordinate system
-     * @param aY the new y position expressed in the parent's coordinate system
-     *
-     **/
-    NS_IMETHOD Move(double aX, double aY) = 0;
-
-    /**
-     * Reposition this widget so that the client area has the given offset.
-     *
-     * @param aX       the new x offset of the client area expressed as an
-     *                 offset from the origin of the client area of the parent
-     *                 widget (for root widgets and popup widgets it is in
-     *                 screen coordinates)
-     * @param aY       the new y offset of the client area expressed as an
-     *                 offset from the origin of the client area of the parent
-     *                 widget (for root widgets and popup widgets it is in
-     *                 screen coordinates)
-     *
-     **/
-    NS_IMETHOD MoveClient(double aX, double aY) = 0;
-
-    /**
-     * Resize this widget. Any size constraints set for the window by a
-     * previous call to SetSizeConstraints will be applied.
-     *
-     * @param aWidth  the new width expressed in the parent's coordinate system
-     * @param aHeight the new height expressed in the parent's coordinate system
-     * @param aRepaint whether the widget should be repainted
-     *
-     */
-    NS_IMETHOD Resize(double aWidth,
-                      double aHeight,
-                      bool   aRepaint) = 0;
-
-    /**
-     * Move or resize this widget. Any size constraints set for the window by
-     * a previous call to SetSizeConstraints will be applied.
-     *
-     * @param aX       the new x position expressed in the parent's coordinate system
-     * @param aY       the new y position expressed in the parent's coordinate system
-     * @param aWidth   the new width expressed in the parent's coordinate system
-     * @param aHeight  the new height expressed in the parent's coordinate system
-     * @param aRepaint whether the widget should be repainted if the size changes
-     *
-     */
-    NS_IMETHOD Resize(double aX,
-                      double aY,
-                      double aWidth,
-                      double aHeight,
-                      bool   aRepaint) = 0;
-
-    /**
-     * Resize the widget so that the inner client area has the given size.
-     *
-     * @param aWidth   the new width of the client area.
-     * @param aHeight  the new height of the client area.
-     * @param aRepaint whether the widget should be repainted
-     *
-     */
-    NS_IMETHOD ResizeClient(double aWidth,
-                            double aHeight,
-                            bool   aRepaint) = 0;
-
-    /**
-     * Resize and reposition the widget so tht inner client area has the given
-     * offset and size.
-     *
-     * @param aX       the new x offset of the client area expressed as an
-     *                 offset from the origin of the client area of the parent
-     *                 widget (for root widgets and popup widgets it is in
-     *                 screen coordinates)
-     * @param aY       the new y offset of the client area expressed as an
-     *                 offset from the origin of the client area of the parent
-     *                 widget (for root widgets and popup widgets it is in
-     *                 screen coordinates)
-     * @param aWidth   the new width of the client area.
-     * @param aHeight  the new height of the client area.
-     * @param aRepaint whether the widget should be repainted
-     *
-     */
-    NS_IMETHOD ResizeClient(double aX,
-                            double aY,
-                            double aWidth,
-                            double aHeight,
-                            bool   aRepaint) = 0;
-
-    /**
-     * Sets the widget's z-index.
-     */
-    virtual void SetZIndex(int32_t aZIndex) = 0;
-
-    /**
-     * Gets the widget's z-index. 
-     */
-    int32_t GetZIndex()
-    {
-      return mZIndex;
-    }
-
-    /**
-     * Position this widget just behind the given widget. (Used to
-     * control z-order for top-level widgets. Get/SetZIndex by contrast
-     * control z-order for child widgets of other widgets.)
-     * @param aPlacement top, bottom, or below a widget
-     *                   (if top or bottom, param aWidget is ignored)
-     * @param aWidget    widget to place this widget behind
-     *                   (only if aPlacement is eZPlacementBelow).
-     *                   null is equivalent to aPlacement of eZPlacementTop
-     * @param aActivate  true to activate the widget after placing it
-     */
-    NS_IMETHOD PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
-                           nsIWidget *aWidget, bool aActivate) = 0;
-
-    /**
-     * Minimize, maximize or normalize the window size.
-     * Takes a value from nsSizeMode (see nsIWidgetListener.h)
-     */
-    NS_IMETHOD SetSizeMode(int32_t aMode) = 0;
-
-    /**
-     * Return size mode (minimized, maximized, normalized).
-     * Returns a value from nsSizeMode (see nsIWidgetListener.h)
-     */
-    virtual int32_t SizeMode() = 0;
-
-    /**
-     * Enable or disable this Widget
-     *
-     * @param aState true to enable the Widget, false to disable it.
-     *
-     */
-    NS_IMETHOD Enable(bool aState) = 0;
-
-    /**
-     * Ask whether the widget is enabled
-     */
-    virtual bool IsEnabled() const = 0;
-
-    /**
-     * Request activation of this window or give focus to this widget.
-     *
-     * @param aRaise If true, this function requests activation of this
-     *               widget's toplevel window.
-     *               If false, the appropriate toplevel window (which in
-     *               the case of popups may not be this widget's toplevel
-     *               window) is already active.
-     */
-    NS_IMETHOD SetFocus(bool aRaise = false) = 0;
-
-    /**
-     * Get this widget's outside dimensions relative to its parent widget. For
-     * popup widgets the returned rect is in screen coordinates and not
-     * relative to its parent widget.
-     *
-     * @param aRect   On return it holds the  x, y, width and height of
-     *                this widget.
-     */
-    NS_IMETHOD GetBounds(nsIntRect &aRect) = 0;
-
-    /**
-     * Get this widget's outside dimensions in global coordinates. This
-     * includes any title bar on the window.
-     *
-     * @param aRect   On return it holds the  x, y, width and height of
-     *                this widget.
-     */
-    NS_IMETHOD GetScreenBounds(nsIntRect &aRect) = 0;
-
-    /**
-     * Similar to GetScreenBounds except that this function will always
-     * get the size when the widget is in the nsSizeMode_Normal size mode
-     * even if the current size mode is not nsSizeMode_Normal.
-     * This method will fail if the size mode is not nsSizeMode_Normal and
-     * the platform doesn't have the ability.
-     * This method will always succeed if the current size mode is
-     * nsSizeMode_Normal.
-     *
-     * @param aRect   On return it holds the  x, y, width and height of
-     *                this widget.
-     */
-    NS_IMETHOD GetRestoredBounds(nsIntRect &aRect) = 0;
-
-    /**
-     * Get this widget's client area bounds, if the window has a 3D border
-     * appearance this returns the area inside the border. The position is the
-     * position of the client area relative to the client area of the parent
-     * widget (for root widgets and popup widgets it is in screen coordinates).
-     *
-     * @param aRect   On return it holds the  x. y, width and height of
-     *                the client area of this widget.
-     */
-    NS_IMETHOD GetClientBounds(nsIntRect &aRect) = 0;
-
-    /**
-     * Get the non-client area dimensions of the window.
-     * 
-     */
-    NS_IMETHOD GetNonClientMargins(nsIntMargin &margins) = 0;
-
-    /**
-     * Sets the non-client area dimensions of the window. Pass -1 to restore
-     * the system default frame size for that border. Pass zero to remove
-     * a border, or pass a specific value adjust a border. Units are in
-     * pixels. (DPI dependent)
-     *
-     * Platform notes:
-     *  Windows: shrinking top non-client height will remove application
-     *  icon and window title text. Glass desktops will refuse to set
-     *  dimensions between zero and size < system default.
-     *
-     */
-    NS_IMETHOD SetNonClientMargins(nsIntMargin &margins) = 0;
-
-    /**
-     * Get the client offset from the window origin.
-     *
-     * @return the x and y of the offset.
-     *
-     */
-    virtual nsIntPoint GetClientOffset() = 0;
-
-
-    /**
-     * Equivalent to GetClientBounds but only returns the size.
-     */
-    virtual mozilla::gfx::IntSize GetClientSize() {
-      // Dependeing on the backend, overloading this method may be useful if
-      // if requesting the client offset is expensive.
-      nsIntRect rect;
-      GetClientBounds(rect);
-      return mozilla::gfx::IntSize(rect.width, rect.height);
-    }
-
-    /**
-     * Set the background color for this widget
-     *
-     * @param aColor the new background color
-     *
-     */
-
-    virtual void SetBackgroundColor(const nscolor &aColor) { }
-
-    /**
-     * Get the cursor for this widget.
-     *
-     * @return this widget's cursor.
-     */
-
-    virtual nsCursor GetCursor(void) = 0;
-
-    /**
-     * Set the cursor for this widget
-     *
-     * @param aCursor the new cursor for this widget
-     */
-
-    NS_IMETHOD SetCursor(nsCursor aCursor) = 0;
-
-    /**
-     * If a cursor type is currently cached locally for this widget, clear the
-     * cached cursor to force an update on the next SetCursor call.
-     */
-
-    virtual void ClearCachedCursor() = 0;
-
-    /**
-     * Sets an image as the cursor for this widget.
-     *
-     * @param aCursor the cursor to set
-     * @param aX the X coordinate of the hotspot (from left).
-     * @param aY the Y coordinate of the hotspot (from top).
-     * @retval NS_ERROR_NOT_IMPLEMENTED if setting images as cursors is not
-     *         supported
-     */
-    NS_IMETHOD SetCursor(imgIContainer* aCursor,
-                         uint32_t aHotspotX, uint32_t aHotspotY) = 0;
-
-    /** 
-     * Get the window type of this widget.
-     */
-    nsWindowType WindowType() { return mWindowType; }
-
-    /**
-     * Determines if this widget is one of the three types of plugin widgets.
-     */
-    bool IsPlugin() {
-      return mWindowType == eWindowType_plugin ||
-             mWindowType == eWindowType_plugin_ipc_chrome ||
-             mWindowType == eWindowType_plugin_ipc_content;
-    }
-
-    /**
-     * Set the transparency mode of the top-level window containing this widget.
-     * So, e.g., if you call this on the widget for an IFRAME, the top level
-     * browser window containing the IFRAME actually gets set. Be careful.
-     *
-     * This can fail if the platform doesn't support
-     * transparency/glass. By default widgets are not
-     * transparent.  This will also fail if the toplevel window is not
-     * a Mozilla window, e.g., if the widget is in an embedded
-     * context.
-     *
-     * After transparency/glass has been enabled, the initial alpha channel
-     * value for all pixels is 1, i.e., opaque.
-     * If the window is resized then the alpha channel values for
-     * all pixels are reset to 1.
-     * Pixel RGB color values are already premultiplied with alpha channel values.
-     */
-    virtual void SetTransparencyMode(nsTransparencyMode aMode) = 0;
-
-    /**
-     * Get the transparency mode of the top-level window that contains this
-     * widget.
-     */
-    virtual nsTransparencyMode GetTransparencyMode() = 0;
-
-    /**
-     * This represents a command to set the bounds and clip region of
-     * a child widget.
-     */
-    struct Configuration {
-        nsCOMPtr<nsIWidget> mChild;
-        uintptr_t mWindowID; // e10s specific, the unique plugin port id
-        bool mVisible; // e10s specific, widget visibility
-        nsIntRect mBounds;
-        nsTArray<nsIntRect> mClipRegion;
-    };
-
-    /**
-     * Sets the clip region of each mChild (which must actually be a child
-     * of this widget) to the union of the pixel rects given in
-     * mClipRegion, all relative to the top-left of the child
-     * widget. Clip regions are not implemented on all platforms and only
-     * need to actually work for children that are plugins.
-     *
-     * Also sets the bounds of each child to mBounds.
-     *
-     * This will invalidate areas of the children that have changed, but
-     * does not need to invalidate any part of this widget.
-     *
-     * Children should be moved in the order given; the array is
-     * sorted so to minimize unnecessary invalidation if children are
-     * moved in that order.
-     */
-    virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) = 0;
-    virtual nsresult SetWindowClipRegion(const nsTArray<nsIntRect>& aRects,
-                                         bool aIntersectWithExisting) = 0;
-
-    /**
-     * Appends to aRects the rectangles constituting this widget's clip
-     * region. If this widget is not clipped, appends a single rectangle
-     * (0, 0, bounds.width, bounds.height).
-     */
-    virtual void GetWindowClipRegion(nsTArray<nsIntRect>* aRects) = 0;
-
-    /**
-     * Register or unregister native plugin widgets which receive Configuration
-     * data from the content process via the compositor.
-     *
-     * Lookups are used by the main thread via the compositor to lookup widgets
-     * based on a unique window id. On Windows and Linux this is the
-     * NS_NATIVE_PLUGIN_PORT (hwnd/XID). This tracking maintains a reference to
-     * widgets held. Consumers are responsible for removing widgets from this
-     * list.
-     */
-    virtual void RegisterPluginWindowForRemoteUpdates() = 0;
-    virtual void UnregisterPluginWindowForRemoteUpdates() = 0;
-    static nsIWidget* LookupRegisteredPluginWindow(uintptr_t aWindowID);
-
-    /**
-     * Iterates across the list of registered plugin widgets and updates thier
-     * visibility based on which plugins are included in the 'visible' list.
-     *
-     * The compositor knows little about tabs, but it does know which plugin
-     * widgets are currently included in the visible layer tree. It calls this
-     * helper to hide widgets it knows nothing about.
-     */
-    static void UpdateRegisteredPluginWindowVisibility(uintptr_t aOwnerWidget,
-                                                       nsTArray<uintptr_t>& aVisibleList);
-
-    /**
-     * Set the shadow style of the window.
-     *
-     * Ignored on child widgets and on non-Mac platforms.
-     */
-    NS_IMETHOD SetWindowShadowStyle(int32_t aStyle) = 0;
-
-    /*
-     * On Mac OS X, this method shows or hides the pill button in the titlebar
-     * that's used to collapse the toolbar.
-     *
-     * Ignored on child widgets and on non-Mac platforms.
-     */
-    virtual void SetShowsToolbarButton(bool aShow) = 0;
-
-    /*
-     * On Mac OS X Lion, this method shows or hides the full screen button in
-     * the titlebar that handles native full screen mode.
-     *
-     * Ignored on child widgets, non-Mac platforms, & pre-Lion Mac.
-     */
-    virtual void SetShowsFullScreenButton(bool aShow) = 0;
-
-    enum WindowAnimationType {
-      eGenericWindowAnimation,
-      eDocumentWindowAnimation
-    };
-
-    /**
-     * Sets the kind of top-level window animation this widget should have.  On
-     * Mac OS X, this causes a particular kind of animation to be shown when the
-     * window is first made visible.
-     *
-     * Ignored on child widgets and on non-Mac platforms.
-     */
-    virtual void SetWindowAnimationType(WindowAnimationType aType) = 0;
-
-    /**
-     * Specifies whether the window title should be drawn even if the window
-     * contents extend into the titlebar. Ignored on windows that don't draw
-     * in the titlebar. Only implemented on OS X.
-     */
-    virtual void SetDrawsTitle(bool aDrawTitle) {}
-
-    /**
-     * Indicates whether the widget should attempt to make titlebar controls
-     * easier to see on dark titlebar backgrounds.
-     */
-    virtual void SetUseBrightTitlebarForeground(bool aBrightForeground) {}
-
-    /** 
-     * Hide window chrome (borders, buttons) for this widget.
-     *
-     */
-    NS_IMETHOD HideWindowChrome(bool aShouldHide) = 0;
-
-    enum FullscreenTransitionStage
-    {
-      eBeforeFullscreenToggle,
-      eAfterFullscreenToggle
-    };
-
-    /**
-     * Prepares for fullscreen transition and returns whether the widget
-     * supports fullscreen transition. If this method returns false,
-     * PerformFullscreenTransition() must never be called. Otherwise,
-     * caller should call that method twice with "before" and "after"
-     * stages respectively in order. In the latter case, this method may
-     * return some data via aData pointer. Caller must pass that data to
-     * PerformFullscreenTransition() if any, and caller is responsible
-     * for releasing that data.
-     */
-    virtual bool PrepareForFullscreenTransition(nsISupports** aData) = 0;
-
-    /**
-     * Performs fullscreen transition. This method returns immediately,
-     * and will post aCallback to the main thread when the transition
-     * finishes.
-     */
-    virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage,
-                                             uint16_t aDuration,
-                                             nsISupports* aData,
-                                             nsIRunnable* aCallback) = 0;
-
-    /**
-     * Put the toplevel window into or out of fullscreen mode.
-     * If aTargetScreen is given, attempt to go fullscreen on that screen,
-     * if possible.  (If not, it behaves as if aTargetScreen is null.)
-     * If !aFullScreen, aTargetScreen is ignored.
-     * aTargetScreen support is currently only implemented on Windows.
-     */
-    NS_IMETHOD MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen = nullptr) = 0;
-
-    /**
-     * Same as MakeFullScreen, except that, on systems which natively
-     * support fullscreen transition, calling this method explicitly
-     * requests that behavior.
-     * It is currently only supported on OS X 10.7+.
-     */
-    NS_IMETHOD MakeFullScreenWithNativeTransition(
-      bool aFullScreen, nsIScreen* aTargetScreen = nullptr)
-    {
-      return MakeFullScreen(aFullScreen, aTargetScreen);
-    }
-
-    /**
-     * Invalidate a specified rect for a widget so that it will be repainted
-     * later.
-     */
-    NS_IMETHOD Invalidate(const nsIntRect & aRect) = 0;
-
-    enum LayerManagerPersistence
-    {
-      LAYER_MANAGER_CURRENT = 0,
-      LAYER_MANAGER_PERSISTENT
-    };
-
-    /**
-     * Return the widget's LayerManager. The layer tree for that
-     * LayerManager is what gets rendered to the widget.
-     *
-     * @param aAllowRetaining an outparam that states whether the returned
-     * layer manager should be used for retained layers
-     */
-    inline LayerManager* GetLayerManager(bool* aAllowRetaining = nullptr)
-    {
-        return GetLayerManager(nullptr, mozilla::layers::LayersBackend::LAYERS_NONE,
-                               LAYER_MANAGER_CURRENT, aAllowRetaining);
-    }
-
-    inline LayerManager* GetLayerManager(LayerManagerPersistence aPersistence,
-                                         bool* aAllowRetaining = nullptr)
-    {
-        return GetLayerManager(nullptr, mozilla::layers::LayersBackend::LAYERS_NONE,
-                               aPersistence, aAllowRetaining);
-    }
-
-    /**
-     * Like GetLayerManager(), but prefers creating a layer manager of
-     * type |aBackendHint| instead of what would normally be created.
-     * LayersBackend::LAYERS_NONE means "no hint".
-     */
-    virtual LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager,
-                                          LayersBackend aBackendHint,
-                                          LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
-                                          bool* aAllowRetaining = nullptr) = 0;
-
-    /**
-     * Called before each layer manager transaction to allow any preparation
-     * for DrawWindowUnderlay/Overlay that needs to be on the main thread.
-     *
-     * Always called on the main thread.
-     */
-    virtual void PrepareWindowEffects() = 0;
-
-    /**
-     * Called when shutting down the LayerManager to clean-up any cached resources.
-     *
-     * Always called from the compositing thread, which may be the main-thread if
-     * OMTC is not enabled.
-     */
-    virtual void CleanupWindowEffects() = 0;
-
-    /**
-     * Called before rendering using OMTC. Returns false when the widget is
-     * not ready to be rendered (for example while the window is closed).
-     *
-     * Always called from the compositing thread, which may be the main-thread if
-     * OMTC is not enabled.
-     */
-    virtual bool PreRender(LayerManagerComposite* aManager) = 0;
-
-    /**
-     * Called after rendering using OMTC. Not called when rendering was
-     * cancelled by a negative return value from PreRender.
-     *
-     * Always called from the compositing thread, which may be the main-thread if
-     * OMTC is not enabled.
-     */
-    virtual void PostRender(LayerManagerComposite* aManager) = 0;
-
-    /**
-     * Called before the LayerManager draws the layer tree.
-     *
-     * Always called from the compositing thread.
-     */
-    virtual void DrawWindowUnderlay(LayerManagerComposite* aManager, nsIntRect aRect) = 0;
-
-    /**
-     * Called after the LayerManager draws the layer tree
-     *
-     * Always called from the compositing thread.
-     */
-    virtual void DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect) = 0;
-
-    /**
-     * Return a DrawTarget for the window which can be composited into.
-     *
-     * Called by BasicCompositor on the compositor thread for OMTC drawing
-     * before each composition.
-     */
-    virtual already_AddRefed<mozilla::gfx::DrawTarget> StartRemoteDrawing() = 0;
-
-    /**
-     * Ensure that what was painted into the DrawTarget returned from
-     * StartRemoteDrawing reaches the screen.
-     *
-     * Called by BasicCompositor on the compositor thread for OMTC drawing
-     * after each composition.
-     */
-    virtual void EndRemoteDrawing() = 0;
-    virtual void EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget, nsIntRegion& aInvalidRegion) {
-      EndRemoteDrawing();
-    }
-
-    /**
-     * A hook for the widget to prepare a Compositor, during the latter's initialization.
-     *
-     * If this method returns true, it means that the widget will be able to
-     * present frames from the compoositor.
-     * Returning false will cause the compositor's initialization to fail, and
-     * a different compositor backend will be used (if any).
-     */
-    virtual bool InitCompositor(mozilla::layers::Compositor*) { return true; }
-
-    /**
-     * Clean up any resources used by Start/EndRemoteDrawing.
-     *
-     * Called by BasicCompositor on the compositor thread for OMTC drawing
-     * when the compositor is destroyed.
-     */
-    virtual void CleanupRemoteDrawing() = 0;
-
-    /**
-     * Called when Gecko knows which themed widgets exist in this window.
-     * The passed array contains an entry for every themed widget of the right
-     * type (currently only NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR and
-     * NS_THEME_TOOLBAR) within the window, except for themed widgets which are
-     * transformed or have effects applied to them (e.g. CSS opacity or
-     * filters).
-     * This could sometimes be called during display list construction
-     * outside of painting.
-     * If called during painting, it will be called before we actually
-     * paint anything.
-     */
-    virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) = 0;
-
-    /**
-     * Informs the widget about the region of the window that is opaque.
-     *
-     * @param aOpaqueRegion the region of the window that is opaque.
-     */
-    virtual void UpdateOpaqueRegion(const nsIntRegion &aOpaqueRegion) {}
-
-    /**
-     * Informs the widget about the region of the window that is draggable.
-     */
-    virtual void UpdateWindowDraggingRegion(const nsIntRegion& aRegion) {}
-
-    /**
-     * Internal methods
-     */
-
-    //@{
-    virtual void AddChild(nsIWidget* aChild) = 0;
-    virtual void RemoveChild(nsIWidget* aChild) = 0;
-    virtual void* GetNativeData(uint32_t aDataType) = 0;
-    virtual void SetNativeData(uint32_t aDataType, uintptr_t aVal) = 0;
-    virtual void FreeNativeData(void * data, uint32_t aDataType) = 0;//~~~
-
-    //@}
-
-    /**
-     * Set the widget's title.
-     * Must be called after Create.
-     *
-     * @param aTitle string displayed as the title of the widget
-     */
-
-    NS_IMETHOD SetTitle(const nsAString& aTitle) = 0;
-
-    /**
-     * Set the widget's icon.
-     * Must be called after Create.
-     *
-     * @param anIconSpec string specifying the icon to use; convention is to pass
-     *                   a resource: URL from which a platform-dependent resource
-     *                   file name will be constructed
-     */
-
-    NS_IMETHOD SetIcon(const nsAString& anIconSpec) = 0;
-
-    /**
-     * Return this widget's origin in screen coordinates. The untyped version
-     * exists temporarily to ease conversion to typed coordinates.
-     *
-     * @return screen coordinates stored in the x,y members
-     */
-
-    virtual mozilla::LayoutDeviceIntPoint WidgetToScreenOffset() = 0;
-    virtual nsIntPoint WidgetToScreenOffsetUntyped() {
-      return mozilla::LayoutDeviceIntPoint::ToUntyped(WidgetToScreenOffset());
-    }
-
-    /**
-     * Given the specified client size, return the corresponding window size,
-     * which includes the area for the borders and titlebar. This method
-     * should work even when the window is not yet visible.
-     */
-    virtual mozilla::LayoutDeviceIntSize ClientToWindowSize(
-                const mozilla::LayoutDeviceIntSize& aClientSize) = 0;
-
-    /**
-     * Dispatches an event to the widget
-     *
-     */
-    NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* event,
-                             nsEventStatus & aStatus) = 0;
-
-    /**
-     * Dispatches an event that must be handled by APZ first, when APZ is
-     * enabled. If invoked in the child process, it is forwarded to the
-     * parent process synchronously.
-     */
-    virtual nsEventStatus DispatchAPZAwareEvent(mozilla::WidgetInputEvent* aEvent) = 0;
-
-    /**
-     * Dispatches an event that must be transformed by APZ first, but is not
-     * actually handled by APZ. If invoked in the child process, it is
-     * forwarded to the parent process synchronously.
-     */
-    virtual nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent) = 0;
-
-    /**
-     * Confirm an APZ-aware event target. This should be used when APZ will
-     * not need a layers update to process the event.
-     */
-    virtual void SetConfirmedTargetAPZC(uint64_t aInputBlockId,
-                                        const nsTArray<mozilla::layers::ScrollableLayerGuid>& aTargets) const = 0;
-
-    /**
-     * Returns true if APZ is in use, false otherwise.
-     */
-    virtual bool AsyncPanZoomEnabled() const = 0;
-
-    /**
-     * Enables the dropping of files to a widget (XXX this is temporary)
-     *
-     */
-    NS_IMETHOD EnableDragDrop(bool aEnable) = 0;
-   
-    /**
-     * Enables/Disables system mouse capture.
-     * @param aCapture true enables mouse capture, false disables mouse capture 
-     *
-     */
-    NS_IMETHOD CaptureMouse(bool aCapture) = 0;
-
-    /**
-     * Classify the window for the window manager. Mostly for X11.
-     */
-    NS_IMETHOD SetWindowClass(const nsAString& xulWinType) = 0;
-
-    /**
-     * Enables/Disables system capture of any and all events that would cause a
-     * popup to be rolled up. aListener should be set to a non-null value for
-     * any popups that are not managed by the popup manager.
-     * @param aDoCapture true enables capture, false disables capture 
-     *
-     */
-    NS_IMETHOD CaptureRollupEvents(nsIRollupListener* aListener, bool aDoCapture) = 0;
-
-    /**
-     * Bring this window to the user's attention.  This is intended to be a more
-     * gentle notification than popping the window to the top or putting up an
-     * alert.  See, for example, Win32 FlashWindow or the NotificationManager on
-     * the Mac.  The notification should be suppressed if the window is already
-     * in the foreground and should be dismissed when the user brings this window
-     * to the foreground.
-     * @param aCycleCount Maximum number of times to animate the window per system 
-     *                    conventions. If set to -1, cycles indefinitely until 
-     *                    window is brought into the foreground.
-     */
-    NS_IMETHOD GetAttention(int32_t aCycleCount) = 0;
-
-    /**
-     * Ask whether there user input events pending.  All input events are
-     * included, including those not targeted at this nsIwidget instance.
-     */
-    virtual bool HasPendingInputEvent() = 0;
-
-    /**
-     * Set the background color of the window titlebar for this widget. On Mac,
-     * for example, this will remove the grey gradient and bottom border and
-     * instead show a single, solid color.
-     *
-     * Ignored on any platform that does not support it. Ignored by widgets that
-     * do not represent windows.
-     *
-     * @param aColor  The color to set the title bar background to. Alpha values 
-     *                other than fully transparent (0) are respected if possible  
-     *                on the platform. An alpha of 0 will cause the window to 
-     *                draw with the default style for the platform.
-     *
-     * @param aActive Whether the color should be applied to active or inactive
-     *                windows.
-     */
-    NS_IMETHOD SetWindowTitlebarColor(nscolor aColor, bool aActive) = 0;
-
-    /**
-     * If set to true, the window will draw its contents into the titlebar
-     * instead of below it.
-     *
-     * Ignored on any platform that does not support it. Ignored by widgets that
-     * do not represent windows.
-     * May result in a resize event, so should only be called from places where
-     * reflow and painting is allowed.
-     *
-     * @param aState Whether drawing into the titlebar should be activated.
-     */
-    virtual void SetDrawsInTitlebar(bool aState) = 0;
-
-    /*
-     * Determine whether the widget shows a resize widget. If it does,
-     * aResizerRect returns the resizer's rect.
-     *
-     * Returns false on any platform that does not support it.
-     *
-     * @param aResizerRect The resizer's rect in device pixels.
-     * @return Whether a resize widget is shown.
-     */
-    virtual bool ShowsResizeIndicator(nsIntRect* aResizerRect) = 0;
-
-    /**
-     * Return the popup that was last rolled up, or null if there isn't one.
-     */
-    virtual nsIContent* GetLastRollup() = 0;
-
-    /**
-     * Begin a window resizing drag, based on the event passed in.
-     */
-    NS_IMETHOD BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
-                               int32_t aHorizontal,
-                               int32_t aVertical) = 0;
-
-    /**
-     * Begin a window moving drag, based on the event passed in.
-     */
-    NS_IMETHOD BeginMoveDrag(mozilla::WidgetMouseEvent* aEvent) = 0;
-
-    enum Modifiers {
-        CAPS_LOCK = 0x01, // when CapsLock is active
-        NUM_LOCK = 0x02, // when NumLock is active
-        SHIFT_L = 0x0100,
-        SHIFT_R = 0x0200,
-        CTRL_L = 0x0400,
-        CTRL_R = 0x0800,
-        ALT_L = 0x1000, // includes Option
-        ALT_R = 0x2000,
-        COMMAND_L = 0x4000,
-        COMMAND_R = 0x8000,
-        HELP = 0x10000,
-        FUNCTION = 0x100000,
-        NUMERIC_KEY_PAD = 0x01000000 // when the key is coming from the keypad
-    };
-    /**
-     * Utility method intended for testing. Dispatches native key events
-     * to this widget to simulate the press and release of a key.
-     * @param aNativeKeyboardLayout a *platform-specific* constant.
-     * On Mac, this is the resource ID for a 'uchr' or 'kchr' resource.
-     * On Windows, it is converted to a hex string and passed to
-     * LoadKeyboardLayout, see
-     * http://msdn.microsoft.com/en-us/library/ms646305(VS.85).aspx
-     * @param aNativeKeyCode a *platform-specific* keycode.
-     * On Windows, this is the virtual key code.
-     * @param aModifiers some combination of the above 'Modifiers' flags;
-     * not all flags will apply to all platforms. Mac ignores the _R
-     * modifiers. Windows ignores COMMAND, NUMERIC_KEY_PAD, HELP and
-     * FUNCTION.
-     * @param aCharacters characters that the OS would decide to generate
-     * from the event. On Windows, this is the charCode passed by
-     * WM_CHAR.
-     * @param aUnmodifiedCharacters characters that the OS would decide
-     * to generate from the event if modifier keys (other than shift)
-     * were assumed inactive. Needed on Mac, ignored on Windows.
-     * @param aObserver the observer that will get notified once the events
-     * have been dispatched.
-     * @return NS_ERROR_NOT_AVAILABLE to indicate that the keyboard
-     * layout is not supported and the event was not fired
-     */
-    virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
-                                              int32_t aNativeKeyCode,
-                                              uint32_t aModifierFlags,
-                                              const nsAString& aCharacters,
-                                              const nsAString& aUnmodifiedCharacters,
-                                              nsIObserver* aObserver) = 0;
-
-    /**
-     * Utility method intended for testing. Dispatches native mouse events
-     * may even move the mouse cursor. On Mac the events are guaranteed to
-     * be sent to the window containing this widget, but on Windows they'll go
-     * to whatever's topmost on the screen at that position, so for
-     * cross-platform testing ensure that your window is at the top of the
-     * z-order.
-     * @param aPoint screen location of the mouse, in device
-     * pixels, with origin at the top left
-     * @param aNativeMessage *platform-specific* event type (e.g. on Mac,
-     * NSMouseMoved; on Windows, MOUSEEVENTF_MOVE, MOUSEEVENTF_LEFTDOWN etc)
-     * @param aModifierFlags *platform-specific* modifier flags (ignored
-     * on Windows)
-     * @param aObserver the observer that will get notified once the events
-     * have been dispatched.
-     */
-    virtual nsresult SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
-                                                uint32_t aNativeMessage,
-                                                uint32_t aModifierFlags,
-                                                nsIObserver* aObserver) = 0;
-
-    /**
-     * A shortcut to SynthesizeNativeMouseEvent, abstracting away the native message.
-     * aPoint is location in device pixels to which the mouse pointer moves to.
-     * @param aObserver the observer that will get notified once the events
-     * have been dispatched.
-     */
-    virtual nsresult SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
-                                               nsIObserver* aObserver) = 0;
-
-    /**
-     * Utility method intended for testing. Dispatching native mouse scroll
-     * events may move the mouse cursor.
-     *
-     * @param aPoint            Mouse cursor position in screen coordinates.
-     *                          In device pixels, the origin at the top left of
-     *                          the primary display.
-     * @param aNativeMessage    Platform native message.
-     * @param aDeltaX           The delta value for X direction.  If the native
-     *                          message doesn't indicate X direction scrolling,
-     *                          this may be ignored.
-     * @param aDeltaY           The delta value for Y direction.  If the native
-     *                          message doesn't indicate Y direction scrolling,
-     *                          this may be ignored.
-     * @param aDeltaZ           The delta value for Z direction.  If the native
-     *                          message doesn't indicate Z direction scrolling,
-     *                          this may be ignored.
-     * @param aModifierFlags    Must be values of Modifiers, or zero.
-     * @param aAdditionalFlags  See nsIDOMWidnowUtils' consts and their
-     *                          document.
-     * @param aObserver         The observer that will get notified once the
-     *                          events have been dispatched.
-     */
-    virtual nsresult SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
-                                                      uint32_t aNativeMessage,
-                                                      double aDeltaX,
-                                                      double aDeltaY,
-                                                      double aDeltaZ,
-                                                      uint32_t aModifierFlags,
-                                                      uint32_t aAdditionalFlags,
-                                                      nsIObserver* aObserver) = 0;
-
-    /*
-     * TouchPointerState states for SynthesizeNativeTouchPoint. Match
-     * touch states in nsIDOMWindowUtils.idl.
-     */
-    enum TouchPointerState {
-      // The pointer is in a hover state above the digitizer
-      TOUCH_HOVER    = (1 << 0),
-      // The pointer is in contact with the digitizer
-      TOUCH_CONTACT  = (1 << 1),
-      // The pointer has been removed from the digitizer detection area
-      TOUCH_REMOVE   = (1 << 2),
-      // The pointer has been canceled. Will cancel any pending os level
-      // gestures that would triggered as a result of completion of the
-      // input sequence. This may not cancel moz platform related events
-      // that might get tirggered by input already delivered.
-      TOUCH_CANCEL   = (1 << 3),
-
-      // ALL_BITS used for validity checking during IPC serialization
-      ALL_BITS       = (1 << 4) - 1
-    };
-
-    /*
-     * Create a new or update an existing touch pointer on the digitizer.
-     * To trigger os level gestures, individual touch points should
-     * transition through a complete set of touch states which should be
-     * sent as individual messages.
-     *
-     * @param aPointerId The touch point id to create or update.
-     * @param aPointerState one or more of the touch states listed above
-     * @param aScreenX, aScreenY screen coords of this event
-     * @param aPressure 0.0 -> 1.0 float val indicating pressure
-     * @param aOrientation 0 -> 359 degree value indicating the
-     * orientation of the pointer. Use 90 for normal taps.
-     * @param aObserver The observer that will get notified once the events
-     * have been dispatched.
-     */
-    virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
-                                                TouchPointerState aPointerState,
-                                                nsIntPoint aPointerScreenPoint,
-                                                double aPointerPressure,
-                                                uint32_t aPointerOrientation,
-                                                nsIObserver* aObserver) = 0;
-
-    /*
-     * Helper for simulating a simple tap event with one touch point. When
-     * aLongTap is true, simulates a native long tap with a duration equal to
-     * ui.click_hold_context_menus.delay. This pref is compatible with the
-     * apzc long tap duration. Defaults to 1.5 seconds.
-     * @param aObserver The observer that will get notified once the events
-     * have been dispatched.
-     */
-    virtual nsresult SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint,
-                                              bool aLongTap,
-                                              nsIObserver* aObserver);
-
-    /*
-     * Cancels all active simulated touch input points and pending long taps.
-     * Native widgets should track existing points such that they can clear the
-     * digitizer state when this call is made.
-     * @param aObserver The observer that will get notified once the touch
-     * sequence has been cleared.
-     */
-    virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver);
-
-    /*
-     * Snapshot the contents of the widget by reading pixels back from the
-     * Operating System. Unlike RenderDocument(), this does not read from our
-     * own backbuffers, so that we can test if there is a difference in how
-     * our buffers are being presented.
-     *
-     * This is only supported for widgets using OMTC.
-     */
-    already_AddRefed<mozilla::gfx::SourceSurface> SnapshotWidgetOnScreen();
-
-    /*
-     * Implementation of SnapshotWidgetOnScreen. This is invoked by the
-     * compositor for SnapshotWidgetOnScreen(), and should not be called
-     * otherwise.
-     */
-    virtual bool CaptureWidgetOnScreen(mozilla::RefPtr<mozilla::gfx::DrawTarget> aDT) = 0;
-
-private:
-  class LongTapInfo
-  {
-  public:
-    LongTapInfo(int32_t aPointerId, nsIntPoint& aPoint,
-                mozilla::TimeDuration aDuration,
-                nsIObserver* aObserver) :
-      mPointerId(aPointerId),
-      mPosition(aPoint),
-      mDuration(aDuration),
-      mObserver(aObserver),
-      mStamp(mozilla::TimeStamp::Now())
-    {
-    }
-
-    int32_t mPointerId;
-    nsIntPoint mPosition;
-    mozilla::TimeDuration mDuration;
-    nsCOMPtr<nsIObserver> mObserver;
-    mozilla::TimeStamp mStamp;
-  };
-
-  static void OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure);
-
-  nsAutoPtr<LongTapInfo> mLongTapTouchPoint;
-  nsCOMPtr<nsITimer> mLongTapTimer;
-  static int32_t sPointerIdCounter;
-
-public:
-    /**
-     * Activates a native menu item at the position specified by the index
-     * string. The index string is a string of positive integers separated
-     * by the "|" (pipe) character. The last integer in the string represents
-     * the item index in a submenu located using the integers preceding it.
-     *
-     * Example: 1|0|4
-     * In this string, the first integer represents the top-level submenu
-     * in the native menu bar. Since the integer is 1, it is the second submeu
-     * in the native menu bar. Within that, the first item (index 0) is a
-     * submenu, and we want to activate the 5th item within that submenu.
-     */
-    virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) = 0;
-
-    /**
-     * This is used for native menu system testing.
-     *
-     * Updates a native menu at the position specified by the index string.
-     * The index string is a string of positive integers separated by the "|" 
-     * (pipe) character.
-     *
-     * Example: 1|0|4
-     * In this string, the first integer represents the top-level submenu
-     * in the native menu bar. Since the integer is 1, it is the second submeu
-     * in the native menu bar. Within that, the first item (index 0) is a
-     * submenu, and we want to update submenu at index 4 within that submenu.
-     *
-     * If this is called with an empty string it forces a full reload of the
-     * menu system.
-     */
-    virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) = 0;
-
-    /**
-     * Notify IME of the specified notification.
-     *
-     * @return If the notification is mouse button event and it's consumed by
-     *         IME, this returns NS_SUCCESS_EVENT_CONSUMED.
-     */
-    NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) = 0;
-
-    /**
-     * Start plugin IME.  If this results in a string getting committed, the
-     * result is in aCommitted (otherwise aCommitted is empty).
-     *
-     * aKeyboardEvent     The event with which plugin IME is to be started
-     * panelX and panelY  Location in screen coordinates of the IME input panel
-     *                    (should be just under the plugin)
-     * aCommitted         The string committed during IME -- otherwise empty
-     */
-    NS_IMETHOD StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent,
-                              int32_t aPanelX, int32_t aPanelY,
-                              nsString& aCommitted) = 0;
-
-    /**
-     * Tells the widget whether or not a plugin (inside the widget) has the
-     * keyboard focus.  Should be sent when the keyboard focus changes too or
-     * from a plugin.
-     *
-     * aFocused  Whether or not a plugin is focused
-     */
-    NS_IMETHOD SetPluginFocused(bool& aFocused) = 0;
-
-    /*
-     * Notifies the input context changes.
-     */
-    NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
-                                      const InputContextAction& aAction) = 0;
-
-    /*
-     * Get current input context.
-     */
-    NS_IMETHOD_(InputContext) GetInputContext() = 0;
-
-    /*
-     * Given a WidgetKeyboardEvent, this method synthesizes a corresponding
-     * native (OS-level) event for it. This method allows tests to simulate
-     * keystrokes that trigger native key bindings (which require a native
-     * event).
-     */
-    NS_IMETHOD AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) = 0;
-
-    /*
-     * Execute native key bindings for aType.
-     */
-    typedef void (*DoCommandCallback)(mozilla::Command, void*);
-    enum NativeKeyBindingsType
-    {
-      NativeKeyBindingsForSingleLineEditor,
-      NativeKeyBindingsForMultiLineEditor,
-      NativeKeyBindingsForRichTextEditor
-    };
-    NS_IMETHOD_(bool) ExecuteNativeKeyBinding(
-                        NativeKeyBindingsType aType,
-                        const mozilla::WidgetKeyboardEvent& aEvent,
-                        DoCommandCallback aCallback,
-                        void* aCallbackData) = 0;
-
-    /*
-     * Get toggled key states.
-     * aKeyCode should be NS_VK_CAPS_LOCK or  NS_VK_NUM_LOCK or
-     * NS_VK_SCROLL_LOCK.
-     * aLEDState is the result for current LED state of the key.
-     * If the LED is 'ON', it returns TRUE, otherwise, FALSE.
-     * If the platform doesn't support the LED state (or we cannot get the
-     * state), this method returns NS_ERROR_NOT_IMPLEMENTED.
-     */
-    NS_IMETHOD GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState) = 0;
-
-    /*
-     * Retrieves preference for IME updates
-     */
-    virtual nsIMEUpdatePreference GetIMEUpdatePreference() = 0;
-
-    /*
-     * Call this method when a dialog is opened which has a default button.
-     * The button's rectangle should be supplied in aButtonRect.
-     */ 
-    NS_IMETHOD OnDefaultButtonLoaded(const nsIntRect &aButtonRect) = 0;
-
-    /**
-     * Compute the overridden system mouse scroll speed on the root content of
-     * web pages.  The widget may set the same value as aOriginalDelta.  E.g.,
-     * when the system scrolling settings were customized, widget can respect
-     * the will of the user.
-     *
-     * This is called only when the mouse wheel event scrolls the root content
-     * of the web pages by line.  In other words, this isn't called when the
-     * mouse wheel event is used for zoom, page scroll and other special
-     * actions.  And also this isn't called when the user doesn't use the
-     * system wheel speed settings.
-     *
-     * @param aOriginalDeltaX   The X delta value of the current mouse wheel
-     *                          scrolling event.
-     * @param aOriginalDeltaX   The Y delta value of the current mouse wheel
-     *                          scrolling event.
-     * @param aOverriddenDeltaX The overridden mouse scrolling speed along X
-     *                          axis. This value may be same as aOriginalDeltaX.
-     * @param aOverriddenDeltaY The overridden mouse scrolling speed along Y
-     *                          axis. This value may be same as aOriginalDeltaY.
-     */
-    NS_IMETHOD OverrideSystemMouseScrollSpeed(double aOriginalDeltaX,
-                                              double aOriginalDeltaY,
-                                              double& aOverriddenDeltaX,
-                                              double& aOverriddenDeltaY) = 0;
-
-    /**
-     * Return true if this process shouldn't use platform widgets, and
-     * so should use PuppetWidgets instead.  If this returns true, the
-     * result of creating and using a platform widget is undefined,
-     * and likely to end in crashes or other buggy behavior.
-     */
-    static bool
-    UsePuppetWidgets()
-    {
-      return XRE_IsContentProcess();
-    }
-
-    /**
-     * Allocate and return a "puppet widget" that doesn't directly
-     * correlate to a platform widget; platform events and data must
-     * be fed to it.  Currently used in content processes.  NULL is
-     * returned if puppet widgets aren't supported in this build
-     * config, on this platform, or for this process type.
-     *
-     * This function is called "Create" to match CreateInstance().
-     * The returned widget must still be nsIWidget::Create()d.
-     */
-    static already_AddRefed<nsIWidget>
-    CreatePuppetWidget(TabChild* aTabChild);
-
-    /**
-     * Allocate and return a "plugin proxy widget", a subclass of PuppetWidget
-     * used in wrapping a PPluginWidget connection for remote widgets. Note
-     * this call creates the base object, it does not create the widget. Use
-     * nsIWidget's Create to do this.
-     */
-    static already_AddRefed<nsIWidget>
-    CreatePluginProxyWidget(TabChild* aTabChild,
-                            mozilla::plugins::PluginWidgetChild* aActor);
-
-    /**
-     * Reparent this widget's native widget.
-     * @param aNewParent the native widget of aNewParent is the new native
-     *                   parent widget
-     */
-    NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent) = 0;
-
-    /**
-     * Return the internal format of the default framebuffer for this
-     * widget.
-     */
-    virtual uint32_t GetGLFrameBufferFormat() { return 0; /*GL_NONE*/ }
-
-    /**
-     * Return true if widget has it's own GL context
-     */
-    virtual bool HasGLContext() { return false; }
-
-    /**
-     * Returns true to indicate that this widget paints an opaque background
-     * that we want to be visible under the page, so layout should not force
-     * a default background.
-     */
-    virtual bool WidgetPaintsBackground() { return false; }
-
-    virtual bool NeedsPaint() {
-       if (!IsVisible()) {
-           return false;
-       }
-       nsIntRect bounds;
-       nsresult rv = GetBounds(bounds);
-       NS_ENSURE_SUCCESS(rv, false);
-       return !bounds.IsEmpty();
-    }
-
-    /**
-     * Get the natural bounds of this widget.  This method is only
-     * meaningful for widgets for which Gecko implements screen
-     * rotation natively.  When this is the case, GetBounds() returns
-     * the widget bounds taking rotation into account, and
-     * GetNaturalBounds() returns the bounds *not* taking rotation
-     * into account.
-     *
-     * No code outside of the composition pipeline should know or care
-     * about this.  If you're not an agent of the compositor, you
-     * probably shouldn't call this method.
-     */
-    virtual nsIntRect GetNaturalBounds() {
-        nsIntRect bounds;
-        GetBounds(bounds);
-        return bounds;
-    }
-
-    /**
-     * Set size constraints on the window size such that it is never less than
-     * the specified minimum size and never larger than the specified maximum
-     * size. The size constraints are sizes of the outer rectangle including
-     * the window frame and title bar. Use 0 for an unconstrained minimum size
-     * and NS_MAXSIZE for an unconstrained maximum size. Note that this method
-     * does not necessarily change the size of a window to conform to this size,
-     * thus Resize should be called afterwards.
-     *
-     * @param aConstraints: the size constraints in device pixels
-     */
-    virtual void SetSizeConstraints(const SizeConstraints& aConstraints) = 0;
-
-    /**
-     * Return the size constraints currently observed by the widget.
-     *
-     * @return the constraints in device pixels
-     */
-    virtual const SizeConstraints& GetSizeConstraints() const = 0;
-
-    /**
-     * If this is owned by a TabChild, return that.  Otherwise return
-     * null.
-     */
-    virtual TabChild* GetOwningTabChild() { return nullptr; }
-
-    /**
-     * If this isn't directly compositing to its window surface,
-     * return the compositor which is doing that on our behalf.
-     */
-    virtual CompositorChild* GetRemoteRenderer()
-    { return nullptr; }
-
-    /**
-     * If this widget has a more efficient composer available for its
-     * native framebuffer, return it.
-     *
-     * This can be called from a non-main thread, but that thread must
-     * hold a strong reference to this.
-     */
-    virtual Composer2D* GetComposer2D()
-    { return nullptr; }
-
-    /**
-     * Some platforms (only cocoa right now) round widget coordinates to the
-     * nearest even pixels (see bug 892994), this function allows us to
-     * determine how widget coordinates will be rounded.
-     */
-    virtual int32_t RoundsWidgetCoordinatesTo() { return 1; }
-
-    virtual void UpdateZoomConstraints(const uint32_t& aPresShellId,
-                                       const FrameMetrics::ViewID& aViewId,
-                                       const mozilla::Maybe<ZoomConstraints>& aConstraints) {};
-
-    /**
-     * GetTextEventDispatcher() returns TextEventDispatcher belonging to the
-     * widget.  Note that this never returns nullptr.
-     */
-    NS_IMETHOD_(TextEventDispatcher*) GetTextEventDispatcher() = 0;
-
-protected:
-    /**
-     * Like GetDefaultScale, but taking into account only the system settings
-     * and ignoring Gecko preferences.
-     */
-    virtual double GetDefaultScaleInternal() { return 1.0; }
-
-    // keep the list of children.  We also keep track of our siblings.
-    // The ownership model is as follows: parent holds a strong ref to
-    // the first element of the list, and each element holds a strong
-    // ref to the next element in the list.  The prevsibling and
-    // lastchild pointers are weak, which is fine as long as they are
-    // maintained properly.
-    nsCOMPtr<nsIWidget> mFirstChild;
-    nsIWidget* MOZ_NON_OWNING_REF mLastChild;
-    nsCOMPtr<nsIWidget> mNextSibling;
-    nsIWidget* MOZ_NON_OWNING_REF mPrevSibling;
-    // When Destroy() is called, the sub class should set this true.
-    bool mOnDestroyCalled;
-    nsWindowType mWindowType;
-    int32_t mZIndex;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIWidget, NS_IWIDGET_IID)
-
-#endif // nsIWidget_h__
+#endif // #ifndef mozilla_widget_IMEData_h_
--- a/widget/gonk/HwcComposer2D.cpp
+++ b/widget/gonk/HwcComposer2D.cpp
@@ -746,16 +746,19 @@ HwcComposer2D::Render(nsIWidget* aWidget
         return false;
     }
 
     if (mPrepared) {
         // No mHwc prepare, if already prepared in current draw cycle
         mList->hwLayers[mList->numHwLayers - 1].handle = dispSurface->lastHandle;
         mList->hwLayers[mList->numHwLayers - 1].acquireFenceFd = dispSurface->GetPrevDispAcquireFd();
     } else {
+        // Update screen rect to handle a case that TryRenderWithHwc() is not called.
+        mScreenRect = screen->GetNaturalBounds();
+
         mList->flags = HWC_GEOMETRY_CHANGED;
         mList->numHwLayers = 2;
         mList->hwLayers[0].hints = 0;
         mList->hwLayers[0].compositionType = HWC_FRAMEBUFFER;
         mList->hwLayers[0].flags = HWC_SKIP_LAYER;
         mList->hwLayers[0].backgroundColor = {0};
         mList->hwLayers[0].acquireFenceFd = -1;
         mList->hwLayers[0].releaseFenceFd = -1;
@@ -768,17 +771,18 @@ HwcComposer2D::Render(nsIWidget* aWidget
 }
 
 void
 HwcComposer2D::Prepare(buffer_handle_t dispHandle, int fence, nsScreenGonk* screen)
 {
     if (mPrepared) {
         LOGE("Multiple hwc prepare calls!");
     }
-    mHal->Prepare(mList, screen->GetDisplayType(), dispHandle, fence);
+    hwc_rect_t dispRect = {0, 0, mScreenRect.width, mScreenRect.height};
+    mHal->Prepare(mList, screen->GetDisplayType(), dispRect, dispHandle, fence);
     mPrepared = true;
 }
 
 bool
 HwcComposer2D::Commit(nsScreenGonk* aScreen)
 {
     for (uint32_t j=0; j < (mList->numHwLayers - 1); j++) {
         mList->hwLayers[j].acquireFenceFd = -1;
--- a/widget/gonk/hwchal/HwcHAL.cpp
+++ b/widget/gonk/hwchal/HwcHAL.cpp
@@ -27,18 +27,16 @@ HwcHAL::HwcHAL()
 {
     // Some HALs don't want to open hwc twice.
     // If GetDisplay already load hwc module, we don't need to load again
     mHwc = (HwcDevice*)GetGonkDisplay()->GetHWCDevice();
     if (!mHwc) {
         printf_stderr("HwcHAL Error: Cannot load hwcomposer");
         return;
     }
-
-    GetHwcAttributes();
 }
 
 HwcHAL::~HwcHAL()
 {
     mHwc = nullptr;
 }
 
 bool
@@ -74,16 +72,17 @@ int
 HwcHAL::ResetHwc()
 {
     return Set(nullptr, HWC_DISPLAY_PRIMARY);
 }
 
 int
 HwcHAL::Prepare(HwcList *aList,
                 uint32_t aDisp,
+                hwc_rect_t aDispRect,
                 buffer_handle_t aHandle,
                 int aFenceFd)
 {
     MOZ_ASSERT(mHwc);
     if (!mHwc) {
         printf_stderr("HwcHAL Error: HwcDevice doesn't exist. A fence might be leaked.");
         return -1;
     }
@@ -98,18 +97,18 @@ HwcHAL::Prepare(HwcList *aList,
 
     const auto idx = aList->numHwLayers - 1;
     aList->hwLayers[idx].hints = 0;
     aList->hwLayers[idx].flags = 0;
     aList->hwLayers[idx].transform = 0;
     aList->hwLayers[idx].handle = aHandle;
     aList->hwLayers[idx].blending = HWC_BLENDING_PREMULT;
     aList->hwLayers[idx].compositionType = HWC_FRAMEBUFFER_TARGET;
-    SetCrop(aList->hwLayers[idx], mHwcRect);
-    aList->hwLayers[idx].displayFrame = mHwcRect;
+    SetCrop(aList->hwLayers[idx], aDispRect);
+    aList->hwLayers[idx].displayFrame = aDispRect;
     aList->hwLayers[idx].visibleRegionScreen.numRects = 1;
     aList->hwLayers[idx].visibleRegionScreen.rects = &aList->hwLayers[idx].displayFrame;
     aList->hwLayers[idx].acquireFenceFd = aFenceFd;
     aList->hwLayers[idx].releaseFenceFd = -1;
 #if ANDROID_VERSION >= 18
     aList->hwLayers[idx].planeAlpha = 0xFF;
 #endif
     return mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
@@ -190,30 +189,16 @@ HwcHAL::RegisterHwcEventCallback(const H
     // with JellyBean.
 #if (ANDROID_VERSION == 19 || ANDROID_VERSION >= 21)
     return true;
 #else
     return false;
 #endif
 }
 
-void
-HwcHAL::GetHwcAttributes()
-{
-    int32_t values[2];
-    const uint32_t attrs[] = {
-        HWC_DISPLAY_WIDTH,
-        HWC_DISPLAY_HEIGHT,
-        HWC_DISPLAY_NO_ATTRIBUTE
-    };
-
-    mHwc->getDisplayAttributes(mHwc, 0, 0, attrs, values);
-    mHwcRect = {0, 0, values[0], values[1]};
-}
-
 uint32_t
 HwcHAL::GetAPIVersion() const
 {
     if (!mHwc) {
         // default value: HWC_MODULE_API_VERSION_0_1
         return 1;
     }
     return mHwc->common.version;
--- a/widget/gonk/hwchal/HwcHAL.h
+++ b/widget/gonk/hwchal/HwcHAL.h
@@ -26,49 +26,45 @@ namespace mozilla {
 class HwcHAL final : public HwcHALBase {
 public:
     explicit HwcHAL();
 
     virtual ~HwcHAL();
 
     virtual bool HasHwc() const override { return static_cast<bool>(mHwc); }
 
-    virtual const hwc_rect_t GetHwcRect() const override { return mHwcRect; }
-
     virtual void SetEGLInfo(hwc_display_t aDpy,
                             hwc_surface_t aSur) override { }
 
     virtual bool Query(QueryType aType) override;
 
     virtual int Set(HwcList *aList,
                     uint32_t aDisp) override;
 
     virtual int ResetHwc() override;
 
     virtual int Prepare(HwcList *aList,
                         uint32_t aDisp,
+                        hwc_rect_t aDispRect,
                         buffer_handle_t aHandle,
                         int aFenceFd) override;
 
     virtual bool SupportTransparency() const override;
 
     virtual uint32_t GetGeometryChangedFlag(bool aGeometryChanged) const override;
 
     virtual void SetCrop(HwcLayer &aLayer,
                          const hwc_rect_t &aSrcCrop) const override;
 
     virtual bool EnableVsync(bool aEnable) override;
 
     virtual bool RegisterHwcEventCallback(const HwcHALProcs_t &aProcs) override;
 
 private:
-    void GetHwcAttributes();
-
     uint32_t GetAPIVersion() const;
 
 private:
     HwcDevice  *mHwc = nullptr;
-    hwc_rect_t  mHwcRect = {0};
 };
 
 } // namespace mozilla
 
 #endif // mozilla_HwcHAL
--- a/widget/gonk/hwchal/HwcHALBase.h
+++ b/widget/gonk/hwchal/HwcHALBase.h
@@ -80,19 +80,16 @@ public:
     // Create HwcHAL module, Only HwcComposer2D calls this.
     // If other modules want to use HwcHAL, please use APIs in
     // HwcComposer2D
     static UniquePtr<HwcHALBase> CreateHwcHAL();
 
     // Check if mHwc exists
     virtual bool HasHwc() const = 0;
 
-    // Get HwcRect
-    virtual const hwc_rect_t GetHwcRect() const = 0;
-
     // Set EGL info (only ICS need this info)
     virtual void SetEGLInfo(hwc_display_t aEGLDisplay,
                             hwc_surface_t aEGLSurface) = 0;
 
     // HwcDevice query properties
     virtual bool Query(QueryType aType) = 0;
 
     // HwcDevice set
@@ -100,16 +97,17 @@ public:
                     uint32_t aDisp) = 0;
 
     // Reset HwcDevice
     virtual int ResetHwc() = 0;
 
     // HwcDevice prepare
     virtual int Prepare(HwcList *aList,
                         uint32_t aDisp,
+                        hwc_rect_t aDispRect,
                         buffer_handle_t aHandle,
                         int aFenceFd) = 0;
 
     // Check transparency support
     virtual bool SupportTransparency() const = 0;
 
     // Get a geometry change flag
     virtual uint32_t GetGeometryChangedFlag(bool aGeometryChanged) const = 0;
--- a/widget/gonk/hwchal/HwcICS.cpp
+++ b/widget/gonk/hwchal/HwcICS.cpp
@@ -81,16 +81,17 @@ int
 HwcICS::ResetHwc()
 {
     return -1;
 }
 
 int
 HwcICS::Prepare(HwcList *aList,
                 uint32_t aDisp,
+                hwc_rect_t aDispRect,
                 buffer_handle_t aHandle,
                 int aFenceFd)
 {
     return mHwc->prepare(mHwc, aList);
 }
 
 bool
 HwcICS::SupportTransparency() const
--- a/widget/gonk/hwchal/HwcICS.h
+++ b/widget/gonk/hwchal/HwcICS.h
@@ -26,30 +26,29 @@ namespace mozilla {
 class HwcICS final : public HwcHALBase {
 public:
     explicit HwcICS();
 
     virtual ~HwcICS();
 
     virtual bool HasHwc() const override { return static_cast<bool>(mHwc); }
 
-    virtual const hwc_rect_t GetHwcRect() const override { return {0}; }
-
     virtual void SetEGLInfo(hwc_display_t aEGLDisplay,
                             hwc_surface_t aEGLSurface) override;
 
     virtual bool Query(QueryType aType) override;
 
     virtual int Set(HwcList *aList,
                     uint32_t aDisp) override;
 
     virtual int ResetHwc() override;
 
     virtual int Prepare(HwcList *aList,
                         uint32_t aDisp,
+                        hwc_rect_t aDispRect,
                         buffer_handle_t aHandle,
                         int aFenceFd) override;
 
     virtual bool SupportTransparency() const override;
 
     virtual uint32_t GetGeometryChangedFlag(bool aGeometryChanged) const override;
 
     virtual void SetCrop(HwcLayer &aLayer,
--- a/widget/gonk/nsScreenManagerGonk.cpp
+++ b/widget/gonk/nsScreenManagerGonk.cpp
@@ -116,16 +116,17 @@ nsScreenGonk::nsScreenGonk(uint32_t aId,
     : mId(aId)
     , mNativeWindow(aNativeData.mNativeWindow)
     , mDpi(aNativeData.mXdpi)
     , mScreenRotation(nsIScreen::ROTATION_0_DEG)
     , mPhysicalScreenRotation(nsIScreen::ROTATION_0_DEG)
 #if ANDROID_VERSION >= 17
     , mDisplaySurface(aNativeData.mDisplaySurface)
 #endif
+    , mIsMirroring(false)
     , mDisplayType(aDisplayType)
     , mEGLDisplay(EGL_NO_DISPLAY)
     , mEGLSurface(EGL_NO_SURFACE)
     , mGLContext(nullptr)
 {
     if (mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &mVirtualBounds.width) ||
         mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &mVirtualBounds.height) ||
         mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_FORMAT, &mSurfaceFormat)) {
@@ -138,18 +139,32 @@ nsScreenGonk::nsScreenGonk(uint32_t aId,
         char propValue[PROPERTY_VALUE_MAX];
         property_get("ro.sf.hwrotation", propValue, "0");
         mPhysicalScreenRotation = atoi(propValue) / 90;
     }
 
     mColorDepth = SurfaceFormatToColorDepth(mSurfaceFormat);
 }
 
+static void
+ReleaseGLContextSync(mozilla::gl::GLContext* aGLContext) {
+    MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+    aGLContext->Release();
+}
+
 nsScreenGonk::~nsScreenGonk()
 {
+    // Release GLContext on compositor thread
+    if (mGLContext) {
+        CompositorParent::CompositorLoop()->PostTask(
+            FROM_HERE,
+            NewRunnableFunction(&ReleaseGLContextSync,
+                                mGLContext.forget().take()));
+        mGLContext = nullptr;
+    }
 }
 
 bool
 nsScreenGonk::IsPrimaryScreen()
 {
     return mDisplayType == GonkDisplay::DISPLAY_PRIMARY;
 }
 
@@ -354,17 +369,18 @@ nsScreenGonk::GetPrevDispAcquireFd()
 
 GonkDisplay::DisplayType
 nsScreenGonk::GetDisplayType()
 {
     return mDisplayType;
 }
 
 void
-nsScreenGonk::SetEGLInfo(hwc_display_t aDisplay, hwc_surface_t aSurface,
+nsScreenGonk::SetEGLInfo(hwc_display_t aDisplay,
+                         hwc_surface_t aSurface,
                          gl::GLContext* aGLContext)
 {
     MOZ_ASSERT(CompositorParent::IsInCompositorThread());
     mEGLDisplay = aDisplay;
     mEGLSurface = aSurface;
     mGLContext = aGLContext;
 }
 
@@ -373,18 +389,124 @@ nsScreenGonk::GetEGLDisplay()
 {
     MOZ_ASSERT(CompositorParent::IsInCompositorThread());
     return mEGLDisplay;
 }
 
 hwc_surface_t
 nsScreenGonk::GetEGLSurface()
 {
+    return mEGLSurface;
+}
+
+static void
+UpdateMirroringWidgetSync(nsScreenGonk* aScreen, nsWindow* aWindow) {
     MOZ_ASSERT(CompositorParent::IsInCompositorThread());
-    return mEGLSurface;
+    already_AddRefed<nsWindow> window(aWindow);
+    aScreen->UpdateMirroringWidget(window);
+}
+
+bool
+nsScreenGonk::EnableMirroring()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!IsPrimaryScreen());
+
+    nsRefPtr<nsScreenGonk> primaryScreen = nsScreenManagerGonk::GetPrimaryScreen();
+    NS_ENSURE_TRUE(primaryScreen, false);
+
+    bool ret = primaryScreen->SetMirroringScreen(this);
+    NS_ENSURE_TRUE(ret, false);
+
+    // Create a widget for mirroring
+    nsWidgetInitData initData;
+    initData.mScreenId = mId;
+    nsRefPtr<nsWindow> window = new nsWindow();
+    window->Create(nullptr, nullptr, mNaturalBounds, &initData);
+    MOZ_ASSERT(static_cast<nsWindow*>(window)->GetScreen() == this);
+
+    // Update mMirroringWidget on compositor thread
+    CompositorParent::CompositorLoop()->PostTask(
+        FROM_HERE,
+        NewRunnableFunction(&UpdateMirroringWidgetSync,
+                            primaryScreen,
+                            window.forget().take()));
+
+    mIsMirroring = true;
+    return true;
+}
+
+bool
+nsScreenGonk::DisableMirroring()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!IsPrimaryScreen());
+
+    mIsMirroring = false;
+    nsRefPtr<nsScreenGonk> primaryScreen = nsScreenManagerGonk::GetPrimaryScreen();
+    NS_ENSURE_TRUE(primaryScreen, false);
+
+    bool ret = primaryScreen->ClearMirroringScreen(this);
+    NS_ENSURE_TRUE(ret, false);
+
+    // Update mMirroringWidget on compositor thread
+    CompositorParent::CompositorLoop()->PostTask(
+        FROM_HERE,
+        NewRunnableFunction(&UpdateMirroringWidgetSync,
+                            primaryScreen,
+                            nullptr));
+    return true;
+}
+
+bool
+nsScreenGonk::SetMirroringScreen(nsScreenGonk* aScreen)
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(IsPrimaryScreen());
+
+    if (mMirroringScreen) {
+        return false;
+    }
+    mMirroringScreen = mMirroringScreen;
+    return true;
+}
+
+bool
+nsScreenGonk::ClearMirroringScreen(nsScreenGonk* aScreen)
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(IsPrimaryScreen());
+
+    if (mMirroringScreen != aScreen) {
+        return false;
+    }
+    mMirroringScreen = nullptr;
+    return true;
+}
+
+void
+nsScreenGonk::UpdateMirroringWidget(already_AddRefed<nsWindow>& aWindow)
+{
+    MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+    MOZ_ASSERT(IsPrimaryScreen());
+
+    if (mMirroringWidget) {
+        nsCOMPtr<nsIWidget> widget = mMirroringWidget.forget();
+        NS_ReleaseOnMainThread(widget);
+    }
+    mMirroringWidget = aWindow;
+}
+
+nsWindow*
+nsScreenGonk::GetMirroringWidget()
+{
+    MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+    MOZ_ASSERT(IsPrimaryScreen());
+
+    return mMirroringWidget;
 }
 
 NS_IMPL_ISUPPORTS(nsScreenManagerGonk, nsIScreenManager)
 
 nsScreenManagerGonk::nsScreenManagerGonk()
     : mInitialized(false)
 {
 }
@@ -606,50 +728,59 @@ NotifyDisplayChange(uint32_t aId, bool a
 {
     NS_DispatchToMainThread(new NotifyTask(aId, aConnected));
 }
 
 } // end of unnamed namespace.
 
 nsresult
 nsScreenManagerGonk::AddScreen(GonkDisplay::DisplayType aDisplayType,
-                               android::IGraphicBufferProducer* aProducer)
+                               android::IGraphicBufferProducer* aSink)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ENSURE_TRUE(aDisplayType < GonkDisplay::DisplayType::NUM_DISPLAY_TYPES,
                    NS_ERROR_FAILURE);
 
     uint32_t id = GetIdFromType(aDisplayType);
     NS_ENSURE_TRUE(!IsScreenConnected(id), NS_ERROR_FAILURE);
 
     GonkDisplay::NativeData nativeData =
-        GetGonkDisplay()->GetNativeData(aDisplayType, aProducer);
+        GetGonkDisplay()->GetNativeData(aDisplayType, aSink);
     nsScreenGonk* screen = new nsScreenGonk(id, aDisplayType, nativeData);
 
     mScreens.AppendElement(screen);
 
     NotifyDisplayChange(id, true);
 
+    // By default, non primary screen does mirroring.
+    if (aDisplayType != GonkDisplay::DISPLAY_PRIMARY &&
+        gfxPrefs::ScreenMirroringEnabled()) {
+        screen->EnableMirroring();
+    }
+
     return NS_OK;
 }
 
 nsresult
 nsScreenManagerGonk::RemoveScreen(GonkDisplay::DisplayType aDisplayType)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ENSURE_TRUE(aDisplayType < GonkDisplay::DisplayType::NUM_DISPLAY_TYPES,
                    NS_ERROR_FAILURE);
 
     uint32_t screenId = GetIdFromType(aDisplayType);
     NS_ENSURE_TRUE(IsScreenConnected(screenId), NS_ERROR_FAILURE);
 
     for (size_t i = 0; i < mScreens.Length(); i++) {
         if (mScreens[i]->GetId() == screenId) {
+            if (mScreens[i]->IsMirroring()) {
+                mScreens[i]->DisableMirroring();
+            }
             mScreens.RemoveElementAt(i);
             break;
         }
     }
 
     NotifyDisplayChange(screenId, false);
 
     return NS_OK;
--- a/widget/gonk/nsScreenManagerGonk.h
+++ b/widget/gonk/nsScreenManagerGonk.h
@@ -12,24 +12,26 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef nsScreenManagerGonk_h___
 #define nsScreenManagerGonk_h___
 
-#include "mozilla/Hal.h"
-
 #include "cutils/properties.h"
 #include "hardware/hwcomposer.h"
+
 #include "libdisplay/GonkDisplay.h"
+#include "mozilla/Hal.h"
+#include "mozilla/Mutex.h"
 #include "nsBaseScreen.h"
 #include "nsCOMPtr.h"
 #include "nsIScreenManager.h"
+#include "nsProxyRelease.h"
 
 #include <android/native_window.h>
 
 class nsRunnable;
 class nsWindow;
 
 namespace android {
     class DisplaySurface;
@@ -82,42 +84,59 @@ public:
     void UnregisterWindow(nsWindow* aWindow);
     void BringToTop(nsWindow* aWindow);
 
     const nsTArray<nsWindow*>& GetTopWindows() const
     {
         return mTopWindows;
     }
 
-    // Set EGL info of primary display. Used for BLIT Composition.
+    // Non-primary screen only
+    bool EnableMirroring();
+    bool DisableMirroring();
+    bool IsMirroring()
+    {
+        return mIsMirroring;
+    }
+
+    // Primary screen only
+    bool SetMirroringScreen(nsScreenGonk* aScreen);
+    bool ClearMirroringScreen(nsScreenGonk* aScreen);
+
+    // Called only on compositor thread
     void SetEGLInfo(hwc_display_t aDisplay, hwc_surface_t aSurface,
                     mozilla::gl::GLContext* aGLContext);
     hwc_display_t GetEGLDisplay();
     hwc_surface_t GetEGLSurface();
+    void UpdateMirroringWidget(already_AddRefed<nsWindow>& aWindow); // Primary screen only
+    nsWindow* GetMirroringWidget(); // Primary screen only
 
 protected:
     uint32_t mId;
     int32_t mColorDepth;
     android::sp<ANativeWindow> mNativeWindow;
     float mDpi;
     int32_t mSurfaceFormat;
     nsIntRect mNaturalBounds; // Screen bounds w/o rotation taken into account.
     nsIntRect mVirtualBounds; // Screen bounds w/ rotation taken into account.
     uint32_t mScreenRotation;
     uint32_t mPhysicalScreenRotation;
     nsTArray<nsWindow*> mTopWindows;
 #if ANDROID_VERSION >= 17
     android::sp<android::DisplaySurface> mDisplaySurface;
 #endif
+    bool mIsMirroring; // Non-primary screen only
+    nsRefPtr<nsScreenGonk> mMirroringScreen; // Primary screen only
 
     // Accessed and updated only on compositor thread
     GonkDisplay::DisplayType mDisplayType;
     hwc_display_t mEGLDisplay;
     hwc_surface_t mEGLSurface;
-    mozilla::gl::GLContext* mGLContext;
+    nsRefPtr<mozilla::gl::GLContext> mGLContext;
+    nsRefPtr<nsWindow> mMirroringWidget; // Primary screen only
 };
 
 class nsScreenManagerGonk final : public nsIScreenManager
 {
 public:
     typedef mozilla::GonkDisplay GonkDisplay;
 
 public:
@@ -128,17 +147,17 @@ public:
 
     static already_AddRefed<nsScreenManagerGonk> GetInstance();
     static already_AddRefed<nsScreenGonk> GetPrimaryScreen();
 
     void Initialize();
     void DisplayEnabled(bool aEnabled);
 
     nsresult AddScreen(GonkDisplay::DisplayType aDisplayType,
-                       android::IGraphicBufferProducer* aProducer = nullptr);
+                       android::IGraphicBufferProducer* aSink = nullptr);
 
     nsresult RemoveScreen(GonkDisplay::DisplayType aDisplayType);
 
 protected:
     ~nsScreenManagerGonk();
     void VsyncControl(bool aEnabled);
     uint32_t GetIdFromType(GonkDisplay::DisplayType aDisplayType);
     bool IsScreenConnected(uint32_t aId);
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -124,16 +124,17 @@ EXPORTS.mozilla += [
     'TextEvents.h',
     'TextRange.h',
     'TouchEvents.h',
     'VsyncDispatcher.h',
     'WidgetUtils.h',
 ]
 
 EXPORTS.mozilla.widget += [
+    'IMEData.h',
     'PuppetBidiKeyboard.h',
     'WidgetMessageUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'ContentCache.cpp',
     'ContentHelper.cpp',
     'GfxDriverInfo.cpp',
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -19,16 +19,17 @@
 #include "nsITimer.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/gfx/Point.h"
+#include "mozilla/widget/IMEData.h"
 #include "nsDataHashtable.h"
 #include "nsIObserver.h"
 #include "FrameMetrics.h"
 #include "Units.h"
 
 // forward declarations
 class   nsIRollupListener;
 class   imgIContainer;
@@ -36,17 +37,16 @@ class   nsIContent;
 class   ViewWrapper;
 class   nsIWidgetListener;
 class   nsIntRegion;
 class   nsIScreen;
 class   nsIRunnable;
 
 namespace mozilla {
 class CompositorVsyncDispatcher;
-class WritingMode;
 namespace dom {
 class TabChild;
 } // namespace dom
 namespace plugins {
 class PluginWidgetChild;
 } // namespace plugins
 namespace layers {
 class Composer2D;
@@ -219,339 +219,19 @@ enum nsTopLevelWidgetZPlacement { // for
 #define NS_WIDGET_SUSPEND_PROCESS_OBSERVER_TOPIC "suspend_process_notification"
 
 /**
  * After the current process resumes from being suspended, this topic is
  * notified.
  */
 #define NS_WIDGET_RESUME_PROCESS_OBSERVER_TOPIC "resume_process_notification"
 
-/**
- * Preference for receiving IME updates
- *
- * If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
- * change and/or selection change and call nsIWidget::NotifyIME() with
- * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE.
- * Please note that the text change observing cost is very expensive especially
- * on an HTML editor has focus.
- * If the IME implementation on a particular platform doesn't care about
- * NOTIFY_IME_OF_SELECTION_CHANGE and/or NOTIFY_IME_OF_TEXT_CHANGE,
- * they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
- * If the IME implementation needs notifications even while our process is
- * deactive, it should also set NOTIFY_DURING_DEACTIVE.
- */
-struct nsIMEUpdatePreference {
-
-  typedef uint8_t Notifications;
-
-  enum : Notifications
-  {
-    NOTIFY_NOTHING                       = 0,
-    NOTIFY_SELECTION_CHANGE              = 1 << 0,
-    NOTIFY_TEXT_CHANGE                   = 1 << 1,
-    NOTIFY_POSITION_CHANGE               = 1 << 2,
-    // NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR is used when mouse button is pressed
-    // or released on a character in the focused editor.  The notification is
-    // notified to IME as a mouse event.  If it's consumed by IME, NotifyIME()
-    // returns NS_SUCCESS_EVENT_CONSUMED.  Otherwise, it returns NS_OK if it's
-    // handled without any error.
-    NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR    = 1 << 3,
-    // Following values indicate when widget needs or doesn't need notification.
-    NOTIFY_CHANGES_CAUSED_BY_COMPOSITION = 1 << 6,
-    // NOTE: NOTIFY_DURING_DEACTIVE isn't supported in environments where two
-    //       or more compositions are possible.  E.g., Mac and Linux (GTK).
-    NOTIFY_DURING_DEACTIVE               = 1 << 7,
-    // Changes are notified in following conditions if the instance is
-    // just constructed.  If some platforms don't need change notifications
-    // in some of following conditions, the platform should remove following
-    // flags before returing the instance from nsIWidget::GetUpdatePreference().
-    DEFAULT_CONDITIONS_OF_NOTIFYING_CHANGES =
-      NOTIFY_CHANGES_CAUSED_BY_COMPOSITION
-  };
-
-  nsIMEUpdatePreference()
-    : mWantUpdates(DEFAULT_CONDITIONS_OF_NOTIFYING_CHANGES)
-  {
-  }
-
-  explicit nsIMEUpdatePreference(Notifications aWantUpdates)
-    : mWantUpdates(aWantUpdates | DEFAULT_CONDITIONS_OF_NOTIFYING_CHANGES)
-  {
-  }
-
-  void DontNotifyChangesCausedByComposition()
-  {
-    mWantUpdates &= ~DEFAULT_CONDITIONS_OF_NOTIFYING_CHANGES;
-  }
-
-  bool WantSelectionChange() const
-  {
-    return !!(mWantUpdates & NOTIFY_SELECTION_CHANGE);
-  }
-
-  bool WantTextChange() const
-  {
-    return !!(mWantUpdates & NOTIFY_TEXT_CHANGE);
-  }
-
-  bool WantPositionChanged() const
-  {
-    return !!(mWantUpdates & NOTIFY_POSITION_CHANGE);
-  }
-
-  bool WantChanges() const
-  {
-    return WantSelectionChange() || WantTextChange();
-  }
-
-  bool WantMouseButtonEventOnChar() const
-  {
-    return !!(mWantUpdates & NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR);
-  }
-
-  bool WantChangesCausedByComposition() const
-  {
-    return WantChanges() &&
-             !!(mWantUpdates & NOTIFY_CHANGES_CAUSED_BY_COMPOSITION);
-  }
-
-  bool WantDuringDeactive() const
-  {
-    return !!(mWantUpdates & NOTIFY_DURING_DEACTIVE);
-  }
-
-  Notifications mWantUpdates;
-};
-
-
-/* 
- * Contains IMEStatus plus information about the current 
- * input context that the IME can use as hints if desired.
- */
-
 namespace mozilla {
 namespace widget {
 
-struct IMEState {
-  /**
-   * IME enabled states, the mEnabled value of
-   * SetInputContext()/GetInputContext() should be one value of following
-   * values.
-   *
-   * WARNING: If you change these values, you also need to edit:
-   *   nsIDOMWindowUtils.idl
-   *   nsContentUtils::GetWidgetStatusFromIMEStatus
-   */
-  enum Enabled {
-    /**
-     * 'Disabled' means the user cannot use IME. So, the IME open state should
-     * be 'closed' during 'disabled'.
-     */
-    DISABLED,
-    /**
-     * 'Enabled' means the user can use IME.
-     */
-    ENABLED,
-    /**
-     * 'Password' state is a special case for the password editors.
-     * E.g., on mac, the password editors should disable the non-Roman
-     * keyboard layouts at getting focus. Thus, the password editor may have
-     * special rules on some platforms.
-     */
-    PASSWORD,
-    /**
-     * This state is used when a plugin is focused.
-     * When a plug-in is focused content, we should send native events
-     * directly. Because we don't process some native events, but they may
-     * be needed by the plug-in.
-     */
-    PLUGIN
-  };
-  Enabled mEnabled;
-
-  /**
-   * IME open states the mOpen value of SetInputContext() should be one value of
-   * OPEN, CLOSE or DONT_CHANGE_OPEN_STATE.  GetInputContext() should return
-   * OPEN, CLOSE or OPEN_STATE_NOT_SUPPORTED.
-   */
-  enum Open {
-    /**
-     * 'Unsupported' means the platform cannot return actual IME open state.
-     * This value is used only by GetInputContext().
-     */
-    OPEN_STATE_NOT_SUPPORTED,
-    /**
-     * 'Don't change' means the widget shouldn't change IME open state when
-     * SetInputContext() is called.
-     */
-    DONT_CHANGE_OPEN_STATE = OPEN_STATE_NOT_SUPPORTED,
-    /**
-     * 'Open' means that IME should compose in its primary language (or latest
-     * input mode except direct ASCII character input mode).  Even if IME is
-     * opened by this value, users should be able to close IME by theirselves.
-     * Web contents can specify this value by |ime-mode: active;|.
-     */
-    OPEN,
-    /**
-     * 'Closed' means that IME shouldn't handle key events (or should handle
-     * as ASCII character inputs on mobile device).  Even if IME is closed by
-     * this value, users should be able to open IME by theirselves.
-     * Web contents can specify this value by |ime-mode: inactive;|.
-     */
-    CLOSED
-  };
-  Open mOpen;
-
-  IMEState() : mEnabled(ENABLED), mOpen(DONT_CHANGE_OPEN_STATE) { }
-
-  explicit IMEState(Enabled aEnabled, Open aOpen = DONT_CHANGE_OPEN_STATE) :
-    mEnabled(aEnabled), mOpen(aOpen)
-  {
-  }
-
-  // Returns true if the user can input characters.
-  // This means that a plain text editor, an HTML editor, a password editor or
-  // a plain text editor whose ime-mode is "disabled".
-  bool IsEditable() const
-  {
-    return mEnabled == ENABLED || mEnabled == PASSWORD;
-  }
-  // Returns true if the user might be able to input characters.
-  // This means that a plain text editor, an HTML editor, a password editor,
-  // a plain text editor whose ime-mode is "disabled" or a windowless plugin
-  // has focus.
-  bool MaybeEditable() const
-  {
-    return IsEditable() || mEnabled == PLUGIN;
-  }
-};
-
-struct InputContext {
-  InputContext()
-    : mNativeIMEContext(nullptr)
-    , mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT)
-    , mMayBeIMEUnaware(false)
-  {}
-
-  bool IsPasswordEditor() const
-  {
-    return mHTMLInputType.LowerCaseEqualsLiteral("password");
-  }
-
-  IMEState mIMEState;
-
-  /* The type of the input if the input is a html input field */
-  nsString mHTMLInputType;
-
-  /* The type of the inputmode */
-  nsString mHTMLInputInputmode;
-
-  /* A hint for the action that is performed when the input is submitted */
-  nsString mActionHint;
-
-  /* Native IME context for the widget.  This doesn't come from the argument of
-     SetInputContext().  If there is only one context in the process, this may
-     be nullptr. */
-  void* mNativeIMEContext;
-
-  /**
-   * mOrigin indicates whether this focus event refers to main or remote content.
-   */
-  enum Origin