Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 15 Nov 2016 12:33:34 +0100
changeset 367708 59be52cc8c6ccbae8b8bc3bd8eefcbc1341f1466
parent 367707 8e124e64cef2554db60cc5cbfa1e7bc303efe627 (current diff)
parent 367557 e16d1a881481d7d83493481d9cc2c4ef1e6b9a36 (diff)
child 367709 c0a8755a25891c60b325e10aa32a263e2b129017
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
media/webrtc/moz.build
media/webrtc/signaling/test/moz.build
media/webrtc/signaling/test/sdp_file_parser.cpp
testing/web-platform/meta/MANIFEST.json
--- a/.clang-format
+++ b/.clang-format
@@ -1,4 +1,30 @@
 BasedOnStyle: Mozilla
 
 # Ignore all comments because they aren't reflowed properly.
 CommentPragmas: "^"
+
+# Force pointers to the type for C++.
+DerivePointerAlignment: false
+PointerAlignment: Left
+
+# Prevent the loss of indentation with these macros
+MacroBlockBegin: "^\
+NS_INTERFACE_MAP_BEGIN|\
+NS_INTERFACE_TABLE_HEAD|\
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION|\
+NS_IMPL_CYCLE_COLLECTION_.*_BEGIN|\
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED|\
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED|\
+NS_INTERFACE_TABLE_BEGIN|\
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED|\
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED$"
+MacroBlockEnd: "^\
+NS_INTERFACE_MAP_END|\
+NS_IMPL_CYCLE_COLLECTION_.*_END|\
+NS_INTERFACE_TABLE_END|\
+NS_INTERFACE_MAP_END_INHERITING|\
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END_INHERITED|\
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED$"
+
+SortIncludes: false
+
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -1,3 +1,70 @@
-^mfbt/.*
-^js/.*
-^media/.*
+^build/clang-plugin/tests/.*
+^config/gcc-stl-wrapper.template.h
+^config/msvc-stl-wrapper.template.h
+^js/src/jsapi-tests/.*
+
+# Generated from ./tools/rewriting/ThirdPartyPaths.txt
+# awk '{print "^"$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt
+^browser/components/translation/cld2/.*
+^build/stlport/.*
+^db/sqlite3/src/.*
+^dom/media/platforms/ffmpeg/libav.*
+^extensions/spellcheck/hunspell/src/.*
+^gfx/2d/convolver.*
+^gfx/2d/image_operations.*
+^gfx/angle/.*
+^gfx/cairo/.*
+^gfx/graphite2/.*
+^gfx/harfbuzz/.*
+^gfx/ots/.*
+^gfx/qcms/.*
+^gfx/skia/.*
+^gfx/ycbcr/.*
+^intl/hyphenation/hyphen/.*
+^intl/icu/.*
+^ipc/chromium/.*
+^js/src/ctypes/libffi/.*
+^js/src/dtoa.c.*
+^js/src/jit/arm64/vixl/.*
+^media/gmp-clearkey/0.1/openaes/.*
+^media/kiss_fft/.*
+^media/libav/.*
+^media/libcubeb/.*
+^media/libjpeg/.*
+^media/libmkv/.*
+^media/libnestegg/.*
+^media/libogg/.*
+^media/libopus/.*
+^media/libpng/.*
+^media/libsoundtouch/.*
+^media/libspeex_resampler/.*
+^media/libstagefright/.*
+^media/libtheora/.*
+^media/libtremor/.*
+^media/libvorbis/.*
+^media/libvpx/.*
+^media/libyuv/.*
+^media/mtransport/.*
+^media/openmax_dl/.*
+^media/pocketsphinx/.*
+^media/sphinxbase/.*
+^media/webrtc/trunk/.*
+^memory/jemalloc/src/.*
+^mfbt/decimal/.*
+^mfbt/double-conversion/.*
+^mfbt/lz4.*
+^mobile/android/thirdparty/.*
+^modules/brotli/.*
+^modules/freetype2/.*
+^modules/libbz2/.*
+^modules/libmar/.*
+^modules/zlib/.*
+^netwerk/sctp/src/.*
+^netwerk/srtp/src/.*
+^nsprpub/.*
+^other-licenses/.*
+^security/sandbox/chromium/.*
+^testing/gtest/gmock/.*
+^testing/gtest/gtest/.*
+^toolkit/components/protobuf/.*
+^toolkit/crashreporter/google-breakpad/.*
--- a/.eslintignore
+++ b/.eslintignore
@@ -90,17 +90,16 @@ devtools/client/netmonitor/har/test/**
 devtools/client/projecteditor/**
 devtools/client/promisedebugger/**
 devtools/client/responsivedesign/**
 devtools/client/scratchpad/**
 devtools/client/shadereditor/**
 devtools/client/shared/*.jsm
 devtools/client/shared/webgl-utils.js
 devtools/client/shared/widgets/*.jsm
-devtools/client/sourceeditor/test/*.js
 devtools/client/webaudioeditor/**
 devtools/client/webconsole/**
 !devtools/client/webconsole/panel.js
 !devtools/client/webconsole/jsterm.js
 !devtools/client/webconsole/console-commands.js
 devtools/client/webide/**
 !devtools/client/webide/components/webideCli.js
 devtools/server/*.js
@@ -167,16 +166,17 @@ devtools/shared/pretty-fast/*
 devtools/shared/sourcemap/*
 devtools/shared/sprintfjs/*
 devtools/shared/qrcode/decoder/*
 devtools/shared/qrcode/encoder/*
 devtools/client/shared/demangle.js
 devtools/client/shared/vendor/*
 devtools/client/sourceeditor/codemirror/*.js
 devtools/client/sourceeditor/codemirror/**/*.js
+devtools/client/sourceeditor/test/cm_mode_ruby.js
 devtools/client/sourceeditor/test/codemirror/*
 devtools/client/inspector/markup/test/lib_*
 devtools/client/jsonview/lib/require.js
 devtools/server/actors/utils/automation-timeline.js
 
 # Ignore devtools files testing sourcemaps / code style
 devtools/client/debugger/test/mochitest/code_binary_search.js
 devtools/client/debugger/test/mochitest/code_math.min.js
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -794,19 +794,16 @@
 
                 if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
                   let findBar = this.mTabBrowser.getFindBar(this.mTab);
 
                   // Close the Find toolbar if we're in old-style TAF mode
                   if (findBar.findMode != findBar.FIND_NORMAL) {
                     findBar.close();
                   }
-
-                  // fix bug 253793 - turn off highlight when page changes
-                  findBar.getElement("highlight").checked = false;
                 }
 
                 // Don't clear the favicon if this onLocationChange was
                 // triggered by a pushState or a replaceState (bug 550565) or
                 // a hash change (bug 408415).
                 if (aWebProgress.isLoadingDocument && !isSameDocument) {
                   this.mBrowser.mIconURL = null;
                 }
@@ -2128,38 +2125,40 @@
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
             var aNoReferrer;
             var aUserContextId;
             var aEventDetail;
             var aRelatedBrowser;
             var aOriginPrincipal;
+            var aDisallowInheritPrincipal;
             var aOpener;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
-              aReferrerURI          = params.referrerURI;
-              aReferrerPolicy       = params.referrerPolicy;
-              aCharset              = params.charset;
-              aPostData             = params.postData;
-              aOwner                = params.ownerTab;
-              aAllowThirdPartyFixup = params.allowThirdPartyFixup;
-              aFromExternal         = params.fromExternal;
-              aRelatedToCurrent     = params.relatedToCurrent;
-              aSkipAnimation        = params.skipAnimation;
-              aAllowMixedContent    = params.allowMixedContent;
-              aForceNotRemote       = params.forceNotRemote;
-              aNoReferrer           = params.noReferrer;
-              aUserContextId        = params.userContextId;
-              aEventDetail          = params.eventDetail;
-              aRelatedBrowser       = params.relatedBrowser;
-              aOriginPrincipal      = params.originPrincipal;
-              aOpener               = params.opener;
+              aReferrerURI              = params.referrerURI;
+              aReferrerPolicy           = params.referrerPolicy;
+              aCharset                  = params.charset;
+              aPostData                 = params.postData;
+              aOwner                    = params.ownerTab;
+              aAllowThirdPartyFixup     = params.allowThirdPartyFixup;
+              aFromExternal             = params.fromExternal;
+              aRelatedToCurrent         = params.relatedToCurrent;
+              aSkipAnimation            = params.skipAnimation;
+              aAllowMixedContent        = params.allowMixedContent;
+              aForceNotRemote           = params.forceNotRemote;
+              aNoReferrer               = params.noReferrer;
+              aUserContextId            = params.userContextId;
+              aEventDetail              = params.eventDetail;
+              aRelatedBrowser           = params.relatedBrowser;
+              aOriginPrincipal          = params.originPrincipal;
+              aDisallowInheritPrincipal = params.disallowInheritPrincipal;
+              aOpener                   = params.opener;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -2236,31 +2235,33 @@
             t.dispatchEvent(evt);
 
             if (!usingPreloadedContent && aOriginPrincipal) {
               b.createAboutBlankContentViewer(aOriginPrincipal);
             }
 
             // If we didn't swap docShells with a preloaded browser
             // then let's just continue loading the page normally.
-            if (!usingPreloadedContent && !uriIsAboutBlank) {
+            if (!usingPreloadedContent && (!uriIsAboutBlank || aDisallowInheritPrincipal)) {
               // pretend the user typed this so it'll be available till
               // the document successfully loads
               if (aURI && gInitialPages.indexOf(aURI) == -1)
                 b.userTypedValue = aURI;
 
               let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
               if (aAllowThirdPartyFixup) {
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
               }
               if (aFromExternal)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
               if (aAllowMixedContent)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
+              if (aDisallowInheritPrincipal)
+                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
               try {
                 b.loadURIWithFlags(aURI, {
                   flags,
                   referrerURI: aNoReferrer ? null: aReferrerURI,
                   referrerPolicy: aReferrerPolicy,
                   charset: aCharset,
                   postData: aPostData,
                 });
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -140,18 +140,17 @@ add_task(function* checkWrongSystemTimeW
   let formatter = new Intl.DateTimeFormat();
 
   // pretend we have a positively skewed (ahead) system time
   let serverDate = new Date("2015/10/27");
   let serverDateFmt = formatter.format(serverDate);
   let localDateFmt = formatter.format(new Date());
 
   let skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
-  yield new Promise(r => SpecialPowers.pushPrefEnv({set:
-    [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with a skewed clock");
   let message = yield Task.spawn(setUpPage);
 
   isnot(message.divDisplay, "none", "Wrong time message information is visible");
   ok(message.text.includes("because your clock appears to show the wrong time"),
      "Correct error message found");
   ok(message.text.includes("expired.example.com"), "URL found in error message");
@@ -162,47 +161,44 @@ add_task(function* checkWrongSystemTimeW
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // pretend we have a negatively skewed (behind) system time
   serverDate = new Date();
   serverDate.setYear(serverDate.getFullYear() + 1);
   serverDateFmt = formatter.format(serverDate);
 
   skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
-  yield new Promise(r => SpecialPowers.pushPrefEnv({set:
-    [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with a skewed clock");
   message = yield Task.spawn(setUpPage);
 
   isnot(message.divDisplay, "none", "Wrong time message information is visible");
   ok(message.text.includes("because your clock appears to show the wrong time"),
      "Correct error message found");
   ok(message.text.includes("expired.example.com"), "URL found in error message");
   ok(message.systemDate.includes(localDateFmt), "correct local date displayed");
   ok(message.actualDate.includes(serverDateFmt), "correct server date displayed");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // pretend we only have a slightly skewed system time, four hours
   skew = 60 * 60 * 4;
-  yield new Promise(r => SpecialPowers.pushPrefEnv({set:
-    [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with an only slightly skewed clock");
   message = yield Task.spawn(setUpPage);
 
   is(message.divDisplay, "none", "Wrong time message information is not visible");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   // now pretend we have no skewed system time
   skew = 0;
-  yield new Promise(r => SpecialPowers.pushPrefEnv({set:
-    [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r));
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
 
   info("Loading a bad cert page with no skewed clock");
   message = yield Task.spawn(setUpPage);
 
   is(message.divDisplay, "none", "Wrong time message information is not visible");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/base/content/test/general/browser_bug537013.js
+++ b/browser/base/content/test/general/browser_bug537013.js
@@ -40,17 +40,17 @@ function test() {
   });
   texts.forEach(aText => addTabWithText(aText));
 
   // Set up the first tab
   gBrowser.selectedTab = tabs[0];
 
   setFindString(texts[0]);
   // Turn on highlight for testing bug 891638
-  gFindBar.getElement("highlight").checked = true;
+  gFindBar.toggleHighlight(true);
 
   // Make sure the second tab is correct, then set it up
   gBrowser.selectedTab = tabs[1];
   gBrowser.selectedTab.addEventListener("TabFindInitialized", continueTests1);
   // Initialize the findbar
   gFindBar;
 }
 function continueTests1() {
@@ -67,30 +67,29 @@ function continueTests1() {
   gBrowser.selectedTab = tabs[0];
   ok(!gFindBar.hidden, "First tab shows find bar!");
   // When the Find Clipboard is supported, this test not relevant.
   if (!HasFindClipboard)
     is(gFindBar._findField.value, texts[0], "First tab persists find value!");
   ok(gFindBar.getElement("highlight").checked,
      "Highlight button state persists!");
 
-  // While we're here, let's test bug 253793
+  // While we're here, let's test the backout of bug 253793.
   gBrowser.reload();
   gBrowser.addEventListener("DOMContentLoaded", continueTests2, true);
 }
 
 function continueTests2() {
   gBrowser.removeEventListener("DOMContentLoaded", continueTests2, true);
-  waitForCondition(() => !gFindBar.getElement("highlight").checked,
-                   continueTests3,
-                   "Highlight never reset!");
+  ok(gFindBar.getElement("highlight").checked, "Highlight never reset!");
+  continueTests3();
 }
 
 function continueTests3() {
-  ok(!gFindBar.getElement("highlight").checked, "Highlight button reset!");
+  ok(gFindBar.getElement("highlight").checked, "Highlight button reset!");
   gFindBar.close();
   ok(gFindBar.hidden, "First tab doesn't show find bar!");
   gBrowser.selectedTab = tabs[1];
   ok(!gFindBar.hidden, "Second tab shows find bar!");
   // Test for bug 892384
   is(gFindBar._findField.getAttribute("focused"), "true",
      "Open findbar refocused on tab change!");
   gURLBar.focus();
@@ -124,13 +123,13 @@ function continueTests3() {
 // Test that findbar gets restored when a tab is moved to a new window.
 function checkNewWindow() {
   ok(!newWindow.gFindBar.hidden, "New window shows find bar!");
   // Disabled the following assertion due to intermittent failure on OSX 10.6 Debug.
   if (!HasFindClipboard) {
     is(newWindow.gFindBar._findField.value, texts[1],
        "New window find bar has correct find value!");
   }
-  ok(!newWindow.gFindBar.getElement("find-next").disabled,
-     "New window findbar has enabled buttons!");
+  ok(newWindow.gFindBar.getElement("find-next").disabled,
+     "New window findbar has disabled buttons!");
   newWindow.close();
   finish();
 }
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -543,16 +543,20 @@ extensions.registerSchemaAPI("tabs", "ad
               if (!containerId) {
                 return Promise.reject({message: `No cookie store exists with ID ${createProperties.cookieStoreId}`});
               }
 
               options.userContextId = containerId;
             }
           }
 
+          // Make sure things like about:blank and data: URIs never inherit,
+          // and instead always get a NullPrincipal.
+          options.disallowInheritPrincipal = true;
+
           tabListener.initTabReady();
           let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
 
           let active = true;
           if (createProperties.active !== null) {
             active = createProperties.active;
           }
           if (active) {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
@@ -83,16 +83,30 @@ add_task(function* testExecuteScript() {
           browser.test.assertEq(2, result.length, "Result has correct length");
 
           browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "First result is correct");
           browser.test.assertEq("http://mochi.test:8888/", result[1], "Second result is correct");
         }),
 
         browser.tabs.executeScript({
           code: "location.href;",
+          allFrames: true,
+          matchAboutBlank: true,
+        }).then(result => {
+          browser.test.assertTrue(Array.isArray(result), "Result is an array");
+
+          browser.test.assertEq(3, result.length, "Result has correct length");
+
+          browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "First result is correct");
+          browser.test.assertEq("http://mochi.test:8888/", result[1], "Second result is correct");
+          browser.test.assertEq("about:blank", result[2], "Thirds result is correct");
+        }),
+
+        browser.tabs.executeScript({
+          code: "location.href;",
           runAt: "document_end",
         }).then(result => {
           browser.test.assertEq(1, result.length, "Expected callback result");
           browser.test.assertEq("string", typeof result[0], "Result is a string");
 
           browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "Result is correct");
         }),
 
@@ -182,16 +196,22 @@ add_task(function* testExecuteScript() {
         browser.tabs.create({url: "http://example.com/"}).then(async tab => {
           let result = await browser.tabs.executeScript(tab.id, {code: "location.href"});
 
           browser.test.assertEq("http://example.com/", result[0], "Script executed correctly in new tab");
 
           await browser.tabs.remove(tab.id);
         }),
 
+        browser.tabs.create({url: "about:blank"}).then(async tab => {
+          const result = await browser.tabs.executeScript(tab.id, {code: "location.href", matchAboutBlank: true});
+          browser.test.assertEq("about:blank", result[0], "Script executed correctly in new tab");
+          await browser.tabs.remove(tab.id);
+        }),
+
         new Promise(resolve => {
           browser.runtime.onMessage.addListener(message => {
             browser.test.assertEq("script ran", message, "Expected runtime message");
             resolve();
           });
         }),
       ]);
 
--- a/browser/components/extensions/test/browser/file_iframe_document.html
+++ b/browser/components/extensions/test/browser/file_iframe_document.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <title></title>
 </head>
 <body>
   <iframe src="/"></iframe>
+  <iframe src="about:blank"></iframe>
 </body>
 </html>
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -641,35 +641,36 @@ nsWindowsShellService::LaunchControlPane
     pAARUI->Release();
   }
   return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsWindowsShellService::LaunchControlPanelDefaultPrograms()
 {
-  // Default Programs is a Vista+ feature
-  if (!IsVistaOrLater()) {
+  // This Default Programs feature is Win7+ only.
+  if (!IsWin7OrLater()) {
     return NS_ERROR_FAILURE;
   }
 
   // Build the path control.exe path safely
   WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' };
   if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
     return NS_ERROR_FAILURE;
   }
   LPCWSTR controlEXE = L"control.exe";
   if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) {
     return NS_ERROR_FAILURE;
   }
   if (!PathAppendW(controlEXEPath, controlEXE)) {
     return NS_ERROR_FAILURE;
   }
 
-  WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram";
+  WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page "
+    "pageDefaultProgram\\pageAdvancedSettings?pszAppName=" APP_REG_NAME;
   STARTUPINFOW si = {sizeof(si), 0};
   si.dwFlags = STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_SHOWDEFAULT;
   PROCESS_INFORMATION pi = {0};
   if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE,
                       0, nullptr, nullptr, &si, &pi)) {
     return NS_ERROR_FAILURE;
   }
@@ -723,18 +724,20 @@ SettingsAppBelievesConnected()
   }
 
   return !!value;
 }
 
 nsresult
 nsWindowsShellService::LaunchModernSettingsDialogDefaultApps()
 {
-  if (!IsWindowsLogonConnected() && SettingsAppBelievesConnected()) {
-    // Use the classic Control Panel to work around a bug of Windows 10.
+  if (!IsWindowsBuildOrLater(14965) &&
+      !IsWindowsLogonConnected() && SettingsAppBelievesConnected()) {
+    // Use the classic Control Panel to work around a bug of older
+    // builds of Windows 10.
     return LaunchControlPanelDefaultPrograms();
   }
 
   IApplicationActivationManager* pActivator;
   HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager,
                                 nullptr,
                                 CLSCTX_INPROC,
                                 IID_IApplicationActivationManager,
--- a/build/moz.configure/compile-checks.configure
+++ b/build/moz.configure/compile-checks.configure
@@ -70,22 +70,22 @@ def check_header(header, language='C++',
 @template
 def check_headers(*headers, **kwargs):
     checks = []
     for header in headers:
         checks.append(check_header(header, **kwargs))
     return checks
 
 
-@depends(c_compiler)
-def warnings_cflags(c_compiler):
+@dependable
+def warnings_cflags():
     return []
 
-@depends(cxx_compiler)
-def warnings_cxxflags(cxx_compiler):
+@dependable
+def warnings_cxxflags():
     return []
 
 
 # Tests whether GCC or clang support the given warning flag, and if it is,
 # add it to the list of warning flags for the build.
 # - `warning` is the warning flag (e.g. -Wfoo)
 # - `compiler` (optional) is the compiler to test against (c_compiler or
 #   cxx_compiler, from toolchain.configure). When omitted, both compilers
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -487,18 +487,18 @@ add_old_configure_assignment('HAVE_64BIT
 
 # Autoconf needs these set
 @depends(host)
 def host_for_old_configure(host):
     return '--host=%s' % host.alias
 
 add_old_configure_arg(host_for_old_configure)
 
-@depends(host, target)
-def target_for_old_configure(host, target):
+@depends(target)
+def target_for_old_configure(target):
     target_alias = target.alias
     # old-configure does plenty of tests against $target and $target_os
     # and expects darwin for iOS, so make it happy.
     if target.os == 'iOS':
         target_alias = target_alias.replace('-ios', '-darwin')
     return '--target=%s' % target_alias
 
 add_old_configure_arg(target_for_old_configure)
@@ -754,20 +754,20 @@ def project_flag(env=None, set_for_old_c
 
 # milestone.is_nightly corresponds to cases NIGHTLY_BUILD is set.
 @depends(milestone, '--help')
 def enabled_in_nightly(milestone, _):
     return milestone.is_nightly
 
 # Set the MOZ_CONFIGURE_OPTIONS variable with all the options that
 # were passed somehow (environment, command line, mozconfig)
-@depends(mozconfig_options)
+@dependable
 @imports(_from='mozbuild.shellutil', _import='quote')
 @imports('__sandbox__')
-def all_configure_options(_):
+def all_configure_options():
     result = []
     previous = None
     for option in __sandbox__._options.itervalues():
         # __sandbox__._options contains items for both option.name and
         # option.env. But it's also an OrderedDict, meaning both are
         # consecutive.
         # Also ignore OLD_CONFIGURE and MOZCONFIG because they're not
         # interesting.
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -66,23 +66,23 @@ def rust_compiler(value, rustc, rustc_in
             version {} of the 'rustc' toolchain and make sure it is
             first in your path.
             You can verify this by typing 'rustc --version'.
             '''.format(version, min_version)))
         return True
 
 set_config('MOZ_RUST', rust_compiler)
 
-@depends(rust_compiler, rustc, target, cross_compiling)
+@depends(rust_compiler, rustc, target)
 @imports('os')
 @imports('subprocess')
 @imports(_from='mozbuild.configure.util', _import='LineIO')
 @imports(_from='mozbuild.shellutil', _import='quote')
 @imports(_from='tempfile', _import='mkstemp')
-def rust_target(rust_compiler, rustc, target, cross_compiling):
+def rust_target(rust_compiler, rustc, target):
     if rust_compiler:
         # Rust's --target options are similar to, but not exactly the same
         # as, the autoconf-derived targets we use.  An example would be that
         # Rust uses distinct target triples for targetting the GNU C++ ABI
         # and the MSVC C++ ABI on Win32, whereas autoconf has a single
         # triple and relies on the user to ensure that everything is
         # compiled for the appropriate ABI.  We need to perform appropriate
         # munging to get the correct option to rustc.
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -151,18 +151,18 @@ def using_compiler_wrapper(compiler_wrap
 set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
 
 
 # Cross-compilation related things.
 # ==============================================================
 js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
           help='Prefix for the target toolchain')
 
-@depends('--with-toolchain-prefix', target, host, cross_compiling)
-def toolchain_prefix(value, target, host, cross_compiling):
+@depends('--with-toolchain-prefix', target, cross_compiling)
+def toolchain_prefix(value, target, cross_compiling):
     if value:
         return tuple(value)
     if cross_compiling:
         return ('%s-' % target.toolchain, '%s-' % target.alias)
 
 @depends(toolchain_prefix, target)
 def first_toolchain_prefix(toolchain_prefix, target):
     # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
@@ -878,18 +878,18 @@ add_old_configure_assignment('MOZ_DEBUG_
 @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)
 
-@depends(c_compiler, target, check_build_environment)
-def visibility_flags(c_compiler, target, env):
+@depends(target, check_build_environment)
+def visibility_flags(target, env):
     if target.os != 'WINNT':
         if target.kernel == 'Darwin':
             return ('-fvisibility=hidden', '-fvisibility-inlines-hidden')
         return ('-I%s/system_wrappers' % os.path.join(env.dist),
                 '-include',
                 '%s/config/gcc_hidden.h' % env.topsrcdir)
 
 @depends(target, visibility_flags)
--- a/config/external/nss/nss.symbols
+++ b/config/external/nss/nss.symbols
@@ -685,16 +685,17 @@ SSL_ResetHandshake
 SSL_SendAdditionalKeyShares
 SSL_SetCanFalseStartCallback
 SSL_SetDowngradeCheckVersion
 SSL_SetNextProtoNego
 SSL_SetPKCS11PinArg
 SSL_SetSockPeerID
 SSL_SetSRTPCiphers
 SSL_SetStapledOCSPResponses
+SSL_SetTrustAnchors
 SSL_SetURL
 SSL_ShutdownServerSessionIDCache
 SSL_SignatureSchemePrefSet
 SSL_SNISocketConfigHook
 SSL_VersionRangeGet
 SSL_VersionRangeGetDefault
 SSL_VersionRangeGetSupported
 SSL_VersionRangeSet
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -56,15 +56,16 @@ skip-if = os == "linux" && !debug # Bug 
 [browser_animation_timeline_rewind_button.js]
 [browser_animation_timeline_scrubber_exists.js]
 [browser_animation_timeline_scrubber_movable.js]
 [browser_animation_timeline_scrubber_moves.js]
 [browser_animation_timeline_setCurrentTime.js]
 [browser_animation_timeline_shows_delay.js]
 [browser_animation_timeline_shows_endDelay.js]
 [browser_animation_timeline_shows_iterations.js]
+[browser_animation_timeline_shows_name_label.js]
 [browser_animation_timeline_shows_time_info.js]
 [browser_animation_timeline_takes_rate_into_account.js]
 [browser_animation_timeline_ui.js]
 [browser_animation_toggle_button_resets_on_navigate.js]
 [browser_animation_toggle_button_toggles_animations.js]
 [browser_animation_toolbar_exists.js]
 [browser_animation_ui_updates_when_animation_data_changes.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_timeline_shows_name_label.js
@@ -0,0 +1,46 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Check the text content and width of name label.
+
+add_task(function* () {
+  yield addTab(URL_ROOT + "doc_simple_animation.html");
+  let {inspector, panel} = yield openAnimationInspector();
+
+  info("Selecting 'simple-animation' animation which is running on compositor");
+  yield selectNodeAndWaitForAnimations(".animated", inspector);
+  checkNameLabel(panel.animationsTimelineComponent.rootWrapperEl, "simple-animation");
+
+  info("Selecting 'no-compositor' animation which is not running on compositor");
+  yield selectNodeAndWaitForAnimations(".no-compositor", inspector);
+  checkNameLabel(panel.animationsTimelineComponent.rootWrapperEl, "no-compositor");
+});
+
+function checkNameLabel(rootWrapperEl, expectedLabelContent) {
+  const timeblockEl = rootWrapperEl.querySelector(".time-block");
+  const labelEl = rootWrapperEl.querySelector(".name div");
+  is(labelEl.textContent, expectedLabelContent,
+     `Text content of labelEl sould be ${ expectedLabelContent }`);
+
+  // Expand timeblockEl to avoid max-width of the label.
+  timeblockEl.style.width = "10000px";
+  const originalLabelWidth = labelEl.clientWidth;
+  ok(originalLabelWidth < timeblockEl.clientWidth / 2,
+     "Label width should be less than 50%");
+
+  // Set timeblockEl width to double of original label width.
+  timeblockEl.style.width = `${ originalLabelWidth * 2 }px`;
+  is(labelEl.clientWidth + labelEl.offsetLeft, originalLabelWidth,
+     `Label width + offsetLeft should be ${ originalLabelWidth }px`);
+
+  // Shrink timeblockEl to enable max-width.
+  timeblockEl.style.width = `${ originalLabelWidth }px`;
+  is(labelEl.clientWidth + labelEl.offsetLeft,
+     Math.round(timeblockEl.clientWidth / 2),
+     "Label width + offsetLeft should be half of timeblockEl");
+}
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -236,16 +236,17 @@ sticky_pref("devtools.theme", "light");
 #endif
 
 // Web console filters
 pref("devtools.webconsole.filter.error", true);
 pref("devtools.webconsole.filter.warn", true);
 pref("devtools.webconsole.filter.info", true);
 pref("devtools.webconsole.filter.log", true);
 pref("devtools.webconsole.filter.debug", true);
+pref("devtools.webconsole.filter.css", false);
 pref("devtools.webconsole.filter.net", false);
 pref("devtools.webconsole.filter.netxhr", false);
 // Deprecated - old console frontend
 pref("devtools.webconsole.filter.network", true);
 pref("devtools.webconsole.filter.networkinfo", false);
 pref("devtools.webconsole.filter.netwarn", true);
 pref("devtools.webconsole.filter.csserror", true);
 pref("devtools.webconsole.filter.cssparser", false);
--- a/devtools/client/shared/components/reps/element-node.js
+++ b/devtools/client/shared/components/reps/element-node.js
@@ -82,24 +82,41 @@ define(function (require, exports, modul
         "<",
         nodeNameElement,
         ...attributeElements,
         ">",
       ];
     },
 
     render: function () {
-      let {object, mode} = this.props;
+      let {
+        object,
+        mode,
+        onDOMNodeMouseOver,
+        onDOMNodeMouseOut
+      } = this.props;
       let elements = this.getElements(object, mode);
-      const baseElement = span({className: "objectBox"}, ...elements);
+      let objectLink = this.props.objectLink || span;
 
-      if (this.props.objectLink) {
-        return this.props.objectLink({object}, baseElement);
+      let baseConfig = {className: "objectBox objectBox-node"};
+      if (onDOMNodeMouseOver) {
+        Object.assign(baseConfig, {
+          onMouseOver: _ => onDOMNodeMouseOver(object)
+        });
       }
-      return baseElement;
+
+      if (onDOMNodeMouseOut) {
+        Object.assign(baseConfig, {
+          onMouseOut: onDOMNodeMouseOut
+        });
+      }
+
+      return objectLink({object},
+        span(baseConfig, ...elements)
+      );
     },
   });
 
   // Registration
   function supportsObject(object, type) {
     if (!isGrip(object)) {
       return false;
     }
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/reps/error.js
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+  // ReactJS
+  const React = require("devtools/client/shared/vendor/react");
+  // Dependencies
+  const { isGrip } = require("./rep-utils");
+  // Shortcuts
+  const { span } = React.DOM;
+
+  /**
+   * Renders Error objects.
+   */
+  const ErrorRep = React.createClass({
+    displayName: "Error",
+
+    propTypes: {
+      object: React.PropTypes.object.isRequired,
+      mode: React.PropTypes.string
+    },
+
+    render: function () {
+      let object = this.props.object;
+      let preview = object.preview;
+      let name = preview && preview.name
+        ? preview.name
+        : "Error";
+
+      let content = this.props.mode === "tiny"
+        ? name
+        : `${name}: ${preview.message}`;
+
+      if (preview.stack && this.props.mode !== "tiny") {
+        /*
+         * Since Reps are used in the JSON Viewer, we can't localize
+         * the "Stack trace" label (defined in debugger.properties as
+         * "variablesViewErrorStacktrace" property), until Bug 1317038 lands.
+         */
+        content = `${content}\nStack trace:\n${preview.stack}`;
+      }
+
+      let objectLink = this.props.objectLink || span;
+      return (
+        objectLink({object, className: "objectBox-stackTrace"},
+          span({}, content)
+        )
+      );
+    },
+  });
+
+  // Registration
+  function supportsObject(object, type) {
+    if (!isGrip(object)) {
+      return false;
+    }
+    return (object.preview && type === "Error");
+  }
+
+  // Exports from this module
+  exports.ErrorRep = {
+    rep: ErrorRep,
+    supportsObject: supportsObject
+  };
+});
--- a/devtools/client/shared/components/reps/moz.build
+++ b/devtools/client/shared/components/reps/moz.build
@@ -7,16 +7,17 @@
 DevToolsModules(
     'array.js',
     'attribute.js',
     'caption.js',
     'comment-node.js',
     'date-time.js',
     'document.js',
     'element-node.js',
+    'error.js',
     'event.js',
     'function.js',
     'grip-array.js',
     'grip-map.js',
     'grip.js',
     'infinity.js',
     'long-string.js',
     'nan.js',
--- a/devtools/client/shared/components/reps/rep.js
+++ b/devtools/client/shared/components/reps/rep.js
@@ -32,16 +32,17 @@ define(function (require, exports, modul
   const { Event } = require("./event");
   const { Func } = require("./function");
   const { PromiseRep } = require("./promise");
   const { RegExp } = require("./regexp");
   const { StyleSheet } = require("./stylesheet");
   const { CommentNode } = require("./comment-node");
   const { ElementNode } = require("./element-node");
   const { TextNode } = require("./text-node");
+  const { ErrorRep } = require("./error");
   const { Window } = require("./window");
   const { ObjectWithText } = require("./object-with-text");
   const { ObjectWithURL } = require("./object-with-url");
   const { GripArray } = require("./grip-array");
   const { GripMap } = require("./grip-map");
   const { Grip } = require("./grip");
 
   // List of all registered template.
@@ -59,16 +60,17 @@ define(function (require, exports, modul
     LongStringRep,
     Func,
     PromiseRep,
     ArrayRep,
     Document,
     Window,
     ObjectWithText,
     ObjectWithURL,
+    ErrorRep,
     GripArray,
     GripMap,
     Grip,
     Undefined,
     Null,
     StringRep,
     Number,
     SymbolRep,
--- a/devtools/client/shared/components/reps/text-node.js
+++ b/devtools/client/shared/components/reps/text-node.js
@@ -39,30 +39,43 @@ define(function (require, exports, modul
       }
       return "";
     },
 
     render: function () {
       let grip = this.props.object;
       let mode = this.props.mode || "short";
 
+      let baseConfig = {className: "objectBox objectBox-textNode"};
+      if (this.props.onDOMNodeMouseOver) {
+        Object.assign(baseConfig, {
+          onMouseOver: _ => this.props.onDOMNodeMouseOver(grip)
+        });
+      }
+
+      if (this.props.onDOMNodeMouseOut) {
+        Object.assign(baseConfig, {
+          onMouseOut: this.props.onDOMNodeMouseOut
+        });
+      }
+
       if (mode == "short" || mode == "tiny") {
         return (
-          DOM.span({className: "objectBox objectBox-textNode"},
+          DOM.span(baseConfig,
             this.getTitle(grip),
             DOM.span({className: "nodeValue"},
               "\"" + this.getTextContent(grip) + "\""
             )
           )
         );
       }
 
       let objectLink = this.props.objectLink || DOM.span;
       return (
-        DOM.span({className: "objectBox objectBox-textNode"},
+        DOM.span(baseConfig,
           this.getTitle(grip),
           objectLink({
             object: grip
           }, "<"),
           DOM.span({className: "nodeTag"}, "TextNode"),
           " textContent=\"",
           DOM.span({className: "nodeValue"},
             this.getTextContent(grip)
--- a/devtools/client/shared/components/test/mochitest/chrome.ini
+++ b/devtools/client/shared/components/test/mochitest/chrome.ini
@@ -8,16 +8,17 @@ support-files =
 [test_notification_box_02.html]
 [test_notification_box_03.html]
 [test_reps_array.html]
 [test_reps_attribute.html]
 [test_reps_comment-node.html]
 [test_reps_date-time.html]
 [test_reps_document.html]
 [test_reps_element-node.html]
+[test_reps_error.html]
 [test_reps_event.html]
 [test_reps_function.html]
 [test_reps_grip.html]
 [test_reps_grip-array.html]
 [test_reps_grip-map.html]
 [test_reps_infinity.html]
 [test_reps_long-string.html]
 [test_reps_nan.html]
--- a/devtools/client/shared/components/test/mochitest/test_reps_element-node.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_element-node.html
@@ -26,16 +26,19 @@ window.onload = Task.async(function* () 
     yield testBodyNode();
     yield testDocumentElement();
     yield testNode();
     yield testNodeWithLeadingAndTrailingSpacesClassName();
     yield testNodeWithoutAttributes();
     yield testLotsOfAttributes();
     yield testSvgNode();
     yield testSvgNodeInXHTML();
+
+    yield testOnMouseOver();
+    yield testOnMouseOut();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBodyNode() {
     const stub = getGripStub("testBodyNode");
@@ -166,16 +169,49 @@ window.onload = Task.async(function* () 
       "Element node rep has expected text content for XHTML SVG element node");
 
     const tinyRenderedComponent = renderComponent(
       ElementNode.rep, { object: stub, mode: "tiny" });
     is(tinyRenderedComponent.textContent, `svg:circle.svg-element`,
       "Element node rep has expected text content for XHTML SVG element in tiny mode");
   }
 
+  function testOnMouseOver() {
+    const stub = getGripStub("testNode");
+
+    let mouseOverValue;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+    };
+    const renderedComponent = renderComponent(
+      ElementNode.rep, {object: stub, onDOMNodeMouseOver});
+
+    const node = renderedComponent.querySelector(".objectBox-node");
+    TestUtils.Simulate.mouseOver(node);
+
+    is(mouseOverValue, stub, "onDOMNodeMouseOver is called with the expected argument " +
+      "when mouseover is fired on the Rep");
+  }
+
+  function testOnMouseOut() {
+    const stub = getGripStub("testNode");
+
+    let called = false;
+    let onDOMNodeMouseOut = (object) => {
+      called = true;
+    };
+    const renderedComponent = renderComponent(
+      ElementNode.rep, {object: stub, onDOMNodeMouseOut});
+
+    const node = renderedComponent.querySelector(".objectBox-node");
+    TestUtils.Simulate.mouseOut(node);
+
+    is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
+  }
+
   function getGripStub(name) {
     switch (name) {
       case "testBodyNode":
         return {
           "type": "object",
           "actor": "server1.conn1.child1/obj30",
           "class": "HTMLBodyElement",
           "ownPropertyLength": 0,
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/test/mochitest/test_reps_error.html
@@ -0,0 +1,421 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test Error rep
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Rep test - Error</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+"use strict";
+
+window.onload = Task.async(function* () {
+  let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
+  let { ErrorRep } = browserRequire("devtools/client/shared/components/reps/error");
+
+  try {
+    // Test errors with different properties
+    yield testSimpleError();
+    yield testMultilineStackError();
+    yield testErrorWithoutStacktrace();
+
+    // Test different kind of errors
+    yield testEvalError();
+    yield testInternalError();
+    yield testRangeError();
+    yield testReferenceError();
+    yield testSyntaxError();
+    yield testTypeError();
+    yield testURIError();
+  } catch (e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+
+  function testSimpleError() {
+    // Test object = `new Error("Error message")`
+    const stub = getGripStub("testSimpleError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for Error object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "Error: Error message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:1:13\n",
+      "Error Rep has expected text content for a simple error");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "Error",
+      "Error Rep has expected text content for a simple error in tiny mode");
+  }
+
+  function testMultilineStackError() {
+    /*
+     * Test object = `
+     *   function errorFoo() {
+     *     errorBar();
+     *   }
+     *   function errorBar() {
+     *     console.log(new Error("bar"));
+     *   }
+     *   errorFoo();`
+     */
+    const stub = getGripStub("testMultilineStackError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for Error object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "Error: bar\n" +
+      "Stack trace:\n" +
+      "errorBar@debugger eval code:6:15\n" +
+      "errorFoo@debugger eval code:3:3\n" +
+      "@debugger eval code:8:1\n",
+      "Error Rep has expected text content for an error with a multiple line");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "Error",
+      "Error Rep has expected text content for an error with a multiple line in tiny mode");
+  }
+
+  function testErrorWithoutStacktrace() {
+    const stub = getGripStub("testErrorWithoutStacktrace");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for Error object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "Error: Error message",
+      "Error Rep has expected text content for an error without stacktrace");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "Error",
+      "Error Rep has expected text content for an error without stacktrace in tiny mode");
+  }
+
+  function testEvalError() {
+    // Test object = `new EvalError("EvalError message")`
+    const stub = getGripStub("testEvalError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for EvalError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "EvalError: EvalError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:10:13\n",
+      "Error Rep has expected text content for an EvalError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "EvalError",
+      "Error Rep has expected text content for an EvalError in tiny mode");
+  }
+
+  function testInternalError() {
+    // Test object = `new InternalError("InternalError message")`
+    const stub = getGripStub("testInternalError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for InternalError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "InternalError: InternalError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:11:13\n",
+      "Error Rep has expected text content for an InternalError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "InternalError",
+      "Error Rep has expected text content for an InternalError in tiny mode");
+  }
+
+  function testRangeError() {
+    // Test object = `new RangeError("RangeError message")`
+    const stub = getGripStub("testRangeError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for RangeError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "RangeError: RangeError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:12:13\n",
+      "Error Rep has expected text content for RangeError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "RangeError",
+      "Error Rep has expected text content for RangeError in tiny mode");
+  }
+
+  function testReferenceError() {
+    // Test object = `new ReferenceError("ReferenceError message"`
+    const stub = getGripStub("testReferenceError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for ReferenceError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "ReferenceError: ReferenceError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:13:13\n",
+      "Error Rep has expected text content for ReferenceError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "ReferenceError",
+      "Error Rep has expected text content for ReferenceError in tiny mode");
+  }
+
+  function testSyntaxError() {
+    // Test object = `new SyntaxError("SyntaxError message"`
+    const stub = getGripStub("testSyntaxError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for SyntaxError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "SyntaxError: SyntaxError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:14:13\n",
+      "Error Rep has expected text content for SyntaxError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "SyntaxError",
+      "SyntaxError Rep has expected text content for SyntaxError in tiny mode");
+  }
+
+  function testTypeError() {
+    // Test object = `new TypeError("TypeError message"`
+    const stub = getGripStub("testTypeError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for TypeError`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "TypeError: TypeError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:15:13\n",
+      "Error Rep has expected text content for TypeError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "TypeError",
+      "Error Rep has expected text content for a TypeError in tiny mode");
+  }
+
+  function testURIError() {
+    // Test object = `new URIError("URIError message")`
+    const stub = getGripStub("testURIError");
+    const renderedRep = shallowRenderComponent(Rep, {object: stub});
+    is(renderedRep.type, ErrorRep.rep,
+      `Rep correctly selects ${ErrorRep.rep.displayName} for URIError object`);
+
+    const renderedComponent = renderComponent(ErrorRep.rep, {object: stub});
+    is(renderedComponent.textContent,
+      "URIError: URIError message\n" +
+      "Stack trace:\n" +
+      "@debugger eval code:16:13\n",
+      "Error Rep has expected text content for URIError");
+
+    const tinyRenderedComponent = renderComponent(ErrorRep.rep, {object: stub, mode: "tiny"});
+    is(tinyRenderedComponent.textContent,
+      "URIError",
+      "Error Rep has expected text content for URIError in tiny mode");
+  }
+
+  function getGripStub(name) {
+    switch (name) {
+      case "testSimpleError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1020",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "Error",
+            "message": "Error message",
+            "stack": "@debugger eval code:1:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 1,
+            "columnNumber": 13
+          }
+        };
+      case "testMultilineStackError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1021",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "Error",
+            "message": "bar",
+            "stack": "errorBar@debugger eval code:6:15\n" +
+                     "errorFoo@debugger eval code:3:3\n" +
+                     "@debugger eval code:8:1\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 6,
+            "columnNumber": 15
+          }
+        };
+      case "testErrorWithoutStacktrace":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1020",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "Error",
+            "message": "Error message",
+          }
+        };
+      case "testEvalError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1022",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "EvalError",
+            "message": "EvalError message",
+            "stack": "@debugger eval code:10:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 10,
+            "columnNumber": 13
+          }
+        };
+      case "testInternalError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1023",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "InternalError",
+            "message": "InternalError message",
+            "stack": "@debugger eval code:11:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 11,
+            "columnNumber": 13
+          }
+        };
+      case "testRangeError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1024",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "RangeError",
+            "message": "RangeError message",
+            "stack": "@debugger eval code:12:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 12,
+            "columnNumber": 13
+          }
+        };
+      case "testReferenceError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1025",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "ReferenceError",
+            "message": "ReferenceError message",
+            "stack": "@debugger eval code:13:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 13,
+            "columnNumber": 13
+          }
+        };
+      case "testSyntaxError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1026",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "SyntaxError",
+            "message": "SyntaxError message",
+            "stack": "@debugger eval code:14:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 14,
+            "columnNumber": 13
+          }
+        };
+      case "testTypeError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1027",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "TypeError",
+            "message": "TypeError message",
+            "stack": "@debugger eval code:15:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 15,
+            "columnNumber": 13
+          }
+        };
+      case "testURIError":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj1028",
+          "class": "Error",
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Error",
+            "name": "URIError",
+            "message": "URIError message",
+            "stack": "@debugger eval code:16:13\n",
+            "fileName": "debugger eval code",
+            "lineNumber": 16,
+            "columnNumber": 13
+          }
+        };
+    }
+
+    return null;
+  }
+});
+</script>
+</pre>
+</body>
+</html>
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
@@ -33,16 +33,21 @@ window.onload = Task.async(function* () 
     yield testMaxProps();
     yield testMoreThanShortMaxProps();
     yield testMoreThanLongMaxProps();
     yield testRecursiveArray();
     yield testPreviewLimit();
     yield testNamedNodeMap();
     yield testNodeList();
     yield testDocumentFragment();
+
+    yield testOnMouseOver();
+    yield testOnMouseOut();
+
+
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBasic() {
     // Test array: `[]`
@@ -300,16 +305,53 @@ window.onload = Task.async(function* () 
         mode: "long",
         expectedOutput: longOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
+
+
+  function testOnMouseOver() {
+    const stub = getGripStub("testNodeList");
+
+    let mouseOverValue;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+    };
+    const renderedComponent = renderComponent(
+      GripArray.rep, {object: stub, onDOMNodeMouseOver});
+
+    const nodes = renderedComponent.querySelectorAll(".objectBox-node");
+    nodes.forEach((node, index) => {
+      TestUtils.Simulate.mouseOver(node);
+
+      is(mouseOverValue, stub.preview.items[index], "onDOMNodeMouseOver is called with " +
+        "the expected argument when mouseover is fired on the Rep");
+    });
+  }
+
+  function testOnMouseOut() {
+    const stub = getGripStub("testNodeList");
+
+    let called = false;
+    let onDOMNodeMouseOut = (object) => {
+      called = true;
+    };
+    const renderedComponent = renderComponent(
+      GripArray.rep, {object: stub, onDOMNodeMouseOut});
+
+    const node = renderedComponent.querySelector(".objectBox-node");
+    TestUtils.Simulate.mouseOut(node);
+
+    is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
+  }
+
   function getGripStub(functionName) {
     switch (functionName) {
       case "testBasic":
         return {
           "type": "object",
           "class": "Array",
           "actor": "server1.conn0.obj35",
           "extensible": true,
--- a/devtools/client/shared/components/test/mochitest/test_reps_text-node.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_text-node.html
@@ -44,16 +44,19 @@ window.onload = Task.async(function* () 
       object: gripStubs.get("testRendering")
     });
 
     is(renderedRep.type, TextNode.rep,
       `Rep correctly selects ${TextNode.rep.displayName}`);
 
     yield testRendering();
     yield testRenderingWithEOL();
+
+    yield testOnMouseOver();
+    yield testOnMouseOut();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testRendering() {
     const stub = gripStubs.get("testRendering");
@@ -103,13 +106,42 @@ window.onload = Task.async(function* () 
       {
         mode: "long",
         expectedOutput: defaultLongOutput,
       }
     ];
 
     testRepRenderModes(modeTests, "testRenderingWithEOL", TextNode, stub);
   }
+
+  function testOnMouseOver() {
+    const stub = gripStubs.get("testRendering");
+
+    let mouseOverValue;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+    };
+    const renderedComponent = renderComponent(
+      TextNode.rep, {object: stub, onDOMNodeMouseOver});
+
+    TestUtils.Simulate.mouseOver(renderedComponent);
+    is(mouseOverValue, stub, "onDOMNodeMouseOver is called with the expected argument " +
+      "when mouseover is fired on the Rep");
+  }
+
+  function testOnMouseOut() {
+    const stub = gripStubs.get("testRendering");
+
+    let called = false;
+    let onDOMNodeMouseOut = (object) => {
+      called = true;
+    };
+    const renderedComponent = renderComponent(
+      TextNode.rep, {object: stub, onDOMNodeMouseOut});
+
+    TestUtils.Simulate.mouseOut(renderedComponent);
+    is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
+  }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/sourceeditor/test/.eslintrc.js
+++ b/devtools/client/sourceeditor/test/.eslintrc.js
@@ -1,6 +1,10 @@
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
-  "extends": "../../../.eslintrc.mochitests.js"
+  "extends": "../../../.eslintrc.mochitests.js",
+  "globals": {
+    "runCodeMirrorTest": true,
+    "gBrowser": true
+  }
 };
--- a/devtools/client/sourceeditor/test/browser_css_autocompletion.js
+++ b/devtools/client/sourceeditor/test/browser_css_autocompletion.js
@@ -8,17 +8,17 @@ const CSSCompleter = require("devtools/c
 const {InspectorFront} = require("devtools/shared/fronts/inspector");
 
 const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
                 "/test/css_statemachine_testcases.css";
 const TESTS_URI = "http://mochi.test:8888/browser/devtools/client" +
                   "/sourceeditor/test/css_autocompletion_tests.json";
 
 const source = read(CSS_URI);
-const tests = eval(read(TESTS_URI));
+const {tests} = JSON.parse(read(TESTS_URI));
 
 const TEST_URI = "data:text/html;charset=UTF-8," + encodeURIComponent(
   ["<!DOCTYPE html>",
    "<html>",
    " <head>",
    "  <title>CSS State machine tests.</title>",
    "  <style type='text/css'>",
    "#progress {",
@@ -55,17 +55,19 @@ const TEST_URI = "data:text/html;charset
    "  <div id='devtools-toolbarbutton' class='devtools-menulist'></div>",
    "  <div id='devtools-anotherone'></div>",
    "  <div id='devtools-yetagain'></div>",
    "  <div id='devtools-itjustgoeson'></div>",
    "  <div id='devtools-okstopitnow'></div>",
    "  <div class='hidden-labels-box devtools-toolbarbutton devtools-menulist'></div>",
    "  <div class='devtools-menulist'></div>",
    "  <div class='devtools-menulist'></div>",
+   /* eslint-disable max-len */
    "  <tabs class='devtools-toolbarbutton'><tab></tab><tab></tab><tab></tab></tabs><tabs></tabs>",
+   /* eslint-enable max-len */
    "  <button class='category-name visible'></button>",
    "  <div class='devtools-toolbarbutton' label='true'>",
    "   <hbox class='toolbarbutton-menubutton-button'></hbox></div>",
    " </body>",
    " </html>"
   ].join("\n"));
 
 let doc = null;
@@ -73,17 +75,19 @@ let index = 0;
 let completer = null;
 let progress;
 let progressDiv;
 let inspector;
 
 function test() {
   waitForExplicitFinish();
   addTab(TEST_URI).then(function () {
+    /* eslint-disable mozilla/no-cpows-in-tests */
     doc = content.document;
+    /* eslint-enable mozilla/no-cpows-in-tests */
     runTests();
   });
 }
 
 function runTests() {
   progress = doc.getElementById("progress");
   progressDiv = doc.querySelector("#progress > div");
   let target = TargetFactory.forTab(gBrowser.selectedTab);
--- a/devtools/client/sourceeditor/test/browser_css_getInfo.js
+++ b/devtools/client/sourceeditor/test/browser_css_getInfo.js
@@ -51,16 +51,18 @@ const source = [
 // Format of test cases :
 // [
 //  {line, ch}, - The caret position at which the getInfo call should be made
 //  expectedState, - The expected state at the caret
 //  expectedSelector, - The expected selector for the state
 //  expectedProperty, - The expected property name for states value and property
 //  expectedValue, - If state is value, then the expected value
 // ]
+
+/* eslint-disable max-len */
 const tests = [
   [{line: 0, ch: 13}, "selector", ".devtools-toolbar"],
   [{line: 8, ch: 13}, "property", ["#devtools-menu.devtools-menulist",
                                    ".devtools-toolbarbutton#devtools-menu "], "-moz-appearance"],
   [{line: 28, ch: 25}, "value", [".devtools-menulist:active",
                                  "#devtools-toolbarbutton:focus "], "outline-offset", "-4px"],
   [{line: 4, ch: 1}, "null"],
   [{line: 5, ch: 0}, "null"],
@@ -74,16 +76,17 @@ const tests = [
    "linear-gradient(hsla(212,7%,57%,.35),\n              hsla(212,7%,57%,.1)) padding-box"],
   [{line: 16, ch: 3}, "value", ["#devtools-menu.devtools-menulist",
                                 ".devtools-toolbarbutton#devtools-menu "], "background",
    "linear-gradient(hsla(212,7%,57%,.35),\n              hsla(212,7%,57%,.1)) padding-box"],
   [{line: 15, ch: 25}, "value", ["#devtools-menu.devtools-menulist",
                                  ".devtools-toolbarbutton#devtools-menu "], "background",
    "linear-gradient(hsla(212,7%,57%,.35),\n              hsla(212,7%,57%,.1)) padding-box"],
 ];
+/* eslint-enable max-len */
 
 const TEST_URI = "data:text/html;charset=UTF-8," + encodeURIComponent(
   ["<!DOCTYPE html>",
    "<html>",
    " <head>",
    "  <title>CSS contextual information tests.</title>",
    "  <style type='text/css'>",
    "#progress {",
@@ -120,24 +123,28 @@ const TEST_URI = "data:text/html;charset
    " </html>"
   ].join("\n"));
 
 let doc = null;
 function test() {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab();
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+    /* eslint-disable mozilla/no-cpows-in-tests */
     doc = content.document;
+    /* eslint-enable mozilla/no-cpows-in-tests */
     runTests();
   });
   gBrowser.loadURI(TEST_URI);
 }
 
 function runTests() {
-  let completer = new CSSCompleter({cssProperties: getClientCssProperties()});
+  let completer = new CSSCompleter({
+    cssProperties: getClientCssProperties()
+  });
   let matches = (arr, toCheck) => !arr.some((x, i) => x != toCheck[i]);
   let checkState = (expected, actual) => {
     if (expected[0] == "null" && actual == null) {
       return true;
     } else if (expected[0] == actual.state && expected[0] == "selector" &&
                expected[1] == actual.selector) {
       return true;
     } else if (expected[0] == actual.state && expected[0] == "property" &&
--- a/devtools/client/sourceeditor/test/browser_css_statemachine.js
+++ b/devtools/client/sourceeditor/test/browser_css_statemachine.js
@@ -7,17 +7,17 @@
 const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
 
 const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
                 "/test/css_statemachine_testcases.css";
 const TESTS_URI = "http://mochi.test:8888/browser/devtools/client" +
                   "/sourceeditor/test/css_statemachine_tests.json";
 
 const source = read(CSS_URI);
-const tests = eval(read(TESTS_URI));
+const {tests} = JSON.parse(read(TESTS_URI));
 
 const TEST_URI = "data:text/html;charset=UTF-8," + encodeURIComponent(
   ["<!DOCTYPE html>",
    "<html>",
    " <head>",
    "  <title>CSS State machine tests.</title>",
    "  <style type='text/css'>",
    "#progress {",
@@ -54,23 +54,27 @@ const TEST_URI = "data:text/html;charset
    " </html>"
   ].join("\n"));
 
 var doc = null;
 function test() {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab(TEST_URI);
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+    /* eslint-disable mozilla/no-cpows-in-tests */
     doc = content.document;
+    /* eslint-enable mozilla/no-cpows-in-tests */
     runTests();
   });
 }
 
 function runTests() {
-  let completer = new CSSCompleter({cssProperties: getClientCssProperties()});
+  let completer = new CSSCompleter({
+    cssProperties: getClientCssProperties()
+  });
   let checkState = state => {
     if (state[0] == "null" && (!completer.state || completer.state == "null")) {
       return true;
     } else if (state[0] == completer.state && state[0] == "selector" &&
                state[1] == completer.selectorState &&
                state[2] == completer.completing &&
                state[3] == completer.selector) {
       return true;
@@ -84,25 +88,25 @@ function runTests() {
       return true;
     }
     return false;
   };
 
   let progress = doc.getElementById("progress");
   let progressDiv = doc.querySelector("#progress > div");
   let i = 0;
-  for (let test of tests) {
+  for (let testcase of tests) {
     progress.dataset.progress = ++i;
     progressDiv.style.width = 100 * i / tests.length + "%";
-    completer.resolveState(limit(source, test[0]),
-                           {line: test[0][0], ch: test[0][1]});
-    if (checkState(test[1])) {
+    completer.resolveState(limit(source, testcase[0]),
+                           {line: testcase[0][0], ch: testcase[0][1]});
+    if (checkState(testcase[1])) {
       ok(true, "Test " + i + " passed. ");
     } else {
-      ok(false, "Test " + i + " failed. Expected state : [" + test[1] + "] " +
+      ok(false, "Test " + i + " failed. Expected state : [" + testcase[1] + "] " +
          "but found [" + completer.state + ", " + completer.selectorState +
          ", " + completer.completing + ", " +
          (completer.propertyName || completer.selector) + "].");
       progress.classList.add("failed");
     }
   }
   gBrowser.removeCurrentTab();
   finish();
--- a/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
+++ b/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
@@ -1,16 +1,15 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {InspectorFront} = require("devtools/shared/fronts/inspector");
-const AUTOCOMPLETION_PREF = "devtools.editor.autocomplete";
 const TEST_URI = "data:text/html;charset=UTF-8,<html><body><bar></bar>" +
                  "<div id='baz'></div><body></html>";
 
 add_task(function* () {
   yield addTab(TEST_URI);
   yield runTests();
 });
 
@@ -94,17 +93,19 @@ function* testKeyboardCycleForPrefixedSt
 
 function* testKeyboardCSSComma(ed, win) {
   ed.focus();
   ed.setText("b");
   ed.setCursor({line: 1, ch: 1});
 
   let isPopupOpened = false;
   let popupOpened = ed.getAutocompletionPopup().once("popup-opened");
-  popupOpened.then(() => isPopupOpened = true);
+  popupOpened.then(() => {
+    isPopupOpened = true;
+  });
 
   EventUtils.synthesizeKey(",", { }, win);
 
   yield wait(500);
 
   ok(!isPopupOpened, "Autocompletion shouldn't be opened");
 }
 
--- a/devtools/client/sourceeditor/test/browser_editor_find_again.js
+++ b/devtools/client/sourceeditor/test/browser_editor_find_again.js
@@ -10,17 +10,18 @@ const L10N = new LocalizationHelper("dev
 
 const { OS } = Services.appinfo;
 
 // On linux, getting immediately the selection's range here fails, returning
 const FIND_KEY = L10N.getStr("find.key");
 const FINDNEXT_KEY = L10N.getStr("findNext.key");
 const FINDPREV_KEY = L10N.getStr("findPrev.key");
 // the replace's key with the appropriate modifiers based on OS
-const REPLACE_KEY = OS == "Darwin" ? L10N.getStr("replaceAllMac.key") : L10N.getStr("replaceAll.key");
+const REPLACE_KEY = OS == "Darwin" ? L10N.getStr("replaceAllMac.key")
+                                   : L10N.getStr("replaceAll.key");
 
 // values like it's not selected – even if the selection is visible.
 // For the record, setting the selection's range immediately doesn't have
 // any effect.
 // It's like the <input> is not ready yet.
 // Therefore, we trigger the UI focus event to the <input>, waiting for the
 // response.
 // Using a timeout could also work, but that is more precise, ensuring also
--- a/devtools/client/sourceeditor/test/cm_script_injection_test.js
+++ b/devtools/client/sourceeditor/test/cm_script_injection_test.js
@@ -1,8 +1,10 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* global editor */
+
 "use strict";
 
 window.addEventListener("editorReady", function () {
   editor.setText("Script successfully injected!");
 });
--- a/devtools/client/sourceeditor/test/css_autocompletion_tests.json
+++ b/devtools/client/sourceeditor/test/css_autocompletion_tests.json
@@ -1,39 +1,43 @@
-// Test states to be tested for css state machine in css-autocompelter.js file.
-// Test cases are of the following format:
-// [
-//   [
-//     line, // The line location of the cursor
-//     ch    // The column locaiton of the cursor
-//   ],
-//   suggestions // Array of expected results
-// ]
-[
-  [[0, 10], []],
-  [[4,  7], ['.devtools-menulist', '.devtools-toolbarbutton']],
-  [[5,  8], ['-moz-animation', '-moz-animation-delay', '-moz-animation-direction',
-             '-moz-animation-duration', '-moz-animation-fill-mode',
-             '-moz-animation-iteration-count', '-moz-animation-name',
-             '-moz-animation-play-state', '-moz-animation-timing-function',
-             '-moz-appearance']],
-  [[12, 20], ['none', 'number-input']],
-  [[12, 22], ['none']],
-  [[17, 22], ['hsl', 'hsla']],
-  [[19, 10], ['background', 'background-attachment', 'background-blend-mode',
-              'background-clip', 'background-color', 'background-image',
-              'background-origin', 'background-position', 'background-position-x',
-              'background-position-y', 'background-repeat', 'background-size']],
-  [[21,  9], ["-moz-calc", "auto", "calc", "inherit", "initial","unset"]],
-  [[25, 26], ['.devtools-toolbarbutton > tab',
-              '.devtools-toolbarbutton > hbox',
-              '.devtools-toolbarbutton > .toolbarbutton-menubutton-button']],
-  [[25, 31], ['.devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button']],
-  [[29, 20], ['.devtools-menulist:after', '.devtools-menulist:active']],
-  [[30, 10], ['#devtools-anotherone', '#devtools-itjustgoeson', '#devtools-menu',
-              '#devtools-okstopitnow', '#devtools-toolbarbutton', '#devtools-yetagain']],
-  [[39, 39], ['.devtools-toolbarbutton:not([label]) > tab']],
-  [[43, 51], ['.devtools-toolbarbutton:not([checked=true]):hover:after',
-              '.devtools-toolbarbutton:not([checked=true]):hover:active']],
-  [[58, 36], ['!important;']],
-  [[73, 42], [':lang(', ':last-of-type', ':link', ':last-child']],
-  [[77, 25], ['.visible']],
-]
+{
+  "description": [
+    "Test states to be tested for css state machine in css-autocompelter.js file.",
+    "Test cases are of the following format:",
+    "[",
+    "  [",
+    "    line, # The line location of the cursor",
+    "    ch    # The column locaiton of the cursor",
+    "  ],",
+    "  suggestions # Array of expected results",
+    "]"
+  ],
+  "tests": [
+    [[0, 10], []],
+    [[4,  7], [".devtools-menulist", ".devtools-toolbarbutton"]],
+    [[5,  8], ["-moz-animation", "-moz-animation-delay", "-moz-animation-direction",
+               "-moz-animation-duration", "-moz-animation-fill-mode",
+               "-moz-animation-iteration-count", "-moz-animation-name",
+               "-moz-animation-play-state", "-moz-animation-timing-function",
+               "-moz-appearance"]],
+    [[12, 20], ["none", "number-input"]],
+    [[12, 22], ["none"]],
+    [[17, 22], ["hsl", "hsla"]],
+    [[19, 10], ["background", "background-attachment", "background-blend-mode",
+                "background-clip", "background-color", "background-image",
+                "background-origin", "background-position", "background-position-x",
+                "background-position-y", "background-repeat", "background-size"]],
+    [[21,  9], ["-moz-calc", "auto", "calc", "inherit", "initial","unset"]],
+    [[25, 26], [".devtools-toolbarbutton > tab",
+                ".devtools-toolbarbutton > hbox",
+                ".devtools-toolbarbutton > .toolbarbutton-menubutton-button"]],
+    [[25, 31], [".devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button"]],
+    [[29, 20], [".devtools-menulist:after", ".devtools-menulist:active"]],
+    [[30, 10], ["#devtools-anotherone", "#devtools-itjustgoeson", "#devtools-menu",
+                "#devtools-okstopitnow", "#devtools-toolbarbutton", "#devtools-yetagain"]],
+    [[39, 39], [".devtools-toolbarbutton:not([label]) > tab"]],
+    [[43, 51], [".devtools-toolbarbutton:not([checked=true]):hover:after",
+                ".devtools-toolbarbutton:not([checked=true]):hover:active"]],
+    [[58, 36], ["!important;"]],
+    [[73, 42], [":lang(", ":last-of-type", ":link", ":last-child"]],
+    [[77, 25], [".visible"]]
+  ]
+}
--- a/devtools/client/sourceeditor/test/css_statemachine_tests.json
+++ b/devtools/client/sourceeditor/test/css_statemachine_tests.json
@@ -1,84 +1,88 @@
-// Test states to be tested for css state machine in css-autocompelter.js file.
-// Test cases are of the following format:
-// [
-//   [
-//     line, // The line location of the cursor
-//     ch    // The column locaiton of the cursor
-//   ],
-//   [
-//     state,         // one of CSS_STATES
-//     selectorState, // one of SELECTOR_STATES
-//     completing,    // what is being completed
-//     propertyName,  // what property is being completed in case of value state
-//                    // or the current selector that is being completed
-//   ]
-// ]
-[
-  [[0, 10], ['null', '', '', '']],
-  [[4,  3], ['selector', 'class', 'de', '.de']],
-  [[5,  8], ['property', 'null', '-moz-a']],
-  [[5, 21], ['value', 'null', 'no', '-moz-appearance']],
-  [[6, 18], ['property', 'null', 'padding']],
-  [[6, 24], ['value', 'null', '3', 'padding']],
-  [[6, 29], ['property', 'null', 'bo']],
-  [[6, 50], ['value', 'null', '1p', 'border-bottom-width']],
-  [[7, 24], ['value', 'null', 's', 'border-bottom-style']],
-  [[9,  0], ['null', 'null', '', '']],
-  [[10, 6], ['selector', 'id', 'devto', '#devto']],
-  [[10, 17], ['selector', 'class', 'de', '#devtools-menu.de']],
-  [[11,  5], ['selector', 'class', 'devt', '.devt']],
-  [[11, 30], ['selector', 'id', 'devtoo', '.devtools-toolbarbutton#devtoo']],
-  [[12, 10], ['property', 'null', '-moz-app']],
-  [[16, 27], ['value', 'null', 'hsl', 'text-shadow']],
-  [[19, 24], ['value', 'null', 'linear-gra', 'background']],
-  [[19, 55], ['value', 'null', 'hsl', 'background']],
-  [[19, 79], ['value', 'null', 'paddin', 'background']],
-  [[20, 47], ['value', 'null', 'ins', 'box-shadow']],
-  [[22, 15], ['value', 'null', 'inheri', 'color']],
-  [[25, 26], ['selector', 'null', '', '.devtools-toolbarbutton > ']],
-  [[25, 28], ['selector', 'tag', 'hb', '.devtools-toolbarbutton > hb']],
-  [[25, 41], ['selector', 'class', 'toolbarbut', '.devtools-toolbarbutton > hbox.toolbarbut']],
-  [[29, 21], ['selector', 'pseudo', 'ac', '.devtools-menulist:ac']],
-  [[30, 27], ['selector', 'pseudo', 'foc', '#devtools-toolbarbutton:foc']],
-  [[31, 18], ['value', 'null', 'dot', 'outline']],
-  [[32, 25], ['value', 'null', '-4p', 'outline-offset']],
-  [[35, 26], ['selector', 'pseudo', 'no', '.devtools-toolbarbutton:no']],
-  [[35, 28], ['selector', 'null', 'not', '']],
-  [[35, 30], ['selector', 'attribute', 'l', '[l']],
-  [[39, 46], ['selector', 'class', 'toolba', '.devtools-toolbarbutton:not([label]) > .toolba']],
-  [[43, 39], ['selector', 'value', 'tr', '[checked=tr']],
-  [[43, 47], ['selector', 'pseudo', 'hov', '.devtools-toolbarbutton:not([checked=true]):hov']],
-  [[43, 53], ['selector', 'pseudo', 'act', '.devtools-toolbarbutton:not([checked=true]):hover:act']],
-  [[47, 22], ['selector', 'attribute', 'op', '.devtools-menulist[op']],
-  [[47, 33], ['selector', 'value', 'tr', '.devtools-menulist[open =tr']],
-  [[48, 38], ['selector', 'value', 'tr', '.devtools-toolbarbutton[open = tr']],
-  [[49, 40], ['selector', 'value', 'true', '.devtools-toolbarbutton[checked= true']],
-  [[53, 34], ['selector', 'value', '=', '.devtools-toolbarbutton[checked=']],
-  [[58, 38], ['value', 'null', '!impor', 'background-color']],
-  [[61, 41], ['selector', 'pseudo', 'hov', '.devtools-toolbarbutton[checked=true]:hov']],
-  [[65, 47], ['selector', 'class', 'to', '.devtools-toolbarbutton[type=menu-button] > .to']],
-  [[69, 44], ['selector', 'pseudo', 'first-of', '.devtools-sidebar-tabs > tabs > tab:first-of']],
-  [[73, 45], ['selector', 'pseudo', 'last', ':last']],
-  [[77, 27], ['selector', 'class', 'vis', '.vis']],
-  [[78, 34], ['selector', 'class', 'hidd', '.hidden-labels-box.visible ~ .hidd']],
-  [[83,  5], ['media', 'null', 'medi']],
-  [[83, 22], ['media', 'null', '800']],
-  [[84,  9], ['selector', 'class', 'catego', '.catego']],
-  [[89,  9], ['media', 'null', 'al']],
-  [[90,  6], ['selector', 'id', 'err', '#err']],
-  [[93, 11], ['property', 'null', 'backgro']],
-  [[98,  6], ['selector', 'tag', 'butt', 'butt']],
-  [[99, 22], ['value', 'null', '!impor', 'width']],
-  [[103, 5], ['keyframes', 'null', 'ke']],
-  [[104, 7], ['frame', 'null', 'fro']],
-  [[104, 15], ['property', 'null', 'opac']],
-  [[104, 29], ['property', 'null', 'transf']],
-  [[104, 38], ['value', 'null', 'scal', 'transform']],
-  [[105,  8], ['frame', 'null', '']],
-  [[113,  6], ['keyframes', 'null', 'keyfr']],
-  [[114,  4], ['frame', 'null', 'fr']],
-  [[115,  3], ['frame', 'null', '2']],
-  [[117,  8], ['property', 'null', 'opac']],
-  [[117, 16], ['value', 'null', '0', 'opacity']],
-  [[121,  0], ['null', '', '']],
-]
+{
+  "description": [
+    "Test states to be tested for css state machine in css-autocompleter.js file.",
+    "Test cases are of the following format:",
+    "[",
+    "  [",
+    "    line, // The line location of the cursor",
+    "    ch    // The column locaiton of the cursor",
+    "  ],",
+    "  [",
+    "    state,         // one of CSS_STATES",
+    "    selectorState, // one of SELECTOR_STATES",
+    "    completing,    // what is being completed",
+    "    propertyName,  // what property is being completed in case of value state",
+    "                   // or the current selector that is being completed",
+    "  ]",
+    "]"
+  ],
+  "tests": [
+    [[0, 10], ["null", "", "", ""]],
+    [[4,  3], ["selector", "class", "de", ".de"]],
+    [[5,  8], ["property", "null", "-moz-a"]],
+    [[5, 21], ["value", "null", "no", "-moz-appearance"]],
+    [[6, 18], ["property", "null", "padding"]],
+    [[6, 24], ["value", "null", "3", "padding"]],
+    [[6, 29], ["property", "null", "bo"]],
+    [[6, 50], ["value", "null", "1p", "border-bottom-width"]],
+    [[7, 24], ["value", "null", "s", "border-bottom-style"]],
+    [[9,  0], ["null", "null", "", ""]],
+    [[10, 6], ["selector", "id", "devto", "#devto"]],
+    [[10, 17], ["selector", "class", "de", "#devtools-menu.de"]],
+    [[11,  5], ["selector", "class", "devt", ".devt"]],
+    [[11, 30], ["selector", "id", "devtoo", ".devtools-toolbarbutton#devtoo"]],
+    [[12, 10], ["property", "null", "-moz-app"]],
+    [[16, 27], ["value", "null", "hsl", "text-shadow"]],
+    [[19, 24], ["value", "null", "linear-gra", "background"]],
+    [[19, 55], ["value", "null", "hsl", "background"]],
+    [[19, 79], ["value", "null", "paddin", "background"]],
+    [[20, 47], ["value", "null", "ins", "box-shadow"]],
+    [[22, 15], ["value", "null", "inheri", "color"]],
+    [[25, 26], ["selector", "null", "", ".devtools-toolbarbutton > "]],
+    [[25, 28], ["selector", "tag", "hb", ".devtools-toolbarbutton > hb"]],
+    [[25, 41], ["selector", "class", "toolbarbut", ".devtools-toolbarbutton > hbox.toolbarbut"]],
+    [[29, 21], ["selector", "pseudo", "ac", ".devtools-menulist:ac"]],
+    [[30, 27], ["selector", "pseudo", "foc", "#devtools-toolbarbutton:foc"]],
+    [[31, 18], ["value", "null", "dot", "outline"]],
+    [[32, 25], ["value", "null", "-4p", "outline-offset"]],
+    [[35, 26], ["selector", "pseudo", "no", ".devtools-toolbarbutton:no"]],
+    [[35, 28], ["selector", "null", "not", ""]],
+    [[35, 30], ["selector", "attribute", "l", "[l"]],
+    [[39, 46], ["selector", "class", "toolba", ".devtools-toolbarbutton:not([label]) > .toolba"]],
+    [[43, 39], ["selector", "value", "tr", "[checked=tr"]],
+    [[43, 47], ["selector", "pseudo", "hov", ".devtools-toolbarbutton:not([checked=true]):hov"]],
+    [[43, 53], ["selector", "pseudo", "act", ".devtools-toolbarbutton:not([checked=true]):hover:act"]],
+    [[47, 22], ["selector", "attribute", "op", ".devtools-menulist[op"]],
+    [[47, 33], ["selector", "value", "tr", ".devtools-menulist[open =tr"]],
+    [[48, 38], ["selector", "value", "tr", ".devtools-toolbarbutton[open = tr"]],
+    [[49, 40], ["selector", "value", "true", ".devtools-toolbarbutton[checked= true"]],
+    [[53, 34], ["selector", "value", "=", ".devtools-toolbarbutton[checked="]],
+    [[58, 38], ["value", "null", "!impor", "background-color"]],
+    [[61, 41], ["selector", "pseudo", "hov", ".devtools-toolbarbutton[checked=true]:hov"]],
+    [[65, 47], ["selector", "class", "to", ".devtools-toolbarbutton[type=menu-button] > .to"]],
+    [[69, 44], ["selector", "pseudo", "first-of", ".devtools-sidebar-tabs > tabs > tab:first-of"]],
+    [[73, 45], ["selector", "pseudo", "last", ":last"]],
+    [[77, 27], ["selector", "class", "vis", ".vis"]],
+    [[78, 34], ["selector", "class", "hidd", ".hidden-labels-box.visible ~ .hidd"]],
+    [[83,  5], ["media", "null", "medi"]],
+    [[83, 22], ["media", "null", "800"]],
+    [[84,  9], ["selector", "class", "catego", ".catego"]],
+    [[89,  9], ["media", "null", "al"]],
+    [[90,  6], ["selector", "id", "err", "#err"]],
+    [[93, 11], ["property", "null", "backgro"]],
+    [[98,  6], ["selector", "tag", "butt", "butt"]],
+    [[99, 22], ["value", "null", "!impor", "width"]],
+    [[103, 5], ["keyframes", "null", "ke"]],
+    [[104, 7], ["frame", "null", "fro"]],
+    [[104, 15], ["property", "null", "opac"]],
+    [[104, 29], ["property", "null", "transf"]],
+    [[104, 38], ["value", "null", "scal", "transform"]],
+    [[105,  8], ["frame", "null", ""]],
+    [[113,  6], ["keyframes", "null", "keyfr"]],
+    [[114,  4], ["frame", "null", "fr"]],
+    [[115,  3], ["frame", "null", "2"]],
+    [[117,  8], ["property", "null", "opac"]],
+    [[117, 16], ["value", "null", "0", "opacity"]],
+    [[121,  0], ["null", "", ""]]
+  ]
+}
\ No newline at end of file
--- a/devtools/client/sourceeditor/test/head.js
+++ b/devtools/client/sourceeditor/test/head.js
@@ -1,12 +1,15 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* import-globals-from ../../framework/test/shared-head.js */
+/* exported promiseWaitForFocus, setup, ch, teardown, loadHelperScript,
+            limit, ch, read, codemirrorSetStatus */
+
 "use strict";
 
 // shared-head.js handles imports, constants, and utility functions
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
@@ -99,26 +102,26 @@ function loadHelperScript(filePath) {
   let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
   Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
 }
 
 /**
  * This method returns the portion of the input string `source` up to the
  * [line, ch] location.
  */
-function limit(source, [line, ch]) {
+function limit(source, [line, char]) {
   line++;
   let list = source.split("\n");
   if (list.length < line) {
     return source;
   }
   if (line == 1) {
-    return list[0].slice(0, ch);
+    return list[0].slice(0, char);
   }
-  return [...list.slice(0, line - 1), list[line - 1].slice(0, ch)].join("\n");
+  return [...list.slice(0, line - 1), list[line - 1].slice(0, char)].join("\n");
 }
 
 function read(url) {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
   let channel = NetUtil.newChannel({
     uri: url,
--- a/devtools/client/sourceeditor/test/helper_codemirror_runner.js
+++ b/devtools/client/sourceeditor/test/helper_codemirror_runner.js
@@ -1,11 +1,13 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* exported runCodeMirrorTest */
 /* globals codemirrorSetStatus */
 
 "use strict";
 
 function runCodeMirrorTest(browser) {
   let mm = browser.messageManager;
   mm.addMessageListener("setStatus", function listener({data}) {
     let {statusMsg, type, customMsg} = data;
@@ -19,20 +21,22 @@ function runCodeMirrorTest(browser) {
     mm = null;
     finish();
   });
 
   // Interact with the content iframe, giving it a function to
   //  1) Proxy CM test harness calls into ok() calls
   //  2) Detecting when it finishes by checking the DOM and
   //     setting a timeout to check again if not.
+  /* eslint-disable max-len */
   mm.loadFrameScript("data:," +
     "content.wrappedJSObject.mozilla_setStatus = function(statusMsg, type, customMsg) {" +
     "  sendSyncMessage('setStatus', {statusMsg: statusMsg, type: type, customMsg: customMsg});" +
     "};" +
     "function check() { " +
     "  var doc = content.document; var out = doc.getElementById('status'); " +
     "  if (!out || !out.classList.contains('done')) { return setTimeout(check, 100); }" +
     "  sendAsyncMessage('done', { failed: content.wrappedJSObject.failed });" +
     "}" +
     "check();"
   , true);
+  /* eslint-enable max-len */
 }
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -392,20 +392,16 @@ body {
   /* Flex items don't support text-overflow, so a child div is used */
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
   background-color: rgba(255, 255, 255, 0.7);
   max-width: 50%;
 }
 
-.animation-timeline .fast-track .name div {
-  width: calc(100% - var(--fast-track-icon-width));
-}
-
 .animation-timeline .fast-track .name::after {
   /* Animations running on the compositor have the fast-track background image*/
   content: "";
   display: block;
   position: absolute;
   top: 1px;
   right: 0;
   height: 100%;
--- a/devtools/client/webconsole/new-console-output/components/filter-bar.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js
@@ -111,16 +111,22 @@ const FilterBar = createClass({
             label: "Debug",
             filterKey: MESSAGE_LEVEL.DEBUG,
             dispatch
           }),
           dom.span({
             className: "devtools-separator",
           }),
           FilterButton({
+            active: filter.css,
+            label: "CSS",
+            filterKey: "css",
+            dispatch
+          }),
+          FilterButton({
             active: filter.netxhr,
             label: "XHR",
             filterKey: "netxhr",
             dispatch
           }),
           FilterButton({
             active: filter.net,
             label: "Requests",
--- a/devtools/client/webconsole/new-console-output/components/grip-message-body.js
+++ b/devtools/client/webconsole/new-console-output/components/grip-message-body.js
@@ -40,28 +40,37 @@ GripMessageBody.propTypes = {
 function GripMessageBody(props) {
   const { grip, userProvidedStyle, serviceContainer } = props;
 
   let styleObject;
   if (userProvidedStyle && userProvidedStyle !== "") {
     styleObject = cleanupStyle(userProvidedStyle, serviceContainer.createElement);
   }
 
+  let onDOMNodeMouseOver;
+  let onDOMNodeMouseOut;
+  if (serviceContainer) {
+    onDOMNodeMouseOver = (object) => serviceContainer.highlightDomElement(object);
+    onDOMNodeMouseOut = serviceContainer.unHighlightDomElement;
+  }
+
   return (
     // @TODO once there is a longString rep, also turn off quotes for those.
     typeof grip === "string"
       ? StringRep({
         object: grip,
         useQuotes: false,
         mode: props.mode,
         style: styleObject
       })
       : Rep({
         object: grip,
         objectLink: VariablesViewLink,
+        onDOMNodeMouseOver,
+        onDOMNodeMouseOut,
         defaultRep: Grip,
         mode: props.mode,
       })
   );
 }
 
 function cleanupStyle(userProvidedStyle, createElement) {
   // Regular expression that matches the allowed CSS property names.
--- a/devtools/client/webconsole/new-console-output/components/message-container.js
+++ b/devtools/client/webconsole/new-console-output/components/message-container.js
@@ -61,16 +61,17 @@ const MessageContainer = createClass({
 });
 
 function getMessageComponent(message) {
   switch (message.source) {
     case MESSAGE_SOURCE.CONSOLE_API:
       return componentMap.get("ConsoleApiCall");
     case MESSAGE_SOURCE.NETWORK:
       return componentMap.get("NetworkEventMessage");
+    case MESSAGE_SOURCE.CSS:
     case MESSAGE_SOURCE.JAVASCRIPT:
       switch (message.type) {
         case MESSAGE_TYPE.COMMAND:
           return componentMap.get("ConsoleCommand");
         case MESSAGE_TYPE.RESULT:
           return componentMap.get("EvaluationResult");
         // @TODO this is probably not the right behavior, but works for now.
         // Chrome doesn't distinguish between page errors and log messages. We
--- a/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
@@ -35,17 +35,17 @@ function EvaluationResult(props) {
     exceptionDocURL,
     frame,
   } = message;
 
   let messageBody;
   if (message.messageText) {
     messageBody = message.messageText;
   } else {
-    messageBody = GripMessageBody({grip: message.parameters});
+    messageBody = GripMessageBody({grip: message.parameters, serviceContainer});
   }
 
   const topLevelClasses = ["cm-s-mozilla"];
 
   const childProps = {
     source,
     type,
     level,
--- a/devtools/client/webconsole/new-console-output/components/variables-view-link.js
+++ b/devtools/client/webconsole/new-console-output/components/variables-view-link.js
@@ -15,20 +15,20 @@ const {openVariablesView} = require("dev
 
 VariablesViewLink.displayName = "VariablesViewLink";
 
 VariablesViewLink.propTypes = {
   object: PropTypes.object.isRequired
 };
 
 function VariablesViewLink(props) {
-  const { object, children } = props;
+  const { className, object, children } = props;
 
   return (
     dom.a({
       onClick: openVariablesView.bind(null, object),
-      className: "cm-variable",
+      className: className || "cm-variable",
       draggable: false,
     }, children)
   );
 }
 
 module.exports = VariablesViewLink;
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -21,28 +21,30 @@ const actionTypes = {
 const prefs = {
   PREFS: {
     FILTER: {
       ERROR: "devtools.webconsole.filter.error",
       WARN: "devtools.webconsole.filter.warn",
       INFO: "devtools.webconsole.filter.info",
       LOG: "devtools.webconsole.filter.log",
       DEBUG: "devtools.webconsole.filter.debug",
+      CSS: "devtools.webconsole.filter.css",
       NET: "devtools.webconsole.filter.net",
       NETXHR: "devtools.webconsole.filter.netxhr",
     },
     UI: {
       FILTER_BAR: "devtools.webconsole.ui.filterbar"
     }
   }
 };
 
 const chromeRDPEnums = {
   MESSAGE_SOURCE: {
     XML: "xml",
+    CSS: "css",
     JAVASCRIPT: "javascript",
     NETWORK: "network",
     CONSOLE_API: "console-api",
     STORAGE: "storage",
     APPCACHE: "appcache",
     RENDERING: "rendering",
     SECURITY: "security",
     OTHER: "other",
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -53,17 +53,27 @@ NewConsoleOutputWrapper.prototype = {
           return this.toolbox.selectTool("netmonitor").then(panel => {
             return panel.panelWin.NetMonitorController.inspectRequest(requestId);
           });
         },
         sourceMapService: this.toolbox ? this.toolbox._sourceMapService : null,
         openLink: url => this.jsterm.hud.owner.openLink.call(this.jsterm.hud.owner, url),
         createElement: nodename => {
           return this.document.createElementNS("http://www.w3.org/1999/xhtml", nodename);
-        }
+        },
+        highlightDomElement: (grip, options = {}) => {
+          return this.toolbox && this.toolbox.highlighterUtils
+            ? this.toolbox.highlighterUtils.highlightDomValueGrip(grip, options)
+            : null;
+        },
+        unHighlightDomElement: (forceHide = false) => {
+          return this.toolbox && this.toolbox.highlighterUtils
+            ? this.toolbox.highlighterUtils.unhighlight(forceHide)
+            : null;
+        },
       }
     });
     let filterBar = FilterBar({
       serviceContainer: {
         attachRefToHud
       }
     });
     let provider = React.createElement(
--- a/devtools/client/webconsole/new-console-output/reducers/filters.js
+++ b/devtools/client/webconsole/new-console-output/reducers/filters.js
@@ -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/. */
 "use strict";
 
 const Immutable = require("devtools/client/shared/vendor/immutable");
 const constants = require("devtools/client/webconsole/new-console-output/constants");
 
 const FilterState = Immutable.Record({
+  css: false,
   debug: true,
   error: true,
   info: true,
   log: true,
   net: false,
   netxhr: false,
   text: "",
   warn: true,
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -24,16 +24,17 @@ function getAllMessages(state) {
   return prune(
     messages.filter(message => {
       return (
         isInOpenedGroup(message, groups, messagesUI)
         && (
           isUnfilterable(message)
           || (
             matchLevelFilters(message, filters)
+            && matchCssFilters(message, filters)
             && matchNetworkFilters(message, filters)
             && matchSearchFilters(message, filters)
           )
         )
       );
     }),
     logLimit
   );
@@ -91,16 +92,23 @@ function matchLevelFilters(message, filt
 function matchNetworkFilters(message, filters) {
   return (
     message.source !== MESSAGE_SOURCE.NETWORK
     || (filters.get("net") === true && message.isXHR === false)
     || (filters.get("netxhr") === true && message.isXHR === true)
   );
 }
 
+function matchCssFilters(message, filters) {
+  return (
+    message.source != MESSAGE_SOURCE.CSS
+    || filters.get("css") === true
+  );
+}
+
 function matchSearchFilters(message, filters) {
   let text = filters.text || "";
   return (
     text === ""
     // @TODO currently we return true for any object grip. We should find a way to
     // search object grips.
     || (message.parameters !== null && !Array.isArray(message.parameters))
     // Look for a match in location.
--- a/devtools/client/webconsole/new-console-output/store.js
+++ b/devtools/client/webconsole/new-console-output/store.js
@@ -25,16 +25,17 @@ function configureStore() {
     prefs: new PrefState({
       logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1),
     }),
     filters: new FilterState({
       error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR),
       warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN),
       info: Services.prefs.getBoolPref(PREFS.FILTER.INFO),
       log: Services.prefs.getBoolPref(PREFS.FILTER.LOG),
+      css: Services.prefs.getBoolPref(PREFS.FILTER.CSS),
       net: Services.prefs.getBoolPref(PREFS.FILTER.NET),
       netxhr: Services.prefs.getBoolPref(PREFS.FILTER.NETXHR),
     }),
     ui: new UiState({
       filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR),
     })
   };
 
--- a/devtools/client/webconsole/new-console-output/test/components/message-container.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/message-container.test.js
@@ -34,16 +34,22 @@ describe("MessageContainer component:", 
       },
       {
         component: EvaluationResult,
         message: stubPreparedMessages.get("new Date(0)")
       },
       {
         component: PageError,
         message: stubPreparedMessages.get("ReferenceError: asdf is not defined")
+      },
+      {
+        component: PageError,
+        message: stubPreparedMessages.get(
+          "Unknown property ‘such-unknown-property’.  Declaration dropped."
+        )
       }
     ];
 
     messageTypes.forEach(info => {
       const { component, message } = info;
       const rendered = shallowRenderComponent(MessageContainer, {
         message,
         serviceContainer,
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser.ini
@@ -1,18 +1,22 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   !/devtools/client/framework/test/shared-head.js
   test-console-api.html
+  test-css-message.html
   test-network-event.html
+  test-tempfile.css
   test-tempfile.js
 
 [browser_webconsole_update_stubs_console_api.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
+[browser_webconsole_update_stubs_css_message.js]
+skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_evaluation_result.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_network_event.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_page_error.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_css_message.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/osfile.jsm");
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html";
+
+const { cssMessage: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
+
+let stubs = {
+  preparedMessages: [],
+  packets: [],
+};
+
+add_task(function* () {
+  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
+  ok(true, "make the test not fail");
+
+  for (let [key, code] of snippets) {
+    OS.File.writeAtomic(TEMP_CSS_FILE_PATH, code);
+    let received = new Promise(resolve => {
+      /* CSS errors are considered as pageError on the server */
+      toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
+        toolbox.target.client.removeListener("pageError", onPacket);
+        info("Received css message:" + e + " " + JSON.stringify(packet, null, "\t"));
+
+        let message = prepareMessage(packet, {getNextId: () => 1});
+        stubs.packets.push(formatPacket(message.messageText, packet));
+        stubs.preparedMessages.push(formatStub(message.messageText, packet));
+        resolve();
+      });
+    });
+
+    yield ContentTask.spawn(gBrowser.selectedBrowser, key, function (snippetKey) {
+      let stylesheet = content.document.createElement("link");
+      stylesheet.rel = "stylesheet";
+      stylesheet.href = "test-tempfile.css?key=" + encodeURIComponent(snippetKey);
+      content.document.body.appendChild(stylesheet);
+    });
+
+    yield received;
+  }
+
+  let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "cssMessage.js");
+  OS.File.writeAtomic(filePath, formatFile(stubs));
+  OS.File.writeAtomic(TEMP_CSS_FILE_PATH, "");
+});
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
@@ -17,16 +17,18 @@ registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
 });
 
 const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js");
 
 const BASE_PATH = "../../../../devtools/client/webconsole/new-console-output/test/fixtures";
 const TEMP_FILE_PATH = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
+const TEMP_CSS_FILE_PATH = OS.Path.join(`${BASE_PATH}/stub-generators`,
+                                        "test-tempfile.css");
 
 let cachedPackets = {};
 
 function getCleanedPacket(key, packet) {
   if(Object.keys(cachedPackets).includes(key)) {
     return cachedPackets[key];
   }
 
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
@@ -84,16 +84,31 @@ consoleApi.set("console.log(%cfoobar)", 
   keys: ["console.log(%cfoobar)"],
   code: `
 console.log(
   "%cfoo%cbar",
   "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
   "color:red;background:\\165rl('http://example.com/test')");
 `});
 
+// CSS messages
+const cssMessage = new Map();
+
+cssMessage.set("Unknown property", `
+p {
+  such-unknown-property: wow;
+}
+`);
+
+cssMessage.set("Invalid property value", `
+p {
+  padding-top: invalid value;
+}
+`);
+
 // Evaluation Result
 const evaluationResultCommands = [
   "new Date(0)",
   "asdf()",
   "1 + @"
 ];
 
 let evaluationResult = new Map(evaluationResultCommands.map(cmd => [cmd, cmd]));
@@ -137,12 +152,13 @@ pageError.set("Reference Error", `
     bar()
   }
 
   foo()
 `);
 
 module.exports = {
   consoleApi,
+  cssMessage,
   evaluationResult,
   networkEvent,
   pageError,
 };
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Stub generator</title>
+  </head>
+  <body>
+    <p>Stub generator</p>
+    <link rel="stylesheet" href="test-tempfile.css"/>
+  </body>
+</html>
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/cssMessage.js
@@ -0,0 +1,105 @@
+/* Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
+ */
+
+const { ConsoleMessage, NetworkEventMessage } = require("devtools/client/webconsole/new-console-output/types");
+
+let stubPreparedMessages = new Map();
+let stubPackets = new Map();
+
+
+stubPreparedMessages.set("Unknown property ‘such-unknown-property’.  Declaration dropped.", new ConsoleMessage({
+	"id": "1",
+	"allowRepeating": true,
+	"source": "css",
+	"type": "log",
+	"level": "warn",
+	"messageText": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
+	"parameters": null,
+	"repeat": 1,
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Unknown property ‘such-unknown-property’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property\",\"line\":3,\"column\":23},\"groupId\":null,\"userProvidedStyles\":null}",
+	"stacktrace": null,
+	"frame": {
+		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property",
+		"line": 3,
+		"column": 23
+	},
+	"groupId": null,
+	"userProvidedStyles": null
+}));
+
+stubPreparedMessages.set("Error in parsing value for ‘padding-top’.  Declaration dropped.", new ConsoleMessage({
+	"id": "1",
+	"allowRepeating": true,
+	"source": "css",
+	"type": "log",
+	"level": "warn",
+	"messageText": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
+	"parameters": null,
+	"repeat": 1,
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Error in parsing value for ‘padding-top’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value\",\"line\":3,\"column\":15},\"groupId\":null,\"userProvidedStyles\":null}",
+	"stacktrace": null,
+	"frame": {
+		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value",
+		"line": 3,
+		"column": 15
+	},
+	"groupId": null,
+	"userProvidedStyles": null
+}));
+
+
+stubPackets.set("Unknown property ‘such-unknown-property’.  Declaration dropped.", {
+	"from": "server1.conn0.child1/consoleActor2",
+	"type": "pageError",
+	"pageError": {
+		"errorMessage": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
+		"errorMessageName": "",
+		"sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property",
+		"lineText": "  such-unknown-property: wow;",
+		"lineNumber": 3,
+		"columnNumber": 23,
+		"category": "CSS Parser",
+		"timeStamp": 1478964550247,
+		"warning": true,
+		"error": false,
+		"exception": false,
+		"strict": false,
+		"info": false,
+		"private": false,
+		"stacktrace": null
+	}
+});
+
+stubPackets.set("Error in parsing value for ‘padding-top’.  Declaration dropped.", {
+	"from": "server1.conn0.child1/consoleActor2",
+	"type": "pageError",
+	"pageError": {
+		"errorMessage": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
+		"errorMessageName": "",
+		"sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value",
+		"lineText": "  padding-top: invalid value;",
+		"lineNumber": 3,
+		"columnNumber": 15,
+		"category": "CSS Parser",
+		"timeStamp": 1478964550282,
+		"warning": true,
+		"error": false,
+		"exception": false,
+		"strict": false,
+		"info": false,
+		"private": false,
+		"stacktrace": null
+	}
+});
+
+
+module.exports = {
+  stubPreparedMessages,
+  stubPackets,
+}
\ No newline at end of file
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js
@@ -2,28 +2,31 @@
   http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 let maps = [];
 
 [
   "consoleApi",
+  "cssMessage",
   "evaluationResult",
   "networkEvent",
   "pageError",
 ].forEach((filename) => {
   maps[filename] = require(`./${filename}`);
 });
 
 // Combine all the maps into a single map.
 module.exports = {
-    stubPreparedMessages: new Map([
-      ...maps.consoleApi.stubPreparedMessages,
-      ...maps.evaluationResult.stubPreparedMessages,
-      ...maps.networkEvent.stubPreparedMessages,
-      ...maps.pageError.stubPreparedMessages, ]),
-    stubPackets: new Map([
-      ...maps.consoleApi.stubPackets,
-      ...maps.evaluationResult.stubPackets,
-      ...maps.networkEvent.stubPackets,
-      ...maps.pageError.stubPackets, ]),
+  stubPreparedMessages: new Map([
+    ...maps.consoleApi.stubPreparedMessages,
+    ...maps.cssMessage.stubPreparedMessages,
+    ...maps.evaluationResult.stubPreparedMessages,
+    ...maps.networkEvent.stubPreparedMessages,
+    ...maps.pageError.stubPreparedMessages, ]),
+  stubPackets: new Map([
+    ...maps.consoleApi.stubPackets,
+    ...maps.cssMessage.stubPackets,
+    ...maps.evaluationResult.stubPackets,
+    ...maps.networkEvent.stubPackets,
+    ...maps.pageError.stubPackets, ]),
 };
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/moz.build
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/moz.build
@@ -1,11 +1,12 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
   'consoleApi.js',
+  'cssMessage.js',
   'evaluationResult.js',
   'index.js',
   'networkEvent.js',
   'pageError.js',
 )
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -12,10 +12,11 @@ support-files =
 
 [browser_webconsole_batching.js]
 [browser_webconsole_console_group.js]
 [browser_webconsole_console_table.js]
 [browser_webconsole_filters.js]
 [browser_webconsole_init.js]
 [browser_webconsole_input_focus.js]
 [browser_webconsole_keyboard_accessibility.js]
+[browser_webconsole_nodes_highlight.js]
 [browser_webconsole_observer_notifications.js]
 [browser_webconsole_vview_close_on_esc_key.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_nodes_highlight.js
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Check hovering logged nodes highlight them in the content page.
+
+const HTML = `
+  <!DOCTYPE html>
+  <html>
+    <body>
+      <h1>Node Highlight  Test</h1>
+    </body>
+    <script>
+      function logNode(selector) {
+        console.log(document.querySelector(selector));
+      }
+    </script>
+  </html>
+`;
+const TEST_URI = "data:text/html;charset=utf-8," + encodeURI(HTML);
+
+add_task(function* () {
+  const hud = yield openNewTabAndConsole(TEST_URI);
+  const toolbox = gDevTools.getToolbox(hud.target);
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
+    content.wrappedJSObject.logNode("h1");
+  });
+
+  let msg = yield waitFor(() => findMessage(hud, "<h1>"));
+  let node = msg.querySelector(".objectBox-node");
+  ok(node !== null, "Node was logged as expected");
+  const view = node.ownerDocument.defaultView;
+
+  info("Highlight the node by moving the cursor on it");
+  let onNodeHighlight = toolbox.once("node-highlight");
+  EventUtils.synthesizeMouseAtCenter(node, {type: "mousemove"}, view);
+
+  let nodeFront = yield onNodeHighlight;
+  is(nodeFront.displayName, "h1", "The correct node was highlighted");
+
+  info("Unhighlight the node by moving away from the node");
+  let onNodeUnhighlight = toolbox.once("node-unhighlight");
+  let btn = toolbox.doc.querySelector(".toolbox-dock-button");
+  EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"}, view);
+
+  yield onNodeUnhighlight;
+  ok(true, "node-unhighlight event was fired when moving away from the node");
+});
--- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js
@@ -55,16 +55,30 @@ describe("Filtering", () => {
 
     it("filters error messages", () => {
       store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
 
       let messages = getAllMessages(store.getState());
       expect(messages.size).toEqual(numMessages - 1);
     });
 
+    it("filters css messages", () => {
+      let message = stubPreparedMessages.get(
+        "Unknown property ‘such-unknown-property’.  Declaration dropped."
+      );
+      store.dispatch(messageAdd(message));
+
+      let messages = getAllMessages(store.getState());
+      expect(messages.size).toEqual(numMessages);
+
+      store.dispatch(actions.filterToggle("css"));
+      messages = getAllMessages(store.getState());
+      expect(messages.size).toEqual(numMessages + 1);
+    });
+
     it("filters xhr messages", () => {
       let message = stubPreparedMessages.get("XHR GET request");
       store.dispatch(messageAdd(message));
 
       let messages = getAllMessages(store.getState());
       expect(messages.size).toEqual(numMessages);
 
       store.dispatch(actions.filterToggle("netxhr"));
@@ -162,30 +176,32 @@ describe("Clear filters", () => {
 
     // Setup test case
     store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
     store.dispatch(actions.filterToggle("netxhr"));
     store.dispatch(actions.filterTextSet("foobar"));
 
     let filters = getAllFilters(store.getState());
     expect(filters.toJS()).toEqual({
+      "css": true,
       "debug": true,
       "error": false,
       "info": true,
       "log": true,
       "net": false,
       "netxhr": true,
       "warn": true,
       "text": "foobar"
     });
 
     store.dispatch(actions.filtersClear());
 
     filters = getAllFilters(store.getState());
     expect(filters.toJS()).toEqual({
+      "css": false,
       "debug": true,
       "error": true,
       "info": true,
       "log": true,
       "net": false,
       "netxhr": false,
       "warn": true,
       "text": ""
--- a/devtools/client/webconsole/new-console-output/utils/messages.js
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -152,18 +152,21 @@ function transformPacket(packet) {
       }
 
       const frame = pageError.sourceName ? {
         source: pageError.sourceName,
         line: pageError.lineNumber,
         column: pageError.columnNumber
       } : null;
 
+      let matchesCSS = /^(?:CSS|Layout)\b/.test(pageError.category);
+      let messageSource = matchesCSS ? MESSAGE_SOURCE.CSS
+                                     : MESSAGE_SOURCE.JAVASCRIPT;
       return new ConsoleMessage({
-        source: MESSAGE_SOURCE.JAVASCRIPT,
+        source: messageSource,
         type: MESSAGE_TYPE.LOG,
         level,
         messageText: pageError.errorMessage,
         stacktrace: pageError.stacktrace ? pageError.stacktrace : null,
         frame,
         exceptionDocURL: pageError.exceptionDocURL,
       });
     }
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -3294,19 +3294,16 @@ WebConsoleConnectionProxy.prototype = {
       console.error("Web Console getCachedMessages error: invalid state.");
     }
 
     let messages =
       response.messages.concat(...this.webConsoleClient.getNetworkEvents());
     messages.sort((a, b) => a.timeStamp - b.timeStamp);
 
     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
-      // Filter out CSS page errors.
-      messages = messages.filter(message => !(message._type == "PageError"
-          && Utils.categoryForScriptError(message) === CATEGORY_CSS));
       this.dispatchMessagesAdd(messages);
     } else {
       this.webConsoleFrame.displayCachedMessages(messages);
       if (!this._hasNativeConsoleAPI) {
         this.webConsoleFrame.logWarningAboutReplacedAPI();
       }
     }
 
@@ -3322,20 +3319,17 @@ WebConsoleConnectionProxy.prototype = {
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    */
   _onPageError: function (type, packet) {
     if (this.webConsoleFrame && packet.from == this._consoleActor) {
       if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
-        let category = Utils.categoryForScriptError(packet.pageError);
-        if (category !== CATEGORY_CSS) {
-          this.dispatchMessageAdd(packet);
-        }
+        this.dispatchMessageAdd(packet);
         return;
       }
       this.webConsoleFrame.handlePageError(packet.pageError);
     }
   },
 
   /**
    * The "logMessage" message type handler. We redirect any message to the UI
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8475,20 +8475,18 @@ nsContentUtils::SetFetchReferrerURIWithP
       referrerURI = docCurURI;
     }
   }
 
   if (!referrerURI) {
     referrerURI = principalURI;
   }
 
-  net::ReferrerPolicy referrerPolicy = aReferrerPolicy;
-  if (referrerPolicy == net::RP_Default) {
-    referrerPolicy = aDoc->GetReferrerPolicy();
-  }
+  net::ReferrerPolicy referrerPolicy = (aReferrerPolicy != net::RP_Unset) ?
+                                       aReferrerPolicy : net::RP_Default;
   return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
 }
 
 // static
 net::ReferrerPolicy
 nsContentUtils::GetReferrerPolicyFromHeader(const nsAString& aHeader)
 {
   // Multiple headers could be concatenated into one comma-separated
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -192,17 +192,20 @@ static_assert(int(HeadersGuardEnum::None
               int(HeadersGuardEnum::EndGuard_) == 5,
               "HeadersGuardEnum values are as expected");
 static_assert(int(ReferrerPolicy::_empty) == 0 &&
               int(ReferrerPolicy::No_referrer) == 1 &&
               int(ReferrerPolicy::No_referrer_when_downgrade) == 2 &&
               int(ReferrerPolicy::Origin) == 3 &&
               int(ReferrerPolicy::Origin_when_cross_origin) == 4 &&
               int(ReferrerPolicy::Unsafe_url) == 5 &&
-              int(ReferrerPolicy::EndGuard_) == 6,
+              int(ReferrerPolicy::Same_origin) == 6 &&
+              int(ReferrerPolicy::Strict_origin) == 7 &&
+              int(ReferrerPolicy::Strict_origin_when_cross_origin) == 8 &&
+              int(ReferrerPolicy::EndGuard_) == 9,
               "ReferrerPolicy values are as expected");
 static_assert(int(RequestMode::Same_origin) == 0 &&
               int(RequestMode::No_cors) == 1 &&
               int(RequestMode::Cors) == 2 &&
               int(RequestMode::Navigate) == 3 &&
               int(RequestMode::EndGuard_) == 4,
               "RequestMode values are as expected");
 static_assert(int(RequestCredentials::Omit) == 0 &&
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -30,16 +30,17 @@
 #include "nsHttpChannel.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/workers/Workers.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/Unused.h"
 
 #include "Fetch.h"
+#include "FetchUtil.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(FetchDriver,
                   nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor,
@@ -255,72 +256,40 @@ FetchDriver::HttpFetch()
     nsAutoCString method;
     mRequest->GetMethod(method);
     rv = httpChan->SetRequestMethod(method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set the same headers.
     SetRequestHeaders(httpChan);
 
-    // Step 2. Set the referrer.
-    nsAutoString referrer;
-    mRequest->GetReferrer(referrer);
+    net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
+    // Step 6 of
+    // https://fetch.spec.whatwg.org/#main-fetch
+    // If request's referrer policy is the empty string and request's client is
+    // non-null, then set request's referrer policy to request's client's
+    // associated referrer policy.
+    // Basically, "client" is not in our implementation, we use
+    // EnvironmentReferrerPolicy of the worker or document context
+    if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
+      mRequest->SetReferrerPolicy(net_referrerPolicy);
+    }
+    // Step 7 of
+    // https://fetch.spec.whatwg.org/#main-fetch
+    // If request’s referrer policy is the empty string,
+    // then set request’s referrer policy to "no-referrer-when-downgrade".
+    if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
+      mRequest->SetReferrerPolicy(net::RP_No_Referrer_When_Downgrade);
+    }
 
-    // The Referrer Policy in Request can be used to override a referrer policy
-    // associated with an environment settings object.
-    // If there's no Referrer Policy in the request, it should be inherited
-    // from environment.
-    ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
-    net::ReferrerPolicy net_referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
-    switch (referrerPolicy) {
-    case ReferrerPolicy::_empty:
-      break;
-    case ReferrerPolicy::No_referrer:
-      net_referrerPolicy = net::RP_No_Referrer;
-      break;
-    case ReferrerPolicy::No_referrer_when_downgrade:
-      net_referrerPolicy = net::RP_No_Referrer_When_Downgrade;
-      break;
-    case ReferrerPolicy::Origin:
-      net_referrerPolicy = net::RP_Origin;
-      break;
-    case ReferrerPolicy::Origin_when_cross_origin:
-      net_referrerPolicy = net::RP_Origin_When_Crossorigin;
-      break;
-    case ReferrerPolicy::Unsafe_url:
-      net_referrerPolicy = net::RP_Unsafe_URL;
-      break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
-      break;
-    }
-    if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
-      rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
-                                                         mDocument,
-                                                         httpChan,
-                                                         net_referrerPolicy);
-      NS_ENSURE_SUCCESS(rv, rv);
-    } else if (referrer.IsEmpty()) {
-      rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
-      NS_ENSURE_SUCCESS(rv, rv);
-    } else {
-      // From "Determine request's Referrer" step 3
-      // "If request's referrer is a URL, let referrerSource be request's
-      // referrer."
-      nsCOMPtr<nsIURI> referrerURI;
-      rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv =
-        httpChan->SetReferrerWithPolicy(referrerURI,
-                                        referrerPolicy == ReferrerPolicy::_empty ?
-                                          mRequest->GetEnvironmentReferrerPolicy() :
-                                          net_referrerPolicy);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = FetchUtil::SetRequestReferrer(mPrincipal,
+                                       mDocument,
+                                       httpChan,
+                                       mRequest);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     // Bug 1120722 - Authorization will be handled later.
     // Auth may require prompting, we don't support it yet.
     // The next patch in this same bug prevents this from aborting the request.
     // Credentials checks for CORS are handled by nsCORSListenerProxy,
 
     nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
 
@@ -818,39 +787,25 @@ FetchDriver::AsyncOnChannelRedirect(nsIC
   mRequest->AddURL(spec, fragment);
   NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
   // updates request’s associated referrer policy according to the
   // Referrer-Policy header (if any).
   if (!tRPHeaderValue.IsEmpty()) {
     net::ReferrerPolicy net_referrerPolicy =
       nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
     if (net_referrerPolicy != net::RP_Unset) {
-      ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
-      switch (net_referrerPolicy) {
-        case net::RP_No_Referrer:
-          referrerPolicy = ReferrerPolicy::No_referrer;
-          break;
-        case net::RP_No_Referrer_When_Downgrade:
-          referrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
-          break;
-        case net::RP_Origin:
-          referrerPolicy = ReferrerPolicy::Origin;
-          break;
-        case net::RP_Origin_When_Crossorigin:
-          referrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
-          break;
-        case net::RP_Unsafe_URL:
-          referrerPolicy = ReferrerPolicy::Unsafe_url;
-          break;
-        default:
-          MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
-          break;
+      mRequest->SetReferrerPolicy(net_referrerPolicy);
+      // Should update channel's referrer policy
+      if (httpChannel) {
+        rv = FetchUtil::SetRequestReferrer(mPrincipal,
+                                           mDocument,
+                                           httpChannel,
+                                           mRequest);
+        NS_ENSURE_SUCCESS(rv, rv);
       }
-
-      mRequest->SetReferrerPolicy(referrerPolicy);
     }
   }
 
   aCallback->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/fetch/FetchUtil.cpp
+++ b/dom/fetch/FetchUtil.cpp
@@ -1,15 +1,17 @@
 #include "FetchUtil.h"
 
 #include "nsError.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsString.h"
+#include "nsIDocument.h"
 
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/InternalRequest.h"
 
 namespace mozilla {
 namespace dom {
 
 // static
 nsresult
 FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
 {
@@ -105,10 +107,64 @@ FetchUtil::ExtractHeader(nsACString::con
   if (!NS_IsReasonableHTTPHeaderValue(aHeaderValue)) {
     return false;
   }
   aHeaderValue.CompressWhitespace();
 
   return PushOverLine(aStart, aEnd);
 }
 
+// static
+nsresult
+FetchUtil::SetRequestReferrer(nsIPrincipal* aPrincipal,
+                              nsIDocument* aDoc,
+                              nsIHttpChannel* aChannel,
+                              InternalRequest* aRequest) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoString referrer;
+  aRequest->GetReferrer(referrer);
+  net::ReferrerPolicy policy = aRequest->GetReferrerPolicy();
+
+  nsresult rv = NS_OK;
+  if (referrer.IsEmpty()) {
+    // This is the case request’s referrer is "no-referrer"
+    rv = aChannel->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
+    rv = nsContentUtils::SetFetchReferrerURIWithPolicy(aPrincipal,
+                                                       aDoc,
+                                                       aChannel,
+                                                       policy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    // From "Determine request's Referrer" step 3
+    // "If request's referrer is a URL, let referrerSource be request's
+    // referrer."
+    nsCOMPtr<nsIURI> referrerURI;
+    rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = aChannel->SetReferrerWithPolicy(referrerURI, policy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIURI> referrerURI;
+  aChannel->GetReferrer(getter_AddRefs(referrerURI));
+
+  // Step 8 https://fetch.spec.whatwg.org/#main-fetch
+  // If request’s referrer is not "no-referrer", set request’s referrer to
+  // the result of invoking determine request’s referrer.
+  if (referrerURI) {
+    nsAutoCString spec;
+    rv = referrerURI->GetSpec(spec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aRequest->SetReferrer(NS_ConvertUTF8toUTF16(spec));
+  } else {
+    aRequest->SetReferrer(EmptyString());
+  }
+
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/FetchUtil.h
+++ b/dom/fetch/FetchUtil.h
@@ -3,19 +3,25 @@
 
 #include "nsString.h"
 #include "nsError.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
 
+class nsIPrincipal;
+class nsIDocument;
+class nsIHttpChannel;
+
 namespace mozilla {
 namespace dom {
 
+class InternalRequest;
+
 class FetchUtil final
 {
 private:
   FetchUtil() = delete;
 
 public:
   /**
   * Sets outMethod to a valid HTTP request method string based on an input method.
@@ -30,13 +36,20 @@ public:
    * Extracts an HTTP header from a substring range.
    */
   static bool
   ExtractHeader(nsACString::const_iterator& aStart,
                 nsACString::const_iterator& aEnd,
                 nsCString& aHeaderName,
                 nsCString& aHeaderValue,
                 bool* aWasEmptyHeader);
+
+  static nsresult
+  SetRequestReferrer(nsIPrincipal* aPrincipal,
+                     nsIDocument* aDoc,
+                     nsIHttpChannel* aChannel,
+                     InternalRequest* aRequest);
+
 };
 
 } // namespace dom
 } // namespace mozilla
 #endif
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -228,16 +228,82 @@ public:
   }
 
   void
   SetReferrerPolicy(ReferrerPolicy aReferrerPolicy)
   {
     mReferrerPolicy = aReferrerPolicy;
   }
 
+  void
+  SetReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
+  {
+    switch (aReferrerPolicy) {
+      case net::RP_Unset:
+        mReferrerPolicy = ReferrerPolicy::_empty;
+        break;
+      case net::RP_No_Referrer:
+        mReferrerPolicy = ReferrerPolicy::No_referrer;
+        break;
+      case net::RP_No_Referrer_When_Downgrade:
+        mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
+        break;
+      case net::RP_Origin:
+        mReferrerPolicy = ReferrerPolicy::Origin;
+        break;
+      case net::RP_Origin_When_Crossorigin:
+        mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
+        break;
+      case net::RP_Unsafe_URL:
+        mReferrerPolicy = ReferrerPolicy::Unsafe_url;
+        break;
+      case net::RP_Same_Origin:
+        mReferrerPolicy = ReferrerPolicy::Same_origin;
+        break;
+      case net::RP_Strict_Origin:
+        mReferrerPolicy = ReferrerPolicy::Strict_origin;
+        break;
+      case net::RP_Strict_Origin_When_Cross_Origin:
+        mReferrerPolicy = ReferrerPolicy::Strict_origin_when_cross_origin;
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
+        break;
+    }
+  }
+
+  net::ReferrerPolicy
+  GetReferrerPolicy()
+  {
+    switch (mReferrerPolicy) {
+      case ReferrerPolicy::_empty:
+        return net::RP_Unset;
+      case ReferrerPolicy::No_referrer:
+        return net::RP_No_Referrer;
+      case ReferrerPolicy::No_referrer_when_downgrade:
+        return net::RP_No_Referrer_When_Downgrade;
+      case ReferrerPolicy::Origin:
+        return net::RP_Origin;
+      case ReferrerPolicy::Origin_when_cross_origin:
+        return net::RP_Origin_When_Crossorigin;
+      case ReferrerPolicy::Unsafe_url:
+        return net::RP_Unsafe_URL;
+      case ReferrerPolicy::Strict_origin:
+        return net::RP_Strict_Origin;
+      case ReferrerPolicy::Same_origin:
+        return net::RP_Same_Origin;
+      case ReferrerPolicy::Strict_origin_when_cross_origin:
+        return net::RP_Strict_Origin_When_Cross_Origin;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy enum value?");
+        break;
+    }
+    return net::RP_Unset;
+  }
+
   net::ReferrerPolicy
   GetEnvironmentReferrerPolicy() const
   {
     return mEnvironmentReferrerPolicy;
   }
 
   void
   SetEnvironmentReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 #include "mozilla/dom/HTMLSourceElement.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/MediaEncryptedEvent.h"
+#include "mozilla/EMEUtils.h"
 
 #include "base/basictypes.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIDOMHTMLSourceElement.h"
 #include "TimeRanges.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsPresContext.h"
@@ -5287,35 +5288,41 @@ void HTMLMediaElement::SuspendOrResumeEl
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     UpdateSrcMediaStreamPlaying();
     UpdateAudioChannelPlayingState();
     if (aPauseElement) {
       ReportTelemetry();
       ReportEMETelemetry();
 
-      // For EME content, force destruction of the CDM client (and CDM
+      // For EME content, we may force destruction of the CDM client (and CDM
       // instance if this is the last client for that CDM instance) and
       // the CDM's decoder. This ensures the CDM gets reliable and prompt
       // shutdown notifications, as it may have book-keeping it needs
       // to do on shutdown.
       if (mMediaKeys) {
-        mMediaKeys->Shutdown();
-        mMediaKeys = nullptr;
-        if (mDecoder) {
-          ShutdownDecoder();
+        nsAutoString keySystem;
+        mMediaKeys->GetKeySystem(keySystem);
+        // If we're using Primetime we need to shutdown the key system and
+        // decoder to preserve secure stop like behavior, other CDMs don't
+        // implement this so we don't need to worry with them on a suspend.
+        if (IsPrimetimeKeySystem(keySystem)) {
+          mMediaKeys->Shutdown();
+          mMediaKeys = nullptr;
+          if (mDecoder) {
+            ShutdownDecoder();
+          }
         }
       }
       if (mDecoder) {
         mDecoder->Pause();
         mDecoder->Suspend();
       }
       mEventDeliveryPaused = aSuspendEvents;
     } else {
-      MOZ_ASSERT(!mMediaKeys);
       if (mDecoder) {
         mDecoder->Resume();
         if (!mPaused && !mDecoder->IsEnded()) {
           mDecoder->Play();
         }
       }
       if (mEventDeliveryPaused) {
         mEventDeliveryPaused = false;
@@ -5349,16 +5356,25 @@ void HTMLMediaElement::NotifyOwnerDocume
 
   if (mDecoder && !IsBeingDestroyed()) {
     mDecoder->NotifyOwnerActivityChanged(visible);
   }
 
   bool pauseElement = ShouldElementBePaused();
   SuspendOrResumeElement(pauseElement, !IsActive());
 
+  // If the owning document has become inactive we should shutdown the CDM.
+  if (!OwnerDoc()->IsCurrentActiveDocument() && mMediaKeys) {
+      mMediaKeys->Shutdown();
+      mMediaKeys = nullptr;
+      if (mDecoder) {
+        ShutdownDecoder();
+      }
+    }
+
   AddRemoveSelfReference();
 }
 
 void HTMLMediaElement::AddRemoveSelfReference()
 {
   // XXX we could release earlier here in many situations if we examined
   // which event listeners are attached. Right now we assume there is a
   // potential listener for every event. We would also have to keep the
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -93,16 +93,18 @@ public:
   // Loads the CDM corresponding to mKeySystem.
   // Calls MediaKeys::OnCDMCreated() when the CDM is created.
   virtual void Init(PromiseId aPromiseId,
                     const nsAString& aOrigin,
                     const nsAString& aTopLevelOrigin,
                     const nsAString& aName,
                     bool aInPrivateBrowsing) = 0;
 
+  virtual void OnSetDecryptorId(uint32_t aId) {}
+
   // Main thread only.
   // Uses the CDM to create a key session.
   // Calls MediaKeys::OnSessionActivated() when session is created.
   // Assumes ownership of (Move()s) aInitData's contents.
   virtual void CreateSession(uint32_t aCreateSessionToken,
                              MediaKeySessionType aSessionType,
                              PromiseId aPromiseId,
                              const nsAString& aInitDataType,
@@ -214,16 +216,18 @@ public:
 
   virtual void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                                      nsTArray<nsCString>& aSessionIds) = 0;
 
 #ifdef DEBUG
   virtual bool IsOnOwnerThread() = 0;
 #endif
 
+  virtual uint32_t GetDecryptorId() { return 0; }
+
 protected:
   virtual ~CDMProxy() {}
 
   // Helper to enforce that a raw pointer is only accessed on the main thread.
   template<class Type>
   class MainThreadOnlyRawPtr {
   public:
     explicit MainThreadOnlyRawPtr(Type* aPtr)
--- a/dom/media/eme/DecryptorProxyCallback.h
+++ b/dom/media/eme/DecryptorProxyCallback.h
@@ -10,16 +10,18 @@
 #include "mozilla/dom/MediaKeyMessageEventBinding.h" // For MediaKeyMessageType
 #include "mozilla/CDMProxy.h"
 
 class DecryptorProxyCallback {
 public:
 
   virtual ~DecryptorProxyCallback() {}
 
+  virtual void SetDecryptorId(uint32_t aId) = 0;
+
   virtual void SetSessionId(uint32_t aCreateSessionId,
                             const nsCString& aSessionId) = 0;
 
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccess) = 0;
 
   virtual void ResolvePromise(uint32_t aPromiseId) = 0;
 
--- a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
+++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
@@ -12,16 +12,19 @@
 
 namespace mozilla {
 class CDMProxy;
 // Proxies call backs from the MediaDrmProxy -> MediaDrmProxySupport back to the MediaKeys
 // object on the main thread.
 // We used annotation calledFrom = "gecko" to ensure running on main thread.
 class MediaDrmCDMCallbackProxy : public DecryptorProxyCallback {
 public:
+
+  void SetDecryptorId(uint32_t aId) override {}
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override;
 
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override;
 
   void ResolvePromise(uint32_t aPromiseId) override;
 
--- a/dom/media/gmp/GMPCDMCallbackProxy.cpp
+++ b/dom/media/gmp/GMPCDMCallbackProxy.cpp
@@ -17,16 +17,29 @@
 
 namespace mozilla {
 
 GMPCDMCallbackProxy::GMPCDMCallbackProxy(CDMProxy* aProxy)
   : mProxy(aProxy)
 {}
 
 void
+GMPCDMCallbackProxy::SetDecryptorId(uint32_t aId)
+{
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
+
+  RefPtr<CDMProxy> proxy = mProxy;
+  NS_DispatchToMainThread(
+    NS_NewRunnableFunction([proxy, aId] ()
+    {
+      proxy->OnSetDecryptorId(aId);
+    })
+  );}
+
+void
 GMPCDMCallbackProxy::SetSessionId(uint32_t aToken,
                                   const nsCString& aSessionId)
 {
   MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   RefPtr<CDMProxy> proxy = mProxy;
   auto sid = NS_ConvertUTF8toUTF16(aSessionId);
   NS_DispatchToMainThread(
--- a/dom/media/gmp/GMPCDMCallbackProxy.h
+++ b/dom/media/gmp/GMPCDMCallbackProxy.h
@@ -12,16 +12,19 @@
 #include "GMPDecryptorProxy.h"
 
 namespace mozilla {
 
 // Proxies call backs from the CDM on the GMP thread back to the MediaKeys
 // object on the main thread.
 class GMPCDMCallbackProxy : public GMPDecryptorProxyCallback {
 public:
+
+  void SetDecryptorId(uint32_t aId) override;
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override;
 
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override;
 
   void ResolvePromise(uint32_t aPromiseId) override;
 
--- a/dom/media/gmp/GMPCDMProxy.cpp
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -33,16 +33,18 @@ GMPCDMProxy::GMPCDMProxy(dom::MediaKeys*
   : CDMProxy(aKeys,
              aKeySystem,
              aDistinctiveIdentifierRequired,
              aPersistentStateRequired)
   , mCrashHelper(aCrashHelper)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
   , mShutdownCalled(false)
+  , mDecryptorId(0)
+  , mCreatePromiseId(0)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(GMPCDMProxy);
 }
 
 GMPCDMProxy::~GMPCDMProxy()
 {
   MOZ_COUNT_DTOR(GMPCDMProxy);
@@ -126,20 +128,29 @@ GMPCDMProxy::gmp_InitDone(GMPDecryptorPr
     return;
   }
 
   mCDM = aCDM;
   mCallback = new GMPCDMCallbackProxy(this);
   mCDM->Init(mCallback,
              mDistinctiveIdentifierRequired,
              mPersistentStateRequired);
+
+  // Await the OnSetDecryptorId callback.
+  mCreatePromiseId = aData->mPromiseId;
+}
+
+void GMPCDMProxy::OnSetDecryptorId(uint32_t aId)
+{
+  MOZ_ASSERT(mCreatePromiseId);
+  mDecryptorId = aId;
   nsCOMPtr<nsIRunnable> task(
     NewRunnableMethod<uint32_t>(this,
                                 &GMPCDMProxy::OnCDMCreated,
-                                aData->mPromiseId));
+                                mCreatePromiseId));
   NS_DispatchToMainThread(task);
 }
 
 class gmp_InitDoneCallback : public GetGMPDecryptorCallback
 {
 public:
   gmp_InitDoneCallback(GMPCDMProxy* aGMPCDMProxy,
                        nsAutoPtr<GMPCDMProxy::InitData>&& aData)
@@ -763,14 +774,26 @@ GMPCDMProxy::GetSessionIdsForKeyId(const
   caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
 }
 
 void
 GMPCDMProxy::Terminated()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("CDM terminated");
+  if (mCreatePromiseId) {
+    RejectPromise(mCreatePromiseId,
+                  NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                  NS_LITERAL_CSTRING("Crashed waiting for CDM to initialize"));
+    mCreatePromiseId = 0;
+  }
   if (!mKeys.IsNull()) {
     mKeys->Terminated();
   }
 }
 
+uint32_t
+GMPCDMProxy::GetDecryptorId()
+{
+  return mDecryptorId;
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/GMPCDMProxy.h
+++ b/dom/media/gmp/GMPCDMProxy.h
@@ -29,16 +29,18 @@ public:
               bool aPersistentStateRequired);
 
   void Init(PromiseId aPromiseId,
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
             const nsAString& aGMPName,
             bool aInPrivateBrowsing) override;
 
+  void OnSetDecryptorId(uint32_t aId) override;
+
   void CreateSession(uint32_t aCreateSessionToken,
                      dom::MediaKeySessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
                      nsTArray<uint8_t>& aInitData) override;
 
   void LoadSession(PromiseId aPromiseId,
                    const nsAString& aSessionId) override;
@@ -104,16 +106,18 @@ public:
 
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                              nsTArray<nsCString>& aSessionIds) override;
 
 #ifdef DEBUG
   bool IsOnOwnerThread() override;
 #endif
 
+  uint32_t GetDecryptorId() override;
+
 private:
   friend class gmp_InitDoneCallback;
   friend class gmp_InitGetGMPDecryptorCallback;
 
   struct InitData {
     uint32_t mPromiseId;
     nsString mOrigin;
     nsString mTopLevelOrigin;
@@ -245,14 +249,18 @@ private:
   // mDecryptionJobs as that shrinks as jobs are completed and removed
   // from it.
   // GMP thread only.
   uint32_t mDecryptionJobCount;
 
   // True if GMPCDMProxy::gmp_Shutdown was called.
   // GMP thread only.
   bool mShutdownCalled;
+
+  uint32_t mDecryptorId;
+
+  PromiseId mCreatePromiseId;
 };
 
 
 } // namespace mozilla
 
 #endif // GMPCDMProxy_h_
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -272,22 +272,25 @@ GMPChild::RecvSetNodeId(const nsCString&
   // Store the per origin salt for the node id. Note: we do this in a
   // separate message than RecvStartPlugin() so that the string is not
   // sitting in a string on the IPC code's call stack.
   mNodeId = aNodeId;
   return true;
 }
 
 GMPErr
-GMPChild::GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI)
+GMPChild::GetAPI(const char* aAPIName,
+                 void* aHostAPI,
+                 void** aPluginAPI,
+                 uint32_t aDecryptorId)
 {
   if (!mGMPLoader) {
     return GMPGenericErr;
   }
-  return mGMPLoader->GetAPI(aAPIName, aHostAPI, aPluginAPI);
+  return mGMPLoader->GetAPI(aAPIName, aHostAPI, aPluginAPI, aDecryptorId);
 }
 
 bool
 GMPChild::RecvPreloadLibs(const nsCString& aLibs)
 {
 #ifdef XP_WIN
   // Pre-load DLLs that need to be used by the EME plugin but that can't be
   // loaded after the sandbox has started
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -71,17 +71,17 @@ private:
 
   bool RecvCrashPluginNow() override;
   bool RecvBeginAsyncShutdown() override;
   bool RecvCloseActive() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void ProcessingError(Result aCode, const char* aReason) override;
 
-  GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI);
+  GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI, uint32_t aDecryptorId = 0);
 
   nsTArray<UniquePtr<GMPContentChild>> mGMPContentChildren;
 
   GMPAsyncShutdown* mAsyncShutdown;
   RefPtr<GMPTimerChild> mTimerChild;
   RefPtr<GMPStorageChild> mStorage;
 
   MessageLoop* mGMPMessageLoop;
--- a/dom/media/gmp/GMPContentChild.cpp
+++ b/dom/media/gmp/GMPContentChild.cpp
@@ -75,17 +75,17 @@ GMPContentChild::AllocPGMPDecryptorChild
 bool
 GMPContentChild::DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor)
 {
   static_cast<GMPDecryptorChild*>(aActor)->Release();
   return true;
 }
 
 PGMPVideoDecoderChild*
-GMPContentChild::AllocPGMPVideoDecoderChild()
+GMPContentChild::AllocPGMPVideoDecoderChild(const uint32_t& aDecryptorId)
 {
   GMPVideoDecoderChild* actor = new GMPVideoDecoderChild(this);
   actor->AddRef();
   return actor;
 }
 
 bool
 GMPContentChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor)
@@ -201,17 +201,17 @@ private:
 
 bool
 GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
 {
   GMPDecryptorChild* child = static_cast<GMPDecryptorChild*>(aActor);
   GMPDecryptorHost* host = static_cast<GMPDecryptorHost*>(child);
 
   void* ptr = nullptr;
-  GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &ptr);
+  GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, host, &ptr, aActor->Id());
   GMPDecryptor* decryptor = nullptr;
   if (GMP_SUCCEEDED(err) && ptr) {
     decryptor = static_cast<GMPDecryptor*>(ptr);
   } else if (err != GMPNoErr) {
     // We Adapt the previous GMPDecryptor version to the current, so that
     // Gecko thinks it's only talking to the current version. v7 differs
     // from v9 in its Init() function arguments, and v9 has extra enumeration
     // members at the end of the key status enumerations.
@@ -239,22 +239,23 @@ GMPContentChild::RecvPGMPAudioDecoderCon
   }
 
   vdc->Init(static_cast<GMPAudioDecoder*>(vd));
 
   return true;
 }
 
 bool
-GMPContentChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor)
+GMPContentChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor,
+                                                 const uint32_t& aDecryptorId)
 {
   auto vdc = static_cast<GMPVideoDecoderChild*>(aActor);
 
   void* vd = nullptr;
-  GMPErr err = mGMPChild->GetAPI(GMP_API_VIDEO_DECODER, &vdc->Host(), &vd);
+  GMPErr err = mGMPChild->GetAPI(GMP_API_VIDEO_DECODER, &vdc->Host(), &vd, aDecryptorId);
   if (err != GMPNoErr || !vd) {
     NS_WARNING("GMPGetAPI call failed trying to construct decoder.");
     return false;
   }
 
   vdc->Init(static_cast<GMPVideoDecoder*>(vd));
 
   return true;
--- a/dom/media/gmp/GMPContentChild.h
+++ b/dom/media/gmp/GMPContentChild.h
@@ -20,26 +20,26 @@ class GMPContentChild : public PGMPConte
 public:
   explicit GMPContentChild(GMPChild* aChild);
   virtual ~GMPContentChild();
 
   MessageLoop* GMPMessageLoop();
 
   bool RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor) override;
   bool RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) override;
-  bool RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor) override;
+  bool RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor, const uint32_t& aDecryptorId) override;
   bool RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) override;
 
   PGMPAudioDecoderChild* AllocPGMPAudioDecoderChild() override;
   bool DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor) override;
 
   PGMPDecryptorChild* AllocPGMPDecryptorChild() override;
   bool DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor) override;
 
-  PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild() override;
+  PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild(const uint32_t& aDecryptorId) override;
   bool DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) override;
 
   PGMPVideoEncoderChild* AllocPGMPVideoEncoderChild() override;
   bool DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void ProcessingError(Result aCode, const char* aReason) override;
 
--- a/dom/media/gmp/GMPContentParent.cpp
+++ b/dom/media/gmp/GMPContentParent.cpp
@@ -189,20 +189,21 @@ GMPContentParent::GetGMPAudioDecoder(GMP
   NS_ADDREF(vap);
   *aGMPAD = vap;
   mAudioDecoders.AppendElement(vap);
 
   return NS_OK;
 }
 
 nsresult
-GMPContentParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD)
+GMPContentParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD,
+                                     uint32_t aDecryptorId)
 {
   // returned with one anonymous AddRef that locks it until Destroy
-  PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor();
+  PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor(aDecryptorId);
   if (!pvdp) {
     return NS_ERROR_FAILURE;
   }
   GMPVideoDecoderParent *vdp = static_cast<GMPVideoDecoderParent*>(pvdp);
   // This addref corresponds to the Proxy pointer the consumer is returned.
   // It's dropped by calling Close() on the interface.
   NS_ADDREF(vdp);
   *aGMPVD = vdp;
@@ -225,17 +226,17 @@ GMPContentParent::GetGMPVideoEncoder(GMP
   NS_ADDREF(vep);
   *aGMPVE = vep;
   mVideoEncoders.AppendElement(vep);
 
   return NS_OK;
 }
 
 PGMPVideoDecoderParent*
-GMPContentParent::AllocPGMPVideoDecoderParent()
+GMPContentParent::AllocPGMPVideoDecoderParent(const uint32_t& aDecryptorId)
 {
   GMPVideoDecoderParent* vdp = new GMPVideoDecoderParent(this);
   NS_ADDREF(vdp);
   return vdp;
 }
 
 bool
 GMPContentParent::DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor)
--- a/dom/media/gmp/GMPContentParent.h
+++ b/dom/media/gmp/GMPContentParent.h
@@ -22,17 +22,18 @@ class GMPVideoEncoderParent;
 class GMPContentParent final : public PGMPContentParent,
                                public GMPSharedMem
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPContentParent)
 
   explicit GMPContentParent(GMPParent* aParent = nullptr);
 
-  nsresult GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD);
+  nsresult GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD,
+                              uint32_t aDecryptorId);
   void VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder);
 
   nsresult GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE);
   void VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder);
 
   nsresult GetGMPDecryptor(GMPDecryptorParent** aGMPKS);
   void DecryptorDestroyed(GMPDecryptorParent* aSession);
 
@@ -61,17 +62,17 @@ public:
     return mPluginId;
   }
 
 private:
   ~GMPContentParent();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
-  PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() override;
+  PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent(const uint32_t& aDecryptorId) override;
   bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) override;
 
   PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() override;
   bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) override;
 
   PGMPDecryptorParent* AllocPGMPDecryptorParent() override;
   bool DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor) override;
 
--- a/dom/media/gmp/GMPCrashHelperHolder.h
+++ b/dom/media/gmp/GMPCrashHelperHolder.h
@@ -48,22 +48,22 @@ namespace mozilla {
 // In the crashing case, the GMPCrashHelpers are deallocated when the crash
 // report is processed in GeckoMediaPluginService::RunPluginCrashCallbacks().
 //
 // It's a bit yuck that we have to have two paths for disconnecting the crash
 // helpers, but there aren't really any better options.
 class GMPCrashHelperHolder
 {
 public:
-  
+
   void SetCrashHelper(GMPCrashHelper* aHelper)
   {
     mCrashHelper = aHelper;
   }
-  
+
   GMPCrashHelper* GetCrashHelper()
   {
     return mCrashHelper;
   }
 
   void MaybeDisconnect(bool aAbnormalShutdown)
   {
     if (!aAbnormalShutdown) {
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -68,16 +68,21 @@ GMPDecryptorChild::CallOnGMPThread(Metho
   }
 }
 
 void
 GMPDecryptorChild::Init(GMPDecryptor* aSession)
 {
   MOZ_ASSERT(aSession);
   mSession = aSession;
+  // The ID of this decryptor is the IPDL actor ID. Note it's unique inside
+  // the child process, but not necessarily across all gecko processes. However,
+  // since GMPDecryptors are segregated by node ID/origin, we shouldn't end up
+  // with clashes in the content process.
+  SendSetDecryptorId(Id());
 }
 
 void
 GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken,
                                 const char* aSessionId,
                                 uint32_t aSessionIdLength)
 {
   CALL_ON_GMP_THREAD(SendSetSessionId,
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -35,16 +35,27 @@ GMPDecryptorParent::GMPDecryptorParent(G
 {
   MOZ_ASSERT(mPlugin && mGMPThread);
 }
 
 GMPDecryptorParent::~GMPDecryptorParent()
 {
 }
 
+bool
+GMPDecryptorParent::RecvSetDecryptorId(const uint32_t& aId)
+{
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use a dead GMP decrypter!");
+    return false;
+  }
+  mCallback->SetDecryptorId(aId);
+  return true;
+}
+
 nsresult
 GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback,
                          bool aDistinctiveIdentifierRequired,
                          bool aPersistentStateRequired)
 {
   LOGD(("GMPDecryptorParent[%p]::Init()", this));
 
   if (mIsOpen) {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -67,16 +67,18 @@ public:
 
   void Shutdown();
 
 private:
   ~GMPDecryptorParent();
 
   // PGMPDecryptorParent
 
+  bool RecvSetDecryptorId(const uint32_t& aId) override;
+
   bool RecvSetSessionId(const uint32_t& aCreateSessionToken,
                         const nsCString& aSessionId) override;
 
   bool RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
                                      const bool& aSuccess) override;
 
   bool RecvResolvePromise(const uint32_t& aPromiseId) override;
 
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -34,17 +34,18 @@ public:
             uint32_t aUTF8LibPathLen,
             char* aOriginSalt,
             uint32_t aOriginSaltLen,
             const GMPPlatformAPI* aPlatformAPI,
             GMPAdapter* aAdapter) override;
 
   GMPErr GetAPI(const char* aAPIName,
                 void* aHostAPI,
-                void** aPluginAPI) override;
+                void** aPluginAPI,
+                uint32_t aDecryptorId) override;
 
   void Shutdown() override;
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) override;
 #endif
 
 private:
@@ -75,17 +76,20 @@ public:
     }
     GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
     if (!initFunc) {
       return GMPNotImplementedErr;
     }
     return initFunc(aPlatformAPI);
   }
 
-  GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) override
+  GMPErr GMPGetAPI(const char* aAPIName,
+                   void* aHostAPI,
+                   void** aPluginAPI,
+                   uint32_t aDecryptorId) override
   {
     if (!mLib) {
       return GMPGenericErr;
     }
     GMPGetAPIFunc getapiFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
     if (!getapiFunc) {
       return GMPNotImplementedErr;
     }
@@ -185,19 +189,20 @@ GMPLoaderImpl::Load(const char* aUTF8Lib
   mAdapter->GMPSetNodeId(nodeId.c_str(), nodeId.size());
 
   return true;
 }
 
 GMPErr
 GMPLoaderImpl::GetAPI(const char* aAPIName,
                       void* aHostAPI,
-                      void** aPluginAPI)
+                      void** aPluginAPI,
+                      uint32_t aDecryptorId)
 {
-  return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI);
+  return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI, aDecryptorId);
 }
 
 void
 GMPLoaderImpl::Shutdown()
 {
   if (mAdapter) {
     mAdapter->GMPShutdown();
   }
--- a/dom/media/gmp/GMPLoader.h
+++ b/dom/media/gmp/GMPLoader.h
@@ -37,17 +37,20 @@ public:
   virtual ~GMPAdapter() {}
   // Sets the adapted to plugin library module.
   // Note: the GMPAdapter is responsible for calling PR_UnloadLibrary on aLib
   // when it's finished with it.
   virtual void SetAdaptee(PRLibrary* aLib) = 0;
 
   // These are called in place of the corresponding GMP API functions.
   virtual GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) = 0;
-  virtual GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
+  virtual GMPErr GMPGetAPI(const char* aAPIName,
+                           void* aHostAPI,
+                           void** aPluginAPI,
+                           uint32_t aDecryptorId) = 0;
   virtual void GMPShutdown() = 0;
   virtual void GMPSetNodeId(const char* aNodeId, uint32_t aLength) = 0;
 };
 
 // Encapsulates generating the device-bound node id, activating the sandbox,
 // loading the GMP, and passing the node id to the GMP (in that order).
 //
 // In Desktop Gecko, the implementation of this lives in plugin-container,
@@ -78,17 +81,20 @@ public:
   virtual bool Load(const char* aUTF8LibPath,
                     uint32_t aLibPathLen,
                     char* aOriginSalt,
                     uint32_t aOriginSaltLen,
                     const GMPPlatformAPI* aPlatformAPI,
                     GMPAdapter* aAdapter = nullptr) = 0;
 
   // Retrieves an interface pointer from the GMP.
-  virtual GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
+  virtual GMPErr GetAPI(const char* aAPIName,
+                        void* aHostAPI,
+                        void** aPluginAPI,
+                        uint32_t aDecryptorId) = 0;
 
   // Calls the GMPShutdown function exported by the GMP lib, and unloads the
   // plugin library.
   virtual void Shutdown() = 0;
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   // On OS X we need to set Mac-specific sandbox info just before we start the
   // sandbox, which we don't yet know when the GMPLoader and SandboxStarter
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -344,54 +344,58 @@ GeckoMediaPluginService::GetGMPAudioDeco
 
   return NS_OK;
 }
 
 class GetGMPContentParentForVideoDecoderDone : public GetGMPContentParentCallback
 {
 public:
   explicit GetGMPContentParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
-                                                  GMPCrashHelper* aHelper)
+                                                  GMPCrashHelper* aHelper,
+                                                  uint32_t aDecryptorId)
    : mCallback(Move(aCallback))
    , mHelper(aHelper)
+   , mDecryptorId(aDecryptorId)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPVideoDecoderParent* gmpVDP = nullptr;
     GMPVideoHostImpl* videoHost = nullptr;
-    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoDecoder(&gmpVDP))) {
+    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoDecoder(&gmpVDP, mDecryptorId))) {
       videoHost = &gmpVDP->Host();
       gmpVDP->SetCrashHelper(mHelper);
     }
     mCallback->Done(gmpVDP, videoHost);
   }
 
 private:
   UniquePtr<GetGMPVideoDecoderCallback> mCallback;
   RefPtr<GMPCrashHelper> mHelper;
+  const uint32_t mDecryptorId;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPVideoDecoder(GMPCrashHelper* aHelper,
-                                            nsTArray<nsCString>* aTags,
-                                            const nsACString& aNodeId,
-                                            UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+GeckoMediaPluginService::GetDecryptingGMPVideoDecoder(GMPCrashHelper* aHelper,
+                                                      nsTArray<nsCString>* aTags,
+                                                      const nsACString& aNodeId,
+                                                      UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
+                                                      uint32_t aDecryptorId)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForVideoDecoderDone(Move(aCallback), aHelper));
+    new GetGMPContentParentForVideoDecoderDone(Move(aCallback), aHelper, aDecryptorId));
   if (!GetContentParentFrom(aHelper,
                             aNodeId,
                             NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
                             *aTags,
                             Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -64,20 +64,21 @@ public:
   static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
 
   virtual nsresult Init();
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // mozIGeckoMediaPluginService
   NS_IMETHOD GetThread(nsIThread** aThread) override;
-  NS_IMETHOD GetGMPVideoDecoder(GMPCrashHelper* aHelper,
-                                nsTArray<nsCString>* aTags,
-                                const nsACString& aNodeId,
-                                UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+  NS_IMETHOD GetDecryptingGMPVideoDecoder(GMPCrashHelper* aHelper,
+                                          nsTArray<nsCString>* aTags,
+                                          const nsACString& aNodeId,
+                                          UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
+                                          uint32_t aDecryptorId)
     override;
   NS_IMETHOD GetGMPVideoEncoder(GMPCrashHelper* aHelper,
                                 nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
                                 UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
     override;
   NS_IMETHOD GetGMPAudioDecoder(GMPCrashHelper* aHelper,
                                 nsTArray<nsCString>* aTags,
@@ -85,16 +86,26 @@ public:
                                 UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
     override;
   NS_IMETHOD GetGMPDecryptor(GMPCrashHelper* aHelper,
                              nsTArray<nsCString>* aTags,
                              const nsACString& aNodeId,
                              UniquePtr<GetGMPDecryptorCallback>&& aCallback)
     override;
 
+  // Helper for backwards compatibility with WebRTC/tests.
+  NS_IMETHOD
+  GetGMPVideoDecoder(GMPCrashHelper* aHelper,
+                     nsTArray<nsCString>* aTags,
+                     const nsACString& aNodeId,
+                     UniquePtr<GetGMPVideoDecoderCallback>&& aCallback) override
+  {
+    return GetDecryptingGMPVideoDecoder(aHelper, aTags, aNodeId, Move(aCallback), 0);
+  }
+
   int32_t AsyncShutdownTimeoutMs();
 
   NS_IMETHOD RunPluginCrashCallbacks(uint32_t aPluginId,
                                      const nsACString& aPluginName) override;
 
   RefPtr<AbstractThread> GetAbstractGMPThread();
 
   void ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper);
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -1383,25 +1383,22 @@ GeckoMediaPluginServiceParent::GetNodeId
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__,
        NS_ConvertUTF16toUTF8(aOrigin).get(),
        NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
        (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
 
   nsresult rv;
 
-  if (aGMPName.EqualsLiteral("gmp-widevinecdm") ||
-      aOrigin.EqualsLiteral("null") ||
+  if (aOrigin.EqualsLiteral("null") ||
       aOrigin.IsEmpty() ||
       aTopLevelOrigin.EqualsLiteral("null") ||
       aTopLevelOrigin.IsEmpty()) {
-    // This is for the Google Widevine CDM, which doesn't have persistent
-    // storage and which can't handle being used by more than one origin at
-    // once in the same plugin instance, or at least one of the
-    // (origin, topLevelOrigin) is null or empty; probably a local file.
+    // (origin, topLevelOrigin) is null or empty; this is for an anonymous
+    // origin, probably a local file, for which we don't provide persistent storage.
     // Generate a random node id, and don't store it so that the GMP's storage
     // is temporary and the process for this GMP is not shared with GMP
     // instances that have the same nodeId.
     nsAutoCString salt;
     rv = GenerateRandomPathName(salt, NodeIdSaltLength);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
--- a/dom/media/gmp/PGMPContent.ipdl
+++ b/dom/media/gmp/PGMPContent.ipdl
@@ -20,14 +20,14 @@ intr protocol PGMPContent
   manages PGMPAudioDecoder;
   manages PGMPDecryptor;
   manages PGMPVideoDecoder;
   manages PGMPVideoEncoder;
 
 child:
   async PGMPAudioDecoder();
   async PGMPDecryptor();
-  async PGMPVideoDecoder();
+  async PGMPVideoDecoder(uint32_t aDecryptorId);
   async PGMPVideoEncoder();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/PGMPDecryptor.ipdl
+++ b/dom/media/gmp/PGMPDecryptor.ipdl
@@ -48,16 +48,18 @@ child:
                 uint8_t[] aBuffer,
                 GMPDecryptionData aMetadata);
 
   async DecryptingComplete();
 
 parent:
   async __delete__();
 
+  async SetDecryptorId(uint32_t aId);
+
   async SetSessionId(uint32_t aCreateSessionToken,
                      nsCString aSessionId);
 
   async ResolveLoadSessionPromise(uint32_t aPromiseId,
                                   bool aSuccess);
 
   async ResolvePromise(uint32_t aPromiseId);
 
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -89,16 +89,29 @@ interface mozIGeckoMediaPluginService : 
    */
   [noscript]
   void getGMPVideoDecoder(in GMPCrashHelperPtr helper,
                           in TagArray tags,
                           [optional] in ACString nodeId,
                           in GetGMPVideoDecoderCallback callback);
 
   /**
+   * Gets a video decoder as per getGMPVideoDecoder, except it is linked to
+   * with a corresponding GMPDecryptor via the decryptor's ID.
+   * This is a temporary measure, until we can implement a Chromium CDM
+   * GMP protocol which does both decryption and decoding.
+   */
+  [noscript]
+  void getDecryptingGMPVideoDecoder(in GMPCrashHelperPtr helper,
+                                    in TagArray tags,
+                                    in ACString nodeId,
+                                    in GetGMPVideoDecoderCallback callback,
+                                    in uint32_t decryptorId);
+
+  /**
    * Get a video encoder that supports the specified tags.
    * The array of tags should at least contain a codec tag, and optionally
    * other tags.
    * Callable only on GMP thread.
    * This is an asynchronous operation, the Done method of the callback object
    * will be called on the GMP thread with the result (which might be null in
    * the case of failure). This method always takes ownership of the callback
    * object, but if this method returns an error then the Done method of the
--- a/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
@@ -8,24 +8,21 @@
 #include "VideoUtils.h"
 #include "WidevineDecryptor.h"
 #include "WidevineUtils.h"
 #include "WidevineVideoDecoder.h"
 #include "gmp-api/gmp-entrypoints.h"
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-video-codec.h"
 #include "gmp-api/gmp-platform.h"
-#include "mozilla/StaticPtr.h"
 
 static const GMPPlatformAPI* sPlatform = nullptr;
 
 namespace mozilla {
 
-StaticRefPtr<CDMWrapper> sCDMWrapper;
-
 GMPErr GMPGetCurrentTime(GMPTimestamp* aOutTime) {
   return sPlatform->getcurrenttime(aOutTime);
 }
 
 // Call on main thread only.
 GMPErr GMPSetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS) {
   return sPlatform->settimer(aTask, aTimeoutMS);
 }
@@ -83,61 +80,62 @@ WidevineAdapter::GMPInit(const GMPPlatfo
   init();
 
   return GMPNoErr;
 }
 
 GMPErr
 WidevineAdapter::GMPGetAPI(const char* aAPIName,
                            void* aHostAPI,
-                           void** aPluginAPI)
+                           void** aPluginAPI,
+                           uint32_t aDecryptorId)
 {
-  Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p",
-      aAPIName, aHostAPI, aPluginAPI, this);
+  Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p",
+      aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
   if (!strcmp(aAPIName, GMP_API_DECRYPTOR)) {
-    if (sCDMWrapper) {
-      // We only support one CDM instance per GMP process. Fail!
-      Log("WidevineAdapter::GMPGetAPI() Tried to create more than once CDM per process! FAIL!");
+    if (WidevineDecryptor::GetInstance(aDecryptorId)) {
+      // We only support one CDM instance per PGMPDecryptor. Fail!
+      Log("WidevineAdapter::GMPGetAPI() Tried to create more than once CDM per IPDL actor! FAIL!");
       return GMPQuotaExceededErr;
     }
     auto create = reinterpret_cast<decltype(::CreateCdmInstance)*>(
       PR_FindFunctionSymbol(mLib, "CreateCdmInstance"));
     if (!create) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p FAILED to find CreateCdmInstance",
-        aAPIName, aHostAPI, aPluginAPI, this);
+      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to find CreateCdmInstance",
+        aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
       return GMPGenericErr;
     }
 
     WidevineDecryptor* decryptor = new WidevineDecryptor();
 
     auto cdm = reinterpret_cast<cdm::ContentDecryptionModule*>(
       create(cdm::ContentDecryptionModule::kVersion,
              kEMEKeySystemWidevine.get(),
              kEMEKeySystemWidevine.Length(),
              &GetCdmHost,
              decryptor));
     if (!cdm) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p FAILED to create cdm",
-          aAPIName, aHostAPI, aPluginAPI, this);
+      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to create cdm",
+          aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
       return GMPGenericErr;
     }
     Log("cdm: 0x%x", cdm);
-    sCDMWrapper = new CDMWrapper(cdm);
-    decryptor->SetCDM(RefPtr<CDMWrapper>(sCDMWrapper));
+    RefPtr<CDMWrapper> wrapper(new CDMWrapper(cdm));
+    decryptor->SetCDM(wrapper, aDecryptorId);
     *aPluginAPI = decryptor;
 
   } else if (!strcmp(aAPIName, GMP_API_VIDEO_DECODER)) {
-    if (!sCDMWrapper) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p No cdm for video decoder",
-          aAPIName, aHostAPI, aPluginAPI, this);
+    RefPtr<CDMWrapper> wrapper = WidevineDecryptor::GetInstance(aDecryptorId);
+    if (!wrapper) {
+      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p No cdm for video decoder",
+          aAPIName, aHostAPI, aPluginAPI, thiss, aDecryptorId);
       return GMPGenericErr;
     }
     *aPluginAPI = new WidevineVideoDecoder(static_cast<GMPVideoHost*>(aHostAPI),
-                                           RefPtr<CDMWrapper>(sCDMWrapper));
-
+                                           wrapper);
   }
   return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
 }
 
 void
 WidevineAdapter::GMPShutdown()
 {
   Log("WidevineAdapter::GMPShutdown()");
--- a/dom/media/gmp/widevine-adapter/WidevineAdapter.h
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.h
@@ -16,17 +16,20 @@ namespace mozilla {
 
 class WidevineAdapter : public gmp::GMPAdapter {
 public:
 
   void SetAdaptee(PRLibrary* aLib) override;
 
   // These are called in place of the corresponding GMP API functions.
   GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override;
-  GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) override;
+  GMPErr GMPGetAPI(const char* aAPIName,
+                   void* aHostAPI,
+                   void** aPluginAPI,
+                   uint32_t aDecryptorId) override;
   void GMPShutdown() override;
   void GMPSetNodeId(const char* aNodeId, uint32_t aLength) override;
 
   static bool Supports(int32_t aModuleVersion,
                        int32_t aInterfaceVersion,
                        int32_t aHostVersion);
 
 private:
--- a/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
@@ -11,33 +11,48 @@
 #include <mozilla/SizePrintfMacros.h>
 #include <stdarg.h>
 
 using namespace cdm;
 using namespace std;
 
 namespace mozilla {
 
+static map<uint32_t, RefPtr<CDMWrapper>> sDecryptors;
+
+/* static */
+RefPtr<CDMWrapper>
+WidevineDecryptor::GetInstance(uint32_t aInstanceId)
+{
+  auto itr = sDecryptors.find(aInstanceId);
+  if (itr != sDecryptors.end()) {
+    return itr->second;
+  }
+  return nullptr;
+}
+
 
 WidevineDecryptor::WidevineDecryptor()
   : mCallback(nullptr)
 {
   Log("WidevineDecryptor created this=%p", this);
   AddRef(); // Released in DecryptingComplete().
 }
 
 WidevineDecryptor::~WidevineDecryptor()
 {
   Log("WidevineDecryptor destroyed this=%p", this);
 }
 
 void
-WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM)
+WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aInstanceId)
 {
   mCDM = aCDM;
+  mInstanceId = aInstanceId;
+  sDecryptors[mInstanceId] = aCDM;
 }
 
 void
 WidevineDecryptor::Init(GMPDecryptorCallback* aCallback,
                         bool aDistinctiveIdentifierRequired,
                         bool aPersistentStateRequired)
 {
   Log("WidevineDecryptor::Init() this=%p distinctiveId=%d persistentState=%d",
@@ -205,17 +220,22 @@ WidevineDecryptor::Decrypt(GMPBuffer* aB
   }
   mCallback->Decrypted(aBuffer, ToGMPErr(rv));
 }
 
 void
 WidevineDecryptor::DecryptingComplete()
 {
   Log("WidevineDecryptor::DecryptingComplete() this=%p", this);
+  // Drop our references to the CDMWrapper. When any other references
+  // held elsewhere are dropped (for example references held by a
+  // WidevineVideoDecoder, or a runnable), the CDMWrapper destroys
+  // the CDM.
   mCDM = nullptr;
+  sDecryptors.erase(mInstanceId);
   mCallback = nullptr;
   Release();
 }
 
 class WidevineBuffer : public cdm::Buffer {
 public:
   explicit WidevineBuffer(size_t aSize) {
     Log("WidevineBuffer(size=" PRIuSIZE ") created", aSize);
--- a/dom/media/gmp/widevine-adapter/WidevineDecryptor.h
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.h
@@ -19,17 +19,19 @@ class WidevineDecryptor : public GMPDecr
                         , public cdm::Host_8
 {
 public:
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WidevineDecryptor)
 
   WidevineDecryptor();
 
-  void SetCDM(RefPtr<CDMWrapper> aCDM);
+  void SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aDecryptorId);
+
+  static RefPtr<CDMWrapper> GetInstance(uint32_t aDecryptorId);
 
   // GMPDecryptor
   void Init(GMPDecryptorCallback* aCallback,
             bool aDistinctiveIdentifierRequired,
             bool aPersistentStateRequired) override;
 
   void CreateSession(uint32_t aCreateSessionToken,
                      uint32_t aPromiseId,
@@ -119,13 +121,14 @@ private:
   ~WidevineDecryptor();
   RefPtr<CDMWrapper> mCDM;
   cdm::ContentDecryptionModule_8* CDM() { return mCDM->GetCDM(); }
 
   GMPDecryptorCallback* mCallback;
   std::map<uint32_t, uint32_t> mPromiseIdToNewSessionTokens;
   bool mDistinctiveIdentifierRequired = false;
   bool mPersistentStateRequired = false;
+  uint32_t mInstanceId = 0;
 };
 
 } // namespace mozilla
 
 #endif // WidevineDecryptor_h_
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -591,37 +591,33 @@ class GMPStorageTest : public GMPDecrypt
     EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
 
     SetFinished();
   }
 
   class CreateDecryptorDone : public GetGMPDecryptorCallback
   {
   public:
-    CreateDecryptorDone(GMPStorageTest* aRunner, nsIRunnable* aContinuation)
-      : mRunner(aRunner),
-        mContinuation(aContinuation)
+    explicit CreateDecryptorDone(GMPStorageTest* aRunner)
+      : mRunner(aRunner)
     {
     }
 
     void Done(GMPDecryptorProxy* aDecryptor) override
     {
       mRunner->mDecryptor = aDecryptor;
       EXPECT_TRUE(!!mRunner->mDecryptor);
 
       if (mRunner->mDecryptor) {
         mRunner->mDecryptor->Init(mRunner, false, true);
       }
-      nsCOMPtr<nsIThread> thread(GetGMPThread());
-      thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
     }
 
   private:
     RefPtr<GMPStorageTest> mRunner;
-    nsCOMPtr<nsIRunnable> mContinuation;
   };
 
   void CreateDecryptor(const nsCString& aNodeId,
                        const nsCString& aUpdate)
   {
     nsTArray<nsCString> updates;
     updates.AppendElement(aUpdate);
     nsCOMPtr<nsIRunnable> continuation(new Updates(this, Move(updates)));
@@ -674,17 +670,23 @@ class GMPStorageTest : public GMPDecrypt
 
     mNodeId = aNodeId;
     EXPECT_TRUE(!mNodeId.IsEmpty());
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
     UniquePtr<GetGMPDecryptorCallback> callback(
-      new CreateDecryptorDone(this, aContinuation));
+      new CreateDecryptorDone(this));
+
+    // Continue after the OnSetDecryptorId message, so that we don't
+    // get warnings in the async shutdown tests due to receiving the
+    // SetDecryptorId message after we've started shutdown.
+    mSetDecryptorIdContinuation = aContinuation;
+
     nsresult rv =
       service->GetGMPDecryptor(nullptr, &tags, mNodeId, Move(callback));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
@@ -1368,16 +1370,26 @@ class GMPStorageTest : public GMPDecrypt
       nsCOMPtr<nsIRunnable> continuation = mExpected[0].mContinuation;
       mExpected.RemoveElementAt(0);
       if (continuation) {
         NS_DispatchToCurrentThread(continuation);
       }
     }
   }
 
+  void SetDecryptorId(uint32_t aId) override
+  {
+    if (!mSetDecryptorIdContinuation) {
+      return;
+    }
+    nsCOMPtr<nsIThread> thread(GetGMPThread());
+    thread->Dispatch(mSetDecryptorIdContinuation, NS_DISPATCH_NORMAL);
+    mSetDecryptorIdContinuation = nullptr;
+  }
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override { }
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override {}
   void ResolvePromise(uint32_t aPromiseId) override {}
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aException,
                      const nsCString& aSessionId) override { }
@@ -1411,16 +1423,18 @@ private:
       , mContinuation(aContinuation)
     {}
     nsCString mMessage;
     nsCOMPtr<nsIRunnable> mContinuation;
   };
 
   nsTArray<ExpectedMessage> mExpected;
 
+  RefPtr<nsIRunnable> mSetDecryptorIdContinuation;
+
   GMPDecryptorProxy* mDecryptor;
   Monitor mMonitor;
   Atomic<bool> mFinished;
   nsCString mNodeId;
 };
 
 void
 GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&))
--- a/dom/media/platforms/agnostic/eme/EMEVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.cpp
@@ -27,16 +27,17 @@ EMEVideoCallbackAdapter::Error(GMPErr aE
 
 EMEVideoDecoder::EMEVideoDecoder(CDMProxy* aProxy,
                                  const GMPVideoDecoderParams& aParams)
   : GMPVideoDecoder(GMPVideoDecoderParams(aParams).WithAdapter(
                     new EMEVideoCallbackAdapter(aParams.mCallback,
                                                 VideoInfo(aParams.mConfig.mDisplay),
                                                 aParams.mImageContainer)))
   , mProxy(aProxy)
+  , mDecryptorId(aProxy->GetDecryptorId())
 {}
 
 void
 EMEVideoDecoder::InitTags(nsTArray<nsCString>& aTags)
 {
   VideoInfo config = GetConfig();
   if (MP4Decoder::IsH264(config.mMimeType)) {
     aTags.AppendElement(NS_LITERAL_CSTRING("h264"));
--- a/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h
+++ b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h
@@ -28,16 +28,18 @@ public:
 
 class EMEVideoDecoder : public GMPVideoDecoder {
 public:
   EMEVideoDecoder(CDMProxy* aProxy, const GMPVideoDecoderParams& aParams);
 
 private:
   void InitTags(nsTArray<nsCString>& aTags) override;
   nsCString GetNodeId() override;
+  uint32_t DecryptorId() const override { return mDecryptorId; }
   GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample) override;
 
   RefPtr<CDMProxy> mProxy;
+  uint32_t mDecryptorId;
 };
 
 } // namespace mozilla
 
 #endif // EMEVideoDecoder_h_
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
@@ -306,17 +306,21 @@ GMPVideoDecoder::Init()
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   UniquePtr<GetGMPVideoDecoderCallback> callback(new GMPInitDoneCallback(this));
-  if (NS_FAILED(mMPS->GetGMPVideoDecoder(mCrashHelper, &tags, GetNodeId(), Move(callback)))) {
+  if (NS_FAILED(mMPS->GetDecryptingGMPVideoDecoder(mCrashHelper,
+                                                   &tags,
+                                                   GetNodeId(),
+                                                   Move(callback),
+                                                   DecryptorId()))) {
     mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
   }
 
   return promise;
 }
 
 void
 GMPVideoDecoder::Input(MediaRawData* aSample)
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -77,16 +77,17 @@ public:
   const char* GetDescriptionName() const override
   {
     return "GMP video decoder";
   }
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
+  virtual uint32_t DecryptorId() const { return 0; }
   virtual GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample);
   virtual const VideoInfo& GetConfig() const;
 
 private:
 
   class GMPInitDoneCallback : public GetGMPVideoDecoderCallback
   {
   public:
--- a/dom/presentation/provider/AndroidCastDeviceProvider.js
+++ b/dom/presentation/provider/AndroidCastDeviceProvider.js
@@ -403,17 +403,17 @@ AndroidCastDeviceProvider.prototype = {
     if (!this._listener) {
       // remove observer
       Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED);
       Services.obs.removeObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED);
       return;
     }
 
     // Sync all device already found by Android.
-    Services.obs.notifyObservers(null, TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE, "");
+    Messaging.sendRequest({ type: TOPIC_ANDROID_CAST_DEVICE_SYNCDEVICE });
     // Observer registration
     Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_ADDED, false);
     Services.obs.addObserver(this, TOPIC_ANDROID_CAST_DEVICE_REMOVED, false);
   },
 
   get listener() {
     return this._listener;
   },
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -58,9 +58,13 @@ enum RequestContext {
   "sharedworker", "subresource", "style", "track", "video", "worker", "xmlhttprequest",
   "xslt"
 };
 
 enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
 enum RequestCredentials { "omit", "same-origin", "include" };
 enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" };
 enum RequestRedirect { "follow", "error", "manual" };
-enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" };
+enum ReferrerPolicy {
+  "", "no-referrer", "no-referrer-when-downgrade", "origin",
+  "origin-when-cross-origin", "unsafe-url", "same-origin", "strict-origin",
+  "strict-origin-when-cross-origin"
+};
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -200,18 +200,20 @@ ChannelFromScriptURL(nsIPrincipal* princ
                        nullptr, // aCallbacks
                        aLoadFlags,
                        ios);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
+    mozilla::net::ReferrerPolicy referrerPolicy = parentDoc ?
+      parentDoc->GetReferrerPolicy() : mozilla::net::RP_Default;
     rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
-                                                       httpChannel, mozilla::net::RP_Default);
+                                                       httpChannel, referrerPolicy);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   channel.forget(aChannel);
   return rv;
 }
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1336,37 +1336,53 @@ public:
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
     MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
 
     nsAutoCString referrer;
     // Ignore the return value since the Referer header may not exist.
     httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Referer"), referrer);
     if (!referrer.IsEmpty()) {
       mReferrer = referrer;
+    } else {
+      // If there's no referrer Header, means the header was omitted for
+      // security/privacy reason.
+      mReferrer = EmptyCString();
     }
 
     uint32_t referrerPolicy = 0;
     rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
     NS_ENSURE_SUCCESS(rv, rv);
     switch (referrerPolicy) {
+      case nsIHttpChannel::REFERRER_POLICY_UNSET:
+      mReferrerPolicy = ReferrerPolicy::_empty;
+      break;
     case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER:
       mReferrerPolicy = ReferrerPolicy::No_referrer;
       break;
     case nsIHttpChannel::REFERRER_POLICY_ORIGIN:
       mReferrerPolicy = ReferrerPolicy::Origin;
       break;
     case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
       mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
       break;
     case nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
       mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
       break;
     case nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL:
       mReferrerPolicy = ReferrerPolicy::Unsafe_url;
       break;
+    case nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Same_origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Strict_origin_when_cross_origin;
+      break;
+    case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN:
+      mReferrerPolicy = ReferrerPolicy::Strict_origin;
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("Invalid Referrer Policy enum value?");
       break;
     }
 
     rv = httpChannel->GetRequestMethod(mMethod);
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2536,19 +2536,20 @@ XMLHttpRequestMainThread::InitiateFetch(
       mAuthorRequestHeaders.Set("accept", NS_LITERAL_CSTRING("*/*"));
     }
 
     mAuthorRequestHeaders.ApplyToChannel(httpChannel);
 
     if (!IsSystemXHR()) {
       nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
       nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
+      mozilla::net::ReferrerPolicy referrerPolicy = doc ?
+        doc->GetReferrerPolicy() : mozilla::net::RP_Default;
       nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
-                                                    httpChannel,
-                                                    mozilla::net::RP_Default);
+                                                    httpChannel, referrerPolicy);
     }
 
     // Some extensions override the http protocol handler and provide their own
     // implementation. The channels returned from that implementation don't
     // always seem to implement the nsIUploadChannel2 interface, presumably
     // because it's a new interface. Eventually we should remove this and simply
     // require that http channels implement the new interface (see bug 529041).
     nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(httpChannel);
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -228,17 +228,17 @@ GPUProcessManager::EnsureVRManager()
 }
 
 void
 GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   if (!mProcess->IsConnected()) {
-    DisableGPUProcess("Failed to launch GPU process");
+    DisableGPUProcess("Failed to connect GPU process");
     return;
   }
 
   mGPUChild = mProcess->GetActor();
   mProcessToken = mProcess->GetProcessToken();
 
   Endpoint<PVsyncBridgeParent> vsyncParent;
   Endpoint<PVsyncBridgeChild> vsyncChild;
@@ -319,17 +319,20 @@ GPUProcessManager::OnProcessDeviceReset(
 void
 GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
 {
   MOZ_ASSERT(mProcess && mProcess == aHost);
 
   DestroyProcess();
 
   if (mNumProcessAttempts > uint32_t(gfxPrefs::GPUProcessDevMaxRestarts())) {
-    DisableGPUProcess("GPU processed crashed too many times");
+    char disableMessage[64];
+    SprintfLiteral(disableMessage, "GPU process disabled after %d attempts",
+                   mNumProcessAttempts);
+    DisableGPUProcess(disableMessage);
   }
 
   HandleProcessLost();
 }
 
 void
 GPUProcessManager::HandleProcessLost()
 {
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -419,22 +419,22 @@ private:
   DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms",   CollectScrollTransforms, bool, false);
   // On b2g, in really bad cases, I've seen up to 80 ms delays between touch events and the main thread
   // processing them. So 80 ms / 16 = 5 vsync events. Double it up just to be on the safe side, so 10.
   DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count",  CompositorUnobserveCount, int32_t, 10);
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
   DECL_GFX_PREF(Once, "gfx.screen-mirroring.enabled",          ScreenMirroringEnabled, bool, false);
 
+  DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist",       IgnoreDXInterop2Blacklist, bool, false);
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
 #if defined(XP_MACOSX)
   DECL_GFX_PREF(Live, "gl.multithreaded",                      GLMultithreaded, bool, false);
 #endif
   DECL_GFX_PREF(Live, "gl.require-hardware",                   RequireHardwareGL, bool, false);
-  DECL_GFX_PREF(Live, "ignore-dx-interop2-blacklist",          IgnoreDXInterop2Blacklist, bool, false);
 
   DECL_GFX_PREF(Once, "image.cache.size",                      ImageCacheSize, int32_t, 5*1024*1024);
   DECL_GFX_PREF(Once, "image.cache.timeweight",                ImageCacheTimeWeight, int32_t, 500);
   DECL_GFX_PREF(Live, "image.decode-immediately.enabled",      ImageDecodeImmediatelyEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);
   DECL_GFX_PREF(Live, "image.infer-src-animation.threshold-ms", ImageInferSrcAnimationThresholdMS, uint32_t, 2000);
   DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time",      ImageMemDecodeBytesAtATime, uint32_t, 200000);
   DECL_GFX_PREF(Live, "image.mem.discardable",                 ImageMemDiscardable, bool, false);
--- a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
@@ -93,31 +93,16 @@ IPDLUnitTestFromString(const char* const
 //===== TEMPLATED =====
 ${STRING_TO_ENUMS}
 //-----------------------------------------------------------------------------
     else
         return static_cast<IPDLUnitTestType>(0);
 }
 
 
-const char*
-IPDLUnitTestToString(IPDLUnitTestType aTest)
-{
-    switch (aTest) {
-//-----------------------------------------------------------------------------
-//===== TEMPLATED =====
-${ENUM_TO_STRINGS}
-//-----------------------------------------------------------------------------
-
-    default:
-        return nullptr;
-    }
-}
-
-
 IPDLUnitTestType
 IPDLUnitTest()
 {
     return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName());
 }
 
 
 } // namespace <anon>
--- a/ipc/ipdl/test/cxx/TestBadActor.h
+++ b/ipc/ipdl/test/cxx/TestBadActor.h
@@ -29,35 +29,35 @@ protected:
   {
     if (AbnormalShutdown != why)
       fail("unexpected destruction");
     passed("ok");
     QuitParent();
   }
 
   virtual PTestBadActorSubParent*
-  AllocPTestBadActorSubParent();
+  AllocPTestBadActorSubParent() override;
 
   virtual bool
-  DeallocPTestBadActorSubParent(PTestBadActorSubParent* actor) {
+  DeallocPTestBadActorSubParent(PTestBadActorSubParent* actor) override {
     delete actor;
     return true;
   }
 };
 
 class TestBadActorSubParent
   : public PTestBadActorSubParent
 {
 public:
   TestBadActorSubParent() { }
   virtual ~TestBadActorSubParent() { }
 
 protected:
   virtual void ActorDestroy(ActorDestroyReason why) override {}
-  virtual bool RecvPing();
+  virtual bool RecvPing() override;
 };
 
 class TestBadActorChild
   : public PTestBadActorChild
 {
 public:
   TestBadActorChild() { }
   virtual ~TestBadActorChild() { }
--- a/ipc/ipdl/test/cxx/TestDataStructures.cpp
+++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp
@@ -22,22 +22,16 @@ assert_arrays_equal(const InfallibleTArr
 }
 
 inline static TestDataStructuresSub&
 Cast(PTestDataStructuresSubParent* a)
 {
     return *static_cast<TestDataStructuresSub*>(a);
 }
 
-inline static TestDataStructuresSub&
-Cast(PTestDataStructuresSubChild* a)
-{
-    return *static_cast<TestDataStructuresSub*>(a);
-}
-
 //-----------------------------------------------------------------------------
 // parent
 
 TestDataStructuresParent::TestDataStructuresParent()
 {
     MOZ_COUNT_CTOR(TestDataStructuresParent);
 }
 
--- a/ipc/ipdl/test/cxx/TestEndpointOpens.h
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.h
@@ -25,17 +25,17 @@ public:
   virtual ~TestEndpointOpensParent() {}
 
   static bool RunTestInProcesses() { return true; }
   static bool RunTestInThreads() { return false; }
 
   void Main();
 
 protected:
-  virtual bool RecvStartSubprotocol(mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint);
+  virtual bool RecvStartSubprotocol(mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint) override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 };
 
 } // namespace _ipdltest
 
 namespace _ipdltest2 {
 
--- a/ipc/ipdl/test/cxx/TestHangs.h
+++ b/ipc/ipdl/test/cxx/TestHangs.h
@@ -23,17 +23,17 @@ public:
     //                  clear how that should work in threads.
     static bool RunTestInThreads() { return false; }
 
     void Main();
 
 protected:
     virtual bool ShouldContinueFromReplyTimeout() override;
 
-    virtual bool RecvNonce() {
+    virtual bool RecvNonce() override {
         return true;
     }
 
     virtual bool AnswerStackFrame() override;
 
     virtual void ActorDestroy(ActorDestroyReason why) override
     {
         if (AbnormalShutdown != why)
--- a/ipc/ipdl/test/cxx/TestShutdown.h
+++ b/ipc/ipdl/test/cxx/TestShutdown.h
@@ -192,17 +192,17 @@ public:
     {
     }
     virtual ~TestShutdownChild()
     {
     }
 
 protected:
     virtual bool
-    RecvStart();
+    RecvStart() override;
 
     virtual PTestShutdownSubChild*
     AllocPTestShutdownSubChild(
         const bool& expectCrash) override
     {
         return new TestShutdownSubChild(expectCrash);
     }
 
--- a/ipc/ipdl/test/cxx/TestUrgency.h
+++ b/ipc/ipdl/test/cxx/TestUrgency.h
@@ -17,22 +17,22 @@ public:
     TestUrgencyParent();
     virtual ~TestUrgencyParent();
 
     static bool RunTestInProcesses() { return true; }
     static bool RunTestInThreads() { return false; }
 
     void Main();
 
-    bool RecvTest1(uint32_t *value);
-    bool RecvTest2();
-    bool RecvTest3(uint32_t *value);
+    bool RecvTest1(uint32_t *value) override;
+    bool RecvTest2() override;
+    bool RecvTest3(uint32_t *value) override;
     bool RecvTest4_Begin();
     bool RecvTest4_NestedSync();
-    bool RecvFinalTest_Begin();
+    bool RecvFinalTest_Begin() override;
 
     bool ShouldContinueFromReplyTimeout() override
     {
       return false;
     }
     virtual void ActorDestroy(ActorDestroyReason why) override
     {
         passed("ok");
@@ -46,19 +46,19 @@ private:
 
 class TestUrgencyChild :
     public PTestUrgencyChild
 {
 public:
     TestUrgencyChild();
     virtual ~TestUrgencyChild();
 
-    bool RecvStart();
-    bool RecvReply1(uint32_t *reply);
-    bool RecvReply2(uint32_t *reply);
+    bool RecvStart() override;
+    bool RecvReply1(uint32_t *reply) override;
+    bool RecvReply2(uint32_t *reply) override;
 
     virtual void ActorDestroy(ActorDestroyReason why) override
     {
         QuitChild();
     }
 
 private:
     uint32_t test_;
--- a/ipc/ipdl/test/cxx/TestUrgentHangs.h
+++ b/ipc/ipdl/test/cxx/TestUrgentHangs.h
@@ -19,19 +19,19 @@ public:
 
     static bool RunTestInProcesses() { return true; }
     static bool RunTestInThreads() { return false; }
 
     void Main();
     void SecondStage();
     void ThirdStage();
 
-    bool RecvTest1_2();
-    bool RecvTestInner();
-    bool RecvTestInnerUrgent();
+    bool RecvTest1_2() override;
+    bool RecvTestInner() override;
+    bool RecvTestInnerUrgent() override;
 
     bool ShouldContinueFromReplyTimeout() override
     {
       return false;
     }
     virtual void ActorDestroy(ActorDestroyReason why) override
     {
 	if (mInnerCount != 1) {
@@ -51,24 +51,24 @@ private:
 
 class TestUrgentHangsChild :
     public PTestUrgentHangsChild
 {
 public:
     TestUrgentHangsChild();
     virtual ~TestUrgentHangsChild();
 
-    bool RecvTest1_1();
-    bool RecvTest1_3();
-    bool RecvTest2();
-    bool RecvTest3();
-    bool RecvTest4();
-    bool RecvTest4_1();
-    bool RecvTest5();
-    bool RecvTest5_1();
+    bool RecvTest1_1() override;
+    bool RecvTest1_3() override;
+    bool RecvTest2() override;
+    bool RecvTest3() override;
+    bool RecvTest4() override;
+    bool RecvTest4_1() override;
+    bool RecvTest5() override;
+    bool RecvTest5_1() override;
 
     virtual void ActorDestroy(ActorDestroyReason why) override
     {
         QuitChild();
     }
 };
 
 
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -259,16 +259,23 @@ struct CSSPixel {
   }
 
   static nsRect ToAppUnits(const CSSRect& aRect) {
     return nsRect(NSToCoordRoundWithClamp(aRect.x * float(AppUnitsPerCSSPixel())),
                   NSToCoordRoundWithClamp(aRect.y * float(AppUnitsPerCSSPixel())),
                   NSToCoordRoundWithClamp(aRect.width * float(AppUnitsPerCSSPixel())),
                   NSToCoordRoundWithClamp(aRect.height * float(AppUnitsPerCSSPixel())));
   }
+
+  static nsRect ToAppUnits(const CSSIntRect& aRect) {
+    return nsRect(NSToCoordRoundWithClamp(float(aRect.x) * float(AppUnitsPerCSSPixel())),
+                  NSToCoordRoundWithClamp(float(aRect.y) * float(AppUnitsPerCSSPixel())),
+                  NSToCoordRoundWithClamp(float(aRect.width) * float(AppUnitsPerCSSPixel())),
+                  NSToCoordRoundWithClamp(float(aRect.height) * float(AppUnitsPerCSSPixel())));
+  }
 };
 
 /*
  * The pixels that are referred to as "device pixels" in layout code. In
  * general values measured in LayoutDevicePixels are obtained by dividing a
  * value in app units by AppUnitsPerDevPixel(). Conversion between CSS pixels
  * and LayoutDevicePixels is affected by:
  * 1) the "full zoom" (see nsPresContext::SetFullZoom)
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -2714,26 +2714,31 @@ nsCSSRendering::PaintGradient(nsPresCont
     // things easier, and then rotate the matrix to turn everything back the
     // right way up.
     if (lineStart.x > lineEnd.x || lineStart.y > lineEnd.y) {
       std::swap(lineStart, lineEnd);
       matrix.Scale(-1, -1);
     }
 
     // Fit the gradient line exactly into the source rect.
+    // aSrc is relative to aIntrinsincSize.
+    // srcRectDev will be relative to srcSize, so in the same coordinate space
+    // as lineStart / lineEnd.
+    gfxRect srcRectDev = nsLayoutUtils::RectToGfxRect(
+      CSSPixel::ToAppUnits(aSrc), appUnitsPerDevPixel);
     if (lineStart.x != lineEnd.x) {
-      rectLen = aPresContext->CSSPixelsToDevPixels(aSrc.width);
-      offset = ((double)aSrc.x - lineStart.x) / lineLength;
-      lineStart.x = aSrc.x;
-      lineEnd.x = aSrc.x + rectLen;
+      rectLen = srcRectDev.width;
+      offset = (srcRectDev.x - lineStart.x) / lineLength;
+      lineStart.x = srcRectDev.x;
+      lineEnd.x = srcRectDev.XMost();
     } else {
-      rectLen = aPresContext->CSSPixelsToDevPixels(aSrc.height);
-      offset = ((double)aSrc.y - lineStart.y) / lineLength;
-      lineStart.y = aSrc.y;
-      lineEnd.y = aSrc.y + rectLen;
+      rectLen = srcRectDev.height;
+      offset = (srcRectDev.y - lineStart.y) / lineLength;
+      lineStart.y = srcRectDev.y;
+      lineEnd.y = srcRectDev.YMost();
     }
 
     // Adjust gradient stop positions for the new gradient line.
     double scale = lineLength / rectLen;
     for (size_t i = 0; i < stops.Length(); i++) {
       stops[i].mPosition = (stops[i].mPosition - offset) * fabs(scale);
     }
 
--- a/layout/generic/BlockReflowInput.cpp
+++ b/layout/generic/BlockReflowInput.cpp
@@ -324,35 +324,35 @@ BlockReflowInput::ReplacedBlockFitsInAva
            replacedISize.borderBoxISize +
            (mContentArea.IEnd(wm) -
             aFloatAvailableSpace.mRect.IEnd(wm)) <=
          mContentArea.ISize(wm);
 }
 
 nsFlowAreaRect
 BlockReflowInput::GetFloatAvailableSpaceWithState(
-                      nscoord aBCoord,
-                      nsFloatManager::SavedState *aState) const
+                    nscoord aBCoord, ShapeType aShapeType,
+                    nsFloatManager::SavedState* aState) const
 {
   WritingMode wm = mReflowInput.GetWritingMode();
 #ifdef DEBUG
   // Verify that the caller setup the coordinate system properly
   nscoord wI, wB;
   mFloatManager->GetTranslation(wI, wB);
 
   NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB),
                "bad coord system");
 #endif
 
   nscoord blockSize = (mContentArea.BSize(wm) == nscoord_MAX)
     ? nscoord_MAX : std::max(mContentArea.BEnd(wm) - aBCoord, 0);
   nsFlowAreaRect result =
-    mFloatManager->GetFlowArea(wm, aBCoord, nsFloatManager::BAND_FROM_POINT,
-                               blockSize, mContentArea, aState,
-                               ContainerSize());
+    mFloatManager->GetFlowArea(wm, aBCoord, blockSize,
+                               BandInfoType::BandFromPoint, aShapeType,
+                               mContentArea, aState, ContainerSize());
   // Keep the inline size >= 0 for compatibility with nsSpaceManager.
   if (result.mRect.ISize(wm) < 0) {
     result.mRect.ISize(wm) = 0;
   }
 
 #ifdef DEBUG
   if (nsBlockFrame::gNoisyReflow) {
     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
@@ -374,18 +374,20 @@ BlockReflowInput::GetFloatAvailableSpace
   // Verify that the caller setup the coordinate system properly
   nscoord wI, wB;
   mFloatManager->GetTranslation(wI, wB);
 
   NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB),
                "bad coord system");
 #endif
   nsFlowAreaRect result =
-    mFloatManager->GetFlowArea(wm, aBCoord, nsFloatManager::WIDTH_WITHIN_HEIGHT,
-                               aBSize, mContentArea, aState, ContainerSize());
+    mFloatManager->GetFlowArea(wm, aBCoord, aBSize,
+                               BandInfoType::WidthWithinHeight,
+                               ShapeType::ShapeOutside,
+                               mContentArea, aState, ContainerSize());
   // Keep the width >= 0 for compatibility with nsSpaceManager.
   if (result.mRect.ISize(wm) < 0) {
     result.mRect.ISize(wm) = 0;
   }
 
 #ifdef DEBUG
   if (nsBlockFrame::gNoisyReflow) {
     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
@@ -613,17 +615,18 @@ BlockReflowInput::AddFloat(nsLineLayout*
 
   bool placed;
 
   // Now place the float immediately if possible. Otherwise stash it
   // away in mBelowCurrentLineFloats and place it later.
   // If one or more floats has already been pushed to the next line,
   // don't let this one go on the current line, since that would violate
   // float ordering.
-  LogicalRect floatAvailableSpace = GetFloatAvailableSpace().mRect;
+  LogicalRect floatAvailableSpace =
+    GetFloatAvailableSpaceForPlacingFloat(mBCoord).mRect;
   if (mBelowCurrentLineFloats.IsEmpty() &&
       (aLineLayout->LineIsEmpty() ||
        mBlock->ComputeFloatISize(*this, floatAvailableSpace, aFloat)
        <= aAvailableISize)) {
     // And then place it
     placed = FlowAndPlaceFloat(aFloat);
     if (placed) {
       // Pass on updated available space to the current inline reflow engine
@@ -737,18 +740,19 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF
 
   // See if the float should clear any preceding floats...
   // XXX We need to mark this float somehow so that it gets reflowed
   // when floats are inserted before it.
   if (StyleClear::None != floatDisplay->mBreakType) {
     // XXXldb Does this handle vertical margins correctly?
     mBCoord = ClearFloats(mBCoord, floatDisplay->PhysicalBreakType(wm));
   }
-    // Get the band of available space
-  nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(mBCoord);
+  // Get the band of available space with respect to margin box.
+  nsFlowAreaRect floatAvailableSpace =
+    GetFloatAvailableSpaceForPlacingFloat(mBCoord);
   LogicalRect adjustedAvailableSpace =
     mBlock->AdjustFloatAvailableSpace(*this, floatAvailableSpace.mRect, aFloat);
 
   NS_ASSERTION(aFloat->GetParent() == mBlock,
                "Float frame has wrong parent");
 
   SizeComputationInput offsets(aFloat, mReflowInput.mRenderingContext,
                            wm, mReflowInput.ComputedISize());
@@ -813,17 +817,17 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF
     // Nope. try to advance to the next band.
     if (StyleDisplay::Table != floatDisplay->mDisplay ||
           eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) {
 
       mBCoord += floatAvailableSpace.mRect.BSize(wm);
       if (adjustedAvailableSpace.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
         adjustedAvailableSpace.BSize(wm) -= floatAvailableSpace.mRect.BSize(wm);
       }
-      floatAvailableSpace = GetFloatAvailableSpace(mBCoord);
+      floatAvailableSpace = GetFloatAvailableSpaceForPlacingFloat(mBCoord);
     } else {
       // This quirk matches the one in nsBlockFrame::AdjustFloatAvailableSpace
       // IE handles float tables in a very special way
 
       // see if the previous float is also a table and has "align"
       nsFloatCache* fc = mCurrentLineFloats.Head();
       nsIFrame* prevFrame = nullptr;
       while (fc) {
@@ -854,17 +858,17 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF
           }
         }
       }
 
       // the table does not fit anymore in this line so advance to next band 
       mBCoord += floatAvailableSpace.mRect.BSize(wm);
       // To match nsBlockFrame::AdjustFloatAvailableSpace, we have to
       // get a new width for the new band.
-      floatAvailableSpace = GetFloatAvailableSpace(mBCoord);
+      floatAvailableSpace = GetFloatAvailableSpaceForPlacingFloat(mBCoord);
       adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this,
                                  floatAvailableSpace.mRect, aFloat);
       floatMarginISize = FloatMarginISize(mReflowInput,
                                           adjustedAvailableSpace.ISize(wm),
                                           aFloat, offsets);
     }
 
     mustPlaceFloat = false;
--- a/layout/generic/BlockReflowInput.h
+++ b/layout/generic/BlockReflowInput.h
@@ -17,16 +17,18 @@ class nsFrameList;
 class nsOverflowContinuationTracker;
 
 namespace mozilla {
 
 // BlockReflowInput contains additional reflow input information that the
 // block frame uses along with ReflowInput. Like ReflowInput, this
 // is read-only data that is passed down from a parent frame to its children.
 class BlockReflowInput {
+  using BandInfoType = nsFloatManager::BandInfoType;
+  using ShapeType = nsFloatManager::ShapeType;
 
   // Block reflow input flags.
   struct Flags {
     Flags()
       : mHasUnconstrainedBSize(false)
       , mIsBStartMarginRoot(false)
       , mIsBEndMarginRoot(false)
       , mShouldApplyBStartMargin(false)
@@ -116,20 +118,24 @@ public:
    * our coordinate system, which is the content box, with (0, 0) in the
    * upper left.
    *
    * Returns whether there are floats present at the given block-direction
    * coordinate and within the inline size of the content rect.
    */
   nsFlowAreaRect GetFloatAvailableSpace() const
     { return GetFloatAvailableSpace(mBCoord); }
+  nsFlowAreaRect GetFloatAvailableSpaceForPlacingFloat(nscoord aBCoord) const
+    { return GetFloatAvailableSpaceWithState(
+        aBCoord, ShapeType::Margin, nullptr); }
   nsFlowAreaRect GetFloatAvailableSpace(nscoord aBCoord) const
-    { return GetFloatAvailableSpaceWithState(aBCoord, nullptr); }
+    { return GetFloatAvailableSpaceWithState(
+        aBCoord, ShapeType::ShapeOutside, nullptr); }
   nsFlowAreaRect
-    GetFloatAvailableSpaceWithState(nscoord aBCoord,
+    GetFloatAvailableSpaceWithState(nscoord aBCoord, ShapeType aShapeType,
                                     nsFloatManager::SavedState *aState) const;
   nsFlowAreaRect
     GetFloatAvailableSpaceForBSize(nscoord aBCoord, nscoord aBSize,
                                    nsFloatManager::SavedState *aState) const;
 
   /*
    * The following functions all return true if they were able to
    * place the float, false if the float did not fit in available
--- a/layout/generic/ReflowInput.h
+++ b/layout/generic/ReflowInput.h
@@ -172,75 +172,75 @@ public:
   }
 
   SizeComputationInput(nsIFrame *aFrame, nsRenderingContext *aRenderingContext,
                    mozilla::WritingMode aContainingBlockWritingMode,
                    nscoord aContainingBlockISize);
 
   struct ReflowInputFlags {
     ReflowInputFlags() { memset(this, 0, sizeof(*this)); }
-    uint32_t mSpecialBSizeReflow:1;  // used by tables to communicate special reflow (in process) to handle
+    bool mSpecialBSizeReflow : 1;    // used by tables to communicate special reflow (in process) to handle
                                      // percent bsize frames inside cells which may not have computed bsizes
-    uint32_t mNextInFlowUntouched:1; // nothing in the frame's next-in-flow (or its descendants)
+    bool mNextInFlowUntouched : 1;   // nothing in the frame's next-in-flow (or its descendants)
                                      // is changing
-    uint32_t mIsTopOfPage:1;         // Is the current context at the top of a
+    bool mIsTopOfPage : 1;           // Is the current context at the top of a
                                      // page?  When true, we force something
                                      // that's too tall for a page/column to
                                      // fit anyway to avoid infinite loops.
-    uint32_t mAssumingHScrollbar:1;  // parent frame is an nsIScrollableFrame and it
+    bool mAssumingHScrollbar : 1;    // parent frame is an nsIScrollableFrame and it
                                      // is assuming a horizontal scrollbar
-    uint32_t mAssumingVScrollbar:1;  // parent frame is an nsIScrollableFrame and it
+    bool mAssumingVScrollbar : 1;    // parent frame is an nsIScrollableFrame and it
                                      // is assuming a vertical scrollbar
 
-    uint32_t mIsIResize:1;           // Is frame (a) not dirty and (b) a
+    bool mIsIResize : 1;             // Is frame (a) not dirty and (b) a
                                      // different inline-size than before?
 
-    uint32_t mIsBResize:1;           // Is frame (a) not dirty and (b) a
+    bool mIsBResize : 1;             // Is frame (a) not dirty and (b) a
                                      // different block-size than before or
                                      // (potentially) in a context where
                                      // percent block-sizes have a different
                                      // basis?
-    uint32_t mTableIsSplittable:1;   // tables are splittable, this should happen only inside a page
+    bool mTableIsSplittable : 1;     // tables are splittable, this should happen only inside a page
                                      // and never insider a column frame
-    uint32_t mHeightDependsOnAncestorCell:1;   // Does frame height depend on
+    bool mHeightDependsOnAncestorCell : 1;     // Does frame height depend on
                                                // an ancestor table-cell?
-    uint32_t mIsColumnBalancing:1;   // nsColumnSetFrame is balancing columns
-    uint32_t mIsFlexContainerMeasuringHeight:1; // nsFlexContainerFrame is
+    bool mIsColumnBalancing : 1;     // nsColumnSetFrame is balancing columns
+    bool mIsFlexContainerMeasuringHeight : 1;   // nsFlexContainerFrame is
                                                 // reflowing this child to
                                                 // measure its intrinsic height.
-    uint32_t mDummyParentReflowInput:1; // a "fake" reflow state made
+    bool mDummyParentReflowInput : 1;   // a "fake" reflow state made
                                         // in order to be the parent
                                         // of a real one
-    uint32_t mMustReflowPlaceholders:1; // Should this frame reflow its place-
+    bool mMustReflowPlaceholders : 1;   // Should this frame reflow its place-
                                         // holder children? If the available
                                         // height of this frame didn't change,
                                         // but its in a paginated environment
                                         // (e.g. columns), it should always
                                         // reflow its placeholder children.
-    uint32_t mShrinkWrap:1; // stores the COMPUTE_SIZE_SHRINK_WRAP ctor flag
-    uint32_t mUseAutoBSize:1; // stores the COMPUTE_SIZE_USE_AUTO_BSIZE ctor flag
-    uint32_t mStaticPosIsCBOrigin:1; // the STATIC_POS_IS_CB_ORIGIN ctor flag
-    uint32_t mIClampMarginBoxMinSize:1; // the I_CLAMP_MARGIN_BOX_MIN_SIZE ctor flag
-    uint32_t mBClampMarginBoxMinSize:1; // the B_CLAMP_MARGIN_BOX_MIN_SIZE ctor flag
+    bool mShrinkWrap : 1; // stores the COMPUTE_SIZE_SHRINK_WRAP ctor flag
+    bool mUseAutoBSize : 1; // stores the COMPUTE_SIZE_USE_AUTO_BSIZE ctor flag
+    bool mStaticPosIsCBOrigin : 1; // the STATIC_POS_IS_CB_ORIGIN ctor flag
+    bool mIClampMarginBoxMinSize : 1; // the I_CLAMP_MARGIN_BOX_MIN_SIZE ctor flag
+    bool mBClampMarginBoxMinSize : 1; // the B_CLAMP_MARGIN_BOX_MIN_SIZE ctor flag
 
     // If set, the following two flags indicate that:
     // (1) this frame is absolutely-positioned (or fixed-positioned).
     // (2) this frame's static position depends on the CSS Box Alignment.
     // (3) we do need to compute the static position, because the frame's
     //     {Inline and/or Block} offsets actually depend on it.
     // When these bits are set, the offset values (IStart/IEnd, BStart/BEnd)
     // represent the "start" edge of the frame's CSS Box Alignment container
     // area, in that axis -- and these offsets need to be further-resolved
     // (with CSS Box Alignment) after we know the OOF frame's size.
     // NOTE: The "I" and "B" (for "Inline" and "Block") refer the axes of the
     // *containing block's writing-mode*, NOT mFrame's own writing-mode. This
     // is purely for convenience, since that's the writing-mode we're dealing
     // with when we set & react to these bits.
-    uint32_t mIOffsetsNeedCSSAlign:1;
-    uint32_t mBOffsetsNeedCSSAlign:1;
+    bool mIOffsetsNeedCSSAlign : 1;
+    bool mBOffsetsNeedCSSAlign : 1;
   };
 
 #ifdef DEBUG
   // Reflow trace methods.  Defined in nsFrame.cpp so they have access
   // to the display-reflow infrastructure.
   static void* DisplayInitOffsetsEnter(
                                      nsIFrame* aFrame,
                                      SizeComputationInput* aState,
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -64,16 +64,17 @@
 static const int MIN_LINES_NEEDING_CURSOR = 20;
 
 static const char16_t kDiscCharacter = 0x2022;
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 using namespace mozilla::layout;
+using ShapeType = nsFloatManager::ShapeType;
 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
 
 static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
 {
   nsLineList::iterator line = aBlock->LinesBegin();
   nsLineList::iterator endLine = aBlock->LinesEnd();
   while (line != endLine) {
     if (line->IsBlock()) {
@@ -3472,16 +3473,17 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl
           advanced = true;
         }
         // ClearFloats might be able to advance us further once we're there.
         aState.mBCoord =
           aState.ClearFloats(newBCoord, StyleClear::None, replacedBlock);
         // Start over with a new available space rect at the new height.
         floatAvailableSpace =
           aState.GetFloatAvailableSpaceWithState(aState.mBCoord,
+                                                 ShapeType::ShapeOutside,
                                                  &floatManagerState);
       }
 
       LogicalRect oldAvailSpace(availSpace);
       aState.ComputeBlockAvailSpace(frame, floatAvailableSpace,
                                     replacedBlock != nullptr, availSpace);
 
       if (!advanced && availSpace.IsEqualEdges(oldAvailSpace)) {
@@ -7098,17 +7100,17 @@ nsBlockFrame::ReflowBullet(nsIFrame* aBu
   aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowInput, status);
 
   // Get the float available space using our saved state from before we
   // started reflowing the block, so that we ignore any floats inside
   // the block.
   // FIXME: aLineTop isn't actually set correctly by some callers, since
   // they reposition the line.
   LogicalRect floatAvailSpace =
-    aState.GetFloatAvailableSpaceWithState(aLineTop,
+    aState.GetFloatAvailableSpaceWithState(aLineTop, ShapeType::ShapeOutside,
                                            &aState.mFloatManagerStateBefore)
           .mRect;
   // FIXME (bug 25888): need to check the entire region that the first
   // line overlaps, not just the top pixel.
 
   // Place the bullet now.  We want to place the bullet relative to the
   // border-box of the associated block (using the right/left margin of
   // the bullet frame as separation).  However, if a line box would be
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -116,27 +116,27 @@ void nsFloatManager::Shutdown()
   sCachedFloatManagerCount = -1;
 }
 
 #define CHECK_BLOCK_DIR(aWM) \
   NS_ASSERTION((aWM).GetBlockDir() == mWritingMode.GetBlockDir(), \
   "incompatible writing modes")
 
 nsFlowAreaRect
-nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBOffset,
-                            BandInfoType aInfoType, nscoord aBSize,
+nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBCoord, nscoord aBSize,
+                            BandInfoType aBandInfoType, ShapeType aShapeType,
                             LogicalRect aContentArea, SavedState* aState,
                             const nsSize& aContainerSize) const
 {
   CHECK_BLOCK_DIR(aWM);
   NS_ASSERTION(aBSize >= 0, "unexpected max block size");
   NS_ASSERTION(aContentArea.ISize(aWM) >= 0,
                "unexpected content area inline size");
 
-  nscoord blockStart = aBOffset + mBlockStart;
+  nscoord blockStart = aBCoord + mBlockStart;
   if (blockStart < nscoord_MIN) {
     NS_WARNING("bad value");
     blockStart = nscoord_MIN;
   }
 
   // Determine the last float that we should consider.
   uint32_t floatCount;
   if (aState) {
@@ -148,25 +148,25 @@ nsFloatManager::GetFlowArea(WritingMode 
     floatCount = mFloats.Length();
   }
 
   // If there are no floats at all, or we're below the last one, return
   // quickly.
   if (floatCount == 0 ||
       (mFloats[floatCount-1].mLeftBEnd <= blockStart &&
        mFloats[floatCount-1].mRightBEnd <= blockStart)) {
-    return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBOffset,
+    return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBCoord,
                           aContentArea.ISize(aWM), aBSize, false);
   }
 
   nscoord blockEnd;
   if (aBSize == nscoord_MAX) {
     // This warning (and the two below) are possible to hit on pages
     // with really large objects.
-    NS_WARNING_ASSERTION(aInfoType == BAND_FROM_POINT, "bad height");
+    NS_WARNING_ASSERTION(aBandInfoType == BandInfoType::BandFromPoint, "bad height");
     blockEnd = nscoord_MAX;
   } else {
     blockEnd = blockStart + aBSize;
     if (blockEnd < blockStart || blockEnd > nscoord_MAX) {
       NS_WARNING("bad value");
       blockEnd = nscoord_MAX;
     }
   }
@@ -188,61 +188,68 @@ nsFloatManager::GetFlowArea(WritingMode 
     }
     if (fi.IsEmpty()) {
       // For compatibility, ignore floats with empty rects, even though it
       // disagrees with the spec.  (We might want to fix this in the
       // future, though.)
       continue;
     }
 
-    nscoord floatBStart = fi.BStart();
-    nscoord floatBEnd = fi.BEnd();
-    if (blockStart < floatBStart && aInfoType == BAND_FROM_POINT) {
+    nscoord floatBStart = fi.BStart(aShapeType);
+    nscoord floatBEnd = fi.BEnd(aShapeType);
+    if (blockStart < floatBStart && aBandInfoType == BandInfoType::BandFromPoint) {
       // This float is below our band.  Shrink our band's height if needed.
       if (floatBStart < blockEnd) {
         blockEnd = floatBStart;
       }
     }
-    // If blockStart == blockEnd (which happens only with WIDTH_WITHIN_HEIGHT),
+    // If blockStart == blockEnd (which happens only with WidthWithinHeight),
     // we include floats that begin at our 0-height vertical area.  We
-    // need to to this to satisfy the invariant that a
-    // WIDTH_WITHIN_HEIGHT call is at least as narrow on both sides as a
-    // BAND_WITHIN_POINT call beginning at its blockStart.
+    // need to do this to satisfy the invariant that a
+    // WidthWithinHeight call is at least as narrow on both sides as a
+    // BandFromPoint call beginning at its blockStart.
     else if (blockStart < floatBEnd &&
              (floatBStart < blockEnd ||
               (floatBStart == blockEnd && blockStart == blockEnd))) {
       // This float is in our band.
 
-      // Shrink our band's height if needed.
-      if (floatBEnd < blockEnd && aInfoType == BAND_FROM_POINT) {
-        blockEnd = floatBEnd;
-      }
-
       // Shrink our band's width if needed.
       StyleFloat floatStyle = fi.mFrame->StyleDisplay()->PhysicalFloats(aWM);
+
+      // When aBandInfoType is BandFromPoint, we're only intended to
+      // consider a point along the y axis rather than a band.
+      const nscoord bandBlockEnd =
+        aBandInfoType == BandInfoType::BandFromPoint ? blockStart : blockEnd;
       if (floatStyle == StyleFloat::Left) {
         // A left float
-        nscoord lineRightEdge = fi.LineRight();
+        nscoord lineRightEdge =
+          fi.LineRight(aShapeType, blockStart, bandBlockEnd);
         if (lineRightEdge > lineLeft) {
           lineLeft = lineRightEdge;
           // Only set haveFloats to true if the float is inside our
           // containing block.  This matches the spec for what some
           // callers want and disagrees for other callers, so we should
           // probably provide better information at some point.
           haveFloats = true;
         }
       } else {
         // A right float
-        nscoord lineLeftEdge = fi.LineLeft();
+        nscoord lineLeftEdge =
+          fi.LineLeft(aShapeType, blockStart, bandBlockEnd);
         if (lineLeftEdge < lineRight) {
           lineRight = lineLeftEdge;
           // See above.
           haveFloats = true;
         }
       }
+
+      // Shrink our band's height if needed.
+      if (floatBEnd < blockEnd && aBandInfoType == BandInfoType::BandFromPoint) {
+        blockEnd = floatBEnd;
+      }
     }
   }
 
   nscoord blockSize = (blockEnd == nscoord_MAX) ?
                        nscoord_MAX : (blockEnd - blockStart);
   // convert back from LineLeft/Right to IStart
   nscoord inlineStart = aWM.IsBidiLTR()
                         ? lineLeft - mLineLeft
@@ -256,21 +263,18 @@ nsFloatManager::GetFlowArea(WritingMode 
 nsresult
 nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect,
                          WritingMode aWM, const nsSize& aContainerSize)
 {
   CHECK_BLOCK_DIR(aWM);
   NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!");
   NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!");
 
-  FloatInfo info(aFloatFrame,
-                 aMarginRect.LineLeft(aWM, aContainerSize) + mLineLeft,
-                 aMarginRect.BStart(aWM) + mBlockStart,
-                 aMarginRect.ISize(aWM),
-                 aMarginRect.BSize(aWM));
+  FloatInfo info(aFloatFrame, mLineLeft, mBlockStart, aMarginRect, aWM,
+                 aContainerSize);
 
   // Set mLeftBEnd and mRightBEnd.
   if (HasAnyFloats()) {
     FloatInfo &tail = mFloats[mFloats.Length() - 1];
     info.mLeftBEnd = tail.mLeftBEnd;
     info.mRightBEnd = tail.mRightBEnd;
   } else {
     info.mLeftBEnd = nscoord_MIN;
@@ -524,40 +528,226 @@ nsFloatManager::ClearContinues(StyleClea
           (aBreakType == StyleClear::Both ||
            aBreakType == StyleClear::Right));
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // FloatInfo
 
 nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame,
-                                     nscoord aLineLeft, nscoord aBStart,
-                                     nscoord aISize, nscoord aBSize)
+                                     nscoord aLineLeft, nscoord aBlockStart,
+                                     const LogicalRect& aMarginRect,
+                                     WritingMode aWM,
+                                     const nsSize& aContainerSize)
   : mFrame(aFrame)
-  , mRect(aLineLeft, aBStart, aISize, aBSize)
+  , mRect(aMarginRect.LineLeft(aWM, aContainerSize) + aLineLeft,
+          aMarginRect.BStart(aWM) + aBlockStart,
+          aMarginRect.ISize(aWM),
+          aMarginRect.BSize(aWM))
 {
   MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
+
+  const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside;
+
+  if (shapeOutside.GetType() == StyleShapeSourceType::Box) {
+    // Initialize shape-box reference rect.
+    LogicalRect rect = aMarginRect;
+
+    switch (shapeOutside.GetReferenceBox()) {
+      case StyleShapeOutsideShapeBox::Content:
+        rect.Deflate(aWM, mFrame->GetLogicalUsedPadding(aWM));
+        MOZ_FALLTHROUGH;
+      case StyleShapeOutsideShapeBox::Padding:
+        rect.Deflate(aWM, mFrame->GetLogicalUsedBorder(aWM));
+        MOZ_FALLTHROUGH;
+      case StyleShapeOutsideShapeBox::Border:
+        rect.Deflate(aWM, mFrame->GetLogicalUsedMargin(aWM));
+        break;
+      case StyleShapeOutsideShapeBox::Margin:
+        // Do nothing. rect is already a margin rect.
+        break;
+      case StyleShapeOutsideShapeBox::NoBox:
+        MOZ_ASSERT_UNREACHABLE("Why don't we have a shape-box?");
+        break;
+    }
+
+    mShapeBoxRect.emplace(rect.LineLeft(aWM, aContainerSize) + aLineLeft,
+                          rect.BStart(aWM) + aBlockStart,
+                          rect.ISize(aWM), rect.BSize(aWM));
+  }
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsFloatManager::FloatInfo::FloatInfo(const FloatInfo& aOther)
-  : mFrame(aOther.mFrame),
-    mLeftBEnd(aOther.mLeftBEnd),
-    mRightBEnd(aOther.mRightBEnd),
-    mRect(aOther.mRect)
+  : mFrame(aOther.mFrame)
+  , mLeftBEnd(aOther.mLeftBEnd)
+  , mRightBEnd(aOther.mRightBEnd)
+  , mRect(aOther.mRect)
+  , mShapeBoxRect(aOther.mShapeBoxRect)
 {
   MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
 }
 
 nsFloatManager::FloatInfo::~FloatInfo()
 {
   MOZ_COUNT_DTOR(nsFloatManager::FloatInfo);
 }
 #endif
 
+nscoord
+nsFloatManager::FloatInfo::LineLeft(ShapeType aShapeType,
+                                    const nscoord aBStart,
+                                    const nscoord aBEnd) const
+{
+  if (aShapeType == ShapeType::Margin) {
+    return LineLeft();
+  }
+
+  MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
+  const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside;
+  if (shapeOutside.GetType() == StyleShapeSourceType::None) {
+    return LineLeft();
+  }
+
+  if (shapeOutside.GetType() == StyleShapeSourceType::Box) {
+    nscoord radii[8];
+    bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii);
+
+    if (!hasRadii) {
+      return ShapeBoxRect().x;
+    }
+    // Bug 1316549: Fix non-ltr direction and writing-mode.
+    nscoord lineLeftDiff =
+      ComputeEllipseXInterceptDiff(
+        ShapeBoxRect().y, ShapeBoxRect().YMost(),
+        radii[NS_CORNER_TOP_LEFT_X], radii[NS_CORNER_TOP_LEFT_Y],
+        radii[NS_CORNER_BOTTOM_LEFT_X], radii[NS_CORNER_BOTTOM_LEFT_Y],
+        aBStart, aBEnd);
+    return ShapeBoxRect().x + lineLeftDiff;
+  }
+
+  // XXX: Other shape source types are not implemented yet.
+
+  return LineLeft();
+}
+
+nscoord
+nsFloatManager::FloatInfo::LineRight(ShapeType aShapeType,
+                                     const nscoord aBStart,
+                                     const nscoord aBEnd) const
+{
+  if (aShapeType == ShapeType::Margin) {
+    return LineRight();
+  }
+
+  MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
+  const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside;
+  if (shapeOutside.GetType() == StyleShapeSourceType::None) {
+    return LineRight();
+  }
+
+  if (shapeOutside.GetType() == StyleShapeSourceType::Box) {
+    nscoord radii[8];
+    bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii);
+
+    if (!hasRadii) {
+      return ShapeBoxRect().XMost();
+    }
+    // Bug 1316549: Fix non-ltr direction and writing-mode.
+    nscoord lineRightDiff =
+      ComputeEllipseXInterceptDiff(
+        ShapeBoxRect().y, ShapeBoxRect().YMost(),
+        radii[NS_CORNER_TOP_RIGHT_X], radii[NS_CORNER_TOP_RIGHT_Y],
+        radii[NS_CORNER_BOTTOM_RIGHT_X], radii[NS_CORNER_BOTTOM_RIGHT_Y],
+        aBStart, aBEnd);
+    return ShapeBoxRect().XMost() - lineRightDiff;
+  }
+
+  // XXX: Other shape source types are not implemented yet.
+
+  return LineRight();
+}
+
+/* static */ nscoord
+nsFloatManager::FloatInfo::ComputeEllipseXInterceptDiff(
+  const nscoord aShapeBoxY, const nscoord aShapeBoxYMost,
+  const nscoord aTopCornerRadiusX, const nscoord aTopCornerRadiusY,
+  const nscoord aBottomCornerRadiusX, const nscoord aBottomCornerRadiusY,
+  const nscoord aBandY, const nscoord aBandYMost)
+{
+  // An Example for the band intersects with the top right corner of an ellipse.
+  //
+  //                                xIntercept xDiff
+  //                                    |       |
+  //  +---------------------------------|-------|-+---- aShapeBoxY
+  //  |                ##########^      |       | |
+  //  |            ##############|####  |       | |
+  //  +---------#################|######|-------|-+---- aBandY
+  //  |       ###################|######|##     | |
+  //  |      # aTopCornerRadiusY |######|###    | |
+  //  |    ######################|######|#####  | |
+  //  +---#######################|<-----------><->^---- aBandYMost
+  //  |  ########################|##############  |
+  //  |  ########################|##############  |---- y
+  //  | #########################|############### |
+  //  | ######################## v<-------------->v
+  //  |######################### aTopCornerRadiusX|
+  //  |###########################################|
+  //  |###########################################|
+  //  |###########################################|
+  //  |###########################################|
+  //  | ######################################### |
+  //  | ######################################### |
+  //  |  #######################################  |
+  //  |  #######################################  |
+  //  |   #####################################   |
+  //  |    ###################################    |
+  //  |      ###############################      |
+  //  |       #############################       |
+  //  |         #########################         |
+  //  |            ###################            |
+  //  |                ###########                |
+  //  +-------------------------------------------+----- aShapeBoxYMost
+
+  NS_ASSERTION(aShapeBoxY <= aShapeBoxYMost, "Bad shape box coordinates!");
+  NS_ASSERTION(aBandY <= aBandYMost, "Bad band coordinates!");
+
+  nscoord xDiff = 0;
+
+  // If the band intersects both the top and bottom corners, we don't need
+  // to enter either branch because the correct xDiff is 0.
+  if (aBandYMost >= aShapeBoxY &&
+      aBandYMost <= aShapeBoxY + aTopCornerRadiusY) {
+    // The band intersects only the top corner.
+    nscoord y = aTopCornerRadiusY - (aBandYMost - aShapeBoxY);
+    nscoord xIntercept =
+      XInterceptAtY(y, aTopCornerRadiusX, aTopCornerRadiusY);
+    xDiff = aTopCornerRadiusX - xIntercept;
+  } else if (aBandY >= aShapeBoxYMost - aBottomCornerRadiusY &&
+             aBandY <= aShapeBoxYMost) {
+    // The band intersects only the bottom corner.
+    nscoord y = aBottomCornerRadiusY - (aShapeBoxYMost - aBandY);
+    nscoord xIntercept =
+      XInterceptAtY(y, aBottomCornerRadiusX, aBottomCornerRadiusY);
+    xDiff = aBottomCornerRadiusX - xIntercept;
+  }
+
+  return xDiff;
+}
+
+/* static */ nscoord
+nsFloatManager::FloatInfo::XInterceptAtY(const nscoord aY,
+                                         const nscoord aRadiusX,
+                                         const nscoord aRadiusY)
+{
+  // Solve for x in the ellipse equation (x/radiusX)^2 + (y/radiusY)^2 = 1.
+  MOZ_ASSERT(aRadiusY > 0);
+  return aRadiusX * std::sqrt(1 - (aY * aY) / double(aRadiusY * aRadiusY));
+}
+
 //----------------------------------------------------------------------
 
 nsAutoFloatManager::~nsAutoFloatManager()
 {
   // Restore the old float manager in the reflow input if necessary.
   if (mNew) {
 #ifdef DEBUG
     if (nsBlockFrame::gNoisyFloatManager) {
--- a/layout/generic/nsFloatManager.h
+++ b/layout/generic/nsFloatManager.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* class that manages rules for positioning floats */
 
 #ifndef nsFloatManager_h_
 #define nsFloatManager_h_
 
 #include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/WritingModes.h"
 #include "nsCoord.h"
 #include "nsFrameList.h" // for DEBUG_FRAME_DUMP
 #include "nsIntervalSet.h"
 #include "nsTArray.h"
 
 class nsIPresShell;
 class nsIFrame;
@@ -119,55 +120,61 @@ public:
   {
     aLineLeft = mLineLeft;
     aBlockStart = mBlockStart;
   }
 
   /**
    * Get information about the area available to content that flows
    * around floats.  Two different types of space can be requested:
-   *   BAND_FROM_POINT: returns the band containing block-dir coordinate
+   *   BandFromPoint: returns the band containing block-dir coordinate
    *     |aBCoord| (though actually with the top truncated to begin at
    *     aBCoord), but up to at most |aBSize| (which may be nscoord_MAX).
    *     This will return the tallest rectangle whose block start is
    *     |aBCoord| and in which there are no changes in what floats are
    *     on the sides of that rectangle, but will limit the block size
    *     of the rectangle to |aBSize|.  The inline start and end edges
    *     of the rectangle give the area available for line boxes in that
    *     space. The inline size of this resulting rectangle will not be
    *     negative.
-   *   WIDTH_WITHIN_HEIGHT: This returns a rectangle whose block start
+   *   WidthWithinHeight: This returns a rectangle whose block start
    *     is aBCoord and whose block size is exactly aBSize.  Its inline
    *     start and end edges give the corresponding edges of the space
    *     that can be used for line boxes *throughout* that space.  (It
    *     is possible that more inline space could be used in part of the
    *     space if a float begins or ends in it.)  The inline size of the
    *     resulting rectangle can be negative.
    *
-   * @param aBCoord [in] block-dir coordinate for block start of
-   *           available space desired
+   * ShapeType can be used to request two different types of flow areas.
+   * (This is the float area defined in CSS Shapes Module Level 1 §1.4):
+   *    Margin: uses the float element's margin-box to request the flow area.
+   *    ShapeOutside: uses the float element's shape-outside value to request
+   *      the float area.
+   *
+   * @param aBCoord [in] block-dir coordinate for block start of available space
+   *          desired, which are positioned relative to the current translation.
    * @param aBSize [in] see above
    * @param aContentArea [in] an nsRect representing the content area
    * @param aState [in] If null, use the current state, otherwise, do
    *                    computation based only on floats present in the given
    *                    saved state.
    * @return An nsFlowAreaRect whose:
    *           mRect is the resulting rectangle for line boxes.  It will not
    *             extend beyond aContentArea's inline bounds, but may be
    *             narrower when floats are present.
-   *          mBandHasFloats is whether there are floats at the sides of the
-   *            return value including those that do not reduce the line box
-   *            inline size at all (because they are entirely in the margins)
-   *
-   * aBCoord and aAvailSpace are positioned relative to the current translation
+   *           mHasFloats is whether there are floats at the sides of the
+   *             return value including those that do not reduce the line box
+   *             inline size at all (because they are entirely in the margins)
    */
-  enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT };
+  enum class BandInfoType { BandFromPoint, WidthWithinHeight };
+  enum class ShapeType { Margin, ShapeOutside };
   nsFlowAreaRect GetFlowArea(mozilla::WritingMode aWM,
-                             nscoord aBCoord, BandInfoType aInfoType,
-                             nscoord aBSize, mozilla::LogicalRect aContentArea,
+                             nscoord aBCoord, nscoord aBSize,
+                             BandInfoType aBandInfoType, ShapeType aShapeType,
+                             mozilla::LogicalRect aContentArea,
                              SavedState* aState,
                              const nsSize& aContainerSize) const;
 
   /**
    * Add a float that comes after all floats previously added.  Its
    * block start must be even with or below the top of all previous
    * floats.
    *
@@ -306,39 +313,79 @@ public:
 private:
 
   struct FloatInfo {
     nsIFrame *const mFrame;
     // The lowest block-ends of left/right floats up to and including
     // this one.
     nscoord mLeftBEnd, mRightBEnd;
 
-    FloatInfo(nsIFrame* aFrame, nscoord aLineLeft, nscoord aBStart,
-              nscoord aISize, nscoord aBSize);
+    FloatInfo(nsIFrame* aFrame, nscoord aLineLeft, nscoord aBlockStart,
+              const mozilla::LogicalRect& aMarginRect,
+              mozilla::WritingMode aWM, const nsSize& aContainerSize);
 
     nscoord LineLeft() const { return mRect.x; }
     nscoord LineRight() const { return mRect.XMost(); }
     nscoord ISize() const { return mRect.width; }
     nscoord BStart() const { return mRect.y; }
     nscoord BEnd() const { return mRect.YMost(); }
     nscoord BSize() const { return mRect.height; }
     bool IsEmpty() const { return mRect.IsEmpty(); }
 
+    nsRect ShapeBoxRect() const { return mShapeBoxRect.valueOr(mRect); }
+
+    // aBStart and aBEnd are the starting and ending coordinate of a band.
+    // LineLeft() and LineRight() return the innermost line-left extent and
+    // line-right extent within the given band, respectively.
+    nscoord LineLeft(ShapeType aShapeType, const nscoord aBStart,
+                     const nscoord aBEnd) const;
+    nscoord LineRight(ShapeType aShapeType, const nscoord aBStart,
+                     const nscoord aBEnd) const;
+
+    nscoord BStart(ShapeType aShapeType) const
+    {
+      return aShapeType == ShapeType::Margin ? BStart() : ShapeBoxRect().y;
+    }
+    nscoord BEnd(ShapeType aShapeType) const
+    {
+      return aShapeType == ShapeType::Margin ? BEnd() : ShapeBoxRect().YMost();
+    }
+
+    // Compute the minimum x-axis difference between the bounding shape box
+    // and its rounded corner within the given band (y-axis region). This is
+    // used as a helper function to compute the LineRight() and LineLeft().
+    // See the picture in the implementation for an example.
+    //
+    // Returns the x-axis diff, or 0 if there's no rounded corner within
+    // the given band.
+    static nscoord ComputeEllipseXInterceptDiff(
+      const nscoord aShapeBoxY, const nscoord aShapeBoxYMost,
+      const nscoord aTopCornerRadiusX, const nscoord aTopCornerRadiusY,
+      const nscoord aBottomCornerRadiusX, const nscoord aBottomCornerRadiusY,
+      const nscoord aBandY, const nscoord aBandYMost);
+
+    static nscoord XInterceptAtY(const nscoord aY, const nscoord aRadiusX,
+                                 const nscoord aRadiusY);
+
 #ifdef NS_BUILD_REFCNT_LOGGING
     FloatInfo(const FloatInfo& aOther);
     ~FloatInfo();
 #endif
 
     // NB! This is really a logical rect in a writing mode suitable for
     // placing floats, which is not necessarily the actual writing mode
     // either of the block which created the frame manager or the block
     // that is calling the frame manager. The inline coordinates are in
     // the line-relative axis of the frame manager and its block
     // coordinates are in the frame manager's real block direction.
     nsRect mRect;
+    // This is the reference box of css shape-outside if specified, which
+    // implements the <shape-box> value in the CSS Shapes Module Level 1.
+    // The coordinate setup is the same as mRect.
+    mozilla::Maybe<nsRect> mShapeBoxRect;
   };
 
 #ifdef DEBUG
   mozilla::WritingMode mWritingMode;
 #endif
 
   // Translation from local to global coordinate space.
   nscoord mLineLeft, mBlockStart;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1330,24 +1330,41 @@ nsIFrame::InsetBorderRadii(nscoord aRadi
     aRadii[hc1] = std::max(0, aRadii[hc1] - offset);
     aRadii[hc2] = std::max(0, aRadii[hc2] - offset);
   }
 }
 
 /* static */ void
 nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets)
 {
+  auto AdjustOffset = [] (const uint32_t aRadius, const nscoord aOffset) {
+    // Implement the cubic formula to adjust offset when aOffset > 0 and
+    // aRadius / aOffset < 1.
+    // https://drafts.csswg.org/css-shapes/#valdef-shape-box-margin-box
+    if (aOffset > 0) {
+      const double ratio = aRadius / double(aOffset);
+      if (ratio < 1.0) {
+        return nscoord(aOffset * (1.0 + std::pow(ratio - 1, 3)));
+      }
+    }
+    return aOffset;
+  };
+
   NS_FOR_CSS_SIDES(side) {
-    nscoord offset = aOffsets.Side(side);
-    uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false);
-    uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false);
-    if (aRadii[hc1] > 0)
-      aRadii[hc1] += offset;
-    if (aRadii[hc2] > 0)
-      aRadii[hc2] += offset;
+    const nscoord offset = aOffsets.Side(side);
+    const uint32_t hc1 = NS_SIDE_TO_HALF_CORNER(side, false, false);
+    const uint32_t hc2 = NS_SIDE_TO_HALF_CORNER(side, true, false);
+    if (aRadii[hc1] > 0) {
+      const nscoord offset1 = AdjustOffset(aRadii[hc1], offset);
+      aRadii[hc1] = std::max(0, aRadii[hc1] + offset1);
+    }
+    if (aRadii[hc2] > 0) {
+      const nscoord offset2 = AdjustOffset(aRadii[hc2], offset);
+      aRadii[hc2] = std::max(0, aRadii[hc2] + offset2);
+    }
   }
 }
 
 /* virtual */ bool
 nsIFrame::GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea,
                          Sides aSkipSides, nscoord aRadii[8]) const
 {
   if (IsThemed()) {
@@ -1371,16 +1388,31 @@ nsIFrame::GetBorderRadii(const nsSize& a
 bool
 nsIFrame::GetBorderRadii(nscoord aRadii[8]) const
 {
   nsSize sz = GetSize();
   return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
 }
 
 bool
+nsIFrame::GetMarginBoxBorderRadii(nscoord aRadii[8]) const
+{
+  if (!GetBorderRadii(aRadii)) {
+    return false;
+  }
+  OutsetBorderRadii(aRadii, GetUsedMargin());
+  NS_FOR_CSS_HALF_CORNERS(corner) {
+    if (aRadii[corner]) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool
 nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const
 {
   if (!GetBorderRadii(aRadii))
     return false;
   InsetBorderRadii(aRadii, GetUsedBorder());
   NS_FOR_CSS_HALF_CORNERS(corner) {
     if (aRadii[corner])
       return true;
@@ -1396,16 +1428,34 @@ nsIFrame::GetContentBoxBorderRadii(nscoo
   InsetBorderRadii(aRadii, GetUsedBorderAndPadding());
   NS_FOR_CSS_HALF_CORNERS(corner) {
     if (aRadii[corner])
       return true;
   }
   return false;
 }
 
+bool
+nsIFrame::GetShapeBoxBorderRadii(nscoord aRadii[8]) const
+{
+  switch (StyleDisplay()->mShapeOutside.GetReferenceBox()) {
+    case StyleShapeOutsideShapeBox::NoBox:
+      return false;
+    case StyleShapeOutsideShapeBox::Content:
+      return GetContentBoxBorderRadii(aRadii);
+    case StyleShapeOutsideShapeBox::Padding:
+      return GetPaddingBoxBorderRadii(aRadii);
+    case StyleShapeOutsideShapeBox::Border:
+      return GetBorderRadii(aRadii);
+    case StyleShapeOutsideShapeBox::Margin:
+      return GetMarginBoxBorderRadii(aRadii);
+  }
+  return false;
+}
+
 nsStyleContext*
 nsFrame::GetAdditionalStyleContext(int32_t aIndex) const
 {
   NS_PRECONDITION(aIndex >= 0, "invalid index number");
   return nullptr;
 }
 
 void
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1167,25 +1167,30 @@ public:
   static void InsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets);
   static void OutsetBorderRadii(nscoord aRadii[8], const nsMargin &aOffsets);
 
   /**
    * Fill in border radii for this frame.  Return whether any are nonzero.
    * Indices into aRadii are the NS_CORNER_* constants in nsStyleConsts.h
    * aSkipSides is a union of SIDE_BIT_LEFT/RIGHT/TOP/BOTTOM bits that says
    * which side(s) to skip.
+   *
+   * Note: GetMarginBoxBorderRadii() and GetShapeBoxBorderRadii() work only
+   * on frames that establish block formatting contexts since they don't
+   * participate in margin-collapsing.
    */
   virtual bool GetBorderRadii(const nsSize& aFrameSize,
                               const nsSize& aBorderArea,
                               Sides aSkipSides,
                               nscoord aRadii[8]) const;
   bool GetBorderRadii(nscoord aRadii[8]) const;
-
+  bool GetMarginBoxBorderRadii(nscoord aRadii[8]) const;
   bool GetPaddingBoxBorderRadii(nscoord aRadii[8]) const;
   bool GetContentBoxBorderRadii(nscoord aRadii[8]) const;
+  bool GetShapeBoxBorderRadii(nscoord aRadii[8]) const;
 
   /**
    * Get the position of the frame's baseline, relative to the top of
    * the frame (its top border edge).  Only valid when Reflow is not
    * needed.
    */
   virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const = 0;
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1315113-1-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html reftest-zoom="2">
+<meta charset="utf-8">
+<title>Reference for test for bug 1315113: Gradient in border image</title>
+<style>
+
+body {
+  margin: 0;
+}
+
+#box {
+  width: 200px;
+  border: 80px solid transparent;
+  padding: 20px;
+  background: linear-gradient(red, blue);
+  background-origin: border-box;
+}
+
+</style>
+
+<div id="box"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1315113-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html reftest-zoom="2">
+<meta charset="utf-8">
+<title>Test for bug 1315113: Gradient in border image</title>
+<style>
+
+body {
+  margin: 0;
+}
+
+#box {
+  width: 200px;
+  border: 100px solid;
+  border-image-source: linear-gradient(red, blue);
+  border-image-slice: 40% 40% fill;
+  border-image-width: 80px 80px;
+  border-image-repeat: round stretch;
+  background-color: black;
+}
+
+</style>
+
+<div id="box"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1315113-2-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html reftest-zoom="0.5">
+<meta charset="utf-8">
+<title>Reference for test for bug 1315113: Gradient in border image</title>
+<style>
+
+body {
+  margin: 0;
+}
+
+#box {
+  width: 200px;
+  border: 80px solid transparent;
+  padding: 20px;
+  background: linear-gradient(red, blue);
+  background-origin: border-box;
+}
+
+</style>
+
+<div id="box"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1315113-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html reftest-zoom="0.5">
+<meta charset="utf-8">
+<title>Test for bug 1315113: Gradient in border image</title>
+<style>
+
+body {
+  margin: 0;
+}
+
+#box {
+  width: 200px;
+  border: 100px solid;
+  border-image-source: linear-gradient(red, blue);
+  border-image-slice: 40% 40% fill;
+  border-image-width: 80px 80px;
+  border-image-repeat: round stretch;
+  background-color: black;
+}
+
+</style>
+
+<div id="box"></div>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1968,16 +1968,18 @@ random-if(!winWidget) == 1273154-2.html 
 == 1275411-1.html 1275411-1-ref.html
 == 1288255.html 1288255-ref.html
 fuzzy(8,1900) == 1291528.html 1291528-ref.html
 # Buttons in 2 pages have different position and the rendering result can be
 # different, but they should use the same button style and the background color
 # should be same.  |fuzzy()| here allows the difference in border, but not
 # background color.
 fuzzy(255,1000) skip-if(!cocoaWidget) == 1294102-1.html 1294102-1-ref.html
+fuzzy(2,320000) == 1315113-1.html 1315113-1-ref.html
+fuzzy(2,20000) == 1315113-2.html 1315113-2-ref.html
 == 1315632-1.html 1315632-1-ref.html
 fuzzy(2,40000) == 1316719-1a.html 1316719-1-ref.html
 fuzzy(2,40000) == 1316719-1b.html 1316719-1-ref.html
 fuzzy(2,40000) == 1316719-1c.html 1316719-1-ref.html
 
 HTTP == 652991-1a.html 652991-1-ref.html
 HTTP == 652991-1b.html 652991-1-ref.html
 HTTP == 652991-2.html 652991-2-ref.html
--- a/layout/reftests/w3c-css/submitted/reftest.list
+++ b/layout/reftests/w3c-css/submitted/reftest.list
@@ -47,16 +47,19 @@ include masking/reftest.list
 include multicol3/reftest.list
 
 # Ruby Layout Module
 include ruby/reftest.list
 
 # Selectors Level 4
 include selectors4/reftest.list
 
+# Shapes Level 1
+include shapes1/reftest.list
+
 # Text Level 3
 include text3/reftest.list
 
 # Text Decoration Level 3
 include text-decor-3/reftest.list
 
 # Transforms
 include transforms/reftest.list
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/reftest.list
@@ -0,0 +1,25 @@
+default-preferences pref(layout.css.shape-outside.enabled,true)
+
+# <shape-box> only
+== shape-outside-margin-box-001.html shape-outside-margin-box-001-ref.html
+== shape-outside-margin-box-002.html shape-outside-margin-box-002-ref.html
+== shape-outside-border-box-001.html shape-outside-border-box-001-ref.html
+== shape-outside-border-box-002.html shape-outside-border-box-002-ref.html
+== shape-outside-padding-box-001.html shape-outside-padding-box-001-ref.html
+== shape-outside-padding-box-002.html shape-outside-padding-box-002-ref.html
+== shape-outside-content-box-001.html shape-outside-content-box-001-ref.html
+== shape-outside-content-box-002.html shape-outside-content-box-002-ref.html
+
+# <shape-box> with border-radius
+== shape-outside-margin-box-border-radius-001.html shape-outside-margin-box-border-radius-001-ref.html
+fails == shape-outside-margin-box-border-radius-002.html shape-outside-margin-box-border-radius-002-ref.html # Bug 1309830
+== shape-outside-margin-box-border-radius-003.html shape-outside-margin-box-border-radius-003-ref.html
+fails == shape-outside-margin-box-border-radius-004.html shape-outside-margin-box-border-radius-004-ref.html # Bug 1309830
+== shape-outside-border-box-border-radius-001.html shape-outside-border-box-border-radius-001-ref.html
+fails == shape-outside-border-box-border-radius-002.html shape-outside-border-box-border-radius-002-ref.html # Bug 1309830
+== shape-outside-border-box-border-radius-003.html shape-outside-border-box-border-radius-003-ref.html
+fails == shape-outside-border-box-border-radius-004.html shape-outside-border-box-border-radius-004-ref.html # Bug 1309830
+== shape-outside-padding-box-border-radius-001.html shape-outside-padding-box-border-radius-001-ref.html
+== shape-outside-padding-box-border-radius-002.html shape-outside-padding-box-border-radius-002-ref.html
+== shape-outside-content-box-border-radius-001.html shape-outside-content-box-border-radius-001-ref.html
+== shape-outside-content-box-border-radius-002.html shape-outside-content-box-border-radius-002-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-001-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, border-box reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: border-box; */
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin-left: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="longbox"></div>  <!-- Saturate the margin space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div>  <!-- Saturate the margin space -->
+
+    <div class="longbox"></div>  <!-- Saturate the margin space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div>  <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-001.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, border-box</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-border-box-001-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the border-box value.">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: border-box;
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-002-ref.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, border-box reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: border-box; */
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin-right: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="longbox"></div>  <!-- Saturate the margin space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div>  <!-- Saturate the margin space -->
+
+    <div class="longbox"></div>  <!-- Saturate the margin space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div>  <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-002.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, border-box</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-border-box-002-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the border-box value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: border-box;
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-001-ref.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, border-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: border-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 20px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    position: absolute;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox" style="top: 0; left: 0;"></div> <!-- Saturate the margin space -->
+    <div class="box" style="height: 24px; top: 20px; left: 128px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 44px; left: 140px;"></div>
+    <div class="box" style="height: 36px; top: 80px; left: 140px;"></div>
+    <div class="box" style="height: 24px; top: 116px; left: 128px;"></div> <!-- Box at corner -->
+    <div class="longbox" style="top: 140px; left: 0;"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-001.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, border-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-border-box-border-radius-001-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the border-box and border-radius value.">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: border-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 20px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-002-ref.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, border-box, border-radius, no margin reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: border-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 24px; top: 0px; left: 108px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 24px; left: 120px;"></div>
+    <div class="box" style="height: 36px; top: 60px; left: 120px;"></div>
+    <div class="box" style="height: 24px; top: 96px; left: 108px;"></div> <!-- Box at corner -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-002.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, border-box, border-radius, no margin</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-border-box-border-radius-002-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the border-box and border-radius value.">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: border-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    /* No margin. */
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-003-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, border-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: border-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 20px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    position: absolute;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox" style="top: 0; right: 0;"></div> <!-- Saturate the margin space -->
+    <div class="box" style="height: 24px; top: 20px; right: 128px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 44px; right: 140px;"></div>
+    <div class="box" style="height: 36px; top: 80px; right: 140px;"></div>
+    <div class="box" style="height: 24px; top: 116px; right: 128px;"></div> <!-- Box at corner -->
+    <div class="longbox" style="top: 140px; right: 0;"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-003.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, border-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-border-box-border-radius-003-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the border-box and border-radius value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: border-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 20px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-004-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, border-box, border-radius, no margin reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: border-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 24px; top: 0px; right: 108px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 24px; right: 120px;"></div>
+    <div class="box" style="height: 36px; top: 60px; right: 120px;"></div>
+    <div class="box" style="height: 24px; top: 96px; right: 108px;"></div> <!-- Box at corner -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-004.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, border-box, border-radius, no margin</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-border-box-border-radius-004-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the border-box and border-radius value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: border-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    /* No margin. */
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-content-box-001-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, content-box reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    width: 175px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: content-box; */
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding-left: 25px;
+    border-left: 25px solid lightgreen;
+    margin-left: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 50px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 175px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-content-box-001.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, content-box</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-content-box-001-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the content-box value.">
+  <style>
+  .container {
+    width: 175px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: content-box;
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 50px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 175px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-content-box-002-ref.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, content-box reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    width: 175px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: content-box; */
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding-right: 25px;
+    border-right: 25px solid lightgreen;
+    margin-right: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 50px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 175px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-content-box-002.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <title>CSS Shape Test: float right, content-box</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-content-box-002-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the content-box value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 175px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: content-box;
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 50px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 175px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the padding space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-content-box-border-radius-001-ref.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, content-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: content-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 120px;
+    width: 120px;
+    padding: 10px;
+    border: 5px solid lightgreen;
+    margin: 5px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    position: absolute;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox" style="top: 0; left: 0;"></div> <!-- Saturate the margin and border space -->
+    <div class="box" style="height: 24px; top: 20px; left: 128px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 44px; left: 140px;"></div>
+    <div class="box" style="height: 36px; top: 80px; left: 140px;"></div>
+    <div class="box" style="height: 24px; top: 116px; left: 128px;"></div> <!-- Box at corner -->
+    <div class="longbox" style="top: 140px; left: 0;"></div> <!-- Saturate the margin and border space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-content-box-border-radius-001.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, content-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-content-box-border-radius-001-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the content-box and border-radius value.">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: content-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 120px;
+    width: 120px;
+    padding: 10px;
+    border: 5px solid lightgreen;
+    margin: 5px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin and border space -->
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="longbox"></div> <!-- Saturate the margin and border space -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-content-box-border-radius-002-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, content-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: content-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 120px;
+    width: 120px;
+    padding: 10px;
+    border: 5px solid lightgreen;
+    margin: 5px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    position: absolute;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox" style="top: 0; right: 0;"></div> <!-- Saturate the margin and border space -->
+    <div class="box" style="height: 24px; top: 20px; right: 128px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 44px; right: 140px;"></div>
+    <div class="box" style="height: 36px; top: 80px; right: 140px;"></div>
+    <div class="box" style="height: 24px; top: 116px; right: 128px;"></div> <!-- Box at corner -->
+    <div class="longbox" style="top: 140px; right: 0;"></div> <!-- Saturate the margin and border space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-content-box-border-radius-002.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, content-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-content-box-border-radius-002-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the content-box and border-radius value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: content-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 120px;
+    width: 120px;
+    padding: 10px;
+    border: 5px solid lightgreen;
+    margin: 5px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin and border space -->
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="longbox"></div> <!-- Saturate the margin and border space -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-001-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, margin-box reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: margin-box; */
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-001.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, margin-box</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-margin-box-001-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the margin-box value.">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: margin-box;
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-002-ref.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, margin-box reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: margin-box; */
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-002.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, margin-box</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-margin-box-002-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the margin-box value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: margin-box;
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-border-radius-001-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, margin-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: margin-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 20px;
+    width: 20px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 12px; top: 0px; left: 96px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 12px; top: 12px; left: 108px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 24px; left: 120px;"></div>
+    <div class="box" style="height: 36px; top: 60px; left: 120px;"></div>
+    <div class="box" style="height: 12px; top: 96px; left: 108px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 12px; top: 108px; left: 96px;"></div> <!-- Box at corner -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-border-radius-001.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, margin-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-margin-box-border-radius-001-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the margin-box and border-radius value.">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: margin-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 20px;
+    width: 20px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 12px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 12px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 12px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 12px;"></div> <!-- Box at corner -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-border-radius-002-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, margin-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: margin-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 20px;
+    width: 20px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 24px; top: 0px; left: 108px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 24px; left: 120px;"></div>
+    <div class="box" style="height: 36px; top: 60px; left: 120px;"></div>
+    <div class="box" style="height: 24px; top: 96px; left: 108px;"></div> <!-- Box at corner -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-border-radius-002.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, margin-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-margin-box-border-radius-002-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the margin-box and border-radius value.">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: margin-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 20px;
+    width: 20px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-border-radius-003-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, margin-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: margin-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 20px;
+    width: 20px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 12px; top: 0px; right: 96px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 12px; top: 12px; right: 108px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 24px; right: 120px;"></div>
+    <div class="box" style="height: 36px; top: 60px; right: 120px;"></div>
+    <div class="box" style="height: 12px; top: 96px; right: 108px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 12px; top: 108px; right: 96px;"></div> <!-- Box at corner -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-border-radius-003.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, margin-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-margin-box-border-radius-003-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the margin-box and border-radius value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: margin-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 20px;
+    width: 20px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 12px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 12px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 12px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 12px;"></div> <!-- Box at corner -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-border-radius-004-ref.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, margin-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: margin-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 20px;
+    width: 20px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    position: absolute;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 24px; top: 0px; right: 108px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 24px; right: 120px;"></div>
+    <div class="box" style="height: 36px; top: 60px; right: 120px;"></div>
+    <div class="box" style="height: 24px; top: 96px; right: 108px;"></div> <!-- Box at corner -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-margin-box-border-radius-004.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, margin-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-margin-box-border-radius-004-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the margin-box and border-radius value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: margin-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 20px;
+    width: 20px;
+    padding: 20px;
+    border: 20px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-padding-box-001-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, padding-box reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    width: 175px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: padding-box; */
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border-left: 25px solid lightgreen;
+    margin-left: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 175px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-padding-box-001.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, padding-box</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-padding-box-001-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the padding-box value.">
+  <style>
+  .container {
+    width: 175px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: padding-box;
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 175px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-padding-box-002-ref.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, padding-box reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    width: 175px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: padding-box; */
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border-right: 25px solid lightgreen;
+    margin-right: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 175px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="shape"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-padding-box-002.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <title>CSS Shape Test: float right, padding-box</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-padding-box-002-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the padding-box value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 175px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: padding-box;
+    box-sizing: content-box;
+    height: 25px;
+    width: 25px;
+    padding: 25px;
+    border: 25px solid lightgreen;
+    margin: 25px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 175px;
+    height: 25px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="box"></div>
+    <div class="longbox"></div> <!-- Saturate the border space -->
+    <div class="longbox"></div> <!-- Saturate the margin space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-padding-box-border-radius-001-ref.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, padding-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    /* Omit shape-outside: border-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 40px;
+    border: 10px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    position: absolute;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox" style="top: 0; left: 0;"></div> <!-- Saturate the margin and border space -->
+    <div class="box" style="height: 24px; top: 20px; left: 128px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 44px; left: 140px;"></div>
+    <div class="box" style="height: 36px; top: 80px; left: 140px;"></div>
+    <div class="box" style="height: 24px; top: 116px; left: 128px;"></div> <!-- Box at corner -->
+    <div class="longbox" style="top: 140px; left: 0;"></div> <!-- Saturate the margin and border space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-padding-box-border-radius-001.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float left, padding-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-padding-box-border-radius-001-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the left float shape defined by the padding-box and border-radius value.">
+  <style>
+  .container {
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: left;
+    shape-outside: padding-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 40px;
+    border: 10px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin and border space -->
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="longbox"></div> <!-- Saturate the margin and border space -->
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-padding-box-border-radius-002-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, padding-box, border-radius reference</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <style>
+  .container {
+    direction: rtl;
+    position: absolute;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    /* Omit shape-outside: border-box; */
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 40px;
+    border: 10px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    position: absolute;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    position: absolute;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox" style="top: 0; right: 0;"></div> <!-- Saturate the margin and border space -->
+    <div class="box" style="height: 24px; top: 20px; right: 128px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px; top: 44px; right: 140px;"></div>
+    <div class="box" style="height: 36px; top: 80px; right: 140px;"></div>
+    <div class="box" style="height: 24px; top: 116px; right: 128px;"></div> <!-- Box at corner -->
+    <div class="longbox" style="top: 140px; right: 0;"></div> <!-- Saturate the margin and border space -->
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-padding-box-border-radius-002.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+  <meta charset="utf-8">
+  <title>CSS Shape Test: float right, padding-box, border-radius</title>
+  <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-shapes/#shapes-from-box-values">
+  <link rel="match" href="shape-outside-padding-box-border-radius-002-ref.html">
+  <meta name="flags" content="">
+  <meta name="assert" content="Test the boxes are wrapping around the right float shape defined by the padding-box and border-radius value.">
+  <style>
+  .container {
+    direction: rtl;
+    width: 200px;
+    line-height: 0;
+  }
+
+  .shape {
+    float: right;
+    shape-outside: padding-box;
+    border-radius: 50%;
+    box-sizing: content-box;
+    height: 40px;
+    width: 40px;
+    padding: 40px;
+    border: 10px solid lightgreen;
+    margin: 10px;
+    background-color: orange;
+  }
+
+  .box {
+    display: inline-block;
+    width: 60px;
+    background-color: blue;
+  }
+
+  .longbox {
+    display: inline-block;
+    width: 200px;
+    height: 20px;
+    background-color: blue;
+  }
+  </style>
+
+  <body class="container">
+    <div class="shape"></div>
+    <div class="longbox"></div> <!-- Saturate the margin and border space -->
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 36px;"></div>
+    <div class="box" style="height: 24px;"></div> <!-- Box at corner -->
+    <div class="longbox"></div> <!-- Saturate the margin and border space -->
+</body>
+</html>
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -138,16 +138,17 @@ public:
                                                       size_t aTrackNumber) const;
   bool CanSeek() const;
 
   const CryptoFile& Crypto() const;
 
   bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID);
 
 private:
+  void UpdateCrypto();
   Maybe<uint32_t> TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const;
 
   CryptoFile mCrypto;
   RefPtr<Stream> mSource;
   RustStreamAdaptor mRustSource;
   mozilla::UniquePtr<mp4parse_parser, FreeMP4Parser> mRustParser;
 };
 #endif
@@ -330,17 +331,33 @@ bool
 MP4Metadata::CanSeek() const
 {
   return mStagefright->CanSeek();
 }
 
 const CryptoFile&
 MP4Metadata::Crypto() const
 {
-  return mStagefright->Crypto();
+  const CryptoFile& crypto = mStagefright->Crypto();
+
+#ifdef MOZ_RUST_MP4PARSE
+  const CryptoFile& rustCrypto = mRust->Crypto();
+
+#ifndef RELEASE_OR_BETA
+  if (mRustTestMode) {
+    MOZ_DIAGNOSTIC_ASSERT(rustCrypto.pssh == crypto.pssh);
+  }
+#endif
+
+  if (mPreferRust) {
+    return rustCrypto;
+  }
+#endif
+
+  return crypto;
 }
 
 bool
 MP4Metadata::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
 {
 #ifdef MOZ_RUST_MP4PARSE
   if (mRust && mPreferRust && mRust->ReadTrackIndex(aDest, aTrackID)) {
     return true;
@@ -635,22 +652,39 @@ MP4MetadataRust::MP4MetadataRust(Stream*
   mp4parse_error rv = mp4parse_read(mRustParser.get());
   MOZ_LOG(sLog, LogLevel::Debug, ("rust parser returned %d\n", rv));
   Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS,
                         rv == MP4PARSE_OK);
   if (rv != MP4PARSE_OK) {
     MOZ_ASSERT(rv > 0);
     Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_ERROR_CODE, rv);
   }
+
+  UpdateCrypto();
 }
 
 MP4MetadataRust::~MP4MetadataRust()
 {
 }
 
+void
+MP4MetadataRust::UpdateCrypto()
+{
+  mp4parse_pssh_info info = {};
+  if (mp4parse_get_pssh_info(mRustParser.get(), &info) != MP4PARSE_OK) {
+    return;
+  }
+
+  if (info.data.length == 0) {
+    return;
+  }
+
+  mCrypto.Update(info.data.data, info.data.length);
+}
+
 bool
 TrackTypeEqual(TrackInfo::TrackType aLHS, mp4parse_track_type aRHS)
 {
   switch (aLHS) {
   case TrackInfo::kAudioTrack:
     return aRHS == MP4PARSE_TRACK_TYPE_AUDIO;
   case TrackInfo::kVideoTrack:
     return aRHS == MP4PARSE_TRACK_TYPE_VIDEO;
@@ -834,17 +868,16 @@ MP4MetadataRust::CanSeek() const
 {
   MOZ_ASSERT(false, "Not yet implemented");
   return false;
 }
 
 const CryptoFile&
 MP4MetadataRust::Crypto() const
 {
-  MOZ_ASSERT(false, "Not yet implemented");
   return mCrypto;
 }
 
 bool
 MP4MetadataRust::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
 {
   uint8_t fragmented = false;
   auto rv = mp4parse_is_fragmented(mRustParser.get(), aTrackID, &fragmented);
--- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
@@ -33,16 +33,20 @@ namespace mp4_demuxer
 class MP4Demuxer;
 
 struct PsshInfo
 {
   PsshInfo() {}
   PsshInfo(const PsshInfo& aOther) : uuid(aOther.uuid), data(aOther.data) {}
   nsTArray<uint8_t> uuid;
   nsTArray<uint8_t> data;
+
+  bool operator==(const PsshInfo& aOther) const {
+    return uuid == aOther.uuid && data == aOther.data;
+  }
 };
 
 class CryptoFile
 {
 public:
   CryptoFile() : valid(false) {}
   CryptoFile(const CryptoFile& aCryptoFile) : valid(aCryptoFile.valid)
   {
--- a/media/libstagefright/binding/include/mp4parse.h
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -43,26 +43,30 @@ typedef enum mp4parse_codec {
 typedef struct mp4parse_track_info {
 	mp4parse_track_type track_type;
 	mp4parse_codec codec;
 	uint32_t track_id;
 	uint64_t duration;
 	int64_t media_time;
 } mp4parse_track_info;
 
-typedef struct mp4parse_codec_specific_config {
+typedef struct mp4parse_byte_data {
 	uint32_t length;
 	uint8_t const* data;
-} mp4parse_codec_specific_config;
+} mp4parse_byte_data;
+
+typedef struct mp4parse_pssh_info {
+	mp4parse_byte_data data;
+} mp4parse_pssh_info;
 
 typedef struct mp4parse_track_audio_info {
 	uint16_t channels;
 	uint16_t bit_depth;
 	uint32_t sample_rate;
-	mp4parse_codec_specific_config codec_specific_config;
+	mp4parse_byte_data codec_specific_config;
 } mp4parse_track_audio_info;
 
 typedef struct mp4parse_track_video_info {
 	uint32_t display_width;
 	uint32_t display_height;
 	uint16_t image_width;
 	uint16_t image_height;
 } mp4parse_track_video_info;
@@ -94,20 +98,30 @@ mp4parse_error mp4parse_get_track_count(
 mp4parse_error mp4parse_get_track_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_info* info);
 
 /// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`.
 mp4parse_error mp4parse_get_track_audio_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_audio_info* info);
 
 /// Fill the supplied `mp4parse_track_video_info` with metadata for `track`.
 mp4parse_error mp4parse_get_track_video_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_video_info* info);
 
+/// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file.
 mp4parse_error mp4parse_get_fragment_info(mp4parse_parser* parser, mp4parse_fragment_info* info);
 
+/// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
 mp4parse_error mp4parse_is_fragmented(mp4parse_parser* parser, uint32_t track_id, uint8_t* fragmented);
 
+/// Get 'pssh' system id and 'pssh' box content for eme playback.
+///
+/// The data format in 'info' passing to gecko is:
+///   system_id
+///   pssh box size (in native endian)
+///   pssh box content (including header)
+mp4parse_error mp4parse_get_pssh_info(mp4parse_parser* parser, mp4parse_pssh_info* info);
+
 
 
 #ifdef __cplusplus
 }
 #endif
 
 
 #endif
--- a/media/libstagefright/binding/mp4parse-cargo.patch
+++ b/media/libstagefright/binding/mp4parse-cargo.patch
@@ -20,23 +20,24 @@ index ff9422c..814c4c6 100644
 -
  # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
  [profile.release]
  debug-assertions = true
 diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
 index aeeebc65..5c0836a 100644
 --- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml
 +++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
-@@ -18,17 +18,9 @@ exclude = [
+@@ -18,18 +18,10 @@ exclude = [
    "*.mp4",
  ]
  
 -build = "build.rs"
 -
  [dependencies]
+ byteorder = "0.5.0"
  "mp4parse" = {version = "0.6.0", path = "../mp4parse"}
  
 -[build-dependencies]
 -rusty-cheddar = "0.3.2"
 -
 -[features]
 -fuzz = ["mp4parse/fuzz"]
 -
--- a/media/libstagefright/binding/mp4parse/src/boxes.rs
+++ b/media/libstagefright/binding/mp4parse/src/boxes.rs
@@ -18,45 +18,46 @@ macro_rules! box_database {
                     _ => UnknownBox(t),
                 }
             }
         }
     }
 }
 
 box_database!(
-    FileTypeBox                0x66747970, // "ftyp"
-    MovieBox                   0x6d6f6f76, // "moov"
-    MovieHeaderBox             0x6d766864, // "mvhd"
-    TrackBox                   0x7472616b, // "trak"
-    TrackHeaderBox             0x746b6864, // "tkhd"
-    EditBox                    0x65647473, // "edts"
-    MediaBox                   0x6d646961, // "mdia"
-    EditListBox                0x656c7374, // "elst"
-    MediaHeaderBox             0x6d646864, // "mdhd"
-    HandlerBox                 0x68646c72, // "hdlr"
-    MediaInformationBox        0x6d696e66, // "minf"
-    SampleTableBox             0x7374626c, // "stbl"
-    SampleDescriptionBox       0x73747364, // "stsd"
-    TimeToSampleBox            0x73747473, // "stts"
-    SampleToChunkBox           0x73747363, // "stsc"
-    SampleSizeBox              0x7374737a, // "stsz"
-    ChunkOffsetBox             0x7374636f, // "stco"
-    ChunkLargeOffsetBox        0x636f3634, // "co64"
-    SyncSampleBox              0x73747373, // "stss"
-    AVCSampleEntry             0x61766331, // "avc1"
-    AVC3SampleEntry            0x61766333, // "avc3" - Need to check official name in spec.
-    AVCConfigurationBox        0x61766343, // "avcC"
-    MP4AudioSampleEntry        0x6d703461, // "mp4a"
-    ESDBox                     0x65736473, // "esds"
-    VP8SampleEntry             0x76703038, // "vp08"
-    VP9SampleEntry             0x76703039, // "vp09"
-    VPCodecConfigurationBox    0x76706343, // "vpcC"
-    FLACSampleEntry            0x664c6143, // "fLaC"
-    FLACSpecificBox            0x64664c61, // "dfLa"
-    OpusSampleEntry            0x4f707573, // "Opus"
-    OpusSpecificBox            0x644f7073, // "dOps"
-    ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
-    ProtectedAudioSampleEntry  0x656e6361, // "enca" - Need to check official name in spec.
-    MovieExtendsBox            0x6d766578, // "mvex"
-    MovieExtendsHeaderBox      0x6d656864, // "mehd"
-    QTWaveAtom                 0x77617665, // "wave" - quicktime atom
+    FileTypeBox                       0x66747970, // "ftyp"
+    MovieBox                          0x6d6f6f76, // "moov"
+    MovieHeaderBox                    0x6d766864, // "mvhd"
+    TrackBox                          0x7472616b, // "trak"
+    TrackHeaderBox                    0x746b6864, // "tkhd"
+    EditBox                           0x65647473, // "edts"
+    MediaBox                          0x6d646961, // "mdia"
+    EditListBox                       0x656c7374, // "elst"
+    MediaHeaderBox                    0x6d646864, // "mdhd"
+    HandlerBox                        0x68646c72, // "hdlr"
+    MediaInformationBox               0x6d696e66, // "minf"
+    SampleTableBox                    0x7374626c, // "stbl"
+    SampleDescriptionBox              0x73747364, // "stsd"
+    TimeToSampleBox                   0x73747473, // "stts"
+    SampleToChunkBox                  0x73747363, // "stsc"
+    SampleSizeBox                     0x7374737a, // "stsz"
+    ChunkOffsetBox                    0x7374636f, // "stco"
+    ChunkLargeOffsetBox               0x636f3634, // "co64"
+    SyncSampleBox                     0x73747373, // "stss"
+    AVCSampleEntry                    0x61766331, // "avc1"
+    AVC3SampleEntry                   0x61766333, // "avc3" - Need to check official name in spec.
+    AVCConfigurationBox               0x61766343, // "avcC"
+    MP4AudioSampleEntry               0x6d703461, // "mp4a"
+    ESDBox                            0x65736473, // "esds"
+    VP8SampleEntry                    0x76703038, // "vp08"
+    VP9SampleEntry                    0x76703039, // "vp09"
+    VPCodecConfigurationBox           0x76706343, // "vpcC"
+    FLACSampleEntry                   0x664c6143, // "fLaC"
+    FLACSpecificBox                   0x64664c61, // "dfLa"
+    OpusSampleEntry                   0x4f707573, // "Opus"
+    OpusSpecificBox                   0x644f7073, // "dOps"
+    ProtectedVisualSampleEntry        0x656e6376, // "encv" - Need to check official name in spec.
+    ProtectedAudioSampleEntry         0x656e6361, // "enca" - Need to check official name in spec.
+    MovieExtendsBox                   0x6d766578, // "mvex"
+    MovieExtendsHeaderBox             0x6d656864, // "mehd"
+    QTWaveAtom                        0x77617665, // "wave" - quicktime atom
+    ProtectionSystemSpecificHeaderBox 0x70737368, // "pssh"
 );
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -4,17 +4,17 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
 #![cfg_attr(feature = "fuzz", feature(plugin))]
 #![cfg_attr(feature = "fuzz", plugin(afl_plugin))]
 #[cfg(feature = "fuzz")]
 extern crate afl;
 
 extern crate byteorder;
-use byteorder::ReadBytesExt;
+use byteorder::{ReadBytesExt, WriteBytesExt};
 use std::io::{Read, Take};
 use std::io::Cursor;
 use std::cmp;
 
 mod boxes;
 use boxes::BoxType;
 
 // Unit tests.
@@ -289,23 +289,36 @@ pub struct OpusSpecificBox {
     channel_mapping_table: Option<ChannelMappingTable>,
 }
 
 #[derive(Debug)]
 pub struct MovieExtendsBox {
     pub fragment_duration: Option<MediaScaledTime>,
 }
 
+pub type ByteData = Vec<u8>;
+
+#[derive(Debug, Default)]
+pub struct ProtectionSystemSpecificHeaderBox {
+    pub system_id: ByteData,
+    pub kid: Vec<ByteData>,
+    pub data: ByteData,
+
+    // The entire pssh box (include header) required by Gecko.
+    pub box_content: ByteData,
+}
+
 /// Internal data structures.
 #[derive(Debug, Default)]
 pub struct MediaContext {
     pub timescale: Option<MediaTimeScale>,
     /// Tracks found in the file.
     pub tracks: Vec<Track>,
     pub mvex: Option<MovieExtendsBox>,
+    pub psshs: Vec<ProtectionSystemSpecificHeaderBox>
 }
 
 impl MediaContext {
     pub fn new() -> MediaContext {
         Default::default()
     }
 }
 
@@ -596,23 +609,67 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>
                 try!(read_trak(&mut b, &mut track));
                 context.tracks.push(track);
             }
             BoxType::MovieExtendsBox => {
                 let mvex = try!(read_mvex(&mut b));
                 log!("{:?}", mvex);
                 context.mvex = Some(mvex);
             }
+            BoxType::ProtectionSystemSpecificHeaderBox => {
+                let pssh = try!(read_pssh(&mut b));
+                log!("{:?}", pssh);
+                context.psshs.push(pssh);
+            }
             _ => try!(skip_box_content(&mut b)),
         };
         check_parser_state!(b.content);
     }
     Ok(())
 }
 
+fn read_pssh<T: Read>(src: &mut BMFFBox<T>) -> Result<ProtectionSystemSpecificHeaderBox> {
+    let mut box_content = Vec::with_capacity(src.head.size as usize);
+    try!(src.read_to_end(&mut box_content));
+
+    let (system_id, kid, data) = {
+        let pssh = &mut Cursor::new(box_content.as_slice());
+
+        let (version, _) = try!(read_fullbox_extra(pssh));
+
+        let system_id = try!(read_buf(pssh, 16));
+
+        let mut kid: Vec<ByteData> = Vec::new();
+        if version > 0 {
+            let count = try!(be_i32(pssh));
+            for _ in 0..count {
+                let item = try!(read_buf(pssh, 16));
+                kid.push(item);
+            }
+        }
+
+        let data_size = try!(be_i32(pssh)) as usize;
+        let data = try!(read_buf(pssh, data_size));
+
+        (system_id, kid, data)
+    };
+
+    let mut pssh_box = Vec::new();
+    try!(write_be_u32(&mut pssh_box, src.head.size as u32));
+    pssh_box.append(&mut b"pssh".to_vec());
+    pssh_box.append(&mut box_content);
+
+    Ok(ProtectionSystemSpecificHeaderBox {
+        system_id: system_id,
+        kid: kid,
+        data: data,
+        box_content: pssh_box,
+    })
+}
+
 fn read_mvex<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieExtendsBox> {
     let mut iter = src.box_iter();
     let mut fragment_duration = None;
     while let Some(mut b) = try!(iter.next_box()) {
         match b.head.name {
             BoxType::MovieExtendsHeaderBox => {
                 let duration = try!(read_mehd(&mut b));
                 fragment_duration = Some(duration);
@@ -1697,8 +1754,12 @@ fn be_u24<T: ReadBytesExt>(src: &mut T) 
 
 fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
     src.read_u32::<byteorder::BigEndian>().map_err(From::from)
 }
 
 fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
     src.read_u64::<byteorder::BigEndian>().map_err(From::from)
 }
+
+fn write_be_u32<T: WriteBytesExt>(des: &mut T, num: u32) -> Result<()> {
+    des.write_u32::<byteorder::BigEndian>(num).map_err(From::from)
+}
--- a/media/libstagefright/binding/mp4parse/tests/public.rs
+++ b/media/libstagefright/binding/mp4parse/tests/public.rs
@@ -90,8 +90,40 @@ fn public_api() {
                 }, "ES");
                 assert!(a.samplesize > 0);
                 assert!(a.samplerate > 0);
             }
             Some(mp4::SampleEntry::Unknown) | None => {}
         }
     }
 }
+
+#[test]
+fn public_cenc() {
+    let mut fd = File::open("tests/bipbop_480wp_1001kbps-cenc-video-key1-init.mp4").expect("Unknown file");
+    let mut buf = Vec::new();
+    fd.read_to_end(&mut buf).expect("File error");
+
+    let mut c = Cursor::new(&buf);
+    let mut context = mp4::MediaContext::new();
+    mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
+    for track in context.tracks {
+        assert_eq!(track.codec_type, mp4::CodecType::EncryptedVideo);
+    }
+
+    let system_id = vec![0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b];
+
+    let kid = vec![0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x11];
+
+    let pssh_box = vec![0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x01, 0x00, 0x00, 0x00, 0x10, 0x77,
+                        0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00,
+                        0x00, 0x01, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57,
+                        0x1d, 0x11, 0x00, 0x00, 0x00, 0x00];
+
+    for pssh in context.psshs {
+        assert_eq!(pssh.system_id, system_id);
+        for kid_id in pssh.kid {
+            assert_eq!(kid_id, kid);
+        }
+        assert_eq!(pssh.data.len(), 0);
+        assert_eq!(pssh.box_content, pssh_box);
+    }
+}
--- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml
+++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
@@ -14,13 +14,14 @@ license = "MPL-2.0"
 repository = "https://github.com/mozilla/mp4parse-rust"
 
 # Avoid complaints about trying to package test files.
 exclude = [
   "*.mp4",
 ]
 
 [dependencies]
+byteorder = "0.5.0"
 "mp4parse" = {version = "0.6.0", path = "../mp4parse"}
 
 # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
 [profile.release]
 debug-assertions = true
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -30,19 +30,21 @@
 //! }
 //! ```
 
 // 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 https://mozilla.org/MPL/2.0/.
 
 extern crate mp4parse;
+extern crate byteorder;
 
 use std::io::Read;
 use std::collections::HashMap;
+use byteorder::WriteBytesExt;
 
 // Symbols we need from our rust api.
 use mp4parse::MediaContext;
 use mp4parse::TrackType;
 use mp4parse::read_mp4;
 use mp4parse::Error;
 use mp4parse::SampleEntry;
 use mp4parse::AudioCodecSpecific;
@@ -98,40 +100,53 @@ pub struct mp4parse_track_info {
     pub codec: mp4parse_codec,
     pub track_id: u32,
     pub duration: u64,
     pub media_time: i64, // wants to be u64? understand how elst adjustment works
     // TODO(kinetik): include crypto guff
 }
 
 #[repr(C)]
-pub struct mp4parse_codec_specific_config {
+pub struct mp4parse_byte_data {
     pub length: u32,
     pub data: *const u8,
 }
 
-impl Default for mp4parse_codec_specific_config {
+impl Default for mp4parse_byte_data {
     fn default() -> Self {
-        mp4parse_codec_specific_config {
+        mp4parse_byte_data {
             length: 0,
             data: std::ptr::null_mut(),
         }
     }
 }
 
+impl mp4parse_byte_data {
+    fn set_data(&mut self, data: &Vec<u8>) {
+        self.length = data.len() as u32;
+        self.data = data.as_ptr();
+    }
+}
+
+#[repr(C)]
+#[derive(Default)]
+pub struct mp4parse_pssh_info {
+    pub data: mp4parse_byte_data,
+}
+
 #[derive(Default)]
 #[repr(C)]
 pub struct mp4parse_track_audio_info {
     pub channels: u16,
     pub bit_depth: u16,
     pub sample_rate: u32,
     // TODO(kinetik):
     // int32_t profile;
     // int32_t extended_profile; // check types
-    codec_specific_config: mp4parse_codec_specific_config,
+    codec_specific_config: mp4parse_byte_data,
 }
 
 #[repr(C)]
 pub struct mp4parse_track_video_info {
     pub display_width: u32,
     pub display_height: u32,
     pub image_width: u16,
     pub image_height: u16,
@@ -149,16 +164,17 @@ pub struct mp4parse_fragment_info {
 
 // Even though mp4parse_parser is opaque to C, rusty-cheddar won't let us
 // use more than one member, so we introduce *another* wrapper.
 struct Wrap {
     context: MediaContext,
     io: mp4parse_io,
     poisoned: bool,
     opus_header: HashMap<u32, Vec<u8>>,
+    pssh_data: Vec<u8>,
 }
 
 #[repr(C)]
 #[allow(non_camel_case_types)]
 pub struct mp4parse_parser(Wrap);
 
 impl mp4parse_parser {
     fn context(&self) -> &MediaContext {
@@ -179,16 +195,20 @@ impl mp4parse_parser {
 
     fn set_poisoned(&mut self, poisoned: bool) {
         self.0.poisoned = poisoned;
     }
 
     fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> {
         &mut self.0.opus_header
     }
+
+    fn pssh_data_mut(&mut self) -> &mut Vec<u8> {
+        &mut self.0.pssh_data
+    }
 }
 
 #[repr(C)]
 #[derive(Clone)]
 pub struct mp4parse_io {
     pub read: extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize,
     pub userdata: *mut std::os::raw::c_void,
 }
@@ -223,16 +243,17 @@ pub unsafe extern fn mp4parse_new(io: *c
     if ((*io).read as *mut std::os::raw::c_void).is_null() {
         return std::ptr::null_mut();
     }
     let parser = Box::new(mp4parse_parser(Wrap {
         context: MediaContext::new(),
         io: (*io).clone(),
         poisoned: false,
         opus_header: HashMap::new(),
+        pssh_data: Vec::new(),
     }));
     Box::into_raw(parser)
 }
 
 /// Free an `mp4parse_parser*` allocated by `mp4parse_new()`.
 #[no_mangle]
 pub unsafe extern fn mp4parse_free(parser: *mut mp4parse_parser) {
     assert!(!parser.is_null());
@@ -524,16 +545,17 @@ pub unsafe extern fn mp4parse_get_track_
         return MP4PARSE_ERROR_INVALID;
     }
     (*info).image_width = video.width;
     (*info).image_height = video.height;
 
     MP4PARSE_OK
 }
 
+/// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file.
 #[no_mangle]
 pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error {
     if parser.is_null() || info.is_null() || (*parser).poisoned() {
         return MP4PARSE_ERROR_BADARG;
     }
 
     let context = (*parser).context();
     let info: &mut mp4parse_fragment_info = &mut *info;
@@ -550,17 +572,17 @@ pub unsafe extern fn mp4parse_get_fragme
             Some(time_us) => time_us as u64,
             None => return MP4PARSE_ERROR_INVALID,
         }
     }
 
     MP4PARSE_OK
 }
 
-// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
+/// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
 #[no_mangle]
 pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_id: u32, fragmented: *mut u8) -> mp4parse_error {
     if parser.is_null() || (*parser).poisoned() {
         return MP4PARSE_ERROR_BADARG;
     }
 
     let context = (*parser).context_mut();
     let tracks = &context.tracks;
@@ -576,16 +598,51 @@ pub unsafe extern fn mp4parse_is_fragmen
         Some(track) if track.empty_sample_boxes.all_empty() => (*fragmented) = true as u8,
         Some(_) => {},
         None => return MP4PARSE_ERROR_BADARG,
     }
 
     MP4PARSE_OK
 }
 
+/// Get 'pssh' system id and 'pssh' box content for eme playback.
+///
+/// The data format in 'info' passing to gecko is:
+///   system_id
+///   pssh box size (in native endian)
+///   pssh box content (including header)
+#[no_mangle]
+pub unsafe extern fn mp4parse_get_pssh_info(parser: *mut mp4parse_parser, info: *mut mp4parse_pssh_info) -> mp4parse_error {
+    if parser.is_null() || info.is_null() || (*parser).poisoned() {
+        return MP4PARSE_ERROR_BADARG;
+    }
+
+    let context = (*parser).context_mut();
+    let pssh_data = (*parser).pssh_data_mut();
+    let info: &mut mp4parse_pssh_info = &mut *info;
+
+    pssh_data.clear();
+    for pssh in &context.psshs {
+        let mut data_len = Vec::new();
+        match data_len.write_u32::<byteorder::NativeEndian>(pssh.box_content.len() as u32) {
+            Err(_) => {
+                return MP4PARSE_ERROR_IO;
+            },
+            _ => (),
+        }
+        pssh_data.extend_from_slice(pssh.system_id.as_slice());
+        pssh_data.extend_from_slice(data_len.as_slice());
+        pssh_data.extend_from_slice(pssh.box_content.as_slice());
+    }
+
+    info.data.set_data(&pssh_data);
+
+    MP4PARSE_OK
+}
+
 #[cfg(test)]
 extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
     panic!("panic_read shouldn't be called in these tests");
 }
 
 #[cfg(test)]
 extern fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
     -1
--- a/media/mtransport/transportlayerdtls.cpp
+++ b/media/mtransport/transportlayerdtls.cpp
@@ -520,16 +520,23 @@ bool TransportLayerDtls::Setup() {
     rv = SSL_ConfigSecureServer(ssl_fd.get(), identity_->cert().get(),
                                 identity_->privkey(),
                                 identity_->auth_type());
     if (rv != SECSuccess) {
       MOZ_MTLOG(ML_ERROR, "Couldn't set identity");
       return false;
     }
 
+    UniqueCERTCertList zero_certs(CERT_NewCertList());
+    rv = SSL_SetTrustAnchors(ssl_fd.get(), zero_certs.get());
+    if (rv != SECSuccess) {
+      MOZ_MTLOG(ML_ERROR, "Couldn't set trust anchors");
+      return false;
+    }
+
     // Insist on a certificate from the client
     rv = SSL_OptionSet(ssl_fd.get(), SSL_REQUEST_CERTIFICATE, PR_TRUE);
     if (rv != SECSuccess) {
       MOZ_MTLOG(ML_ERROR, "Couldn't request certificate");
       return false;
     }
 
     rv = SSL_OptionSet(ssl_fd.get(), SSL_REQUIRE_CERTIFICATE, PR_TRUE);
--- a/media/webrtc/moz.build
+++ b/media/webrtc/moz.build
@@ -116,8 +116,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk
         GYP_DIRS['signalingtest'].variables = gyp_vars.copy()
         GYP_DIRS['signalingtest'].variables.update(
             build_for_test=1,
             moz_webrtc_mediacodec=0,
             build_for_standalone=0
         )
         GYP_DIRS['signalingtest'].sandbox_vars['ALLOW_COMPILER_WARNINGS'] = True
         GYP_DIRS['signalingtest'].non_unified_sources += signaling_non_unified_sources
+
+DIRS += ['signaling/fuzztest']
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/fuzztest/moz.build
@@ -0,0 +1,48 @@
+# -*- Mode: python; 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/.
+
+if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'uikit':
+    DISABLE_STL_WRAPPING = True
+    DEFINES['MOZ_NO_MOZALLOC'] = True
+
+    if CONFIG['OS_TARGET'] == 'Darwin':
+        DEFINES['SIP_OS_OSX'] = True
+    else:
+        DEFINES['SIP_OS_LINUX'] = True
+
+    LOCAL_INCLUDES += [
+        '../..',
+        '/media/mtransport',
+        '/media/webrtc/signaling/src/common/browser_logging',
+    ]
+
+    USE_LIBS += [
+        '/media/webrtc/trunk/testing/gtest_gtest/gtest',
+        'nspr'
+    ]
+
+    SOURCES = [
+      '/media/webrtc/signaling/src/sdp/SdpAttribute.cpp',
+      '/media/webrtc/signaling/src/sdp/SdpHelper.cpp',
+      '/media/webrtc/signaling/src/sdp/SdpMediaSection.cpp',
+      '/media/webrtc/signaling/src/sdp/sipcc/cpr_string.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_access.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_base64.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_config.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_services_unix.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_token.c',
+      '/media/webrtc/signaling/src/sdp/sipcc/sdp_utils.c',
+      '/media/webrtc/signaling/src/sdp/SipccSdp.cpp',
+      '/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp',
+      '/media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp',
+      '/media/webrtc/signaling/src/sdp/SipccSdpParser.cpp',
+      'sdp_file_parser.cpp',
+    ]
+
+    GeckoProgram('sdp_file_parser')
rename from media/webrtc/signaling/test/sdp_file_parser.cpp
rename to media/webrtc/signaling/fuzztest/sdp_file_parser.cpp
--- a/media/webrtc/signaling/test/sdp_file_parser.cpp
+++ b/media/webrtc/signaling/fuzztest/sdp_file_parser.cpp
@@ -5,29 +5,51 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <string>
 #include <iostream>
 #include <fstream>
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
-#include "gtest_utils.h"
-
-// without this include linking fails
-#include "FakeMediaStreamsImpl.h"
-#include "FakeLogging.h"
 
 #include "signaling/src/sdp/SipccSdpParser.h"
 
-#include "FakeIPC.h"
-#include "FakeIPC.cpp"
+#include "CSFLog.h"
+
+void CSFLog(CSFLogLevel priority, const char* sourceFile, int sourceLine, const char* tag , const char* format, ...)
+{
+  va_list ap;
+  va_start(ap, format);
+
+  printf("%s\n:", tag);
+  vprintf(format, ap);
+
+  va_end(ap);
+}
 
 namespace mozilla {
 
+enum class LogLevel {
+
+};
+
+namespace detail {
+
+void log_print(PRLogModuleInfo const* info, LogLevel level, char const* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+