Merge autoland to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Fri, 04 Oct 2019 12:46:05 +0300
changeset 496421 81ebffadd73ab463785bacfbcd38644fa576bacd
parent 496420 678d4d2c3c4dfe1dbb2edfdc26d9f0723acebdd2 (current diff)
parent 496279 d2706c5d06eafdc10bbd25fba3f332b6954e3bd3 (diff)
child 496422 8d68e16d3d775740b85377b775eecb2173c6a03c
push id97227
push userncsoregi@mozilla.com
push dateFri, 04 Oct 2019 21:43:23 +0000
treeherderautoland@02bc638506ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone71.0a1
first release with
nightly linux32
81ebffadd73a / 71.0a1 / 20191004094656 / files
nightly linux64
81ebffadd73a / 71.0a1 / 20191004094656 / files
nightly mac
81ebffadd73a / 71.0a1 / 20191004094656 / files
nightly win32
81ebffadd73a / 71.0a1 / 20191004094656 / files
nightly win64
81ebffadd73a / 71.0a1 / 20191004094656 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
--- a/browser/components/customizableui/test/browser_PanelMultiView_keyboard.js
+++ b/browser/components/customizableui/test/browser_PanelMultiView_keyboard.js
@@ -103,17 +103,17 @@ add_task(async function setup() {
   gMainMenulist.appendChild(menuPopup);
   let item = document.createXULElement("menuitem");
   item.setAttribute("value", "1");
   item.setAttribute("selected", "true");
   menuPopup.appendChild(item);
   item = document.createXULElement("menuitem");
   item.setAttribute("value", "2");
   menuPopup.appendChild(item);
-  gMainTextbox = document.createXULElement("textbox");
+  gMainTextbox = document.createElement("input");
   gMainTextbox.id = "gMainTextbox";
   gMainView.appendChild(gMainTextbox);
   gMainTextbox.setAttribute("value", "value");
   gMainButton2 = document.createXULElement("button");
   gMainButton2.id = "gMainButton2";
   gMainView.appendChild(gMainButton2);
   gMainButton3 = document.createXULElement("button");
   gMainButton3.id = "gMainButton3";
--- a/dom/tests/mochitest/chrome/focus_window2.xul
+++ b/dom/tests/mochitest/chrome/focus_window2.xul
@@ -16,11 +16,11 @@ function focused()
 }
 
 SimpleTest.waitForFocus(focused);
 
 ]]>
 </script>
 
 <button id="other" label="OK"/>
-<textbox id="other-textbox" value="test"/>
+<input xmlns="http://www.w3.org/1999/xhtml" id="other-input" value="test"/>
 
 </window>
--- a/dom/tests/mochitest/chrome/window_focus.xul
+++ b/dom/tests/mochitest/chrome/window_focus.xul
@@ -298,17 +298,17 @@ function mouseWillTriggerFocus(element)
   }
 
   return false;
 }
 
 function mouseOnElement(element, expectedElement, focusChanged, testid)
 {
   var expectedWindow = (element.ownerGlobal == gChildWindow) ? gChildWindow : window;
-  // on Mac, form elements are not focused when clicking, except for lists and textboxes.
+  // on Mac, form elements are not focused when clicking, except for lists and inputs.
   var noFocusOnMouse = !mouseWillTriggerFocus(element)
 
   if (noFocusOnMouse) {
     // no focus so the last focus method will be 0
     gLastFocusMethod = 0;
     expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal),
                      expectedWindow, null, true, testid);
     gLastFocusMethod = fm.FLAG_BYMOUSE;
@@ -365,17 +365,17 @@ function startTest()
   synthesizeMouse(getById("image"), 4, 4, { type: "mousemove" }, gChildWindow);
 
   initEvents(window);
 
   is(fm.activeWindow, window, "activeWindow");
   is(gChildWindow.document.hasFocus(), false, " child document hasFocus");
 
   // test to see if the Mac Full Keyboard Access setting is set. If t3 is
-  // focused after tab is pressed, then it is set to textboxes and lists only.
+  // focused after tab is pressed, then it is set to inputs and lists only.
   // Otherwise, all elements are in the tab order.
   pressTab();
 
   if (fm.focusedElement.id == "t3")
     gPartialTabbing = true;
   else
     is(fm.focusedElement.id, "t1", "initial tab key");
 
@@ -424,20 +424,20 @@ function startTest()
   var t19 = getById("t19");
   is(t19.selectionStart, 0, "input focused from tab key selectionStart");
   is(t19.selectionEnd, 5, "input focused from tab key selectionEnd");
   t19.setSelectionRange(0, 0);
 
   gLastFocusMethod = 0;
   var selectFired = false;
   function selectListener() { selectFired = true; }
-  t19.addEventListener("select", selectListener, false); 
+  t19.addEventListener("select", selectListener, false);
   expectFocusShift(() => t19.select(),
                    null, getById("t" + 19), true, "input.select()");
-  t19.removeEventListener("select", selectListener, false); 
+  t19.removeEventListener("select", selectListener, false);
   ok(selectFired, "select event fires for input");
 
   // mouse clicking
   gLastFocusMethod = fm.FLAG_BYMOUSE;
   for (idx = kTabbableSteps; idx >= 1; idx--) {
     // skip the document root and the overflow element
     if (idx == kChildDocumentRootIndex || idx == kOverflowElementIndex)
       continue;
@@ -455,17 +455,17 @@ function startTest()
         navigator.platform.indexOf("Mac") == 0) {
       // after focusing a listbox on Mac, clear the focus before continuing.
       setFocusTo(null, window);
     }
   }
 
   ok(t19.selectionStart == t19.selectionEnd, "input focused from mouse selection");
 
-  // mouse clicking on elements that are not tabbable 
+  // mouse clicking on elements that are not tabbable
   for (idx = 1; idx <= kFocusSteps; idx++) {
     var element = getById("o" + (idx % 2 ? idx : idx - 1));
 
     mouseOnElement(element, element, idx % 2,
                    "mouse on non-tabbable element o" + idx);
   }
 
   // mouse clicking on elements that are not tabbable and have user-focus: none
@@ -497,17 +497,17 @@ function startTest()
   $("t1").focus();
   ok(gEvents === "", "focusing element that is already focused");
 
   $("t2").blur();
   $("t7").blur();
   ok(gEvents === "", "blurring element that is not focused");
   is(document.activeElement, $("t1"), "old element still focused after blur() on another element");
 
-  // focus() method on elements that are not tabbable 
+  // focus() method on elements that are not tabbable
   for (idx = 1; idx <= kFocusSteps; idx++) {
     var expected = getById("o" + (idx % 2 ? idx : idx - 1));
     expectFocusShift(() => getById("o" + idx).focus(),
                      expected.ownerGlobal,
                      expected, idx % 2, "focus method on non-tabbable element o" + idx);
   }
 
   // focus() method on elements that are not tabbable and have user-focus: none
@@ -700,17 +700,17 @@ function startTest()
     element.removeEventListener("focus", eventListener, false);
   }
 
   fm.clearFocus(window);
   gEvents = "";
 
   // next, check cases where the focus is adjusted during the focus event
 
-  // switch focus to an element in the same document  
+  // switch focus to an element in the same document
   trapFocus(o5, shiftFocusParentDocument);
   compareEvents("commandupdate: cu focus: o5 commandupdate: cu blur: o5 commandupdate: cu focus: o9",
                 window, o9, "change focus to sibling during element focus");
 
   // similar, but the new element (t17) is in a child document
   trapFocus(o5, shiftFocusChildDocument);
   compareEvents("commandupdate: cu blur: o9 " +
                 "commandupdate: cu focus: o5 commandupdate: cu blur: o5 " +
@@ -782,41 +782,41 @@ function startTest()
   inscroll.parentNode.scrollTop = 0;
   fm.setFocus(inscroll, fm.FLAG_NOSCROLL);
   is(inscroll.parentNode.scrollTop, 0, "scroll position after noscroll focus");
 
   getById("t9").focus();
   getById("inpopup1").focus();
   is(fm.focusedElement, getById("t9"), "focus in closed popup");
 
-  // ---- tests to check if tabbing out of a textbox works
+  // ---- tests to check if tabbing out of a input works
 
   setFocusTo("t1", window);
 
-  var textbox1 = document.createXULElement("textbox");
-  $("innerbox").appendChild(textbox1);
+  var input1 = document.createElement("input");
+  $("innerbox").appendChild(input1);
 
-  var textbox2 = document.createXULElement("textbox");
-  $("innerbox").appendChild(textbox2);
+  var input2 = document.createElement("input");
+  $("innerbox").appendChild(input2);
 
   gLastFocusMethod = 0;
-  expectFocusShift(() => textbox2.focus(),
-                   null, textbox2.inputField, true, "focus on textbox");
+  expectFocusShift(() => input2.focus(),
+                   null, input2, true, "focus on input");
   gLastFocusMethod = fm.FLAG_BYKEY;
   expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
-                   null, textbox1.inputField, true, "shift+tab on textbox");
+                   null, input1, true, "shift+tab on input");
 
-  textbox1.tabIndex = 2;
-  textbox2.tabIndex = 2;
+  input1.tabIndex = 2;
+  input2.tabIndex = 2;
   gLastFocusMethod = 0;
-  expectFocusShift(() => textbox2.focus(),
-                   null, textbox2.inputField, true, "focus on textbox with tabindex set");
+  expectFocusShift(() => input2.focus(),
+                   null, input2, true, "focus on input with tabindex set");
   gLastFocusMethod = fm.FLAG_BYKEY;
   expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
-                   null, textbox1.inputField, true, "shift+tab on textbox with tabindex set");
+                   null, input1, true, "shift+tab on input with tabindex set");
 
   // ---- test for bug 618907 which ensures that canceling the mousedown event still focuses the
   //      right frame
 
   var childContentFrame = document.getElementById("ifa")
   childContentFrame.style.MozUserFocus = "";
 
   var frab = childContentFrame.contentDocument.getElementById("fra-b");
@@ -1061,25 +1061,25 @@ function testMoveFocus()
   // cases with selection in an <input>
 
   var t19 = getById("t19");
   t19.setSelectionRange(0, 0);
   setFocusTo("t18", gChildWindow);
 
   gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
   expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FORWARD, 0),
-                   gChildWindow, t19, true, "moveFocus to next textbox");
+                   gChildWindow, t19, true, "moveFocus to next input");
   is(t19.selectionStart, 0, "input focused after moveFocus selectionStart");
   is(t19.selectionEnd, 5, "input focused after moveFocus selectionEnd");
 
   t19.setSelectionRange(0, 0);
   setFocusTo("t18", gChildWindow);
   gLastFocusMethod = fm.FLAG_BYKEY;
   expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FORWARD, fm.FLAG_BYKEY),
-                   gChildWindow, t19, true, "moveFocus to next textbox by key");
+                   gChildWindow, t19, true, "moveFocus to next input by key");
   is(t19.selectionStart, 0, "input focused after moveFocus by key selectionStart");
   is(t19.selectionEnd, 5, "input focused after moveFocus by key selectionEnd");
 }
 
 function otherWindowFocused(otherWindow)
 {
   var expectedElement = getById("t9");
 
@@ -1324,55 +1324,55 @@ function switchWindowTest(otherWindow, f
   gNewExpectedWindow = otherWindow;
   expectFocusShift(() => fm.setFocus(otherElement, fm.FLAG_RAISE),
                    gNewExpectedWindow, getById("other"), true, "switch to window with raise");
 
   getTopWindow(framesetWindow).document.commandDispatcher.focusedWindow = gOldExpectedWindow;
   is(fm.activeWindow, gNewExpectedWindow, "setting commandDispatcher focusedWindow doesn't raise window");
 
   fm.moveFocus(otherWindow, null, fm.MOVEFOCUS_FORWARD, 0);
-  var otherTextbox = otherWindow.document.getElementById("other-textbox");
-  otherTextbox.setSelectionRange(2, 3);
+  var otherInput = otherWindow.document.getElementById("other-input");
+  otherInput.setSelectionRange(2, 3);
   fm.activeWindow = topWindow;
   fm.activeWindow = otherWindow;
-  is(otherTextbox.selectionStart, 2, "selectionStart after textbox focus and window raise");
-  is(otherTextbox.selectionEnd, 3, "selectionEnd after textbox focus and window raise");
-  is(fm.getLastFocusMethod(null), fm.FLAG_BYMOVEFOCUS, "last focus method after textbox focus and window raise");
+  is(otherInput.selectionStart, 2, "selectionStart after input focus and window raise");
+  is(otherInput.selectionEnd, 3, "selectionEnd after input focus and window raise");
+  is(fm.getLastFocusMethod(null), fm.FLAG_BYMOVEFOCUS, "last focus method after input focus and window raise");
 
   fm.clearFocus(otherWindow);
 
   // test to ensure that a synthetic event won't move focus
   var synevent = new FocusEvent("focus", {});
-  otherTextbox.inputField.dispatchEvent(synevent);
+  otherInput.dispatchEvent(synevent);
   is(synevent.type, "focus", "event.type after synthetic focus event");
-  is(synevent.target, otherTextbox, "event.target after synthetic focus event");
+  is(synevent.target, otherInput, "event.target after synthetic focus event");
   is(fm.focusedElement, null, "focusedElement after synthetic focus event");
   is(otherWindow.document.activeElement, otherWindow.document.documentElement,
      "document.activeElement after synthetic focus event");
 
   // check accessing a focus event after the event has finishing firing
   function continueTest(event) {
     is(event.type, "focus", "event.type after accessing focus event in timeout");
-    is(event.target, otherTextbox, "event.target after accessing focus event in timeout");
+    is(event.target, otherInput, "event.target after accessing focus event in timeout");
 
     gOldExpectedWindow = null;
     gNewExpectedWindow = null;
     otherWindow.close();
     framesetWindow.close();
 
     SimpleTest.waitForFocus(doWindowNoRootTest);
   }
 
-  function textboxFocused(event) {
-    otherTextbox.removeEventListener("focus", textboxFocused, true);
+  function inputFocused(event) {
+    otherInput.removeEventListener("focus", inputFocused, true);
     setTimeout(continueTest, 0, event);
   }
 
-  otherTextbox.addEventListener("focus", textboxFocused, true);
-  otherTextbox.focus();
+  otherInput.addEventListener("focus", inputFocused, true);
+  otherInput.focus();
 }
 
 // open a window with no root element
 var noRootWindow = null;
 function doWindowNoRootTest()
 {
   addEventListener("focus", doFrameSwitchingTests, true);
   noRootWindow = window.open("window_focus_inner.xul", "_blank", "chrome,width=100,height=100");
@@ -1444,17 +1444,17 @@ function doFrameSwitchingTests()
   gExpectedEvents = [[inputa, "blur", null, framea.contentWindow, window, framea],
                      [framea.contentDocument, "blur", null, null, window, null],
                      [framea.contentWindow, "blur", null, null, window, null],
                      [frameb.contentDocument, "focus", null, frameb.contentWindow, window, frameb],
                      [frameb.contentWindow, "focus", null, frameb.contentWindow, window, frameb],
                      [inputb, "focus", inputb, frameb.contentWindow, window, frameb]];
   inputb.focus();
   ok(gEventMatched && gExpectedEvents.length == 0, "frame switch from input to sibling frame");
-  is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), inputa, 
+  is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), inputa,
      "blurred frame still has input as focus");
 
   // focus a descendant in a sibling
   var inputd = framed.contentDocument.body.firstChild;
   gEventMatched = true;
   gExpectedEvents = [[inputb, "blur", null, frameb.contentWindow, window, frameb],
                      [frameb.contentDocument, "blur", null, null, window, null],
                      [frameb.contentWindow, "blur", null, null, window, null],
@@ -1689,17 +1689,17 @@ SimpleTest.waitForFocus(startTest);
       <button id="nohtab2" tabindex="7"/>
       <checkbox id="htab2" tabindex="0"/>
     </tabpanel>
   </tabpanels>
 </tabbox>
 <hbox>
 <panel>
   <button id="inpopup1" label="One"/>
-  <textbox label="Two"/>
+  <input xmlns="http://www.w3.org/1999/xhtml" label="Two"/>
 </panel>
 <description label="o" accesskey="v"/>
 <button id="t38"/>
 <!-- The 't' element tests end here so it doesn't matter that these elements are tabbable -->
 <label id="aj" value="j" accesskey="j" control="o9"/>
 <label id="ak" accesskey="k" control="n4">k</label>
 <checkbox id="o5"/><checkbox id="o7"/><hbox><checkbox id="o9"/></hbox>
 <checkbox id="o13"/><checkbox id="o15"/><checkbox id="o17"/><checkbox id="o19"/><checkbox id="o21"/><checkbox id="o23"/><checkbox id="o25"/>
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1199,42 +1199,52 @@ impl BatchBuilder {
                                     .map(&local_tile_clip_rect)
                                     .expect("bug: unable to map clip rect");
                                 let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round();
                                 let z_id = composite_config.z_generator.next();
                                 for key in &tile_cache.tiles_to_draw {
                                     let tile = &tile_cache.tiles[key];
                                     let device_rect = (tile.world_rect * ctx.global_device_pixel_scale).round();
                                     let surface = tile.surface.as_ref().expect("no tile surface set!");
-                                    let (surface, is_opaque) = match surface {
+                                    match surface {
                                         TileSurface::Color { color } => {
-                                            (CompositeTileSurface::Color { color: *color }, true)
+                                            composite_config.opaque_tiles.push(CompositeTile {
+                                                surface: CompositeTileSurface::Color { color: *color },
+                                                rect: device_rect,
+                                                clip_rect: device_clip_rect,
+                                                z_id,
+                                            });
+                                        }
+                                        TileSurface::Clear => {
+                                            composite_config.clear_tiles.push(CompositeTile {
+                                                surface: CompositeTileSurface::Clear,
+                                                rect: device_rect,
+                                                clip_rect: device_clip_rect,
+                                                z_id,
+                                            });
                                         }
                                         TileSurface::Texture { handle, .. } => {
                                             let cache_item = ctx.resource_cache.texture_cache.get(handle);
 
-                                            (
-                                                CompositeTileSurface::Texture {
+                                            let composite_tile = CompositeTile {
+                                                surface: CompositeTileSurface::Texture {
                                                     texture_id: cache_item.texture_id,
                                                     texture_layer: cache_item.texture_layer,
                                                 },
-                                                tile.is_opaque || tile_cache.is_opaque(),
-                                            )
+                                                rect: device_rect,
+                                                clip_rect: device_clip_rect,
+                                                z_id,
+                                            };
+
+                                            if tile.is_opaque || tile_cache.is_opaque() {
+                                                composite_config.opaque_tiles.push(composite_tile);
+                                            } else {
+                                                composite_config.alpha_tiles.push(composite_tile);
+                                            }
                                         }
-                                    };
-                                    let composite_tile = CompositeTile {
-                                        surface,
-                                        rect: device_rect,
-                                        clip_rect: device_clip_rect,
-                                        z_id,
-                                    };
-                                    if is_opaque {
-                                        composite_config.opaque_tiles.push(composite_tile);
-                                    } else {
-                                        composite_config.alpha_tiles.push(composite_tile);
                                     }
                                 }
                             }
                             PictureCompositeMode::Filter(ref filter) => {
                                 assert!(filter.is_visible());
                                 match filter {
                                     Filter::Blur(..) => {
                                         let kind = BatchKind::Brush(
--- a/gfx/wr/webrender/src/composite.rs
+++ b/gfx/wr/webrender/src/composite.rs
@@ -18,16 +18,17 @@ use crate::internal_types::TextureSource
 pub enum CompositeTileSurface {
     Texture {
         texture_id: TextureSource,
         texture_layer: i32,
     },
     Color {
         color: ColorF,
     },
+    Clear,
 }
 
 /// Describes the geometry and surface of a tile to be composited
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CompositeTile {
     pub surface: CompositeTileSurface,
     pub rect: DeviceRect,
@@ -36,20 +37,22 @@ pub struct CompositeTile {
 }
 
 /// The list of tiles to be drawn this frame
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CompositeConfig {
     pub opaque_tiles: Vec<CompositeTile>,
     pub alpha_tiles: Vec<CompositeTile>,
+    pub clear_tiles: Vec<CompositeTile>,
     pub z_generator: ZBufferIdGenerator,
 }
 
 impl CompositeConfig {
     pub fn new() -> Self {
         CompositeConfig {
             opaque_tiles: Vec::new(),
             alpha_tiles: Vec::new(),
+            clear_tiles: Vec::new(),
             z_generator: ZBufferIdGenerator::new(0),
         }
     }
 }
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -374,23 +374,25 @@ pub enum TileSurface {
         /// Handle to the texture cache entry which gets drawn to.
         handle: TextureCacheHandle,
         /// Bitfield specifying the dirty region(s) that are relevant to this tile.
         visibility_mask: PrimitiveVisibilityMask,
     },
     Color {
         color: ColorF,
     },
+    Clear,
 }
 
 impl TileSurface {
     fn kind(&self) -> &'static str {
         match *self {
             TileSurface::Color { .. } => "Color",
             TileSurface::Texture { .. } => "Texture",
+            TileSurface::Clear => "Clear",
         }
     }
 }
 
 /// Information about a cached tile.
 pub struct Tile {
     /// The current world rect of this tile.
     pub world_rect: WorldRect,
@@ -650,36 +652,43 @@ impl Tile {
         self.root.maybe_merge_or_split(
             0,
             &self.current_descriptor.prims,
             max_split_level,
         );
 
         // See if this tile is a simple color, in which case we can just draw
         // it as a rect, and avoid allocating a texture surface and drawing it.
-        let is_solid_color = self.current_descriptor.prims.len() == 1 && self.is_opaque;
+        let is_simple_prim = self.current_descriptor.prims.len() == 1 && self.is_opaque;
 
         // Set up the backing surface for this tile.
-        let mut surface = if is_solid_color {
+        let mut surface = if is_simple_prim {
             // If we determine the tile can be represented by a color, set the
             // surface unconditionally (this will drop any previously used
             // texture cache backing surface).
-            TileSurface::Color {
-                color: ctx.backdrop.color,
+            match ctx.backdrop.kind {
+                BackdropKind::Color { color } => {
+                    TileSurface::Color {
+                        color,
+                    }
+                }
+                BackdropKind::Clear => {
+                    TileSurface::Clear
+                }
             }
         } else {
             // If this tile will be backed by a surface, we want to retain
             // the texture handle from the previous frame, if possible. If
             // the tile was previously a color, or not set, then just set
             // up a new texture cache handle.
             match self.surface.take() {
                 Some(old_surface @ TileSurface::Texture { .. }) => {
                     old_surface
                 }
-                Some(TileSurface::Color { .. }) | None => {
+                Some(TileSurface::Color { .. }) | Some(TileSurface::Clear) | None => {
                     TileSurface::Texture {
                         handle: TextureCacheHandle::invalid(),
                         visibility_mask: PrimitiveVisibilityMask::empty(),
                     }
                 }
             }
         };
 
@@ -1062,32 +1071,42 @@ impl ::std::fmt::Display for RecordedDir
 }
 
 impl ::std::fmt::Debug for RecordedDirtyRegion {
     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
         ::std::fmt::Display::fmt(self, f)
     }
 }
 
+#[derive(Debug, Copy, Clone)]
+enum BackdropKind {
+    Color {
+        color: ColorF,
+    },
+    Clear,
+}
+
 /// Stores information about the calculated opaque backdrop of this slice.
 #[derive(Debug, Copy, Clone)]
 struct BackdropInfo {
     /// The picture space rectangle that is known to be opaque. This is used
     /// to determine where subpixel AA can be used, and where alpha blending
     /// can be disabled.
     rect: PictureRect,
-    /// Color of the backdrop.
-    color: ColorF,
+    /// Kind of the backdrop
+    kind: BackdropKind,
 }
 
 impl BackdropInfo {
     fn empty() -> Self {
         BackdropInfo {
             rect: PictureRect::zero(),
-            color: ColorF::BLACK,
+            kind: BackdropKind::Color {
+                color: ColorF::BLACK,
+            },
         }
     }
 }
 
 /// Represents a cache of tiles that make up a picture primitives.
 pub struct TileCacheInstance {
     /// Index of the tile cache / slice for this frame builder. It's determined
     /// by the setup_picture_caching method during flattening, which splits the
@@ -1614,17 +1633,19 @@ impl TileCacheInstance {
                         prim_spatial_node.coordinate_system_id == surface_spatial_node.coordinate_system_id
                     };
 
                     if let Some(ref clip_chain) = prim_clip_chain {
                         if prim_is_opaque && same_coord_system && !clip_chain.needs_mask && on_picture_surface {
                             if clip_chain.pic_clip_rect.contains_rect(&self.backdrop.rect) {
                                 self.backdrop = BackdropInfo {
                                     rect: clip_chain.pic_clip_rect,
-                                    color,
+                                    kind: BackdropKind::Color {
+                                        color,
+                                    },
                                 };
                             }
                         }
                     };
                 } else {
                     let opacity_binding = &opacity_binding_store[opacity_binding_index];
                     for binding in &opacity_binding.bindings {
                         prim_info.opacity_bindings.push(OpacityBinding::from(*binding));
@@ -1680,18 +1701,25 @@ impl TileCacheInstance {
 
                     if on_picture_surface && subpx_requested {
                         if !self.backdrop.rect.contains_rect(&prim_info.prim_clip_rect) {
                             self.subpixel_mode = SubpixelMode::Deny;
                         }
                     }
                 }
             }
+            PrimitiveInstanceKind::Clear { .. } => {
+                if let Some(ref clip_chain) = prim_clip_chain {
+                    self.backdrop = BackdropInfo {
+                        rect: clip_chain.pic_clip_rect,
+                        kind: BackdropKind::Clear,
+                    };
+                }
+            }
             PrimitiveInstanceKind::LineDecoration { .. } |
-            PrimitiveInstanceKind::Clear { .. } |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::LinearGradient { .. } |
             PrimitiveInstanceKind::RadialGradient { .. } |
             PrimitiveInstanceKind::Backdrop { .. } => {
                 // These don't contribute dependencies
             }
         };
 
@@ -2282,16 +2310,18 @@ bitflags! {
         /// Is a backdrop-filter cluster that requires special handling during post_update.
         const IS_BACKDROP_FILTER = 8;
         /// Force creation of a picture caching slice before this cluster.
         const CREATE_PICTURE_CACHE_PRE = 16;
         /// Force creation of a picture caching slice after this cluster.
         const CREATE_PICTURE_CACHE_POST = 32;
         /// If set, this cluster represents a scroll bar container.
         const SCROLLBAR_CONTAINER = 64;
+        /// If set, this cluster contains clear rectangle primitives.
+        const IS_CLEAR_PRIMITIVE = 128;
     }
 }
 
 /// Descriptor for a cluster of primitives. For now, this is quite basic but will be
 /// extended to handle more spatial clustering of primitives.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 pub struct PrimitiveCluster {
     /// The positioning node for this cluster.
@@ -2395,16 +2425,19 @@ impl PrimitiveList {
         // iterate all pictures in a given primitive list.
         match prim_instance.kind {
             PrimitiveInstanceKind::Picture { .. } => {
                 flags.insert(ClusterFlags::IS_PICTURE);
             }
             PrimitiveInstanceKind::Backdrop { .. } => {
                 flags.insert(ClusterFlags::IS_BACKDROP_FILTER);
             }
+            PrimitiveInstanceKind::Clear { .. } => {
+                flags.insert(ClusterFlags::IS_CLEAR_PRIMITIVE);
+            }
             _ => {}
         }
 
         if prim_flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE) {
             flags.insert(ClusterFlags::IS_BACKFACE_VISIBLE);
         }
 
         if prim_flags.contains(PrimitiveFlags::IS_SCROLLBAR_CONTAINER) {
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -3844,16 +3844,19 @@ impl Renderer {
         let mut instances = Vec::new();
 
         for tile in tiles_iter {
             // Work out the draw params based on the tile surface
             let (texture, layer, color) = match tile.surface {
                 CompositeTileSurface::Color { color } => {
                     (TextureSource::Dummy, 0.0, color)
                 }
+                CompositeTileSurface::Clear => {
+                    (TextureSource::Dummy, 0.0, ColorF::BLACK)
+                }
                 CompositeTileSurface::Texture { texture_id, texture_layer } => {
                     (texture_id, texture_layer as f32, ColorF::WHITE)
                 }
             };
             let textures = BatchTextures::color(texture);
 
             // Flush this batch if the textures aren't compatible
             if !current_textures.is_compatible_with(&textures) {
@@ -3920,16 +3923,28 @@ impl Renderer {
             self.set_blend(false, FramebufferKind::Main);
             self.draw_tile_list(
                 composite_config.opaque_tiles.iter().rev(),
                 stats,
             );
             self.gpu_profile.finish_sampler(opaque_sampler);
         }
 
+        if !composite_config.clear_tiles.is_empty() {
+            let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
+            self.device.disable_depth_write();
+            self.set_blend(true, FramebufferKind::Main);
+            self.device.set_blend_mode_premultiplied_dest_out();
+            self.draw_tile_list(
+                composite_config.clear_tiles.iter(),
+                stats,
+            );
+            self.gpu_profile.finish_sampler(transparent_sampler);
+        }
+
         // Draw alpha tiles
         if !composite_config.alpha_tiles.is_empty() {
             let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
             self.device.disable_depth_write();
             self.set_blend(true, FramebufferKind::Main);
             self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main);
             self.draw_tile_list(
                 composite_config.alpha_tiles.iter(),
--- a/gfx/wr/webrender/src/scene_building.rs
+++ b/gfx/wr/webrender/src/scene_building.rs
@@ -506,17 +506,19 @@ impl<'a> SceneBuilder<'a> {
         // If true, the cache is out of date and needs to be rebuilt.
         let mut update_shared_clips = true;
         // The last prim clip chain we build prim_clips for.
         let mut last_prim_clip_chain_id = ClipChainId::NONE;
 
         // Walk the supplied top level of clusters, slicing into slices as appropriate
         for cluster in main_prim_list.clusters.drain(..) {
             // Check if this cluster requires a new slice
-            create_slice |= cluster.flags.contains(ClusterFlags::CREATE_PICTURE_CACHE_PRE);
+            create_slice |= cluster.flags.intersects(
+                ClusterFlags::CREATE_PICTURE_CACHE_PRE | ClusterFlags::IS_CLEAR_PRIMITIVE
+            );
 
             if create_slice {
                 // When creating a slice, close off any open clip chains on prev slice.
                 if let Some(prev_slice) = slices.last_mut() {
                     prev_slice.pop_clip_instances(&clip_chain_instance_stack);
                 }
 
                 let mut slice = Slice {
@@ -603,17 +605,19 @@ impl<'a> SceneBuilder<'a> {
                         *shared_clips = Some(prim_clips.clone());
                     }
                 }
 
                 update_shared_clips = false;
             }
 
             // If this cluster creates a slice after, then note that for next cluster
-            create_slice |= cluster.flags.contains(ClusterFlags::CREATE_PICTURE_CACHE_POST);
+            create_slice |= cluster.flags.intersects(
+                ClusterFlags::CREATE_PICTURE_CACHE_POST | ClusterFlags::IS_CLEAR_PRIMITIVE
+            );
 
             // Finally, add this cluster to the current slice
             slices.last_mut().unwrap().prim_list.add_cluster(cluster);
         }
 
         // Close off any open clip chains on prev slice.
         if let Some(prev_slice) = slices.last_mut() {
             prev_slice.pop_clip_instances(&clip_chain_instance_stack);
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -79,17 +79,17 @@ typedef enum JSGCParamKey {
   JSGC_MAX_BYTES = 0,
 
   /**
    * Maximum size of the generational GC nurseries.
    *
    * This will be rounded to the nearest gc::ChunkSize.
    *
    * Pref: javascript.options.mem.nursery.max_kb
-   * Default: JS::DefaultNurseryBytes
+   * Default: JS::DefaultNurseryMaxBytes
    */
   JSGC_MAX_NURSERY_BYTES = 2,
 
   /** Amount of bytes allocated by the GC. */
   JSGC_BYTES = 3,
 
   /** Number of times GC has been invoked. Includes both major and minor GC. */
   JSGC_NUMBER = 4,
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -113,21 +113,21 @@ namespace JS {
 enum StackKind {
   StackForSystemCode,       // C++, such as the GC, running on behalf of the VM.
   StackForTrustedScript,    // Script running with trusted principals.
   StackForUntrustedScript,  // Script running with untrusted principals.
   StackKindCount
 };
 
 /*
- * Default size for the generational nursery in bytes.
- * This is the initial nursery size, when running in the browser this is
- * updated by JS_SetGCParameter().
+ * Default maximum size for the generational nursery in bytes. This is the
+ * initial value. In the browser this configured by the
+ * javascript.options.mem.nursery.max_kb pref.
  */
-const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
+const uint32_t DefaultNurseryMaxBytes = 16 * js::gc::ChunkSize;
 
 /* Default maximum heap size in bytes to pass to JS_NewContext(). */
 const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
 
 namespace shadow {
 
 struct Zone {
   enum GCState : uint8_t {
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -132,17 +132,17 @@ impl Runtime {
                     let is_debug_mozjs = cfg!(feature = "debugmozjs");
                     let diagnostic = JS::detail::InitWithFailureDiagnostic(is_debug_mozjs);
                     if !diagnostic.is_null() {
                         panic!("JS::detail::InitWithFailureDiagnostic failed: {}",
                                ffi::CStr::from_ptr(diagnostic).to_string_lossy());
                     }
 
                     let context = JS_NewContext(
-                        DEFAULT_HEAPSIZE, ChunkSize as u32, ptr::null_mut());
+                        DEFAULT_HEAPSIZE, ptr::null_mut());
                     assert!(!context.is_null());
                     JS::InitSelfHostedCode(context);
                     PARENT.set(context);
 
                     // The last JSRuntime child died, resume the execution by destroying the parent.
                     rx.recv().unwrap();
                     let cx = PARENT.get();
                     JS_DestroyContext(cx);
@@ -155,17 +155,16 @@ impl Runtime {
                 while PARENT.get().is_null() {
                     thread::yield_now();
                 }
             });
 
             assert_eq!(IsDebugBuild(), cfg!(feature = "debugmozjs"));
 
             let js_context = JS_NewContext(DEFAULT_HEAPSIZE,
-                                           ChunkSize as u32,
                                            JS_GetParentRuntime(PARENT.get()));
             assert!(!js_context.is_null());
 
             // Unconstrain the runtime's threshold on nominal heap size, to avoid
             // triggering GC too often if operating continuously near an arbitrary
             // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
             // still in effect to cause periodical, and we hope hygienic,
             // last-ditch GCs from within the GC's allocator.
@@ -554,22 +553,16 @@ impl Default for jsid {
 impl Default for JS::Value {
     fn default() -> JS::Value { jsval::UndefinedValue() }
 }
 
 impl Default for JS::RealmOptions {
     fn default() -> Self { unsafe { ::std::mem::zeroed() } }
 }
 
-const ChunkShift: usize = 20;
-const ChunkSize: usize = 1 << ChunkShift;
-
-#[cfg(target_pointer_width = "32")]
-const ChunkLocationOffset: usize = ChunkSize - 2 * 4 - 8;
-
 pub trait GCMethods {
     unsafe fn initial() -> Self;
     unsafe fn write_barriers(v: *mut Self, prev: Self, next: Self);
 }
 
 impl GCMethods for jsid {
     unsafe fn initial() -> jsid { Default::default() }
     unsafe fn write_barriers(_: *mut jsid, _: jsid, _: jsid) {}
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1197,25 +1197,23 @@ void js::gc::DumpArenaInfo() {
     fprintf(stderr, "%25s %8zu %8zu %8zu\n", AllocKindName(kind),
             Arena::thingSize(kind), Arena::thingsPerArena(kind),
             Arena::firstThingOffset(kind) - ArenaHeaderSize);
   }
 }
 
 #endif  // JS_GC_ZEAL
 
-bool GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) {
+bool GCRuntime::init(uint32_t maxbytes) {
   MOZ_ASSERT(SystemPageSize());
 
   {
     AutoLockGCBgAlloc lock(rt);
 
     MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_BYTES, maxbytes, lock));
-    MOZ_ALWAYS_TRUE(
-        tunables.setParameter(JSGC_MAX_NURSERY_BYTES, maxNurseryBytes, lock));
 
     const char* size = getenv("JSGC_MARK_STACK_LIMIT");
     if (size) {
       setMarkStackLimit(atoi(size), lock);
     }
 
     if (!nursery().init(lock)) {
       return false;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -235,17 +235,17 @@ class ZoneList {
   ZoneList& operator=(const ZoneList& other) = delete;
 };
 
 class GCRuntime {
   friend GCMarker::MarkQueueProgress GCMarker::processMarkQueue();
 
  public:
   explicit GCRuntime(JSRuntime* rt);
-  MOZ_MUST_USE bool init(uint32_t maxbytes, uint32_t maxNurseryBytes);
+  MOZ_MUST_USE bool init(uint32_t maxbytes);
   void finishRoots();
   void finish();
 
   inline bool hasZealMode(ZealMode mode);
   inline void clearZealMode(ZealMode mode);
   inline bool upcomingZealousGC();
   inline bool needZealousGC();
   inline bool hasIncrementalTwoSliceZealMode();
--- a/js/src/gc/Scheduling.cpp
+++ b/js/src/gc/Scheduling.cpp
@@ -33,17 +33,17 @@ static constexpr float LowFrequencyEager
  */
 static constexpr float MinHeapGrowthFactor =
     1.0f / Min(HighFrequencyEagerAllocTriggerFactor,
                LowFrequencyEagerAllocTriggerFactor);
 
 GCSchedulingTunables::GCSchedulingTunables()
     : gcMaxBytes_(0),
       gcMinNurseryBytes_(TuningDefaults::GCMinNurseryBytes),
-      gcMaxNurseryBytes_(0),
+      gcMaxNurseryBytes_(JS::DefaultNurseryMaxBytes),
       gcZoneAllocThresholdBase_(TuningDefaults::GCZoneAllocThresholdBase),
       nonIncrementalFactor_(TuningDefaults::NonIncrementalFactor),
       avoidInterruptFactor_(TuningDefaults::AvoidInterruptFactor),
       zoneAllocDelayBytes_(TuningDefaults::ZoneAllocDelayBytes),
       dynamicHeapGrowthEnabled_(TuningDefaults::DynamicHeapGrowthEnabled),
       highFrequencyThreshold_(
           TimeDuration::FromSeconds(TuningDefaults::HighFrequencyThreshold)),
       highFrequencyLowLimitBytes_(TuningDefaults::HighFrequencyLowLimitBytes),
@@ -272,17 +272,17 @@ void GCSchedulingTunables::resetParamete
   switch (key) {
     case JSGC_MAX_BYTES:
       gcMaxBytes_ = 0xffffffff;
       break;
     case JSGC_MIN_NURSERY_BYTES:
     case JSGC_MAX_NURSERY_BYTES:
       // Reset these togeather to maintain their min <= max invariant.
       gcMinNurseryBytes_ = TuningDefaults::GCMinNurseryBytes;
-      gcMaxNurseryBytes_ = JS::DefaultNurseryBytes;
+      gcMaxNurseryBytes_ = JS::DefaultNurseryMaxBytes;
       break;
     case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
       highFrequencyThreshold_ =
           TimeDuration::FromSeconds(TuningDefaults::HighFrequencyThreshold);
       break;
     case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
       setHighFrequencyLowLimit(TuningDefaults::HighFrequencyLowLimitBytes);
       break;
--- a/js/src/jsapi-tests/testGCOutOfMemory.cpp
+++ b/js/src/jsapi-tests/testGCOutOfMemory.cpp
@@ -59,19 +59,20 @@ BEGIN_TEST(testGCOutOfMemory) {
 virtual JSContext* createContext() override {
   // Note that the max nursery size must be less than the whole heap size, or
   // the test will fail because 'max' (the number of allocations required for
   // OOM) will be based on the nursery size, and that will overflow the
   // tenured heap, which will cause the second pass with max/4 allocations to
   // OOM. (Actually, this only happens with nursery zeal, because normally
   // the nursery will start out with only a single chunk before triggering a
   // major GC.)
-  JSContext* cx = JS_NewContext(1024 * 1024, js::gc::ChunkSize);
+  JSContext* cx = JS_NewContext(1024 * 1024);
   if (!cx) {
     return nullptr;
   }
+  JS_SetGCParameter(cx, JSGC_MAX_NURSERY_BYTES, js::gc::ChunkSize);
   setNativeStackQuota(cx);
   return cx;
 }
 
 virtual void destroyContext() override { JS_DestroyContext(cx); }
 
 END_TEST(testGCOutOfMemory)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -360,27 +360,26 @@ JS_PUBLIC_API bool JS_IsFunctionBound(JS
 
 JS_PUBLIC_API JSObject* JS_GetBoundFunctionTarget(JSFunction* fun) {
   return fun->isBoundFunction() ? fun->getBoundFunctionTarget() : nullptr;
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API JSContext* JS_NewContext(uint32_t maxbytes,
-                                       uint32_t maxNurseryBytes,
                                        JSRuntime* parentRuntime) {
   MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running,
              "must call JS_Init prior to creating any JSContexts");
 
   // Make sure that all parent runtimes are the topmost parent.
   while (parentRuntime && parentRuntime->parentRuntime) {
     parentRuntime = parentRuntime->parentRuntime;
   }
 
-  return NewContext(maxbytes, maxNurseryBytes, parentRuntime);
+  return NewContext(maxbytes, parentRuntime);
 }
 
 JS_PUBLIC_API JSContext* JS_NewCooperativeContext(JSContext* siblingContext) {
   MOZ_CRASH("Cooperative scheduling is unsupported");
 }
 
 JS_PUBLIC_API void JS_YieldCooperativeContext(JSContext* cx) {
   MOZ_CRASH("Cooperative scheduling is unsupported");
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -333,18 +333,17 @@ extern JS_PUBLIC_API bool JS_IsBuiltinFu
  * be created, in a single-threaded fashion.  Otherwise the behavior of the
  * library is undefined.
  * See:
  * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference
  */
 
 // Create a new context (and runtime) for this thread.
 extern JS_PUBLIC_API JSContext* JS_NewContext(
-    uint32_t maxbytes, uint32_t maxNurseryBytes = JS::DefaultNurseryBytes,
-    JSRuntime* parentRuntime = nullptr);
+    uint32_t maxbytes, JSRuntime* parentRuntime = nullptr);
 
 // The methods below for controlling the active context in a cooperatively
 // multithreaded runtime are not threadsafe, and the caller must ensure they
 // are called serially if there is a chance for contention between threads.
 
 // Called from the active context for a runtime, yield execution so that
 // this context is no longer active and can no longer use the API.
 extern JS_PUBLIC_API void JS_YieldCooperativeContext(JSContext* cx);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4019,22 +4019,23 @@ static void DestroyShellCompartmentPriva
 }
 
 static void SetWorkerContextOptions(JSContext* cx);
 static bool ShellBuildId(JS::BuildIdCharVector* buildId);
 
 static void WorkerMain(WorkerInput* input) {
   MOZ_ASSERT(input->parentRuntime);
 
-  JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L,
-                                input->parentRuntime);
+  JSContext* cx = JS_NewContext(8L * 1024L * 1024L, input->parentRuntime);
   if (!cx) {
     return;
   }
 
+  JS_SetGCParameter(cx, JSGC_MAX_NURSERY_BYTES, 2L * 1024L * 1024L);
+
   ShellContext* sc = js_new<ShellContext>(cx);
   if (!sc) {
     return;
   }
 
   auto guard = mozilla::MakeScopeExit([&] {
     CancelOffThreadJobsForContext(cx);
     sc->markObservers.reset();
@@ -11254,17 +11255,18 @@ int main(int argc, char** argv, char** e
       !op.addBoolOption('\0', "mips-sim-icache-checks",
                         "Enable icache flush checks in the MIPS "
                         "simulator.") ||
       !op.addIntOption('\0', "mips-sim-stop-at", "NUMBER",
                        "Stop the MIPS simulator after the given "
                        "NUMBER of instructions.",
                        -1) ||
       !op.addIntOption('\0', "nursery-size", "SIZE-MB",
-                       "Set the maximum nursery size in MB", 16) ||
+                       "Set the maximum nursery size in MB",
+                       JS::DefaultNurseryMaxBytes / 1024 / 1024) ||
 #ifdef JS_GC_ZEAL
       !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
                           gc::ZealModeHelpText) ||
 #else
       !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
                           "option ignored in non-gc-zeal builds") ||
 #endif
       !op.addStringOption('\0', "module-load-path", "DIR",
@@ -11380,24 +11382,25 @@ int main(int argc, char** argv, char** e
   int32_t cpuCount = op.getIntOption("cpu-count");  // What we're really setting
   if (cpuCount < 0) {
     cpuCount = op.getIntOption("thread-count");  // Legacy name
   }
   if (cpuCount >= 0 && !SetFakeCPUCount(cpuCount)) {
     return 1;
   }
 
-  size_t nurseryBytes = JS::DefaultNurseryBytes;
-  nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
-
   /* Use the same parameters as the browser in xpcjsruntime.cpp. */
-  JSContext* const cx = JS_NewContext(JS::DefaultHeapMaxBytes, nurseryBytes);
+  JSContext* const cx = JS_NewContext(JS::DefaultHeapMaxBytes);
   if (!cx) {
     return 1;
   }
+
+  size_t nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
+  JS_SetGCParameter(cx, JSGC_MAX_NURSERY_BYTES, nurseryBytes);
+
   auto destroyCx = MakeScopeExit([cx] { JS_DestroyContext(cx); });
 
   UniquePtr<ShellContext> sc = MakeUnique<ShellContext>(cx);
   if (!sc) {
     return 1;
   }
   auto destroyShellContext = MakeScopeExit([cx, &sc] {
     // Must clear out some of sc's pointer containers before JS_DestroyContext.
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -130,18 +130,17 @@ bool JSContext::init(ContextKind kind) {
 
   // Set the ContextKind last, so that ProtectedData checks will allow us to
   // initialize this context before it becomes the runtime's active context.
   kind_ = kind;
 
   return true;
 }
 
-JSContext* js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes,
-                          JSRuntime* parentRuntime) {
+JSContext* js::NewContext(uint32_t maxBytes, JSRuntime* parentRuntime) {
   AutoNoteSingleThreadedRegion anstr;
 
   MOZ_RELEASE_ASSERT(!TlsContext.get());
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
   js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
                                         : js::THREAD_TYPE_WORKER);
 #endif
@@ -158,17 +157,17 @@ JSContext* js::NewContext(uint32_t maxBy
   }
 
   if (!cx->init(ContextKind::MainThread)) {
     js_delete(cx);
     js_delete(runtime);
     return nullptr;
   }
 
-  if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
+  if (!runtime->init(cx, maxBytes)) {
     runtime->destroyRuntime();
     js_delete(cx);
     js_delete(runtime);
     return nullptr;
   }
 
   return cx;
 }
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -1044,18 +1044,17 @@ struct MOZ_RAII AutoResolving {
   AutoResolving* const link;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * Create and destroy functions for JSContext, which is manually allocated
  * and exclusively owned.
  */
-extern JSContext* NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes,
-                             JSRuntime* parentRuntime);
+extern JSContext* NewContext(uint32_t maxBytes, JSRuntime* parentRuntime);
 
 extern void DestroyContext(JSContext* cx);
 
 enum ErrorArgumentsType {
   ArgumentsAreUnicode,
   ArgumentsAreASCII,
   ArgumentsAreLatin1,
   ArgumentsAreUTF8
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -188,32 +188,31 @@ JSRuntime::~JSRuntime() {
   MOZ_ASSERT(offThreadParsesRunning_ == 0);
   MOZ_ASSERT(!offThreadParsingBlocked_);
 
   MOZ_ASSERT(numRealms == 0);
   MOZ_ASSERT(numDebuggeeRealms_ == 0);
   MOZ_ASSERT(numDebuggeeRealmsObservingCoverage_ == 0);
 }
 
-bool JSRuntime::init(JSContext* cx, uint32_t maxbytes,
-                     uint32_t maxNurseryBytes) {
+bool JSRuntime::init(JSContext* cx, uint32_t maxbytes) {
 #ifdef DEBUG
   MOZ_ASSERT(!initialized_);
   initialized_ = true;
 #endif
 
   if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized()) {
     return false;
   }
 
   mainContext_ = cx;
 
   defaultFreeOp_ = cx->defaultFreeOp();
 
-  if (!gc.init(maxbytes, maxNurseryBytes)) {
+  if (!gc.init(maxbytes)) {
     return false;
   }
 
   UniquePtr<Zone> atomsZone = MakeUnique<Zone>(this);
   if (!atomsZone || !atomsZone->init(true)) {
     return false;
   }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -844,17 +844,17 @@ struct JSRuntime {
 
   explicit JSRuntime(JSRuntime* parentRuntime);
   ~JSRuntime();
 
   // destroyRuntime is used instead of a destructor, to ensure the downcast
   // to JSContext remains valid. The final GC triggered here depends on this.
   void destroyRuntime();
 
-  bool init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes);
+  bool init(JSContext* cx, uint32_t maxbytes);
 
   JSRuntime* thisFromCtor() { return this; }
 
  public:
   void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); }
 
   /*
    * This should be called after system malloc/calloc/realloc returns nullptr
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -1095,17 +1095,17 @@ CycleCollectedJSRuntime* XPCJSContext::C
 }
 
 nsresult XPCJSContext::Initialize(XPCJSContext* aPrimaryContext) {
   nsresult rv;
   if (aPrimaryContext) {
     rv = CycleCollectedJSContext::InitializeNonPrimary(aPrimaryContext);
   } else {
     rv = CycleCollectedJSContext::Initialize(nullptr, JS::DefaultHeapMaxBytes,
-                                             JS::DefaultNurseryBytes);
+                                             JS::DefaultNurseryMaxBytes);
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(Context());
   JSContext* cx = Context();
 
--- a/xpcom/base/CycleCollectedJSContext.cpp
+++ b/xpcom/base/CycleCollectedJSContext.cpp
@@ -150,21 +150,23 @@ void CycleCollectedJSContext::Initialize
 }
 
 nsresult CycleCollectedJSContext::Initialize(JSRuntime* aParentRuntime,
                                              uint32_t aMaxBytes,
                                              uint32_t aMaxNurseryBytes) {
   MOZ_ASSERT(!mJSContext);
 
   mozilla::dom::InitScriptSettings();
-  mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentRuntime);
+  mJSContext = JS_NewContext(aMaxBytes, aParentRuntime);
   if (!mJSContext) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  JS_SetGCParameter(mJSContext, JSGC_MAX_NURSERY_BYTES, aMaxNurseryBytes);
+
   mRuntime = CreateRuntime(mJSContext);
 
   InitializeCommon();
 
   nsCycleCollector_registerJSContext(this);
 
   return NS_OK;
 }