Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Mon, 14 Jan 2019 23:51:26 +0200
changeset 453849 8ec327de0ba7c25c185b887a57877657bda20d47
parent 453786 50b3268954b1300941453d11934d032478425a55 (current diff)
parent 453848 43022e5e375774afce6db2316028e16bb09a9ec9 (diff)
child 453850 7f08c65a8bd31ec37dd3b15df3de548b3b591bb4
child 453896 45294093578dfcc64b1bb4f7610c186be78485ed
push id76039
push usercbrindusan@mozilla.com
push dateMon, 14 Jan 2019 21:54:07 +0000
treeherderautoland@7f08c65a8bd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
8ec327de0ba7 / 66.0a1 / 20190114215201 / files
nightly linux64
8ec327de0ba7 / 66.0a1 / 20190114215201 / files
nightly mac
8ec327de0ba7 / 66.0a1 / 20190114215201 / files
nightly win32
8ec327de0ba7 / 66.0a1 / 20190114215201 / files
nightly win64
8ec327de0ba7 / 66.0a1 / 20190114215201 / 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 inbound to mozilla-central. a=merge
--- a/devtools/client/inspector/rules/reducers/rules.js
+++ b/devtools/client/inspector/rules/reducers/rules.js
@@ -61,16 +61,18 @@ function getRuleState(rule) {
     // An object containing information about the CSS rule's inheritance.
     inheritance: rule.inheritance,
     // Whether or not the rule does not match the current selected element.
     isUnmatched: rule.isUnmatched,
     // Whether or not the rule is an user agent style.
     isUserAgentStyle: rule.isSystem,
     // An object containing information about the CSS keyframes rules.
     keyframesRule: rule.keyframesRule,
+    // The pseudo-element keyword used in the rule.
+    pseudoElement: rule.pseudoElement,
     // An object containing information about the CSS rule's selector.
     selector: rule.selector,
     // An object containing information about the CSS rule's stylesheet source.
     sourceLink: rule.sourceLink,
     // The CSS rule type.
     type: rule.domRule.type,
   };
 }
--- a/devtools/client/inspector/rules/types.js
+++ b/devtools/client/inspector/rules/types.js
@@ -98,16 +98,19 @@ exports.rule = {
   // An object containing information about the CSS keyframes rules.
   keyframesRule: PropTypes.shape({
     // The actor ID of the keyframes rule.
     id: PropTypes.string,
     // The keyframes rule name.
     keyframesName: PropTypes.string,
   }),
 
+  // The pseudo-element keyword used in the rule.
+  pseudoElement: PropTypes.string,
+
   // An object containing information about the CSS rule's selector.
   selector: PropTypes.shape(selector),
 
   // An object containing information about the CSS rule's stylesheet source.
   sourceLink: PropTypes.shape(sourceLink),
 
   // The CSS rule type.
   type: PropTypes.number,
--- a/devtools/client/locales/en-US/webconsole.properties
+++ b/devtools/client/locales/en-US/webconsole.properties
@@ -187,16 +187,21 @@ webconsole.menu.selectAll.label=Select a
 webconsole.menu.selectAll.accesskey=A
 
 # LOCALIZATION NOTE (webconsole.menu.openInSidebar.label)
 # Label used for a context-menu item displayed for object/variable logs. Clicking on it
 # opens the webconsole sidebar for the logged variable.
 webconsole.menu.openInSidebar.label=Open in sidebar
 webconsole.menu.openInSidebar.accesskey=V
 
+# LOCALIZATION NOTE (webconsole.menu.exportClipboard.label)
+# Label used for a context-menu item displayed on the output. Clicking on it
+# copies the entire output of the console to the clipboard.
+webconsole.menu.exportClipboard.label=Export visible messages to clipboard
+
 # LOCALIZATION NOTE (webconsole.menu.timeWarp.label)
 # Label used for a context-menu item displayed for any log. Clicking on it will
 # jump to the execution point where the log item was generated.
 webconsole.menu.timeWarp.label=Jump here
 
 # LOCALIZATION NOTE (webconsole.jumpButton.tooltip)
 # Label used for the tooltip on the "jump" button in the console. It's displayed when
 # the user recorded execution with WebReplay, is now paused in the debugger, and hover a
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -278,16 +278,18 @@ skip-if = true # Bug 1405250
 [browser_webconsole_console_dir.js]
 [browser_webconsole_console_dir_uninspectable.js]
 [browser_webconsole_console_error_expand_object.js]
 [browser_webconsole_console_group.js]
 [browser_webconsole_console_logging_workers_api.js]
 skip-if = e10s # SharedWorkers console events are not received on the current process because they could run on any process.
 [browser_webconsole_console_table.js]
 [browser_webconsole_console_trace_duplicates.js]
+[browser_webconsole_context_menu_export_console_output_clipboard.js]
+subsuite = clipboard
 [browser_webconsole_context_menu_copy_entire_message.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_webconsole_context_menu_copy_link_location.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) || (os == 'linux') # bug 1328915, disable linux32 debug devtools for timeouts, bug 1473120
 [browser_webconsole_context_menu_copy_object.js]
 subsuite = clipboard
--- a/devtools/client/webconsole/test/mochitest/browser_console_context_menu_entries.js
+++ b/devtools/client/webconsole/test/mochitest/browser_console_context_menu_entries.js
@@ -39,16 +39,17 @@ async function performTests() {
 
   let expectedContextMenu = addPrefBasedEntries([
     "#console-menu-copy-url (a)",
     "#console-menu-open-url (T)",
     "#console-menu-store (S) [disabled]",
     "#console-menu-copy (C)",
     "#console-menu-copy-object (o) [disabled]",
     "#console-menu-select (A)",
+    "#console-menu-export-clipboard ()",
   ]);
   is(getSimplifiedContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
     "The context menu has the expected entries for a network message");
 
   info("Logging a text message in the content window");
   const onLogMessage = waitForMessage(hud, "simple text message");
   ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
     content.wrappedJSObject.console.log("simple text message");
@@ -58,16 +59,17 @@ async function performTests() {
   menuPopup = await openContextMenu(hud, logMessage.node);
   ok(menuPopup, "The context menu is displayed on a log message");
 
   expectedContextMenu = addPrefBasedEntries([
     "#console-menu-store (S) [disabled]",
     "#console-menu-copy (C)",
     "#console-menu-copy-object (o) [disabled]",
     "#console-menu-select (A)",
+    "#console-menu-export-clipboard ()",
   ]);
   is(getSimplifiedContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
     "The context menu has the expected entries for a simple log message");
 
   menuPopup = await openContextMenu(hud, hud.jsterm.node || hud.jsterm.inputNode);
 
   expectedContextMenu = [
     "#editmenu-undo (editmenu-undo) [disabled]",
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_context_menu_export_console_output_clipboard.js
@@ -0,0 +1,69 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+`data:text/html;charset=utf-8,` +
+`<script>window.logStuff = function () {console.log("hello");};</script>`;
+const TEST_DATA = {
+  msg: "simple text message",
+  msg2: "second message test",
+};
+
+// Test the export visible messages to clipboard of the webconsole copies the expected
+// clipboard text for different log messages to find if everything is copied to clipboard.
+
+add_task(async function() {
+  const hud = await openNewTabAndConsole(TEST_URI);
+  hud.ui.clearOutput();
+
+  info("Call the log function defined in the test page");
+  await ContentTask.spawn(gBrowser.selectedBrowser, TEST_DATA, function(testData) {
+    content.wrappedJSObject.console.log(testData.msg);
+    content.wrappedJSObject.console.log(testData.msg2);
+    content.wrappedJSObject.console.log("object:", {a: 1},
+                                        "array:", ["b", "c"]);
+    content.wrappedJSObject.logStuff();
+  });
+
+  info("Test export to clipboard ");
+  await waitFor(() => findMessages(hud, "").length === 4);
+  const message = findMessage(hud, TEST_DATA.msg);
+  const clipboardText = await exportAllToClipboard(hud, message);
+  ok(true, "Clipboard text was found and saved");
+
+  const clipboardLines = clipboardText.split("\n");
+  info("Check if all messages where copied to clipboard");
+  is(clipboardLines[0].trim(), TEST_DATA.msg,
+    "found first text message in clipboard");
+  is(clipboardLines[1].trim(), TEST_DATA.msg2,
+    "found second text message in clipboard");
+  is(clipboardLines[2].trim(), 'object: Object { a: 1 } array: Array [ "b", "c" ]',
+    "found object and array in clipboard");
+  const CLEAN_URI = TEST_URI.replace("text/html;charset=utf-8,", "");
+  is(clipboardLines[3].trim(), `hello ${CLEAN_URI}:1:32`,
+    "found text from data uri");
+});
+
+/**
+ * Simple helper method to open the context menu on a given message, and click on the
+ * export visible messages to clipboard.
+ */
+async function exportAllToClipboard(hud, message) {
+  const menuPopup = await openContextMenu(hud, message);
+  const exportClipboard = menuPopup.querySelector("#console-menu-export-clipboard");
+  ok(exportClipboard, "copy menu item is enabled");
+
+  let clipboardText;
+  await waitForClipboardPromise(
+    () => exportClipboard.click(),
+    data => {
+      clipboardText = data;
+      return data;
+    }
+  );
+  return clipboardText;
+}
--- a/devtools/client/webconsole/utils/context-menu.js
+++ b/devtools/client/webconsole/utils/context-menu.js
@@ -165,16 +165,27 @@ function createContextMenu(hud, parentNo
     accesskey: l10n.getStr("webconsole.menu.selectAll.accesskey"),
     disabled: false,
     click: () => {
       const webconsoleOutput = parentNode.querySelector(".webconsole-output");
       selection.selectAllChildren(webconsoleOutput);
     },
   }));
 
+  // Export to clipboard
+  menu.append(new MenuItem({
+    id: "console-menu-export-clipboard",
+    label: l10n.getStr("webconsole.menu.exportClipboard.label"),
+    disabled: false,
+    click: () => {
+      const webconsoleOutput = parentNode.querySelector(".webconsole-output");
+      clipboardHelper.copyString(webconsoleOutput.textContent);
+    },
+  }));
+
   // Open object in sidebar.
   if (openSidebar) {
     menu.append(new MenuItem({
       id: "console-menu-open-sidebar",
       label: l10n.getStr("webconsole.menu.openInSidebar.label"),
       accesskey: l10n.getStr("webconsole.menu.openInSidebar.accesskey"),
       disabled: !rootActorId,
       click: () => openSidebar(message.messageId),
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -7763,16 +7763,29 @@ exports.CSS_PROPERTIES = {
     ],
     "supports": [],
     "values": [
       "inherit",
       "initial",
       "unset"
     ]
   },
+  "padding-block": {
+    "isInherited": false,
+    "subproperties": [
+      "padding-block-start",
+      "padding-block-end"
+    ],
+    "supports": [],
+    "values": [
+      "inherit",
+      "initial",
+      "unset"
+    ]
+  },
   "padding-block-end": {
     "isInherited": false,
     "subproperties": [
       "padding-block-end"
     ],
     "supports": [],
     "values": [
       "inherit",
@@ -7799,16 +7812,29 @@ exports.CSS_PROPERTIES = {
     ],
     "supports": [],
     "values": [
       "inherit",
       "initial",
       "unset"
     ]
   },
+  "padding-inline": {
+    "isInherited": false,
+    "subproperties": [
+      "padding-inline-start",
+      "padding-inline-end"
+    ],
+    "supports": [],
+    "values": [
+      "inherit",
+      "initial",
+      "unset"
+    ]
+  },
   "padding-inline-end": {
     "isInherited": false,
     "subproperties": [
       "padding-inline-end"
     ],
     "supports": [],
     "values": [
       "inherit",
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -304,16 +304,31 @@ SharedSurfacesChild::AsSourceSurfaceShar
     MOZ_ASSERT(data);
     aKey = data->UpdateKey(aManager, aResources, dirtyRect);
   }
 
   return rv;
 }
 
 /* static */ nsresult SharedSurfacesChild::Share(
+    SourceSurface* aSurface, RenderRootStateManager* aManager,
+    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aSurface);
+  MOZ_ASSERT(aManager);
+
+  auto sharedSurface = AsSourceSurfaceSharedData(aSurface);
+  if (!sharedSurface) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  return Share(sharedSurface, aManager, aResources, aKey);
+}
+
+/* static */ nsresult SharedSurfacesChild::Share(
     ImageContainer* aContainer, RenderRootStateManager* aManager,
     wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aContainer);
   MOZ_ASSERT(aManager);
 
   if (aContainer->IsAsync()) {
     return NS_ERROR_NOT_IMPLEMENTED;
--- a/gfx/layers/ipc/SharedSurfacesChild.h
+++ b/gfx/layers/ipc/SharedSurfacesChild.h
@@ -67,16 +67,26 @@ class SharedSurfacesChild final {
    * This must be called from the main thread.
    */
   static nsresult Share(gfx::SourceSurfaceSharedData* aSurface,
                         RenderRootStateManager* aManager,
                         wr::IpcResourceUpdateQueue& aResources,
                         wr::ImageKey& aKey);
 
   /**
+   * Request that the surface be mapped into the compositor thread's memory
+   * space, and a valid ImageKey be generated for it for use with WebRender.
+   * This must be called from the main thread.
+   */
+  static nsresult Share(gfx::SourceSurface* aSurface,
+                        RenderRootStateManager* aManager,
+                        wr::IpcResourceUpdateQueue& aResources,
+                        wr::ImageKey& aKey);
+
+  /**
    * Request that the first surface in the image container's current images be
    * mapped into the compositor thread's memory space, and a valid ImageKey be
    * generated for it for use with WebRender. If a different method should be
    * used to share the image data for this particular container, it will return
    * NS_ERROR_NOT_IMPLEMENTED. This must be called from the main thread.
    */
   static nsresult Share(ImageContainer* aContainer,
                         RenderRootStateManager* aManager,
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderCommandBuilder.h"
 
 #include "BasicLayers.h"
 #include "mozilla/AutoRestore.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/Types.h"
 #include "mozilla/layers/ClipManager.h"
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
@@ -179,28 +180,24 @@ static void DestroyBlobGroupDataProperty
 
 static void TakeExternalSurfaces(
     WebRenderDrawEventRecorder* aRecorder,
     std::vector<RefPtr<SourceSurface>>& aExternalSurfaces,
     RenderRootStateManager* aManager, wr::IpcResourceUpdateQueue& aResources) {
   aRecorder->TakeExternalSurfaces(aExternalSurfaces);
 
   for (auto& surface : aExternalSurfaces) {
-    if (surface->GetType() != SurfaceType::DATA_SHARED) {
-      MOZ_ASSERT_UNREACHABLE("External surface that is not a shared surface!");
-      continue;
-    }
-
     // While we don't use the image key with the surface, because the blob image
     // renderer doesn't have easy access to the resource set, we still want to
     // ensure one is generated. That will ensure the surface remains alive until
     // at least the last epoch which the blob image could be used in.
     wr::ImageKey key;
-    auto sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
-    SharedSurfacesChild::Share(sharedSurface, aManager, aResources, key);
+    DebugOnly<nsresult> rv =
+        SharedSurfacesChild::Share(surface, aManager, aResources, key);
+    MOZ_ASSERT(rv.value != NS_ERROR_NOT_IMPLEMENTED);
   }
 }
 
 struct DIGroup;
 struct Grouper {
   explicit Grouper(ClipManager& aClipManager)
       : mAppUnitsPerDevPixel(0),
         mDisplayListBuilder(nullptr),
--- a/image/AnimationFrameBuffer.cpp
+++ b/image/AnimationFrameBuffer.cpp
@@ -347,19 +347,17 @@ void AnimationFrameRecyclingQueue::Advan
   }
 
   RefPtr<imgFrame>& front = mDisplay.front();
   RecycleEntry newEntry(mForceUseFirstFrameRefreshArea ? mFirstFrameRefreshArea
                                                        : front->GetDirtyRect());
 
   // If we are allowed to recycle the frame, then we should save it before the
   // base class's AdvanceInternal discards it.
-  if (front->ShouldRecycle()) {
-    newEntry.mFrame = std::move(front);
-  }
+  newEntry.mFrame = std::move(front);
 
   // Even if the frame itself isn't saved, we want the dirty rect to calculate
   // the recycle rect for future recycled frames.
   mRecycle.push_back(std::move(newEntry));
   mDisplay.pop_front();
   MOZ_ASSERT(!mDisplay.empty());
   MOZ_ASSERT(mDisplay.front());
 
@@ -388,21 +386,19 @@ void AnimationFrameRecyclingQueue::Advan
 bool AnimationFrameRecyclingQueue::ResetInternal() {
   // We should save any display frames that we can to save on at least the
   // allocation. The first frame refresh area is guaranteed to be the aggregate
   // dirty rect or the entire frame, and so the bare minimum area we can
   // recycle. We don't need to worry about updating the dirty rect for the
   // existing mRecycle entries, because that will happen in RecycleFrame when
   // we try to pull out a frame to redecode the first frame.
   for (RefPtr<imgFrame>& frame : mDisplay) {
-    if (frame->ShouldRecycle()) {
-      RecycleEntry newEntry(mFirstFrameRefreshArea);
-      newEntry.mFrame = std::move(frame);
-      mRecycle.push_back(std::move(newEntry));
-    }
+    RecycleEntry newEntry(mFirstFrameRefreshArea);
+    newEntry.mFrame = std::move(frame);
+    mRecycle.push_back(std::move(newEntry));
   }
 
   return AnimationFrameDiscardingQueue::ResetInternal();
 }
 
 RawAccessFrameRef AnimationFrameRecyclingQueue::RecycleFrame(
     gfx::IntRect& aRecycleRect) {
   if (mInsertIndex == 0) {
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -345,18 +345,17 @@ RawAccessFrameRef Decoder::AllocateFrame
     ref = mFrameRecycler->RecycleFrame(mRecycleRect);
     if (ref) {
       // If the recycled frame is actually the current restore frame, we cannot
       // use it. If the next restore frame is the new frame we are creating, in
       // theory we could reuse it, but we would need to store the restore frame
       // animation parameters elsewhere. For now we just drop it.
       bool blocked = ref.get() == mRestoreFrame.get();
       if (!blocked) {
-        nsresult rv = ref->InitForDecoderRecycle(aAnimParams.ref());
-        blocked = NS_WARN_IF(NS_FAILED(rv));
+        blocked = NS_FAILED(ref->InitForDecoderRecycle(aAnimParams.ref()));
       }
 
       if (blocked) {
         ref.reset();
       }
     }
   }
 
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -300,17 +300,22 @@ nsresult imgFrame::InitForDecoder(const 
 nsresult imgFrame::InitForDecoderRecycle(const AnimationParams& aAnimParams) {
   // We want to recycle this frame, but there is no guarantee that consumers are
   // done with it in a timely manner. Let's ensure they are done with it first.
   MonitorAutoLock lock(mMonitor);
 
   MOZ_ASSERT(mIsFullFrame);
   MOZ_ASSERT(mLockCount > 0);
   MOZ_ASSERT(mLockedSurface);
-  MOZ_ASSERT(mShouldRecycle);
+
+  if (!mShouldRecycle) {
+    // This frame either was never marked as recyclable, or the flag was cleared
+    // for a caller which does not support recycling.
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   if (mRecycleLockCount > 0) {
     if (NS_IsMainThread()) {
       // We should never be both decoding and recycling on the main thread. Sync
       // decoding can only be used to produce the first set of frames. Those
       // either never use recycling because advancing was blocked (main thread
       // is busy) or we were auto-advancing (to seek to a frame) and the frames
       // were never accessed (and thus cannot have recycle locks).
@@ -619,27 +624,35 @@ bool imgFrame::Draw(gfxContext* aContext
 
     // Most draw targets will just use the surface only during DrawPixelSnapped
     // but captures/recordings will retain a reference outside this stack
     // context. While in theory a decoder thread could be trying to recycle this
     // frame at this very moment, in practice the only way we can get here is if
     // this frame is the current frame of the animation. Since we can only
     // advance on the main thread, we know nothing else will try to use it.
     DrawTarget* drawTarget = aContext->GetDrawTarget();
-    bool temporary = !drawTarget->IsCaptureDT() &&
-                     drawTarget->GetBackendType() != BackendType::RECORDING;
+    bool recording = drawTarget->GetBackendType() == BackendType::RECORDING;
+    bool temporary = !drawTarget->IsCaptureDT() && !recording;
     RefPtr<SourceSurface> surf = GetSourceSurfaceInternal(temporary);
     if (!surf) {
       return false;
     }
 
     bool doTile = !imageRect.Contains(aRegion.Rect()) &&
                   !(aImageFlags & imgIContainer::FLAG_CLAMP);
 
     surfaceResult = SurfaceForDrawing(doPartialDecode, doTile, region, surf);
+
+    // If we are recording, then we cannot recycle the surface. The blob
+    // rasterizer is not properly synchronized for recycling in the compositor
+    // process. The easiest thing to do is just mark the frames it consumes as
+    // non-recyclable.
+    if (recording && surfaceResult.IsValid()) {
+      mShouldRecycle = false;
+    }
   }
 
   if (surfaceResult.IsValid()) {
     gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
                                imageRect.Size(), region, surfaceResult.mFormat,
                                aSamplingFilter, aImageFlags, aOpacity);
   }
 
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -197,18 +197,16 @@ class imgFrame {
   const IntRect& GetDirtyRect() const { return mDirtyRect; }
   void SetDirtyRect(const IntRect& aDirtyRect) { mDirtyRect = aDirtyRect; }
 
   bool IsFullFrame() const { return mIsFullFrame; }
 
   bool GetCompositingFailed() const;
   void SetCompositingFailed(bool val);
 
-  bool ShouldRecycle() const { return mShouldRecycle; }
-
   void SetOptimizable();
 
   void FinalizeSurface();
   already_AddRefed<SourceSurface> GetSourceSurface();
 
   struct AddSizeOfCbData {
     AddSizeOfCbData()
         : heap(0), nonHeap(0), handles(0), index(0), externalId(0) {}
--- a/image/test/gtest/TestAnimationFrameBuffer.cpp
+++ b/image/test/gtest/TestAnimationFrameBuffer.cpp
@@ -26,16 +26,27 @@ static already_AddRefed<imgFrame> Create
   frame->SetRawAccessOnly();
   // Normally the blend animation filter would set the dirty rect, but since
   // we aren't producing an actual animation here, we need to fake it.
   frame->SetDirtyRect(aFrameRect);
   frame->Finish();
   return frame.forget();
 }
 
+static bool ReinitForRecycle(RawAccessFrameRef& aFrame) {
+  if (!aFrame) {
+    return false;
+  }
+
+  AnimationParams animParams{aFrame->GetRect(), FrameTimeout::Forever(),
+                             /* aFrameNum */ 1, BlendMethod::OVER,
+                             DisposalMethod::NOT_SPECIFIED};
+  return NS_SUCCEEDED(aFrame->InitForDecoderRecycle(animParams));
+}
+
 static void PrepareForDiscardingQueue(AnimationFrameRetainedBuffer& aQueue) {
   ASSERT_EQ(size_t(0), aQueue.Size());
   ASSERT_LT(size_t(1), aQueue.Batch());
 
   AnimationFrameBuffer::InsertStatus status = aQueue.Insert(CreateEmptyFrame());
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
 
   while (true) {
@@ -95,21 +106,17 @@ static void VerifyAdvance(AnimationFrame
 
   if (aQueue.IsRecycling()) {
     const AnimationFrameRecyclingQueue& queue =
         *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
     EXPECT_FALSE(queue.Recycle().back().mDirtyRect.IsEmpty());
     EXPECT_TRUE(
         queue.Recycle().back().mDirtyRect.Contains(oldFrame->GetDirtyRect()));
     EXPECT_EQ(totalRecycled + 1, queue.Recycle().size());
-    if (oldFrame->ShouldRecycle()) {
-      EXPECT_EQ(oldFrame.get(), queue.Recycle().back().mFrame.get());
-    } else {
-      EXPECT_EQ(nullptr, queue.Recycle().back().mFrame.get());
-    }
+    EXPECT_EQ(oldFrame.get(), queue.Recycle().back().mFrame.get());
   }
 }
 
 static void VerifyInsertAndAdvance(
     AnimationFrameBuffer& aQueue, size_t aExpectedFrame,
     AnimationFrameBuffer::InsertStatus aExpectedStatus) {
   // Insert the decoded frame.
   RefPtr<imgFrame> frame = CreateEmptyFrame();
@@ -559,23 +566,24 @@ TEST_F(ImageAnimationFrameBuffer, Recycl
     RefPtr<imgFrame> expectedFrame = buffer.Recycle().front().mFrame;
     EXPECT_FALSE(expectedRect.IsEmpty());
     EXPECT_TRUE(expectedFrame.get() != nullptr);
 
     IntRect gotRect;
     RawAccessFrameRef gotFrame = buffer.RecycleFrame(gotRect);
     EXPECT_EQ(expectedFrame.get(), gotFrame.get());
     EXPECT_EQ(expectedRect, gotRect);
+    EXPECT_TRUE(ReinitForRecycle(gotFrame));
   }
 
   // Trying to pull a recycled frame when we have nothing should be safe too.
   IntRect gotRect;
   RawAccessFrameRef gotFrame = buffer.RecycleFrame(gotRect);
   EXPECT_TRUE(gotFrame.get() == nullptr);
-  EXPECT_TRUE(gotRect.IsEmpty());
+  EXPECT_FALSE(ReinitForRecycle(gotFrame));
 }
 
 static void TestDiscardingQueueReset(AnimationFrameDiscardingQueue& aQueue,
                                      const imgFrame* aFirstFrame,
                                      size_t aThreshold, size_t aBatch,
                                      size_t aStartFrame) {
   // We should be advanced right up to the last decoded frame.
   EXPECT_TRUE(aQueue.MayDiscard());
@@ -740,17 +748,23 @@ TEST_F(ImageAnimationFrameBuffer, Recycl
   status = retained.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_YIELD, status);
 
   AnimationFrameRecyclingQueue buffer(std::move(retained));
   bool restartDecoding = buffer.Reset();
   EXPECT_TRUE(restartDecoding);
 
   // None of the buffers were recyclable.
-  EXPECT_TRUE(buffer.Recycle().empty());
+  EXPECT_FALSE(buffer.Recycle().empty());
+  while (!buffer.Recycle().empty()) {
+    IntRect recycleRect;
+    RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
+    EXPECT_TRUE(frameRef);
+    EXPECT_FALSE(ReinitForRecycle(frameRef));
+  }
 
   // Reinsert the first two frames as recyclable and reset again.
   frame = CreateEmptyFrame(kImageSize, kImageRect, true);
   status = buffer.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
 
   frame = CreateEmptyFrame(kImageSize, IntRect(IntPoint(10, 10), IntSize(1, 1)),
                            true);
@@ -768,86 +782,83 @@ TEST_F(ImageAnimationFrameBuffer, Recycl
   }
 }
 
 TEST_F(ImageAnimationFrameBuffer, RecyclingRect) {
   const size_t kThreshold = 5;
   const size_t kBatch = 2;
   const size_t kStartFrame = 0;
   const IntSize kImageSize(100, 100);
+  const IntRect kImageRect(IntPoint(0, 0), kImageSize);
   AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
 
   // Let's get to the recycling state while marking all of the frames as not
   // recyclable, just like AnimationFrameBuffer / the decoders would do.
   RefPtr<imgFrame> frame;
-  frame =
-      CreateEmptyFrame(kImageSize, IntRect(IntPoint(0, 0), kImageSize), false);
+  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
   AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
 
-  frame =
-      CreateEmptyFrame(kImageSize, IntRect(IntPoint(0, 0), kImageSize), false);
+  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
   status = retained.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
 
-  frame =
-      CreateEmptyFrame(kImageSize, IntRect(IntPoint(0, 0), kImageSize), false);
+  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
   status = retained.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
 
-  frame =
-      CreateEmptyFrame(kImageSize, IntRect(IntPoint(0, 0), kImageSize), false);
+  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
   status = retained.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
 
   VerifyAdvance(retained, 1, false);
   VerifyAdvance(retained, 2, true);
   VerifyAdvance(retained, 3, false);
 
-  frame =
-      CreateEmptyFrame(kImageSize, IntRect(IntPoint(0, 0), kImageSize), false);
+  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
   status = retained.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE, status);
 
   AnimationFrameRecyclingQueue buffer(std::move(retained));
 
   // The first frame is now the candidate for recycling. Since it was marked as
   // not recyclable, we should get nothing.
   VerifyAdvance(buffer, 4, false);
 
   IntRect recycleRect;
   EXPECT_FALSE(buffer.Recycle().empty());
   RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
-  EXPECT_FALSE(frameRef);
-  EXPECT_TRUE(recycleRect.IsEmpty());
+  EXPECT_TRUE(frameRef);
+  EXPECT_FALSE(ReinitForRecycle(frameRef));
   EXPECT_TRUE(buffer.Recycle().empty());
 
   // Insert a recyclable partial frame. Its dirty rect shouldn't matter since
   // the previous frame was not recyclable.
   frame = CreateEmptyFrame(kImageSize, IntRect(0, 0, 25, 25));
   status = buffer.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
 
   VerifyAdvance(buffer, 5, true);
   EXPECT_FALSE(buffer.Recycle().empty());
   frameRef = buffer.RecycleFrame(recycleRect);
-  EXPECT_FALSE(frameRef);
-  EXPECT_TRUE(recycleRect.IsEmpty());
+  EXPECT_TRUE(frameRef);
+  EXPECT_FALSE(ReinitForRecycle(frameRef));
   EXPECT_TRUE(buffer.Recycle().empty());
 
   // Insert a recyclable partial frame. Its dirty rect should match the recycle
   // rect since it is the only frame in the buffer.
   frame = CreateEmptyFrame(kImageSize, IntRect(25, 0, 50, 50));
   status = buffer.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
 
   VerifyAdvance(buffer, 6, true);
   EXPECT_FALSE(buffer.Recycle().empty());
   frameRef = buffer.RecycleFrame(recycleRect);
   EXPECT_TRUE(frameRef);
+  EXPECT_TRUE(ReinitForRecycle(frameRef));
   EXPECT_EQ(IntRect(25, 0, 50, 50), recycleRect);
   EXPECT_TRUE(buffer.Recycle().empty());
 
   // Insert the last frame and mark us as complete. The next recycled frame is
   // producing the first frame again, so we should use the first frame refresh
   // area instead of its dirty rect.
   frame = CreateEmptyFrame(kImageSize, IntRect(10, 10, 60, 10));
   status = buffer.Insert(std::move(frame));
@@ -855,26 +866,27 @@ TEST_F(ImageAnimationFrameBuffer, Recycl
 
   bool continueDecoding = buffer.MarkComplete(IntRect(0, 0, 75, 50));
   EXPECT_FALSE(continueDecoding);
 
   VerifyAdvance(buffer, 7, true);
   EXPECT_FALSE(buffer.Recycle().empty());
   frameRef = buffer.RecycleFrame(recycleRect);
   EXPECT_TRUE(frameRef);
+  EXPECT_TRUE(ReinitForRecycle(frameRef));
   EXPECT_EQ(IntRect(0, 0, 75, 50), recycleRect);
   EXPECT_TRUE(buffer.Recycle().empty());
 
   // Now let's reinsert the first frame. The recycle rect should still be the
   // first frame refresh area instead of the dirty rect of the first frame (e.g.
   // the full frame).
-  frame =
-      CreateEmptyFrame(kImageSize, IntRect(IntPoint(0, 0), kImageSize), false);
+  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
   status = buffer.Insert(std::move(frame));
   EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
 
   VerifyAdvance(buffer, 0, true);
   EXPECT_FALSE(buffer.Recycle().empty());
   frameRef = buffer.RecycleFrame(recycleRect);
   EXPECT_TRUE(frameRef);
+  EXPECT_TRUE(ReinitForRecycle(frameRef));
   EXPECT_EQ(IntRect(0, 0, 75, 50), recycleRect);
   EXPECT_TRUE(buffer.Recycle().empty());
 }
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -1039,24 +1039,28 @@ bool GeckoChildProcessHost::PerformAsync
   } else
 #endif  // defined(MOZ_SANDBOX)
   {
     base::LaunchApp(cmdLine, *mLaunchOptions, &process);
 
 #ifdef MOZ_SANDBOX
     // We need to be able to duplicate handles to some types of non-sandboxed
     // child processes.
-    if (mProcessType == GeckoProcessType_Content ||
-        mProcessType == GeckoProcessType_GPU ||
-        mProcessType == GeckoProcessType_RDD ||
-        mProcessType == GeckoProcessType_VR ||
-        mProcessType == GeckoProcessType_GMPlugin) {
-      if (!mSandboxBroker.AddTargetPeer(process)) {
-        NS_WARNING("Failed to add content process as target peer.");
-      }
+    switch (mProcessType) {
+      case GeckoProcessType_Default:
+        MOZ_CRASH("shouldn't be launching a parent process");
+      case GeckoProcessType_Plugin:
+      case GeckoProcessType_IPDLUnitTest:
+        // No handle duplication necessary.
+        break;
+      default:
+        if (!mSandboxBroker.AddTargetPeer(process)) {
+          NS_WARNING("Failed to add child process as target peer.");
+        }
+        break;
     }
 #endif  // MOZ_SANDBOX
   }
 
 #else  // goes with defined(OS_POSIX)
 #error Sorry
 #endif  // defined(OS_POSIX)
 
--- a/ipc/glue/ProcessUtils.h
+++ b/ipc/glue/ProcessUtils.h
@@ -58,16 +58,17 @@ class SharedPreferenceDeserializer final
   const FileDescriptor& GetPrefMapHandle() const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SharedPreferenceDeserializer);
   Maybe<base::SharedMemoryHandle> mPrefsHandle;
   Maybe<FileDescriptor> mPrefMapHandle;
   Maybe<size_t> mPrefsLen;
   Maybe<size_t> mPrefMapSize;
+  base::SharedMemory mShmem;
 };
 
 #ifdef ANDROID
 // Android doesn't use -prefsHandle or -prefMapHandle. It gets those FDs
 // another way.
 void SetPrefsFd(int aFd);
 void SetPrefMapFd(int aFd);
 #endif
--- a/ipc/glue/ProcessUtils_common.cpp
+++ b/ipc/glue/ProcessUtils_common.cpp
@@ -134,26 +134,25 @@ bool SharedPreferenceDeserializer::Deser
     return false;
   }
 
   // Init the shared-memory base preference mapping first, so that only changed
   // preferences wind up in heap memory.
   Preferences::InitSnapshot(mPrefMapHandle.ref(), *mPrefMapSize);
 
   // Set up early prefs from the shared memory.
-  base::SharedMemory shm;
-  if (!shm.SetHandle(*mPrefsHandle, /* read_only */ true)) {
+  if (!mShmem.SetHandle(*mPrefsHandle, /* read_only */ true)) {
     NS_ERROR("failed to open shared memory in the child");
     return false;
   }
-  if (!shm.Map(*mPrefsLen)) {
+  if (!mShmem.Map(*mPrefsLen)) {
     NS_ERROR("failed to map shared memory in the child");
     return false;
   }
-  Preferences::DeserializePreferences(static_cast<char*>(shm.memory()),
+  Preferences::DeserializePreferences(static_cast<char*>(mShmem.memory()),
                                       *mPrefsLen);
 
   return true;
 }
 
 const base::SharedMemoryHandle& SharedPreferenceDeserializer::GetPrefsHandle()
     const {
   MOZ_ASSERT(mPrefsHandle.isSome());
@@ -163,9 +162,9 @@ const base::SharedMemoryHandle& SharedPr
 
 const FileDescriptor& SharedPreferenceDeserializer::GetPrefMapHandle() const {
   MOZ_ASSERT(mPrefMapHandle.isSome());
 
   return mPrefMapHandle.ref();
 }
 
 }  // namespace ipc
-}  // namespace mozilla
\ No newline at end of file
+}  // namespace mozilla
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -7244,45 +7244,54 @@ GCRuntime::IncrementalResult GCRuntime::
     budget.makeUnlimited();
     stats().nonincremental(unsafeReason);
     return resetIncrementalGC(unsafeReason);
   }
 
   if (mallocCounter.shouldTriggerGC(tunables) == NonIncrementalTrigger) {
     budget.makeUnlimited();
     stats().nonincremental(AbortReason::MallocBytesTrigger);
-  }
-
-  bool reset = false;
+    if (isIncrementalGCInProgress() && state() > State::Sweep) {
+      return resetIncrementalGC(AbortReason::MallocBytesTrigger);
+    }
+  }
+
+  AbortReason resetReason = AbortReason::None;
   for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
     if (!zone->canCollect()) {
       continue;
     }
 
     if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
       CheckZoneIsScheduled(zone, reason, "GC bytes");
       budget.makeUnlimited();
       stats().nonincremental(AbortReason::GCBytesTrigger);
+      if (zone->wasGCStarted() && zone->gcState() > Zone::Sweep) {
+        resetReason = AbortReason::GCBytesTrigger;
+      }
     }
 
     if (zone->shouldTriggerGCForTooMuchMalloc() == NonIncrementalTrigger) {
       CheckZoneIsScheduled(zone, reason, "malloc bytes");
       budget.makeUnlimited();
       stats().nonincremental(AbortReason::MallocBytesTrigger);
+      if (zone->wasGCStarted() && zone->gcState() > Zone::Sweep) {
+        resetReason = AbortReason::MallocBytesTrigger;
+      }
     }
 
     if (isIncrementalGCInProgress() &&
         zone->isGCScheduled() != zone->wasGCStarted()) {
-      reset = true;
-    }
-  }
-
-  if (reset) {
-    budget.makeUnlimited();
-    return resetIncrementalGC(AbortReason::ZoneChange);
+      budget.makeUnlimited();
+      resetReason = AbortReason::ZoneChange;
+    }
+  }
+
+  if (resetReason != AbortReason::None) {
+    return resetIncrementalGC(resetReason);
   }
 
   return IncrementalResult::Ok;
 }
 
 namespace {
 
 class AutoScheduleZonesForGC {
@@ -7626,16 +7635,25 @@ void GCRuntime::collect(bool nonincremen
         reason = JS::gcreason::ROOTS_REMOVED;
       } else if (shouldRepeatForDeadZone(reason)) {
         repeat = true;
         reason = JS::gcreason::COMPARTMENT_REVIVED;
       }
     }
   } while (repeat);
 
+#ifdef DEBUG
+  if (!isIncrementalGCInProgress()) {
+    for (ZonesIter zone(rt, WithAtoms); zone.done(); zone.next()) {
+      MOZ_ASSERT(!zone->gcMallocCounter.triggered());
+      MOZ_ASSERT(!zone->jitCodeCounter.triggered());
+    }
+  }
+#endif
+
   if (reason == JS::gcreason::COMPARTMENT_REVIVED) {
     maybeDoCycleCollection();
   }
 
 #ifdef JS_GC_ZEAL
   if (hasZealMode(ZealMode::CheckHeapAfterGC)) {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::TRACE_HEAP);
     CheckHeapAfterGC(rt);
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4265,16 +4265,36 @@ var gCSSProperties = {
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "padding-top", "padding-right", "padding-bottom", "padding-left" ],
     initial_values: [ "0", "0px 0 0em", "0% 0px 0em 0pt", "calc(0px) calc(0em) calc(-2px) calc(-1%)" ],
     other_values: [ "3px 0", "2em 4px 2pt", "1em 2em 3px 4px" ],
     invalid_values: [ "1px calc(nonsense)", "1px red", "-1px" ],
     unbalanced_values: [ "1px calc(" ],
     quirks_values: { "5": "5px", "3px 6px 2 5px": "3px 6px 2px 5px" },
   },
+  "padding-block": {
+    domProp: "paddingBlock",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "padding-block-start", "padding-block-end" ],
+    initial_values: [ "0", "0px 0em" ],
+    other_values: [ "3px 0", "2% 4px", "1em", "calc(1px) calc(-1%)" ],
+    invalid_values: [ "1px calc(nonsense)", "1px red", "-1px", "auto", "none" ],
+    unbalanced_values: [ "1px calc(" ],
+  },
+  "padding-inline": {
+    domProp: "paddingInline",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "padding-inline-start", "padding-inline-end" ],
+    initial_values: [ "0", "0px 0em" ],
+    other_values: [ "3px 0", "2% 4px", "1em", "calc(1px) calc(-1%)" ],
+    invalid_values: [ "1px calc(nonsense)", "1px red", "-1px", "auto", "none" ],
+    unbalanced_values: [ "1px calc(" ],
+  },
   "padding-bottom": {
     domProp: "paddingBottom",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     // No applies_to_placeholder because we have a !important rule in forms.css.
     initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
     other_values: [ "1px", "2em", "5%",
--- a/servo/components/style/properties/shorthands/padding.mako.rs
+++ b/servo/components/style/properties/shorthands/padding.mako.rs
@@ -2,8 +2,51 @@
  * 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 ${helpers.four_sides_shorthand("padding", "padding-%s", "specified::NonNegativeLengthPercentage::parse",
                                spec="https://drafts.csswg.org/css-box-3/#propdef-padding",
                                allow_quirks=True)}
+
+% for axis in ["block", "inline"]:
+    <%
+        spec = "https://drafts.csswg.org/css-logical/#propdef-padding-%s" % axis
+    %>
+    <%helpers:shorthand
+        name="padding-${axis}"
+        sub_properties="${' '.join(
+            'padding-%s-%s' % (axis, side)
+            for side in ['start', 'end']
+        )}"
+        spec="${spec}">
+
+        use crate::parser::Parse;
+        use crate::values::specified::length::NonNegativeLengthPercentage;
+        pub fn parse_value<'i, 't>(
+            context: &ParserContext,
+            input: &mut Parser<'i, 't>,
+        ) -> Result<Longhands, ParseError<'i>> {
+            let start_value = NonNegativeLengthPercentage::parse(context, input)?;
+            let end_value =
+                input.try(|input| NonNegativeLengthPercentage::parse(context, input)).unwrap_or_else(|_| start_value.clone());
+
+            Ok(expanded! {
+                padding_${axis}_start: start_value,
+                padding_${axis}_end: end_value,
+            })
+        }
+
+        impl<'a> ToCss for LonghandsToSerialize<'a>  {
+            fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
+                self.padding_${axis}_start.to_css(dest)?;
+
+                if self.padding_${axis}_end != self.padding_${axis}_start {
+                    dest.write_str(" ")?;
+                    self.padding_${axis}_end.to_css(dest)?;
+                }
+
+                Ok(())
+            }
+        }
+    </%helpers:shorthand>
+% endfor
--- a/testing/geckodriver/doc/index.rst
+++ b/testing/geckodriver/doc/index.rst
@@ -42,17 +42,17 @@ For developers
    Building.md
    Testing.md
    Releasing.md
 
 
 Communication
 =============
 
-The mailing list for Marionette discussion is
+The mailing list for geckodriver discussion is
 tools-marionette@lists.mozilla.org (`subscribe`_, `archive`_).
 
 If you prefer real-time chat, there is often someone in the #ateam IRC
 channel on irc.mozilla.org.  Don’t ask if you may ask a question;
 just go ahead and ask, and please wait for an answer as we might
 not be in your timezone.
 
 .. _subscribe: https://lists.mozilla.org/listinfo/tools-marionette
--- a/testing/web-platform/meta/css/css-logical/logical-box-padding.html.ini
+++ b/testing/web-platform/meta/css/css-logical/logical-box-padding.html.ini
@@ -1,40 +1,13 @@
 [logical-box-padding.html]
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '.]
-    expected: FAIL
-
-  [Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '.]
-    expected: FAIL
-
   [Test that padding shorthand sets longhands and serializes correctly.]
     expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=137688
 
   [Test that padding-inline shorthand sets longhands and serializes correctly.]
     expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=137688
 
   [Test that padding-block shorthand sets longhands and serializes correctly.]
     expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=137688
 
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -449,17 +449,22 @@ nsThreadManager::NewNamedThread(const ns
 
   thr.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThreadManager::GetMainThread(nsIThread** aResult) {
   // Keep this functioning during Shutdown
-  if (NS_WARN_IF(!mMainThread)) {
+  if (!mMainThread) {
+    if (!NS_IsMainThread()) {
+      NS_WARNING(
+          "Called GetMainThread but there isn't a main thread and "
+          "we're not the main thread.");
+    }
     return NS_ERROR_NOT_INITIALIZED;
   }
   NS_ADDREF(*aResult = mMainThread);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThreadManager::GetCurrentThread(nsIThread** aResult) {