merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 07 Jul 2016 11:41:27 +0200
changeset 304011 4764b9f8e6d4ef9823237f01ca3901759ce8daeb
parent 304010 44e5098659ef8c0476fb4ff8085025a37e4dc216 (current diff)
parent 303966 1052561d1faf90af585c7b17909049d7bd960050 (diff)
child 304012 7c3cd3ede92b74156071563df320ae030f4df947
child 304029 63cc31d6cc1c8089590461016ce0b4a2fb77ecbc
child 304050 43d6baebd3718afba8f78caec220ca8dc2e60f1e
child 304111 4a8d8898e507cc918bb1a06cccfd69cb4f3e04b3
push id79220
push usercbook@mozilla.com
push dateThu, 07 Jul 2016 09:44:18 +0000
treeherdermozilla-inbound@7c3cd3ede92b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
4764b9f8e6d4 / 50.0a1 / 20160707030220 / files
nightly linux64
4764b9f8e6d4 / 50.0a1 / 20160707030220 / files
nightly mac
4764b9f8e6d4 / 50.0a1 / 20160707030201 / files
nightly win32
4764b9f8e6d4 / 50.0a1 / 20160707030220 / files
nightly win64
4764b9f8e6d4 / 50.0a1 / 20160707030220 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/themes/osx/browser.css
browser/themes/windows/browser.css
dom/tests/mochitest/geolocation/test_mozsettings.html
dom/tests/mochitest/geolocation/test_mozsettingsWatch.html
gfx/layers/basic/BasicCompositor.cpp
gfx/layers/ipc/CompositorBridgeParent.cpp
media/ffvpx/compat/msvcrt/snprintf.c
media/ffvpx/compat/msvcrt/snprintf.h
media/ffvpx/compat/strtod.c
taskcluster/ci/legacy/tasks/tests/b2g_unittest_base.yml
taskcluster/ci/legacy/tasks/tests/mulet_build_test.yml
taskcluster/ci/legacy/tasks/tests/mulet_build_unit.yml
taskcluster/ci/legacy/tasks/tests/mulet_gaia_unit.yml
taskcluster/ci/legacy/tasks/tests/mulet_gaia_unit_oop.yml
taskcluster/ci/legacy/tasks/tests/mulet_linter.yml
taskcluster/ci/legacy/tasks/tests/mulet_mochitests.yml
taskcluster/ci/legacy/tasks/tests/mulet_reftests.yml
toolkit/components/telemetry/Histograms.json
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/themes/linux/global/icons/loading.png
toolkit/themes/osx/global/icons/loading.png
toolkit/themes/osx/global/icons/loading@2x.png
toolkit/themes/windows/global/icons/loading.png
toolkit/themes/windows/global/icons/loading@2x.png
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -294,20 +294,20 @@ pref("editor.singleLine.pasteNewlines", 
 // The names of the preferences are to be in sync with EventStateManager.cpp
 pref("ui.dragThresholdX", 25);
 pref("ui.dragThresholdY", 25);
 
 // Layers Acceleration.  We can only have nice things on gonk, because
 // they're not maintained anywhere else.
 #ifndef MOZ_WIDGET_GONK
 pref("dom.ipc.tabs.disabled", true);
+pref("layers.async-pan-zoom.enabled", false);
 #else
 pref("dom.ipc.tabs.disabled", false);
 pref("layers.acceleration.disabled", false);
-pref("layers.async-pan-zoom.enabled", true);
 pref("gfx.content.azure.backends", "cairo");
 #endif
 
 // Web Notifications
 pref("notification.feature.enabled", true);
 
 // prevent video elements from preloading too much data
 pref("media.preload.default", 1); // default to preload none
--- a/b2g/app/nsBrowserApp.cpp
+++ b/b2g/app/nsBrowserApp.cpp
@@ -21,19 +21,16 @@
 
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 #ifdef XP_WIN
 // we want a wmain entry point
 #include "nsWindowsWMain.cpp"
-#if defined(_MSC_VER) && (_MSC_VER < 1900)
-#define snprintf _snprintf
-#endif
 #define strcasecmp _stricmp
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "BootAnimation.h"
 #endif
 
 #include "BinaryPath.h"
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -31,19 +31,16 @@
 
 #ifdef XP_WIN
 #ifdef MOZ_ASAN
 // ASAN requires firefox.exe to be built with -MD, and it's OK if we don't
 // support Windows XP SP2 in ASAN builds.
 #define XRE_DONT_SUPPORT_XPSP2
 #endif
 #define XRE_WANT_ENVIRON
-#if defined(_MSC_VER) && (_MSC_VER < 1900)
-#define snprintf _snprintf
-#endif
 #define strcasecmp _stricmp
 #ifdef MOZ_SANDBOX
 #include "mozilla/sandboxing/SandboxInitialization.h"
 #endif
 #endif
 #include "BinaryPath.h"
 
 #include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -968,16 +968,36 @@ pref("security.sandbox.windows.log.stack
 // 1 -> "an imperfect sandbox designed to allow firefox to run reasonably well"
 // 2 -> "an ideal sandbox which may break many things"
 // This setting is read when the content process is started. On Mac the content
 // process is killed when all windows are closed, so a change will take effect
 // when the 1st window is opened.
 pref("security.sandbox.content.level", 1);
 #endif
 
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
+// This pref is introduced as part of bug 742434, the naming is inspired from
+// its Windows/Mac counterpart, but on Linux it's an integer which means:
+// 0 -> "no sandbox"
+// 1 -> "content sandbox using seccomp-bpf when available"
+// 2 -> "seccomp-bpf + file broker"
+// Content sandboxing on Linux is currently in the stage of
+// 'just getting it enabled', which includes a very permissive whitelist. We
+// enable seccomp-bpf on nightly to see if everything is running, or if we need
+// to whitelist more system calls.
+//
+// So the purpose of this setting is to allow nightly users to disable the
+// sandbox while we fix their problems. This way, they won't have to wait for
+// another nightly release which disables seccomp-bpf again.
+//
+// This setting may not be required anymore once we decide to permanently
+// enable the content sandbox.
+pref("security.sandbox.content.level", 1);
+#endif
+
 #if defined(XP_MACOSX) || defined(XP_WIN)
 #if defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
 // ID (a UUID when set by gecko) that is used to form the name of a
 // sandbox-writable temporary directory to be used by content processes
 // when a temporary writable file is required in a level 1 sandbox.
 pref("security.sandbox.content.tempDirSuffix", "");
 #endif
 #endif
@@ -1361,20 +1381,16 @@ pref("browser.tabs.remote.autostart.2", 
 #endif
 
 // For the about:tabcrashed page
 pref("browser.tabs.crashReporting.sendReport", true);
 pref("browser.tabs.crashReporting.includeURL", false);
 pref("browser.tabs.crashReporting.emailMe", false);
 pref("browser.tabs.crashReporting.email", "");
 
-#ifndef MOZ_MULET
-pref("layers.async-pan-zoom.enabled", true);
-#endif
-
 // Enable e10s add-on interposition by default.
 pref("extensions.interposition.enabled", true);
 pref("extensions.interposition.prefetching", true);
 
 // Enable blocking of e10s for add-on users on beta/release.
 #ifdef RELEASE_BUILD
 pref("extensions.e10sBlocksEnabling", true);
 #endif
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -364,17 +364,19 @@ var CookieStore = {
     let cookies = [];
 
     let appendCookiesForHost = host => {
       if (!this._hosts.has(host)) {
         return;
       }
 
       for (let pathToNamesMap of this._hosts.get(host).values()) {
-        cookies.push(...pathToNamesMap.values());
+        for (let nameToCookiesMap of pathToNamesMap.values()) {
+          cookies.push(...nameToCookiesMap.values());
+        }
       }
     }
 
     // Try to find cookies for the given host, e.g. <www.example.com>.
     // The full hostname will be in the map if the Set-Cookie header did not
     // have a domain= attribute, i.e. the cookie will only be stored for the
     // request domain. Also, try to find cookies for subdomains, e.g.
     // <.example.com>. We will find those variants with a leading dot in the
@@ -450,17 +452,24 @@ var CookieStore = {
    * @return The newly created Map instance mapping cookie names to
    *         internal jscookies, in the given path of the given host.
    */
   _ensureMap: function (cookie) {
     if (!this._hosts.has(cookie.host)) {
       this._hosts.set(cookie.host, new Map());
     }
 
-    let pathToNamesMap = this._hosts.get(cookie.host);
+    let originAttributesMap = this._hosts.get(cookie.host);
+    // If cookie.originAttributes is null, originAttributes will be an empty string.
+    let originAttributes = ChromeUtils.originAttributesToSuffix(cookie.originAttributes);
+    if (!originAttributesMap.has(originAttributes)) {
+      originAttributesMap.set(originAttributes, new Map());
+    }
+
+    let pathToNamesMap = originAttributesMap.get(originAttributes);
 
     if (!pathToNamesMap.has(cookie.path)) {
       pathToNamesMap.set(cookie.path, new Map());
     }
 
     return pathToNamesMap.get(cookie.path);
   }
 };
--- a/browser/components/sessionstore/test/browser_sessionStoreContainer.js
+++ b/browser/components/sessionstore/test/browser_sessionStoreContainer.js
@@ -64,8 +64,78 @@ add_task(function* () {
     Assert.equal(docShell.getOriginAttributes().userContextId,
                  args.expectedId,
                  "The docShell has the correct userContextId");
   });
 
   yield promiseRemoveTab(tab2);
 });
 
+// Opens "uri" in a new tab with the provided userContextId and focuses it.
+// Returns the newly opened tab.
+function* openTabInUserContext(userContextId) {
+  // Open the tab in the correct userContextId.
+  let tab = gBrowser.addTab("http://example.com", { userContextId });
+
+  // Select tab and make sure its browser is focused.
+  gBrowser.selectedTab = tab;
+  tab.ownerDocument.defaultView.focus();
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+  return { tab, browser };
+}
+
+function waitForNewCookie() {
+  return new Promise(resolve => {
+    Services.obs.addObserver(function observer(subj, topic, data) {
+      let cookie = subj.QueryInterface(Ci.nsICookie2);
+      if (data == "added") {
+        Services.obs.removeObserver(observer, topic);
+        resolve();
+      }
+    }, "cookie-changed", false);
+  });
+}
+
+add_task(function* test() {
+  const USER_CONTEXTS = [
+    "default",
+    "personal",
+    "work",
+  ];
+
+  const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+  const { TabStateFlusher } = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
+
+  // Make sure userContext is enabled.
+  yield SpecialPowers.pushPrefEnv({
+    "set": [ [ "privacy.userContext.enabled", true ] ]
+  });
+
+  let lastSessionRestore;
+  for (let userContextId of Object.keys(USER_CONTEXTS)) {
+    // Load the page in 3 different contexts and set a cookie
+    // which should only be visible in that context.
+    let cookie = USER_CONTEXTS[userContextId];
+
+    // Open our tab in the given user context.
+    let { tab, browser } = yield* openTabInUserContext(userContextId);
+
+    yield Promise.all([
+      waitForNewCookie(),
+      ContentTask.spawn(browser, cookie, cookie => content.document.cookie = cookie)
+    ]);
+
+    // Ensure the tab's session history is up-to-date.
+    yield TabStateFlusher.flush(browser);
+
+    lastSessionRestore = ss.getWindowState(window);
+
+    // Remove the tab.
+    gBrowser.removeTab(tab);
+  }
+
+  let state = JSON.parse(lastSessionRestore);
+  is(state.windows[0].cookies.length, USER_CONTEXTS.length,
+    "session restore should have each container's cookie");
+});
+
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2425,22 +2425,16 @@ toolbarbutton.chevron > .toolbarbutton-m
 %include ../shared/tabs.inc.css
 
 .tab-label {
   margin-top: 1px;
   margin-bottom: 0;
   text-align: center;
 }
 
-@media (min-resolution: 1.1dppx) {
-  .tab-throbber[progress] {
-    list-style-image: url("chrome://global/skin/icons/loading@2x.png");
-  }
-}
-
 @media (-moz-mac-yosemite-theme) {
   /* image preloading hack from shared/tabs.inc.css */
   #tabbrowser-tabs::before {
     background-image:
       url(chrome://browser/skin/yosemite/tab-selected-end-inactive.svg),
       url(chrome://browser/skin/yosemite/tab-selected-start-inactive.svg),
       url(chrome://browser/skin/yosemite/tab-stroke-start-inactive.png),
       url(chrome://browser/skin/yosemite/tab-active-middle-inactive.png),
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -552,9 +552,13 @@
 
   .tab-throbber[busy] {
     list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
   }
 
   .tab-icon-image {
     list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
   }
+
+  .tab-throbber[progress] {
+    list-style-image: url("chrome://global/skin/icons/loading@2x.png");
+  }
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1912,22 +1912,16 @@ html|span.ac-emphasize-text-url {
   #main-window[sizemode=normal] #TabsToolbar {
     padding-left: 2px;
     padding-right: 2px;
   }
 }
 
 %include ../shared/tabs.inc.css
 
-@media (min-resolution: 1.1dppx) {
-  .tab-throbber[progress] {
-    list-style-image: url("chrome://global/skin/icons/loading@2x.png");
-  }
-}
-
 /* Remove border between tab strip and navigation toolbar on Windows 10+ */
 @media not all and (-moz-os-version: windows-xp) {
   @media not all and (-moz-os-version: windows-vista) {
     @media not all and (-moz-os-version: windows-win7) {
       @media not all and (-moz-os-version: windows-win8) {
         @media (-moz-windows-default-theme) {
           .tab-background-end[visuallyselected=true]::after,
           .tab-background-start[visuallyselected=true]::after {
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -635,9 +635,53 @@ def debug_flags(env_debug_flags, enable_
         return enable_debug_flags[0]
     if env_debug_flags:
         return env_debug_flags[0]
     return default_debug_flags
 
 set_config('MOZ_DEBUG_FLAGS', debug_flags)
 add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags)
 
+# Some standard library headers (notably bionic on Android) declare standard
+# functions (e.g. getchar()) and also #define macros for those standard
+# functions.  libc++ deals with this by doing something like the following
+# (explanatory comments added):
+#
+#   #ifdef FUNC
+#   // Capture the definition of FUNC.
+#   inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
+#   #undef FUNC
+#   // Use a real inline definition.
+#   inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
+#   #endif
+#
+# _LIBCPP_INLINE_VISIBILITY is typically defined as:
+#
+#   __attribute__((__visibility__("hidden"), __always_inline__))
+#
+# Unfortunately, this interacts badly with our system header wrappers, as the:
+#
+#   #pragma GCC visibility push(default)
+#
+# that they do prior to including the actual system header is treated by the
+# compiler as an explicit declaration of visibility on every function declared
+# in the header.  Therefore, when the libc++ code above is encountered, it is
+# as though the compiler has effectively seen:
+#
+#   int FUNC(...) __attribute__((__visibility__("default")));
+#   int FUNC(...) __attribute__((__visibility__("hidden")));
+#
+# and the compiler complains about the mismatched visibility declarations.
+#
+# However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
+# existing definition.  We can therefore define it to the empty string (since
+# we are properly managing visibility ourselves) and avoid this whole mess.
+# Note that we don't need to do this with gcc, as libc++ detects gcc and
+# effectively does the same thing we are doing here.
+@depends(c_compiler, target)
+def libcxx_inline_visibility(c_compiler, target):
+    if c_compiler.type == 'clang' and target.os == 'Android':
+        return ''
+
+set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_inline_visibility)
+set_define('_LIBCPP_INLINE_VISIBILITY_EXCEPT_GCC49', libcxx_inline_visibility)
+
 include('rust.configure')
index 484fdd73d8477155422d9d5ccf6b7c2bdefd8c6e..9e61dec1d598d7c00e07eb9d03f89784fa450485
GIT binary patch
literal 4648
zc$}43cTm&YwoVAWOK%>U0s(<g6hQ(A(tEQ|r9=!>S_mLjiZlfwAXVXDs8T`)1*w71
zTj)h3RFU36F5a2@&b!VzGw<y^YtMYY`R#9iYwxxGSj#{gM9c&L0LTI4g|iwgA&b70
zGynjM9sv0DRae#IuBfJ-nz)ObAKcm5M$E(0X66C>b`s6ht(fvs{!>X9xck}byBNkl
zldiuJm3}Lo%r3E5&dIL*#<DtI;M!>##$I2`5Wx_g?+oioO7VM_w;a&OJy*JU^y=HW
z&%47auCaibF>fm@MZ9b|M1UPfffL|-3CvJmk|yiS5O_>a-cAH*UV%;UKECTG7fh&O
z6nfnw2Z&!i1xn^B&=HY_G(+NHz~Kqa;0<wc`fe)&<Z}NtdTk+U+UiOr3Q*{2FgvY`
z0O>qbzU<|<dv4-6GRa|r3<i_{k2xGs%E@q{lXEm=SD^k#Zu1V$jja-5`DSLDkllmU
zJKqe1lx!3MM2ZXhDU3tQm&Pvg+S{F%;rSvo2$m@5*N~G0Xm8T8C)(CJInyG%xaN1<
z`E%!jxpdbdNotUho?xd^01$qvbVZ39l;m<e&SV%Iy-*&c!XwYp4tjPWoYmgLxg2uX
z##lBRbhbRUOC*094D5TNvv)nd3`w-W^@k5hd-+)PJIj#OpEu_Fh16~z)&96dTN(+^
z6t3k;B`CFA(NO4{MT#>FMXs!WesCVdq)p0wavB5}9KPH6n701&Vb5bpwtI(xjPfY2
zi)}TY<bEYZL7|-3I{*DD=9@24^=gi;yx@7Du;z!>_dAd(#YPN*UuP*&=59icJ%atq
z^fI%RbA@rDMzjn-4vY%-!_rEKmNnH&m)x4#MGscjkCslY7d-|O>^>(F6_A5mRnLbZ
z9v)DV3B%)oheAO@W``X&WG@7kG){Q6vqpxb{eAh_5#8Gs(lMVGs(zx0E0ieAk<y@E
zL~Y*vcqd1*P1jBrkfvP0CnhLrH=Brqki&~oJ1a#o={wED1^`~Yi%Vf$7GJseMDpA%
z*EdKsR)zvfI6xnijiRUIV<bHroK!gaEDJl_sn1;{vczr=NZ_pbTDt6L=NyDz^1bNU
z0(u*WV~c5y(49|a5Vmqlc95;Gy}<oU0%KX?U4RUkKaSZvH&@<yQkwAx&q_o6r*nLf
z!O3d({6q8X>$2)A87oJvWw2H|(tdEIj9Rwu*xPPv@4Lu*k5)U}kS1e&lHU6*YpJ<I
zb&y*nf12sYiPNo73Gzt~>Gms3#0Cl_p%Fi`nw^BRES4WPV46*$%8C|m==BB4NTZZF
zc)7Ch#H1+=ZIO*?c{XlEKB|=_i=nTRafMv4+EVNrR1f>62BQW{iy&g^&=lP@Q!qXM
zz*?>bGJa)#<&H$^6^8F$m3MwFKz4L9rY;mVZtvm_u*8f(qkXlHAjJ2`POxa@12TT2
zCZy|bh?!Hs7opw5kzpUAIR%xWqb~{SEYm?{GbJYj3p)khdd5D*+%+>zw*^hRpRs|%
z$qW5^W`x$Oay@1~@e^}mF(8V`I?6kN*l_ln<r!LY;vFQiOS6`M0`o^BcQ+8JKA9%|
z=fZQP9%H2G=9<EV+9(&t@!*U%VdLD5cAOPaliLGQvhe&24e&y;pUh;PWK(7&pE}P7
zrx^UPyuh#qgM%5mM3^!(_R-i%1$<kRwVIJWvK>j~ljz5gmvbG1p^Qt4g!GPN$x+U|
z*~E$ra=cjyb!^9z(xIeo@SenMW+2|^>GF$~taZW^t=NJlxp=1E_w}X+t?34NbS>bF
z*@Mjgkl~K#SU{mnlyzs?%))wW8psl9-#Ecoii=$A2%y=@N!9xI-=2n*sfe0Rx!HYp
z=8DTV)lM69wv8B{0p!kews}3Op}=>4LVVZbx0_gX-x3WoE7)4pshNfN7XsB7^9`j2
zbg2v7eW=dtT0QBj0_L(IEdd;kPAIJZqvrkbqmiNkV50tttZ;zH5-8K_RPfNj4VHCk
zX%PTzjdHfHWq3QybW2x;l6R8ORuqyhNcf2UF(~;-ggc}#ch%()E}5UO<kF1)p4sYD
zMRMKVHfV_}JrX^=c)$@VUXX|PY}8lURef=9PJl};nE%99eKMiVzd{Bm=}g9>;t*sg
z7nVqSb_lka`L;WMVJycAx)(SoTPl-Ki}iZAIQR7Gy5^Z?D-OWnZF1#faei*Ce(R~h
z>fBb-()5`d^(;Q}kB^K?f!#M|lfd&jmSry)_B0c$bWg{Bq<Mz23?06#$pe2~KpT~J
zZl#mmJe-N$w1mLxCEvj;W(7{2R|an4w>dfeYL?*RI&oeQDnfpep*eGA2kmF>LIy$6
z(?9dmg&IMb!LQ4zSu!jYh1S|^tRY?mzvot8qD&LSLX4AT`DPyVw*P3`q2nOG$U3Te
zyEOG4LtnQ;5;I)VMZE0UoKm=-&SJr<Ro7U`l*q*4I|RC_M~*x@Qj*Rq(dI4b(pKL^
zaRna3>=42p6?O~PTrs9*d8^njQRPy+9#+tIsMQmDQ$}%HdItKBe6Kv?uZ<BXw}Uz2
zX<A2vH_`Y&GV8F<R1etvCkE1O&Z${vUvhIEi*ZZ}$hWz%(9|$B)X(J0YNRz)9d&N6
zP)a5U#v9(3RZ9CBBV&<aR%ldlWdpjCwy;l>;+*6?Xy^*QodDf3xEN^(Xzhm=mm53I
zoxCvS9}!PZnlc#;bdgIyZ*3$^5RBDJD~qhj(>z$d<D*rklLR9yt36GGN4De~kUwYb
zSk!bn2Ix7J#nIhtM(}GWi+8Nchs`;jBM;uRN!%V|EZjaYEA|aIe`@TmqW(HfJcHey
zA>rt=622c>uZckh|EXQ;#<Q|i%&&}=*1e8c5sPzf!rR98N6|jKs4SAE9-X@vd)4;d
z^=S4R-nQ|N$$NI50@=N&d0r#|@t?DqLR40P<m8o9Wt;r3J_`zYxy9K4HWiYmvBdzX
zSVI=loIHnMrNELj@;gbaI2B5d#q2(V#3w~i8`T6p;gjdKgG$K3MoTV^EV8iWux%4X
zvq}Nfr|U=ECMf%u7#7*?4`a)bk#iL1L1<!0a^m+kI;=v1N=c$ndM4p^5%;KjJ4VI^
zNAITc=VMKu9UR0Tw)p;hF&>!JH1sujK4Dv5==w!`7U%0ey*%9vIa}z2`+#I|XbQj0
zSg-GT_}f!M_uW)pay8EM58yM2`40cgoc?*sMum+cY!Mj~RX9EzHL7=_ve%6>(e5D=
ziA=~GCi8m9a6$&Hn(OO-R)}$;+FLW-t`f)1MC{0LM=zY*nk?c?w>eRF2>|c;`C7Md
zgfCqgxyH{|?_x8wB$#^=&8WPWB3TC$FuE=8gmFwPnn(1z)$bI|fU3qbva3PFQj4g%
z3=Gl3km3sPu^#IM+wp7iCn|KqSEC0qIW6qJSuHFlFRx^{FW{b*nD}drGF2H!W>vT1
z3W@_e-Y<oHOL-N~GI3ZI%iftytQC0m1LN?+f{b3Wb3`xDr`*0r*$>{B#C(t$S8?oz
zPtiW{u#;hVU7=PS{Ntu{u&^XI7e0f9A?qF7jOFZJ)mr6dOo5FrE>KBQR)v7cO1x_Q
z&iDGvha)AD622h3X~_nBabY9JWbNackdJ1Ew?doB*Vs5up8}u7Fe2+kK#uX-Q{%~F
zm^+2uI(m3UP407MXT(&yt)|Ycs*jEVZa9-$sXN$EFxHco3R{hMlC%1*=rnwm!EJYR
zVd#$_%*_*t6mzkob)q4oJ)67$te<|KqE+xSM3JIx%LAc99;H;j_aq`=J3@5CGL<|j
zn8w?oYipZ@*u1wU53M5N-gnX$K~?R_1p|V&ST97ei{kJD^KQZf=&XtHu0c==B6!P<
z3L5>5yPk~x!X-TCptiMvaJEx<Q3G?uJ2$abuwuMstsPFWtD$e2z4rC}(yrbgUvD8D
z9D{t<vGaV=91-iBM7<m5jao=yUfRddb7zz=1;%u<F*-n9=I&9_H5>a2;LQ2pK)e`n
z$iND9A(HetVt<Q#->ANjU3O-7X}efxs@n`HrM}w8-V?SA(~hjvvyACU6Lw~iTiq*S
zhL_~77rHQZ4|YS%h(o-s(R6FrH9c$}(nSv5el6bG1?w_r&q6!CBJI_>9R+u&$HlC~
zEaX+BtMwt@gy1ps$?L7)srw1Ki5A^5V>YS8tK6*Q3KkMS@C|hz85R3dqHbp1^nHbA
zpvVPVUp4#u^Lg*YMNC+=e|*@f0Kxqj_|aT0d9-`k_|C#Cd>m);<$k3;{(v^c$2@W4
zMC5%8KQpg>O-n@9LE)Tl9W#w5t!`8x(T1OU0W2{dAvUJNZ?D}tk3HTHbOmQ_S)8|S
z&?SFVtQ0^vvvGC@%D5!R7^4D`>EfXGt%vq}Rp*1*8s3tc35><bih0&&pYN{qwLbDE
z$ySP)t>ag-vrU6rf&5-Lk|4N9t&gY5Li<;r--h;~hkB;JVute0>X^A94h=8Yq3~)8
z9CBFGzH7O;3$0KI^eGECXKF7`KaNJ~9W@TeuExvb0_xmH`@U)%hV7+FcCe{dr$n%3
zcs&h8iOxr3Hxre4jU2pQ7P~vDbJ>z^7n7T)iim4H-mo_KpV~>2I#Q#c&i9u4C3=2o
zY?spqW(z}liQ2ijdcj=1L_O_1;Lp6o96e1<NC2SY43Y=lNGbr3cm)Ii{Cm;v>lf+k
z*BtHc(jPPMI5?zo-={JpN6_w2lN)Yr`}p{H8xB=2Eok)#;rew)MTC1rx`>C4eH#nf
z+oM(s{Gz~Mh1B-z1-=EU92|A+t7(+g2+C0lO3JQI($iD$Q9UWyo%#mhRiR*$N1mgO
z9zeif@*f7oYM>8Kn%XW=C=dXkxs>l>;|hNQ^ZcdQ&Gp}!ADmMEt~vZw7i}nnK4Mc|
zby$ji?`iBdG?v$ny=pSNQZ+8j$LHyzS5}ifO+7LJ9c*Kd7nuX{tyex(Mr@AX;-&#{
zz~@7B1d1u1679BhB`ZwHhGbzMztGerTpTmE6?qun*9@;VHTqK9o;aGQwno!FEXGfP
zNo8+u(|BpC%vE)+_2E6!{oRKgD_8k!g+~e`jPxYRZ;a8mUH!B9@V&{5EP?JUqd!D_
zXBOGq@1c}%fU+;q982YBK|0x~=JDSOrdroscJ2%`_^%2W2$l8jy+nM#-z(t%+=t7P
zxL3Mvp%6gC<~j3cP78MoF9n$n^GMlywyIgCJNcC)Q=}u-rS{w@mI(jEWzAVZ1&ssE
zs`6VV@cWu{lL5qEuy+mUqTR+7*R_axcJ!!`R^TU;*-$n+GPL327t3XhwPd~?y!Msi
zDGRWve9cgeJ0e7LWSPfKH&v28y3tT#RV9V{6KD7ORP;LCS5Fi#M4>}P<rpmv;kXY{
z?Q3F7bKthXJE(^JX|S!2fn6?(q3%WQcdKo9Vrk3MJHy~5y|=ICEG4S6YXZ>nfd6)%
z7Sn3sc!_*3jq&dq0G>~%`gHoWZ-7O$MrpJ~h5LH?D~uo`Qu90B5~JQ-I{KnwgZsKY
z-8vx2Vx}EFQOi&JBE8zm-Fw<Gdo<HSiLE^#7nizFC>M<c%3qG~Hj?05D?qSE35JI=
zFZl|b+8@J?V2)&y9`3&a6iCDb`seP%e?~A6@DII;UkrbM{y9f~jbEE9L*y@c|Gydg
zzuEsTh5lkcy=4DCCDiXO{~nHixlBa&_rdu)!|yNp7Xy&~Z{OEIn}qb&4<wfpad~V8
H{V(({xVLMX
--- a/devtools/client/framework/test/browser_toolbox_options_disable_js.js
+++ b/devtools/client/framework/test/browser_toolbox_options_disable_js.js
@@ -2,108 +2,123 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that disabling JavaScript for a tab works as it should.
 
 const TEST_URI = URL_ROOT + "browser_toolbox_options_disable_js.html";
 
-var doc;
-var toolbox;
-
 function test() {
   gBrowser.selectedTab = gBrowser.addTab();
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
-    doc = content.document;
     gDevTools.showToolbox(target).then(testSelectTool);
   }, true);
 
-  content.location = TEST_URI;
+  BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
 }
 
-function testSelectTool(aToolbox) {
-  toolbox = aToolbox;
-  toolbox.once("options-selected", testJSEnabled);
+function testSelectTool(toolbox) {
+  toolbox.once("options-selected", () => testToggleJS(toolbox));
   toolbox.selectTool("options");
 }
 
-function testJSEnabled(event, tool, secondPass) {
+let testToggleJS = Task.async(function* (toolbox) {
   ok(true, "Toolbox selected via selectTool method");
+
+  yield testJSEnabled();
+  yield testJSEnabledIframe();
+
+  // Disable JS.
+  yield toggleJS(toolbox);
+
+  yield testJSDisabled();
+  yield testJSDisabledIframe();
+
+  // Re-enable JS.
+  yield toggleJS(toolbox);
+
+  yield testJSEnabled();
+  yield testJSEnabledIframe();
+
+  finishUp(toolbox);
+});
+
+function* testJSEnabled() {
   info("Testing that JS is enabled");
 
-  // We use executeSoon here because switching docSehll.allowJavascript to true
+  // We use waitForTick here because switching docShell.allowJavascript to true
   // takes a while to become live.
-  executeSoon(function () {
+  yield waitForTick();
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+    let doc = content.document;
     let output = doc.getElementById("output");
     doc.querySelector("#logJSEnabled").click();
     is(output.textContent, "JavaScript Enabled", 'Output is "JavaScript Enabled"');
-    testJSEnabledIframe(secondPass);
   });
 }
 
-function testJSEnabledIframe(secondPass) {
+function* testJSEnabledIframe() {
   info("Testing that JS is enabled in the iframe");
 
-  let iframe = doc.querySelector("iframe");
-  let iframeDoc = iframe.contentDocument;
-  let output = iframeDoc.getElementById("output");
-  iframeDoc.querySelector("#logJSEnabled").click();
-  is(output.textContent, "JavaScript Enabled",
-                          'Output is "JavaScript Enabled" in iframe');
-  if (secondPass) {
-    finishUp();
-  } else {
-    toggleJS().then(testJSDisabled);
-  }
+  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+    let doc = content.document;
+    let iframe = doc.querySelector("iframe");
+    let iframeDoc = iframe.contentDocument;
+    let output = iframeDoc.getElementById("output");
+    iframeDoc.querySelector("#logJSEnabled").click();
+    is(output.textContent, "JavaScript Enabled",
+                            'Output is "JavaScript Enabled" in iframe');
+  });
 }
 
-let toggleJS = Task.async(function* () {
+function* toggleJS(toolbox) {
   let panel = toolbox.getCurrentPanel();
   let cbx = panel.panelDoc.getElementById("devtools-disable-javascript");
 
   if (cbx.checked) {
     info("Clearing checkbox to re-enable JS");
   } else {
     info("Checking checkbox to disable JS");
   }
 
   let browserLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
   cbx.click();
   yield browserLoaded;
-  doc = content.document;
-});
+}
 
-function testJSDisabled() {
+function* testJSDisabled() {
   info("Testing that JS is disabled");
 
-  let output = doc.getElementById("output");
-  doc.querySelector("#logJSDisabled").click();
-
-  ok(output.textContent !== "JavaScript Disabled",
-     'output is not "JavaScript Disabled"');
-  testJSDisabledIframe();
-}
+  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+    let doc = content.document;
+    let output = doc.getElementById("output");
+    doc.querySelector("#logJSDisabled").click();
 
-function testJSDisabledIframe() {
-  info("Testing that JS is disabled in the iframe");
-
-  let iframe = doc.querySelector("iframe");
-  let iframeDoc = iframe.contentDocument;
-  let output = iframeDoc.getElementById("output");
-  iframeDoc.querySelector("#logJSDisabled").click();
-  ok(output.textContent !== "JavaScript Disabled",
-     'output is not "JavaScript Disabled" in iframe');
-  toggleJS().then(function () {
-    testJSEnabled(null, null, true);
+    ok(output.textContent !== "JavaScript Disabled",
+       'output is not "JavaScript Disabled"');
   });
 }
 
-function finishUp() {
+function* testJSDisabledIframe() {
+  info("Testing that JS is disabled in the iframe");
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+    let doc = content.document;
+    let iframe = doc.querySelector("iframe");
+    let iframeDoc = iframe.contentDocument;
+    let output = iframeDoc.getElementById("output");
+    iframeDoc.querySelector("#logJSDisabled").click();
+    ok(output.textContent !== "JavaScript Disabled",
+       'output is not "JavaScript Disabled" in iframe');
+  });
+}
+
+function finishUp(toolbox) {
   toolbox.destroy().then(function () {
     gBrowser.removeCurrentTab();
-    toolbox = doc = null;
     finish();
   });
 }
--- a/devtools/client/webconsole/console-output.js
+++ b/devtools/client/webconsole/console-output.js
@@ -28,16 +28,18 @@ const WebConsoleUtils = require("devtool
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const {Task} = require("devtools/shared/task");
 const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
 const nodeConstants = require("devtools/shared/dom-node-constants");
 
 const MAX_STRING_GRIP_LENGTH = 36;
 const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
 
+const validProtocols = /^(http|https|ftp|data|javascript|resource|chrome):/i;
+
 // Constants for compatibility with the Web Console output implementation before
 // bug 778766.
 // TODO: remove these once bug 778766 is fixed.
 const COMPAT = {
   // The various categories of messages.
   CATEGORIES: {
     NETWORK: 0,
     CSS: 1,
@@ -2218,19 +2220,21 @@ Widgets.URLString.prototype = extend(Wid
    *
    * @param string token
    *        The token.
    * @return boolean
    *         Whenther the token is a URL.
    */
   _isURL: function (token) {
     try {
-      let url = new URL(token);
-      return ["http:", "https:", "ftp:", "data:", "javascript:",
-              "resource:", "chrome:"].includes(url.protocol);
+      if (!validProtocols.test(token)) {
+        return false;
+      }
+      new URL(token);
+      return true;
     } catch (e) {
       return false;
     }
   },
 
   /**
    * Renders a string as a URL.
    *
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -58,19 +58,19 @@ using ::google::protobuf::io::ZeroCopyIn
 using JS::ubi::AtomOrTwoByteChars;
 using JS::ubi::ShortestPaths;
 
 MallocSizeOf
 GetCurrentThreadDebuggerMallocSizeOf()
 {
   auto ccrt = CycleCollectedJSRuntime::Get();
   MOZ_ASSERT(ccrt);
-  auto rt = ccrt->Runtime();
-  MOZ_ASSERT(rt);
-  auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt);
+  auto cx = ccrt->Context();
+  MOZ_ASSERT(cx);
+  auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(cx);
   MOZ_ASSERT(mallocSizeOf);
   return mallocSizeOf;
 }
 
 /*** Cycle Collection Boilerplate *****************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HeapSnapshot, mParent)
 
@@ -1241,18 +1241,17 @@ public:
     auto typeName = TwoByteString(ubiNode.typeName());
     if (NS_WARN_IF(!attachTwoByteString(typeName,
                                         [&] (std::string* name) { protobufNode.set_allocated_typename_(name); },
                                         [&] (uint64_t ref) { protobufNode.set_typenameref(ref); })))
     {
       return false;
     }
 
-    JSRuntime* rt = JS_GetRuntime(cx);
-    mozilla::MallocSizeOf mallocSizeOf = dbg::GetDebuggerMallocSizeOf(rt);
+    mozilla::MallocSizeOf mallocSizeOf = dbg::GetDebuggerMallocSizeOf(cx);
     MOZ_ASSERT(mallocSizeOf);
     protobufNode.set_size(ubiNode.size(mallocSizeOf));
 
     if (includeEdges) {
       auto edges = ubiNode.edges(JS_GetRuntime(cx), wantNames);
       if (NS_WARN_IF(!edges))
         return false;
 
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -55,21 +55,29 @@ GetComputedTimingDictionary(const Comput
                      ? PositiveInfinity<double>()
                      : static_cast<double>(aComputedTiming.mCurrentIteration);
     aRetVal.mCurrentIteration.SetValue(iteration);
   }
 }
 
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly,
-                                   AnimationEffectReadOnly,
-                                   mTarget,
-                                   mAnimation,
-                                   mTiming)
+NS_IMPL_CYCLE_COLLECTION_CLASS(KeyframeEffectReadOnly)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(KeyframeEffectReadOnly,
+                                                AnimationEffectReadOnly)
+  if (tmp->mTiming) {
+    tmp->mTiming->Unlink();
+  }
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTarget, mAnimation, mTiming)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(KeyframeEffectReadOnly,
+                                                  AnimationEffectReadOnly)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget, mAnimation, mTiming)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly,
                                                AnimationEffectReadOnly)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly)
 NS_INTERFACE_MAP_END_INHERITING(AnimationEffectReadOnly)
 
@@ -512,37 +520,77 @@ KeyframeEffectReadOnly::HasAnimationOfPr
   for (size_t i = 0; i < aPropertyCount; i++) {
     if (HasAnimationOfProperty(aProperties[i])) {
       return true;
     }
   }
   return false;
 }
 
+#ifdef DEBUG
+bool
+SpecifiedKeyframeArraysAreEqual(const nsTArray<Keyframe>& aA,
+                                const nsTArray<Keyframe>& aB)
+{
+  if (aA.Length() != aB.Length()) {
+    return false;
+  }
+
+  for (size_t i = 0; i < aA.Length(); i++) {
+    const Keyframe& a = aA[i];
+    const Keyframe& b = aB[i];
+    if (a.mOffset         != b.mOffset ||
+        a.mTimingFunction != b.mTimingFunction ||
+        a.mPropertyValues != b.mPropertyValues) {
+      return false;
+    }
+  }
+
+  return true;
+}
+#endif
+
 void
 KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
 {
   MOZ_ASSERT(aStyleContext);
 
   nsTArray<AnimationProperty> properties;
   if (mTarget) {
+    // When GetComputedKeyframeValues or GetAnimationPropertiesFromKeyframes
+    // calculate computed values from |mKeyframes|, they could possibly
+    // trigger a subsequent restyle in which we rebuild animations. If that
+    // happens we could find that |mKeyframes| is overwritten while it is
+    // being iterated over. Normally that shouldn't happen but just in case we
+    // make a copy of |mKeyframes| first and iterate over that instead.
+    auto keyframesCopy(mKeyframes);
+
     nsTArray<ComputedKeyframeValues> computedValues =
-      KeyframeUtils::GetComputedKeyframeValues(mKeyframes, mTarget->mElement,
+      KeyframeUtils::GetComputedKeyframeValues(keyframesCopy,
+                                               mTarget->mElement,
                                                aStyleContext);
 
     if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
-      KeyframeUtils::ApplySpacing(mKeyframes, SpacingMode::paced,
+      KeyframeUtils::ApplySpacing(keyframesCopy, SpacingMode::paced,
                                   mEffectOptions.mPacedProperty,
                                   computedValues);
     }
 
     properties =
-      KeyframeUtils::GetAnimationPropertiesFromKeyframes(mKeyframes,
+      KeyframeUtils::GetAnimationPropertiesFromKeyframes(keyframesCopy,
                                                          computedValues,
                                                          aStyleContext);
+
+#ifdef DEBUG
+    MOZ_ASSERT(SpecifiedKeyframeArraysAreEqual(mKeyframes, keyframesCopy),
+               "Apart from the computed offset members, the keyframes array"
+               " should not be modified");
+#endif
+
+    mKeyframes.SwapElements(keyframesCopy);
   }
 
   if (mProperties == properties) {
     return;
   }
 
   // Preserve the state of mWinsInCascade and mIsRunningOnCompositor flags.
   nsCSSPropertySet winningInCascadeProperties;
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -11,16 +11,17 @@
  */
 
 #include "mozilla/dom/ElementInlines.h"
 
 #include "AnimationCommon.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/Attr.h"
+#include "mozilla/dom/Grid.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "nsIContentInlines.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "nsIDocumentInlines.h"
 #include "mozilla/dom/DocumentTimeline.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMDocument.h"
@@ -948,17 +949,16 @@ Element::GetClientRects()
 
   nsLayoutUtils::RectListBuilder builder(rectList);
   nsLayoutUtils::GetAllInFlowRects(frame,
           nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
           nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
   return rectList.forget();
 }
 
-
 //----------------------------------------------------------------------
 
 void
 Element::AddToIdTable(nsIAtom* aId)
 {
   NS_ASSERTION(HasID(), "Node doesn't have an ID?");
   if (IsInShadowTree()) {
     ShadowRoot* containingShadow = GetContainingShadow();
@@ -3284,16 +3284,31 @@ Element::RequestFullscreen(JSContext* aC
 }
 
 void
 Element::MozRequestPointerLock()
 {
   OwnerDoc()->RequestPointerLock(this);
 }
 
+void
+Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult)
+{
+  nsIFrame* frame = GetPrimaryFrame();
+  if (frame && (frame->GetType() == nsGkAtoms::gridContainerFrame)) {
+    // If primary frame is a nsGridContainerFrame, all the next frames
+    // in flow will also be nsGridContainerFrame.
+    for (; frame != nullptr; frame = frame->GetNextInFlow()) {
+      aResult.AppendElement(
+        new Grid(this, static_cast<nsGridContainerFrame*>(frame))
+      );
+    }
+  }
+}
+
 already_AddRefed<Animation>
 Element::Animate(JSContext* aContext,
                  JS::Handle<JSObject*> aKeyframes,
                  const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
                  ErrorResult& aError)
 {
   Nullable<ElementOrCSSPseudoElement> target;
   target.SetValue().SetAsElement() = this;
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -135,16 +135,17 @@ class EventStateManager;
 namespace dom {
 
 class Animation;
 class Link;
 class UndoManager;
 class DOMRect;
 class DOMRectList;
 class DestinationInsertionPointList;
+class Grid;
 
 // IID for the dom::Element interface
 #define NS_ELEMENT_IID \
 { 0xc67ed254, 0xfd3b, 0x4b10, \
   { 0x96, 0xa2, 0xc5, 0x8b, 0x7b, 0x64, 0x97, 0xd1 } }
 
 class Element : public FragmentOrElement
 {
@@ -825,16 +826,18 @@ public:
   int32_t ScrollLeftMax()
   {
     nsIScrollableFrame* sf = GetScrollFrame();
     return sf ?
            nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().XMost()) :
            0;
   }
 
+  void GetGridFragments(nsTArray<RefPtr<Grid>>& aResult);
+
   virtual already_AddRefed<UndoManager> GetUndoManager()
   {
     return nullptr;
   }
 
   virtual bool UndoScope()
   {
     return false;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11147,25 +11147,24 @@ nsGlobalWindow::ShowSlowScriptDialog()
                          (nsIPrompt::BUTTON_TITLE_IS_STRING *
                           (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
 
   // Add a third button if necessary.
   if (showDebugButton)
     buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
 
   // Null out the operation callback while we're re-entering JS here.
-  JSRuntime* rt = JS_GetRuntime(cx);
-  JSInterruptCallback old = JS_SetInterruptCallback(rt, nullptr);
+  JSInterruptCallback old = JS_SetInterruptCallback(cx, nullptr);
 
   // Open the dialog.
   rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
                          debugButton, neverShowDlg, &neverShowDlgChk,
                          &buttonPressed);
 
-  JS_SetInterruptCallback(rt, old);
+  JS_SetInterruptCallback(cx, old);
 
   if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
     return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;
   }
   if (buttonPressed == 2) {
     if (debugCallback) {
       rv = debugCallback->HandleSlowScriptDebug(this);
       return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -53,17 +53,17 @@ public:
   // the entry point computed from aCallback). If no override is required, the
   // caller should pass null.  |aCx| is used to capture the current
   // stack, which is later used as an async parent when the callback
   // is invoked.  aCx can be nullptr, in which case no stack is
   // captured.
   explicit CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
                           nsIGlobalObject* aIncumbentGlobal)
   {
-    if (aCx && JS::RuntimeOptionsRef(aCx).asyncStack()) {
+    if (aCx && JS::ContextOptionsRef(aCx).asyncStack()) {
       JS::RootedObject stack(aCx);
       if (!JS::CaptureCurrentStack(aCx, &stack)) {
         JS_ClearPendingException(aCx);
       }
       Init(aCallback, stack, aIncumbentGlobal);
     } else {
       Init(aCallback, nullptr, aIncumbentGlobal);
     }
@@ -225,17 +225,17 @@ protected:
 
   // Just like the public version without the FastCallbackConstructor argument,
   // except for not calling HoldJSObjects.  If you use this, you MUST ensure
   // that the object is traced until the HoldJSObjects happens!
   CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
                  nsIGlobalObject* aIncumbentGlobal,
                  const FastCallbackConstructor&)
   {
-    if (aCx && JS::RuntimeOptionsRef(aCx).asyncStack()) {
+    if (aCx && JS::ContextOptionsRef(aCx).asyncStack()) {
       JS::RootedObject stack(aCx);
       if (!JS::CaptureCurrentStack(aCx, &stack)) {
         JS_ClearPendingException(aCx);
       }
       InitNoHold(aCallback, stack, aIncumbentGlobal);
     } else {
       InitNoHold(aCallback, nullptr, aIncumbentGlobal);
     }
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -69,16 +69,17 @@ LOCAL_INCLUDES += [
     '/dom/workers',
     '/dom/xbl',
     '/dom/xml',
     '/dom/xslt/base',
     '/dom/xslt/xpath',
     '/dom/xul',
     '/js/xpconnect/src',
     '/js/xpconnect/wrappers',
+    '/layout/generic',
     '/layout/style',
     '/layout/xul/tree',
     '/media/mtransport',
     '/media/webrtc/',
     '/media/webrtc/signaling/src/common/time_profiling',
     '/media/webrtc/signaling/src/peerconnection',
 ]
 
--- a/dom/camera/CameraPreviewMediaStream.cpp
+++ b/dom/camera/CameraPreviewMediaStream.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CameraPreviewMediaStream.h"
 #include "CameraCommon.h"
+#include "MediaStreamListener.h"
 
 /**
  * Maximum number of outstanding invalidates before we start to drop frames;
  * if we hit this threshold, it is an indicator that the main thread is
  * either very busy or the device is busy elsewhere (e.g. encoding or
  * persisting video data).
  */
 #define MAX_INVALIDATE_PENDING 4
@@ -84,31 +85,31 @@ CameraPreviewMediaStream::AddListener(Me
 
 void
 CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   MutexAutoLock lock(mMutex);
 
   RefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamListener::EVENT_REMOVED);
+  listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamGraphEvent::EVENT_REMOVED);
 }
 
 void
 CameraPreviewMediaStream::OnPreviewStateChange(bool aActive)
 {
   if (aActive) {
     MutexAutoLock lock(mMutex);
     if (!mTrackCreated) {
       mTrackCreated = true;
       VideoSegment tmpSegment;
       for (uint32_t j = 0; j < mListeners.Length(); ++j) {
         MediaStreamListener* l = mListeners[j];
         l->NotifyQueuedTrackChanges(mFakeMediaStreamGraph, TRACK_VIDEO, 0,
-                                    MediaStreamListener::TRACK_EVENT_CREATED,
+                                    TrackEventCommand::TRACK_EVENT_CREATED,
                                     tmpSegment);
         l->NotifyFinishedTrackCreation(mFakeMediaStreamGraph);
       }
     }
   }
 }
 
 void
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -62,22 +62,22 @@ public:
     if (!mCameraControl) {
       return;
     }
 
     mCameraControl->TrackCreated(aTrackID);
   }
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                StreamTime aTrackOffset, uint32_t aTrackEvents,
+                                StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
-    if (aTrackEvents & TRACK_EVENT_CREATED) {
+    if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod<TrackID>(
           this, &TrackCreatedListener::DoNotifyTrackCreated, aID));
     }
   }
 
 protected:
   ~TrackCreatedListener() {}
 
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1432,19 +1432,19 @@ protected:
     uint32_t mPixelStore_PackRowLength;
     uint32_t mPixelStore_PackSkipRows;
     uint32_t mPixelStore_PackSkipPixels;
     uint32_t mPixelStore_PackAlignment;
 
     CheckedUint32 GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
                                 uint32_t depth, uint8_t bytesPerPixel);
 
-    CheckedUint32 GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel,
-                              CheckedUint32* const out_startOffset,
-                              CheckedUint32* const out_rowStride);
+    bool ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
+                          uint8_t bytesPerPixel, uint32_t* const out_rowStride,
+                          uint32_t* const out_endOffset);
 
     GLenum mPixelStore_ColorspaceConversion;
     bool mPixelStore_FlipY;
     bool mPixelStore_PremultiplyAlpha;
 
     ////////////////////////////////////
     class FakeBlackTexture {
     public:
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1219,40 +1219,43 @@ WebGLContext::DoReadPixelsAndConvert(GLi
                                      GLenum destFormat, GLenum destType, void* destBytes,
                                      GLenum auxReadFormat, GLenum auxReadType)
 {
     GLenum readFormat = destFormat;
     GLenum readType = destType;
 
     if (gl->WorkAroundDriverBugs() &&
         gl->IsANGLE() &&
+        gl->Version() < 300 && // ANGLE ES2 doesn't support HALF_FLOAT reads properly.
         readType == LOCAL_GL_FLOAT &&
         auxReadFormat == destFormat &&
         auxReadType == LOCAL_GL_HALF_FLOAT)
     {
+        MOZ_RELEASE_ASSERT(!IsWebGL2()); // No SKIP_PIXELS, etc.
+
         readType = auxReadType;
 
+        const char funcName[] = "readPixels";
         const auto readBytesPerPixel = webgl::BytesPerPixel({readFormat, readType});
         const auto destBytesPerPixel = webgl::BytesPerPixel({destFormat, destType});
 
-        CheckedUint32 readOffset;
-        CheckedUint32 readStride;
-        const CheckedUint32 readSize = GetPackSize(width, height, readBytesPerPixel,
-                                                   &readOffset, &readStride);
-
-        CheckedUint32 destOffset;
-        CheckedUint32 destStride;
-        const CheckedUint32 destSize = GetPackSize(width, height, destBytesPerPixel,
-                                                   &destOffset, &destStride);
-        if (!readSize.isValid() || !destSize.isValid()) {
+        uint32_t readStride;
+        uint32_t readByteCount;
+        uint32_t destStride;
+        uint32_t destByteCount;
+        if (!ValidatePackSize(funcName, width, height, readBytesPerPixel, &readStride,
+                              &readByteCount) ||
+            !ValidatePackSize(funcName, width, height, destBytesPerPixel, &destStride,
+                              &destByteCount))
+        {
             ErrorOutOfMemory("readPixels: Overflow calculating sizes for conversion.");
             return false;
         }
 
-        UniqueBuffer readBuffer = malloc(readSize.value());
+        UniqueBuffer readBuffer = malloc(readByteCount);
         if (!readBuffer) {
             ErrorOutOfMemory("readPixels: Failed to alloc temp buffer for conversion.");
             return false;
         }
 
         gl::GLContext::LocalErrorScope errorScope(*gl);
 
         gl->fReadPixels(x, y, width, height, readFormat, readType, readBuffer.get());
@@ -1263,35 +1266,35 @@ WebGLContext::DoReadPixelsAndConvert(GLi
             return false;
         }
 
         if (error) {
             MOZ_RELEASE_ASSERT(false, "GFX: Unexpected driver error.");
             return false;
         }
 
-        size_t channelsPerRow = std::min(readStride.value() / sizeof(uint16_t),
-                                         destStride.value() / sizeof(float));
-
-        const uint8_t* srcRow = (uint8_t*)(readBuffer.get()) + readOffset.value();
-        uint8_t* dstRow = (uint8_t*)(destBytes) + destOffset.value();
+        size_t channelsPerRow = std::min(readStride / sizeof(uint16_t),
+                                         destStride / sizeof(float));
+
+        const uint8_t* srcRow = (uint8_t*)readBuffer.get();
+        uint8_t* dstRow = (uint8_t*)destBytes;
 
         for (size_t j = 0; j < (size_t)height; j++) {
             auto src = (const uint16_t*)srcRow;
             auto dst = (float*)dstRow;
 
             const auto srcEnd = src + channelsPerRow;
             while (src != srcEnd) {
                 *dst = unpackFromFloat16(*src);
                 ++src;
                 ++dst;
             }
 
-            srcRow += readStride.value();
-            dstRow += destStride.value();
+            srcRow += readStride;
+            dstRow += destStride;
         }
 
         return true;
     }
 
     gl->fReadPixels(x, y, width, height, destFormat, destType, destBytes);
     return true;
 }
@@ -1386,53 +1389,66 @@ IsIntegerFormatAndTypeUnpackable(GLenum 
         return format == LOCAL_GL_RGB;
 
     default:
         return false;
     }
 }
 
 
-CheckedUint32
-WebGLContext::GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel,
-                          CheckedUint32* const out_startOffset,
-                          CheckedUint32* const out_rowStride)
+bool
+WebGLContext::ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
+                               uint8_t bytesPerPixel, uint32_t* const out_rowStride,
+                               uint32_t* const out_endOffset)
 {
     if (!width || !height) {
-        *out_startOffset = 0;
         *out_rowStride = 0;
-        return 0;
+        *out_endOffset = 0;
+        return true;
     }
 
-    const CheckedUint32 pixelsPerRow = (mPixelStore_PackRowLength ? mPixelStore_PackRowLength
-                                                                  : width);
-    const CheckedUint32 skipPixels = mPixelStore_PackSkipPixels;
-    const CheckedUint32 skipRows = mPixelStore_PackSkipRows;
-    const CheckedUint32 alignment = mPixelStore_PackAlignment;
-
     // GLES 3.0.4, p116 (PACK_ functions like UNPACK_)
-    const auto totalBytesPerRow = bytesPerPixel * pixelsPerRow;
-    const auto rowStride = RoundUpToMultipleOf(totalBytesPerRow, alignment);
-
-    const auto startOffset = rowStride * skipRows + bytesPerPixel * skipPixels;
-    const auto usedBytesPerRow = bytesPerPixel * width;
-
-    const auto bytesNeeded = startOffset + rowStride * (height - 1) + usedBytesPerRow;
-
-    *out_startOffset = startOffset;
-    *out_rowStride = rowStride;
-    return bytesNeeded;
+
+    const auto rowLength = (mPixelStore_PackRowLength ? mPixelStore_PackRowLength
+                                                      : width);
+    const auto skipPixels = mPixelStore_PackSkipPixels;
+    const auto skipRows = mPixelStore_PackSkipRows;
+    const auto alignment = mPixelStore_PackAlignment;
+
+    const auto usedPixelsPerRow = CheckedUint32(skipPixels) + width;
+    const auto usedRowsPerImage = CheckedUint32(skipRows) + height;
+
+    if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > rowLength) {
+        ErrorInvalidOperation("%s: SKIP_PIXELS + width > ROW_LENGTH.", funcName);
+        return false;
+    }
+
+    const auto rowLengthBytes = CheckedUint32(rowLength) * bytesPerPixel;
+    const auto rowStride = RoundUpToMultipleOf(rowLengthBytes, alignment);
+
+    const auto usedBytesPerRow = usedPixelsPerRow * bytesPerPixel;
+    const auto usedBytesPerImage = (usedRowsPerImage - 1) * rowStride + usedBytesPerRow;
+
+    if (!rowStride.isValid() || !usedBytesPerImage.isValid()) {
+        ErrorInvalidOperation("%s: Invalid UNPACK_ params.", funcName);
+        return false;
+    }
+
+    *out_rowStride = rowStride.value();
+    *out_endOffset = usedBytesPerImage.value();
+    return true;
 }
 
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                          GLenum type,
                          const dom::Nullable<dom::ArrayBufferView>& pixels,
                          ErrorResult& out_error)
 {
+    const char funcName[] = "readPixels";
     if (IsContextLost())
         return;
 
     if (mCanvasElement &&
         mCanvasElement->IsWriteOnly() &&
         !nsContentUtils::IsCallerChrome())
     {
         GenerateWarning("readPixels: Not allowed");
@@ -1534,50 +1550,54 @@ WebGLContext::ReadPixels(GLint x, GLint 
         bytesPerPixel = 2*channels;
         requiredDataType = js::Scalar::Uint16;
         break;
 
     default:
         MOZ_CRASH("GFX: bad `type`");
     }
 
+    //////
+
     const auto& view = pixels.Value();
 
     // Compute length and data.  Don't reenter after this point, lest the
     // precomputed go out of sync with the instant length/data.
     view.ComputeLengthAndData();
     void* data = view.DataAllowShared();
     size_t bytesAvailable = view.LengthAllowShared();
     js::Scalar::Type dataType = JS_GetArrayBufferViewType(view.Obj());
 
     // Check the pixels param type
     if (dataType != requiredDataType)
         return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
 
-    CheckedUint32 startOffset;
-    CheckedUint32 rowStride;
-    const auto bytesNeeded = GetPackSize(width, height, bytesPerPixel, &startOffset,
-                                         &rowStride);
-    if (!bytesNeeded.isValid()) {
-        ErrorInvalidOperation("readPixels: Integer overflow computing the needed buffer"
-                              " size.");
-        return;
-    }
-
-    if (bytesNeeded.value() > bytesAvailable) {
-        ErrorInvalidOperation("readPixels: buffer too small");
-        return;
-    }
-
     if (!data) {
         ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
         out_error.Throw(NS_ERROR_OUT_OF_MEMORY);
         return;
     }
 
+    //////
+
+    uint32_t rowStride;
+    uint32_t bytesNeeded;
+    if (!ValidatePackSize(funcName, width, height, bytesPerPixel, &rowStride,
+                          &bytesNeeded))
+    {
+        return;
+    }
+
+    if (bytesNeeded > bytesAvailable) {
+        ErrorInvalidOperation("readPixels: buffer too small");
+        return;
+    }
+
+    //////
+
     MakeContextCurrent();
 
     const webgl::FormatUsageInfo* srcFormat;
     uint32_t srcWidth;
     uint32_t srcHeight;
     GLenum srcMode;
     if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight, &srcMode))
         return;
@@ -1638,91 +1658,62 @@ WebGLContext::ReadPixels(GLint x, GLint 
 
     uint32_t readX, readY;
     uint32_t writeX, writeY;
     uint32_t rwWidth, rwHeight;
     Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
     Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
 
     if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) {
-        // Warning: Possibly shared memory.  See bug 1225033.
         DoReadPixelsAndConvert(x, y, width, height, format, type, data, auxReadFormat,
                                auxReadType);
         return;
     }
 
     // Read request contains out-of-bounds pixels. Unfortunately:
     // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer":
     // "If any of these pixels lies outside of the window allocated to the current GL
     //  context, or outside of the image attached to the currently bound framebuffer
     //  object, then the values obtained for those pixels are undefined."
 
     // This is a slow-path, so warn people away!
     GenerateWarning("readPixels: Out-of-bounds reads with readPixels are deprecated, and"
                     " may be slow.");
 
-    // Currently, the spec dictates that we need to zero the out-of-bounds pixels.
-
-    // Ideally we could just ReadPixels into the buffer, then zero the undefined parts.
-    // However, we can't do this for *shared* ArrayBuffers, as they can have racey
-    // accesses from Workers.
-
-    // We can use a couple tricks to do this faster, but we shouldn't encourage this
-    // anyway. Why not just do it the really safe, dead-simple way, even if it is
-    // hilariously slow?
-
-    ////////////////////////////////////
-    // Clear the targetted pixels to zero.
-
-    if (mPixelStore_PackRowLength ||
-        mPixelStore_PackSkipPixels ||
-        mPixelStore_PackSkipRows)
-    {
-        // Targetted bytes might not be contiguous, so do it row-by-row.
-        uint8_t* row = (uint8_t*)data + startOffset.value();
-        const auto bytesPerRow = bytesPerPixel * width;
-        for (uint32_t j = 0; j < uint32_t(height); j++) {
-            std::memset(row, 0, bytesPerRow);
-            row += rowStride.value();
-        }
-    } else {
-        std::memset(data, 0, bytesNeeded.value());
-    }
-
     ////////////////////////////////////
     // Read only the in-bounds pixels.
 
     if (!rwWidth || !rwHeight) {
         // There aren't any, so we're 'done'.
         DummyReadFramebufferOperation("readPixels");
         return;
     }
 
     if (IsWebGL2()) {
         if (!mPixelStore_PackRowLength) {
-            gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, width);
+            gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackSkipPixels + width);
         }
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY);
 
         DoReadPixelsAndConvert(readX, readY, rwWidth, rwHeight, format, type, data,
                                auxReadFormat, auxReadType);
 
         gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels);
         gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows);
     } else {
         // I *did* say "hilariously slow".
 
-        uint8_t* row = (uint8_t*)data + startOffset.value() + writeX * bytesPerPixel;
-        row += writeY * rowStride.value();
+        uint8_t* row = (uint8_t*)data + writeX * bytesPerPixel;
+        row += writeY * rowStride;
         for (uint32_t j = 0; j < rwHeight; j++) {
             DoReadPixelsAndConvert(readX, readY+j, rwWidth, 1, format, type, row,
                                    auxReadFormat, auxReadType);
-            row += rowStride.value();
+            row += rowStride;
         }
     }
 }
 
 void
 WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
                                        GLsizei samples, GLenum internalFormat,
                                        GLsizei width, GLsizei height)
--- a/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
+++ b/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
@@ -38,18 +38,14 @@ WebGLExtensionColorBufferHalfFloat::WebG
 
 WebGLExtensionColorBufferHalfFloat::~WebGLExtensionColorBufferHalfFloat()
 {
 }
 
 bool
 WebGLExtensionColorBufferHalfFloat::IsSupported(const WebGLContext* webgl)
 {
-    gl::GLContext* gl = webgl->GL();
-
-    // ANGLE doesn't support ReadPixels from a RGBA16F with RGBA/FLOAT.
-    return gl->IsSupported(gl::GLFeature::renderbuffer_color_half_float) ||
-           gl->IsANGLE();
+    return webgl->GL()->IsSupported(gl::GLFeature::renderbuffer_color_half_float);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionColorBufferHalfFloat, EXT_color_buffer_half_float)
 
 } // namespace mozilla
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -23,42 +23,39 @@ support-files =
 [test_pointerevent_lostpointercapture_for_disconnected_node-manual.html]
   support-files = pointerevent_lostpointercapture_for_disconnected_node-manual.html
 [test_pointerevent_lostpointercapture_is_first-manual.html]
   support-files = pointerevent_lostpointercapture_is_first-manual.html
 [test_pointerevent_pointercancel_touch-manual.html]
   support-files = pointerevent_pointercancel_touch-manual.html
 [test_pointerevent_pointerdown-manual.html]
   support-files = pointerevent_pointerdown-manual.html
-  disabled = should be investigated
 [test_pointerevent_pointerenter_does_not_bubble-manual.html]
   support-files = pointerevent_pointerenter_does_not_bubble-manual.html
 [test_pointerevent_pointerenter_nohover-manual.html]
   support-files = pointerevent_pointerenter_nohover-manual.html
 [test_pointerevent_pointerenter-manual.html]
   support-files = pointerevent_pointerenter-manual.html
 [test_pointerevent_pointerleave_after_pointercancel_touch-manual.html]
   support-files = pointerevent_pointerleave_after_pointercancel_touch-manual.html
 [test_pointerevent_pointerleave_after_pointerup_nohover-manual.html]
   support-files = pointerevent_pointerleave_after_pointerup_nohover-manual.html
 [test_pointerevent_pointerleave_descendant_over-manual.html]
   support-files = pointerevent_pointerleave_descendant_over-manual.html
-  skip-if = (os == 'linux') # Bug 1180188 - Issue on Linux
 [test_pointerevent_pointerleave_descendants-manual.html]
   support-files = pointerevent_pointerleave_descendants-manual.html
 [test_pointerevent_pointerleave_does_not_bubble-manual.html]
   support-files = pointerevent_pointerleave_does_not_bubble-manual.html
 [test_pointerevent_pointerleave_mouse-manual.html]
   support-files = pointerevent_pointerleave_mouse-manual.html
 [test_pointerevent_pointerleave_pen-manual.html]
   support-files = pointerevent_pointerleave_pen-manual.html
   disabled = should be investigated
 [test_pointerevent_pointerleave_touch-manual.html]
   support-files = pointerevent_pointerleave_touch-manual.html
-  skip-if = (os == 'linux') # Bug 1180188 - Issue on Linux
 [test_pointerevent_pointermove-manual.html]
   support-files = pointerevent_pointermove-manual.html
 [test_pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html]
   support-files = pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html
 [test_pointerevent_pointermove_pointertype-manual.html]
   support-files = pointerevent_pointermove_pointertype-manual.html
 [test_pointerevent_pointerout-manual.html]
   support-files = pointerevent_pointerout-manual.html
@@ -92,30 +89,28 @@ support-files =
 [test_pointerevent_releasepointercapture_onpointercancel_touch-manual.html]
   support-files = pointerevent_releasepointercapture_onpointercancel_touch-manual.html
 [test_pointerevent_releasepointercapture_onpointerup_mouse-manual.html]
   support-files = pointerevent_releasepointercapture_onpointerup_mouse-manual.html
 [test_pointerevent_setpointercapture_disconnected-manual.html]
   support-files = pointerevent_setpointercapture_disconnected-manual.html
 [test_pointerevent_setpointercapture_inactive_button_mouse-manual.html]
   support-files = pointerevent_setpointercapture_inactive_button_mouse-manual.html
-  skip-if = (os == 'linux') && e10s # Bug 1180188 - Issue on Linux
 [test_pointerevent_setpointercapture_invalid_pointerid-manual.html]
   support-files = pointerevent_setpointercapture_invalid_pointerid-manual.html
 [test_pointerevent_setpointercapture_relatedtarget-manual.html]
   support-files = pointerevent_setpointercapture_relatedtarget-manual.html
 [test_pointerevent_touch-action-auto-css_touch-manual.html]
   support-files = pointerevent_touch-action-auto-css_touch-manual.html
   disabled = disabled
 [test_pointerevent_touch-action-button-test_touch-manual.html]
   support-files = pointerevent_touch-action-button-test_touch-manual.html
   disabled = disabled
 [test_pointerevent_touch-action-illegal.html]
   support-files = pointerevent_touch-action-illegal.html
-  disabled = disabled
 [test_pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html]
   support-files = pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
   disabled = disabled
 [test_pointerevent_touch-action-inherit_child-none_touch-manual.html]
   support-files = pointerevent_touch-action-inherit_child-none_touch-manual.html
   disabled = disabled
 [test_pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html]
   support-files = pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsXULAppAPI.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/Telemetry.h"
 
 #include "nsAutoPtr.h"
-#include "nsISettingsService.h"
 
 #include "nsGeolocation.h"
 #include "nsDOMClassInfoID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsContentPermissionHelper.h"
 #include "nsIDocument.h"
@@ -27,17 +26,16 @@
 #include "mozilla/Hal.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
 #include "mozilla/dom/WakeLock.h"
 
 class nsIPrincipal;
 
 #ifdef MOZ_ENABLE_QT5GEOPOSITION
 #include "QTMLocationProvider.h"
 #endif
 
@@ -57,19 +55,16 @@ class nsIPrincipal;
 #include "WindowsLocationProvider.h"
 #include "mozilla/WindowsVersion.h"
 #endif
 
 // Some limit to the number of get or watch geolocation requests
 // that a window can make.
 #define MAX_GEO_REQUESTS_PER_WINDOW  1500
 
-// The settings key.
-#define GEO_SETTINGS_ENABLED          "geolocation.enabled"
-
 using mozilla::Unused;          // <snicker>
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
 class nsGeolocationRequest final
  : public nsIContentPermissionRequest
  , public nsIGeolocationUpdate
@@ -145,63 +140,16 @@ CreatePositionOptionsCopy(const Position
 
   geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
   geoOptions->mMaximumAge = aOptions.mMaximumAge;
   geoOptions->mTimeout = aOptions.mTimeout;
 
   return geoOptions.forget();
 }
 
-class GeolocationSettingsCallback : public nsISettingsServiceCallback
-{
-  virtual ~GeolocationSettingsCallback() {
-    MOZ_COUNT_DTOR(GeolocationSettingsCallback);
-  }
-
-public:
-  NS_DECL_ISUPPORTS
-
-  GeolocationSettingsCallback() {
-    MOZ_COUNT_CTOR(GeolocationSettingsCallback);
-  }
-
-  NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // The geolocation is enabled by default:
-    bool value = true;
-    if (aResult.isBoolean()) {
-        value = aResult.toBoolean();
-    }
-
-    MozSettingValue(value);
-    return NS_OK;
-  }
-
-  NS_IMETHOD HandleError(const nsAString& aName) override
-  {
-    NS_WARNING("Unable to get value for '" GEO_SETTINGS_ENABLED "'");
-
-    // Default it's enabled:
-    MozSettingValue(true);
-    return NS_OK;
-  }
-
-  void MozSettingValue(const bool aValue)
-  {
-    RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
-    if (gs) {
-      gs->HandleMozsettingValue(aValue);
-    }
-  }
-};
-
-NS_IMPL_ISUPPORTS(GeolocationSettingsCallback, nsISettingsServiceCallback)
-
 class RequestPromptEvent : public Runnable
 {
 public:
   RequestPromptEvent(nsGeolocationRequest* aRequest, nsWeakPtr aWindow)
     : mRequest(aRequest)
     , mWindow(aWindow)
   {
   }
@@ -377,22 +325,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
 NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
+
 void
 nsGeolocationRequest::Notify()
 {
   SetTimeoutTimer();
   NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
 }
+
 void
 nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
 {
   MOZ_ASSERT(!mShutdown, "timeout after shutdown");
   if (!mIsWatchPositionRequest) {
     Shutdown();
     mLocator->RemoveRequest(this);
   }
@@ -554,16 +504,17 @@ nsGeolocationRequest::Allow(JS::HandleVa
 
 NS_IMETHODIMP
 nsGeolocationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
 {
   NS_ENSURE_ARG_POINTER(aRequester);
 
   nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
   requester.forget(aRequester);
+
   return NS_OK;
 }
 
 void
 nsGeolocationRequest::SetTimeoutTimer()
 {
   StopTimeoutTimer();
 
@@ -641,32 +592,34 @@ nsGeolocationRequest::SendLocation(nsIDO
     nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
     MOZ_ASSERT(callback);
     callback->HandleEvent(aPosition);
   }
   SetTimeoutTimer();
   MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
              "non-shutdown getCurrentPosition request after callback!");
 }
+
 nsIPrincipal*
 nsGeolocationRequest::GetPrincipal()
 {
   if (!mLocator) {
     return nullptr;
   }
   return mLocator->GetPrincipal();
 }
 
 NS_IMETHODIMP
 nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
 {
   nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
   NS_DispatchToMainThread(ev);
   return NS_OK;
 }
+
 NS_IMETHODIMP
 nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
 {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
   positionError->NotifyCallback(mErrorCallback);
   return NS_OK;
 }
@@ -698,16 +651,17 @@ NS_IMPL_ISUPPORTS(nsGeolocationRequest::
 
 NS_IMETHODIMP
 nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*)
 {
   if (mRequest && mRequest->mLocator) {
     RefPtr<nsGeolocationRequest> request(mRequest);
     request->Notify();
   }
+
   return NS_OK;
 }
 
 
 ////////////////////////////////////////////////////
 // nsGeolocationService
 ////////////////////////////////////////////////////
 NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
@@ -716,59 +670,38 @@ NS_INTERFACE_MAP_BEGIN(nsGeolocationServ
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsGeolocationService)
 NS_IMPL_RELEASE(nsGeolocationService)
 
 
 static bool sGeoEnabled = true;
-static bool sGeoInitPending = true;
 static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
 
 nsresult nsGeolocationService::Init()
 {
   Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
   Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
 
   if (!sGeoEnabled) {
     return NS_ERROR_FAILURE;
   }
 
   if (XRE_IsContentProcess()) {
-    sGeoInitPending = false;
     return NS_OK;
   }
 
-  // check if the geolocation service is enable from settings
-  nsCOMPtr<nsISettingsService> settings =
-    do_GetService("@mozilla.org/settingsService;1");
-
-  if (settings) {
-    nsCOMPtr<nsISettingsServiceLock> settingsLock;
-    nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    RefPtr<GeolocationSettingsCallback> callback = new GeolocationSettingsCallback();
-    rv = settingsLock->Get(GEO_SETTINGS_ENABLED, callback);
-    NS_ENSURE_SUCCESS(rv, rv);
-  } else {
-    // If we cannot obtain the settings service, we continue
-    // assuming that the geolocation is enabled:
-    sGeoInitPending = false;
-  }
-
   // geolocation service can be enabled -> now register observer
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     return NS_ERROR_FAILURE;
   }
 
   obs->AddObserver(this, "xpcom-shutdown", false);
-  obs->AddObserver(this, "mozsettings-changed", false);
 
 #ifdef MOZ_ENABLE_QT5GEOPOSITION
   mProvider = new QTMLocationProvider();
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
   mProvider = new AndroidLocationProvider();
 #endif
@@ -815,82 +748,35 @@ nsresult nsGeolocationService::Init()
 
   return NS_OK;
 }
 
 nsGeolocationService::~nsGeolocationService()
 {
 }
 
-void
-nsGeolocationService::HandleMozsettingChanged(nsISupports* aSubject)
-{
-    // The string that we're interested in will be a JSON string that looks like:
-    //  {"key":"gelocation.enabled","value":true}
-
-    RootedDictionary<SettingChangeNotification> setting(nsContentUtils::RootingCxForThread());
-    if (!WrappedJSToDictionary(aSubject, setting)) {
-      return;
-    }
-    if (!setting.mKey.EqualsASCII(GEO_SETTINGS_ENABLED)) {
-      return;
-    }
-    if (!setting.mValue.isBoolean()) {
-      return;
-    }
-
-    HandleMozsettingValue(setting.mValue.toBoolean());
-}
-
-void
-nsGeolocationService::HandleMozsettingValue(const bool aValue)
-{
-    if (!aValue) {
-      // turn things off
-      StopDevice();
-      Update(nullptr);
-      mLastPosition.position = nullptr;
-      sGeoEnabled = false;
-    } else {
-      sGeoEnabled = true;
-    }
-
-    if (sGeoInitPending) {
-      sGeoInitPending = false;
-      for (uint32_t i = 0, length = mGeolocators.Length(); i < length; ++i) {
-        mGeolocators[i]->ServiceReady();
-      }
-    }
-}
-
 NS_IMETHODIMP
 nsGeolocationService::Observe(nsISupports* aSubject,
                               const char* aTopic,
                               const char16_t* aData)
 {
   if (!strcmp("xpcom-shutdown", aTopic)) {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
       obs->RemoveObserver(this, "xpcom-shutdown");
-      obs->RemoveObserver(this, "mozsettings-changed");
     }
 
     for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
       mGeolocators[i]->Shutdown();
     }
     StopDevice();
 
     return NS_OK;
   }
 
-  if (!strcmp("mozsettings-changed", aTopic)) {
-    HandleMozsettingChanged(aSubject);
-    return NS_OK;
-  }
-
   if (!strcmp("timer-callback", aTopic)) {
     // decide if we can close down the service.
     for (uint32_t i = 0; i< mGeolocators.Length(); i++)
       if (mGeolocators[i]->HasActiveCallbacks()) {
         SetDisconnectTimer();
         return NS_OK;
       }
 
@@ -904,27 +790,31 @@ nsGeolocationService::Observe(nsISupport
 }
 
 NS_IMETHODIMP
 nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
 {
   if (aSomewhere) {
     SetCachedPosition(aSomewhere);
   }
+
   for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
     mGeolocators[i]->Update(aSomewhere);
   }
+
   return NS_OK;
 }
+
 NS_IMETHODIMP
 nsGeolocationService::NotifyError(uint16_t aErrorCode)
 {
   for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
     mGeolocators[i]->NotifyError(aErrorCode);
   }
+
   return NS_OK;
 }
 
 void
 nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
 {
   mLastPosition.position = aPosition;
   mLastPosition.isHighAccuracy = mHigherAccuracy;
@@ -934,17 +824,17 @@ CachedPositionAndAccuracy
 nsGeolocationService::GetCachedPosition()
 {
   return mLastPosition;
 }
 
 nsresult
 nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
 {
-  if (!sGeoEnabled || sGeoInitPending) {
+  if (!sGeoEnabled) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // we do not want to keep the geolocation devices online
   // indefinitely.  Close them down after a reasonable period of
   // inactivivity
   SetDisconnectTimer();
 
@@ -985,17 +875,16 @@ void
 nsGeolocationService::StopDisconnectTimer()
 {
   if (mDisconnectTimer) {
     mDisconnectTimer->Cancel();
     mDisconnectTimer = nullptr;
   }
 }
 
-
 void
 nsGeolocationService::SetDisconnectTimer()
 {
   if (!mDisconnectTimer) {
     mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
   } else {
     mDisconnectTimer->Cancel();
   }
@@ -1008,51 +897,47 @@ nsGeolocationService::SetDisconnectTimer
 bool
 nsGeolocationService::HighAccuracyRequested()
 {
   for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
     if (mGeolocators[i]->HighAccuracyRequested()) {
       return true;
     }
   }
+
   return false;
 }
 
 void
 nsGeolocationService::UpdateAccuracy(bool aForceHigh)
 {
   bool highRequired = aForceHigh || HighAccuracyRequested();
 
   if (XRE_IsContentProcess()) {
     ContentChild* cpc = ContentChild::GetSingleton();
     if (cpc->IsAlive()) {
       cpc->SendSetGeolocationHigherAccuracy(highRequired);
     }
+
     return;
   }
 
-  if (!mHigherAccuracy && highRequired) {
-      mProvider->SetHighAccuracy(true);
-  }
-
-  if (mHigherAccuracy && !highRequired) {
-      mProvider->SetHighAccuracy(false);
-  }
-
+  mProvider->SetHighAccuracy(!mHigherAccuracy && highRequired);
   mHigherAccuracy = highRequired;
 }
 
 void
 nsGeolocationService::StopDevice()
 {
   StopDisconnectTimer();
 
   if (XRE_IsContentProcess()) {
     ContentChild* cpc = ContentChild::GetSingleton();
     cpc->SendRemoveGeolocationListener();
+
     return; // bail early
   }
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     return;
   }
 
@@ -1078,18 +963,20 @@ nsGeolocationService::GetGeolocationServ
     result = nsGeolocationService::sService;
     return result.forget();
   }
 
   result = new nsGeolocationService();
   if (NS_FAILED(result->Init())) {
     return nullptr;
   }
+
   ClearOnShutdown(&nsGeolocationService::sService);
   nsGeolocationService::sService = result;
+
   return result.forget();
 }
 
 void
 nsGeolocationService::AddLocator(Geolocation* aLocator)
 {
   mGeolocators.AppendElement(aLocator);
 }
@@ -1182,31 +1069,31 @@ Geolocation::Init(nsPIDOMWindowInner* aC
 
   // If no aContentDom was passed into us, we are being used
   // by chrome/c++ and have no mOwner, no mPrincipal, and no need
   // to prompt.
   mService = nsGeolocationService::GetGeolocationService();
   if (mService) {
     mService->AddLocator(this);
   }
+
   return NS_OK;
 }
 
 bool
 Geolocation::ContainsRequest(nsGeolocationRequest* aRequest)
 {
-  if (aRequest->IsWatch()) {
-    if (mWatchingCallbacks.Contains(aRequest)) {
+  if (aRequest->IsWatch() && mWatchingCallbacks.Contains(aRequest)) {
 	return true;
-    }
-  } else {
-    if (mPendingCallbacks.Contains(aRequest)) {
-        return true;
-    }
   }
+
+  if (mPendingCallbacks.Contains(aRequest)) {
+    return true;
+  }
+
   return false;
 }
 
 
 NS_IMETHODIMP
 Geolocation::HandleEvent(nsIDOMEvent* aEvent)
 {
 
@@ -1220,33 +1107,37 @@ Geolocation::HandleEvent(nsIDOMEvent* aE
   MOZ_ASSERT(doc);
 
   if (doc->Hidden()) {
     WakeLockInformation info;
     GetWakeLockInfo(NS_LITERAL_STRING("gps"), &info);
 
     MOZ_ASSERT(XRE_IsContentProcess());
     ContentChild* cpc = ContentChild::GetSingleton();
+
     if (!info.lockingProcesses().Contains(cpc->GetID())) {
       cpc->SendRemoveGeolocationListener();
       mService->StopDisconnectTimer();
     }
-  } else {
-    mService->SetDisconnectTimer();
+
+    return NS_OK;
+  }
+
+  mService->SetDisconnectTimer();
 
-    // We will unconditionally allow all the requests in the callbacks
-    // because if a request is put into either of these two callbacks,
-    // it means that it has been allowed before.
-    // That's why when we resume them, we unconditionally allow them again.
-    for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
-      mWatchingCallbacks[i]->Allow(JS::UndefinedHandleValue);
-    }
-    for (uint32_t i = 0, length = mPendingCallbacks.Length(); i < length; ++i) {
-      mPendingCallbacks[i]->Allow(JS::UndefinedHandleValue);
-    }
+  // We will unconditionally allow all the requests in the callbacks
+  // because if a request is put into either of these two callbacks,
+  // it means that it has been allowed before.
+  // That's why when we resume them, we unconditionally allow them again.
+  for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
+    mWatchingCallbacks[i]->Allow(JS::UndefinedHandleValue);
+  }
+
+  for (uint32_t i = 0, length = mPendingCallbacks.Length(); i < length; ++i) {
+    mPendingCallbacks[i]->Allow(JS::UndefinedHandleValue);
   }
 
   return NS_OK;
 }
 
 void
 Geolocation::Shutdown()
 {
@@ -1337,25 +1228,28 @@ Geolocation::Update(nsIDOMGeoPosition *a
     mPendingCallbacks[i-1]->Update(aSomewhere);
     RemoveRequest(mPendingCallbacks[i-1]);
   }
 
   // notify everyone that is watching
   for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
     mWatchingCallbacks[i]->Update(aSomewhere);
   }
+
   return NS_OK;
 }
+
 NS_IMETHODIMP
 Geolocation::NotifyError(uint16_t aErrorCode)
 {
   if (!WindowOwnerStillExists()) {
     Shutdown();
     return NS_OK;
   }
+
   mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
 
   for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
     mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode);
     //NotifyErrorAndShutdown() removes the request from the array
   }
 
   // notify everyone that is watching
@@ -1369,27 +1263,29 @@ Geolocation::NotifyError(uint16_t aError
 bool
 Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest)
 {
   for (uint32_t i = 0, length = mClearedWatchIDs.Length(); i < length; ++i) {
     if (mClearedWatchIDs[i] == aRequest->WatchId()) {
       return true;
     }
   }
+
   return false;
 }
 
 bool
 Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest)
 {
   if (aRequest->IsWatch() && this->IsAlreadyCleared(aRequest)) {
     this->NotifyAllowedRequest(aRequest);
     this->ClearWatch(aRequest->WatchId());
     return true;
   }
+
   return false;
 }
 
 void
 Geolocation::GetCurrentPosition(PositionCallback& aCallback,
                                 PositionErrorCallback* aErrorCallback,
                                 const PositionOptions& aOptions,
                                 ErrorResult& aRv)
@@ -1442,21 +1338,16 @@ Geolocation::GetCurrentPosition(GeoPosit
     NS_DispatchToMainThread(ev);
     return NS_OK;
   }
 
   if (!mOwner && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
     return NS_ERROR_FAILURE;
   }
 
-  if (sGeoInitPending) {
-    mPendingRequests.AppendElement(request);
-    return NS_OK;
-  }
-
   return GetCurrentPositionReady(request);
 }
 
 nsresult
 Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest)
 {
   if (mOwner) {
     if (!RegisterRequestWithPrompt(aRequest)) {
@@ -1536,21 +1427,16 @@ Geolocation::WatchPosition(GeoPositionCa
     NS_DispatchToMainThread(ev);
     return NS_OK;
   }
 
   if (!mOwner && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
     return NS_ERROR_FAILURE;
   }
 
-  if (sGeoInitPending) {
-    mPendingRequests.AppendElement(request);
-    return NS_OK;
-  }
-
   return WatchPositionReady(request);
 }
 
 nsresult
 Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest)
 {
   if (mOwner) {
     if (!RegisterRequestWithPrompt(aRequest))
--- a/dom/geolocation/nsGeolocation.h
+++ b/dom/geolocation/nsGeolocation.h
@@ -67,19 +67,16 @@ public:
   NS_DECL_NSIOBSERVER
 
   nsGeolocationService() {
       mHigherAccuracy = false;
   }
 
   nsresult Init();
 
-  void HandleMozsettingChanged(nsISupports* aSubject);
-  void HandleMozsettingValue(const bool aValue);
-
   // Management of the Geolocation objects
   void AddLocator(mozilla::dom::Geolocation* locator);
   void RemoveLocator(mozilla::dom::Geolocation* locator);
 
   void SetCachedPosition(nsIDOMGeoPosition* aPosition);
   CachedPositionAndAccuracy GetCachedPosition();
 
   // Find and startup a geolocation device (gps, nmea, etc.)
new file mode 100644
--- /dev/null
+++ b/dom/grid/Grid.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "Grid.h"
+
+#include "GridDimension.h"
+#include "mozilla/dom/GridBinding.h"
+#include "nsGridContainerFrame.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Grid, mParent, mRows, mCols)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Grid)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Grid)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Grid)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+Grid::Grid(nsISupports* aParent,
+           nsGridContainerFrame* aFrame)
+  : mParent(do_QueryInterface(aParent))
+  , mRows(new GridDimension(this))
+  , mCols(new GridDimension(this))
+{
+  MOZ_ASSERT(aFrame,
+    "Should never be instantiated with a null nsGridContainerFrame");
+
+  const ComputedGridTrackInfo* rowTrackInfo = aFrame->GetComputedTemplateRows();
+  mRows->SetTrackInfo(rowTrackInfo);
+  mRows->SetLineInfo(rowTrackInfo);
+
+  const ComputedGridTrackInfo* colTrackInfo = aFrame->GetComputedTemplateColumns();
+  mCols->SetTrackInfo(colTrackInfo);
+  mCols->SetLineInfo(colTrackInfo);
+}
+
+Grid::~Grid()
+{
+}
+
+JSObject*
+Grid::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return GridBinding::Wrap(aCx, this, aGivenProto);
+}
+
+GridDimension*
+Grid::Rows() const
+{
+  return mRows;
+}
+
+GridDimension*
+Grid::Cols() const
+{
+  return mCols;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/grid/Grid.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozilla_dom_Grid_h
+#define mozilla_dom_Grid_h
+
+#include "mozilla/dom/Element.h"
+#include "nsGridContainerFrame.h"
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class GridDimension;
+
+class Grid : public nsISupports
+           , public nsWrapperCache
+{
+public:
+  explicit Grid(nsISupports* aParent, nsGridContainerFrame* aFrame);
+
+protected:
+  virtual ~Grid();
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Grid)
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  Element* GetParentObject()
+  {
+    return mParent;
+  }
+
+  GridDimension* Rows() const;
+  GridDimension* Cols() const;
+
+protected:
+  nsCOMPtr<Element> mParent;
+  RefPtr<GridDimension> mRows;
+  RefPtr<GridDimension> mCols;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_Grid_h */
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridDimension.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "GridDimension.h"
+
+#include "Grid.h"
+#include "GridLines.h"
+#include "GridTracks.h"
+#include "mozilla/dom/GridBinding.h"
+#include "nsGridContainerFrame.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridDimension, mParent, mLines, mTracks)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GridDimension)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GridDimension)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridDimension)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+GridDimension::GridDimension(Grid* aParent)
+  : mParent(aParent)
+  , mLines(new GridLines(this))
+  , mTracks(new GridTracks(this))
+{
+  MOZ_ASSERT(aParent, "Should never be instantiated with a null Grid");
+}
+
+GridDimension::~GridDimension()
+{
+}
+
+JSObject*
+GridDimension::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return GridDimensionBinding::Wrap(aCx, this, aGivenProto);
+}
+
+GridLines*
+GridDimension::Lines() const
+{
+  return mLines;
+}
+
+GridTracks*
+GridDimension::Tracks() const
+{
+  return mTracks;
+}
+
+void
+GridDimension::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo)
+{
+  mTracks->SetTrackInfo(aTrackInfo);
+}
+
+void
+GridDimension::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo)
+{
+  mLines->SetLineInfo(aTrackInfo);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridDimension.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozilla_dom_GridDimension_h
+#define mozilla_dom_GridDimension_h
+
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+
+struct ComputedGridTrackInfo;
+
+namespace dom {
+
+class Grid;
+class GridLines;
+class GridTracks;
+
+class GridDimension : public nsISupports
+                    , public nsWrapperCache
+{
+public:
+  explicit GridDimension(Grid* aParent);
+
+protected:
+  virtual ~GridDimension();
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridDimension)
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  Grid* GetParentObject()
+  {
+    return mParent;
+  }
+
+  GridLines* Lines() const;
+  GridTracks* Tracks() const;
+
+  void SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo);
+  void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo);
+
+protected:
+  RefPtr<Grid> mParent;
+  RefPtr<GridLines> mLines;
+  RefPtr<GridTracks> mTracks;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_GridDimension_h */
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridLine.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "GridLine.h"
+
+#include "GridLines.h"
+#include "mozilla/dom/GridBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLine, mParent)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GridLine)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GridLine)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridLine)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+GridLine::GridLine(GridLines *aParent)
+  : mParent(aParent)
+  , mStart(0.0)
+  , mBreadth(0.0)
+  , mNumber(0)
+{
+  MOZ_ASSERT(aParent, "Should never be instantiated with a null GridLines");
+}
+
+GridLine::~GridLine()
+{
+}
+
+void
+GridLine::GetNames(nsTArray<nsString>& aNames) const
+{
+  aNames = mNames;
+}
+
+JSObject*
+GridLine::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return GridLineBinding::Wrap(aCx, this, aGivenProto);
+}
+
+double
+GridLine::Start() const
+{
+  return mStart;
+}
+
+double
+GridLine::Breadth() const
+{
+  return mBreadth;
+}
+
+uint32_t
+GridLine::Number() const
+{
+  return mNumber;
+}
+
+void
+GridLine::SetLineValues(double aStart,
+                        double aBreadth,
+                        uint32_t aNumber,
+                        const nsTArray<nsString>& aNames)
+{
+  mStart = aStart;
+  mBreadth = aBreadth;
+  mNumber = aNumber;
+  mNames = aNames;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridLine.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozilla_dom_GridLine_h
+#define mozilla_dom_GridLine_h
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class GridLines;
+
+class GridLine : public nsISupports
+               , public nsWrapperCache
+{
+public:
+  explicit GridLine(GridLines* aParent);
+
+protected:
+  virtual ~GridLine();
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridLine)
+
+  void GetNames(nsTArray<nsString>& aNames) const;
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  GridLines* GetParentObject()
+  {
+    return mParent;
+  }
+
+  double Start() const;
+  double Breadth() const;
+  uint32_t Number() const;
+
+  void SetLineValues(double aStart,
+                     double aBreadth,
+                     uint32_t aNumber,
+                     const nsTArray<nsString>& aNames);
+
+protected:
+  RefPtr<GridLines> mParent;
+  double mStart;
+  double mBreadth;
+  uint32_t mNumber;
+  nsTArray<nsString> mNames;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_GridLine_h */
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridLines.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "GridLines.h"
+
+#include "GridDimension.h"
+#include "GridLine.h"
+#include "mozilla/dom/GridBinding.h"
+#include "nsGridContainerFrame.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLines, mParent, mLines)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GridLines)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GridLines)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridLines)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+GridLines::GridLines(GridDimension* aParent)
+  : mParent(aParent)
+{
+  MOZ_ASSERT(aParent,
+    "Should never be instantiated with a null GridDimension");
+}
+
+GridLines::~GridLines()
+{
+}
+
+JSObject*
+GridLines::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return GridLinesBinding::Wrap(aCx, this, aGivenProto);
+}
+
+uint32_t
+GridLines::Length() const
+{
+  return mLines.Length();
+}
+
+GridLine*
+GridLines::Item(uint32_t aIndex)
+{
+  return mLines.SafeElementAt(aIndex);
+}
+
+GridLine*
+GridLines::IndexedGetter(uint32_t aIndex,
+                         bool& aFound)
+{
+  aFound = aIndex < mLines.Length();
+  if (!aFound) {
+    return nullptr;
+  }
+  return mLines[aIndex];
+}
+
+void
+GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo)
+{
+  mLines.Clear();
+
+  if (!aTrackInfo) {
+    return;
+  }
+
+  uint32_t trackCount = aTrackInfo->mEndFragmentTrack -
+                        aTrackInfo->mStartFragmentTrack;
+
+  // If there is at least one track, line count is one more
+  // than the number of tracks.
+  if (trackCount > 0) {
+    double endOfLastTrack = 0.0;
+    double startOfNextTrack;
+
+    for (uint32_t i = aTrackInfo->mStartFragmentTrack;
+         i < aTrackInfo->mEndFragmentTrack + 1;
+         i++) {
+      startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack) ?
+                         aTrackInfo->mPositions[i] :
+                         endOfLastTrack;
+
+      GridLine* line = new GridLine(this);
+      mLines.AppendElement(line);
+      line->SetLineValues(
+        nsPresContext::AppUnitsToDoubleCSSPixels(endOfLastTrack),
+        nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
+                                                 endOfLastTrack),
+        i + 1,
+        nsTArray<nsString>()
+      );
+
+      if (i < aTrackInfo->mEndFragmentTrack) {
+        endOfLastTrack = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i];
+      }
+    }
+  }
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridLines.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozilla_dom_GridLines_h
+#define mozilla_dom_GridLines_h
+
+#include "nsTArray.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class GridDimension;
+class GridLine;
+
+class GridLines : public nsISupports
+                , public nsWrapperCache
+{
+public:
+  explicit GridLines(GridDimension* aParent);
+
+protected:
+  virtual ~GridLines();
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridLines)
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  GridDimension* GetParentObject()
+  {
+    return mParent;
+  }
+
+  uint32_t Length() const;
+  GridLine* Item(uint32_t aIndex);
+  GridLine* IndexedGetter(uint32_t aIndex, bool& aFound);
+
+  void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo);
+
+protected:
+  RefPtr<GridDimension> mParent;
+  nsTArray<RefPtr<GridLine>> mLines;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_GridLines_h */
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridTrack.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "GridTrack.h"
+
+#include "GridTracks.h"
+#include "mozilla/dom/GridBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridTrack, mParent)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GridTrack)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GridTrack)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridTrack)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+GridTrack::GridTrack(GridTracks* aParent)
+  : mParent(aParent)
+  , mStart(0.0)
+  , mBreadth(0.0)
+  , mType(GridTrackType::Implicit)
+  , mState(GridTrackState::Static)
+{
+  MOZ_ASSERT(aParent, "Should never be instantiated with a null GridTracks");
+}
+
+GridTrack::~GridTrack()
+{
+}
+
+JSObject*
+GridTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return GridTrackBinding::Wrap(aCx, this, aGivenProto);
+}
+
+double
+GridTrack::Start() const
+{
+  return mStart;
+}
+
+double
+GridTrack::Breadth() const
+{
+  return mBreadth;
+}
+
+GridTrackType
+GridTrack::Type() const
+{
+  return mType;
+}
+
+GridTrackState
+GridTrack::State() const
+{
+  return mState;
+}
+
+void
+GridTrack::SetTrackValues(double aStart,
+                          double aBreadth,
+                          GridTrackType aType,
+                          GridTrackState aState)
+{
+  mStart = aStart;
+  mBreadth = aBreadth;
+  mType = aType;
+  mState = aState;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridTrack.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozilla_dom_GridTrack_h
+#define mozilla_dom_GridTrack_h
+
+#include "mozilla/dom/GridBinding.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class GridTracks;
+
+class GridTrack : public nsISupports
+                , public nsWrapperCache
+{
+public:
+  explicit GridTrack(GridTracks *parent);
+
+protected:
+  virtual ~GridTrack();
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridTrack)
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  GridTracks* GetParentObject()
+  {
+    return mParent;
+  }
+
+  double Start() const;
+  double Breadth() const;
+  GridTrackType Type() const;
+  GridTrackState State() const;
+
+  void SetTrackValues(double aStart, double aBreadth, GridTrackType aType, GridTrackState aState);
+
+protected:
+  RefPtr<GridTracks> mParent;
+  double mStart;
+  double mBreadth;
+  GridTrackType mType;
+  GridTrackState mState;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_GridTrack_h */
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridTracks.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "GridTracks.h"
+
+#include "GridDimension.h"
+#include "GridTrack.h"
+#include "mozilla/dom/GridBinding.h"
+#include "nsGridContainerFrame.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridTracks, mParent, mTracks)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(GridTracks)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(GridTracks)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridTracks)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+GridTracks::GridTracks(GridDimension *aParent)
+  : mParent(aParent)
+{
+  MOZ_ASSERT(aParent,
+    "Should never be instantiated with a null GridDimension");
+}
+
+GridTracks::~GridTracks()
+{
+}
+
+JSObject*
+GridTracks::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return GridTracksBinding::Wrap(aCx, this, aGivenProto);
+}
+
+uint32_t
+GridTracks::Length() const
+{
+  return mTracks.Length();
+}
+
+GridTrack*
+GridTracks::Item(uint32_t aIndex)
+{
+  return mTracks.SafeElementAt(aIndex);
+}
+
+GridTrack*
+GridTracks::IndexedGetter(uint32_t aIndex,
+                          bool& aFound)
+{
+  aFound = aIndex < mTracks.Length();
+  if (!aFound) {
+    return nullptr;
+  }
+  return mTracks[aIndex];
+}
+
+void
+GridTracks::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo)
+{
+  // rebuild the tracks based on aTrackInfo
+  mTracks.Clear();
+
+  if (!aTrackInfo) {
+    return;
+  }
+
+  for (size_t i = aTrackInfo->mStartFragmentTrack;
+       i < aTrackInfo->mEndFragmentTrack;
+       i++) {
+    GridTrack* track = new GridTrack(this);
+    mTracks.AppendElement(track);
+    track->SetTrackValues(
+      nsPresContext::AppUnitsToDoubleCSSPixels(aTrackInfo->mPositions[i]),
+      nsPresContext::AppUnitsToDoubleCSSPixels(aTrackInfo->mSizes[i]),
+      (
+        // Implicit if index is before the first explicit track, or after
+        // the last explicit track.
+        (i < aTrackInfo->mNumLeadingImplicitTracks) ||
+        (i >= aTrackInfo->mNumLeadingImplicitTracks +
+              aTrackInfo->mNumExplicitTracks) ?
+          GridTrackType::Implicit :
+          GridTrackType::Explicit
+      ),
+      GridTrackState(aTrackInfo->mStates[i])
+    );
+  }
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/grid/GridTracks.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozilla_dom_GridTracks_h
+#define mozilla_dom_GridTracks_h
+
+#include "nsTArray.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+
+struct ComputedGridTrackInfo;
+
+namespace dom {
+
+class GridDimension;
+class GridTrack;
+
+class GridTracks : public nsISupports
+                 , public nsWrapperCache
+{
+public:
+  explicit GridTracks(GridDimension* aParent);
+
+protected:
+  virtual ~GridTracks();
+
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridTracks)
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+  GridDimension* GetParentObject()
+  {
+    return mParent;
+  }
+
+  uint32_t Length() const;
+  GridTrack* Item(uint32_t aIndex);
+  GridTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
+
+  void SetTrackInfo(const mozilla::ComputedGridTrackInfo* aTrackInfo);
+
+protected:
+  RefPtr<GridDimension> mParent;
+  nsTArray<RefPtr<GridTrack>> mTracks;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_GridTracks_h */
new file mode 100644
--- /dev/null
+++ b/dom/grid/moz.build
@@ -0,0 +1,31 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=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/.
+
+MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
+
+EXPORTS.mozilla.dom += [
+    'Grid.h',
+    'GridDimension.h',
+    'GridLine.h',
+    'GridLines.h',
+    'GridTrack.h',
+    'GridTracks.h',
+]
+
+UNIFIED_SOURCES += [
+    'Grid.cpp',
+    'GridDimension.cpp',
+    'GridLine.cpp',
+    'GridLines.cpp',
+    'GridTrack.cpp',
+    'GridTracks.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/layout/generic',
+]
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome.ini
@@ -0,0 +1,3 @@
+[chrome/test_grid_object.html]
+[chrome/test_grid_state.html]
+[chrome/test_grid_repeats.html]
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_object.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+	margin: 40px;
+}
+.wrapper {
+	display: grid;
+	width: 400px;
+	grid-gap: 10px;
+	grid-template-columns: 100px 1fr 1fr 100px;
+	background-color: #f00;
+}
+.box {
+	background-color: #444;
+	color: #fff;
+}
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var boxA = document.getElementById("boxA");
+	var boxB = document.getElementById("boxB");
+	var boxC = document.getElementById("boxC");
+
+	// test function existence
+	is(typeof(wrapper.getGridFragments), "function",
+		"getGridFragments function exists."
+	);
+	
+	// test that display:grid elements have grids and display:block elements don't	
+	is(boxA.getGridFragments().length, 0, "No grid on display:block styled objects.");
+	is(wrapper.getGridFragments().length, 1,
+		"One grid on an un-fragmented display:grid styled object."
+	);
+	
+	var grid = wrapper.getGridFragments()[0];
+	
+	// test column and row track counts
+	is(grid.cols.tracks.length, 4,
+		"Grid.cols.tracks property has length that matches grid-template-columns."
+	);
+	is(grid.rows.tracks.length, 2,
+		"Grid.rows.tracks property has length that matches content."
+	);
+	
+	// test column track position
+	is(grid.cols.tracks[1].start, 110, "Grid column track 1 position is as expected.");
+	
+	// test column track width
+	is(grid.cols.tracks[0].breadth, boxA.offsetWidth,
+		"Grid column track width (fixed size) matches item width."
+	);
+	is(grid.cols.tracks[1].breadth, boxB.offsetWidth,
+		"Grid column track width (flexible size) matches item width."
+	);
+	is(grid.cols.tracks[1].breadth, grid.cols.tracks[2].breadth,
+		"Grid column track widths with equal proportion flexible size actually are same size."
+	);
+	
+	// test explicit / implicit tracks
+	is(grid.cols.tracks[0].type, "explicit", "Grid column track 0 is explicit.");
+	is(grid.rows.tracks[0].type, "implicit", "Grid row track 0 is implicit.");
+	
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+	<div id="wrapper" class="wrapper">
+		<div id="boxA" class="box a">A</div>
+		<div id="boxB" class="box b">B</div>
+		<div id="boxC" class="box c">C</div>
+		<div class="box d">D</div>
+		<div class="box e">E</div>
+		<div class="box f">F</div>
+		<div class="box g">G</div>
+		<div class="box h">H</div>
+	</div>
+	
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_repeats.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+	margin: 40px;
+}
+.wrapper {
+	display: grid;
+	width: 600px;
+	grid-gap: 10px;
+	grid-template-columns: repeat(2, 100px) repeat(auto-fill, 50px);
+	background-color: #f00;
+}
+.box {
+	background-color: #444;
+	color: #fff;
+}
+
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var grid = wrapper.getGridFragments()[0];
+	
+	// test state of tracks
+	is(grid.cols.tracks[1].state, "static", "Grid column track 1 is marked as static.");
+	is(grid.cols.tracks[2].state, "repeat", "Grid column track 2 is marked as repeat.");
+	
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+	<div id="wrapper" class="wrapper">
+		<div id="boxA" class="box a">A</div>
+		<div class="box b">B</div>
+		<div class="box c">C</div>
+		<div class="box d">D</div>
+		<div class="box e">E</div>
+	</div>
+	
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/grid/test/chrome/test_grid_state.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+body {
+	margin: 40px;
+}
+.wrapper {
+	display: grid;
+	width: 600px;
+	grid-gap: 0px;
+	grid-template-columns: 50px repeat(auto-fit, 100px);
+	background-color: #f00;
+}
+.box {
+	background-color: #444;
+	color: #fff;
+}
+.a {
+	grid-column: 3;
+}
+</style>
+
+<script>
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+	var wrapper = document.getElementById("wrapper");
+	var grid = wrapper.getGridFragments()[0];
+	
+	// test count after removal
+	is(grid.cols.tracks.length, 2, "Grid column track array compensates for removed auto-fit columns.");
+	
+	// test state of tracks
+	is(grid.cols.tracks[0].state, "static", "Grid column track 0 is marked as static.");
+	is(grid.cols.tracks[1].state, "repeat", "Grid column track 1 is marked as repeat.");
+	
+	SimpleTest.finish();
+}
+</script>
+</head>
+<body onLoad="runTests();">
+
+	<div id="wrapper" class="wrapper">
+		<div id="boxA" class="box a">A</div>
+	</div>
+	
+</body>
+</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2248,17 +2248,17 @@ HTMLInputElement::ConvertStringToNumber(
         if (!aResultValue.isFinite()) {
           return false;
         }
         return true;
       }
     case NS_FORM_INPUT_DATE:
       {
         uint32_t year, month, day;
-        if (!GetValueAsDate(aValue, &year, &month, &day)) {
+        if (!ParseDate(aValue, &year, &month, &day)) {
           return false;
         }
 
         JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
         if (!time.isValid()) {
           return false;
         }
 
@@ -2505,17 +2505,17 @@ HTMLInputElement::GetValueAsDate(ErrorRe
   }
 
   switch (mType) {
     case NS_FORM_INPUT_DATE:
     {
       uint32_t year, month, day;
       nsAutoString value;
       GetValueInternal(value);
-      if (!GetValueAsDate(value, &year, &month, &day)) {
+      if (!ParseDate(value, &year, &month, &day)) {
         return Nullable<Date>();
       }
 
       JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
       return Nullable<Date>(Date(time));
     }
     case NS_FORM_INPUT_TIME:
     {
@@ -5336,16 +5336,23 @@ HTMLInputElement::SanitizeValue(nsAStrin
       break;
     case NS_FORM_INPUT_TIME:
       {
         if (!aValue.IsEmpty() && !IsValidTime(aValue)) {
           aValue.Truncate();
         }
       }
       break;
+    case NS_FORM_INPUT_MONTH:
+      {
+        if (!aValue.IsEmpty() && !IsValidMonth(aValue)) {
+          aValue.Truncate();
+        }
+      }
+      break;
     case NS_FORM_INPUT_COLOR:
       {
         if (IsValidSimpleColor(aValue)) {
           ToLowerCase(aValue);
         } else {
           // Set default (black) color, if aValue wasn't parsed correctly.
           aValue.AssignLiteral("#000000");
         }
@@ -5366,59 +5373,89 @@ bool HTMLInputElement::IsValidSimpleColo
         !(aValue[i] >= 'A' && aValue[i] <= 'F')) {
       return false;
     }
   }
   return true;
 }
 
 bool
-HTMLInputElement::IsValidDate(const nsAString& aValue) const
-{
-  uint32_t year, month, day;
-  return GetValueAsDate(aValue, &year, &month, &day);
+HTMLInputElement::IsValidMonth(const nsAString& aValue) const
+{
+  uint32_t year, month;
+  return ParseMonth(aValue, &year, &month);
 }
 
 bool
-HTMLInputElement::GetValueAsDate(const nsAString& aValue,
+HTMLInputElement::IsValidDate(const nsAString& aValue) const
+{
+  uint32_t year, month, day;
+  return ParseDate(aValue, &year, &month, &day);
+}
+
+bool HTMLInputElement::ParseYear(const nsAString& aValue, uint32_t* aYear) const
+{
+  if (aValue.Length() < 4) {
+    return false;
+  }
+
+  return DigitSubStringToNumber(aValue, 0, aValue.Length(), aYear) &&
+      *aYear > 0;
+}
+
+bool HTMLInputElement::ParseMonth(const nsAString& aValue,
+                                  uint32_t* aYear,
+                                  uint32_t* aMonth) const
+{
+  // Parse the year, month values out a string formatted as 'yyyy-mm'.
+  if (aValue.Length() < 7) {
+    return false;
+  }
+
+  uint32_t endOfYearOffset = aValue.Length() - 3;
+  if (aValue[endOfYearOffset] != '-') {
+    return false;
+  }
+
+  const nsAString& yearStr = Substring(aValue, 0, endOfYearOffset);
+  if (!ParseYear(yearStr, aYear)) {
+    return false;
+  }
+
+  return DigitSubStringToNumber(aValue, endOfYearOffset + 1, 2, aMonth) &&
+         *aMonth > 0 && *aMonth <= 12;
+}
+
+bool HTMLInputElement::ParseDate(const nsAString& aValue,
                                  uint32_t* aYear,
                                  uint32_t* aMonth,
                                  uint32_t* aDay) const
 {
-
 /*
  * Parse the year, month, day values out a date string formatted as 'yyyy-mm-dd'.
  * -The year must be 4 or more digits long, and year > 0
  * -The month must be exactly 2 digits long, and 01 <= month <= 12
  * -The day must be exactly 2 digit long, and 01 <= day <= maxday
  *  Where maxday is the number of days in the month 'month' and year 'year'
  */
-
   if (aValue.Length() < 10) {
     return false;
   }
 
-  uint32_t endOfYearOffset = aValue.Length() - 6;
-
-  if (aValue[endOfYearOffset]     != '-' ||
-      aValue[endOfYearOffset + 3] != '-') {
+  uint32_t endOfMonthOffset = aValue.Length() - 3;
+  if (aValue[endOfMonthOffset] != '-') {
     return false;
   }
 
-  if (!DigitSubStringToNumber(aValue, 0, endOfYearOffset, aYear) ||
-      *aYear < 1) {
+  const nsAString& yearMonthStr = Substring(aValue, 0, endOfMonthOffset);
+  if (!ParseMonth(yearMonthStr, aYear, aMonth)) {
     return false;
   }
 
-  if (!DigitSubStringToNumber(aValue, endOfYearOffset + 1, 2, aMonth) ||
-      *aMonth < 1 || *aMonth > 12) {
-    return false;
-  }
-
-  return DigitSubStringToNumber(aValue, endOfYearOffset + 4, 2, aDay) &&
+  return DigitSubStringToNumber(aValue, endOfMonthOffset + 1, 2, aDay) &&
          *aDay > 0 && *aDay <= NumberOfDaysInMonth(*aMonth, *aYear);
 }
 
 uint32_t
 HTMLInputElement::NumberOfDaysInMonth(uint32_t aMonth, uint32_t aYear) const
 {
 /*
  * Returns the number of days in a month.
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -1147,33 +1147,63 @@ protected:
    * Parse a color string of the form #XXXXXX where X should be hexa characters
    * @param the string to be parsed.
    * @return whether the string is a valid simple color.
    * Note : this function does not consider the empty string as valid.
    */
   bool IsValidSimpleColor(const nsAString& aValue) const;
 
   /**
+   * Parse a date string of the form yyyy-mm
+   * @param the string to be parsed.
+   * @return whether the string is a valid month.
+   * Note : this function does not consider the empty string as valid.
+   */
+  bool IsValidMonth(const nsAString& aValue) const;
+
+  /**
    * Parse a date string of the form yyyy-mm-dd
    * @param the string to be parsed.
    * @return whether the string is a valid date.
    * Note : this function does not consider the empty string as valid.
    */
   bool IsValidDate(const nsAString& aValue) const;
 
   /**
+   * Parse a year string of the form yyyy
+   *
+   * @param the string to be parsed.
+   *
+   * @return the year in aYear.
+   * @return whether the parsing was successful.
+   */
+  bool ParseYear(const nsAString& aValue, uint32_t* aYear) const;
+
+  /**
+   * Parse a month string of the form yyyy-mm
+   *
+   * @param the string to be parsed.
+   * @return the year and month in aYear and aMonth.
+   * @return whether the parsing was successful.
+   */
+  bool ParseMonth(const nsAString& aValue,
+                  uint32_t* aYear,
+                  uint32_t* aMonth) const;
+
+  /**
    * Parse a date string of the form yyyy-mm-dd
+   *
    * @param the string to be parsed.
    * @return the date in aYear, aMonth, aDay.
    * @return whether the parsing was successful.
    */
-  bool GetValueAsDate(const nsAString& aValue,
-                      uint32_t* aYear,
-                      uint32_t* aMonth,
-                      uint32_t* aDay) const;
+  bool ParseDate(const nsAString& aValue,
+                 uint32_t* aYear,
+                 uint32_t* aMonth,
+                 uint32_t* aDay) const;
 
   /**
    * This methods returns the number of days in a given month, for a given year.
    */
   uint32_t NumberOfDaysInMonth(uint32_t aMonth, uint32_t aYear) const;
 
   /**
    * Returns whether aValue is a valid time as described by HTML specifications:
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -64,16 +64,17 @@
 #include "nsURIHashKey.h"
 #include "nsJSUtils.h"
 #include "MediaStreamGraph.h"
 #include "nsIScriptError.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "mozilla/dom/MediaSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaSourceDecoder.h"
+#include "MediaStreamListener.h"
 #include "DOMMediaStream.h"
 #include "AudioStreamTrack.h"
 #include "VideoStreamTrack.h"
 #include "MediaTrackList.h"
 
 #include "AudioChannelService.h"
 
 #include "mozilla/dom/power/PowerManagerService.h"
@@ -269,16 +270,66 @@ public:
                                                 mSource,
                                                 NS_LITERAL_STRING("error"),
                                                 false,
                                                 false);
   }
 };
 
 /**
+ * This listener observes the first video frame to arrive with a non-empty size,
+ * and calls HTMLMediaElement::ReceivedMediaStreamInitialSize() with that size.
+ */
+class HTMLMediaElement::StreamSizeListener : public DirectMediaStreamTrackListener {
+public:
+  explicit StreamSizeListener(HTMLMediaElement* aElement) :
+    mElement(aElement),
+    mInitialSizeFound(false)
+  {}
+  void Forget() { mElement = nullptr; }
+
+  void ReceivedSize(gfx::IntSize aSize)
+  {
+    if (!mElement) {
+      return;
+    }
+    RefPtr<HTMLMediaElement> deathGrip = mElement;
+    mElement->UpdateInitialMediaSize(aSize);
+  }
+
+  void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
+                               StreamTime aTrackOffset,
+                               const MediaSegment& aMedia) override
+  {
+    if (mInitialSizeFound || aMedia.GetType() != MediaSegment::VIDEO) {
+      return;
+    }
+    const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
+    for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
+      if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) {
+        mInitialSizeFound = true;
+        nsCOMPtr<nsIRunnable> event =
+          NewRunnableMethod<gfx::IntSize>(
+              this, &StreamSizeListener::ReceivedSize,
+              c->mFrame.GetIntrinsicSize());
+        aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+        return;
+      }
+    }
+  }
+
+private:
+  // These fields may only be accessed on the main thread
+  HTMLMediaElement* mElement;
+
+  // These fields may only be accessed on the MSG thread
+  bool mInitialSizeFound;
+};
+
+/**
  * There is a reference cycle involving this class: MediaLoadListener
  * holds a reference to the HTMLMediaElement, which holds a reference
  * to an nsIChannel, which holds a reference to this listener.
  * We break the reference cycle in OnStartRequest by clearing mElement.
  */
 class HTMLMediaElement::MediaLoadListener final : public nsIStreamListener,
                                                   public nsIChannelEventSink,
                                                   public nsIInterfaceRequestor,
@@ -651,16 +702,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioTrackList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoTrackList)
 #ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
 #endif
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedVideoStreamTrack)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
   if (tmp->mSrcStream) {
     // Need to EndMediaStreamPlayback to clear mSrcStream and make sure everything
     // gets unhooked correctly.
     tmp->EndSrcMediaStreamPlayback();
   }
@@ -677,16 +729,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
 #ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
 #endif
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedVideoStreamTrack)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 // nsIDOMHTMLMediaElement
@@ -875,16 +928,24 @@ void HTMLMediaElement::AbortExistingLoad
 
   if (mChannelLoader) {
     mChannelLoader->Cancel();
     mChannelLoader = nullptr;
   }
 
   bool fireTimeUpdate = false;
 
+  // We need to remove StreamSizeListener before VideoTracks get emptied.
+  if (mMediaStreamSizeListener) {
+    mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
+    mSelectedVideoStreamTrack = nullptr;
+    mMediaStreamSizeListener->Forget();
+    mMediaStreamSizeListener = nullptr;
+  }
+
   // When aborting the existing loads, empty the objects in audio track list and
   // video track list, no events (in particular, no removetrack events) are
   // fired as part of this. Ending MediaStream sends track ended notifications,
   // so we empty the track lists prior.
   AudioTracks()->EmptyTracks();
   VideoTracks()->EmptyTracks();
 
   if (mDecoder) {
@@ -3331,19 +3392,19 @@ public:
     if (aBlocked == BLOCKED) {
       event = NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
     } else {
       event = NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
     }
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
   virtual void NotifyEvent(MediaStreamGraph* aGraph,
-                           MediaStreamListener::MediaStreamGraphEvent event) override
+                           MediaStreamGraphEvent event) override
   {
-    if (event == EVENT_FINISHED) {
+    if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
       nsCOMPtr<nsIRunnable> event =
         NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
   virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) override
   {
     MutexAutoLock lock(mMutex);
@@ -3370,69 +3431,16 @@ private:
   bool mBlocked;
   bool mFinished;
 
   // mMutex protects the fields below; they can be accessed on any thread
   Mutex mMutex;
   bool mPendingNotifyOutput;
 };
 
-/**
- * This listener observes the first video frame to arrive with a non-empty size,
- * and calls HTMLMediaElement::ReceivedMediaStreamInitialSize() with that size.
- */
-class HTMLMediaElement::StreamSizeListener : public MediaStreamListener {
-public:
-  explicit StreamSizeListener(HTMLMediaElement* aElement) :
-    mElement(aElement),
-    mInitialSizeFound(false)
-  {}
-  void Forget() { mElement = nullptr; }
-
-  void ReceivedSize(gfx::IntSize aSize)
-  {
-    if (!mElement) {
-      return;
-    }
-    RefPtr<HTMLMediaElement> deathGrip = mElement;
-    mElement->UpdateInitialMediaSize(aSize);
-  }
-
-  void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                StreamTime aTrackOffset,
-                                uint32_t aTrackEvents,
-                                const MediaSegment& aQueuedMedia,
-                                MediaStream* aInputStream,
-                                TrackID aInputTrackID) override
-  {
-    if (mInitialSizeFound || aQueuedMedia.GetType() != MediaSegment::VIDEO) {
-      return;
-    }
-    const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
-    for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
-      if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) {
-        mInitialSizeFound = true;
-        nsCOMPtr<nsIRunnable> event =
-          NewRunnableMethod<gfx::IntSize>(
-              this, &StreamSizeListener::ReceivedSize,
-              c->mFrame.GetIntrinsicSize());
-        aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
-        return;
-      }
-    }
-  }
-
-private:
-  // These fields may only be accessed on the main thread
-  HTMLMediaElement* mElement;
-
-  // These fields may only be accessed on the MSG thread
-  bool mInitialSizeFound;
-};
-
 class HTMLMediaElement::MediaStreamTracksAvailableCallback:
   public OnTracksAvailableCallback
 {
 public:
   explicit MediaStreamTracksAvailableCallback(HTMLMediaElement* aElement):
       OnTracksAvailableCallback(), mElement(aElement)
     {}
   virtual void NotifyTracksAvailable(DOMMediaStream* aStream)
@@ -3541,19 +3549,16 @@ void HTMLMediaElement::SetupSrcMediaStre
   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return;
   }
 
   RefPtr<MediaStream> stream = GetSrcMediaStream();
   if (stream) {
     stream->SetAudioChannelType(mAudioChannel);
-
-    mMediaStreamSizeListener = new StreamSizeListener(this);
-    stream->AddListener(mMediaStreamSizeListener);
   }
 
   UpdateSrcMediaStreamPlaying();
 
   // If we pause this media element, track changes in the underlying stream
   // will continue to fire events at this element and alter its track list.
   // That's simpler than delaying the events, but probably confusing...
   ConstructMediaTracks();
@@ -3574,20 +3579,18 @@ void HTMLMediaElement::SetupSrcMediaStre
 
 void HTMLMediaElement::EndSrcMediaStreamPlayback()
 {
   MOZ_ASSERT(mSrcStream);
 
   UpdateSrcMediaStreamPlaying(REMOVING_SRC_STREAM);
 
   if (mMediaStreamSizeListener) {
-    RefPtr<MediaStream> stream = GetSrcMediaStream();
-    if (stream) {
-      stream->RemoveListener(mMediaStreamSizeListener);
-    }
+    mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
+    mSelectedVideoStreamTrack = nullptr;
     mMediaStreamSizeListener->Forget();
     mMediaStreamSizeListener = nullptr;
   }
 
   mSrcStream->UnregisterTrackListener(mMediaStreamTrackListener);
   mMediaStreamTrackListener = nullptr;
 
   mSrcStream->RemovePrincipalChangeObserver(this);
@@ -3613,17 +3616,18 @@ static already_AddRefed<VideoTrack>
 CreateVideoTrack(VideoStreamTrack* aStreamTrack)
 {
   nsAutoString id;
   nsAutoString label;
   aStreamTrack->GetId(id);
   aStreamTrack->GetLabel(label);
 
   return MediaTrackList::CreateVideoTrack(id, NS_LITERAL_STRING("main"),
-                                          label, EmptyString());
+                                          label, EmptyString(),
+                                          aStreamTrack);
 }
 
 void HTMLMediaElement::ConstructMediaTracks()
 {
   nsTArray<RefPtr<MediaStreamTrack>> tracks;
   mSrcStream->GetTracks(tracks);
 
   int firstEnabledVideo = -1;
@@ -3645,16 +3649,21 @@ void HTMLMediaElement::ConstructMediaTra
   }
 
   if (VideoTracks()->Length() > 0) {
     // If media resource does not indicate a particular set of video tracks to
     // enable, the one that is listed first in the element's videoTracks object
     // must be selected.
     int index = firstEnabledVideo >= 0 ? firstEnabledVideo : 0;
     (*VideoTracks())[index]->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
+    VideoTrack* track = (*VideoTracks())[index];
+    VideoStreamTrack* streamTrack = track->GetVideoStreamTrack();
+    mMediaStreamSizeListener = new StreamSizeListener(this);
+    streamTrack->AddDirectListener(mMediaStreamSizeListener);
+    mSelectedVideoStreamTrack = streamTrack;
   }
 }
 
 void
 HTMLMediaElement::NotifyMediaStreamTrackAdded(const RefPtr<MediaStreamTrack>& aTrack)
 {
   MOZ_ASSERT(aTrack);
 
@@ -3665,18 +3674,30 @@ HTMLMediaElement::NotifyMediaStreamTrack
   LOG(LogLevel::Debug, ("%p, Adding MediaTrack with id %s",
                         this, NS_ConvertUTF16toUTF8(id).get()));
 #endif
 
   if (AudioStreamTrack* t = aTrack->AsAudioStreamTrack()) {
     RefPtr<AudioTrack> audioTrack = CreateAudioTrack(t);
     AudioTracks()->AddTrack(audioTrack);
   } else if (VideoStreamTrack* t = aTrack->AsVideoStreamTrack()) {
+    // TODO: Fix this per the spec on bug 1273443.
+    int32_t selectedIndex = VideoTracks()->SelectedIndex();
     RefPtr<VideoTrack> videoTrack = CreateVideoTrack(t);
     VideoTracks()->AddTrack(videoTrack);
+    // New MediaStreamTrack added, set the new added video track as selected
+    // video track when there is no selected track.
+    if (selectedIndex == -1) {
+      MOZ_ASSERT(!mSelectedVideoStreamTrack);
+      videoTrack->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
+      mMediaStreamSizeListener = new StreamSizeListener(this);
+      t->AddDirectListener(mMediaStreamSizeListener);
+      mSelectedVideoStreamTrack = t;
+    }
+
   }
 }
 
 void
 HTMLMediaElement::NotifyMediaStreamTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
 {
   MOZ_ASSERT(aTrack);
 
@@ -3685,16 +3706,58 @@ HTMLMediaElement::NotifyMediaStreamTrack
 
   LOG(LogLevel::Debug, ("%p, Removing MediaTrack with id %s",
                         this, NS_ConvertUTF16toUTF8(id).get()));
 
   if (MediaTrack* t = AudioTracks()->GetTrackById(id)) {
     AudioTracks()->RemoveTrack(t);
   } else if (MediaTrack* t = VideoTracks()->GetTrackById(id)) {
     VideoTracks()->RemoveTrack(t);
+    // TODO: Fix this per the spec on bug 1273443.
+    // If the removed media stream track is selected video track and there are
+    // still video tracks, change the selected video track to the first
+    // remaining track.
+    if (aTrack == mSelectedVideoStreamTrack) {
+      // The mMediaStreamSizeListener might already reset to nullptr.
+      if (mMediaStreamSizeListener) {
+        mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
+      }
+      mSelectedVideoStreamTrack = nullptr;
+      MOZ_ASSERT(mSrcStream);
+      nsTArray<RefPtr<VideoStreamTrack>> tracks;
+      mSrcStream->GetVideoTracks(tracks);
+
+      for (const RefPtr<VideoStreamTrack>& track : tracks) {
+        if (track->Ended()) {
+          continue;
+        }
+        if (!track->Enabled()) {
+          continue;
+        }
+
+        nsAutoString trackId;
+        track->GetId(trackId);
+        MediaTrack* videoTrack = VideoTracks()->GetTrackById(trackId);
+        MOZ_ASSERT(videoTrack);
+
+        videoTrack->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
+        if (mMediaStreamSizeListener) {
+          track->AddDirectListener(mMediaStreamSizeListener);
+        }
+        mSelectedVideoStreamTrack = track;
+        return;
+      }
+
+      // There is no enabled video track existing, clean the
+      // mMediaStreamSizeListener.
+      if (mMediaStreamSizeListener) {
+        mMediaStreamSizeListener->Forget();
+        mMediaStreamSizeListener = nullptr;
+      }
+    }
   } else {
     // XXX (bug 1208328) Uncomment this when DOMMediaStream doesn't call
     // NotifyTrackRemoved multiple times for the same track, i.e., when it
     // implements the "addtrack" and "removetrack" events.
     // NS_ASSERTION(false, "MediaStreamTrack ended but did not exist in track lists");
     return;
   }
 }
@@ -4603,20 +4666,17 @@ void HTMLMediaElement::UpdateInitialMedi
 {
   if (!mMediaInfo.HasVideo()) {
     UpdateMediaSize(aSize);
   }
 
   if (!mMediaStreamSizeListener) {
     return;
   }
-  RefPtr<MediaStream> stream = GetSrcMediaStream();
-  if (stream) {
-    stream->RemoveListener(mMediaStreamSizeListener);
-  }
+  mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
   mMediaStreamSizeListener->Forget();
   mMediaStreamSizeListener = nullptr;
 }
 
 void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
 {
   LOG(LogLevel::Debug, ("%p SuspendOrResumeElement(pause=%d, suspendEvents=%d) hidden=%d",
       this, aPauseElement, aSuspendEvents, OwnerDoc()->Hidden()));
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1242,16 +1242,18 @@ protected:
   nsTArray<OutputMediaStream> mOutputStreams;
 
   // Holds a reference to the MediaStreamListener attached to mSrcStream's
   // playback stream.
   RefPtr<StreamListener> mMediaStreamListener;
   // Holds a reference to the size-getting MediaStreamListener attached to
   // mSrcStream.
   RefPtr<StreamSizeListener> mMediaStreamSizeListener;
+  // The selected video stream track which contained mMediaStreamSizeListener.
+  RefPtr<VideoStreamTrack> mSelectedVideoStreamTrack;
 
   const RefPtr<ShutdownObserver> mShutdownObserver;
 
   // Holds a reference to the MediaSource, if any, referenced by the src
   // attribute on the media element.
   RefPtr<MediaSource> mSrcMediaSource;
 
   // Holds a reference to the MediaSource supplying data for playback.  This
--- a/dom/html/test/forms/test_input_sanitization.html
+++ b/dom/html/test/forms/test_input_sanitization.html
@@ -69,17 +69,17 @@ function flushDelayedTests(description)
 }
 
 // We are excluding "file" because it's too different from the other types.
 // And it has no sanitizing algorithm.
 var inputTypes =
 [
   "text", "password", "search", "tel", "hidden", "checkbox", "radio",
   "submit", "image", "reset", "button", "email", "url", "number", "date",
-  "time", "range", "color"
+  "time", "range", "color", "month"
 ];
 
 var todoTypes =
 [
   "week", "datetime", "datetime-local",
 ];
 
 var valueModeValue =
@@ -171,16 +171,30 @@ function sanitizeValue(aType, aValue)
         return aValue;
       }
       match = /^.([0-9]{1,3})$/.exec(other);
       if (!match) {
         return "";
       }
       return aValue;
     case "month":
+      // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-month-string
+      var match = /^([0-9]{4,})-([0-9]{2})$/.exec(aValue);
+      if (!match) {
+        return "";
+      }
+      var year = Number(match[1]);
+      if (year === 0) {
+        return "";
+      }
+      var month = Number(match[2]);
+      if (month > 12 || month < 1) {
+        return "";
+      }
+      return aValue;
     case "week":
     case "datetime":
     case "datetime-local":
       // TODO: write the sanitize algorithm.
       ok(false);
       return "";
     case "color":
       return /^#[0-9A-Fa-f]{6}$/.exec(aValue) ? aValue.toLowerCase() : "#000000";
@@ -325,16 +339,37 @@ function checkSanitizing(element, inputT
     "red",
     "#0f0",
     "#FFFFAA",
     "FFAABB",
     "fFAaBb",
     "FFAAZZ",
     "ABCDEF",
     "#7654321",
+    // For month
+    "1970-01",
+    "1234-12",
+    "123456789-01",
+    "2013-13",
+    "0000-00",
+    "2015-00",
+    "0001-01",
+    "1-1",
+    "888-05",
+    "2013-3",
+    "2013-may",
+    "2000-1a",
+    "2013-03-13",
+    "december",
+    "abcdef",
+    "12",
+    "  2013-03",
+    "2013 - 03",
+    "2013 03",
+    "2013/03",
   ];
 
   for (value of testData) {
     element.setAttribute('value', value);
     delayed_is(element.value, sanitizeValue(type, value),
        "The value has not been correctly sanitized for type=" + type);
     delayed_is(element.getAttribute('value'), value,
        "The content value should not have been sanitized");
--- a/dom/html/test/forms/test_input_typing_sanitization.html
+++ b/dom/html/test/forms/test_input_typing_sanitization.html
@@ -176,16 +176,33 @@ function runTest()
         '-00:00',
         '00:00:00.',
         '00:60',
         '10:58:99',
         ':19:10',
         '23:08:09.1012',
       ]
     },
+    {
+      type: 'month',
+      validData: [
+        '0001-01',
+        '2012-12',
+        '100000-01',
+      ],
+      invalidData: [
+        '1-01',
+        '-',
+        'december',
+        '2012-dec',
+        '2012/12',
+        '2012-99',
+        '2012-1',
+      ]
+    },
     { type: 'week', todo: true },
     { type: 'datetime', todo: true },
     { type: 'datetime-local', todo: true },
   ];
 
   for (test of data) {
     gCurrentTest = test;
 
--- a/dom/html/test/test_bug590363.html
+++ b/dom/html/test/test_bug590363.html
@@ -42,39 +42,40 @@ var testData = [
   [ 'month',    true ]
   // 'file' is treated separatly.
 ];
 
 var todoTypes = [
   "datetime", "week", "datetime-local"
 ];
 
-var nonTrivialSanitizing = [ 'number', 'date', 'time', 'color' ];
+var nonTrivialSanitizing = [ 'number', 'date', 'time', 'color', 'month' ];
 
 var length = testData.length;
 for (var i=0; i<length; ++i) {
   for (var j=0; j<length; ++j) {
     var e = document.createElement('input');
     e.type = testData[i][0];
 
     var expectedValue;
 
     // range will sanitize its value to 50 (the default) if it isn't a valid
     // number. We need to handle that specially.
     if (testData[j][0] == 'range' || testData[i][0] == 'range') {
-      if (testData[j][0] == 'date' || testData[j][0] == 'time') {
+      if (testData[j][0] == 'date' || testData[j][0] == 'time' ||
+          testData[j][0] == 'month') {
         expectedValue = '';
       } else if (testData[j][0] == 'color') {
         expectedValue = '#000000';
       } else {
         expectedValue = '50';
       }
     } else if (testData[i][0] == 'color' || testData[j][0] == 'color') {
         if (testData[j][0] == 'number' || testData[j][0] == 'date' ||
-            testData[j][0] == 'time') {
+            testData[j][0] == 'time' || testData[j][0] == 'month') {
           expectedValue = ''
         } else {
           expectedValue = '#000000';
         }
     } else if (nonTrivialSanitizing.indexOf(testData[i][0]) != -1 &&
                nonTrivialSanitizing.indexOf(testData[j][0]) != -1) {
       expectedValue = '';
     } else if (testData[i][0] == 'number' || testData[j][0] == 'number') {
--- a/dom/ipc/ContentBridgeChild.cpp
+++ b/dom/ipc/ContentBridgeChild.cpp
@@ -23,18 +23,16 @@ NS_IMPL_ISUPPORTS(ContentBridgeChild,
                   nsIContentChild)
 
 ContentBridgeChild::ContentBridgeChild(Transport* aTransport)
   : mTransport(aTransport)
 {}
 
 ContentBridgeChild::~ContentBridgeChild()
 {
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 void
 ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy));
 }
 
--- a/dom/ipc/ContentBridgeParent.cpp
+++ b/dom/ipc/ContentBridgeParent.cpp
@@ -22,18 +22,16 @@ NS_IMPL_ISUPPORTS(ContentBridgeParent,
                   nsIObserver)
 
 ContentBridgeParent::ContentBridgeParent(Transport* aTransport)
   : mTransport(aTransport)
 {}
 
 ContentBridgeParent::~ContentBridgeParent()
 {
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 void
 ContentBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
     os->RemoveObserver(this, "content-child-shutdown");
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2266,18 +2266,16 @@ ContentParent::ContentParent(mozIApplica
   GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   ChildPrivileges privs = aIsNuwaProcess
     ? base::PRIVILEGES_INHERIT
     : base::PRIVILEGES_DEFAULT;
   mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs);
-
-  IToplevelProtocol::SetTransport(mSubprocess->GetChannel());
 }
 
 #ifdef MOZ_NUWA_PROCESS
 static const mozilla::ipc::FileDescriptor*
 FindFdProtocolFdMapping(const nsTArray<ProtocolFdMapping>& aFds,
                         ProtocolId aProtoId)
 {
   for (unsigned int i = 0; i < aFds.Length(); i++) {
@@ -2472,16 +2470,24 @@ ContentParent::InitInternal(ProcessPrior
   bool shouldSandbox = true;
 #ifdef MOZ_NUWA_PROCESS
   if (IsNuwaProcess()) {
     shouldSandbox = false;
   }
 #endif
   MaybeFileDesc brokerFd = void_t();
 #ifdef XP_LINUX
+  // XXX: Checking the pref here makes it possible to enable/disable sandboxing
+  // during an active session. Currently the pref is only used for testing
+  // purpose. If the decision is made to permanently rely on the pref, this
+  // should be changed so that it is required to restart firefox for the change
+  // of value to take effect.
+  shouldSandbox = (Preferences::GetInt("security.sandbox.content.level") > 0) &&
+    !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX");
+
   if (shouldSandbox) {
     MOZ_ASSERT(!mSandboxBroker);
     UniquePtr<SandboxBroker::Policy> policy =
       sSandboxBrokerPolicyFactory->GetContentPolicy(Pid());
     if (policy) {
       brokerFd = FileDescriptor();
       mSandboxBroker = SandboxBroker::Create(Move(policy), Pid(), brokerFd);
       if (!mSandboxBroker) {
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -157,30 +157,27 @@ public:
   NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
 
   // Called when a content process shuts down.
   void Clear() {
     mContentParent = nullptr;
     mActor = nullptr;
   }
 
-  /** Sets the information associated with this hang: this includes the ID of
-   * the plugin which caused the hang as well as the content PID.
+  /**
+   * Sets the information associated with this hang: this includes the ID of
+   * the plugin which caused the hang as well as the content PID. The ID of
+   * a minidump taken during the hang can also be provided.
    *
    * @param aHangData The hang information
+   * @param aDumpId The ID of a minidump taken when the hang occurred
    */
-  void SetHangData(const HangData& aHangData) { mHangData = aHangData; }
-
-  /** Sets the ID of the crash dump associated with this hang. When the ID has
-   * been set then the corresponding crash dump will be used for reporting
-   * instead of generating a new one.
-   *
-   * @param aId The ID of the crash dump taken when the hang was detected. */
-  void SetDumpId(nsString& aId) {
-    mDumpId = aId;
+  void SetHangData(const HangData& aHangData, const nsAString& aDumpId) {
+    mHangData = aHangData;
+    mDumpId = aDumpId;
   }
 
   void ClearHang() {
     mHangData = HangData();
     mDumpId.Truncate();
   }
 
 private:
@@ -211,19 +208,27 @@ public:
 
   void Shutdown();
 
   void TerminateScript();
   void BeginStartingDebugger();
   void EndStartingDebugger();
   void CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles);
 
+  /**
+   * Update the dump for the specified plugin. This method is thread-safe and
+   * is used to replace a browser minidump with a full minidump. If aDumpId is
+   * empty this is a no-op.
+   */
+  void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId);
+
   MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
 
- private:
+private:
+  bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
   void ShutdownOnThread();
 
   const RefPtr<ProcessHangMonitor> mHangMonitor;
 
   // This field is read-only after construction.
   bool mReportHangs;
 
   // This field is only accessed on the hang thread.
@@ -253,21 +258,16 @@ HangMonitorChild::HangMonitorChild(Proce
    mShutdownDone(false),
    mIPCOpen(true)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 }
 
 HangMonitorChild::~HangMonitorChild()
 {
-  // For some reason IPDL doesn't automatically delete the channel for a
-  // bridged protocol (bug 1090570). So we have to do it ourselves.
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
-
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInstance == this);
   sInstance = nullptr;
 }
 
 void
 HangMonitorChild::Shutdown()
 {
@@ -470,21 +470,16 @@ HangMonitorParent::HangMonitorParent(Pro
    mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock")
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false);
 }
 
 HangMonitorParent::~HangMonitorParent()
 {
-  // For some reason IPDL doesn't automatically delete the channel for a
-  // bridged protocol (bug 1090570). So we have to do it ourselves.
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
-
 #ifdef MOZ_CRASHREPORTER
   MutexAutoLock lock(mBrowserCrashDumpHashLock);
 
   for (auto iter = mBrowserCrashDumpIds.Iter(); !iter.Done(); iter.Next()) {
     nsString crashId = iter.UserData();
     if (!crashId.IsEmpty()) {
       CrashReporter::DeleteMinidumpFilesForID(crashId);
     }
@@ -545,51 +540,90 @@ HangMonitorParent::Open(Transport* aTran
   DebugOnly<bool> ok = PProcessHangMonitorParent::Open(aTransport, aPid, aIOLoop);
   MOZ_ASSERT(ok);
 }
 
 class HangObserverNotifier final : public Runnable
 {
 public:
   HangObserverNotifier(HangMonitoredProcess* aProcess,
+                       HangMonitorParent *aParent,
                        const HangData& aHangData,
-                       const nsString& aBrowserDumpId)
+                       const nsString& aBrowserDumpId,
+                       bool aTakeMinidump)
     : mProcess(aProcess),
+      mParent(aParent),
       mHangData(aHangData),
-      mBrowserDumpId(aBrowserDumpId)
+      mBrowserDumpId(aBrowserDumpId),
+      mTakeMinidump(aTakeMinidump)
   {}
 
   NS_IMETHOD
   Run()
   {
     // chrome process, main thread
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
     nsString dumpId;
-    if (mHangData.type() == HangData::TPluginHangData) {
+    if ((mHangData.type() == HangData::TPluginHangData) && mTakeMinidump) {
+      // We've been handed a partial minidump; complete it with plugin and
+      // content process dumps.
       const PluginHangData& phd = mHangData.get_PluginHangData();
       plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
                                 mBrowserDumpId, dumpId);
+      mParent->UpdateMinidump(phd.pluginId(), dumpId);
+    } else {
+      // We already have a full minidump; go ahead and use it.
+      dumpId = mBrowserDumpId;
     }
 
-    mProcess->SetHangData(mHangData);
-    mProcess->SetDumpId(dumpId);
+    mProcess->SetHangData(mHangData, dumpId);
 
     nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
     observerService->NotifyObservers(mProcess, "process-hang-report", nullptr);
     return NS_OK;
   }
 
 private:
   RefPtr<HangMonitoredProcess> mProcess;
+  HangMonitorParent* mParent;
   HangData mHangData;
   nsAutoString mBrowserDumpId;
+  bool mTakeMinidump;
 };
 
+// Take a minidump of the browser process if one wasn't already taken for the
+// plugin that caused the hang. Return false if a dump was already available or
+// true if new one has been taken.
+bool
+HangMonitorParent::TakeBrowserMinidump(const PluginHangData& aPhd,
+                                       nsString& aCrashId)
+{
+#ifdef MOZ_CRASHREPORTER
+  MutexAutoLock lock(mBrowserCrashDumpHashLock);
+  if (!mBrowserCrashDumpIds.Get(aPhd.pluginId(), &aCrashId)) {
+    nsCOMPtr<nsIFile> browserDump;
+    if (CrashReporter::TakeMinidump(getter_AddRefs(browserDump), true)) {
+      if (!CrashReporter::GetIDFromMinidump(browserDump, aCrashId)
+          || aCrashId.IsEmpty()) {
+        browserDump->Remove(false);
+        NS_WARNING("Failed to generate timely browser stack, "
+                   "this is bad for plugin hang analysis!");
+      } else {
+        mBrowserCrashDumpIds.Put(aPhd.pluginId(), aCrashId);
+        return true;
+      }
+    }
+  }
+#endif // MOZ_CRASHREPORTER
+
+  return false;
+}
+
 bool
 HangMonitorParent::RecvHangEvidence(const HangData& aHangData)
 {
   // chrome process, background thread
   MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
 
   if (!mReportHangs) {
     return true;
@@ -601,40 +635,27 @@ HangMonitorParent::RecvHangEvidence(cons
   if (IsDebuggerPresent()) {
     return true;
   }
 #endif
 
   // Before we wake up the browser main thread we want to take a
   // browser minidump.
   nsAutoString crashId;
-#ifdef MOZ_CRASHREPORTER
+  bool takeMinidump = false;
   if (aHangData.type() == HangData::TPluginHangData) {
-    MutexAutoLock lock(mBrowserCrashDumpHashLock);
-    const PluginHangData& phd = aHangData.get_PluginHangData();
-    if (!mBrowserCrashDumpIds.Get(phd.pluginId(), &crashId)) {
-      nsCOMPtr<nsIFile> browserDump;
-      if (CrashReporter::TakeMinidump(getter_AddRefs(browserDump), true)) {
-        if (!CrashReporter::GetIDFromMinidump(browserDump, crashId) || crashId.IsEmpty()) {
-          browserDump->Remove(false);
-          NS_WARNING("Failed to generate timely browser stack, this is bad for plugin hang analysis!");
-        } else {
-          mBrowserCrashDumpIds.Put(phd.pluginId(), crashId);
-        }
-      }
-    }
+    takeMinidump = TakeBrowserMinidump(aHangData.get_PluginHangData(), crashId);
   }
-#endif
 
   mHangMonitor->InitiateCPOWTimeout();
 
   MonitorAutoLock lock(mMonitor);
 
   nsCOMPtr<nsIRunnable> notifier =
-    new HangObserverNotifier(mProcess, aHangData, crashId);
+    new HangObserverNotifier(mProcess, this, aHangData, crashId, takeMinidump);
   NS_DispatchToMainThread(notifier);
 
   return true;
 }
 
 class ClearHangNotifier final : public Runnable
 {
 public:
@@ -721,16 +742,27 @@ HangMonitorParent::CleanupPluginHang(uin
   mBrowserCrashDumpIds.Remove(aPluginId);
 #ifdef MOZ_CRASHREPORTER
   if (aRemoveFiles && !crashId.IsEmpty()) {
     CrashReporter::DeleteMinidumpFilesForID(crashId);
   }
 #endif
 }
 
+void
+HangMonitorParent::UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId)
+{
+  if (aDumpId.IsEmpty()) {
+    return;
+  }
+
+  MutexAutoLock lock(mBrowserCrashDumpHashLock);
+  mBrowserCrashDumpIds.Put(aPluginId, aDumpId);
+}
+
 /* HangMonitoredProcess implementation */
 
 NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
 
 NS_IMETHODIMP
 HangMonitoredProcess::GetHangType(uint32_t* aHangType)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
--- a/dom/media/AudioCaptureStream.cpp
+++ b/dom/media/AudioCaptureStream.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Attributes.h"
 #include "AudioCaptureStream.h"
 #include "ImageContainer.h"
@@ -73,17 +74,17 @@ AudioCaptureStream::ProcessInput(GraphTi
   uint32_t inputCount = mInputs.Length();
   StreamTracks::Track* track = EnsureTrack(mTrackId);
   // Notify the DOM everything is in order.
   if (!mTrackCreated) {
     for (uint32_t i = 0; i < mListeners.Length(); i++) {
       MediaStreamListener* l = mListeners[i];
       AudioSegment tmp;
       l->NotifyQueuedTrackChanges(
-        Graph(), mTrackId, 0, MediaStreamListener::TRACK_EVENT_CREATED, tmp);
+        Graph(), mTrackId, 0, TrackEventCommand::TRACK_EVENT_CREATED, tmp);
       l->NotifyFinishedTrackCreation(Graph());
     }
     mTrackCreated = true;
   }
 
   if (IsFinishedOnGraphThread()) {
     return;
   }
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -181,28 +181,28 @@ public:
     if (track) {
       LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at the source. Marking it ended.",
                             mStream, track.get()));
       track->NotifyEnded();
     }
   }
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                StreamTime aTrackOffset, uint32_t aTrackEvents,
+                                StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
-    if (aTrackEvents & TRACK_EVENT_CREATED) {
+    if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
       nsCOMPtr<nsIRunnable> runnable =
         NewRunnableMethod<TrackID, MediaSegment::Type, MediaStream*, TrackID>(
           this, &OwnedStreamListener::DoNotifyTrackCreated,
           aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
-    } else if (aTrackEvents & TRACK_EVENT_ENDED) {
+    } else if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
       nsCOMPtr<nsIRunnable> runnable =
         NewRunnableMethod<MediaStream*, TrackID, TrackID>(
           this, &OwnedStreamListener::DoNotifyTrackEnded,
           aInputStream, aInputTrackID, aID);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
     }
   }
 
@@ -271,22 +271,22 @@ public:
     }
 
     mStream->NotifyTracksCreated();
   }
 
   // The methods below are called on the MediaStreamGraph thread.
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                StreamTime aTrackOffset, uint32_t aTrackEvents,
+                                StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
-    if (aTrackEvents & TRACK_EVENT_ENDED) {
+    if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
       nsCOMPtr<nsIRunnable> runnable =
         NewRunnableMethod<StorensRefPtrPassByPtr<MediaStream>, TrackID>(
           this, &PlaybackStreamListener::DoNotifyTrackEnded, aInputStream, aInputTrackID);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
     }
   }
 
   void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
@@ -752,27 +752,27 @@ DOMMediaStream::HasTrack(const MediaStre
 
 bool
 DOMMediaStream::OwnsTrack(const MediaStreamTrack& aTrack) const
 {
   return !!FindOwnedTrackPort(aTrack);
 }
 
 bool
-DOMMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
+DOMMediaStream::AddDirectListener(DirectMediaStreamListener* aListener)
 {
   if (GetInputStream() && GetInputStream()->AsSourceStream()) {
     GetInputStream()->AsSourceStream()->AddDirectListener(aListener);
     return true; // application should ignore NotifyQueuedTrackData
   }
   return false;
 }
 
 void
-DOMMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener)
+DOMMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
 {
   if (GetInputStream() && GetInputStream()->AsSourceStream()) {
     GetInputStream()->AsSourceStream()->RemoveDirectListener(aListener);
   }
 }
 
 bool
 DOMMediaStream::IsFinished()
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -25,17 +25,17 @@
 
 namespace mozilla {
 
 class DOMHwMediaStream;
 class DOMLocalMediaStream;
 class DOMMediaStream;
 class MediaStream;
 class MediaInputPort;
-class MediaStreamDirectListener;
+class DirectMediaStreamListener;
 class MediaStreamGraph;
 class ProcessedMediaStream;
 
 enum class BlockingMode;
 
 namespace dom {
 class AudioNode;
 class HTMLCanvasElement;
@@ -434,18 +434,18 @@ public:
    * needs special treatment.
    */
   virtual MediaStream* GetCameraStream() const { return nullptr; }
 
   /**
    * Allows users to get access to media data without going through graph
    * queuing. Returns a bool to let us know if direct data will be delivered.
    */
-  bool AddDirectListener(MediaStreamDirectListener *aListener);
-  void RemoveDirectListener(MediaStreamDirectListener *aListener);
+  bool AddDirectListener(DirectMediaStreamListener *aListener);
+  void RemoveDirectListener(DirectMediaStreamListener *aListener);
 
   virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
   virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
 
   bool IsFinished();
   /**
    * Returns a principal indicating who may access this stream. The stream contents
    * can only be accessed by principals subsuming this principal.
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -4,16 +4,17 @@
  * 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 "MediaManager.h"
 
 #include "MediaStreamGraph.h"
 #include "mozilla/dom/MediaStreamTrack.h"
 #include "GetUserMediaRequest.h"
+#include "MediaStreamListener.h"
 #include "nsContentUtils.h"
 #include "nsHashPropertyBag.h"
 #ifdef MOZ_WIDGET_GONK
 #include "nsIAudioManager.h"
 #endif
 #include "nsIEventTarget.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIScriptGlobalObject.h"
@@ -238,16 +239,263 @@ HostHasPermission(nsIURI &docURI)
     }
 
     begin = end + 1;
   } while (end < domainWhiteList.Length());
 
   return false;
 }
 
+/**
+ * This class is an implementation of MediaStreamListener. This is used
+ * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
+ * are assigned and deassigned in content.
+ */
+class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
+{
+public:
+  // Create in an inactive state
+  GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
+    uint64_t aWindowID,
+    const PrincipalHandle& aPrincipalHandle)
+    : mMediaThread(aThread)
+    , mMainThreadCheck(nullptr)
+    , mWindowID(aWindowID)
+    , mPrincipalHandle(aPrincipalHandle)
+    , mStopped(false)
+    , mFinished(false)
+    , mRemoved(false)
+    , mAudioStopped(false)
+    , mVideoStopped(false) {}
+
+  ~GetUserMediaCallbackMediaStreamListener()
+  {
+    Unused << mMediaThread;
+    // It's OK to release mStream on any thread; they have thread-safe
+    // refcounts.
+  }
+
+  void Activate(already_AddRefed<SourceMediaStream> aStream,
+                AudioDevice* aAudioDevice,
+                VideoDevice* aVideoDevice)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mMainThreadCheck = PR_GetCurrentThread();
+    mStream = aStream;
+    mAudioDevice = aAudioDevice;
+    mVideoDevice = aVideoDevice;
+
+    mStream->AddListener(this);
+  }
+
+  MediaStream *Stream() // Can be used to test if Activate was called
+  {
+    return mStream;
+  }
+  SourceMediaStream *GetSourceStream()
+  {
+    NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
+    if (!mStream) {
+      return nullptr;
+    }
+    return mStream->AsSourceStream();
+  }
+
+  void StopSharing();
+
+  void StopTrack(TrackID aID);
+
+  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
+
+  already_AddRefed<PledgeVoid>
+  ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
+                          TrackID aID,
+                          const dom::MediaTrackConstraints& aConstraints);
+
+  // mVideo/AudioDevice are set by Activate(), so we assume they're capturing
+  // if set and represent a real capture device.
+  bool CapturingVideo()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
+           (!mVideoDevice->GetSource()->IsFake() ||
+            Preferences::GetBool("media.navigator.permission.fake"));
+  }
+  bool CapturingAudio()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mAudioDevice && !mStopped &&
+           !mAudioDevice->GetSource()->IsAvailable() &&
+           (!mAudioDevice->GetSource()->IsFake() ||
+            Preferences::GetBool("media.navigator.permission.fake"));
+  }
+  bool CapturingScreen()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
+  }
+  bool CapturingWindow()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
+  }
+  bool CapturingApplication()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           !mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
+  }
+  bool CapturingBrowser()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mVideoDevice && !mStopped &&
+           mVideoDevice->GetSource()->IsAvailable() &&
+           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
+  }
+
+  // implement in .cpp to avoid circular dependency with MediaOperationTask
+  // Can be invoked from EITHER MainThread or MSG thread
+  void Stop();
+
+  void
+  AudioConfig(bool aEchoOn, uint32_t aEcho,
+              bool aAgcOn, uint32_t aAGC,
+              bool aNoiseOn, uint32_t aNoise,
+              int32_t aPlayoutDelay);
+
+  void
+  Remove()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    // allow calling even if inactive (!mStream) for easier cleanup
+    // Caller holds strong reference to us, so no death grip required
+    if (mStream && !mRemoved) {
+      MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
+      mRemoved = true; // RemoveListener is async, avoid races
+      // If it's destroyed, don't call - listener will be removed and we'll be notified!
+      if (!mStream->IsDestroyed()) {
+        mStream->RemoveListener(this);
+      }
+    }
+  }
+
+  // Proxy NotifyPull() to sources
+  void
+  NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
+  {
+    // Currently audio sources ignore NotifyPull, but they could
+    // watch it especially for fake audio.
+    if (mAudioDevice) {
+      mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
+                                            aDesiredTime, mPrincipalHandle);
+    }
+    if (mVideoDevice) {
+      mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
+                                            aDesiredTime, mPrincipalHandle);
+    }
+  }
+
+  void
+  NotifyEvent(MediaStreamGraph* aGraph,
+              MediaStreamGraphEvent aEvent) override
+  {
+    nsresult rv;
+    nsCOMPtr<nsIThread> thread;
+
+    switch (aEvent) {
+      case MediaStreamGraphEvent::EVENT_FINISHED:
+        rv = NS_GetMainThread(getter_AddRefs(thread));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          NS_ASSERTION(false, "Mainthread not available; running on current thread");
+          // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
+          MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
+          NotifyFinished();
+          return;
+        }
+        thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished),
+                         NS_DISPATCH_NORMAL);
+        break;
+      case MediaStreamGraphEvent::EVENT_REMOVED:
+        rv = NS_GetMainThread(getter_AddRefs(thread));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          NS_ASSERTION(false, "Mainthread not available; running on current thread");
+          // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
+          MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
+          NotifyRemoved();
+          return;
+        }
+        thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved),
+                         NS_DISPATCH_NORMAL);
+        break;
+      case MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS:
+        NotifyDirectListeners(aGraph, true);
+        break;
+      case MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS:
+        NotifyDirectListeners(aGraph, false);
+        break;
+      default:
+        break;
+    }
+  }
+
+  void
+  NotifyFinished();
+
+  void
+  NotifyRemoved();
+
+  void
+  NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
+
+  PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
+
+private:
+  // Set at construction
+  base::Thread* mMediaThread;
+  // never ever indirect off this; just for assertions
+  PRThread* mMainThreadCheck;
+
+  uint64_t mWindowID;
+  const PrincipalHandle mPrincipalHandle;
+
+  // true after this listener has sent MEDIA_STOP. MainThread only.
+  bool mStopped;
+
+  // true after the stream this listener is listening to has finished in the
+  // MediaStreamGraph. MainThread only.
+  bool mFinished;
+
+  // true after this listener has been removed from its MediaStream.
+  // MainThread only.
+  bool mRemoved;
+
+  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
+  // MainThread only.
+  bool mAudioStopped;
+
+  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
+  // MainThread only.
+  bool mVideoStopped;
+
+  // Set at Activate on MainThread
+
+  // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
+  // No locking needed as they're only addrefed except on the MediaManager thread
+  RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
+  RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
+  RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
+};
+
 // Generic class for running long media operations like Start off the main
 // thread, and then (because nsDOMMediaStreams aren't threadsafe),
 // ProxyReleases mStream since it's cycle collected.
 class MediaOperationTask : public Runnable
 {
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
   MediaOperationTask(MediaOperation aType,
@@ -3139,24 +3387,24 @@ GetUserMediaCallbackMediaStreamListener:
                                     dom::AudioChannel::Normal);
     graph->UnregisterCaptureStreamForWindow(mWindowID);
     mStream->Destroy();
   }
 }
 
 // ApplyConstraints for track
 
-already_AddRefed<GetUserMediaCallbackMediaStreamListener::PledgeVoid>
+already_AddRefed<media::Pledge<bool, dom::MediaStreamError*>>
 GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
     nsPIDOMWindowInner* aWindow,
     TrackID aTrackID,
     const MediaTrackConstraints& aConstraints)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  RefPtr<PledgeVoid> p = new PledgeVoid();
+  RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = new media::Pledge<bool, dom::MediaStreamError*>();
 
   // XXX to support multiple tracks of a type in a stream, this should key off
   // the TrackID and not just the type
   RefPtr<AudioDevice> audioDevice =
     aTrackID == kAudioTrack ? mAudioDevice.get() : nullptr;
   RefPtr<VideoDevice> videoDevice =
     aTrackID == kVideoTrack ? mVideoDevice.get() : nullptr;
 
@@ -3199,17 +3447,17 @@ GetUserMediaCallbackMediaStreamListener:
     }
     NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv,
                                              badConstraint]() mutable {
       MOZ_ASSERT(NS_IsMainThread());
       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
       if (!mgr) {
         return NS_OK;
       }
-      RefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
+      RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = mgr->mOutstandingVoidPledges.Remove(id);
       if (p) {
         if (NS_SUCCEEDED(rv)) {
           p->Resolve(false);
         } else {
           auto* window = nsGlobalWindow::GetInnerWindowWithId(windowId);
           if (window) {
             if (rv == NS_ERROR_NOT_AVAILABLE) {
               nsString constraint;
@@ -3315,16 +3563,36 @@ GetUserMediaCallbackMediaStreamListener:
   MM_LOG(("Listener removed by DOM Destroy(), mFinished = %d", (int) mFinished));
   mRemoved = true;
 
   if (!mFinished) {
     NotifyFinished();
   }
 }
 
+GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
+    GetUserMediaCallbackMediaStreamListener* aListener,
+    GetUserMediaStatus aStatus,
+    bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
+: mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
+, mIsVideo(aIsVideo), mWindowID(aWindowID) {}
+
+GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
+    GetUserMediaStatus aStatus,
+    already_AddRefed<DOMMediaStream> aStream,
+    OnTracksAvailableCallback* aOnTracksAvailableCallback,
+    bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
+: mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
+  mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
+  mOnFailure(aError) {}
+GetUserMediaNotificationEvent::~GetUserMediaNotificationEvent()
+{
+}
+
 NS_IMETHODIMP
 GetUserMediaNotificationEvent::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Make sure mStream is cleared and our reference to the DOMMediaStream
   // is dropped on the main thread, no matter what happens in this method.
   // Otherwise this object might be destroyed off the main thread,
   // releasing DOMMediaStream off the main thread, which is not allowed.
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -48,16 +48,20 @@
 
 namespace mozilla {
 namespace dom {
 struct MediaStreamConstraints;
 struct MediaTrackConstraints;
 struct MediaTrackConstraintSet;
 } // namespace dom
 
+class MediaManager;
+class GetUserMediaCallbackMediaStreamListener;
+class GetUserMediaTask;
+
 extern LogModule* GetMediaManagerLog();
 #define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 
 class MediaDevice : public nsIMediaDevice
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEDIADEVICE
@@ -112,289 +116,34 @@ public:
   Source* GetSource();
   nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                     const MediaEnginePrefs &aPrefs,
                     const nsACString& aOrigin);
   nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
                    const MediaEnginePrefs &aPrefs);
 };
 
-/**
- * This class is an implementation of MediaStreamListener. This is used
- * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
- * are assigned and deassigned in content.
- */
-class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
-{
-public:
-  // Create in an inactive state
-  GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
-    uint64_t aWindowID,
-    const PrincipalHandle& aPrincipalHandle)
-    : mMediaThread(aThread)
-    , mMainThreadCheck(nullptr)
-    , mWindowID(aWindowID)
-    , mPrincipalHandle(aPrincipalHandle)
-    , mStopped(false)
-    , mFinished(false)
-    , mRemoved(false)
-    , mAudioStopped(false)
-    , mVideoStopped(false) {}
-
-  ~GetUserMediaCallbackMediaStreamListener()
-  {
-    Unused << mMediaThread;
-    // It's OK to release mStream on any thread; they have thread-safe
-    // refcounts.
-  }
-
-  void Activate(already_AddRefed<SourceMediaStream> aStream,
-                AudioDevice* aAudioDevice,
-                VideoDevice* aVideoDevice)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    mMainThreadCheck = PR_GetCurrentThread();
-    mStream = aStream;
-    mAudioDevice = aAudioDevice;
-    mVideoDevice = aVideoDevice;
-
-    mStream->AddListener(this);
-  }
-
-  MediaStream *Stream() // Can be used to test if Activate was called
-  {
-    return mStream;
-  }
-  SourceMediaStream *GetSourceStream()
-  {
-    NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
-    if (!mStream) {
-      return nullptr;
-    }
-    return mStream->AsSourceStream();
-  }
-
-  void StopSharing();
-
-  void StopTrack(TrackID aID);
-
-  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
-
-  already_AddRefed<PledgeVoid>
-  ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
-                          TrackID aID,
-                          const dom::MediaTrackConstraints& aConstraints);
-
-  // mVideo/AudioDevice are set by Activate(), so we assume they're capturing
-  // if set and represent a real capture device.
-  bool CapturingVideo()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           !mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
-           (!mVideoDevice->GetSource()->IsFake() ||
-            Preferences::GetBool("media.navigator.permission.fake"));
-  }
-  bool CapturingAudio()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mAudioDevice && !mStopped &&
-           !mAudioDevice->GetSource()->IsAvailable() &&
-           (!mAudioDevice->GetSource()->IsFake() ||
-            Preferences::GetBool("media.navigator.permission.fake"));
-  }
-  bool CapturingScreen()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           !mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
-  }
-  bool CapturingWindow()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           !mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
-  }
-  bool CapturingApplication()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           !mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
-  }
-  bool CapturingBrowser()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    return mVideoDevice && !mStopped &&
-           mVideoDevice->GetSource()->IsAvailable() &&
-           mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
-  }
-
-  // implement in .cpp to avoid circular dependency with MediaOperationTask
-  // Can be invoked from EITHER MainThread or MSG thread
-  void Stop();
-
-  void
-  AudioConfig(bool aEchoOn, uint32_t aEcho,
-              bool aAgcOn, uint32_t aAGC,
-              bool aNoiseOn, uint32_t aNoise,
-              int32_t aPlayoutDelay);
-
-  void
-  Remove()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    // allow calling even if inactive (!mStream) for easier cleanup
-    // Caller holds strong reference to us, so no death grip required
-    if (mStream && !mRemoved) {
-      MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
-      mRemoved = true; // RemoveListener is async, avoid races
-      // If it's destroyed, don't call - listener will be removed and we'll be notified!
-      if (!mStream->IsDestroyed()) {
-        mStream->RemoveListener(this);
-      }
-    }
-  }
-
-  // Proxy NotifyPull() to sources
-  void
-  NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
-  {
-    // Currently audio sources ignore NotifyPull, but they could
-    // watch it especially for fake audio.
-    if (mAudioDevice) {
-      mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
-                                            aDesiredTime, mPrincipalHandle);
-    }
-    if (mVideoDevice) {
-      mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
-                                            aDesiredTime, mPrincipalHandle);
-    }
-  }
-
-  void
-  NotifyEvent(MediaStreamGraph* aGraph,
-              MediaStreamListener::MediaStreamGraphEvent aEvent) override
-  {
-    nsresult rv;
-    nsCOMPtr<nsIThread> thread;
-
-    switch (aEvent) {
-      case EVENT_FINISHED:
-        rv = NS_GetMainThread(getter_AddRefs(thread));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          NS_ASSERTION(false, "Mainthread not available; running on current thread");
-          // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
-          MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
-          NotifyFinished();
-          return;
-        }
-        thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished),
-                         NS_DISPATCH_NORMAL);
-        break;
-      case EVENT_REMOVED:
-        rv = NS_GetMainThread(getter_AddRefs(thread));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          NS_ASSERTION(false, "Mainthread not available; running on current thread");
-          // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
-          MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
-          NotifyRemoved();
-          return;
-        }
-        thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved),
-                         NS_DISPATCH_NORMAL);
-        break;
-      case EVENT_HAS_DIRECT_LISTENERS:
-        NotifyDirectListeners(aGraph, true);
-        break;
-      case EVENT_HAS_NO_DIRECT_LISTENERS:
-        NotifyDirectListeners(aGraph, false);
-        break;
-      default:
-        break;
-    }
-  }
-
-  void
-  NotifyFinished();
-
-  void
-  NotifyRemoved();
-
-  void
-  NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
-
-  PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
-
-private:
-  // Set at construction
-  base::Thread* mMediaThread;
-  // never ever indirect off this; just for assertions
-  PRThread* mMainThreadCheck;
-
-  uint64_t mWindowID;
-  const PrincipalHandle mPrincipalHandle;
-
-  // true after this listener has sent MEDIA_STOP. MainThread only.
-  bool mStopped;
-
-  // true after the stream this listener is listening to has finished in the
-  // MediaStreamGraph. MainThread only.
-  bool mFinished;
-
-  // true after this listener has been removed from its MediaStream.
-  // MainThread only.
-  bool mRemoved;
-
-  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
-  // MainThread only.
-  bool mAudioStopped;
-
-  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
-  // MainThread only.
-  bool mVideoStopped;
-
-  // Set at Activate on MainThread
-
-  // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
-  // No locking needed as they're only addrefed except on the MediaManager thread
-  RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
-  RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
-  RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
-};
-
 class GetUserMediaNotificationEvent: public Runnable
 {
   public:
     enum GetUserMediaStatus {
       STARTING,
       STOPPING,
       STOPPED_TRACK,
     };
     GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
                                   GetUserMediaStatus aStatus,
-                                  bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
-    : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
-    , mIsVideo(aIsVideo), mWindowID(aWindowID) {}
+                                  bool aIsAudio, bool aIsVideo, uint64_t aWindowID);
 
     GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
                                   already_AddRefed<DOMMediaStream> aStream,
                                   OnTracksAvailableCallback* aOnTracksAvailableCallback,
                                   bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
-                                  already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
-    : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
-      mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
-      mOnFailure(aError) {}
-    virtual ~GetUserMediaNotificationEvent()
-    {
-
-    }
+                                  already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError);
+    virtual ~GetUserMediaNotificationEvent();
 
     NS_IMETHOD Run() override;
 
   protected:
     RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
     RefPtr<DOMMediaStream> mStream;
     nsAutoPtr<OnTracksAvailableCallback> mOnTracksAvailableCallback;
     GetUserMediaStatus mStatus;
@@ -406,19 +155,16 @@ class GetUserMediaNotificationEvent: pub
 
 typedef enum {
   MEDIA_START,
   MEDIA_STOP,
   MEDIA_STOP_TRACK,
   MEDIA_DIRECT_LISTENERS,
 } MediaOperation;
 
-class MediaManager;
-class GetUserMediaTask;
-
 class ReleaseMediaOperationResource : public Runnable
 {
 public:
   ReleaseMediaOperationResource(already_AddRefed<DOMMediaStream> aStream,
     OnTracksAvailableCallback* aOnTracksAvailableCallback):
     mStream(aStream),
     mOnTracksAvailableCallback(aOnTracksAvailableCallback) {}
   NS_IMETHOD Run() override {return NS_OK;}
@@ -570,17 +316,17 @@ private:
 
   // ONLY accessed from MediaManagerThread
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
   media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
   media::CoatCheck<PledgeChar> mOutstandingCharPledges;
-  media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
+  media::CoatCheck<media::Pledge<bool, dom::MediaStreamError*>> mOutstandingVoidPledges;
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   RefPtr<nsDOMCameraManager> mCameraManager;
 #endif
 public:
   media::CoatCheck<media::Pledge<nsCString>> mGetOriginKeyPledges;
   UniquePtr<media::Parent<media::NonE10s>> mNonE10sParent;
 };
 
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/Logging.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioCaptureStream.h"
 #include "AudioChannelService.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
+#include "MediaStreamListener.h"
 #include "mozilla/dom/AudioContextBinding.h"
 #include "mozilla/media/MediaUtils.h"
 #include <algorithm>
 #include "GeckoProfiler.h"
 #include "mozilla/unused.h"
 #include "mozilla/media/MediaUtils.h"
 #ifdef MOZ_WEBRTC
 #include "AudioOutputObserver.h"
@@ -55,16 +56,22 @@ LazyLogModule gMediaStreamGraphLog("Medi
 #    define LIFECYCLE_LOG(...)  __android_log_print(ANDROID_LOG_INFO, "Gecko - MSG", ## __VA_ARGS__); printf(__VA_ARGS__);printf("\n");
 #  else
 #    define LIFECYCLE_LOG(...) printf(__VA_ARGS__);printf("\n");
 #  endif
 #else
 #  define LIFECYCLE_LOG(...)
 #endif
 
+enum SourceMediaStream::TrackCommands : uint32_t {
+  TRACK_CREATE = TrackEventCommand::TRACK_EVENT_CREATED,
+  TRACK_END = TrackEventCommand::TRACK_EVENT_ENDED,
+  TRACK_UNUSED = TrackEventCommand::TRACK_EVENT_UNUSED,
+};
+
 /**
  * A hash table containing the graph instances, one per AudioChannel.
  */
 static nsDataHashtable<nsUint32HashKey, MediaStreamGraphImpl*> gGraphs;
 
 MediaStreamGraphImpl::~MediaStreamGraphImpl()
 {
   NS_ASSERTION(IsEmpty(),
@@ -196,17 +203,17 @@ MediaStreamGraphImpl::ExtractPendingInpu
         if (data->mCommands) {
           MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
           for (MediaStreamListener* l : aStream->mListeners) {
             if (data->mCommands & SourceMediaStream::TRACK_END) {
               l->NotifyQueuedAudioData(this, data->mID,
                                        offset, *(static_cast<AudioSegment*>(data->mData.get())));
             }
             l->NotifyQueuedTrackChanges(this, data->mID,
-                                        offset, data->mCommands, *data->mData);
+                                        offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
             if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
               l->NotifyQueuedAudioData(this, data->mID,
                                        offset, *(static_cast<AudioSegment*>(data->mData.get())));
             }
           }
         } else {
           for (MediaStreamListener* l : aStream->mListeners) {
               l->NotifyQueuedAudioData(this, data->mID,
@@ -216,27 +223,27 @@ MediaStreamGraphImpl::ExtractPendingInpu
       }
 
       // Video case.
       if (data->mData->GetType() == MediaSegment::VIDEO) {
         if (data->mCommands) {
           MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
           for (MediaStreamListener* l : aStream->mListeners) {
             l->NotifyQueuedTrackChanges(this, data->mID,
-                                        offset, data->mCommands, *data->mData);
+                                        offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
           }
         } else {
           // Fixme: This part will be removed in the bug 1201363. It will be
           // removed in changeset "Do not copy video segment to StreamTracks in
           // TrackUnionStream."
 
           // Dealing with video and not TRACK_CREATE and TRACK_END case.
           for (MediaStreamListener* l : aStream->mListeners) {
             l->NotifyQueuedTrackChanges(this, data->mID,
-                                        offset, data->mCommands, *data->mData);
+                                        offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
           }
         }
       }
 
       for (TrackBound<MediaStreamTrackListener>& b : aStream->mTrackListeners) {
         if (b.mTrackID != data->mID) {
           continue;
         }
@@ -351,17 +358,17 @@ MediaStreamGraphImpl::UpdateCurrentTimeF
     // out.
     if (stream->mFinished && !stream->mNotifiedFinished &&
         mProcessedTime >=
           stream->StreamTimeToGraphTime(stream->GetStreamTracks().GetAllTracksEnd())) {
       stream->mNotifiedFinished = true;
       SetStreamOrderDirty();
       for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
         MediaStreamListener* l = stream->mListeners[j];
-        l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
+        l->NotifyEvent(this, MediaStreamGraphEvent::EVENT_FINISHED);
       }
     }
   }
 }
 
 template<typename C, typename Chunk>
 void
 MediaStreamGraphImpl::ProcessChunkMetadataForInterval(MediaStream* aStream,
@@ -1996,16 +2003,24 @@ MediaStream::MediaStream()
   , mMainThreadDestroyed(false)
   , mNrOfMainThreadUsers(0)
   , mGraph(nullptr)
   , mAudioChannelType(dom::AudioChannel::Normal)
 {
   MOZ_COUNT_CTOR(MediaStream);
 }
 
+MediaStream::~MediaStream()
+{
+  MOZ_COUNT_DTOR(MediaStream);
+  NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
+  NS_ASSERTION(mMainThreadListeners.IsEmpty(),
+               "All main thread listeners should have been removed");
+}
+
 size_t
 MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = 0;
 
   // Not owned:
   // - mGraph - Not reported here
   // - mConsumers - elements
@@ -2100,33 +2115,33 @@ StreamTracks::Track*
 MediaStream::EnsureTrack(TrackID aTrackId)
 {
   StreamTracks::Track* track = mTracks.FindTrack(aTrackId);
   if (!track) {
     nsAutoPtr<MediaSegment> segment(new AudioSegment());
     for (uint32_t j = 0; j < mListeners.Length(); ++j) {
       MediaStreamListener* l = mListeners[j];
       l->NotifyQueuedTrackChanges(Graph(), aTrackId, 0,
-                                  MediaStreamListener::TRACK_EVENT_CREATED,
+                                  TrackEventCommand::TRACK_EVENT_CREATED,
                                   *segment);
       // TODO If we ever need to ensure several tracks at once, we will have to
       // change this.
       l->NotifyFinishedTrackCreation(Graph());
     }
     track = &mTracks.AddTrack(aTrackId, 0, segment.forget());
   }
   return track;
 }
 
 void
 MediaStream::RemoveAllListenersImpl()
 {
   for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
     RefPtr<MediaStreamListener> listener = mListeners[i].forget();
-    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
+    listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
   }
   mListeners.Clear();
 }
 
 void
 MediaStream::DestroyImpl()
 {
   for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
@@ -2376,27 +2391,27 @@ MediaStream::AddListenerImpl(already_Add
       // TrackUnionStream guarantees that each of its tracks has an input track.
       // Other types do not implement GetInputStreamFor() and will return null.
       inputStream = ps->GetInputStreamFor(it->GetID());
       MOZ_ASSERT(inputStream);
       inputTrackID = ps->GetInputTrackIDFor(it->GetID());
       MOZ_ASSERT(IsTrackIDExplicit(inputTrackID));
     }
 
-    uint32_t flags = MediaStreamListener::TRACK_EVENT_CREATED;
+    uint32_t flags = TrackEventCommand::TRACK_EVENT_CREATED;
     if (it->IsEnded()) {
-      flags |= MediaStreamListener::TRACK_EVENT_ENDED;
+      flags |= TrackEventCommand::TRACK_EVENT_ENDED;
     }
     nsAutoPtr<MediaSegment> segment(it->GetSegment()->CreateEmptyClone());
     listener->NotifyQueuedTrackChanges(Graph(), it->GetID(), it->GetEnd(),
-                                       flags, *segment,
+                                       static_cast<TrackEventCommand>(flags), *segment,
                                        inputStream, inputTrackID);
   }
   if (mNotifiedFinished) {
-    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
+    listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_FINISHED);
   }
   if (mNotifiedHasCurrentData) {
     listener->NotifyHasCurrentData(GraphImpl());
   }
 }
 
 void
 MediaStream::AddListener(MediaStreamListener* aListener)
@@ -2415,17 +2430,17 @@ MediaStream::AddListener(MediaStreamList
 }
 
 void
 MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
 {
   // wouldn't need this if we could do it in the opposite order
   RefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
+  listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
 }
 
 void
 MediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
@@ -2508,66 +2523,66 @@ MediaStream::RemoveTrackListener(MediaSt
     }
     RefPtr<MediaStreamTrackListener> mListener;
     TrackID mTrackID;
   };
   GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
 }
 
 void
-MediaStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
+MediaStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
                                         TrackID aTrackID)
 {
   // Base implementation, for streams that don't support direct track listeners.
-  RefPtr<MediaStreamTrackDirectListener> listener = aListener;
+  RefPtr<DirectMediaStreamTrackListener> listener = aListener;
   listener->NotifyDirectListenerInstalled(
-    MediaStreamTrackDirectListener::InstallationResult::STREAM_NOT_SUPPORTED);
+    DirectMediaStreamTrackListener::InstallationResult::STREAM_NOT_SUPPORTED);
 }
 
 void
-MediaStream::AddDirectTrackListener(MediaStreamTrackDirectListener* aListener,
+MediaStream::AddDirectTrackListener(DirectMediaStreamTrackListener* aListener,
                                     TrackID aTrackID)
 {
   class Message : public ControlMessage {
   public:
-    Message(MediaStream* aStream, MediaStreamTrackDirectListener* aListener,
+    Message(MediaStream* aStream, DirectMediaStreamTrackListener* aListener,
             TrackID aTrackID) :
       ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
     virtual void Run()
     {
       mStream->AddDirectTrackListenerImpl(mListener.forget(), mTrackID);
     }
-    RefPtr<MediaStreamTrackDirectListener> mListener;
+    RefPtr<DirectMediaStreamTrackListener> mListener;
     TrackID mTrackID;
   };
   GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
 }
 
 void
-MediaStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
+MediaStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
                                            TrackID aTrackID)
 {
   // Base implementation, the listener was never added so nothing to do.
-  RefPtr<MediaStreamTrackDirectListener> listener = aListener;
+  RefPtr<DirectMediaStreamTrackListener> listener = aListener;
 }
 
 void
-MediaStream::RemoveDirectTrackListener(MediaStreamTrackDirectListener* aListener,
+MediaStream::RemoveDirectTrackListener(DirectMediaStreamTrackListener* aListener,
                                        TrackID aTrackID)
 {
   class Message : public ControlMessage {
   public:
-    Message(MediaStream* aStream, MediaStreamTrackDirectListener* aListener,
+    Message(MediaStream* aStream, DirectMediaStreamTrackListener* aListener,
             TrackID aTrackID) :
       ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
     virtual void Run()
     {
       mStream->RemoveDirectTrackListenerImpl(mListener, mTrackID);
     }
-    RefPtr<MediaStreamTrackDirectListener> mListener;
+    RefPtr<DirectMediaStreamTrackListener> mListener;
     TrackID mTrackID;
   };
   GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
 }
 
 void
 MediaStream::RunAfterPendingUpdates(already_AddRefed<nsIRunnable> aRunnable)
 {
@@ -2681,16 +2696,26 @@ MediaStream::AddMainThreadListener(MainT
 
     RefPtr<MediaStream> mStream;
   };
 
   nsCOMPtr<nsIRunnable> runnable = new NotifyRunnable(this);
   NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())));
 }
 
+SourceMediaStream::SourceMediaStream() :
+  MediaStream(),
+  mMutex("mozilla::media::SourceMediaStream"),
+  mUpdateKnownTracksTime(0),
+  mPullEnabled(false),
+  mUpdateFinished(false),
+  mNeedsMixing(false)
+{
+}
+
 nsresult
 SourceMediaStream::OpenAudioInput(int aID,
                                   AudioDataListener *aListener)
 {
   if (GraphImpl()) {
     mInputListener = aListener;
     return GraphImpl()->OpenAudioInput(aID, aListener);
   }
@@ -2746,16 +2771,23 @@ SourceMediaStream::AddTrackInternal(Trac
   data->mData = aSegment;
   ResampleAudioToGraphSampleRate(data, aSegment);
   if (!(aFlags & ADDTRACK_QUEUED) && GraphImpl()) {
     GraphImpl()->EnsureNextIteration();
   }
 }
 
 void
+SourceMediaStream::AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
+                                 AudioSegment* aSegment, uint32_t aFlags)
+{
+  AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
+}
+
+void
 SourceMediaStream::FinishAddTracks()
 {
   MutexAutoLock lock(mMutex);
   mUpdateTracks.AppendElements(Move(mPendingTracks));
   LIFECYCLE_LOG("FinishAddTracks: %lu/%lu", mPendingTracks.Length(), mUpdateTracks.Length());
   if (GraphImpl()) {
     GraphImpl()->EnsureNextIteration();
   }
@@ -2825,139 +2857,144 @@ SourceMediaStream::AppendToTrack(TrackID
 void
 SourceMediaStream::NotifyDirectConsumers(TrackData *aTrack,
                                          MediaSegment *aSegment)
 {
   mMutex.AssertCurrentThreadOwns();
   MOZ_ASSERT(aTrack);
 
   for (uint32_t j = 0; j < mDirectListeners.Length(); ++j) {
-    MediaStreamDirectListener* l = mDirectListeners[j];
+    DirectMediaStreamListener* l = mDirectListeners[j];
     StreamTime offset = 0; // FIX! need a separate StreamTime.... or the end of the internal buffer
     l->NotifyRealtimeData(static_cast<MediaStreamGraph*>(GraphImpl()), aTrack->mID,
                           offset, aTrack->mCommands, *aSegment);
   }
 
-  for (const TrackBound<MediaStreamTrackDirectListener>& source
+  for (const TrackBound<DirectMediaStreamTrackListener>& source
          : mDirectTrackListeners) {
     if (aTrack->mID != source.mTrackID) {
       continue;
     }
     StreamTime offset = 0; // FIX! need a separate StreamTime.... or the end of the internal buffer
     source.mListener->NotifyRealtimeTrackDataAndApplyTrackDisabling(Graph(), offset, *aSegment);
   }
 }
 
 // These handle notifying all the listeners of an event
 void
-SourceMediaStream::NotifyListenersEventImpl(MediaStreamListener::MediaStreamGraphEvent aEvent)
+SourceMediaStream::NotifyListenersEventImpl(MediaStreamGraphEvent aEvent)
 {
   for (uint32_t j = 0; j < mListeners.Length(); ++j) {
     MediaStreamListener* l = mListeners[j];
     l->NotifyEvent(GraphImpl(), aEvent);
   }
 }
 
 void
-SourceMediaStream::NotifyListenersEvent(MediaStreamListener::MediaStreamGraphEvent aNewEvent)
+SourceMediaStream::NotifyListenersEvent(MediaStreamGraphEvent aNewEvent)
 {
   class Message : public ControlMessage {
   public:
-    Message(SourceMediaStream* aStream, MediaStreamListener::MediaStreamGraphEvent aEvent) :
+    Message(SourceMediaStream* aStream, MediaStreamGraphEvent aEvent) :
       ControlMessage(aStream), mEvent(aEvent) {}
     void Run() override
       {
         mStream->AsSourceStream()->NotifyListenersEventImpl(mEvent);
       }
-    MediaStreamListener::MediaStreamGraphEvent mEvent;
+    MediaStreamGraphEvent mEvent;
   };
   GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNewEvent));
 }
 
 void
-SourceMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
+SourceMediaStream::AddDirectListener(DirectMediaStreamListener* aListener)
 {
   bool wasEmpty;
   {
     MutexAutoLock lock(mMutex);
     wasEmpty = mDirectListeners.IsEmpty();
     mDirectListeners.AppendElement(aListener);
   }
 
   if (wasEmpty) {
     // Async
-    NotifyListenersEvent(MediaStreamListener::EVENT_HAS_DIRECT_LISTENERS);
+    NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS);
   }
 }
 
 void
-SourceMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener)
+SourceMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
 {
   bool isEmpty;
   {
     MutexAutoLock lock(mMutex);
     mDirectListeners.RemoveElement(aListener);
     isEmpty = mDirectListeners.IsEmpty();
   }
 
   if (isEmpty) {
     // Async
-    NotifyListenersEvent(MediaStreamListener::EVENT_HAS_NO_DIRECT_LISTENERS);
+    NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS);
   }
 }
 
 void
-SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
+SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
                                               TrackID aTrackID)
 {
   MOZ_ASSERT(IsTrackIDExplicit(aTrackID));
   TrackData* data;
   bool found;
   bool isAudio;
-  RefPtr<MediaStreamTrackDirectListener> listener = aListener;
+  bool isVideo;
+  RefPtr<DirectMediaStreamTrackListener> listener = aListener;
   STREAM_LOG(LogLevel::Debug, ("Adding direct track listener %p bound to track %d to source stream %p",
              listener.get(), aTrackID, this));
+
   {
     MutexAutoLock lock(mMutex);
     data = FindDataForTrack(aTrackID);
     found = !!data;
-    isAudio = found && data->mData->GetType() == MediaSegment::AUDIO;
-    if (found && isAudio) {
-      TrackBound<MediaStreamTrackDirectListener>* sourceListener =
+    if (found) {
+      isAudio = data->mData->GetType() == MediaSegment::AUDIO;
+      isVideo = data->mData->GetType() == MediaSegment::VIDEO;
+    }
+    if (found && (isAudio || isVideo)) {
+      TrackBound<DirectMediaStreamTrackListener>* sourceListener =
         mDirectTrackListeners.AppendElement();
       sourceListener->mListener = listener;
       sourceListener->mTrackID = aTrackID;
     }
   }
   if (!found) {
     STREAM_LOG(LogLevel::Warning, ("Couldn't find source track for direct track listener %p",
                                    listener.get()));
     listener->NotifyDirectListenerInstalled(
-      MediaStreamTrackDirectListener::InstallationResult::TRACK_NOT_FOUND_AT_SOURCE);
+      DirectMediaStreamTrackListener::InstallationResult::TRACK_NOT_FOUND_AT_SOURCE);
     return;
   }
-  if (!isAudio) {
-    STREAM_LOG(LogLevel::Warning, ("Source track for direct track listener %p is not audio",
+  if (!isAudio && !isVideo) {
+    STREAM_LOG(LogLevel::Warning, ("Source track for direct track listener %p is unknown",
                                    listener.get()));
-    listener->NotifyDirectListenerInstalled(
-      MediaStreamTrackDirectListener::InstallationResult::TRACK_TYPE_NOT_SUPPORTED);
+    // It is not a video or audio track.
+    MOZ_ASSERT(true);
     return;
   }
   STREAM_LOG(LogLevel::Debug, ("Added direct track listener %p", listener.get()));
   listener->NotifyDirectListenerInstalled(
-    MediaStreamTrackDirectListener::InstallationResult::SUCCESS);
+    DirectMediaStreamTrackListener::InstallationResult::SUCCESS);
 }
 
 void
-SourceMediaStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
+SourceMediaStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
                                                  TrackID aTrackID)
 {
   MutexAutoLock lock(mMutex);
   for (int32_t i = mDirectTrackListeners.Length() - 1; i >= 0; --i) {
-    const TrackBound<MediaStreamTrackDirectListener>& source =
+    const TrackBound<DirectMediaStreamTrackListener>& source =
       mDirectTrackListeners[i];
     if (source.mListener == aListener && source.mTrackID == aTrackID) {
       aListener->NotifyDirectListenerUninstalled();
       mDirectTrackListeners.RemoveElementAt(i);
     }
   }
 }
 
@@ -2974,17 +3011,17 @@ SourceMediaStream::GetEndOfAppendedData(
 }
 
 void
 SourceMediaStream::EndTrack(TrackID aID)
 {
   MutexAutoLock lock(mMutex);
   TrackData *track = FindDataForTrack(aID);
   if (track) {
-    track->mCommands |= TRACK_END;
+    track->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
   }
   if (auto graph = GraphImpl()) {
     graph->EnsureNextIteration();
   }
 }
 
 void
 SourceMediaStream::AdvanceKnownTracksTime(StreamTime aKnownTime)
@@ -3007,17 +3044,17 @@ SourceMediaStream::FinishWithLockHeld()
   }
 }
 
 void
 SourceMediaStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled)
 {
   {
     MutexAutoLock lock(mMutex);
-    for (TrackBound<MediaStreamTrackDirectListener>& l: mDirectTrackListeners) {
+    for (TrackBound<DirectMediaStreamTrackListener>& l: mDirectTrackListeners) {
       if (l.mTrackID == aTrackID) {
         bool oldEnabled = !mDisabledTrackIDs.Contains(aTrackID);
         if (!oldEnabled && aEnabled) {
           STREAM_LOG(LogLevel::Debug, ("SourceMediaStream %p track %d setting "
                                        "direct listener enabled",
                                        this, aTrackID));
           l.mListener->DecreaseDisabled();
         } else if (oldEnabled && !aEnabled) {
@@ -3033,23 +3070,27 @@ SourceMediaStream::SetTrackEnabledImpl(T
 }
 
 void
 SourceMediaStream::EndAllTrackAndFinish()
 {
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
     SourceMediaStream::TrackData* data = &mUpdateTracks[i];
-    data->mCommands |= TRACK_END;
+    data->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
   }
   mPendingTracks.Clear();
   FinishWithLockHeld();
   // we will call NotifyEvent() to let GetUserMedia know
 }
 
+SourceMediaStream::~SourceMediaStream()
+{
+}
+
 void
 SourceMediaStream::RegisterForAudioMixing()
 {
   MutexAutoLock lock(mMutex);
   mNeedsMixing = true;
 }
 
 bool
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -7,24 +7,23 @@
 #define MOZILLA_MEDIASTREAMGRAPH_H_
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TaskQueue.h"
 
 #include "mozilla/dom/AudioChannelBinding.h"
 
-#include "AudioSegment.h"
 #include "AudioStream.h"
 #include "nsTArray.h"
 #include "nsIRunnable.h"
-#include "StreamTracks.h"
 #include "VideoFrameContainer.h"
 #include "VideoSegment.h"
 #include "MainThreadUtils.h"
+#include "StreamTracks.h"
 #include "nsAutoPtr.h"
 #include "nsAutoRef.h"
 #include <speex/speex_resampler.h>
 
 class nsIRunnable;
 
 template <>
 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
@@ -84,135 +83,16 @@ class AudioNodeStream;
 class CameraPreviewMediaStream;
 class MediaInputPort;
 class MediaStream;
 class MediaStreamGraph;
 class MediaStreamGraphImpl;
 class ProcessedMediaStream;
 class SourceMediaStream;
 
-/**
- * This is a base class for media graph thread listener callbacks.
- * Override methods to be notified of audio or video data or changes in stream
- * state.
- *
- * This can be used by stream recorders or network connections that receive
- * stream input. It could also be used for debugging.
- *
- * All notification methods are called from the media graph thread. Overriders
- * of these methods are responsible for all synchronization. Beware!
- * These methods are called without the media graph monitor held, so
- * reentry into media graph methods is possible, although very much discouraged!
- * You should do something non-blocking and non-reentrant (e.g. dispatch an
- * event to some thread) and return.
- * The listener is not allowed to add/remove any listeners from the stream.
- *
- * When a listener is first attached, we guarantee to send a NotifyBlockingChanged
- * callback to notify of the initial blocking state. Also, if a listener is
- * attached to a stream that has already finished, we'll call NotifyFinished.
- */
-class MediaStreamListener {
-protected:
-  // Protected destructor, to discourage deletion outside of Release():
-  virtual ~MediaStreamListener() {}
-
-public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
-
-  /**
-   * When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
-   * control loop is ready to pull, this gets called. A NotifyPull implementation
-   * is allowed to call the SourceMediaStream methods that alter track
-   * data. It is not allowed to make other MediaStream API calls, including
-   * calls to add or remove MediaStreamListeners. It is not allowed to block
-   * for any length of time.
-   * aDesiredTime is the stream time we would like to get data up to. Data
-   * beyond this point will not be played until NotifyPull runs again, so there's
-   * not much point in providing it. Note that if the stream is blocked for
-   * some reason, then data before aDesiredTime may not be played immediately.
-   */
-  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
-
-  enum Blocking {
-    BLOCKED,
-    UNBLOCKED
-  };
-  /**
-   * Notify that the blocking status of the stream changed. The initial state
-   * is assumed to be BLOCKED.
-   */
-  virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
-
-  /**
-   * Notify that the stream has data in each track
-   * for the stream's current time. Once this state becomes true, it will
-   * always be true since we block stream time from progressing to times where
-   * there isn't data in each track.
-   */
-  virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
-
-  /**
-   * Notify that the stream output is advancing. aCurrentTime is the graph's
-   * current time. MediaStream::GraphTimeToStreamTime can be used to get the
-   * stream time.
-   */
-  virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
-
-  enum MediaStreamGraphEvent {
-    EVENT_FINISHED,
-    EVENT_REMOVED,
-    EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
-    EVENT_HAS_NO_DIRECT_LISTENERS,  // transition to no direct listeners
-  };
-
-  /**
-   * Notify that an event has occurred on the Stream
-   */
-  virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
-
-  // maskable flags, not a simple enumerated value
-  enum {
-    TRACK_EVENT_CREATED = 0x01,
-    TRACK_EVENT_ENDED = 0x02,
-    TRACK_EVENT_UNUSED = ~(TRACK_EVENT_ENDED | TRACK_EVENT_CREATED),
-  };
-  /**
-   * Notify that changes to one of the stream tracks have been queued.
-   * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
-   * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
-   * at aTrackOffset (relative to the start of the stream).
-   * aInputStream and aInputTrackID will be set if the changes originated
-   * from an input stream's track. In practice they will only be used for
-   * ProcessedMediaStreams.
-   */
-  virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
-                                        StreamTime aTrackOffset,
-                                        uint32_t aTrackEvents,
-                                        const MediaSegment& aQueuedMedia,
-                                        MediaStream* aInputStream = nullptr,
-                                        TrackID aInputTrackID = TRACK_INVALID) {}
-
-  /**
-   * Notify queued audio data. Only audio data need to be queued. The video data
-   * will be notified by MediaStreamVideoSink::SetCurrentFrame.
-   */
-  virtual void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
-                                     StreamTime aTrackOffset,
-                                     const AudioSegment& aQueuedMedia,
-                                     MediaStream* aInputStream = nullptr,
-                                     TrackID aInputTrackID = TRACK_INVALID) {}
-
-  /**
-   * Notify that all new tracks this iteration have been created.
-   * This is to ensure that tracks added atomically to MediaStreamGraph
-   * are also notified of atomically to MediaStreamListeners.
-   */
-  virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) {}
-};
-
 class AudioDataListenerInterface {
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~AudioDataListenerInterface() {}
 
 public:
   /* These are for cubeb audio input & output streams: */
   /**
@@ -242,187 +122,16 @@ protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~AudioDataListener() {}
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioDataListener)
 };
 
 /**
- * This is a base class for media graph thread listener callbacks locked to
- * specific tracks. Override methods to be notified of audio or video data or
- * changes in track state.
- *
- * All notification methods are called from the media graph thread. Overriders
- * of these methods are responsible for all synchronization. Beware!
- * These methods are called without the media graph monitor held, so
- * reentry into media graph methods is possible, although very much discouraged!
- * You should do something non-blocking and non-reentrant (e.g. dispatch an
- * event to some thread) and return.
- * The listener is not allowed to add/remove any listeners from the parent
- * stream.
- *
- * If a listener is attached to a track that has already ended, we guarantee
- * to call NotifyEnded.
- */
-class MediaStreamTrackListener
-{
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener)
-
-public:
-  virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph,
-                                   StreamTime aTrackOffset,
-                                   const MediaSegment& aQueuedMedia) {}
-
-  virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
-                                            const PrincipalHandle& aNewPrincipalHandle) {}
-
-  virtual void NotifyEnded() {}
-
-  virtual void NotifyRemoved() {}
-
-protected:
-  virtual ~MediaStreamTrackListener() {}
-};
-
-
-/**
- * This is a base class for media graph thread listener direct callbacks
- * from within AppendToTrack(). Note that your regular listener will
- * still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
- * you must be careful to ignore them if AddDirectListener was successful.
- */
-class MediaStreamDirectListener : public MediaStreamListener
-{
-public:
-  virtual ~MediaStreamDirectListener() {}
-
-  /*
-   * This will be called on any MediaStreamDirectListener added to a
-   * a SourceMediaStream when AppendToTrack() is called.  The MediaSegment
-   * will be the RawSegment (unresampled) if available in AppendToTrack().
-   * Note that NotifyQueuedTrackChanges() calls will also still occur.
-   */
-  virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
-                                  StreamTime aTrackOffset,
-                                  uint32_t aTrackEvents,
-                                  const MediaSegment& aMedia) {}
-};
-
-/**
- * This is a base class for media graph thread listener direct callbacks from
- * within AppendToTrack(). It is bound to a certain track and can only be
- * installed on audio tracks. Once added to a track on any stream in the graph,
- * the graph will try to install it at that track's source of media data.
- *
- * This works for TrackUnionStreams, which will forward the listener to the
- * track's input track if it exists, or wait for it to be created before
- * forwarding if it doesn't.
- * Once it reaches a SourceMediaStream, it can be successfully installed.
- * Other types of streams will fail installation since they are not supported.
- *
- * Note that this listener and others for the same track will still get
- * NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful
- * to ignore them if this listener was successfully installed.
- */
-class MediaStreamTrackDirectListener : public MediaStreamTrackListener
-{
-  friend class SourceMediaStream;
-  friend class TrackUnionStream;
-
-public:
-  /*
-   * This will be called on any MediaStreamTrackDirectListener added to a
-   * SourceMediaStream when AppendToTrack() is called for the listener's bound
-   * track, using the thread of the AppendToTrack() caller. The MediaSegment
-   * will be the RawSegment (unresampled) if available in AppendToTrack().
-   * If the track is enabled at the source but has been disabled in one of the
-   * streams in between the source and where it was originally added, aMedia
-   * will be a disabled version of the one passed to AppendToTrack() as well.
-   * Note that NotifyQueuedTrackChanges() calls will also still occur.
-   */
-  virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
-                                       StreamTime aTrackOffset,
-                                       const MediaSegment& aMedia) {}
-
-  /**
-   * When a direct listener is processed for installation by the
-   * MediaStreamGraph it will be notified with whether the installation was
-   * successful or not. The results of this installation are the following:
-   * TRACK_NOT_FOUND_AT_SOURCE
-   *    We found the source stream of media data for this track, but the track
-   *    didn't exist. This should only happen if you try to install the listener
-   *    directly to a SourceMediaStream that doesn't contain the given TrackID.
-   * TRACK_TYPE_NOT_SUPPORTED
-   *    This is the failure when you install the listener to a non-audio track.
-   * STREAM_NOT_SUPPORTED
-   *    While looking for the data source of this track, we found a MediaStream
-   *    that is not a SourceMediaStream or a TrackUnionStream.
-   * SUCCESS
-   *    Installation was successful and this listener will start receiving
-   *    NotifyRealtimeData on the next AppendToTrack().
-   */
-  enum class InstallationResult {
-    TRACK_NOT_FOUND_AT_SOURCE,
-    TRACK_TYPE_NOT_SUPPORTED,
-    STREAM_NOT_SUPPORTED,
-    SUCCESS
-  };
-  virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {}
-  virtual void NotifyDirectListenerUninstalled() {}
-
-protected:
-  virtual ~MediaStreamTrackDirectListener() {}
-
-  void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo)
-  {
-    aTo.Clear();
-    aTo.AppendNullData(aFrom.GetDuration());
-  }
-
-  void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
-                                                     StreamTime aTrackOffset,
-                                                     MediaSegment& aMedia)
-  {
-    if (mDisabledCount == 0) {
-      NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia);
-      return;
-    }
-
-    if (!mMedia) {
-      mMedia = aMedia.CreateEmptyClone();
-    }
-    if (aMedia.GetType() == MediaSegment::AUDIO) {
-      MirrorAndDisableSegment(static_cast<AudioSegment&>(aMedia),
-                              static_cast<AudioSegment&>(*mMedia));
-    } else {
-      MOZ_CRASH("Unsupported media type");
-    }
-    NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia);
-  }
-
-  void IncreaseDisabled()
-  {
-    ++mDisabledCount;
-  }
-  void DecreaseDisabled()
-  {
-    --mDisabledCount;
-    MOZ_ASSERT(mDisabledCount >= 0, "Double decrease");
-  }
-
-  // Matches the number of disabled streams to which this listener is attached.
-  // The number of streams are those between the stream the listener was added
-  // and the SourceMediaStream that is the input of the data.
-  Atomic<int32_t> mDisabledCount;
-
-  nsAutoPtr<MediaSegment> mMedia;
-};
-
-/**
  * This is a base class for main-thread listener callbacks.
  * This callback is invoked on the main thread when the main-thread-visible
  * state of a stream has changed.
  *
  * These methods are called with the media graph monitor held, so
  * reentry into general media graph methods is not possible.
  * You should do something non-blocking and non-reentrant (e.g. dispatch an
  * event) and return. DispatchFromMainThreadAfterNextStreamStateUpdate
@@ -442,16 +151,33 @@ struct AudioNodeSizes
 {
   AudioNodeSizes() : mDomNode(0), mStream(0), mEngine(0), mNodeType() {}
   size_t mDomNode;
   size_t mStream;
   size_t mEngine;
   nsCString mNodeType;
 };
 
+class AudioNodeEngine;
+class AudioNodeExternalInputStream;
+class AudioNodeStream;
+class AudioSegment;
+class CameraPreviewMediaStream;
+class DirectMediaStreamListener;
+class DirectMediaStreamTrackListener;
+class MediaInputPort;
+class MediaStreamGraphImpl;
+class MediaStreamListener;
+class MediaStreamTrackListener;
+class ProcessedMediaStream;
+class SourceMediaStream;
+
+enum MediaStreamGraphEvent : uint32_t;
+enum TrackEventCommand : uint32_t;
+
 /**
  * Helper struct for binding a track listener to a specific TrackID.
  */
 template<typename Listener>
 struct TrackBound
 {
   RefPtr<Listener> mListener;
   TrackID mTrackID;
@@ -531,23 +257,17 @@ class MediaStream : public mozilla::Link
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
 
   MediaStream();
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
-  virtual ~MediaStream()
-  {
-    MOZ_COUNT_DTOR(MediaStream);
-    NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
-    NS_ASSERTION(mMainThreadListeners.IsEmpty(),
-                 "All main thread listeners should have been removed");
-  }
+  virtual ~MediaStream();
 
 public:
   /**
    * Returns the graph that owns this stream.
    */
   MediaStreamGraphImpl* GraphImpl();
   MediaStreamGraph* Graph();
   /**
@@ -594,28 +314,28 @@ public:
   /**
    * Adds aListener to the source stream of track aTrackID in this stream.
    * When the MediaStreamGraph processes the added listener, it will traverse
    * the graph and add it to the track's source stream (remapping the TrackID
    * along the way).
    * Note that the listener will be notified on the MediaStreamGraph thread
    * with whether the installation of it at the source was successful or not.
    */
-  virtual void AddDirectTrackListener(MediaStreamTrackDirectListener* aListener,
+  virtual void AddDirectTrackListener(DirectMediaStreamTrackListener* aListener,
                                       TrackID aTrackID);
 
   /**
    * Removes aListener from the source stream of track aTrackID in this stream.
    * Note that the listener has already been removed if the link between the
    * source of track aTrackID and this stream has been broken (and made track
    * aTrackID end). The caller doesn't have to care about this, removing when
    * the source cannot be found, or when the listener had already been removed
    * does nothing.
    */
-  virtual void RemoveDirectTrackListener(MediaStreamTrackDirectListener* aListener,
+  virtual void RemoveDirectTrackListener(DirectMediaStreamTrackListener* aListener,
                                          TrackID aTrackID);
 
   // A disabled track has video replaced by black, and audio replaced by
   // silence.
   void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
 
   // Finish event will be notified by calling methods of aListener. It is the
   // responsibility of the caller to remove aListener before it is destroyed.
@@ -708,19 +428,19 @@ public:
   void RemoveVideoOutputImpl(VideoFrameContainer* aContainer);
   void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
   void RemoveListenerImpl(MediaStreamListener* aListener);
   void RemoveAllListenersImpl();
   virtual void AddTrackListenerImpl(already_AddRefed<MediaStreamTrackListener> aListener,
                                     TrackID aTrackID);
   virtual void RemoveTrackListenerImpl(MediaStreamTrackListener* aListener,
                                        TrackID aTrackID);
-  virtual void AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
+  virtual void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
                                           TrackID aTrackID);
-  virtual void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
+  virtual void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
                                              TrackID aTrackID);
   virtual void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
 
   void AddConsumer(MediaInputPort* aPort)
   {
     mConsumers.AppendElement(aPort);
   }
   void RemoveConsumer(MediaInputPort* aPort)
@@ -859,17 +579,17 @@ protected:
 
   // Client-set volume of this stream
   struct AudioOutput {
     explicit AudioOutput(void* aKey) : mKey(aKey), mVolume(1.0f) {}
     void* mKey;
     float mVolume;
   };
   nsTArray<AudioOutput> mAudioOutputs;
-  nsTArray<RefPtr<VideoFrameContainer> > mVideoOutputs;
+  nsTArray<RefPtr<VideoFrameContainer>> mVideoOutputs;
   // We record the last played video frame to avoid playing the frame again
   // with a different frame id.
   VideoFrame mLastPlayedVideoFrame;
   nsTArray<RefPtr<MediaStreamListener> > mListeners;
   nsTArray<TrackBound<MediaStreamTrackListener>> mTrackListeners;
   nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
   nsTArray<TrackID> mDisabledTrackIDs;
 
@@ -948,24 +668,17 @@ protected:
  * This is a stream into which a decoder can write audio and video.
  *
  * Audio and video can be written on any thread, but you probably want to
  * always write from the same thread to avoid unexpected interleavings.
  */
 class SourceMediaStream : public MediaStream
 {
 public:
-  explicit SourceMediaStream() :
-    MediaStream(),
-    mMutex("mozilla::media::SourceMediaStream"),
-    mUpdateKnownTracksTime(0),
-    mPullEnabled(false),
-    mUpdateFinished(false),
-    mNeedsMixing(false)
-  {}
+  explicit SourceMediaStream();
 
   SourceMediaStream* AsSourceStream() override { return this; }
 
   // Media graph thread only
 
   // Users of audio inputs go through the stream so it can track when the
   // last stream referencing an input goes away, so it can close the cubeb
   // input.  Also note: callable on any thread (though it bounces through
@@ -987,20 +700,20 @@ public:
    */
   void SetPullEnabled(bool aEnabled);
 
   /**
    * These add/remove DirectListeners, which allow bypassing the graph and any
    * synchronization delays for e.g. PeerConnection, which wants the data ASAP
    * and lets the far-end handle sync and playout timing.
    */
-  void NotifyListenersEventImpl(MediaStreamListener::MediaStreamGraphEvent aEvent);
-  void NotifyListenersEvent(MediaStreamListener::MediaStreamGraphEvent aEvent);
-  void AddDirectListener(MediaStreamDirectListener* aListener);
-  void RemoveDirectListener(MediaStreamDirectListener* aListener);
+  void NotifyListenersEventImpl(MediaStreamGraphEvent aEvent);
+  void NotifyListenersEvent(MediaStreamGraphEvent aEvent);
+  void AddDirectListener(DirectMediaStreamListener* aListener);
+  void RemoveDirectListener(DirectMediaStreamListener* aListener);
 
   enum {
     ADDTRACK_QUEUED    = 0x01 // Queue track add until FinishAddTracks()
   };
   /**
    * Add a new track to the stream starting at the given base time (which
    * must be greater than or equal to the last time passed to
    * AdvanceKnownTracksTime). Takes ownership of aSegment. aSegment should
@@ -1011,20 +724,17 @@ public:
   {
     AddTrackInternal(aID, GraphRate(), aStart, aSegment, aFlags);
   }
 
   /**
    * Like AddTrack, but resamples audio from aRate to the graph rate.
    */
   void AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
-                     AudioSegment* aSegment, uint32_t aFlags = 0)
-  {
-    AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
-  }
+                     AudioSegment* aSegment, uint32_t aFlags = 0);
 
   /**
    * Call after a series of AddTrack or AddAudioTrack calls to implement
    * any pending track adds.
    */
   void FinishAddTracks();
 
   /**
@@ -1090,21 +800,20 @@ public:
    */
   bool HasPendingAudioTrack();
 
   // XXX need a Reset API
 
   friend class MediaStreamGraphImpl;
 
 protected:
-  enum TrackCommands {
-    TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
-    TRACK_END = MediaStreamListener::TRACK_EVENT_ENDED,
-    TRACK_UNUSED = MediaStreamListener::TRACK_EVENT_UNUSED,
-  };
+  enum TrackCommands : uint32_t;
+
+  virtual ~SourceMediaStream();
+
   /**
    * Data for each track that hasn't ended.
    */
   struct TrackData {
     TrackID mID;
     // Sample rate of the input data.
     TrackRate mInputRate;
     // Resampler if the rate of the input track does not match the
@@ -1121,19 +830,19 @@ protected:
     // this is cleared.
     uint32_t mCommands;
   };
 
   bool NeedsMixing();
 
   void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
 
-  void AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
+  void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
                                   TrackID aTrackID) override;
-  void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
+  void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
                                      TrackID aTrackID) override;
 
   void AddTrackInternal(TrackID aID, TrackRate aRate,
                         StreamTime aStart, MediaSegment* aSegment,
                         uint32_t aFlags);
 
   TrackData* FindDataForTrack(TrackID aID)
   {
@@ -1163,18 +872,18 @@ protected:
 
   // This must be acquired *before* MediaStreamGraphImpl's lock, if they are
   // held together.
   Mutex mMutex;
   // protected by mMutex
   StreamTime mUpdateKnownTracksTime;
   nsTArray<TrackData> mUpdateTracks;
   nsTArray<TrackData> mPendingTracks;
-  nsTArray<RefPtr<MediaStreamDirectListener> > mDirectListeners;
-  nsTArray<TrackBound<MediaStreamTrackDirectListener>> mDirectTrackListeners;
+  nsTArray<RefPtr<DirectMediaStreamListener>> mDirectListeners;
+  nsTArray<TrackBound<DirectMediaStreamTrackListener>> mDirectTrackListeners;
   bool mPullEnabled;
   bool mUpdateFinished;
   bool mNeedsMixing;
 };
 
 /**
  * The blocking mode decides how a track should be blocked in a MediaInputPort.
  */
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaStreamListener.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MediaStreamListener.h"
+
+#include "AudioSegment.h"
+#include "VideoSegment.h"
+#include "StreamTracks.h"
+
+namespace mozilla {
+
+void
+DirectMediaStreamTrackListener::MirrorAndDisableSegment(AudioSegment& aFrom,
+                                                       AudioSegment& aTo)
+{
+  aTo.Clear();
+  aTo.AppendNullData(aFrom.GetDuration());
+}
+
+void
+DirectMediaStreamTrackListener::MirrorAndDisableSegment(VideoSegment& aFrom,
+                                                       VideoSegment& aTo)
+{
+  aTo.Clear();
+  for (VideoSegment::ChunkIterator it(aFrom); !it.IsEnded(); it.Next()) {
+    aTo.AppendFrame(do_AddRef(it->mFrame.GetImage()), it->GetDuration(),
+                    it->mFrame.GetIntrinsicSize(), it->GetPrincipalHandle(), true);
+  }
+}
+
+void
+DirectMediaStreamTrackListener::NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
+                                                                             StreamTime aTrackOffset,
+                                                                             MediaSegment& aMedia)
+{
+  if (mDisabledCount == 0) {
+    NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia);
+    return;
+  }
+
+  if (!mMedia) {
+    mMedia = aMedia.CreateEmptyClone();
+  }
+  if (aMedia.GetType() == MediaSegment::AUDIO) {
+    MirrorAndDisableSegment(static_cast<AudioSegment&>(aMedia),
+                            static_cast<AudioSegment&>(*mMedia));
+  } else if (aMedia.GetType() == MediaSegment::VIDEO) {
+    MirrorAndDisableSegment(static_cast<VideoSegment&>(aMedia),
+                            static_cast<VideoSegment&>(*mMedia));
+  } else {
+    MOZ_CRASH("Unsupported media type");
+  }
+  NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaStreamListener.h
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 MOZILLA_MEDIASTREAMLISTENER_h_
+#define MOZILLA_MEDIASTREAMLISTENER_h_
+
+namespace mozilla {
+
+class MediaStream;
+class MediaStreamGraph;
+
+enum MediaStreamGraphEvent : uint32_t {
+  EVENT_FINISHED,
+  EVENT_REMOVED,
+  EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
+  EVENT_HAS_NO_DIRECT_LISTENERS,  // transition to no direct listeners
+};
+
+// maskable flags, not a simple enumerated value
+enum TrackEventCommand : uint32_t {
+  TRACK_EVENT_NONE = 0x00,
+  TRACK_EVENT_CREATED = 0x01,
+  TRACK_EVENT_ENDED = 0x02,
+  TRACK_EVENT_UNUSED = ~(TRACK_EVENT_ENDED | TRACK_EVENT_CREATED),
+};
+
+/**
+ * This is a base class for media graph thread listener callbacks.
+ * Override methods to be notified of audio or video data or changes in stream
+ * state.
+ *
+ * This can be used by stream recorders or network connections that receive
+ * stream input. It could also be used for debugging.
+ *
+ * All notification methods are called from the media graph thread. Overriders
+ * of these methods are responsible for all synchronization. Beware!
+ * These methods are called without the media graph monitor held, so
+ * reentry into media graph methods is possible, although very much discouraged!
+ * You should do something non-blocking and non-reentrant (e.g. dispatch an
+ * event to some thread) and return.
+ * The listener is not allowed to add/remove any listeners from the stream.
+ *
+ * When a listener is first attached, we guarantee to send a NotifyBlockingChanged
+ * callback to notify of the initial blocking state. Also, if a listener is
+ * attached to a stream that has already finished, we'll call NotifyFinished.
+ */
+class MediaStreamListener {
+protected:
+  // Protected destructor, to discourage deletion outside of Release():
+  virtual ~MediaStreamListener() {}
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
+
+  /**
+   * When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
+   * control loop is ready to pull, this gets called. A NotifyPull implementation
+   * is allowed to call the SourceMediaStream methods that alter track
+   * data. It is not allowed to make other MediaStream API calls, including
+   * calls to add or remove MediaStreamListeners. It is not allowed to block
+   * for any length of time.
+   * aDesiredTime is the stream time we would like to get data up to. Data
+   * beyond this point will not be played until NotifyPull runs again, so there's
+   * not much point in providing it. Note that if the stream is blocked for
+   * some reason, then data before aDesiredTime may not be played immediately.
+   */
+  virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
+
+  enum Blocking {
+    BLOCKED,
+    UNBLOCKED
+  };
+  /**
+   * Notify that the blocking status of the stream changed. The initial state
+   * is assumed to be BLOCKED.
+   */
+  virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
+
+  /**
+   * Notify that the stream has data in each track
+   * for the stream's current time. Once this state becomes true, it will
+   * always be true since we block stream time from progressing to times where
+   * there isn't data in each track.
+   */
+  virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
+
+  /**
+   * Notify that the stream output is advancing. aCurrentTime is the graph's
+   * current time. MediaStream::GraphTimeToStreamTime can be used to get the
+   * stream time.
+   */
+  virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
+
+  /**
+   * Notify that an event has occurred on the Stream
+   */
+  virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
+
+  /**
+   * Notify that changes to one of the stream tracks have been queued.
+   * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
+   * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
+   * at aTrackOffset (relative to the start of the stream).
+   * aInputStream and aInputTrackID will be set if the changes originated
+   * from an input stream's track. In practice they will only be used for
+   * ProcessedMediaStreams.
+   */
+  virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+                                        StreamTime aTrackOffset,
+                                        TrackEventCommand aTrackEvents,
+                                        const MediaSegment& aQueuedMedia,
+                                        MediaStream* aInputStream = nullptr,
+                                        TrackID aInputTrackID = TRACK_INVALID) {}
+
+  /**
+   * Notify queued audio data. Only audio data need to be queued. The video data
+   * will be notified by MediaStreamVideoSink::SetCurrentFrame.
+   */
+  virtual void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
+                                     StreamTime aTrackOffset,
+                                     const AudioSegment& aQueuedMedia,
+                                     MediaStream* aInputStream = nullptr,
+                                     TrackID aInputTrackID = TRACK_INVALID) {}
+
+  /**
+   * Notify that all new tracks this iteration have been created.
+   * This is to ensure that tracks added atomically to MediaStreamGraph
+   * are also notified of atomically to MediaStreamListeners.
+   */
+  virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) {}
+};
+
+/**
+ * This is a base class for media graph thread listener callbacks locked to
+ * specific tracks. Override methods to be notified of audio or video data or
+ * changes in track state.
+ *
+ * All notification methods are called from the media graph thread. Overriders
+ * of these methods are responsible for all synchronization. Beware!
+ * These methods are called without the media graph monitor held, so
+ * reentry into media graph methods is possible, although very much discouraged!
+ * You should do something non-blocking and non-reentrant (e.g. dispatch an
+ * event to some thread) and return.
+ * The listener is not allowed to add/remove any listeners from the parent
+ * stream.
+ *
+ * If a listener is attached to a track that has already ended, we guarantee
+ * to call NotifyEnded.
+ */
+class MediaStreamTrackListener
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener)
+
+public:
+  virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph,
+                                   StreamTime aTrackOffset,
+                                   const MediaSegment& aQueuedMedia) {}
+
+  virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
+                                            const PrincipalHandle& aNewPrincipalHandle) {}
+
+  virtual void NotifyEnded() {}
+
+  virtual void NotifyRemoved() {}
+
+protected:
+  virtual ~MediaStreamTrackListener() {}
+};
+
+
+/**
+ * This is a base class for media graph thread listener direct callbacks
+ * from within AppendToTrack(). Note that your regular listener will
+ * still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
+ * you must be careful to ignore them if AddDirectListener was successful.
+ */
+class DirectMediaStreamListener : public MediaStreamListener
+{
+public:
+  virtual ~DirectMediaStreamListener() {}
+
+  /*
+   * This will be called on any DirectMediaStreamListener added to a
+   * a SourceMediaStream when AppendToTrack() is called.  The MediaSegment
+   * will be the RawSegment (unresampled) if available in AppendToTrack().
+   * Note that NotifyQueuedTrackChanges() calls will also still occur.
+   */
+  virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
+                                  StreamTime aTrackOffset,
+                                  uint32_t aTrackEvents,
+                                  const MediaSegment& aMedia) {}
+};
+
+/**
+ * This is a base class for media graph thread listener direct callbacks from
+ * within AppendToTrack(). It is bound to a certain track and can only be
+ * installed on audio tracks. Once added to a track on any stream in the graph,
+ * the graph will try to install it at that track's source of media data.
+ *
+ * This works for TrackUnionStreams, which will forward the listener to the
+ * track's input track if it exists, or wait for it to be created before
+ * forwarding if it doesn't.
+ * Once it reaches a SourceMediaStream, it can be successfully installed.
+ * Other types of streams will fail installation since they are not supported.
+ *
+ * Note that this listener and others for the same track will still get
+ * NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful
+ * to ignore them if this listener was successfully installed.
+ */
+class DirectMediaStreamTrackListener : public MediaStreamTrackListener
+{
+  friend class SourceMediaStream;
+  friend class TrackUnionStream;
+
+public:
+  /*
+   * This will be called on any DirectMediaStreamTrackListener added to a
+   * SourceMediaStream when AppendToTrack() is called for the listener's bound
+   * track, using the thread of the AppendToTrack() caller. The MediaSegment
+   * will be the RawSegment (unresampled) if available in AppendToTrack().
+   * If the track is enabled at the source but has been disabled in one of the
+   * streams in between the source and where it was originally added, aMedia
+   * will be a disabled version of the one passed to AppendToTrack() as well.
+   * Note that NotifyQueuedTrackChanges() calls will also still occur.
+   */
+  virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
+                                       StreamTime aTrackOffset,
+                                       const MediaSegment& aMedia) {}
+
+  /**
+   * When a direct listener is processed for installation by the
+   * MediaStreamGraph it will be notified with whether the installation was
+   * successful or not. The results of this installation are the following:
+   * TRACK_NOT_FOUND_AT_SOURCE
+   *    We found the source stream of media data for this track, but the track
+   *    didn't exist. This should only happen if you try to install the listener
+   *    directly to a SourceMediaStream that doesn't contain the given TrackID.
+   * STREAM_NOT_SUPPORTED
+   *    While looking for the data source of this track, we found a MediaStream
+   *    that is not a SourceMediaStream or a TrackUnionStream.
+   * SUCCESS
+   *    Installation was successful and this listener will start receiving
+   *    NotifyRealtimeData on the next AppendToTrack().
+   */
+  enum class InstallationResult {
+    TRACK_NOT_FOUND_AT_SOURCE,
+    TRACK_TYPE_NOT_SUPPORTED,
+    STREAM_NOT_SUPPORTED,
+    SUCCESS
+  };
+  virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {}
+  virtual void NotifyDirectListenerUninstalled() {}
+
+protected:
+  virtual ~DirectMediaStreamTrackListener() {}
+
+  void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo);
+  void MirrorAndDisableSegment(VideoSegment& aFrom, VideoSegment& aTo);
+  void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
+                                                     StreamTime aTrackOffset,
+                                                     MediaSegment& aMedia);
+
+  void IncreaseDisabled()
+  {
+    ++mDisabledCount;
+  }
+  void DecreaseDisabled()
+  {
+    --mDisabledCount;
+    MOZ_ASSERT(mDisabledCount >= 0, "Double decrease");
+  }
+
+  // Matches the number of disabled streams to which this listener is attached.
+  // The number of streams are those between the stream the listener was added
+  // and the SourceMediaStream that is the input of the data.
+  Atomic<int32_t> mDisabledCount;
+
+  nsAutoPtr<MediaSegment> mMedia;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_MEDIASTREAMLISTENER_h_
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -4,16 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaStreamTrack.h"
 
 #include "DOMMediaStream.h"
 #include "MediaStreamGraph.h"
 #include "nsIUUIDGenerator.h"
 #include "nsServiceManagerUtils.h"
+#include "MediaStreamListener.h"
 
 #ifdef LOG
 #undef LOG
 #endif
 
 static PRLogModuleInfo* gMediaStreamTrackLog;
 #define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg)
 
@@ -156,16 +157,22 @@ MediaStreamTrack::Destroy()
   }
   if (mPrincipalHandleListener) {
     if (GetOwnedStream()) {
       RemoveListener(mPrincipalHandleListener);
     }
     mPrincipalHandleListener->Forget();
     mPrincipalHandleListener = nullptr;
   }
+  for (auto l : mTrackListeners) {
+    RemoveListener(l);
+  }
+  for (auto l : mDirectTrackListeners) {
+    RemoveDirectListener(l);
+  }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaStreamTrack,
                                                 DOMEventTargetHelper)
   tmp->Destroy();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningStream)
@@ -382,55 +389,70 @@ MediaStreamTrack::GetInputStream()
   DOMMediaStream* inputDOMStream = GetInputDOMStream();
   MOZ_RELEASE_ASSERT(inputDOMStream->GetInputStream());
   return inputDOMStream->GetInputStream();
 }
 
 ProcessedMediaStream*
 MediaStreamTrack::GetOwnedStream()
 {
+  if (!mOwningStream)
+  {
+    return nullptr;
+  }
+
   return mOwningStream->GetOwnedStream();
 }
 
 void
 MediaStreamTrack::AddListener(MediaStreamTrackListener* aListener)
 {
   LOG(LogLevel::Debug, ("MediaStreamTrack %p adding listener %p",
                         this, aListener));
+  MOZ_ASSERT(GetOwnedStream());
 
   GetOwnedStream()->AddTrackListener(aListener, mTrackID);
+  mTrackListeners.AppendElement(aListener);
 }
 
 void
 MediaStreamTrack::RemoveListener(MediaStreamTrackListener* aListener)
 {
   LOG(LogLevel::Debug, ("MediaStreamTrack %p removing listener %p",
                         this, aListener));
 
-  GetOwnedStream()->RemoveTrackListener(aListener, mTrackID);
+  if (GetOwnedStream()) {
+    GetOwnedStream()->RemoveTrackListener(aListener, mTrackID);
+    mTrackListeners.RemoveElement(aListener);
+  }
 }
 
 void
-MediaStreamTrack::AddDirectListener(MediaStreamTrackDirectListener *aListener)
+MediaStreamTrack::AddDirectListener(DirectMediaStreamTrackListener *aListener)
 {
   LOG(LogLevel::Debug, ("MediaStreamTrack %p (%s) adding direct listener %p to "
                         "stream %p, track %d",
                         this, AsAudioStreamTrack() ? "audio" : "video",
                         aListener, GetOwnedStream(), mTrackID));
+  MOZ_ASSERT(GetOwnedStream());
 
   GetOwnedStream()->AddDirectTrackListener(aListener, mTrackID);
+  mDirectTrackListeners.AppendElement(aListener);
 }
 
 void
-MediaStreamTrack::RemoveDirectListener(MediaStreamTrackDirectListener *aListener)
+MediaStreamTrack::RemoveDirectListener(DirectMediaStreamTrackListener *aListener)
 {
   LOG(LogLevel::Debug, ("MediaStreamTrack %p removing direct listener %p from stream %p",
                         this, aListener, GetOwnedStream()));
 
-  GetOwnedStream()->RemoveDirectTrackListener(aListener, mTrackID);
+  if (GetOwnedStream()) {
+    GetOwnedStream()->RemoveDirectTrackListener(aListener, mTrackID);
+    mDirectTrackListeners.RemoveElement(aListener);
+  }
 }
 
 already_AddRefed<MediaInputPort>
 MediaStreamTrack::ForwardTrackContentsTo(ProcessedMediaStream* aStream)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(aStream);
   RefPtr<MediaInputPort> port =
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -19,17 +19,17 @@ namespace mozilla {
 
 class DOMMediaStream;
 class MediaEnginePhotoCallback;
 class MediaInputPort;
 class MediaStream;
 class MediaStreamGraph;
 class MediaStreamGraphImpl;
 class MediaStreamTrackListener;
-class MediaStreamTrackDirectListener;
+class DirectMediaStreamTrackListener;
 class PeerConnectionImpl;
 class PeerConnectionMedia;
 class PeerIdentity;
 class ProcessedMediaStream;
 class RemoteSourceStreamInfo;
 class SourceStreamInfo;
 
 namespace dom {
@@ -358,31 +358,33 @@ public:
   void RemoveListener(MediaStreamTrackListener* aListener);
 
   /**
    * Attempts to add a direct track listener to this track.
    * Callers must listen to the NotifyInstalled event to know if installing
    * the listener succeeded (tracks originating from SourceMediaStreams) or
    * failed (e.g., WebAudio originated tracks).
    */
-  void AddDirectListener(MediaStreamTrackDirectListener *aListener);
-  void RemoveDirectListener(MediaStreamTrackDirectListener  *aListener);
+  void AddDirectListener(DirectMediaStreamTrackListener *aListener);
+  void RemoveDirectListener(DirectMediaStreamTrackListener  *aListener);
 
   /**
    * Sets up a MediaInputPort from the underlying track that this
    * MediaStreamTrack represents, to aStream, and returns it.
    */
   already_AddRefed<MediaInputPort> ForwardTrackContentsTo(ProcessedMediaStream* aStream);
 
   /**
    * Returns true if this track is connected to aPort and forwarded to aPort's
    * output stream.
    */
   bool IsForwardedThrough(MediaInputPort* aPort);
 
+  void SetMediaStreamSizeListener(DirectMediaStreamTrackListener* aListener);
+
 protected:
   virtual ~MediaStreamTrack();
 
   void Destroy();
 
   // Returns the original DOMMediaStream's underlying input stream.
   MediaStream* GetInputStream();
 
@@ -411,16 +413,20 @@ protected:
   RefPtr<DOMMediaStream> mOwningStream;
   TrackID mTrackID;
   TrackID mInputTrackID;
   RefPtr<MediaStreamTrackSource> mSource;
   RefPtr<MediaStreamTrack> mOriginalTrack;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIPrincipal> mPendingPrincipal;
   RefPtr<PrincipalHandleListener> mPrincipalHandleListener;
+  // Keep tracking MediaStreamTrackListener and DirectMediaStreamTrackListener,
+  // so we can remove them in |Destory|.
+  nsTArray<RefPtr<MediaStreamTrackListener>> mTrackListeners;
+  nsTArray<RefPtr<DirectMediaStreamTrackListener>> mDirectTrackListeners;
   nsString mID;
   MediaStreamTrackState mReadyState;
   bool mEnabled;
   const bool mRemote;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/MediaTrackList.cpp
+++ b/dom/media/MediaTrackList.cpp
@@ -4,16 +4,17 @@
  * 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 "MediaTrack.h"
 #include "MediaTrackList.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/AudioTrack.h"
+#include "mozilla/dom/VideoStreamTrack.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/TrackEvent.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 MediaTrackList::MediaTrackList(nsPIDOMWindowInner* aOwnerWindow,
@@ -101,19 +102,20 @@ MediaTrackList::CreateAudioTrack(const n
                                               aEnabled);
   return track.forget();
 }
 
 already_AddRefed<VideoTrack>
 MediaTrackList::CreateVideoTrack(const nsAString& aId,
                                  const nsAString& aKind,
                                  const nsAString& aLabel,
-                                 const nsAString& aLanguage)
+                                 const nsAString& aLanguage,
+                                 VideoStreamTrack* aVideoTrack)
 {
-  RefPtr<VideoTrack> track = new VideoTrack(aId, aKind, aLabel, aLanguage);
+  RefPtr<VideoTrack> track = new VideoTrack(aId, aKind, aLabel, aLanguage, aVideoTrack);
   return track.forget();
 }
 
 void
 MediaTrackList::EmptyTracks()
 {
   for (uint32_t i = 0; i < mTracks.Length(); ++i) {
     mTracks[i]->SetTrackList(nullptr);
--- a/dom/media/MediaTrackList.h
+++ b/dom/media/MediaTrackList.h
@@ -15,16 +15,17 @@ class DOMMediaStream;
 namespace dom {
 
 class HTMLMediaElement;
 class MediaTrack;
 class AudioTrackList;
 class VideoTrackList;
 class AudioTrack;
 class VideoTrack;
+class VideoStreamTrack;
 
 /**
  * Base class of AudioTrackList and VideoTrackList. The AudioTrackList and
  * VideoTrackList objects represent a dynamic list of zero or more audio and
  * video tracks respectively.
  *
  * When a media element is to forget its media-resource-specific tracks, its
  * audio track list and video track list will be emptied.
@@ -53,21 +54,24 @@ public:
 
   static already_AddRefed<AudioTrack>
   CreateAudioTrack(const nsAString& aId,
                    const nsAString& aKind,
                    const nsAString& aLabel,
                    const nsAString& aLanguage,
                    bool aEnabled);
 
+  // For the case of src of HTMLMediaElement is non-MediaStream, leave the
+  // aVideoTrack as default(nullptr).
   static already_AddRefed<VideoTrack>
   CreateVideoTrack(const nsAString& aId,
                    const nsAString& aKind,
                    const nsAString& aLabel,
-                   const nsAString& aLanguage);
+                   const nsAString& aLanguage,
+                   VideoStreamTrack* aVideoTrack = nullptr);
 
   virtual void EmptyTracks();
 
   void CreateAndDispatchChangeEvent();
 
   // WebIDL
   MediaTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
 
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "nsContentUtils.h"
 #include "nsIAppShell.h"
 #include "nsIObserver.h"
@@ -195,17 +196,17 @@ TrackUnionStream::TrackUnionStream() :
     // samples in our input stream to go just beyond the destination time.
     StreamTime outputStart = GraphTimeToStreamTimeWithBlocking(aFrom);
 
     nsAutoPtr<MediaSegment> segment;
     segment = aTrack->GetSegment()->CreateEmptyClone();
     for (uint32_t j = 0; j < mListeners.Length(); ++j) {
       MediaStreamListener* l = mListeners[j];
       l->NotifyQueuedTrackChanges(Graph(), id, outputStart,
-                                  MediaStreamListener::TRACK_EVENT_CREATED,
+                                  TrackEventCommand::TRACK_EVENT_CREATED,
                                   *segment,
                                   aPort->GetSource(), aTrack->GetID());
     }
     segment->AppendNullData(outputStart);
     StreamTracks::Track* track =
       &mTracks.AddTrack(id, outputStart, segment.forget());
     STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p added track %d for input stream %p track %d, start ticks %lld",
                                  this, track->GetID(), aPort->GetSource(), aTrack->GetID(),
@@ -216,17 +217,17 @@ TrackUnionStream::TrackUnionStream() :
     map->mEndOfLastInputIntervalInInputStream = -1;
     map->mEndOfLastInputIntervalInOutputStream = -1;
     map->mInputPort = aPort;
     map->mInputTrackID = aTrack->GetID();
     map->mOutputTrackID = track->GetID();
     map->mSegment = aTrack->GetSegment()->CreateEmptyClone();
 
     for (int32_t i = mPendingDirectTrackListeners.Length() - 1; i >= 0; --i) {
-      TrackBound<MediaStreamTrackDirectListener>& bound =
+      TrackBound<DirectMediaStreamTrackListener>& bound =
         mPendingDirectTrackListeners[i];
       if (bound.mTrackID != map->mOutputTrackID) {
         continue;
       }
       MediaStream* source = map->mInputPort->GetSource();
       map->mOwnedDirectListeners.AppendElement(bound.mListener);
       if (mDisabledTrackIDs.Contains(bound.mTrackID)) {
         bound.mListener->IncreaseDisabled();
@@ -251,17 +252,17 @@ TrackUnionStream::TrackUnionStream() :
       return;
     STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p ending track %d", this, outputTrack->GetID()));
     for (uint32_t j = 0; j < mListeners.Length(); ++j) {
       MediaStreamListener* l = mListeners[j];
       StreamTime offset = outputTrack->GetSegment()->GetDuration();
       nsAutoPtr<MediaSegment> segment;
       segment = outputTrack->GetSegment()->CreateEmptyClone();
       l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), offset,
-                                  MediaStreamListener::TRACK_EVENT_ENDED,
+                                  TrackEventCommand::TRACK_EVENT_ENDED,
                                   *segment,
                                   mTrackMap[aIndex].mInputPort->GetSource(),
                                   mTrackMap[aIndex].mInputTrackID);
     }
     for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
       if (b.mTrackID == outputTrack->GetID()) {
         b.mListener->NotifyEnded();
       }
@@ -329,17 +330,17 @@ TrackUnionStream::TrackUnionStream() :
           l->NotifyQueuedAudioData(Graph(), outputTrack->GetID(),
                                    outputStart,
                                    *static_cast<AudioSegment*>(segment),
                                    map->mInputPort->GetSource(),
                                    map->mInputTrackID);
         } else {
           // This part will be removed in bug 1201363.
           l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
-                                      outputStart, 0, *segment,
+                                      outputStart, TrackEventCommand::TRACK_EVENT_NONE, *segment,
                                       map->mInputPort->GetSource(),
                                       map->mInputTrackID);
         }
       }
       for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
         if (b.mTrackID != outputTrack->GetID()) {
           continue;
         }
@@ -350,17 +351,17 @@ TrackUnionStream::TrackUnionStream() :
   }
 
 void
 TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) {
   for (TrackMapEntry& entry : mTrackMap) {
     if (entry.mOutputTrackID == aTrackID) {
       STREAM_LOG(LogLevel::Info, ("TrackUnionStream %p track %d was explicitly %s",
                                    this, aTrackID, aEnabled ? "enabled" : "disabled"));
-      for (MediaStreamTrackDirectListener* listener : entry.mOwnedDirectListeners) {
+      for (DirectMediaStreamTrackListener* listener : entry.mOwnedDirectListeners) {
         bool oldEnabled = !mDisabledTrackIDs.Contains(aTrackID);
         if (!oldEnabled && aEnabled) {
           STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting "
                                        "direct listener enabled",
                                        this, aTrackID));
           listener->DecreaseDisabled();
         } else if (oldEnabled && !aEnabled) {
           STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting "
@@ -394,20 +395,20 @@ TrackUnionStream::GetInputTrackIDFor(Tra
       return entry.mInputTrackID;
     }
   }
 
   return TRACK_NONE;
 }
 
 void
-TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
+TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
                                              TrackID aTrackID)
 {
-  RefPtr<MediaStreamTrackDirectListener> listener = aListener;
+  RefPtr<DirectMediaStreamTrackListener> listener = aListener;
 
   for (TrackMapEntry& entry : mTrackMap) {
     if (entry.mOutputTrackID == aTrackID) {
       MediaStream* source = entry.mInputPort->GetSource();
       STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener "
                                    "%p for track %d. Forwarding to input "
                                    "stream %p track %d.",
                                    this, listener.get(), aTrackID, source,
@@ -417,24 +418,24 @@ TrackUnionStream::AddDirectTrackListener
         listener->IncreaseDisabled();
       }
       source->AddDirectTrackListenerImpl(listener.forget(),
                                          entry.mInputTrackID);
       return;
     }
   }
 
-  TrackBound<MediaStreamTrackDirectListener>* bound =
+  TrackBound<DirectMediaStreamTrackListener>* bound =
     mPendingDirectTrackListeners.AppendElement();
   bound->mListener = listener.forget();
   bound->mTrackID = aTrackID;
 }
 
 void
-TrackUnionStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
+TrackUnionStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
                                                 TrackID aTrackID)
 {
   for (TrackMapEntry& entry : mTrackMap) {
     // OutputTrackID is unique to this stream so we only need to do this once.
     if (entry.mOutputTrackID != aTrackID) {
       continue;
     }
     for (size_t i = 0; i < entry.mOwnedDirectListeners.Length(); ++i) {
@@ -455,17 +456,17 @@ TrackUnionStream::RemoveDirectTrackListe
     }
     // Forward to the input
     MediaStream* source = entry.mInputPort->GetSource();
     source->RemoveDirectTrackListenerImpl(aListener, entry.mInputTrackID);
     return;
   }
 
   for (size_t i = 0; i < mPendingDirectTrackListeners.Length(); ++i) {
-    TrackBound<MediaStreamTrackDirectListener>& bound =
+    TrackBound<DirectMediaStreamTrackListener>& bound =
       mPendingDirectTrackListeners[i];
     if (bound.mListener == aListener && bound.mTrackID == aTrackID) {
       mPendingDirectTrackListeners.RemoveElementAt(i);
       return;
     }
   }
 }
 } // namespace mozilla
--- a/dom/media/TrackUnionStream.h
+++ b/dom/media/TrackUnionStream.h
@@ -48,42 +48,42 @@ protected:
     // we call StreamTracks::FindTrack, which will return null if
     // the track has been deleted.
     TrackID mInputTrackID;
     TrackID mOutputTrackID;
     nsAutoPtr<MediaSegment> mSegment;
     // These are direct track listeners that have been added to this
     // TrackUnionStream-track and forwarded to the input track. We will update
     // these when this track's disabled status changes.
-    nsTArray<RefPtr<MediaStreamTrackDirectListener>> mOwnedDirectListeners;
+    nsTArray<RefPtr<DirectMediaStreamTrackListener>> mOwnedDirectListeners;
   };
 
   // Add the track to this stream, retaining its TrackID if it has never
   // been previously used in this stream, allocating a new TrackID otherwise.
   uint32_t AddTrack(MediaInputPort* aPort, StreamTracks::Track* aTrack,
                     GraphTime aFrom);
   void EndTrack(uint32_t aIndex);
   void CopyTrackData(StreamTracks::Track* aInputTrack,
                      uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo,
                      bool* aOutputTrackFinished);
 
-  void AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
+  void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
                                   TrackID aTrackID) override;
-  void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
+  void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
                                      TrackID aTrackID) override;
 
   nsTArray<TrackMapEntry> mTrackMap;
 
   // The next available TrackID, starting at 1 and progressing upwards.
   // All TrackIDs in [1, mNextAvailableTrackID) have implicitly been used.
   TrackID mNextAvailableTrackID;
 
   // Sorted array of used TrackIDs that require manual tracking.
   nsTArray<TrackID> mUsedTracks;
 
   // Direct track listeners that have not been forwarded to their input stream
   // yet. We'll forward these as their inputs become available.
-  nsTArray<TrackBound<MediaStreamTrackDirectListener>> mPendingDirectTrackListeners;
+  nsTArray<TrackBound<DirectMediaStreamTrackListener>> mPendingDirectTrackListeners;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASTREAMGRAPH_H_ */
--- a/dom/media/VideoTrack.cpp
+++ b/dom/media/VideoTrack.cpp
@@ -1,31 +1,45 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 et tw=78: */
 /* 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 "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/dom/VideoStreamTrack.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/VideoTrackBinding.h"
 #include "mozilla/dom/VideoTrackList.h"
 
 namespace mozilla {
 namespace dom {
 
 VideoTrack::VideoTrack(const nsAString& aId,
                        const nsAString& aKind,
                        const nsAString& aLabel,
-                       const nsAString& aLanguage)
+                       const nsAString& aLanguage,
+                       VideoStreamTrack* aStreamTarck)
   : MediaTrack(aId, aKind, aLabel, aLanguage)
   , mSelected(false)
+  , mVideoStreamTrack(aStreamTarck)
+{
+}
+
+VideoTrack::~VideoTrack()
 {
 }
 
+NS_IMPL_CYCLE_COLLECTION_INHERITED(VideoTrack, MediaTrack, mVideoStreamTrack)
+
+NS_IMPL_ADDREF_INHERITED(VideoTrack, MediaTrack)
+NS_IMPL_RELEASE_INHERITED(VideoTrack, MediaTrack)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(VideoTrack)
+NS_INTERFACE_MAP_END_INHERITING(MediaTrack)
+
 JSObject*
 VideoTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return VideoTrackBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void VideoTrack::SetSelected(bool aSelected)
 {
--- a/dom/media/VideoTrack.h
+++ b/dom/media/VideoTrack.h
@@ -8,50 +8,63 @@
 #define mozilla_dom_VideoTrack_h
 
 #include "MediaTrack.h"
 
 namespace mozilla {
 namespace dom {
 
 class VideoTrackList;
+class VideoStreamTrack;
 
 class VideoTrack : public MediaTrack
 {
 public:
   VideoTrack(const nsAString& aId,
              const nsAString& aKind,
              const nsAString& aLabel,
-             const nsAString& aLanguage);
+             const nsAString& aLanguage,
+             VideoStreamTrack* aStreamTarck = nullptr);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(VideoTrack, MediaTrack)
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   VideoTrack* AsVideoTrack() override
   {
     return this;
   }
 
   // When fetching media resource, if no video track is selected by the media
   // resource, then the first VideoTrack object in the list is set selected as
   // default. If multiple video tracks are selected by its media resource at
   // fetching phase, then the first enabled video track is set selected.
   // aFlags contains FIRE_NO_EVENTS because no events are fired in such cases.
   void SetEnabledInternal(bool aEnabled, int aFlags) override;
 
+  // Get associated video stream track when the video track comes from
+  // MediaStream. This might be nullptr when the src of owning HTMLMediaElement
+  // is not MediaStream.
+  VideoStreamTrack* GetVideoStreamTrack() { return mVideoStreamTrack; }
+
   // WebIDL
   bool Selected() const
   {
     return mSelected;
   }
 
   // Either zero or one video track is selected in a list; If the selected track
   // is in a VideoTrackList, then all the other VideoTrack objects in that list
   // must be unselected.
   void SetSelected(bool aSelected);
 
 private:
+  virtual ~VideoTrack();
+
   bool mSelected;
+  RefPtr<VideoStreamTrack> mVideoStreamTrack;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_VideoTrack_h
--- a/dom/media/VideoTrackList.cpp
+++ b/dom/media/VideoTrackList.cpp
@@ -26,37 +26,37 @@ VideoTrackList::operator[](uint32_t aInd
 void
 VideoTrackList::RemoveTrack(const RefPtr<MediaTrack>& aTrack)
 {
   // we need to find the video track before |MediaTrackList::RemoveTrack|. Or
   // mSelectedIndex will not be valid. The check of mSelectedIndex == -1
   // need to be done after RemoveTrack. Also the call of
   // |MediaTrackList::RemoveTrack| is necessary even when mSelectedIndex = -1.
   bool found;
-  VideoTrack* videoTrack = IndexedGetter(mSelectedIndex, found);
+  VideoTrack* selectedVideoTrack = IndexedGetter(mSelectedIndex, found);
   MediaTrackList::RemoveTrack(aTrack);
   if (mSelectedIndex == -1) {
     // There was no selected track and we don't select another track on removal.
     return;
   }
   MOZ_ASSERT(found, "When mSelectedIndex is set it should point to a track");
-  MOZ_ASSERT(videoTrack, "The mSelectedIndex should be set to video track only");
+  MOZ_ASSERT(selectedVideoTrack, "The mSelectedIndex should be set to video track only");
 
   // Let the caller of RemoveTrack deal with choosing the new selected track if
   // it removes the currently-selected track.
-  if (aTrack == videoTrack) {
+  if (aTrack == selectedVideoTrack) {
     mSelectedIndex = -1;
     return;
   }
 
   // The removed track was not the selected track and there is a
   // currently-selected video track. We need to find the new location of the
   // selected track.
   for (size_t ix = 0; ix < mTracks.Length(); ix++) {
-    if (mTracks[ix] == videoTrack) {
+    if (mTracks[ix] == selectedVideoTrack) {
       mSelectedIndex = ix;
       return;
     }
   }
 }
 
 void
 VideoTrackList::EmptyTracks()
--- a/dom/media/android/AndroidMediaResourceServer.cpp
+++ b/dom/media/android/AndroidMediaResourceServer.cpp
@@ -16,19 +16,16 @@
 #include "nsReadLine.h"
 #include "nsNetCID.h"
 #include "VideoUtils.h"
 #include "MediaResource.h"
 #include "AndroidMediaResourceServer.h"
 
 #if defined(_MSC_VER)
 #define strtoll _strtoi64
-#if _MSC_VER < 1900
-#define snprintf _snprintf_s
-#endif
 #endif
 
 using namespace mozilla;
 
 /*
   ReadCRLF is a variant of NS_ReadLine from nsReadLine.h that deals
   with the carriage return/line feed requirements of HTTP requests.
 */
--- a/dom/media/encoder/MediaEncoder.cpp
+++ b/dom/media/encoder/MediaEncoder.cpp
@@ -61,25 +61,25 @@ MediaEncoder::NotifyRealtimeData(MediaSt
     }
   }
 }
 
 void
 MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                        TrackID aID,
                                        StreamTime aTrackOffset,
-                                       uint32_t aTrackEvents,
+                                       TrackEventCommand aTrackEvents,
                                        const MediaSegment& aQueuedMedia,
                                        MediaStream* aInputStream,
                                        TrackID aInputTrackID)
 {
   if (!mDirectConnected) {
     NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, aQueuedMedia);
   } else {
-    if (aTrackEvents != 0) {
+    if (aTrackEvents != TrackEventCommand::TRACK_EVENT_NONE) {
       // forward events (TRACK_EVENT_ENDED) but not the media
       if (aQueuedMedia.GetType() == MediaSegment::VIDEO) {
         VideoSegment segment;
         NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
       } else {
         AudioSegment segment;
         NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
       }
@@ -120,17 +120,17 @@ MediaEncoder::NotifyQueuedAudioData(Medi
         mSuspended = RECORD_NOT_SUSPENDED; // no video
       }
     }
   }
 }
 
 void
 MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph,
-                          MediaStreamListener::MediaStreamGraphEvent event)
+                          MediaStreamGraphEvent event)
 {
   // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
   LOG(LogLevel::Debug, ("NotifyRemoved in [MediaEncoder]."));
   if (mAudioEncoder) {
     mAudioEncoder->NotifyEvent(aGraph, event);
   }
   if (mVideoEncoder) {
     mVideoEncoder->NotifyEvent(aGraph, event);
--- a/dom/media/encoder/MediaEncoder.h
+++ b/dom/media/encoder/MediaEncoder.h
@@ -5,16 +5,17 @@
 
 #ifndef MediaEncoder_h_
 #define MediaEncoder_h_
 
 #include "mozilla/DebugOnly.h"
 #include "TrackEncoder.h"
 #include "ContainerWriter.h"
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "nsAutoPtr.h"
 #include "nsIMemoryReporter.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Atomics.h"
 
 namespace mozilla {
 
 /**
@@ -46,17 +47,17 @@ namespace mozilla {
  * 2) Dispatch the task GetEncodedData() to a worker thread.
  *
  * 3) To start encoding, add this component to its source stream.
  *    => sourceStream->AddListener(encoder);
  *
  * 4) To stop encoding, remove this component from its source stream.
  *    => sourceStream->RemoveListener(encoder);
  */
-class MediaEncoder : public MediaStreamDirectListener
+class MediaEncoder : public DirectMediaStreamListener
 {
 public :
   enum {
     ENCODE_METADDATA,
     ENCODE_TRACK,
     ENCODE_DONE,
     ENCODE_ERROR,
   };
@@ -120,17 +121,17 @@ public :
                           const MediaSegment& aRealtimeMedia) override;
 
   /**
    * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
    * track data in form of MediaSegment.
    */
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 StreamTime aTrackOffset,
-                                uint32_t aTrackEvents,
+                                TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override;
 
   /**
    * Notifed by the control loop of MediaStreamGraph; aQueueMedia is the audio
    * data in the form of an AudioSegment.
    */
@@ -139,17 +140,17 @@ public :
                              const AudioSegment& aQueuedMedia,
                              MediaStream* aInputStream,
                              TrackID aInputTrackID) override;
 
   /**
    * * Notified the stream is being removed.
    */
   void NotifyEvent(MediaStreamGraph* aGraph,
-                   MediaStreamListener::MediaStreamGraphEvent event) override;
+                   MediaStreamGraphEvent event) override;
 
   /**
    * Creates an encoder with a given MIME type. Returns null if we are unable
    * to create the encoder. For now, default aMIMEType to "audio/ogg" and use
    * Ogg+Opus if it is empty.
    */
   static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
                                                       uint32_t aAudioBitrate, uint32_t aVideoBitrate,
--- a/dom/media/encoder/TrackEncoder.cpp
+++ b/dom/media/encoder/TrackEncoder.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "TrackEncoder.h"
 #include "AudioChannelFormat.h"
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "mozilla/Logging.h"
 #include "VideoUtils.h"
 
 #undef LOG
 #ifdef MOZ_WIDGET_GONK
 #include <android/log.h>
 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args);
 #else
@@ -36,16 +37,24 @@ TrackEncoder::TrackEncoder()
   , mInitialized(false)
   , mEndOfStream(false)
   , mCanceled(false)
   , mInitCounter(0)
   , mNotInitDuration(0)
 {
 }
 
+void TrackEncoder::NotifyEvent(MediaStreamGraph* aGraph,
+                 MediaStreamGraphEvent event)
+{
+  if (event == MediaStreamGraphEvent::EVENT_REMOVED) {
+    NotifyEndOfStream();
+  }
+}
+
 void
 AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                             TrackID aID,
                                             StreamTime aTrackOffset,
                                             uint32_t aTrackEvents,
                                             const MediaSegment& aQueuedMedia)
 {
   if (mCanceled) {
@@ -86,17 +95,17 @@ AudioTrackEncoder::NotifyQueuedTrackChan
     }
   }
 
   // Append and consume this raw segment.
   AppendAudioSegment(audio);
 
 
   // The stream has stopped and reached the end of track.
-  if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
+  if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
     LOG("[AudioTrackEncoder]: Receive TRACK_EVENT_ENDED .");
     NotifyEndOfStream();
   }
 }
 
 void
 AudioTrackEncoder::NotifyEndOfStream()
 {
@@ -227,17 +236,17 @@ VideoTrackEncoder::NotifyQueuedTrackChan
       NotifyEndOfStream();
       return;
     }
   }
 
   AppendVideoSegment(video);
 
   // The stream has stopped and reached the end of track.
-  if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
+  if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
     LOG("[VideoTrackEncoder]: Receive TRACK_EVENT_ENDED .");
     NotifyEndOfStream();
   }
 
 }
 
 nsresult
 VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment)
--- a/dom/media/encoder/TrackEncoder.h
+++ b/dom/media/encoder/TrackEncoder.h
@@ -43,22 +43,17 @@ public:
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia) = 0;
 
   /**
    * Notified by the same callback of MediaEncoder when it has been removed from
    * MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
   void NotifyEvent(MediaStreamGraph* aGraph,
-                   MediaStreamListener::MediaStreamGraphEvent event)
-  {
-    if (event == MediaStreamListener::MediaStreamGraphEvent::EVENT_REMOVED) {
-      NotifyEndOfStream();
-    }
-  }
+                   MediaStreamGraphEvent event);
 
   /**
    * Creates and sets up meta data for a specific codec, called on the worker
    * thread.
    */
   virtual already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
 
   /**
--- a/dom/media/gmp/GMPContentChild.cpp
+++ b/dom/media/gmp/GMPContentChild.cpp
@@ -18,18 +18,16 @@ GMPContentChild::GMPContentChild(GMPChil
   : mGMPChild(aChild)
 {
   MOZ_COUNT_CTOR(GMPContentChild);
 }
 
 GMPContentChild::~GMPContentChild()
 {
   MOZ_COUNT_DTOR(GMPContentChild);
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 MessageLoop*
 GMPContentChild::GMPMessageLoop()
 {
   return mGMPChild->GMPMessageLoop();
 }
 
--- a/dom/media/gmp/GMPContentParent.cpp
+++ b/dom/media/gmp/GMPContentParent.cpp
@@ -39,18 +39,16 @@ GMPContentParent::GMPContentParent(GMPPa
   if (mParent) {
     SetDisplayName(mParent->GetDisplayName());
     SetPluginId(mParent->GetPluginId());
   }
 }
 
 GMPContentParent::~GMPContentParent()
 {
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 class ReleaseGMPContentParent : public Runnable
 {
 public:
   explicit ReleaseGMPContentParent(GMPContentParent* aToRelease)
     : mToRelease(aToRelease)
   {
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -261,18 +261,16 @@ GeckoMediaPluginServiceChild::RemoveGMPC
 }
 
 GMPServiceChild::GMPServiceChild()
 {
 }
 
 GMPServiceChild::~GMPServiceChild()
 {
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 PGMPContentParent*
 GMPServiceChild::AllocPGMPContentParent(Transport* aTransport,
                                         ProcessId aOtherPid)
 {
   MOZ_ASSERT(!mContentParents.GetWeak(aOtherPid));
 
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -1830,18 +1830,16 @@ GeckoMediaPluginServiceParent::GetById(u
       return do_AddRef(gmp);
     }
   }
   return nullptr;
 }
 
 GMPServiceParent::~GMPServiceParent()
 {
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 bool
 GMPServiceParent::RecvSelectGMP(const nsCString& aNodeId,
                                 const nsCString& aAPI,
                                 nsTArray<nsCString>&& aTags,
                                 uint32_t* aOutPluginId,
                                 nsresult* aOutRv)
--- a/dom/media/gtest/TestVideoTrackEncoder.cpp
+++ b/dom/media/gtest/TestVideoTrackEncoder.cpp
@@ -4,16 +4,17 @@
 
 #include "gtest/gtest.h"
 #include <algorithm>
 
 #include "mozilla/ArrayUtils.h"
 #include "VP8TrackEncoder.h"
 #include "ImageContainer.h"
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "WebMWriter.h" // TODO: it's weird to include muxer header to get the class definition of VP8 METADATA
 
 using ::testing::TestWithParam;
 using ::testing::Values;
 
 using namespace mozilla::layers;
 using namespace mozilla;
 
@@ -291,16 +292,16 @@ TEST(VP8VideoTrackEncoder, EncodeComplet
 {
   // Initiate VP8 encoder
   TestVP8TrackEncoder encoder;
   InitParam param = {true, 640, 480, 90000};
   encoder.TestInit(param);
 
   // track end notification.
   VideoSegment segment;
-  encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, MediaStreamListener::TRACK_EVENT_ENDED, segment);
+  encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, TrackEventCommand::TRACK_EVENT_ENDED, segment);
 
   // Pull Encoded Data back from encoder. Since we have sent
   // EOS to encoder, encoder.GetEncodedTrack should return
   // NS_OK immidiately.
   EncodedFrameContainer container;
   EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container)));
 }
--- a/dom/media/imagecapture/CaptureTask.h
+++ b/dom/media/imagecapture/CaptureTask.h
@@ -3,16 +3,17 @@
 /* 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 CAPTURETASK_H
 #define CAPTURETASK_H
 
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "PrincipalChangeObserver.h"
 
 namespace mozilla {
 
 namespace dom {
 class Blob;
 class ImageCapture;
 class MediaStreamTrack;
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/gfx/Point.h"
 #include "mozilla/SyncRunnable.h"
 
 #include "AudioSegment.h"
 #include "DecodedStream.h"
 #include "MediaData.h"
 #include "MediaQueue.h"
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "OutputStreamManager.h"
 #include "SharedBuffer.h"
 #include "VideoSegment.h"
 #include "VideoUtils.h"
 
 namespace mozilla {
 
 /*
@@ -25,17 +26,16 @@ namespace mozilla {
  * way to DecodedStreamGraphListener from DecodedStream.
  */
 struct PlaybackInfoInit {
   int64_t mStartTime;
   MediaInfo mInfo;
 };
 
 class DecodedStreamGraphListener : public MediaStreamListener {
-  typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
 public:
   DecodedStreamGraphListener(MediaStream* aStream,
                              MozPromiseHolder<GenericPromise>&& aPromise)
     : mMutex("DecodedStreamGraphListener::mMutex")
     , mStream(aStream)
     , mLastOutputTime(aStream->StreamTimeToMicroseconds(aStream->GetCurrentTime()))
   {
     mFinishPromise = Move(aPromise);
@@ -47,17 +47,17 @@ public:
     if (mStream) {
       mLastOutputTime = mStream->StreamTimeToMicroseconds(
           mStream->GraphTimeToStreamTime(aCurrentTime));
     }
   }
 
   void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
   {
-    if (event == EVENT_FINISHED) {
+    if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
       nsCOMPtr<nsIRunnable> event =
         NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
 
   void DoNotifyFinished()
   {
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -122,16 +122,17 @@ EXPORTS += [
     'MediaPrefs.h',
     'MediaQueue.h',
     'MediaRecorder.h',
     'MediaResource.h',
     'MediaResourceCallback.h',
     'MediaSegment.h',
     'MediaStatistics.h',
     'MediaStreamGraph.h',
+    'MediaStreamListener.h',
     'MediaTimer.h',
     'MediaTrack.h',
     'MediaTrackList.h',
     'MP3Decoder.h',
     'MP3Demuxer.h',
     'MP3FrameParser.h',
     'NextFrameSeekTask.h',
     'nsIDocumentActivity.h',
@@ -233,16 +234,17 @@ UNIFIED_SOURCES += [
     'MediaInfo.cpp',
     'MediaManager.cpp',
     'MediaPrefs.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaShutdownManager.cpp',
     'MediaStreamError.cpp',
     'MediaStreamGraph.cpp',
+    'MediaStreamListener.cpp',
     'MediaStreamTrack.cpp',
     'MediaTimer.cpp',
     'MediaTrack.cpp',
     'MediaTrackList.cpp',
     'MP3Decoder.cpp',
     'MP3Demuxer.cpp',
     'MP3FrameParser.cpp',
     'NextFrameSeekTask.cpp',
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioNodeStream.h"
 
 #include "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
 #include "AudioNodeEngine.h"
 #include "ThreeDPoint.h"
 #include "AudioChannelFormat.h"
 #include "AudioParamTimeline.h"
 #include "AudioContext.h"
 #include "nsMathUtils.h"
 
 using namespace mozilla::dom;
@@ -642,32 +643,32 @@ AudioNodeStream::AdvanceOutputSegment()
   }
 
   for (uint32_t j = 0; j < mListeners.Length(); ++j) {
     MediaStreamListener* l = mListeners[j];
     AudioChunk copyChunk = mLastChunks[0].AsAudioChunk();
     AudioSegment tmpSegment;
     tmpSegment.AppendAndConsumeChunk(&copyChunk);
     l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
-                                segment->GetDuration(), 0, tmpSegment);
+                                segment->GetDuration(), TrackEventCommand::TRACK_EVENT_NONE, tmpSegment);
   }
 }
 
 void
 AudioNodeStream::FinishOutput()
 {
   StreamTracks::Track* track = EnsureTrack(AUDIO_TRACK);
   track->SetEnded();
 
   for (uint32_t j = 0; j < mListeners.Length(); ++j) {
     MediaStreamListener* l = mListeners[j];
     AudioSegment emptySegment;
     l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
                                 track->GetSegment()->GetDuration(),
-                                MediaStreamListener::TRACK_EVENT_ENDED, emptySegment);
+                                TrackEventCommand::TRACK_EVENT_ENDED, emptySegment);
   }
 }
 
 void
 AudioNodeStream::AddInput(MediaInputPort* aPort)
 {
   ProcessedMediaStream::AddInput(aPort);
   AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
--- a/dom/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/dom/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -80,15 +80,15 @@ SpeechStreamListener::ConvertAndDispatch
   int16_t* to = static_cast<int16_t*>(samples->Data());
   ConvertAudioSamplesWithScale(aData, to, aDuration, aVolume);
 
   mRecognition->FeedAudioData(samples.forget(), aDuration, this, aTrackRate);
 }
 
 void
 SpeechStreamListener::NotifyEvent(MediaStreamGraph* aGraph,
-                                  MediaStreamListener::MediaStreamGraphEvent event)
+                                  MediaStreamGraphEvent event)
 {
   // TODO dispatch SpeechEnd event so services can be informed
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/recognition/SpeechStreamListener.h
+++ b/dom/media/webspeech/recognition/SpeechStreamListener.h
@@ -3,16 +3,17 @@
 /* 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 mozilla_dom_SpeechStreamListener_h
 #define mozilla_dom_SpeechStreamListener_h
 
 #include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
 #include "AudioSegment.h"
 
 namespace mozilla {
 
 class AudioSegment;
 
 namespace dom {
 
@@ -26,17 +27,17 @@ public:
 
   void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
                              StreamTime aTrackOffset,
                              const AudioSegment& aQueuedMedia,
                              MediaStream* aInputStream,
                              TrackID aInputTrackID) override;
 
   void NotifyEvent(MediaStreamGraph* aGraph,
-                   MediaStreamListener::MediaStreamGraphEvent event) override;
+                   MediaStreamGraphEvent event) override;
 
 private:
   template<typename SampleFormatType>
   void ConvertAndDispatchAudioChunk(int aDuration, float aVolume, SampleFormatType* aData, TrackRate aTrackRate);
   RefPtr<SpeechRecognition> mRecognition;
 };
 
 } // namespace dom
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -2,18 +2,20 @@
 /* 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 "AudioChannelAgent.h"
 #include "AudioChannelService.h"
 #include "AudioSegment.h"
+#include "MediaStreamListener.h"
 #include "nsSpeechTask.h"
 #include "nsSynthVoiceRegistry.h"
+#include "SharedBuffer.h"
 #include "SpeechSynthesis.h"
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with nsSpeechTask::GetCurrentTime().
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
@@ -48,34 +50,34 @@ public:
   {
     if (mSpeechTask) {
       mSpeechTask->DispatchEndInner(mSpeechTask->GetCurrentTime(),
                                     mSpeechTask->GetCurrentCharOffset());
     }
   }
 
   void NotifyEvent(MediaStreamGraph* aGraph,
-                   MediaStreamListener::MediaStreamGraphEvent event) override
+                   MediaStreamGraphEvent event) override
   {
     switch (event) {
-      case EVENT_FINISHED:
+      case MediaStreamGraphEvent::EVENT_FINISHED:
         {
           if (!mStarted) {
             mStarted = true;
             nsCOMPtr<nsIRunnable> startRunnable =
               NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
             aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget());
           }
 
           nsCOMPtr<nsIRunnable> endRunnable =
             NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
           aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget());
         }
         break;
-      case EVENT_REMOVED:
+      case MediaStreamGraphEvent::EVENT_REMOVED:
         mSpeechTask = nullptr;
         // Dereference MediaStream to destroy safety
         mStream = nullptr;
         break;
       default:
         break;
     }
   }
--- a/dom/media/webspeech/synth/nsSpeechTask.h
+++ b/dom/media/webspeech/synth/nsSpeechTask.h
@@ -8,16 +8,19 @@
 #define mozilla_dom_nsSpeechTask_h
 
 #include "MediaStreamGraph.h"
 #include "SpeechSynthesisUtterance.h"
 #include "nsIAudioChannelAgent.h"
 #include "nsISpeechService.h"
 
 namespace mozilla {
+
+class SharedBuffer;
+
 namespace dom {
 
 class SpeechSynthesisUtterance;
 class SpeechSynthesis;
 class SynthStreamListener;
 
 class nsSpeechTask : public nsISpeechTask
                    , public nsIAudioChannelAgentCallback
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -57,16 +57,17 @@ DIRS += [
     'events',
     'fetch',
     'filehandle',
     'filesystem',
     'flyweb',
     'fmradio',
     'gamepad',
     'geolocation',
+    'grid',
     'html',
     'icc',
     'inputport',
     'json',
     'jsurl',
     'asmjscache',
     'mathml',
     'media',
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -103,17 +103,16 @@ using mozilla::gfx::SharedDIB;
 // from Chromium's web plugin delegate src. See 'flash msg throttling
 // helpers' section for details.
 const int kFlashWMUSERMessageThrottleDelayMs = 5;
 
 static const TCHAR kPluginIgnoreSubclassProperty[] = TEXT("PluginIgnoreSubclassProperty");
 
 #elif defined(XP_MACOSX)
 #include <ApplicationServices/ApplicationServices.h>
-#include "nsCocoaFeatures.h"
 #include "PluginUtilsOSX.h"
 #endif // defined(XP_MACOSX)
 
 /**
  * We can't use gfxPlatform::CreateDrawTargetForSurface() because calling
  * gfxPlatform::GetPlatform() instantiates the prefs service, and that's not
  * allowed from processes other than the main process. So we have our own
  * version here.
@@ -3529,20 +3528,18 @@ PluginInstanceChild::EnsureCurrentBuffer
 
     return true;
 #elif defined(XP_MACOSX)
 
     if (!mDoubleBufferCARenderer.HasCALayer()) {
         void *caLayer = nullptr;
         if (mDrawingModel == NPDrawingModelCoreGraphics) {
             if (!mCGLayer) {
-                bool avoidCGCrashes = !nsCocoaFeatures::OnMountainLionOrLater() &&
-                  (GetQuirks() & QUIRK_FLASH_AVOID_CGMODE_CRASHES);
-                caLayer = mozilla::plugins::PluginUtilsOSX::GetCGLayer(CallCGDraw, this,
-                                                                       avoidCGCrashes,
+                caLayer = mozilla::plugins::PluginUtilsOSX::GetCGLayer(CallCGDraw,
+                                                                       this, false,
                                                                        mContentsScaleFactor);
 
                 if (!caLayer) {
                     PLUGIN_LOG_DEBUG(("GetCGLayer failed."));
                     return false;
                 }
             }
             mCGLayer = caLayer;
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -149,25 +149,16 @@ PluginModuleChild::PluginModuleChild(boo
     if (aIsChrome) {
       mac_plugin_interposing::child::SetUpCocoaInterposing();
     }
 #endif
 }
 
 PluginModuleChild::~PluginModuleChild()
 {
-    if (mTransport) {
-        // For some reason IPDL doesn't automatically delete the channel for a
-        // bridged protocol (bug 1090570). So we have to do it ourselves. This
-        // code is only invoked for PluginModuleChild instances created via
-        // bridging; otherwise mTransport is null.
-        RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
-        XRE_GetIOMessageLoop()->PostTask(task.forget());
-    }
-
     if (mIsChrome) {
         MOZ_ASSERT(gChromeInstance == this);
 
         // We don't unload the plugin library in case it uses atexit handlers or
         // other similar hooks.
 
         DeinitGraphics();
         PluginScriptableObjectChild::ClearIdentifiers();
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -718,20 +718,16 @@ PluginModuleParent::~PluginModuleParent(
 PluginModuleContentParent::PluginModuleContentParent(bool aAllowAsyncInit)
     : PluginModuleParent(false, aAllowAsyncInit)
 {
     Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
 PluginModuleContentParent::~PluginModuleContentParent()
 {
-    RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-    XRE_GetIOMessageLoop()->PostTask(task.forget());
-                                     
-
     Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
 bool PluginModuleChromeParent::sInstantiated = false;
 
 PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
                                                    uint32_t aPluginId,
                                                    int32_t aSandboxLevel,
--- a/dom/tests/mochitest/geolocation/mochitest.ini
+++ b/dom/tests/mochitest/geolocation/mochitest.ini
@@ -31,20 +31,16 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_manyCurrentSerial.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_manyWatchConcurrent.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_manyWatchSerial.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_manyWindows.html]
 skip-if = buildapp == 'b2g'
-[test_mozsettings.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android'
-[test_mozsettingsWatch.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_optional_api_params.html]
 skip-if = buildapp == 'b2g'
 [test_shutdown.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_timeoutCurrent.html]
 [test_timerRestartWatch.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_windowClose.html]
deleted file mode 100644
--- a/dom/tests/mochitest/geolocation/test_mozsettings.html
+++ /dev/null
@@ -1,92 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=478911
--->
-<head>
-  <title>Test for getCurrentPosition </title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="geolocation_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=777594">Mozilla Bug 777594</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-SimpleTest.waitForExplicitFinish();
-SimpleTest.requestFlakyTimeout("untriaged");
-
-var timeToWaitMs = 1000;
-
-resume_geolocationProvider(function() {
-  force_prompt(true, test1);
-});
-
-SpecialPowers.importInMainProcess("resource://gre/modules/SettingsRequestManager.jsm");
-
-function test1() {
-  //This pushPermissions call is after pushPrefEnv call and pushPrefEnv calls follow after this
-  SpecialPowers.pushPermissions([{'type': 'settings-read', 'allow': true, 'context': document},
-                                 {'type': 'settings-write', 'allow': true, 'context': document},
-                                 {'type': 'settings-api-write', 'allow': true, 'context': document},
-                                 {'type': 'settings-api-read', 'allow': true, 'context': document}
-  ], test2);
-}
-
-function test2() {
-  ok(navigator.geolocation, "get geolocation object");
-
-  toggleGeolocationSetting(false, function() {
-      ok(true, "turned off geolocation via mozSettings");
-      setTimeout(function() {
-          navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOff,
-                                                   failureCallbackAfterMozsettingOff);
-        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
-  });
-}
-
-function successCallbackAfterMozsettingOff(position) {
-  ok(false, "Success callback should not have been called after setting geolocation.enabled to false.");
-
-  toggleGeolocationSetting(true, function() {
-      ok(true, "turned on geolocation via mozSettings");
-      setTimeout(function() {
-         navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOn,
-                                                  failureCallbackAfterMozsettingOn);
-        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
-    });
-}
-
-function failureCallbackAfterMozsettingOff(error) {
-  ok(true, "Geolocation didn't work after setting geolocation.enabled to false.");
-
-  toggleGeolocationSetting(true, function() {
-      ok(true, "turned on geolocation via mozSettings");
-      setTimeout(function() {
-         navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOn,
-                                                  failureCallbackAfterMozsettingOn);
-        }, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
-    });
-}
-
-function successCallbackAfterMozsettingOn(position) {
-  ok(true, "Geolocation worked after setting geolocation.enabled to true.");
-  SimpleTest.finish();
-}
-
-function failureCallbackAfterMozsettingOn(error) {
-  ok(false, "Geolocation didn't work after setting geolocation.enabled to true.");
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>
-
deleted file mode 100644
--- a/dom/tests/mochitest/geolocation/test_mozsettingsWatch.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=478911
--->
-<head>
-  <title>Test for getCurrentPosition </title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="geolocation_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=777594">Mozilla Bug 777594</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-SimpleTest.waitForExplicitFinish();
-SimpleTest.requestFlakyTimeout("untriaged");
-
-resume_geolocationProvider(function() {
-  force_prompt(true, test1);
-});
-
-SpecialPowers.importInMainProcess("resource://gre/modules/SettingsRequestManager.jsm");
-
-function test1() {
-  //This pushPermissions call is after pushPrefEnv call and pushPrefEnv calls follow after this
-  SpecialPowers.pushPermissions([{'type': 'settings-read', 'allow': true, 'context': document},
-                                 {'type': 'settings-write', 'allow': true, 'context': document},
-                                 {'type': 'settings-api-write', 'allow': true, 'context': document},
-                                 {'type': 'settings-api-read', 'allow': true, 'context': document}
-  ], test2);
-}
-
-var watchId;
-function test2() {
-  ok(navigator.geolocation, "get geolocation object");
-
-  toggleGeolocationSetting(false, function() {
-      ok(true, "turned off geolocation via mozSettings");
-      setTimeout(function() {
-          watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOff,
-                                                        failureCallbackAfterMozsettingOff);
-      }, 500); // need to wait a bit for all of these async callbacks to finish
-  });
-}
-
-function successCallbackAfterMozsettingOff(position) {
-  ok(false, "Success callback should not have been called after setting geolocation.enabled to false.");
-
-  navigator.geolocation.clearWatch(watchId);
-  toggleGeolocationSetting(true, function() {
-      ok(true, "turned on geolocation via mozSettings");
-      setTimeout(function() {
-          watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOn,
-                                                        failureCallbackAfterMozsettingOn);
-        }, 500); // need to wait a bit for all of these async callbacks to finish
-    });
-}
-
-function failureCallbackAfterMozsettingOff(error) {
-  ok(true, "Geolocation didn't work after setting geolocation.enabled to false.");
-
-  navigator.geolocation.clearWatch(watchId);
-  toggleGeolocationSetting(true, function() {
-      ok(true, "turned on geolocation via mozSettings");
-      setTimeout(function() {
-          watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOn,
-                                                        failureCallbackAfterMozsettingOn);
-        }, 500); // need to wait a bit for all of these async callbacks to finish
-    });
-}
-
-function successCallbackAfterMozsettingOn(position) {
-  ok(true, "Geolocation worked after setting geolocation.enabled to true.");
-
-  navigator.geolocation.clearWatch(watchId);
-  SimpleTest.finish();
-}
-
-function failureCallbackAfterMozsettingOn(error) {
-  ok(false, "Geolocation didn't work after setting geolocation.enabled to true.");
-
-  navigator.geolocation.clearWatch(watchId);
-  SimpleTest.finish();
-}
-
-</script>
-</pre>
-</body>
-</html>
-
--- a/dom/webidl/Client.webidl
+++ b/dom/webidl/Client.webidl
@@ -20,16 +20,19 @@ interface Client {
 
 [Exposed=ServiceWorker]
 interface WindowClient : Client {
   readonly attribute VisibilityState visibilityState;
   readonly attribute boolean focused;
 
   [Throws, NewObject]
   Promise<WindowClient> focus();
+
+  [Throws, NewObject]
+  Promise<WindowClient> navigate(USVString url);
 };
 
 enum FrameType {
   "auxiliary",
   "top-level",
   "nested",
   "none"
 };
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -150,16 +150,26 @@ interface Element : Node {
   Attr? setAttributeNodeNS(Attr newAttr);
 
   [ChromeOnly]
   /**
    * Scrolls the element by (dx, dy) CSS pixels without doing any
    * layout flushing.
    */
   boolean scrollByNoFlush(long dx, long dy);
+
+  // Support reporting of Grid properties
+
+  /**
+   * If this element has a display:grid or display:inline-grid style,
+   * this property returns an object with computed values for grid
+   * tracks and lines.
+   */
+  [ChromeOnly, Pure]
+  sequence<Grid> getGridFragments();
 };
 
 // http://dev.w3.org/csswg/cssom-view/
 enum ScrollLogicalPosition { "start", "end" };
 dictionary ScrollIntoViewOptions : ScrollOptions {
   ScrollLogicalPosition block = "start";
 };
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Grid.webidl
@@ -0,0 +1,54 @@
+/* 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/.
+ */
+
+[ChromeOnly]
+interface Grid
+{
+  readonly attribute GridDimension rows;
+  readonly attribute GridDimension cols;
+};
+
+[ChromeOnly]
+interface GridDimension
+{
+  readonly attribute GridLines lines;
+  readonly attribute GridTracks tracks;
+};
+
+[ChromeOnly]
+interface GridLines
+{
+  readonly attribute unsigned long length;
+  getter GridLine? item(unsigned long index);
+};
+
+[ChromeOnly]
+interface GridLine
+{
+  readonly attribute double start;
+  readonly attribute double breadth;
+  readonly attribute unsigned long number;
+  [Cached, Pure]
+  readonly attribute sequence<DOMString> names;
+};
+
+[ChromeOnly]
+interface GridTracks
+{
+  readonly attribute unsigned long length;
+  getter GridTrack? item(unsigned long index);
+};
+
+enum GridTrackType { "explicit", "implicit" };
+enum GridTrackState { "static", "repeat" };
+
+[ChromeOnly]
+interface GridTrack
+{
+  readonly attribute double start;
+  readonly attribute double breadth;
+  readonly attribute GridTrackType type;
+  readonly attribute GridTrackState state;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -171,16 +171,17 @@ WEBIDL_FILES = [
     'FontFaceSet.webidl',
     'FontFaceSource.webidl',
     'FormData.webidl',
     'Function.webidl',
     'GainNode.webidl',
     'Geolocation.webidl',
     'GeometryUtils.webidl',
     'GetUserMediaRequest.webidl',
+    'Grid.webidl',
     'HDMIInputPort.webidl',
     'Headers.webidl',
     'HeapSnapshot.webidl',
     'History.webidl',
     'HTMLAllCollection.webidl',
     'HTMLAnchorElement.webidl',
     'HTMLAppletElement.webidl',
     'HTMLAreaElement.webidl',
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -256,17 +256,17 @@ GenerateSharedWorkerKey(const nsACString
     }
   }
 
   aKey.Append('|');
   aKey.Append(aScriptSpec);
 }
 
 void
-LoadRuntimeOptions(const char* aPrefName, void* /* aClosure */)
+LoadContextOptions(const char* aPrefName, void* /* aClosure */)
 {
   AssertIsOnMainThread();
 
   RuntimeService* rts = RuntimeService::GetService();
   if (!rts) {
     // May be shutting down, just bail.
     return;
   }
@@ -286,33 +286,33 @@ LoadRuntimeOptions(const char* aPrefName
 
 #ifdef JS_GC_ZEAL
   if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) ||
       prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) {
     return;
   }
 #endif
 
-  // Runtime options.
-  JS::RuntimeOptions runtimeOptions;
-  runtimeOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
+  // Context options.
+  JS::ContextOptions contextOptions;
+  contextOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
                 .setWasm(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm")))
                 .setThrowOnAsmJSValidationFailure(GetWorkerPref<bool>(
                       NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure")))
                 .setBaseline(GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit")))
                 .setIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion")))
                 .setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
                 .setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
                 .setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
                 .setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")));
 
-  RuntimeService::SetDefaultRuntimeOptions(runtimeOptions);
+  RuntimeService::SetDefaultContextOptions(contextOptions);
 
   if (rts) {
-    rts->UpdateAllWorkerRuntimeOptions();
+    rts->UpdateAllWorkerContextOptions();
   }
 }
 
 #ifdef JS_GC_ZEAL
 void
 LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */)
 {
   AssertIsOnMainThread();
@@ -753,17 +753,17 @@ InitJSContextForWorker(WorkerPrivate* aW
   aWorkerPrivate->AssertIsOnWorkerThread();
   NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
 
   JSSettings settings;
   aWorkerPrivate->CopyJSSettings(settings);
 
   JSContext* workerCx = JS_GetContext(aRuntime);
 
-  JS::RuntimeOptionsRef(aRuntime) = settings.runtimeOptions;
+  JS::ContextOptionsRef(workerCx) = settings.contextOptions;
 
   JSSettings::JSGCSettingsArray& gcSettings = settings.gcSettings;
 
   // This is the real place where we set the max memory for the runtime.
   for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) {
     const JSSettings::JSGCSetting& setting = gcSettings[index];
     if (setting.IsSet()) {
       NS_ASSERTION(setting.value, "Can't handle 0 values!");
@@ -788,19 +788,19 @@ InitJSContextForWorker(WorkerPrivate* aW
   };
   JS::SetAsmJSCacheOps(workerCx, &asmJSCacheOps);
 
   if (!JS::InitSelfHostedCode(workerCx)) {
     NS_WARNING("Could not init self-hosted code!");
     return nullptr;
   }
 
-  JS_SetInterruptCallback(aRuntime, InterruptCallback);
-
-  js::SetCTypesActivityCallback(aRuntime, CTypesActivityCallback);
+  JS_SetInterruptCallback(workerCx, InterruptCallback);
+
+  js::SetCTypesActivityCallback(workerCx, CTypesActivityCallback);
 
 #ifdef JS_GC_ZEAL
   JS_SetGCZeal(workerCx, settings.gcZeal, settings.gcZealFrequency);
 #endif
 
   return workerCx;
 }
 
@@ -1706,17 +1706,17 @@ RuntimeService::Init()
     RefPtr<BackgroundChildCallback> callback = new BackgroundChildCallback();
     if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) {
       MOZ_CRASH("Unable to connect PBackground actor for the main thread!");
     }
   }
 
   // Initialize JSSettings.
   if (!sDefaultJSSettings.gcSettings[0].IsSet()) {
-    sDefaultJSSettings.runtimeOptions = JS::RuntimeOptions();
+    sDefaultJSSettings.contextOptions = JS::ContextOptions();
     sDefaultJSSettings.chrome.maxScriptRuntime = -1;
     sDefaultJSSettings.chrome.compartmentOptions.behaviors().setVersion(JSVERSION_LATEST);
     sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
 #ifdef JS_GC_ZEAL
     sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
     sDefaultJSSettings.gcZeal = 0;
 #endif
     SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
@@ -1784,20 +1784,20 @@ RuntimeService::Init()
                   callback,                                                   \
                   name,                                                       \
                   nullptr)) ||
 #include "WorkerPrefs.h"
 #undef WORKER_SIMPLE_PREF
 #undef WORKER_PREF
 
       NS_FAILED(Preferences::RegisterCallbackAndCall(
-                                                   LoadRuntimeOptions,
+                                                   LoadContextOptions,
                                                    PREF_WORKERS_OPTIONS_PREFIX,
                                                    nullptr)) ||
-      NS_FAILED(Preferences::RegisterCallback(LoadRuntimeOptions,
+      NS_FAILED(Preferences::RegisterCallback(LoadContextOptions,
                                               PREF_JS_OPTIONS_PREFIX,
                                               nullptr))) {
     NS_WARNING("Failed to register pref callbacks!");
   }
 
   MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!");
   gRuntimeServiceDuringInit = false;
 
@@ -1926,20 +1926,20 @@ RuntimeService::Cleanup()
         }
       }
     }
   }
 
   NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!");
 
   if (mObserved) {
-    if (NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeOptions,
+    if (NS_FAILED(Preferences::UnregisterCallback(LoadContextOptions,
                                                   PREF_JS_OPTIONS_PREFIX,
                                                   nullptr)) ||
-        NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeOptions,
+        NS_FAILED(Preferences::UnregisterCallback(LoadContextOptions,
                                                   PREF_WORKERS_OPTIONS_PREFIX,
                                                   nullptr)) ||
 
 #define WORKER_SIMPLE_PREF(name, getter, NAME)                                \
       NS_FAILED(Preferences::UnregisterCallback(                              \
                   WorkerPrefChanged,                                          \
                   name,                                                       \
                   reinterpret_cast<void*>(WORKERPREF_##NAME))) ||
@@ -2282,19 +2282,19 @@ RuntimeService::NoteIdleThread(WorkerThr
       mIdleThreadTimer->InitWithFuncCallback(ShutdownIdleThreads,
                                              nullptr,
                                              IDLE_THREAD_TIMEOUT_SEC * 1000,
                                              nsITimer::TYPE_ONE_SHOT));
   }
 }
 
 void
-RuntimeService::UpdateAllWorkerRuntimeOptions()
+RuntimeService::UpdateAllWorkerContextOptions()
 {
-  BROADCAST_ALL_WORKERS(UpdateRuntimeOptions, sDefaultJSSettings.runtimeOptions);
+  BROADCAST_ALL_WORKERS(UpdateContextOptions, sDefaultJSSettings.contextOptions);
 }
 
 void
 RuntimeService::UpdateAppNameOverridePreference(const nsAString& aValue)
 {
   AssertIsOnMainThread();
   mNavigatorProperties.mAppNameOverridden = aValue;
 }
@@ -2578,17 +2578,17 @@ WorkerThreadPrimaryRunnable::Run()
     // strong reference to the debugger global scope. These runnables are not
     // visible to the cycle collector, so we need to make sure to clear the
     // debugger event queue before we try to destroy the context. If we don't,
     // the garbage collector will crash.
     mWorkerPrivate->ClearDebuggerEventQueue();
 
     // Perform a full GC. This will collect the main worker global and CC,
     // which should break all cycles that touch JS.
-    JS_GC(JS_GetRuntime(cx));
+    JS_GC(cx);
 
     // Before shutting down the cycle collector we need to do one more pass
     // through the event loop to clean up any C++ objects that need deferred
     // cleanup.
     mWorkerPrivate->ClearMainEventQueue(WorkerPrivate::WorkerRan);
 
     // Now WorkerJSRuntime goes out of scope and its destructor will shut
     // down the cycle collector. This breaks any remaining cycles and collects
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -163,33 +163,33 @@ public:
   static void
   GetDefaultPreferences(bool aPreferences[WORKERPREF_COUNT])
   {
     AssertIsOnMainThread();
     memcpy(aPreferences, sDefaultPreferences, WORKERPREF_COUNT * sizeof(bool));
   }
 
   static void
-  SetDefaultRuntimeOptions(const JS::RuntimeOptions& aRuntimeOptions)
+  SetDefaultContextOptions(const JS::ContextOptions& aContextOptions)
   {
     AssertIsOnMainThread();
-    sDefaultJSSettings.runtimeOptions = aRuntimeOptions;
+    sDefaultJSSettings.contextOptions = aContextOptions;
   }
 
   void
   UpdateAppNameOverridePreference(const nsAString& aValue);
 
   void
   UpdateAppVersionOverridePreference(const nsAString& aValue);
 
   void
   UpdatePlatformOverridePreference(const nsAString& aValue);
 
   void
-  UpdateAllWorkerRuntimeOptions();
+  UpdateAllWorkerContextOptions();
 
   void
   UpdateAllWorkerLanguages(const nsTArray<nsString>& aLanguages);
 
   void
   UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue);
 
   static void
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -2,23 +2,38 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "ServiceWorkerWindowClient.h"
 
+#include "js/Value.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/dom/ClientBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/UniquePtr.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellLoadInfo.h"
+#include "nsIDocument.h"
+#include "nsIGlobalObject.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIWebNavigation.h"
+#include "nsIWebProgress.h"
+#include "nsIWebProgressListener.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+#include "ServiceWorker.h"
+#include "ServiceWorkerInfo.h"
+#include "ServiceWorkerManager.h"
 #include "WorkerPrivate.h"
 #include "WorkerScope.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::workers;
 
 using mozilla::UniquePtr;
@@ -26,48 +41,65 @@ using mozilla::UniquePtr;
 JSObject*
 ServiceWorkerWindowClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return WindowClientBinding::Wrap(aCx, this, aGivenProto);
 }
 
 namespace {
 
-// Passing a null clientInfo will reject the promise with InvalidAccessError.
 class ResolveOrRejectPromiseRunnable final : public WorkerRunnable
 {
   RefPtr<PromiseWorkerProxy> mPromiseProxy;
   UniquePtr<ServiceWorkerClientInfo> mClientInfo;
+  nsresult mRv;
 
 public:
-  ResolveOrRejectPromiseRunnable(WorkerPrivate* aWorkerPrivate,
-                                 PromiseWorkerProxy* aPromiseProxy,
-                                 UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+  // Passing a null clientInfo will resolve the promise with a null value.
+  ResolveOrRejectPromiseRunnable(
+    WorkerPrivate* aWorkerPrivate, PromiseWorkerProxy* aPromiseProxy,
+    UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
     : WorkerRunnable(aWorkerPrivate)
     , mPromiseProxy(aPromiseProxy)
     , mClientInfo(Move(aClientInfo))
+    , mRv(NS_OK)
   {
     AssertIsOnMainThread();
   }
 
+  // Reject the promise with passed nsresult.
+  ResolveOrRejectPromiseRunnable(WorkerPrivate* aWorkerPrivate,
+                                 PromiseWorkerProxy* aPromiseProxy,
+                                 nsresult aRv)
+    : WorkerRunnable(aWorkerPrivate)
+    , mPromiseProxy(aPromiseProxy)
+    , mClientInfo(nullptr)
+    , mRv(aRv)
+  {
+    MOZ_ASSERT(NS_FAILED(aRv));
+    AssertIsOnMainThread();
+  }
+
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
     MOZ_ASSERT(promise);
 
-    if (mClientInfo) {
+    if (NS_WARN_IF(NS_FAILED(mRv))) {
+      promise->MaybeReject(mRv);
+    } else if (mClientInfo) {
       RefPtr<ServiceWorkerWindowClient> client =
         new ServiceWorkerWindowClient(promise->GetParentObject(), *mClientInfo);
       promise->MaybeResolve(client);
     } else {
-      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+      promise->MaybeResolve(JS::NullHandleValue);
     }
 
     // Release the reference on the worker thread.
     mPromiseProxy->CleanUp();
 
     return true;
   }
 };
@@ -109,19 +141,25 @@ private:
   DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
   {
     AssertIsOnMainThread();
     MutexAutoLock lock(mPromiseProxy->Lock());
     if (mPromiseProxy->CleanedUp()) {
       return;
     }
 
-    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
-      new ResolveOrRejectPromiseRunnable(mPromiseProxy->GetWorkerPrivate(),
-                                         mPromiseProxy, Move(aClientInfo));
+    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
+    if (aClientInfo) {
+      resolveRunnable = new ResolveOrRejectPromiseRunnable(
+        mPromiseProxy->GetWorkerPrivate(), mPromiseProxy, Move(aClientInfo));
+    } else {
+      resolveRunnable = new ResolveOrRejectPromiseRunnable(
+        mPromiseProxy->GetWorkerPrivate(), mPromiseProxy,
+        NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    }
 
     resolveRunnable->Dispatch();
   }
 };
 
 } // namespace
 
 already_AddRefed<Promise>
@@ -139,20 +177,349 @@ ServiceWorkerWindowClient::Focus(ErrorRe
     return nullptr;
   }
 
   if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
     RefPtr<PromiseWorkerProxy> promiseProxy =
       PromiseWorkerProxy::Create(workerPrivate, promise);
     if (promiseProxy) {
       RefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
-                                                                promiseProxy);
+                                                              promiseProxy);
       MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
     } else {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     }
 
   } else {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 
   return promise.forget();
 }
+
+class WebProgressListener final : public nsIWebProgressListener,
+                                  public nsSupportsWeakReference
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener,
+                                           nsIWebProgressListener)
+
+  WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
+                      nsPIDOMWindowOuter* aWindow, nsIURI* aBaseURI)
+    : mPromiseProxy(aPromiseProxy)
+    , mWindow(aWindow)
+    , mBaseURI(aBaseURI)
+  {
+    MOZ_ASSERT(aPromiseProxy);
+    MOZ_ASSERT(aWindow);
+    MOZ_ASSERT(aWindow->IsOuterWindow());
+    MOZ_ASSERT(aBaseURI);
+    AssertIsOnMainThread();
+
+    mPromiseProxy->StoreISupports(static_cast<nsIWebProgressListener*>(this));
+  }
+
+  NS_IMETHOD
+  OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+                uint32_t aStateFlags, nsresult aStatus) override
+  {
+    if (!(aStateFlags & STATE_IS_DOCUMENT) ||
+        !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
+      return NS_OK;
+    }
+
+    aWebProgress->RemoveProgressListener(this);
+
+    WorkerPrivate* workerPrivate;
+
+    {
+      MutexAutoLock lock(mPromiseProxy->Lock());
+      if (mPromiseProxy->CleanedUp()) {
+        return NS_OK;
+      }
+
+      workerPrivate = mPromiseProxy->GetWorkerPrivate();
+    }
+
+    nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
+
+    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
+    UniquePtr<ServiceWorkerClientInfo> clientInfo;
+    if (!doc) {
+      resolveRunnable = new ResolveOrRejectPromiseRunnable(
+        workerPrivate, mPromiseProxy, NS_ERROR_TYPE_ERR);
+      resolveRunnable->Dispatch();
+
+      return NS_OK;
+    }
+
+    // Check same origin.
+    nsCOMPtr<nsIScriptSecurityManager> securityManager =
+      nsContentUtils::GetSecurityManager();
+    nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
+                                                      mBaseURI, false);
+
+    if (NS_SUCCEEDED(rv)) {
+      nsContentUtils::DispatchFocusChromeEvent(mWindow->GetOuterWindow());
+      clientInfo.reset(new ServiceWorkerClientInfo(doc));
+    }
+
+    resolveRunnable = new ResolveOrRejectPromiseRunnable(
+      workerPrivate, mPromiseProxy, Move(clientInfo));
+    resolveRunnable->Dispatch();
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+                   int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
+                   int32_t aCurTotalProgress,
+                   int32_t aMaxTotalProgress) override
+  {
+    MOZ_CRASH("Unexpected notification.");
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+                   nsIURI* aLocation, uint32_t aFlags) override
+  {
+    MOZ_CRASH("Unexpected notification.");
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+                 nsresult aStatus, const char16_t* aMessage) override
+  {
+    MOZ_CRASH("Unexpected notification.");
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+                   uint32_t aState) override
+  {
+    MOZ_CRASH("Unexpected notification.");
+    return NS_OK;
+  }
+
+private:
+  ~WebProgressListener() {}
+
+  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+  nsCOMPtr<nsPIDOMWindowOuter> mWindow;
+  nsCOMPtr<nsIURI> mBaseURI;
+};
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
+NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
+                         mWindow)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
+  NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+class ClientNavigateRunnable final : public Runnable
+{
+  uint64_t mWindowId;
+  nsString mUrl;
+  nsCString mBaseUrl;
+  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+  WorkerPrivate* mWorkerPrivate;
+
+public:
+  ClientNavigateRunnable(uint64_t aWindowId, const nsAString& aUrl,
+                         PromiseWorkerProxy* aPromiseProxy)
+    : mWindowId(aWindowId)
+    , mUrl(aUrl)
+    , mPromiseProxy(aPromiseProxy)
+  {
+    MOZ_ASSERT(aPromiseProxy);
+    MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
+    aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsIPrincipal> principal;
+
+    {
+      MutexAutoLock lock(mPromiseProxy->Lock());
+      if (mPromiseProxy->CleanedUp()) {
+        return NS_OK;
+      }
+
+      mWorkerPrivate = mPromiseProxy->GetWorkerPrivate();
+      WorkerPrivate::LocationInfo& info = mWorkerPrivate->GetLocationInfo();
+      mBaseUrl = info.mHref;
+      principal = mWorkerPrivate->GetPrincipal();
+    }
+
+    nsCOMPtr<nsIURI> baseUrl;
+    nsCOMPtr<nsIURI> url;
+    nsresult rv = ParseUrl(getter_AddRefs(baseUrl), getter_AddRefs(url));
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return RejectPromise(NS_ERROR_TYPE_ERR);
+    }
+
+    rv = principal->CheckMayLoad(url, true, false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return RejectPromise(rv);
+    }
+
+    nsGlobalWindow* window;
+    rv = Navigate(url, principal, &window);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return RejectPromise(rv);
+    }
+
+    nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+    nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
+    if (NS_WARN_IF(!webProgress)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIWebProgressListener> listener =
+      new WebProgressListener(mPromiseProxy, window->GetOuterWindow(), baseUrl);
+
+    rv = webProgress->AddProgressListener(
+      listener, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return RejectPromise(rv);
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsresult
+  RejectPromise(nsresult aRv)
+  {
+    MOZ_ASSERT(mWorkerPrivate);
+    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
+      new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy, aRv);
+
+    resolveRunnable->Dispatch();
+    return NS_OK;
+  }
+
+  nsresult
+  ResolvePromise(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
+  {
+    MOZ_ASSERT(mWorkerPrivate);
+    RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
+      new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy,
+                                         Move(aClientInfo));
+
+    resolveRunnable->Dispatch();
+    return NS_OK;
+  }
+
+  nsresult
+  ParseUrl(nsIURI** aBaseUrl, nsIURI** aUrl)
+  {
+    MOZ_ASSERT(aBaseUrl);
+    MOZ_ASSERT(aUrl);
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsIURI> baseUrl;
+    nsresult rv = NS_NewURI(getter_AddRefs(baseUrl), mBaseUrl);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIURI> url;
+    rv = NS_NewURI(getter_AddRefs(url), mUrl, nullptr, baseUrl);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    baseUrl.forget(aBaseUrl);
+    url.forget(aUrl);
+
+    return NS_OK;
+  }
+
+  nsresult
+  Navigate(nsIURI* aUrl, nsIPrincipal* aPrincipal, nsGlobalWindow** aWindow)
+  {
+    MOZ_ASSERT(aWindow);
+
+    nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
+    if (NS_WARN_IF(!window)) {
+      return NS_ERROR_TYPE_ERR;
+    }
+
+    nsCOMPtr<nsIDocument> doc = window->GetDocument();
+    if (NS_WARN_IF(!doc)) {
+      return NS_ERROR_TYPE_ERR;
+    }
+
+    if (NS_WARN_IF(!doc->IsActive())) {
+      return NS_ERROR_TYPE_ERR;
+    }
+
+    nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+    if (NS_WARN_IF(!docShell)) {
+      return NS_ERROR_TYPE_ERR;
+    }
+
+    nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+    nsresult rv = docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    loadInfo->SetOwner(aPrincipal);
+    loadInfo->SetReferrer(doc->GetOriginalURI());
+    loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
+    loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContentAndReplace);
+    loadInfo->SetSourceDocShell(docShell);
+    rv =
+      docShell->LoadURI(aUrl, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    *aWindow = window;
+    return NS_OK;
+  }
+};
+
+already_AddRefed<Promise>
+ServiceWorkerWindowClient::Navigate(const nsAString& aUrl, ErrorResult& aRv)
+{
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+  workerPrivate->AssertIsOnWorkerThread();
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+  MOZ_ASSERT(global);
+
+  RefPtr<Promise> promise = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  if (aUrl.EqualsLiteral("about:blank")) {
+    promise->MaybeReject(NS_ERROR_TYPE_ERR);
+    return promise.forget();
+  }
+
+  RefPtr<PromiseWorkerProxy> promiseProxy =
+    PromiseWorkerProxy::Create(workerPrivate, promise);
+  if (promiseProxy) {
+    RefPtr<ClientNavigateRunnable> r =
+      new ClientNavigateRunnable(mWindowId, aUrl, promiseProxy);
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+  } else {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
+
+  return promise.forget();
+}
--- a/dom/workers/ServiceWorkerWindowClient.h
+++ b/dom/workers/ServiceWorkerWindowClient.h
@@ -41,16 +41,19 @@ public:
   Focused() const
   {
     return mFocused;
   }
 
   already_AddRefed<Promise>
   Focus(ErrorResult& aRv) const;
 
+  already_AddRefed<Promise>
+  Navigate(const nsAString& aUrl,  ErrorResult& aRv);
+
 private:
   ~ServiceWorkerWindowClient()
   { }
 
   mozilla::dom::VisibilityState mVisibilityState;
   bool mFocused;
 };
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1437,33 +1437,32 @@ private:
   nsresult Cancel() override
   {
     // We need to run regardless.
     Run();
     return WorkerRunnable::Cancel();
   }
 };
 
-class UpdateRuntimeOptionsRunnable final : public WorkerControlRunnable
-{
-  JS::RuntimeOptions mRuntimeOptions;
+class UpdateContextOptionsRunnable final : public WorkerControlRunnable
+{
+  JS::ContextOptions mContextOptions;
 
 public:
-  UpdateRuntimeOptionsRunnable(
-                                    WorkerPrivate* aWorkerPrivate,
-                                    const JS::RuntimeOptions& aRuntimeOptions)
+  UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
+                               const JS::ContextOptions& aContextOptions)
   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
-    mRuntimeOptions(aRuntimeOptions)
+    mContextOptions(aContextOptions)
   { }
 
 private:
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
-    aWorkerPrivate->UpdateRuntimeOptionsInternal(aCx, mRuntimeOptions);
+    aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
     return true;
   }
 };
 
 class UpdatePreferenceRunnable final : public WorkerControlRunnable
 {
   WorkerPreference mPref;
   bool mValue;
@@ -3046,28 +3045,28 @@ WorkerPrivateParent<Derived>::PostMessag
 {
   AssertIsOnMainThread();
   PostMessageInternal(aCx, aMessage, aTransferable, Move(aClientInfo),
                       aKeepAliveToken, aRv);
 }
 
 template <class Derived>
 void
-WorkerPrivateParent<Derived>::UpdateRuntimeOptions(
-                                    const JS::RuntimeOptions& aRuntimeOptions)
+WorkerPrivateParent<Derived>::UpdateContextOptions(
+                                    const JS::ContextOptions& aContextOptions)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
-    mJSSettings.runtimeOptions = aRuntimeOptions;
-  }
-
-  RefPtr<UpdateRuntimeOptionsRunnable> runnable =
-    new UpdateRuntimeOptionsRunnable(ParentAsWorkerPrivate(), aRuntimeOptions);
+    mJSSettings.contextOptions = aContextOptions;
+  }
+
+  RefPtr<UpdateContextOptionsRunnable> runnable =
+    new UpdateContextOptionsRunnable(ParentAsWorkerPrivate(), aContextOptions);
   if (!runnable->Dispatch()) {
     NS_WARNING("Failed to update worker context options!");
   }
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdatePreference(WorkerPreference aPref, bool aValue)
@@ -5510,18 +5509,18 @@ WorkerPrivate::DestroySyncLoop(uint32_t 
 
     // This will delete |loopInfo|!
     mSyncLoopStack.RemoveElementAt(aLoopIndex);
   }
 
   MOZ_ALWAYS_SUCCEEDS(aThread->PopEventQueue(nestedEventTarget));
 
   if (mSyncLoopStack.IsEmpty() && mPendingEventQueueClearing) {
+    mPendingEventQueueClearing = false;
     ClearMainEventQueue(WorkerRan);
-    mPendingEventQueueClearing = false;
   }
 
   return result;
 }
 
 void
 WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult)
 {
@@ -6304,26 +6303,26 @@ WorkerPrivate::RescheduleTimeoutTimer(JS
     JS_ReportError(aCx, "Failed to start timer!");
     return false;
   }
 
   return true;
 }
 
 void
-WorkerPrivate::UpdateRuntimeOptionsInternal(
+WorkerPrivate::UpdateContextOptionsInternal(
                                     JSContext* aCx,
-                                    const JS::RuntimeOptions& aRuntimeOptions)
+                                    const JS::ContextOptions& aContextOptions)
 {
   AssertIsOnWorkerThread();
 
-  JS::RuntimeOptionsRef(aCx) = aRuntimeOptions;
+  JS::ContextOptionsRef(aCx) = aContextOptions;
 
   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
-    mChildWorkers[index]->UpdateRuntimeOptions(aRuntimeOptions);
+    mChildWorkers[index]->UpdateContextOptions(aContextOptions);
   }
 }
 
 void
 WorkerPrivate::UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages)
 {
   WorkerGlobalScope* globalScope = GlobalScope();
   if (globalScope) {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -348,17 +348,17 @@ public:
   void
   PostMessageToServiceWorker(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
                              UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
                              const nsMainThreadPtrHandle<nsISupports>& aKeepAliveToken,
                              ErrorResult& aRv);
 
   void
-  UpdateRuntimeOptions(const JS::RuntimeOptions& aRuntimeOptions);
+  UpdateContextOptions(const JS::ContextOptions& aContextOptions);
 
   void
   UpdateLanguages(const nsTArray<nsString>& aLanguages);
 
   void
   UpdatePreference(WorkerPreference aPref, bool aValue);
 
   void
@@ -1163,17 +1163,17 @@ public:
   void
   CloseHandlerFinished()
   {
     AssertIsOnWorkerThread();
     mCloseHandlerFinished = true;
   }
 
   void
-  UpdateRuntimeOptionsInternal(JSContext* aCx, const JS::RuntimeOptions& aRuntimeOptions);
+  UpdateContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContextOptions);
 
   void
   UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages);
 
   void
   UpdatePreferenceInternal(WorkerPreference aPref, bool aValue);
 
   void
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -136,17 +136,17 @@ struct JSSettings
     JSContentChromeSettings()
     : compartmentOptions(), maxScriptRuntime(0)
     { }
   };
 
   JSContentChromeSettings chrome;
   JSContentChromeSettings content;
   JSGCSettingsArray gcSettings;
-  JS::RuntimeOptions runtimeOptions;
+  JS::ContextOptions contextOptions;
 
 #ifdef JS_GC_ZEAL
   uint8_t gcZeal;
   uint32_t gcZealFrequency;
 #endif
 
   JSSettings()
 #ifdef JS_GC_ZEAL
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -4750,17 +4750,18 @@ nsHTMLEditor::CopyLastEditableChildStyle
           CreateNode(childElement->NodeInfo()->NameAtom(), newBlock, 0);
         NS_ENSURE_STATE(newStyles);
       }
       CloneAttributes(newStyles, childElement);
     }
     childElement = childElement->GetParentElement();
   }
   if (deepestStyle) {
-    *aOutBrNode = CreateBR(deepestStyle, 0);
+    RefPtr<Element> retVal = CreateBR(deepestStyle, 0);
+    retVal.forget(aOutBrNode);
     NS_ENSURE_STATE(*aOutBrNode);
   }
   return NS_OK;
 }
 
 nsresult
 nsHTMLEditor::GetElementOrigin(nsIDOMElement * aElement, int32_t & aX, int32_t & aY)
 {
--- a/extensions/pref/autoconfig/src/prefcalls.js
+++ b/extensions/pref/autoconfig/src/prefcalls.js
@@ -147,20 +147,20 @@ function clearPref(prefName) {
         
 }
 
 function setLDAPVersion(version) {
     gVersion = version;
 }
 
 
-function getLDAPAttributes(host, base, filter, attribs) {
+function getLDAPAttributes(host, base, filter, attribs, isSecure) {
     
     try {
-        var urlSpec = "ldap://" + host + "/" + base + "?" + attribs + "?sub?" +
+        var urlSpec = "ldap" + (isSecure ? "s" : "") + "://" + host + (isSecure ? ":636" : "") + "/" + base + "?" + attribs + "?sub?" +
                       filter;
 
         var url = Components.classes["@mozilla.org/network/io-service;1"]
                             .getService(Components.interfaces.nsIIOService)
                             .newURI(urlSpec, null, null)
                             .QueryInterface(Components.interfaces.nsILDAPURL);
 
         var ldapquery = Components.classes[LDAPSyncQueryContractID]
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -939,17 +939,17 @@ DrawTargetD2D1::CreateFilter(FilterType 
 
 bool
 DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat)
 {
   HRESULT hr;
 
   ID2D1Device* device = Factory::GetD2D1Device();
   if (!device) {
-    gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init().";
+    gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
     return false;
   }
 
   hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
 
   if (FAILED(hr)) {
     gfxCriticalError() <<"[D2D1.1] 1Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
     return false;
@@ -1004,17 +1004,17 @@ DrawTargetD2D1::Init(ID3D11Texture2D* aT
 
 bool
 DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
 {
   HRESULT hr;
 
   ID2D1Device* device = Factory::GetD2D1Device();
   if (!device) {
-    gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init().";
+    gfxCriticalNote << "[D2D1.1] Failed to obtain a device for DrawTargetD2D1::Init(IntSize, SurfaceFormat).";
     return false;
   }
 
   hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, getter_AddRefs(mDC));
 
   if (FAILED(hr)) {
     gfxCriticalError() <<"[D2D1.1] 2Failed to create a DeviceContext, code: " << hexa(hr) << " format " << (int)aFormat;
     return false;
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -31,16 +31,17 @@ enum class GLVersion : uint32_t {
     GL4_3 = 430,
 };
 
 enum class GLESVersion : uint32_t {
     NONE  = 0,   // Feature is not support natively by GL ES
     ES2   = 200,
     ES3   = 300,
     ES3_1 = 310,
+    ES3_2 = 320,
 };
 
 // ARB_ES2_compatibility is natively supported in OpenGL 4.1.
 static const GLVersion kGLCoreVersionForES2Compat = GLVersion::GL4_1;
 
 // ARB_ES3_compatibility is natively supported in OpenGL 4.3.
 static const GLVersion kGLCoreVersionForES3Compat = GLVersion::GL4_3;
 
@@ -184,17 +185,17 @@ static const FeatureInfo sFeatureInfoArr
         GLContext::ARB_ES3_compatibility, // no suffix on ARB extension
         {
             GLContext::Extensions_End
         }
     },
     {
         "EXT_color_buffer_float",
         GLVersion::GL3,
-        GLESVersion::NONE,
+        GLESVersion::ES3_2,
         GLContext::Extension_None,
         {
             GLContext::EXT_color_buffer_float,
             GLContext::Extensions_End
         }
     },
     {
         // Removes clamping for float color outputs from frag shaders.
@@ -491,28 +492,28 @@ static const FeatureInfo sFeatureInfoArr
         GLContext::Extension_None,
         {
             GLContext::Extensions_End
         }
     },
     {
         "renderbuffer_color_float",
         GLVersion::GL3,
-        GLESVersion::ES3,
+        GLESVersion::ES3_2,
         GLContext::Extension_None,
         {
             GLContext::ARB_texture_float,
             GLContext::EXT_color_buffer_float,
             GLContext::Extensions_End
         }
     },
     {
         "renderbuffer_color_half_float",
         GLVersion::GL3,
-        GLESVersion::ES3,
+        GLESVersion::ES3_2,
         GLContext::Extension_None,
         {
             GLContext::ARB_texture_float,
             GLContext::EXT_color_buffer_float,
             GLContext::EXT_color_buffer_half_float,
             GLContext::Extensions_End
         }
     },
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -226,17 +226,17 @@ typedef GenericFlingAnimation FlingAnima
  * curve in order to increase the velocity in this range, or an ease-in curve to
  * decrease the velocity. A straight-line curve is equivalent to disabling the
  * curve entirely by setting the threshold to -1. The max velocity pref must
  * also be set in order for the curving to take effect, as it defines the upper
  * bound of the velocity curve.\n
  * The points (x1, y1) and (x2, y2) used as the two intermediate control points
  * in the cubic bezier curve; the first and last points are (0,0) and (1,1).\n
  * Some example values for these prefs can be found at\n
- * http://mxr.mozilla.org/mozilla-central/source/layout/style/nsStyleStruct.cpp?rev=21282be9ad95#2462
+ * https://dxr.mozilla.org/mozilla-central/rev/70e05c6832e831374604ac3ce7433971368dffe0/layout/style/nsStyleStruct.cpp#2729
  *
  * \li\b apz.fling_friction
  * Amount of friction applied during flings. This is used in the following
  * formula: v(t1) = v(t0) * (1 - f)^(t1 - t0), where v(t1) is the velocity
  * for a new sample, v(t0) is the velocity at the previous sample, f is the
  * value of this pref, and (t1 - t0) is the amount of time, in milliseconds,
  * that has elapsed between the two samples.\n
  * NOTE: Not currently used in Android fling calculations.
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -244,17 +244,17 @@ APZCCallbackHelper::UpdateRootFrame(Fram
     // to be returned by window.innerWidth/innerHeight (see bug 1187792).
 
     float presShellResolution = shell->GetResolution();
 
     // If the pres shell resolution has changed on the content side side
     // the time this repaint request was fired, consider this request out of date
     // and drop it; setting a zoom based on the out-of-date resolution can have
     // the effect of getting us stuck with the stale resolution.
-    if (presShellResolution != aMetrics.GetPresShellResolution()) {
+    if (!FuzzyEqualsMultiplicative(presShellResolution, aMetrics.GetPresShellResolution())) {
       return;
     }
 
     // The pres shell resolution is updated by the the async zoom since the
     // last paint.
     presShellResolution = aMetrics.GetPresShellResolution()
                         * aMetrics.GetAsyncZoom().scale;
     shell->SetResolutionAndScaleTo(presShellResolution);
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -616,16 +616,20 @@ BasicCompositor::BeginFrame(const nsIntR
   if (!aOpaqueRegion.IsEmpty()) {
     LayoutDeviceIntRegion clearRegion = mInvalidRegion;
     clearRegion.SubOut(LayoutDeviceIntRegion::FromUnknownRegion(aOpaqueRegion));
     clearRect = clearRegion.GetBounds();
   } else {
     clearRect = mInvalidRect;
   }
 
+  // Prevent CreateRenderTargetForWindow from clearing unwanted area.
+  gfxUtils::ClipToRegion(mDrawTarget,
+                         mInvalidRegion.ToUnknownRegion());
+
   // Setup an intermediate render target to buffer all compositing. We will
   // copy this into mDrawTarget (the widget), and/or mTarget in EndFrame()
   RefPtr<CompositingRenderTarget> target =
     CreateRenderTargetForWindow(mInvalidRect,
                                 clearRect,
                                 bufferMode);
   if (!target) {
     if (!mTarget) {
@@ -634,18 +638,20 @@ BasicCompositor::BeginFrame(const nsIntR
     return;
   }
   SetRenderTarget(target);
 
   // We only allocate a surface sized to the invalidated region, so we need to
   // translate future coordinates.
   mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mRenderTarget->GetOrigin()));
 
-  gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget,
-                         mInvalidRegion.ToUnknownRegion());
+  if (mRenderTarget->mDrawTarget != mDrawTarget) {
+    gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget,
+                           mInvalidRegion.ToUnknownRegion());
+  }
 
   if (aRenderBoundsOut) {
     *aRenderBoundsOut = rect;
   }
 
   if (aClipRectIn) {
     mRenderTarget->mDrawTarget->PushClipRect(Rect(*aClipRectIn));
   } else {
@@ -670,17 +676,20 @@ BasicCompositor::EndFrame()
     float b = float(rand()) / RAND_MAX;
     // We're still clipped to mInvalidRegion, so just fill the bounds.
     mRenderTarget->mDrawTarget->FillRect(
       IntRectToRect(mInvalidRegion.GetBounds()).ToUnknownRect(),
       ColorPattern(Color(r, g, b, 0.2f)));
   }
 
   // Pop aInvalidregion
-  mRenderTarget->mDrawTarget->PopClip();
+  mDrawTarget->PopClip();
+  if (mRenderTarget->mDrawTarget != mDrawTarget) {
+    mRenderTarget->mDrawTarget->PopClip();
+  }
 
   if (mTarget || mRenderTarget->mDrawTarget != mDrawTarget) {
     // Note: Most platforms require us to buffer drawing to the widget surface.
     // That's why we don't draw to mDrawTarget directly.
     RefPtr<SourceSurface> source;
     if (mRenderTarget->mDrawTarget != mDrawTarget) {
       source = mWidget->EndBackBufferDrawing();
     } else {
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -55,19 +55,16 @@ CompositorBridgeChild::CompositorBridgeC
   MOZ_ASSERT(NS_IsMainThread());
 
   // Ensure destruction on the main thread
   SetMessageLoopToPostDestructionTo(mMessageLoop);
 }
 
 CompositorBridgeChild::~CompositorBridgeChild()
 {
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
-
   if (mCanSend) {
     gfxCriticalError() << "CompositorBridgeChild was not deinitialized";
   }
 }
 
 bool
 CompositorBridgeChild::IsSameProcess() const
 {
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1864,19 +1864,18 @@ CompositorBridgeParent::DeallocPComposit
 class CrossProcessCompositorBridgeParent final : public PCompositorBridgeParent,
                                                  public ShadowLayersManager,
                                                  public CompositorBridgeParentIPCAllocator,
                                                  public ShmemAllocator
 {
   friend class CompositorBridgeParent;
 
 public:
-  explicit CrossProcessCompositorBridgeParent(Transport* aTransport)
+  explicit CrossProcessCompositorBridgeParent()
     : CompositorBridgeParentIPCAllocator("CrossProcessCompositorBridgeParent")
-    , mTransport(aTransport)
     , mNotifyAfterRemotePaint(false)
     , mDestroyCalled(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
     // Always run destructor on the main thread
     SetMessageLoopToPostDestructionTo(MessageLoop::current());
   }
 
@@ -2053,17 +2052,16 @@ private:
   virtual ~CrossProcessCompositorBridgeParent();
 
   void DeferredDestroy();
 
   // There can be many CPCPs, and IPDL-generated code doesn't hold a
   // reference to top-level actors.  So we hold a reference to
   // ourself.  This is released (deferred) in ActorDestroy().
   RefPtr<CrossProcessCompositorBridgeParent> mSelfRef;
-  Transport* mTransport;
 
   RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
   // If true, we should send a RemotePaintIsReady message when the layer transaction
   // is received
   bool mNotifyAfterRemotePaint;
   bool mDestroyCalled;
 };
 
@@ -2218,17 +2216,17 @@ OpenCompositor(CrossProcessCompositorBri
 }
 
 /*static*/ PCompositorBridgeParent*
 CompositorBridgeParent::Create(Transport* aTransport, ProcessId aOtherPid)
 {
   gfxPlatform::InitLayersIPC();
 
   RefPtr<CrossProcessCompositorBridgeParent> cpcp =
-    new CrossProcessCompositorBridgeParent(aTransport);
+    new CrossProcessCompositorBridgeParent();
 
   cpcp->mSelfRef = cpcp;
   CompositorLoop()->PostTask(
     NewRunnableFunction(OpenCompositor, cpcp.get(),
                         aTransport, aOtherPid, XRE_GetIOMessageLoop()));
   // The return value is just compared to null for success checking,
   // we're not sharing a ref.
   return cpcp.get();
@@ -2788,34 +2786,33 @@ CrossProcessCompositorBridgeParent::Defe
   mCompositorThreadHolder = nullptr;
   mSelfRef = nullptr;
 }
 
 CrossProcessCompositorBridgeParent::~CrossProcessCompositorBridgeParent()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(XRE_GetIOMessageLoop());
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
+  MOZ_ASSERT(IToplevelProtocol::GetTransport());
 }
 
 IToplevelProtocol*
 CrossProcessCompositorBridgeParent::CloneToplevel(
   const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
   base::ProcessHandle aPeerProcess,
   mozilla::ipc::ProtocolCloneContext* aCtx)
 {
   for (unsigned int i = 0; i < aFds.Length(); i++) {
     if (aFds[i].protocolId() == (unsigned)GetProtocolId()) {
-      Transport* transport = OpenDescriptor(aFds[i].fd(),
-                                            Transport::MODE_SERVER);
+      UniquePtr<Transport> transport =
+        OpenDescriptor(aFds[i].fd(), Transport::MODE_SERVER);
       PCompositorBridgeParent* compositor =
-        CompositorBridgeParent::Create(transport, base::GetProcId(aPeerProcess));
+        CompositorBridgeParent::Create(transport.get(), base::GetProcId(aPeerProcess));
       compositor->CloneManagees(this, aCtx);
-      compositor->IToplevelProtocol::SetTransport(transport);
+      compositor->IToplevelProtocol::SetTransport(Move(transport));
       // The reference to the compositor thread is held in OnChannelConnected().
       // We need to do this for cloned actors, too.
       compositor->OnChannelConnected(base::GetProcId(aPeerProcess));
       return compositor;
     }
   }
   return nullptr;
 }
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -506,21 +506,16 @@ ImageBridgeChild::ImageBridgeChild()
   // Always run destructor on the main thread
   SetMessageLoopToPostDestructionTo(MessageLoop::current());
 
   mTxn = new CompositableTransaction();
 }
 ImageBridgeChild::~ImageBridgeChild()
 {
   MOZ_ASSERT(NS_IsMainThread());
-
-  RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
-
-  XRE_GetIOMessageLoop()->PostTask(task.forget());
-
   delete mTxn;
 }
 
 void
 ImageBridgeChild::MarkShutDown()
 {
   MOZ_ASSERT(!mShuttingDown);
   mTexturesWaitingRecycled.Clear();
@@ -999,17 +994,17 @@ bool ImageBridgeChild::StartUpOnThread(T
   MOZ_ASSERT(aThread, "ImageBridge needs a thread.");
   if (sImageBridgeChildSingleton == nullptr) {
     sImageBridgeChildThread = aThread;
     if (!aThread->IsRunning()) {
       aThread->Start();
     }
     sImageBridgeChildSingleton = new ImageBridgeChild();
     sImageBridgeParentSingleton = new ImageBridgeParent(
-      CompositorThreadHolder::Loop(), nullptr, base::GetCurrentProcId());
+      CompositorThreadHolder::Loop(), base::GetCurrentProcId());
     sImageBridgeChildSingleton->ConnectAsync(sImageBridgeParentSingleton);
     sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
       NewRunnableFunction(CallSendImageBridgeThreadId,
                           sImageBridgeChildSingleton.get()));
     return true;
   } else {
     return false;
   }
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -45,21 +45,19 @@ using namespace mozilla::media;
 std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeParent::sImageBridges;
 
 MessageLoop* ImageBridgeParent::sMainLoop = nullptr;
 
 // defined in CompositorBridgeParent.cpp
 CompositorThreadHolder* GetCompositorThreadHolder();
 
 ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop,
-                                     Transport* aTransport,
                                      ProcessId aChildProcessId)
   : CompositableParentManager("ImageBridgeParent")
   , mMessageLoop(aLoop)
-  , mTransport(aTransport)
   , mSetChildThreadPriority(false)
   , mClosed(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   sMainLoop = MessageLoop::current();
 
   // top-level actors must be destroyed on the main thread.
   SetMessageLoopToPostDestructionTo(sMainLoop);
@@ -72,22 +70,16 @@ ImageBridgeParent::ImageBridgeParent(Mes
   // DeferredDestroy clears mSelfRef.
   mSelfRef = this;
 }
 
 ImageBridgeParent::~ImageBridgeParent()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (mTransport) {
-    MOZ_ASSERT(XRE_GetIOMessageLoop());
-    RefPtr<DeleteTask<Transport>> task(new DeleteTask<Transport>(mTransport));
-    XRE_GetIOMessageLoop()->PostTask(task.forget());
-  }
-
   nsTArray<PImageContainerParent*> parents;
   ManagedPImageContainerParent(parents);
   for (PImageContainerParent* p : parents) {
     delete p;
   }
 
   sImageBridges.erase(OtherPid());
 }
@@ -194,17 +186,17 @@ ConnectImageBridgeInParentProcess(ImageB
 {
   aBridge->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide);
 }
 
 /*static*/ PImageBridgeParent*
 ImageBridgeParent::Create(Transport* aTransport, ProcessId aChildProcessId)
 {
   MessageLoop* loop = CompositorThreadHolder::Loop();
-  RefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aTransport, aChildProcessId);
+  RefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aChildProcessId);
 
   loop->PostTask(NewRunnableFunction(ConnectImageBridgeInParentProcess,
                                      bridge.get(), aTransport, aChildProcessId));
   return bridge.get();
 }
 
 bool ImageBridgeParent::RecvWillClose()
 {
@@ -350,21 +342,21 @@ ImageBridgeParent::GetInstance(ProcessId
 
 IToplevelProtocol*
 ImageBridgeParent::CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
                                  base::ProcessHandle aPeerProcess,
                                  mozilla::ipc::ProtocolCloneContext* aCtx)
 {
   for (unsigned int i = 0; i < aFds.Length(); i++) {
     if (aFds[i].protocolId() == unsigned(GetProtocolId())) {
-      Transport* transport = OpenDescriptor(aFds[i].fd(),
-                                            Transport::MODE_SERVER);
-      PImageBridgeParent* bridge = Create(transport, base::GetProcId(aPeerProcess));
+      UniquePtr<Transport> transport =
+        OpenDescriptor(aFds[i].fd(), Transport::MODE_SERVER);
+      PImageBridgeParent* bridge = Create(transport.get(), base::GetProcId(aPeerProcess));
       bridge->CloneManagees(this, aCtx);
-      bridge->IToplevelProtocol::SetTransport(transport);
+      bridge->IToplevelProtocol::SetTransport(Move(transport));
       // The reference to the compositor thread is held in OnChannelConnected().
       // We need to do this for cloned actors, too.
       bridge->OnChannelConnected(base::GetProcId(aPeerProcess));
       return bridge;
     }
   }
   return nullptr;
 }
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -41,17 +41,17 @@ class ImageBridgeParent final : public P
                                 public CompositableParentManager,
                                 public ShmemAllocator
 {
 public:
   typedef InfallibleTArray<CompositableOperation> EditArray;
   typedef InfallibleTArray<OpDestroy> OpDestroyArray;
   typedef InfallibleTArray<EditReply> EditReplyArray;
 
-  ImageBridgeParent(MessageLoop* aLoop, Transport* aTransport, ProcessId aChildProcessId);
+  ImageBridgeParent(MessageLoop* aLoop, ProcessId aChildProcessId);
   ~ImageBridgeParent();
 
   virtual ShmemAllocator* AsShmemAllocator() override { return this; }
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   static PImageBridgeParent*
   Create(Transport* aTransport, ProcessId aChildProcessId);
@@ -141,17 +141,16 @@ public:
   virtual bool IPCOpen() const override { return !mClosed; }
 
 protected:
   void OnChannelConnected(int32_t pid) override;
 
 private:
   void DeferredDestroy();
   MessageLoop* mMessageLoop;
-  Transport* mTransport;
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<ImageBridgeParent> mSelfRef;
 
   bool mSetChildThreadPriority;
   bool mClosed;
 
   /**
--- a/gfx/layers/ipc/SharedBufferManagerChild.cpp
+++ b/gfx/layers/ipc/SharedBufferManagerChild.cpp
@@ -154,17 +154,17 @@ SharedBufferManagerChild::StartUpOnThrea
   sSharedBufferManagerChildThread = aThread;
   if (!aThread->IsRunning()) {
     aThread->Start();
   }
   sSharedBufferManagerChildSingleton = new SharedBufferManagerChild();
   char thrname[128];
   base::snprintf(thrname, 128, "BufMgrParent#%d", base::Process::Current().pid());
   sSharedBufferManagerParentSingleton = new SharedBufferManagerParent(
-    nullptr, base::Process::Current().pid(), new base::Thread(thrname));
+    base::Process::Current().pid(), new base::Thread(thrname));
   sSharedBufferManagerChildSingleton->ConnectAsync(sSharedBufferManagerParentSingleton);
   return true;
 }
 
 void
 SharedBufferManagerChild::DestroyManager()
 {
   MOZ_ASSERT(!InSharedBufferManagerChildThread(),
--- a/gfx/layers/ipc/SharedBufferManagerParent.cpp
+++ b/gfx/layers/ipc/SharedBufferManagerParent.cpp
@@ -120,19 +120,18 @@ public:
     explicit DeleteSharedBufferManagerParentTask(UniquePtr<SharedBufferManagerParent> aSharedBufferManager)
         : mSharedBufferManager(Move(aSharedBufferManager)) {
     }
     NS_IMETHOD Run() override { return NS_OK; }
 private:
     UniquePtr<SharedBufferManagerParent> mSharedBufferManager;
 };
 
-SharedBufferManagerParent::SharedBufferManagerParent(Transport* aTransport, base::ProcessId aOwner, base::Thread* aThread)
-  : mTransport(aTransport)
-  , mThread(aThread)
+SharedBufferManagerParent::SharedBufferManagerParent(base::ProcessId aOwner, base::Thread* aThread)
+  : mThread(aThread)
   , mDestroyed(false)
   , mLock("SharedBufferManagerParent.mLock")
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!sManagerMonitor) {
     sManagerMonitor = new Monitor("Manager Monitor");
   }
 
@@ -147,20 +146,16 @@ SharedBufferManagerParent::SharedBufferM
   }
   mOwner = aOwner;
   sManagers[aOwner] = this;
 }
 
 SharedBufferManagerParent::~SharedBufferManagerParent()
 {
   MonitorAutoLock lock(*sManagerMonitor.get());
-  if (mTransport) {
-    RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
-    XRE_GetIOMessageLoop()->PostTask(task.forget());
-  }
   sManagers.erase(mOwner);
   delete mThread;
 }
 
 void
 SharedBufferManagerParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   MutexAutoLock lock(mLock);
@@ -184,17 +179,17 @@ ConnectSharedBufferManagerInParentProces
 PSharedBufferManagerParent* SharedBufferManagerParent::Create(Transport* aTransport,
                                                               ProcessId aOtherPid)
 {
   base::Thread* thread = nullptr;
   char thrname[128];
   base::snprintf(thrname, 128, "BufMgrParent#%d", aOtherPid);
   thread = new base::Thread(thrname);
 
-  SharedBufferManagerParent* manager = new SharedBufferManagerParent(aTransport, aOtherPid, thread);
+  SharedBufferManagerParent* manager = new SharedBufferManagerParent(aOtherPid, thread);
   if (!thread->IsRunning()) {
     thread->Start();
   }
   thread->message_loop()->PostTask(NewRunnableFunction(ConnectSharedBufferManagerInParentProcess,
                                                        manager, aTransport, aOtherPid));
   return manager;
 }
 
@@ -371,21 +366,21 @@ SharedBufferManagerParent::GetGraphicBuf
 
 IToplevelProtocol*
 SharedBufferManagerParent::CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
                                  base::ProcessHandle aPeerProcess,
                                  mozilla::ipc::ProtocolCloneContext* aCtx)
 {
   for (unsigned int i = 0; i < aFds.Length(); i++) {
     if (aFds[i].protocolId() == unsigned(GetProtocolId())) {
-      Transport* transport = OpenDescriptor(aFds[i].fd(),
-                                            Transport::MODE_SERVER);
-      PSharedBufferManagerParent* bufferManager = Create(transport, base::GetProcId(aPeerProcess));
+      UniquePtr<Transport> transport =
+        OpenDescriptor(aFds[i].fd(), Transport::MODE_SERVER);
+      PSharedBufferManagerParent* bufferManager = Create(transport.get(), base::GetProcId(aPeerProcess));
       bufferManager->CloneManagees(this, aCtx);
-      bufferManager->IToplevelProtocol::SetTransport(transport);
+      bufferManager->IToplevelProtocol::SetTransport(Move(transport));
       return bufferManager;
     }
   }
   return nullptr;
 }
 
 } /* namespace layers */
 } /* namespace mozilla */
--- a/gfx/layers/ipc/SharedBufferManagerParent.h
+++ b/gfx/layers/ipc/SharedBufferManagerParent.h
@@ -42,17 +42,17 @@ public:
 
 #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
   android::sp<android::GraphicBuffer> GetGraphicBuffer(int64_t key);
   static android::sp<android::GraphicBuffer> GetGraphicBuffer(GrallocBufferRef aRef);
 #endif
   /**
    * Create a SharedBufferManagerParent but do not open the link
    */
-  SharedBufferManagerParent(Transport* aTransport, ProcessId aOwner, base::Thread* aThread);
+  SharedBufferManagerParent(ProcessId aOwner, base::Thread* aThread);
   virtual ~SharedBufferManagerParent();
 
   /**
    * When the IPC channel down or something bad make this Manager die, clear all the buffer reference!
    */
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual bool RecvAllocateGrallocBuffer(const IntSize&, const uint32_t&, const uint32_t&, mozilla::layers::MaybeMagicGrallocBufferHandle*) override;
@@ -96,17 +96,16 @@ protected:
 
 #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
   /**
    * Buffers owned by this SharedBufferManager pair
    */
   std::map<int64_t, android::sp<android::GraphicBuffer> > mBuffers;
 #endif
   
-  Transport* mTransport;
   base::ProcessId mOwner;
   base::Thread* mThread;
   bool mDestroyed;
   Mutex mLock;
 
   static uint64_t sBufferKey;
   static StaticAutoPtr<Monitor> sManagerMonitor;
 };
--- a/gfx/skia/skia/src/core/SkDraw.cpp
+++ b/gfx/skia/skia/src/core/SkDraw.cpp
@@ -1006,17 +1006,20 @@ DRAW_PATH:
     this->drawPath(path, paint, nullptr, true);
 }
 
 SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
     if (!matrix.hasPerspective()) {
         SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
         SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX],  matrix[SkMatrix::kMScaleY]);
         if (SkScalarsAreFinite(sx, sy)) {
-            return SkTMax(sx, sy);
+            SkScalar scale = SkTMax(sx, sy);
+            if (scale > 0) {
+                return scale;
+            }
         }
     }
     return 1;
 }
 
 void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
                       const SkMatrix* prePathMatrix, bool pathIsMutable,
                       bool drawCoverage, SkBlitter* customBlitter) const {
--- a/gfx/skia/skia/src/core/SkPath.cpp
+++ b/gfx/skia/skia/src/core/SkPath.cpp
@@ -2797,17 +2797,19 @@ static int winding_mono_cubic(const SkPo
         return 0;
     }
     if (x > max) {
         return dir;
     }
 
     // compute the actual x(t) value
     SkScalar t;
-    SkAssertResult(SkCubicClipper::ChopMonoAtY(pts, y, &t));
+    if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) {
+      return 0;
+    }
     SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
     if (SkScalarNearlyEqual(xt, x)) {
         if (x != pts[3].fX || y != pts[3].fY) {  // don't test end points; they're start points
             *onCurveCount += 1;
             return 0;
         }
     }
     return xt < x ? dir : 0;
@@ -3034,17 +3036,19 @@ static void tangent_cubic(const SkPoint 
              && !between(pts[2].fX, x, pts[3].fX)) {
         return;
     }
     SkPoint dst[10];
     int n = SkChopCubicAtYExtrema(pts, dst);
     for (int i = 0; i <= n; ++i) {
         SkPoint* c = &dst[i * 3];
         SkScalar t;
-        SkAssertResult(SkCubicClipper::ChopMonoAtY(c, y, &t));
+        if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) {
+          continue;
+        }
         SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
         if (!SkScalarNearlyEqual(x, xt)) {
             continue;
         }
         SkVector tangent;
         SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
         t